pax_global_header00006660000000000000000000000064146566445310014530gustar00rootroot0000000000000052 comment=c4a32aee21f48d41f781ca3873d4bf1370583204 golang-github-lucas-clemente-quic-go-0.46.0/000077500000000000000000000000001465664453100206075ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/.circleci/000077500000000000000000000000001465664453100224425ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/.circleci/config.yml000066400000000000000000000026441465664453100244400ustar00rootroot00000000000000version: 2.1 executors: test-go122: docker: - image: "cimg/go:1.22" environment: runrace: true TIMESCALE_FACTOR: 3 jobs: "test": &test executor: test-go122 steps: - checkout - run: name: "Build infos" command: go version - run: name: "Run tools tests" command: go run github.com/onsi/ginkgo/v2/ginkgo -race -r -v -randomize-all -trace integrationtests/tools - run: name: "Run self integration tests" command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/self - run: name: "Run version negotiation tests" command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/versionnegotiation - run: name: "Run self integration tests with race detector" command: go run github.com/onsi/ginkgo/v2/ginkgo -race -v -randomize-all -trace integrationtests/self - run: name: "Run self integration tests with qlog" command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/self -- -qlog - run: name: "Run version negotiation tests with qlog" command: go run github.com/onsi/ginkgo/v2/ginkgo -v -randomize-all -trace integrationtests/versionnegotiation -- -qlog go122: <<: *test workflows: workflow: jobs: - go122 golang-github-lucas-clemente-quic-go-0.46.0/.clusterfuzzlite/000077500000000000000000000000001465664453100241435ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/.clusterfuzzlite/Dockerfile000066400000000000000000000011131465664453100261310ustar00rootroot00000000000000FROM gcr.io/oss-fuzz-base/base-builder-go:v1 ARG TARGETPLATFORM RUN echo "TARGETPLATFORM: ${TARGETPLATFORM}" ENV GOVERSION=1.22.0 RUN platform=$(echo ${TARGETPLATFORM} | tr '/' '-') && \ filename="go${GOVERSION}.${platform}.tar.gz" && \ wget https://dl.google.com/go/${filename} && \ mkdir temp-go && \ rm -rf /root/.go/* && \ tar -C temp-go/ -xzf ${filename} && \ mv temp-go/go/* /root/.go/ && \ rm -r ${filename} temp-go RUN apt-get update && apt-get install -y make autoconf automake libtool COPY . $SRC/quic-go WORKDIR quic-go COPY .clusterfuzzlite/build.sh $SRC/ golang-github-lucas-clemente-quic-go-0.46.0/.clusterfuzzlite/build.sh000077500000000000000000000007551465664453100256100ustar00rootroot00000000000000#!/bin/bash -eu export CXX="${CXX} -lresolv" # required by Go 1.20 compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/frames Fuzz frame_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/header Fuzz header_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/transportparameters Fuzz transportparameter_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/tokens Fuzz token_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/handshake Fuzz handshake_fuzzer golang-github-lucas-clemente-quic-go-0.46.0/.clusterfuzzlite/project.yaml000066400000000000000000000000151465664453100264710ustar00rootroot00000000000000language: go golang-github-lucas-clemente-quic-go-0.46.0/.githooks/000077500000000000000000000000001465664453100225145ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/.githooks/README.md000066400000000000000000000002311465664453100237670ustar00rootroot00000000000000# Git Hooks This directory contains useful Git hooks for working with quic-go. Install them by running ```bash git config core.hooksPath .githooks ``` golang-github-lucas-clemente-quic-go-0.46.0/.githooks/pre-commit000077500000000000000000000016221465664453100245170ustar00rootroot00000000000000#!/bin/bash # Check that test files don't contain focussed test cases. errored=false for f in $(git diff --diff-filter=d --cached --name-only); do if [[ $f != *_test.go ]]; then continue; fi output=$(git show :"$f" | grep -n -e "FIt(" -e "FContext(" -e "FDescribe(") if [ $? -eq 0 ]; then echo "$f contains a focussed test:" echo "$output" echo "" errored=true fi done pushd ./integrationtests/gomodvendor > /dev/null go mod tidy if [[ -n $(git diff --diff-filter=d --name-only -- "go.mod" "go.sum") ]]; then echo "go.mod / go.sum in integrationtests/gomodvendor not tidied" errored=true fi popd > /dev/null # Check that all Go files are properly gofumpt-ed. output=$(gofumpt -d $(git diff --diff-filter=d --cached --name-only -- '*.go')) if [ -n "$output" ]; then echo "Found files that are not properly gofumpt-ed." echo "$output" errored=true fi if [ "$errored" = true ]; then exit 1 fi golang-github-lucas-clemente-quic-go-0.46.0/.github/000077500000000000000000000000001465664453100221475ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/.github/FUNDING.yml000066400000000000000000000014641465664453100237710ustar00rootroot00000000000000# These are supported funding model platforms github: [marten-seemann] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] golang-github-lucas-clemente-quic-go-0.46.0/.github/dependabot.yml000066400000000000000000000001661465664453100250020ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/000077500000000000000000000000001465664453100242045ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/build-interop-docker.yml000066400000000000000000000027461465664453100307620ustar00rootroot00000000000000name: Build interop Docker image on: push: branches: - master tags: - 'v*' jobs: interop: runs-on: ${{ fromJSON(vars['DOCKER_RUNNER_UBUNTU'] || '"ubuntu-latest"') }} steps: - uses: actions/checkout@v4 - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: platforms: linux/amd64,linux/arm64 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: set tag name id: tag # Tagged releases won't be picked up by the interop runner automatically, # but they can be useful when debugging regressions. run: | if [[ $GITHUB_REF == refs/tags/* ]]; then echo "tag=${GITHUB_REF#refs/tags/}" | tee -a $GITHUB_OUTPUT; echo "gitref=${GITHUB_REF#refs/tags/}" | tee -a $GITHUB_OUTPUT; else echo 'tag=latest' | tee -a $GITHUB_OUTPUT; echo 'gitref=${{ github.sha }}' | tee -a $GITHUB_OUTPUT; fi - uses: docker/build-push-action@v6 with: context: "{{defaultContext}}:interop" platforms: linux/amd64,linux/arm64 push: true build-args: | GITREF=${{ steps.tag.outputs.gitref }} tags: martenseemann/quic-go-interop:${{ steps.tag.outputs.tag }} golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/clusterfuzz-lite-pr.yml000066400000000000000000000035011465664453100307000ustar00rootroot00000000000000name: ClusterFuzzLite PR fuzzing on: pull_request: paths: - '**' permissions: read-all jobs: PR: runs-on: ${{ fromJSON(vars['CLUSTERFUZZ_LITE_RUNNER_UBUNTU'] || '"ubuntu-latest"') }} concurrency: group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: sanitizer: - address steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@v1 with: language: go github-token: ${{ secrets.GITHUB_TOKEN }} sanitizer: ${{ matrix.sanitizer }} # Optional but recommended: used to only run fuzzers that are affected # by the PR. # See later section on "Git repo for storage". # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git # storage-repo-branch: main # Optional. Defaults to "main" # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@v1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 480 mode: 'code-change' sanitizer: ${{ matrix.sanitizer }} output-sarif: true parallel-fuzzing: true # Optional but recommended: used to download the corpus produced by # batch fuzzing. # See later section on "Git repo for storage". # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git # storage-repo-branch: main # Optional. Defaults to "main" # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/cross-compile.sh000077500000000000000000000015021465664453100273200ustar00rootroot00000000000000#!/bin/bash set -e dist="$1" goos=$(echo "$dist" | cut -d "/" -f1) goarch=$(echo "$dist" | cut -d "/" -f2) # cross-compiling for android is a pain... if [[ "$goos" == "android" ]]; then exit; fi # iOS builds require Cgo, see https://github.com/golang/go/issues/43343 # Cgo would then need a C cross compilation setup. Not worth the hassle. if [[ "$goos" == "ios" ]]; then exit; fi # Write all log output to a temporary file instead of to stdout. # That allows running this script in parallel, while preserving the correct order of the output. log_file=$(mktemp) error_handler() { cat "$log_file" >&2 rm "$log_file" exit 1 } trap 'error_handler' ERR echo "$dist" >> "$log_file" out="main-$goos-$goarch" GOOS=$goos GOARCH=$goarch go build -o $out example/main.go >> "$log_file" 2>&1 rm $out cat "$log_file" rm "$log_file" golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/cross-compile.yml000066400000000000000000000014771465664453100275170ustar00rootroot00000000000000on: [push, pull_request] jobs: crosscompile: strategy: fail-fast: false matrix: go: [ "1.21.x", "1.22.x" ] runs-on: ${{ fromJSON(vars['CROSS_COMPILE_RUNNER_UBUNTU'] || '"ubuntu-latest"') }} name: "Cross Compilation (Go ${{matrix.go}})" timeout-minutes: 30 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Install build utils run: | sudo apt-get update sudo apt-get install -y gcc-multilib - name: Install dependencies run: go build example/main.go - name: Run cross compilation # run in parallel on as many cores as are available on the machine run: go tool dist list | xargs -I % -P "$(nproc)" .github/workflows/cross-compile.sh % golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/go-generate.sh000077500000000000000000000011101465664453100267310ustar00rootroot00000000000000#!/usr/bin/env bash set -e DIR=$(pwd) TMP=$(mktemp -d) cd "$TMP" cp -r "$DIR" orig cp -r "$DIR" generated cd generated # delete all go-generated files generated (that adhere to the comment convention) grep --include \*.go -lrIZ "^// Code generated .* DO NOT EDIT\.$" . | xargs --null rm # First regenerate sys_conn_buffers_write.go. # If it doesn't exist, the following mockgen calls will fail. go generate -run "sys_conn_buffers_write.go" # now generate everything go generate ./... cd .. # don't compare fuzzing corpora diff --exclude=corpus --exclude=.git -ruN orig generated golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/integration.yml000066400000000000000000000071421465664453100272560ustar00rootroot00000000000000on: [push, pull_request] jobs: integration: strategy: fail-fast: false matrix: os: [ "ubuntu" ] go: [ "1.21.x", "1.22.x", "1.23.0-rc.2" ] include: - os: "windows" go: "1.21.x" - os: "macos" go: "1.21.x" runs-on: ${{ fromJSON(vars[format('INTEGRATION_RUNNER_{0}', matrix.os)] || format('"{0}-latest"', matrix.os)) }} timeout-minutes: 30 defaults: run: shell: bash # by default Windows uses PowerShell, which uses a different syntax for setting environment variables env: DEBUG: false # set this to true to export qlogs and save them as artifacts TIMESCALE_FACTOR: 3 name: Integration Tests (${{ matrix.os }}, Go ${{ matrix.go }}) steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - run: go version - name: set qlogger if: env.DEBUG == 'true' run: echo "QLOGFLAG= -qlog" >> $GITHUB_ENV - name: Run other tests run: | go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace -skip-package self,versionnegotiation integrationtests go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/versionnegotiation -- ${{ env.QLOGFLAG }} - name: Run self tests, using QUIC v1 if: success() || failure() # run this step even if the previous one failed run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=1 ${{ env.QLOGFLAG }} - name: Run self tests, using QUIC v2 if: success() || failure() # run this step even if the previous one failed run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=2 ${{ env.QLOGFLAG }} - name: Run self tests, with GSO disabled if: ${{ matrix.os == 'ubuntu' && (success() || failure()) }} # run this step even if the previous one failed env: QUIC_GO_DISABLE_GSO: true run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=1 ${{ env.QLOGFLAG }} - name: Run self tests, with ECN disabled if: ${{ matrix.os == 'ubuntu' && (success() || failure()) }} # run this step even if the previous one failed env: QUIC_GO_DISABLE_ECN: true run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- -version=1 ${{ env.QLOGFLAG }} - name: Run tests (32 bit) if: ${{ matrix.os != 'macos' && (success() || failure()) }} # run this step even if the previous one failed env: GOARCH: 386 run: | go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace -skip-package self,versionnegotiation integrationtests go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/versionnegotiation -- ${{ env.QLOGFLAG }} go run github.com/onsi/ginkgo/v2/ginkgo -r -v -randomize-all -randomize-suites -trace integrationtests/self -- ${{ env.QLOGFLAG }} - name: Run benchmarks run: go test -v -run=^$ -bench=. ./integrationtests/self - name: save qlogs if: ${{ always() && env.DEBUG == 'true' }} uses: actions/upload-artifact@v4 with: name: qlogs-${{ matrix.os }}-go${{ matrix.go }} path: integrationtests/self/*.qlog retention-days: 7 golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/lint.yml000066400000000000000000000057771465664453100257150ustar00rootroot00000000000000on: [push, pull_request] jobs: check: runs-on: ubuntu-latest timeout-minutes: 15 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: "1.22.x" - name: Check that no non-test files import Ginkgo or Gomega run: .github/workflows/no_ginkgo.sh - name: Check for //go:build ignore in .go files run: | IGNORED_FILES=$(grep -rl '//go:build ignore' . --include='*.go') || true if [ -n "$IGNORED_FILES" ]; then echo "::error::Found ignored Go files: $IGNORED_FILES" exit 1 fi - name: Check that go.mod is tidied if: success() || failure() # run this step even if the previous one failed run: | cp go.mod go.mod.orig cp go.sum go.sum.orig go mod tidy diff go.mod go.mod.orig diff go.sum go.sum.orig - name: Run code generators if: success() || failure() # run this step even if the previous one failed run: .github/workflows/go-generate.sh - name: Check that go mod vendor works if: success() || failure() # run this step even if the previous one failed run: | cd integrationtests/gomodvendor go mod vendor golangci-lint: runs-on: ubuntu-latest strategy: fail-fast: false matrix: go: [ "1.21.x", "1.22.x" ] env: GOLANGCI_LINT_VERSION: v1.58.0 name: golangci-lint (Go ${{ matrix.go }}) steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: golangci-lint (Linux) uses: golangci/golangci-lint-action@v6 with: args: --timeout=3m version: ${{ env.GOLANGCI_LINT_VERSION }} - name: golangci-lint (Windows) if: success() || failure() # run this step even if the previous one failed uses: golangci/golangci-lint-action@v6 env: GOOS: "windows" with: args: --timeout=3m version: ${{ env.GOLANGCI_LINT_VERSION }} - name: golangci-lint (OSX) if: success() || failure() # run this step even if the previous one failed uses: golangci/golangci-lint-action@v6 env: GOOS: "darwin" with: args: --timeout=3m version: ${{ env.GOLANGCI_LINT_VERSION }} - name: golangci-lint (FreeBSD) if: success() || failure() # run this step even if the previous one failed uses: golangci/golangci-lint-action@v6 env: GOOS: "freebsd" with: args: --timeout=3m version: ${{ env.GOLANGCI_LINT_VERSION }} - name: golangci-lint (others) if: success() || failure() # run this step even if the previous one failed uses: golangci/golangci-lint-action@v6 env: GOOS: "solaris" # some OS that we don't have any build tags for with: args: --timeout=3m version: ${{ env.GOLANGCI_LINT_VERSION }} golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/no_ginkgo.sh000077500000000000000000000007261465664453100265220ustar00rootroot00000000000000#!/usr/bin/env bash # Verify that no non-test files import Ginkgo or Gomega. set -e HAS_TESTING=false cd .. for f in $(find . -name "*.go" ! -name "*_test.go" ! -name "tools.go"); do if grep -q "github.com/onsi/ginkgo" $f; then echo "$f imports github.com/onsi/ginkgo/v2" HAS_TESTING=true fi if grep -q "github.com/onsi/gomega" $f; then echo "$f imports github.com/onsi/gomega" HAS_TESTING=true fi done if "$HAS_TESTING"; then exit 1 fi exit 0 golang-github-lucas-clemente-quic-go-0.46.0/.github/workflows/unit.yml000066400000000000000000000043411465664453100257100ustar00rootroot00000000000000on: [push, pull_request] jobs: unit: strategy: fail-fast: false matrix: os: [ "ubuntu", "windows", "macos" ] go: [ "1.21.x", "1.22.x", "1.23.0-rc.2" ] runs-on: ${{ fromJSON(vars[format('UNIT_RUNNER_{0}', matrix.os)] || format('"{0}-latest"', matrix.os)) }} name: Unit tests (${{ matrix.os}}, Go ${{ matrix.go }}) timeout-minutes: 30 steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - run: go version - name: Run tests env: TIMESCALE_FACTOR: 10 run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -cover -randomize-all -randomize-suites -trace -skip-package integrationtests - name: Run tests as root if: ${{ matrix.os == 'ubuntu' }} env: TIMESCALE_FACTOR: 10 FILE: sys_conn_helper_linux_test.go run: | test -f $FILE # make sure the file actually exists go run github.com/onsi/ginkgo/v2/ginkgo build -cover -tags root . sudo ./quic-go.test -ginkgo.v -ginkgo.trace -ginkgo.randomize-all -ginkgo.focus-file=$FILE -test.coverprofile coverage-root.txt rm quic-go.test - name: Run tests (32 bit) if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX. env: TIMESCALE_FACTOR: 10 GOARCH: 386 run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -cover -coverprofile coverage.txt -output-dir . -randomize-all -randomize-suites -trace -skip-package integrationtests - name: Run tests with race detector if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow env: TIMESCALE_FACTOR: 20 run: go run github.com/onsi/ginkgo/v2/ginkgo -r -v -race -randomize-all -randomize-suites -trace -skip-package integrationtests - name: Run benchmark tests run: go test -v -run=^$ -benchtime 0.5s -bench=. $(go list ./... | grep -v integrationtests/self) - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 with: files: coverage.txt,coverage-root.txt env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} token: ${{ secrets.CODECOV_TOKEN }} golang-github-lucas-clemente-quic-go-0.46.0/.gitignore000066400000000000000000000003321465664453100225750ustar00rootroot00000000000000debug debug.test main mockgen_tmp.go *.qtr *.qlog *.sqlog *.txt race.[0-9]* fuzzing/*/*.zip fuzzing/*/coverprofile fuzzing/*/crashers fuzzing/*/sonarprofile fuzzing/*/suppressions fuzzing/*/corpus/ gomock_reflect_*/ golang-github-lucas-clemente-quic-go-0.46.0/.golangci.yml000066400000000000000000000014631465664453100231770ustar00rootroot00000000000000linters-settings: misspell: ignore-words: - ect depguard: rules: quicvarint: list-mode: strict files: - "**/github.com/quic-go/quic-go/quicvarint/*" - "!$test" allow: - $gostd linters: disable-all: true enable: - asciicheck - depguard - exhaustive - exportloopref - goimports - gofmt # redundant, since gofmt *should* be a no-op after gofumpt - gofumpt - gosimple - govet - ineffassign - misspell - prealloc - staticcheck - stylecheck - unconvert - unparam - unused issues: exclude-files: - internal/handshake/cipher_suite.go exclude-rules: - path: internal/qtls linters: - depguard - path: _test\.go linters: - exhaustive golang-github-lucas-clemente-quic-go-0.46.0/Changelog.md000066400000000000000000000106131465664453100230210ustar00rootroot00000000000000# Changelog ## v0.22.0 (2021-07-25) - Use `ReadBatch` to read multiple UDP packets from the socket with a single syscall - Add a config option (`Config.DisableVersionNegotiationPackets`) to disable sending of Version Negotiation packets - Drop support for QUIC draft versions 32 and 34 - Remove the `RetireBugBackwardsCompatibilityMode`, which was intended to mitigate a bug when retiring connection IDs in quic-go in v0.17.2 and ealier ## v0.21.2 (2021-07-15) - Update qtls (for Go 1.15, 1.16 and 1.17rc1) to include the fix for the crypto/tls panic (see https://groups.google.com/g/golang-dev/c/5LJ2V7rd-Ag/m/YGLHVBZ6AAAJ for details) ## v0.21.0 (2021-06-01) - quic-go now supports RFC 9000! ## v0.20.0 (2021-03-19) - Remove the `quic.Config.HandshakeTimeout`. Introduce a `quic.Config.HandshakeIdleTimeout`. ## v0.17.1 (2020-06-20) - Supports QUIC WG draft-29. - Improve bundling of ACK frames (#2543). ## v0.16.0 (2020-05-31) - Supports QUIC WG draft-28. ## v0.15.0 (2020-03-01) - Supports QUIC WG draft-27. - Add support for 0-RTT. - Remove `Session.Close()`. Applications need to pass an application error code to the transport using `Session.CloseWithError()`. - Make the TLS Cipher Suites configurable (via `tls.Config.CipherSuites`). ## v0.14.0 (2019-12-04) - Supports QUIC WG draft-24. ## v0.13.0 (2019-11-05) - Supports QUIC WG draft-23. - Add an `EarlyListener` that allows sending of 0.5-RTT data. - Add a `TokenStore` to store address validation tokens. - Issue and use new connection IDs during a connection. ## v0.12.0 (2019-08-05) - Implement HTTP/3. - Rename `quic.Cookie` to `quic.Token` and `quic.Config.AcceptCookie` to `quic.Config.AcceptToken`. - Distinguish between Retry tokens and tokens sent in NEW_TOKEN frames. - Enforce application protocol negotiation (via `tls.Config.NextProtos`). - Use a varint for error codes. - Add support for [quic-trace](https://github.com/google/quic-trace). - Add a context to `Listener.Accept`, `Session.Accept{Uni}Stream` and `Session.Open{Uni}StreamSync`. - Implement TLS key updates. ## v0.11.0 (2019-04-05) - Drop support for gQUIC. For qQUIC support, please switch to the *gquic* branch. - Implement QUIC WG draft-19. - Use [qtls](https://github.com/marten-seemann/qtls) for TLS 1.3. - Return a `tls.ConnectionState` from `quic.Session.ConnectionState()`. - Remove the error return values from `quic.Stream.CancelRead()` and `quic.Stream.CancelWrite()` ## v0.10.0 (2018-08-28) - Add support for QUIC 44, drop support for QUIC 42. ## v0.9.0 (2018-08-15) - Add a `quic.Config` option for the length of the connection ID (for IETF QUIC). - Split Session.Close into one method for regular closing and one for closing with an error. ## v0.8.0 (2018-06-26) - Add support for unidirectional streams (for IETF QUIC). - Add a `quic.Config` option for the maximum number of incoming streams. - Add support for QUIC 42 and 43. - Add dial functions that use a context. - Multiplex clients on a net.PacketConn, when using Dial(conn). ## v0.7.0 (2018-02-03) - The lower boundary for packets included in ACKs is now derived, and the value sent in STOP_WAITING frames is ignored. - Remove `DialNonFWSecure` and `DialAddrNonFWSecure`. - Expose the `ConnectionState` in the `Session` (experimental API). - Implement packet pacing. ## v0.6.0 (2017-12-12) - Add support for QUIC 39, drop support for QUIC 35 - 37 - Added `quic.Config` options for maximal flow control windows - Add a `quic.Config` option for QUIC versions - Add a `quic.Config` option to request omission of the connection ID from a server - Add a `quic.Config` option to configure the source address validation - Add a `quic.Config` option to configure the handshake timeout - Add a `quic.Config` option to configure the idle timeout - Add a `quic.Config` option to configure keep-alive - Rename the STK to Cookie - Implement `net.Conn`-style deadlines for streams - Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/quic-go/quic-go) for details. - Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/quic-go/quic-go/wiki/Logging) for more details. - Rename the `h2quic.QuicRoundTripper` to `h2quic.RoundTripper` - Changed `h2quic.Server.Serve()` to accept a `net.PacketConn` - Drop support for Go 1.7 and 1.8. - Various bugfixes golang-github-lucas-clemente-quic-go-0.46.0/LICENSE000066400000000000000000000021031465664453100216100ustar00rootroot00000000000000MIT License Copyright (c) 2016 the quic-go authors & Google, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-lucas-clemente-quic-go-0.46.0/README.md000066400000000000000000000176271465664453100221030ustar00rootroot00000000000000# A QUIC implementation in pure Go [![Documentation](https://img.shields.io/badge/docs-quic--go.net-red?style=flat)](https://quic-go.net/docs/) [![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/quic-go)](https://pkg.go.dev/github.com/quic-go/quic-go) [![Code Coverage](https://img.shields.io/codecov/c/github/quic-go/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/quic-go/quic-go/) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/quic-go.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:quic-go) quic-go is an implementation of the QUIC protocol ([RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000), [RFC 9001](https://datatracker.ietf.org/doc/html/rfc9001), [RFC 9002](https://datatracker.ietf.org/doc/html/rfc9002)) in Go. It has support for HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) and HTTP Datagrams ([RFC 9297](https://datatracker.ietf.org/doc/html/rfc9297)). In addition to these base RFCs, it also implements the following RFCs: * Unreliable Datagram Extension ([RFC 9221](https://datatracker.ietf.org/doc/html/rfc9221)) * Datagram Packetization Layer Path MTU Discovery (DPLPMTUD, [RFC 8899](https://datatracker.ietf.org/doc/html/rfc8899)) * QUIC Version 2 ([RFC 9369](https://datatracker.ietf.org/doc/html/rfc9369)) * QUIC Event Logging using qlog ([draft-ietf-quic-qlog-main-schema](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-main-schema/) and [draft-ietf-quic-qlog-quic-events](https://datatracker.ietf.org/doc/draft-ietf-quic-qlog-quic-events/)) Support for WebTransport over HTTP/3 ([draft-ietf-webtrans-http3](https://datatracker.ietf.org/doc/draft-ietf-webtrans-http3/)) is implemented in [webtransport-go](https://github.com/quic-go/webtransport-go). Detailed documentation can be found on [quic-go.net](https://quic-go.net/docs/). ## Projects using quic-go | Project | Description | Stars | | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | | [AdGuardHome](https://github.com/AdguardTeam/AdGuardHome) | Free and open source, powerful network-wide ads & trackers blocking DNS server. | ![GitHub Repo stars](https://img.shields.io/github/stars/AdguardTeam/AdGuardHome?style=flat-square) | | [algernon](https://github.com/xyproto/algernon) | Small self-contained pure-Go web server with Lua, Markdown, HTTP/2, QUIC, Redis and PostgreSQL support | ![GitHub Repo stars](https://img.shields.io/github/stars/xyproto/algernon?style=flat-square) | | [caddy](https://github.com/caddyserver/caddy/) | Fast, multi-platform web server with automatic HTTPS | ![GitHub Repo stars](https://img.shields.io/github/stars/caddyserver/caddy?style=flat-square) | | [cloudflared](https://github.com/cloudflare/cloudflared) | A tunneling daemon that proxies traffic from the Cloudflare network to your origins | ![GitHub Repo stars](https://img.shields.io/github/stars/cloudflare/cloudflared?style=flat-square) | | [frp](https://github.com/fatedier/frp) | A fast reverse proxy to help you expose a local server behind a NAT or firewall to the internet | ![GitHub Repo stars](https://img.shields.io/github/stars/fatedier/frp?style=flat-square) | | [go-libp2p](https://github.com/libp2p/go-libp2p) | libp2p implementation in Go, powering [Kubo](https://github.com/ipfs/kubo) (IPFS) and [Lotus](https://github.com/filecoin-project/lotus) (Filecoin), among others | ![GitHub Repo stars](https://img.shields.io/github/stars/libp2p/go-libp2p?style=flat-square) | | [gost](https://github.com/go-gost/gost) | A simple security tunnel written in Go | ![GitHub Repo stars](https://img.shields.io/github/stars/go-gost/gost?style=flat-square) | | [Hysteria](https://github.com/apernet/hysteria) | A powerful, lightning fast and censorship resistant proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/apernet/hysteria?style=flat-square) | | [Mercure](https://github.com/dunglas/mercure) | An open, easy, fast, reliable and battery-efficient solution for real-time communications | ![GitHub Repo stars](https://img.shields.io/github/stars/dunglas/mercure?style=flat-square) | | [OONI Probe](https://github.com/ooni/probe-cli) | Next generation OONI Probe. Library and CLI tool. | ![GitHub Repo stars](https://img.shields.io/github/stars/ooni/probe-cli?style=flat-square) | | [RoadRunner](https://github.com/roadrunner-server/roadrunner) | High-performance PHP application server, process manager written in Go and powered with plugins | ![GitHub Repo stars](https://img.shields.io/github/stars/roadrunner-server/roadrunner?style=flat-square) | | [syncthing](https://github.com/syncthing/syncthing/) | Open Source Continuous File Synchronization | ![GitHub Repo stars](https://img.shields.io/github/stars/syncthing/syncthing?style=flat-square) | | [traefik](https://github.com/traefik/traefik) | The Cloud Native Application Proxy | ![GitHub Repo stars](https://img.shields.io/github/stars/traefik/traefik?style=flat-square) | | [v2ray-core](https://github.com/v2fly/v2ray-core) | A platform for building proxies to bypass network restrictions | ![GitHub Repo stars](https://img.shields.io/github/stars/v2fly/v2ray-core?style=flat-square) | | [YoMo](https://github.com/yomorun/yomo) | Streaming Serverless Framework for Geo-distributed System | ![GitHub Repo stars](https://img.shields.io/github/stars/yomorun/yomo?style=flat-square) | If you'd like to see your project added to this list, please send us a PR. ## Release Policy quic-go always aims to support the latest two Go releases. ## Contributing We are always happy to welcome new contributors! We have a number of self-contained issues that are suitable for first-time contributors, they are tagged with [help wanted](https://github.com/quic-go/quic-go/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22). If you have any questions, please feel free to reach out by opening an issue or leaving a comment. golang-github-lucas-clemente-quic-go-0.46.0/SECURITY.md000066400000000000000000000015071465664453100224030ustar00rootroot00000000000000# Security Policy quic-go still in development. This means that there may be problems in our protocols, or there may be mistakes in our implementations. We take security vulnerabilities very seriously. If you discover a security issue, please bring it to our attention right away! ## Reporting a Vulnerability If you find a vulnerability that may affect live deployments -- for example, by exposing a remote execution exploit -- please [**report privately**](https://github.com/quic-go/quic-go/security/advisories/new). Please **DO NOT file a public issue**. If the issue is an implementation weakness that cannot be immediately exploited or something not yet deployed, just discuss it openly. ## Reporting a non security bug For non-security bugs, please simply file a GitHub [issue](https://github.com/quic-go/quic-go/issues/new). golang-github-lucas-clemente-quic-go-0.46.0/buffer_pool.go000066400000000000000000000043241465664453100234430ustar00rootroot00000000000000package quic import ( "sync" "github.com/quic-go/quic-go/internal/protocol" ) type packetBuffer struct { Data []byte // refCount counts how many packets Data is used in. // It doesn't support concurrent use. // It is > 1 when used for coalesced packet. refCount int } // Split increases the refCount. // It must be called when a packet buffer is used for more than one packet, // e.g. when splitting coalesced packets. func (b *packetBuffer) Split() { b.refCount++ } // Decrement decrements the reference counter. // It doesn't put the buffer back into the pool. func (b *packetBuffer) Decrement() { b.refCount-- if b.refCount < 0 { panic("negative packetBuffer refCount") } } // MaybeRelease puts the packet buffer back into the pool, // if the reference counter already reached 0. func (b *packetBuffer) MaybeRelease() { // only put the packetBuffer back if it's not used any more if b.refCount == 0 { b.putBack() } } // Release puts back the packet buffer into the pool. // It should be called when processing is definitely finished. func (b *packetBuffer) Release() { b.Decrement() if b.refCount != 0 { panic("packetBuffer refCount not zero") } b.putBack() } // Len returns the length of Data func (b *packetBuffer) Len() protocol.ByteCount { return protocol.ByteCount(len(b.Data)) } func (b *packetBuffer) Cap() protocol.ByteCount { return protocol.ByteCount(cap(b.Data)) } func (b *packetBuffer) putBack() { if cap(b.Data) == protocol.MaxPacketBufferSize { bufferPool.Put(b) return } if cap(b.Data) == protocol.MaxLargePacketBufferSize { largeBufferPool.Put(b) return } panic("putPacketBuffer called with packet of wrong size!") } var bufferPool, largeBufferPool sync.Pool func getPacketBuffer() *packetBuffer { buf := bufferPool.Get().(*packetBuffer) buf.refCount = 1 buf.Data = buf.Data[:0] return buf } func getLargePacketBuffer() *packetBuffer { buf := largeBufferPool.Get().(*packetBuffer) buf.refCount = 1 buf.Data = buf.Data[:0] return buf } func init() { bufferPool.New = func() any { return &packetBuffer{Data: make([]byte, 0, protocol.MaxPacketBufferSize)} } largeBufferPool.New = func() any { return &packetBuffer{Data: make([]byte, 0, protocol.MaxLargePacketBufferSize)} } } golang-github-lucas-clemente-quic-go-0.46.0/buffer_pool_test.go000066400000000000000000000026041465664453100245010ustar00rootroot00000000000000package quic import ( "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Buffer Pool", func() { It("returns buffers of cap", func() { buf1 := getPacketBuffer() Expect(buf1.Data).To(HaveCap(protocol.MaxPacketBufferSize)) buf2 := getLargePacketBuffer() Expect(buf2.Data).To(HaveCap(protocol.MaxLargePacketBufferSize)) }) It("releases buffers", func() { buf1 := getPacketBuffer() buf1.Release() buf2 := getLargePacketBuffer() buf2.Release() }) It("gets the length", func() { buf := getPacketBuffer() buf.Data = append(buf.Data, []byte("foobar")...) Expect(buf.Len()).To(BeEquivalentTo(6)) }) It("panics if wrong-sized buffers are passed", func() { buf := getPacketBuffer() buf.Data = make([]byte, 10) Expect(func() { buf.Release() }).To(Panic()) }) It("panics if it is released twice", func() { buf := getPacketBuffer() buf.Release() Expect(func() { buf.Release() }).To(Panic()) }) It("panics if it is decremented too many times", func() { buf := getPacketBuffer() buf.Decrement() Expect(func() { buf.Decrement() }).To(Panic()) }) It("waits until all parts have been released", func() { buf := getPacketBuffer() buf.Split() buf.Split() // now we have 3 parts buf.Decrement() buf.Decrement() buf.Decrement() Expect(func() { buf.Decrement() }).To(Panic()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/client.go000066400000000000000000000161331465664453100224200ustar00rootroot00000000000000package quic import ( "context" "crypto/tls" "errors" "net" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) type client struct { sendConn sendConn use0RTT bool packetHandlers packetHandlerManager onClose func() tlsConf *tls.Config config *Config connIDGenerator ConnectionIDGenerator srcConnID protocol.ConnectionID destConnID protocol.ConnectionID initialPacketNumber protocol.PacketNumber hasNegotiatedVersion bool version protocol.Version handshakeChan chan struct{} conn quicConn tracer *logging.ConnectionTracer tracingID ConnectionTracingID logger utils.Logger } // make it possible to mock connection ID for initial generation in the tests var generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial // DialAddr establishes a new QUIC connection to a server. // It resolves the address, and then creates a new UDP connection to dial the QUIC server. // When the QUIC connection is closed, this UDP connection is closed. // See Dial for more details. func DialAddr(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (Connection, error) { udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err } udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err } tr, err := setupTransport(udpConn, tlsConf, true) if err != nil { return nil, err } return tr.dial(ctx, udpAddr, addr, tlsConf, conf, false) } // DialAddrEarly establishes a new 0-RTT QUIC connection to a server. // See DialAddr for more details. func DialAddrEarly(ctx context.Context, addr string, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err } udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err } tr, err := setupTransport(udpConn, tlsConf, true) if err != nil { return nil, err } conn, err := tr.dial(ctx, udpAddr, addr, tlsConf, conf, true) if err != nil { tr.Close() return nil, err } return conn, nil } // DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn. // See Dial for more details. func DialEarly(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (EarlyConnection, error) { dl, err := setupTransport(c, tlsConf, false) if err != nil { return nil, err } conn, err := dl.DialEarly(ctx, addr, tlsConf, conf) if err != nil { dl.Close() return nil, err } return conn, nil } // Dial establishes a new QUIC connection to a server using a net.PacketConn. // If the PacketConn satisfies the OOBCapablePacketConn interface (as a net.UDPConn does), // ECN and packet info support will be enabled. In this case, ReadMsgUDP and WriteMsgUDP // will be used instead of ReadFrom and WriteTo to read/write packets. // The tls.Config must define an application protocol (using NextProtos). // // This is a convenience function. More advanced use cases should instantiate a Transport, // which offers configuration options for a more fine-grained control of the connection establishment, // including reusing the underlying UDP socket for multiple QUIC connections. func Dial(ctx context.Context, c net.PacketConn, addr net.Addr, tlsConf *tls.Config, conf *Config) (Connection, error) { dl, err := setupTransport(c, tlsConf, false) if err != nil { return nil, err } conn, err := dl.Dial(ctx, addr, tlsConf, conf) if err != nil { dl.Close() return nil, err } return conn, nil } func setupTransport(c net.PacketConn, tlsConf *tls.Config, createdPacketConn bool) (*Transport, error) { if tlsConf == nil { return nil, errors.New("quic: tls.Config not set") } return &Transport{ Conn: c, createdConn: createdPacketConn, isSingleUse: true, }, nil } func dial( ctx context.Context, conn sendConn, connIDGenerator ConnectionIDGenerator, packetHandlers packetHandlerManager, tlsConf *tls.Config, config *Config, onClose func(), use0RTT bool, ) (quicConn, error) { c, err := newClient(conn, connIDGenerator, config, tlsConf, onClose, use0RTT) if err != nil { return nil, err } c.packetHandlers = packetHandlers c.tracingID = nextConnTracingID() if c.config.Tracer != nil { c.tracer = c.config.Tracer(context.WithValue(ctx, ConnectionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID) } if c.tracer != nil && c.tracer.StartedConnection != nil { c.tracer.StartedConnection(c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID) } if err := c.dial(ctx); err != nil { return nil, err } return c.conn, nil } func newClient(sendConn sendConn, connIDGenerator ConnectionIDGenerator, config *Config, tlsConf *tls.Config, onClose func(), use0RTT bool) (*client, error) { srcConnID, err := connIDGenerator.GenerateConnectionID() if err != nil { return nil, err } destConnID, err := generateConnectionIDForInitial() if err != nil { return nil, err } c := &client{ connIDGenerator: connIDGenerator, srcConnID: srcConnID, destConnID: destConnID, sendConn: sendConn, use0RTT: use0RTT, onClose: onClose, tlsConf: tlsConf, config: config, version: config.Versions[0], handshakeChan: make(chan struct{}), logger: utils.DefaultLogger.WithPrefix("client"), } return c, nil } func (c *client) dial(ctx context.Context) error { c.logger.Infof("Starting new connection to %s (%s -> %s), source connection ID %s, destination connection ID %s, version %s", c.tlsConf.ServerName, c.sendConn.LocalAddr(), c.sendConn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) c.conn = newClientConnection( context.WithValue(context.WithoutCancel(ctx), ConnectionTracingKey, c.tracingID), c.sendConn, c.packetHandlers, c.destConnID, c.srcConnID, c.connIDGenerator, c.config, c.tlsConf, c.initialPacketNumber, c.use0RTT, c.hasNegotiatedVersion, c.tracer, c.logger, c.version, ) c.packetHandlers.Add(c.srcConnID, c.conn) errorChan := make(chan error, 1) recreateChan := make(chan errCloseForRecreating) go func() { err := c.conn.run() var recreateErr *errCloseForRecreating if errors.As(err, &recreateErr) { recreateChan <- *recreateErr return } if c.onClose != nil { c.onClose() } errorChan <- err // returns as soon as the connection is closed }() // only set when we're using 0-RTT // Otherwise, earlyConnChan will be nil. Receiving from a nil chan blocks forever. var earlyConnChan <-chan struct{} if c.use0RTT { earlyConnChan = c.conn.earlyConnReady() } select { case <-ctx.Done(): c.conn.destroy(nil) return context.Cause(ctx) case err := <-errorChan: return err case recreateErr := <-recreateChan: c.initialPacketNumber = recreateErr.nextPacketNumber c.version = recreateErr.nextVersion c.hasNegotiatedVersion = true return c.dial(ctx) case <-earlyConnChan: // ready to send 0-RTT data return nil case <-c.conn.HandshakeComplete(): // handshake successfully completed return nil } } golang-github-lucas-clemente-quic-go-0.46.0/client_test.go000066400000000000000000000256171465664453100234660ustar00rootroot00000000000000package quic import ( "context" "crypto/tls" "errors" "net" "time" mocklogging "github.com/quic-go/quic-go/internal/mocks/logging" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) type nullMultiplexer struct{} func (n nullMultiplexer) AddConn(indexableConn) {} func (n nullMultiplexer) RemoveConn(indexableConn) error { return nil } var _ = Describe("Client", func() { var ( cl *client packetConn *MockSendConn connID protocol.ConnectionID origMultiplexer multiplexer tlsConf *tls.Config tracer *mocklogging.MockConnectionTracer config *Config originalClientConnConstructor func( ctx context.Context, conn sendConn, runner connRunner, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, connIDGenerator ConnectionIDGenerator, conf *Config, tlsConf *tls.Config, initialPacketNumber protocol.PacketNumber, enable0RTT bool, hasNegotiatedVersion bool, tracer *logging.ConnectionTracer, logger utils.Logger, v protocol.Version, ) quicConn ) BeforeEach(func() { tlsConf = &tls.Config{NextProtos: []string{"proto1"}} connID = protocol.ParseConnectionID([]byte{0, 0, 0, 0, 0, 0, 0x13, 0x37}) originalClientConnConstructor = newClientConnection var tr *logging.ConnectionTracer tr, tracer = mocklogging.NewMockConnectionTracer(mockCtrl) config = &Config{ Tracer: func(ctx context.Context, perspective logging.Perspective, id ConnectionID) *logging.ConnectionTracer { return tr }, Versions: []protocol.Version{protocol.Version1}, } Eventually(areConnsRunning).Should(BeFalse()) packetConn = NewMockSendConn(mockCtrl) packetConn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes() packetConn.EXPECT().RemoteAddr().Return(&net.UDPAddr{}).AnyTimes() cl = &client{ srcConnID: connID, destConnID: connID, version: protocol.Version1, sendConn: packetConn, tracer: tr, logger: utils.DefaultLogger, } getMultiplexer() // make the sync.Once execute // replace the clientMuxer. getMultiplexer will now return the nullMultiplexer origMultiplexer = connMuxer connMuxer = &nullMultiplexer{} }) AfterEach(func() { connMuxer = origMultiplexer newClientConnection = originalClientConnConstructor }) AfterEach(func() { if s, ok := cl.conn.(*connection); ok { s.destroy(nil) } Eventually(areConnsRunning).Should(BeFalse()) }) Context("Dialing", func() { var origGenerateConnectionIDForInitial func() (protocol.ConnectionID, error) BeforeEach(func() { origGenerateConnectionIDForInitial = generateConnectionIDForInitial generateConnectionIDForInitial = func() (protocol.ConnectionID, error) { return connID, nil } }) AfterEach(func() { generateConnectionIDForInitial = origGenerateConnectionIDForInitial }) It("returns after the handshake is complete", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) run := make(chan struct{}) newClientConnection = func( _ context.Context, _ sendConn, _ connRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ ConnectionIDGenerator, _ *Config, _ *tls.Config, _ protocol.PacketNumber, enable0RTT bool, _ bool, _ *logging.ConnectionTracer, _ utils.Logger, _ protocol.Version, ) quicConn { Expect(enable0RTT).To(BeFalse()) conn := NewMockQUICConn(mockCtrl) conn.EXPECT().run().Do(func() error { close(run); return nil }) c := make(chan struct{}) close(c) conn.EXPECT().HandshakeComplete().Return(c) return conn } cl, err := newClient(packetConn, &protocol.DefaultConnectionIDGenerator{}, populateConfig(config), tlsConf, nil, false) Expect(err).ToNot(HaveOccurred()) cl.packetHandlers = manager Expect(cl).ToNot(BeNil()) Expect(cl.dial(context.Background())).To(Succeed()) Eventually(run).Should(BeClosed()) }) It("returns early connections", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) readyChan := make(chan struct{}) done := make(chan struct{}) newClientConnection = func( _ context.Context, _ sendConn, runner connRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ ConnectionIDGenerator, _ *Config, _ *tls.Config, _ protocol.PacketNumber, enable0RTT bool, _ bool, _ *logging.ConnectionTracer, _ utils.Logger, _ protocol.Version, ) quicConn { Expect(enable0RTT).To(BeTrue()) conn := NewMockQUICConn(mockCtrl) conn.EXPECT().run().Do(func() error { close(done); return nil }) conn.EXPECT().HandshakeComplete().Return(make(chan struct{})) conn.EXPECT().earlyConnReady().Return(readyChan) return conn } cl, err := newClient(packetConn, &protocol.DefaultConnectionIDGenerator{}, populateConfig(config), tlsConf, nil, true) Expect(err).ToNot(HaveOccurred()) cl.packetHandlers = manager Expect(cl).ToNot(BeNil()) Expect(cl.dial(context.Background())).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("returns an error that occurs while waiting for the handshake to complete", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) testErr := errors.New("early handshake error") newClientConnection = func( _ context.Context, _ sendConn, _ connRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ ConnectionIDGenerator, _ *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ *logging.ConnectionTracer, _ utils.Logger, _ protocol.Version, ) quicConn { conn := NewMockQUICConn(mockCtrl) conn.EXPECT().run().Return(testErr) conn.EXPECT().HandshakeComplete().Return(make(chan struct{})) conn.EXPECT().earlyConnReady().Return(make(chan struct{})) return conn } var closed bool cl, err := newClient(packetConn, &protocol.DefaultConnectionIDGenerator{}, populateConfig(config), tlsConf, func() { closed = true }, true) Expect(err).ToNot(HaveOccurred()) cl.packetHandlers = manager Expect(cl).ToNot(BeNil()) Expect(cl.dial(context.Background())).To(MatchError(testErr)) Expect(closed).To(BeTrue()) }) Context("quic.Config", func() { It("setups with the right values", func() { tokenStore := NewLRUTokenStore(10, 4) config := &Config{ HandshakeIdleTimeout: 1337 * time.Minute, MaxIdleTimeout: 42 * time.Hour, MaxIncomingStreams: 1234, MaxIncomingUniStreams: 4321, TokenStore: tokenStore, EnableDatagrams: true, } c := populateConfig(config) Expect(c.HandshakeIdleTimeout).To(Equal(1337 * time.Minute)) Expect(c.MaxIdleTimeout).To(Equal(42 * time.Hour)) Expect(c.MaxIncomingStreams).To(BeEquivalentTo(1234)) Expect(c.MaxIncomingUniStreams).To(BeEquivalentTo(4321)) Expect(c.TokenStore).To(Equal(tokenStore)) Expect(c.EnableDatagrams).To(BeTrue()) }) It("disables bidirectional streams", func() { config := &Config{ MaxIncomingStreams: -1, MaxIncomingUniStreams: 4321, } c := populateConfig(config) Expect(c.MaxIncomingStreams).To(BeZero()) Expect(c.MaxIncomingUniStreams).To(BeEquivalentTo(4321)) }) It("disables unidirectional streams", func() { config := &Config{ MaxIncomingStreams: 1234, MaxIncomingUniStreams: -1, } c := populateConfig(config) Expect(c.MaxIncomingStreams).To(BeEquivalentTo(1234)) Expect(c.MaxIncomingUniStreams).To(BeZero()) }) It("fills in default values if options are not set in the Config", func() { c := populateConfig(&Config{}) Expect(c.Versions).To(Equal(protocol.SupportedVersions)) Expect(c.HandshakeIdleTimeout).To(Equal(protocol.DefaultHandshakeIdleTimeout)) Expect(c.MaxIdleTimeout).To(Equal(protocol.DefaultIdleTimeout)) }) }) It("creates new connections with the right parameters", func() { config := &Config{Versions: []protocol.Version{protocol.Version1}} c := make(chan struct{}) var version protocol.Version var conf *Config done := make(chan struct{}) newClientConnection = func( _ context.Context, connP sendConn, _ connRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ ConnectionIDGenerator, configP *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ *logging.ConnectionTracer, _ utils.Logger, versionP protocol.Version, ) quicConn { version = versionP conf = configP close(c) // TODO: check connection IDs? conn := NewMockQUICConn(mockCtrl) conn.EXPECT().run() conn.EXPECT().HandshakeComplete().Return(make(chan struct{})) conn.EXPECT().destroy(gomock.Any()).MaxTimes(1) close(done) return conn } packetConn := NewMockPacketConn(mockCtrl) packetConn.EXPECT().ReadFrom(gomock.Any()).DoAndReturn(func([]byte) (int, net.Addr, error) { <-done return 0, nil, errors.New("closed") }) packetConn.EXPECT().LocalAddr() packetConn.EXPECT().SetReadDeadline(gomock.Any()).AnyTimes() _, err := Dial(context.Background(), packetConn, &net.UDPAddr{}, tlsConf, config) Expect(err).ToNot(HaveOccurred()) Eventually(c).Should(BeClosed()) Expect(version).To(Equal(config.Versions[0])) Expect(conf.Versions).To(Equal(config.Versions)) }) It("creates a new connections after version negotiation", func() { var counter int newClientConnection = func( _ context.Context, _ sendConn, runner connRunner, _ protocol.ConnectionID, connID protocol.ConnectionID, _ ConnectionIDGenerator, configP *Config, _ *tls.Config, pn protocol.PacketNumber, _ bool, hasNegotiatedVersion bool, _ *logging.ConnectionTracer, _ utils.Logger, versionP protocol.Version, ) quicConn { conn := NewMockQUICConn(mockCtrl) conn.EXPECT().HandshakeComplete().Return(make(chan struct{})) if counter == 0 { Expect(pn).To(BeZero()) Expect(hasNegotiatedVersion).To(BeFalse()) conn.EXPECT().run().DoAndReturn(func() error { runner.Remove(connID) return &errCloseForRecreating{ nextPacketNumber: 109, nextVersion: 789, } }) } else { Expect(pn).To(Equal(protocol.PacketNumber(109))) Expect(hasNegotiatedVersion).To(BeTrue()) conn.EXPECT().run() conn.EXPECT().destroy(gomock.Any()) } counter++ return conn } config := &Config{Tracer: config.Tracer, Versions: []protocol.Version{protocol.Version1}} tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) _, err := DialAddr(context.Background(), "localhost:7890", tlsConf, config) Expect(err).ToNot(HaveOccurred()) Expect(counter).To(Equal(2)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/closed_conn.go000066400000000000000000000034101465664453100234220ustar00rootroot00000000000000package quic import ( "math/bits" "net" "github.com/quic-go/quic-go/internal/utils" ) // A closedLocalConn is a connection that we closed locally. // When receiving packets for such a connection, we need to retransmit the packet containing the CONNECTION_CLOSE frame, // with an exponential backoff. type closedLocalConn struct { counter uint32 logger utils.Logger sendPacket func(net.Addr, packetInfo) } var _ packetHandler = &closedLocalConn{} // newClosedLocalConn creates a new closedLocalConn and runs it. func newClosedLocalConn(sendPacket func(net.Addr, packetInfo), logger utils.Logger) packetHandler { return &closedLocalConn{ sendPacket: sendPacket, logger: logger, } } func (c *closedLocalConn) handlePacket(p receivedPacket) { c.counter++ // exponential backoff // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving if bits.OnesCount32(c.counter) != 1 { return } c.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", c.counter) c.sendPacket(p.remoteAddr, p.info) } func (c *closedLocalConn) destroy(error) {} func (c *closedLocalConn) closeWithTransportError(TransportErrorCode) {} // A closedRemoteConn is a connection that was closed remotely. // For such a connection, we might receive reordered packets that were sent before the CONNECTION_CLOSE. // We can just ignore those packets. type closedRemoteConn struct{} var _ packetHandler = &closedRemoteConn{} func newClosedRemoteConn() packetHandler { return &closedRemoteConn{} } func (c *closedRemoteConn) handlePacket(receivedPacket) {} func (c *closedRemoteConn) destroy(error) {} func (c *closedRemoteConn) closeWithTransportError(TransportErrorCode) {} golang-github-lucas-clemente-quic-go-0.46.0/closed_conn_test.go000066400000000000000000000013571465664453100244710ustar00rootroot00000000000000package quic import ( "net" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Closed local connection", func() { It("repeats the packet containing the CONNECTION_CLOSE frame", func() { written := make(chan net.Addr, 1) conn := newClosedLocalConn(func(addr net.Addr, _ packetInfo) { written <- addr }, utils.DefaultLogger) addr := &net.UDPAddr{IP: net.IPv4(127, 1, 2, 3), Port: 1337} for i := 1; i <= 20; i++ { conn.handlePacket(receivedPacket{remoteAddr: addr}) if i == 1 || i == 2 || i == 4 || i == 8 || i == 16 { Expect(written).To(Receive(Equal(addr))) // receive the CONNECTION_CLOSE } else { Expect(written).ToNot(Receive()) } } }) }) golang-github-lucas-clemente-quic-go-0.46.0/codecov.yml000066400000000000000000000004731465664453100227600ustar00rootroot00000000000000coverage: round: nearest ignore: - http3/gzip_reader.go - interop/ - internal/handshake/cipher_suite.go - internal/utils/linkedlist/linkedlist.go - internal/testdata - testutils/ - fuzzing/ - metrics/ status: project: default: threshold: 0.5 patch: false golang-github-lucas-clemente-quic-go-0.46.0/config.go000066400000000000000000000103101465664453100223760ustar00rootroot00000000000000package quic import ( "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // Clone clones a Config func (c *Config) Clone() *Config { copy := *c return © } func (c *Config) handshakeTimeout() time.Duration { return 2 * c.HandshakeIdleTimeout } func (c *Config) maxRetryTokenAge() time.Duration { return c.handshakeTimeout() } func validateConfig(config *Config) error { if config == nil { return nil } const maxStreams = 1 << 60 if config.MaxIncomingStreams > maxStreams { config.MaxIncomingStreams = maxStreams } if config.MaxIncomingUniStreams > maxStreams { config.MaxIncomingUniStreams = maxStreams } if config.MaxStreamReceiveWindow > quicvarint.Max { config.MaxStreamReceiveWindow = quicvarint.Max } if config.MaxConnectionReceiveWindow > quicvarint.Max { config.MaxConnectionReceiveWindow = quicvarint.Max } if config.InitialPacketSize > 0 && config.InitialPacketSize < protocol.MinInitialPacketSize { config.InitialPacketSize = protocol.MinInitialPacketSize } if config.InitialPacketSize > protocol.MaxPacketBufferSize { config.InitialPacketSize = protocol.MaxPacketBufferSize } // check that all QUIC versions are actually supported for _, v := range config.Versions { if !protocol.IsValidVersion(v) { return fmt.Errorf("invalid QUIC version: %s", v) } } return nil } // populateConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil func populateConfig(config *Config) *Config { if config == nil { config = &Config{} } versions := config.Versions if len(versions) == 0 { versions = protocol.SupportedVersions } handshakeIdleTimeout := protocol.DefaultHandshakeIdleTimeout if config.HandshakeIdleTimeout != 0 { handshakeIdleTimeout = config.HandshakeIdleTimeout } idleTimeout := protocol.DefaultIdleTimeout if config.MaxIdleTimeout != 0 { idleTimeout = config.MaxIdleTimeout } initialStreamReceiveWindow := config.InitialStreamReceiveWindow if initialStreamReceiveWindow == 0 { initialStreamReceiveWindow = protocol.DefaultInitialMaxStreamData } maxStreamReceiveWindow := config.MaxStreamReceiveWindow if maxStreamReceiveWindow == 0 { maxStreamReceiveWindow = protocol.DefaultMaxReceiveStreamFlowControlWindow } initialConnectionReceiveWindow := config.InitialConnectionReceiveWindow if initialConnectionReceiveWindow == 0 { initialConnectionReceiveWindow = protocol.DefaultInitialMaxData } maxConnectionReceiveWindow := config.MaxConnectionReceiveWindow if maxConnectionReceiveWindow == 0 { maxConnectionReceiveWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindow } maxIncomingStreams := config.MaxIncomingStreams if maxIncomingStreams == 0 { maxIncomingStreams = protocol.DefaultMaxIncomingStreams } else if maxIncomingStreams < 0 { maxIncomingStreams = 0 } maxIncomingUniStreams := config.MaxIncomingUniStreams if maxIncomingUniStreams == 0 { maxIncomingUniStreams = protocol.DefaultMaxIncomingUniStreams } else if maxIncomingUniStreams < 0 { maxIncomingUniStreams = 0 } initialPacketSize := config.InitialPacketSize if initialPacketSize == 0 { initialPacketSize = protocol.InitialPacketSize } return &Config{ GetConfigForClient: config.GetConfigForClient, Versions: versions, HandshakeIdleTimeout: handshakeIdleTimeout, MaxIdleTimeout: idleTimeout, KeepAlivePeriod: config.KeepAlivePeriod, InitialStreamReceiveWindow: initialStreamReceiveWindow, MaxStreamReceiveWindow: maxStreamReceiveWindow, InitialConnectionReceiveWindow: initialConnectionReceiveWindow, MaxConnectionReceiveWindow: maxConnectionReceiveWindow, AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, MaxIncomingStreams: maxIncomingStreams, MaxIncomingUniStreams: maxIncomingUniStreams, TokenStore: config.TokenStore, EnableDatagrams: config.EnableDatagrams, InitialPacketSize: initialPacketSize, DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, Allow0RTT: config.Allow0RTT, Tracer: config.Tracer, } } golang-github-lucas-clemente-quic-go-0.46.0/config_test.go000066400000000000000000000150601465664453100234440ustar00rootroot00000000000000package quic import ( "context" "errors" "fmt" "reflect" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Config", func() { Context("validating", func() { It("validates a nil config", func() { Expect(validateConfig(nil)).To(Succeed()) }) It("validates a config with normal values", func() { conf := populateConfig(&Config{ MaxIncomingStreams: 5, MaxStreamReceiveWindow: 10, }) Expect(validateConfig(conf)).To(Succeed()) Expect(conf.MaxIncomingStreams).To(BeEquivalentTo(5)) Expect(conf.MaxStreamReceiveWindow).To(BeEquivalentTo(10)) }) It("clips too large values for the stream limits", func() { conf := &Config{ MaxIncomingStreams: 1<<60 + 1, MaxIncomingUniStreams: 1<<60 + 2, } Expect(validateConfig(conf)).To(Succeed()) Expect(conf.MaxIncomingStreams).To(BeEquivalentTo(int64(1 << 60))) Expect(conf.MaxIncomingUniStreams).To(BeEquivalentTo(int64(1 << 60))) }) It("clips too large values for the flow control windows", func() { conf := &Config{ MaxStreamReceiveWindow: quicvarint.Max + 1, MaxConnectionReceiveWindow: quicvarint.Max + 2, } Expect(validateConfig(conf)).To(Succeed()) Expect(conf.MaxStreamReceiveWindow).To(BeEquivalentTo(uint64(quicvarint.Max))) Expect(conf.MaxConnectionReceiveWindow).To(BeEquivalentTo(uint64(quicvarint.Max))) }) It("increases too small packet sizes", func() { conf := &Config{InitialPacketSize: 10} Expect(validateConfig(conf)).To(Succeed()) Expect(conf.InitialPacketSize).To(BeEquivalentTo(1200)) }) It("clips too large packet sizes", func() { conf := &Config{InitialPacketSize: protocol.MaxPacketBufferSize + 1} Expect(validateConfig(conf)).To(Succeed()) Expect(conf.InitialPacketSize).To(BeEquivalentTo(protocol.MaxPacketBufferSize)) }) It("doesn't modify the InitialPacketSize if it is unset", func() { conf := &Config{InitialPacketSize: 0} Expect(validateConfig(conf)).To(Succeed()) Expect(conf.InitialPacketSize).To(BeZero()) }) }) configWithNonZeroNonFunctionFields := func() *Config { c := &Config{} v := reflect.ValueOf(c).Elem() typ := v.Type() for i := 0; i < typ.NumField(); i++ { f := v.Field(i) if !f.CanSet() { // unexported field; not cloned. continue } switch fn := typ.Field(i).Name; fn { case "GetConfigForClient", "RequireAddressValidation", "GetLogWriter", "AllowConnectionWindowIncrease", "Tracer": // Can't compare functions. case "Versions": f.Set(reflect.ValueOf([]Version{1, 2, 3})) case "ConnectionIDLength": f.Set(reflect.ValueOf(8)) case "ConnectionIDGenerator": f.Set(reflect.ValueOf(&protocol.DefaultConnectionIDGenerator{ConnLen: protocol.DefaultConnectionIDLength})) case "HandshakeIdleTimeout": f.Set(reflect.ValueOf(time.Second)) case "MaxIdleTimeout": f.Set(reflect.ValueOf(time.Hour)) case "TokenStore": f.Set(reflect.ValueOf(NewLRUTokenStore(2, 3))) case "InitialStreamReceiveWindow": f.Set(reflect.ValueOf(uint64(1234))) case "MaxStreamReceiveWindow": f.Set(reflect.ValueOf(uint64(9))) case "InitialConnectionReceiveWindow": f.Set(reflect.ValueOf(uint64(4321))) case "MaxConnectionReceiveWindow": f.Set(reflect.ValueOf(uint64(10))) case "MaxIncomingStreams": f.Set(reflect.ValueOf(int64(11))) case "MaxIncomingUniStreams": f.Set(reflect.ValueOf(int64(12))) case "StatelessResetKey": f.Set(reflect.ValueOf(&StatelessResetKey{1, 2, 3, 4})) case "KeepAlivePeriod": f.Set(reflect.ValueOf(time.Second)) case "EnableDatagrams": f.Set(reflect.ValueOf(true)) case "DisableVersionNegotiationPackets": f.Set(reflect.ValueOf(true)) case "InitialPacketSize": f.Set(reflect.ValueOf(uint16(1350))) case "DisablePathMTUDiscovery": f.Set(reflect.ValueOf(true)) case "Allow0RTT": f.Set(reflect.ValueOf(true)) default: Fail(fmt.Sprintf("all fields must be accounted for, but saw unknown field %q", fn)) } } return c } It("uses twice the handshake idle timeouts for the handshake timeout", func() { c := &Config{HandshakeIdleTimeout: time.Second * 11 / 2} Expect(c.handshakeTimeout()).To(Equal(11 * time.Second)) }) Context("cloning", func() { It("clones function fields", func() { var calledAllowConnectionWindowIncrease, calledTracer bool c1 := &Config{ GetConfigForClient: func(info *ClientHelloInfo) (*Config, error) { return nil, errors.New("nope") }, AllowConnectionWindowIncrease: func(Connection, uint64) bool { calledAllowConnectionWindowIncrease = true; return true }, Tracer: func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer { calledTracer = true return nil }, } c2 := c1.Clone() c2.AllowConnectionWindowIncrease(nil, 1234) Expect(calledAllowConnectionWindowIncrease).To(BeTrue()) _, err := c2.GetConfigForClient(&ClientHelloInfo{}) Expect(err).To(MatchError("nope")) c2.Tracer(context.Background(), logging.PerspectiveClient, protocol.ConnectionID{}) Expect(calledTracer).To(BeTrue()) }) It("clones non-function fields", func() { c := configWithNonZeroNonFunctionFields() Expect(c.Clone()).To(Equal(c)) }) It("returns a copy", func() { c1 := &Config{MaxIncomingStreams: 100} c2 := c1.Clone() c2.MaxIncomingStreams = 200 Expect(c1.MaxIncomingStreams).To(BeEquivalentTo(100)) }) }) Context("populating", func() { It("copies non-function fields", func() { c := configWithNonZeroNonFunctionFields() Expect(populateConfig(c)).To(Equal(c)) }) It("populates empty fields with default values", func() { c := populateConfig(&Config{}) Expect(c.Versions).To(Equal(protocol.SupportedVersions)) Expect(c.HandshakeIdleTimeout).To(Equal(protocol.DefaultHandshakeIdleTimeout)) Expect(c.InitialStreamReceiveWindow).To(BeEquivalentTo(protocol.DefaultInitialMaxStreamData)) Expect(c.MaxStreamReceiveWindow).To(BeEquivalentTo(protocol.DefaultMaxReceiveStreamFlowControlWindow)) Expect(c.InitialConnectionReceiveWindow).To(BeEquivalentTo(protocol.DefaultInitialMaxData)) Expect(c.MaxConnectionReceiveWindow).To(BeEquivalentTo(protocol.DefaultMaxReceiveConnectionFlowControlWindow)) Expect(c.MaxIncomingStreams).To(BeEquivalentTo(protocol.DefaultMaxIncomingStreams)) Expect(c.MaxIncomingUniStreams).To(BeEquivalentTo(protocol.DefaultMaxIncomingUniStreams)) Expect(c.DisablePathMTUDiscovery).To(BeFalse()) Expect(c.GetConfigForClient).To(BeNil()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/conn_id_generator.go000066400000000000000000000106131465664453100246160ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" ) type connIDGenerator struct { generator ConnectionIDGenerator highestSeq uint64 activeSrcConnIDs map[uint64]protocol.ConnectionID initialClientDestConnID *protocol.ConnectionID // nil for the client addConnectionID func(protocol.ConnectionID) getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken removeConnectionID func(protocol.ConnectionID) retireConnectionID func(protocol.ConnectionID) replaceWithClosed func([]protocol.ConnectionID, []byte) queueControlFrame func(wire.Frame) } func newConnIDGenerator( initialConnectionID protocol.ConnectionID, initialClientDestConnID *protocol.ConnectionID, // nil for the client addConnectionID func(protocol.ConnectionID), getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken, removeConnectionID func(protocol.ConnectionID), retireConnectionID func(protocol.ConnectionID), replaceWithClosed func([]protocol.ConnectionID, []byte), queueControlFrame func(wire.Frame), generator ConnectionIDGenerator, ) *connIDGenerator { m := &connIDGenerator{ generator: generator, activeSrcConnIDs: make(map[uint64]protocol.ConnectionID), addConnectionID: addConnectionID, getStatelessResetToken: getStatelessResetToken, removeConnectionID: removeConnectionID, retireConnectionID: retireConnectionID, replaceWithClosed: replaceWithClosed, queueControlFrame: queueControlFrame, } m.activeSrcConnIDs[0] = initialConnectionID m.initialClientDestConnID = initialClientDestConnID return m } func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error { if m.generator.ConnectionIDLen() == 0 { return nil } // The active_connection_id_limit transport parameter is the number of // connection IDs the peer will store. This limit includes the connection ID // used during the handshake, and the one sent in the preferred_address // transport parameter. // We currently don't send the preferred_address transport parameter, // so we can issue (limit - 1) connection IDs. for i := uint64(len(m.activeSrcConnIDs)); i < min(limit, protocol.MaxIssuedConnectionIDs); i++ { if err := m.issueNewConnID(); err != nil { return err } } return nil } func (m *connIDGenerator) Retire(seq uint64, sentWithDestConnID protocol.ConnectionID) error { if seq > m.highestSeq { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: fmt.Sprintf("retired connection ID %d (highest issued: %d)", seq, m.highestSeq), } } connID, ok := m.activeSrcConnIDs[seq] // We might already have deleted this connection ID, if this is a duplicate frame. if !ok { return nil } if connID == sentWithDestConnID { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", seq, connID), } } m.retireConnectionID(connID) delete(m.activeSrcConnIDs, seq) // Don't issue a replacement for the initial connection ID. if seq == 0 { return nil } return m.issueNewConnID() } func (m *connIDGenerator) issueNewConnID() error { connID, err := m.generator.GenerateConnectionID() if err != nil { return err } m.activeSrcConnIDs[m.highestSeq+1] = connID m.addConnectionID(connID) m.queueControlFrame(&wire.NewConnectionIDFrame{ SequenceNumber: m.highestSeq + 1, ConnectionID: connID, StatelessResetToken: m.getStatelessResetToken(connID), }) m.highestSeq++ return nil } func (m *connIDGenerator) SetHandshakeComplete() { if m.initialClientDestConnID != nil { m.retireConnectionID(*m.initialClientDestConnID) m.initialClientDestConnID = nil } } func (m *connIDGenerator) RemoveAll() { if m.initialClientDestConnID != nil { m.removeConnectionID(*m.initialClientDestConnID) } for _, connID := range m.activeSrcConnIDs { m.removeConnectionID(connID) } } func (m *connIDGenerator) ReplaceWithClosed(connClose []byte) { connIDs := make([]protocol.ConnectionID, 0, len(m.activeSrcConnIDs)+1) if m.initialClientDestConnID != nil { connIDs = append(connIDs, *m.initialClientDestConnID) } for _, connID := range m.activeSrcConnIDs { connIDs = append(connIDs, connID) } m.replaceWithClosed(connIDs, connClose) } golang-github-lucas-clemente-quic-go-0.46.0/conn_id_generator_test.go000066400000000000000000000163531465664453100256640ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID Generator", func() { var ( addedConnIDs []protocol.ConnectionID retiredConnIDs []protocol.ConnectionID removedConnIDs []protocol.ConnectionID replacedWithClosed []protocol.ConnectionID queuedFrames []wire.Frame g *connIDGenerator ) initialConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7}) initialClientDestConnID := protocol.ParseConnectionID([]byte{0xa, 0xb, 0xc, 0xd, 0xe}) connIDToToken := func(c protocol.ConnectionID) protocol.StatelessResetToken { b := c.Bytes()[0] return protocol.StatelessResetToken{b, b, b, b, b, b, b, b, b, b, b, b, b, b, b, b} } BeforeEach(func() { addedConnIDs = nil retiredConnIDs = nil removedConnIDs = nil queuedFrames = nil replacedWithClosed = nil g = newConnIDGenerator( initialConnID, &initialClientDestConnID, func(c protocol.ConnectionID) { addedConnIDs = append(addedConnIDs, c) }, connIDToToken, func(c protocol.ConnectionID) { removedConnIDs = append(removedConnIDs, c) }, func(c protocol.ConnectionID) { retiredConnIDs = append(retiredConnIDs, c) }, func(cs []protocol.ConnectionID, _ []byte) { replacedWithClosed = append(replacedWithClosed, cs...) }, func(f wire.Frame) { queuedFrames = append(queuedFrames, f) }, &protocol.DefaultConnectionIDGenerator{ConnLen: initialConnID.Len()}, ) }) It("issues new connection IDs", func() { Expect(g.SetMaxActiveConnIDs(4)).To(Succeed()) Expect(retiredConnIDs).To(BeEmpty()) Expect(addedConnIDs).To(HaveLen(3)) for i := 0; i < len(addedConnIDs)-1; i++ { Expect(addedConnIDs[i]).ToNot(Equal(addedConnIDs[i+1])) } Expect(queuedFrames).To(HaveLen(3)) for i := 0; i < 3; i++ { f := queuedFrames[i] Expect(f).To(BeAssignableToTypeOf(&wire.NewConnectionIDFrame{})) nf := f.(*wire.NewConnectionIDFrame) Expect(nf.SequenceNumber).To(BeEquivalentTo(i + 1)) Expect(nf.ConnectionID.Len()).To(Equal(7)) Expect(nf.StatelessResetToken).To(Equal(connIDToToken(nf.ConnectionID))) } }) It("limits the number of connection IDs that it issues", func() { Expect(g.SetMaxActiveConnIDs(9999999)).To(Succeed()) Expect(retiredConnIDs).To(BeEmpty()) Expect(addedConnIDs).To(HaveLen(protocol.MaxIssuedConnectionIDs - 1)) Expect(queuedFrames).To(HaveLen(protocol.MaxIssuedConnectionIDs - 1)) }) // SetMaxActiveConnIDs is called twice when dialing a 0-RTT connection: // once for the restored from the old connections, once when we receive the transport parameters Context("dealing with 0-RTT", func() { It("doesn't issue new connection IDs when SetMaxActiveConnIDs is called with the same value", func() { Expect(g.SetMaxActiveConnIDs(4)).To(Succeed()) Expect(queuedFrames).To(HaveLen(3)) queuedFrames = nil Expect(g.SetMaxActiveConnIDs(4)).To(Succeed()) Expect(queuedFrames).To(BeEmpty()) }) It("issues more connection IDs if the server allows a higher limit on the resumed connection", func() { Expect(g.SetMaxActiveConnIDs(3)).To(Succeed()) Expect(queuedFrames).To(HaveLen(2)) queuedFrames = nil Expect(g.SetMaxActiveConnIDs(6)).To(Succeed()) Expect(queuedFrames).To(HaveLen(3)) }) It("issues more connection IDs if the server allows a higher limit on the resumed connection, when connection IDs were retired in between", func() { Expect(g.SetMaxActiveConnIDs(3)).To(Succeed()) Expect(queuedFrames).To(HaveLen(2)) queuedFrames = nil g.Retire(1, protocol.ConnectionID{}) Expect(queuedFrames).To(HaveLen(1)) queuedFrames = nil Expect(g.SetMaxActiveConnIDs(6)).To(Succeed()) Expect(queuedFrames).To(HaveLen(3)) }) }) It("errors if the peers tries to retire a connection ID that wasn't yet issued", func() { Expect(g.Retire(1, protocol.ConnectionID{})).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "retired connection ID 1 (highest issued: 0)", })) }) It("errors if the peers tries to retire a connection ID in a packet with that connection ID", func() { Expect(g.SetMaxActiveConnIDs(4)).To(Succeed()) Expect(queuedFrames).ToNot(BeEmpty()) Expect(queuedFrames[0]).To(BeAssignableToTypeOf(&wire.NewConnectionIDFrame{})) f := queuedFrames[0].(*wire.NewConnectionIDFrame) Expect(g.Retire(f.SequenceNumber, f.ConnectionID)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: fmt.Sprintf("retired connection ID %d (%s), which was used as the Destination Connection ID on this packet", f.SequenceNumber, f.ConnectionID), })) }) It("issues new connection IDs, when old ones are retired", func() { Expect(g.SetMaxActiveConnIDs(5)).To(Succeed()) queuedFrames = nil Expect(retiredConnIDs).To(BeEmpty()) Expect(g.Retire(3, protocol.ConnectionID{})).To(Succeed()) Expect(queuedFrames).To(HaveLen(1)) Expect(queuedFrames[0]).To(BeAssignableToTypeOf(&wire.NewConnectionIDFrame{})) nf := queuedFrames[0].(*wire.NewConnectionIDFrame) Expect(nf.SequenceNumber).To(BeEquivalentTo(5)) Expect(nf.ConnectionID.Len()).To(Equal(7)) }) It("retires the initial connection ID", func() { Expect(g.Retire(0, protocol.ConnectionID{})).To(Succeed()) Expect(removedConnIDs).To(BeEmpty()) Expect(retiredConnIDs).To(HaveLen(1)) Expect(retiredConnIDs[0]).To(Equal(initialConnID)) Expect(addedConnIDs).To(BeEmpty()) }) It("handles duplicate retirements", func() { Expect(g.SetMaxActiveConnIDs(11)).To(Succeed()) queuedFrames = nil Expect(retiredConnIDs).To(BeEmpty()) Expect(g.Retire(5, protocol.ConnectionID{})).To(Succeed()) Expect(retiredConnIDs).To(HaveLen(1)) Expect(queuedFrames).To(HaveLen(1)) Expect(g.Retire(5, protocol.ConnectionID{})).To(Succeed()) Expect(retiredConnIDs).To(HaveLen(1)) Expect(queuedFrames).To(HaveLen(1)) }) It("retires the client's initial destination connection ID when the handshake completes", func() { g.SetHandshakeComplete() Expect(retiredConnIDs).To(HaveLen(1)) Expect(retiredConnIDs[0]).To(Equal(initialClientDestConnID)) }) It("removes all connection IDs", func() { Expect(g.SetMaxActiveConnIDs(5)).To(Succeed()) Expect(queuedFrames).To(HaveLen(4)) g.RemoveAll() Expect(removedConnIDs).To(HaveLen(6)) // initial conn ID, initial client dest conn id, and newly issued ones Expect(removedConnIDs).To(ContainElement(initialConnID)) Expect(removedConnIDs).To(ContainElement(initialClientDestConnID)) for _, f := range queuedFrames { nf := f.(*wire.NewConnectionIDFrame) Expect(removedConnIDs).To(ContainElement(nf.ConnectionID)) } }) It("replaces with a closed connection for all connection IDs", func() { Expect(g.SetMaxActiveConnIDs(5)).To(Succeed()) Expect(queuedFrames).To(HaveLen(4)) g.ReplaceWithClosed([]byte("foobar")) Expect(replacedWithClosed).To(HaveLen(6)) // initial conn ID, initial client dest conn id, and newly issued ones Expect(replacedWithClosed).To(ContainElement(initialClientDestConnID)) Expect(replacedWithClosed).To(ContainElement(initialConnID)) for _, f := range queuedFrames { nf := f.(*wire.NewConnectionIDFrame) Expect(replacedWithClosed).To(ContainElement(nf.ConnectionID)) } }) }) golang-github-lucas-clemente-quic-go-0.46.0/conn_id_manager.go000066400000000000000000000152241465664453100242450ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" list "github.com/quic-go/quic-go/internal/utils/linkedlist" "github.com/quic-go/quic-go/internal/wire" ) type newConnID struct { SequenceNumber uint64 ConnectionID protocol.ConnectionID StatelessResetToken protocol.StatelessResetToken } type connIDManager struct { queue list.List[newConnID] handshakeComplete bool activeSequenceNumber uint64 highestRetired uint64 activeConnectionID protocol.ConnectionID activeStatelessResetToken *protocol.StatelessResetToken // We change the connection ID after sending on average // protocol.PacketsPerConnectionID packets. The actual value is randomized // hide the packet loss rate from on-path observers. rand utils.Rand packetsSinceLastChange uint32 packetsPerConnectionID uint32 addStatelessResetToken func(protocol.StatelessResetToken) removeStatelessResetToken func(protocol.StatelessResetToken) queueControlFrame func(wire.Frame) } func newConnIDManager( initialDestConnID protocol.ConnectionID, addStatelessResetToken func(protocol.StatelessResetToken), removeStatelessResetToken func(protocol.StatelessResetToken), queueControlFrame func(wire.Frame), ) *connIDManager { return &connIDManager{ activeConnectionID: initialDestConnID, addStatelessResetToken: addStatelessResetToken, removeStatelessResetToken: removeStatelessResetToken, queueControlFrame: queueControlFrame, } } func (h *connIDManager) AddFromPreferredAddress(connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error { return h.addConnectionID(1, connID, resetToken) } func (h *connIDManager) Add(f *wire.NewConnectionIDFrame) error { if err := h.add(f); err != nil { return err } if h.queue.Len() >= protocol.MaxActiveConnectionIDs { return &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError} } return nil } func (h *connIDManager) add(f *wire.NewConnectionIDFrame) error { // If the NEW_CONNECTION_ID frame is reordered, such that its sequence number is smaller than the currently active // connection ID or if it was already retired, send the RETIRE_CONNECTION_ID frame immediately. if f.SequenceNumber < h.activeSequenceNumber || f.SequenceNumber < h.highestRetired { h.queueControlFrame(&wire.RetireConnectionIDFrame{ SequenceNumber: f.SequenceNumber, }) return nil } // Retire elements in the queue. // Doesn't retire the active connection ID. if f.RetirePriorTo > h.highestRetired { var next *list.Element[newConnID] for el := h.queue.Front(); el != nil; el = next { if el.Value.SequenceNumber >= f.RetirePriorTo { break } next = el.Next() h.queueControlFrame(&wire.RetireConnectionIDFrame{ SequenceNumber: el.Value.SequenceNumber, }) h.queue.Remove(el) } h.highestRetired = f.RetirePriorTo } if f.SequenceNumber == h.activeSequenceNumber { return nil } if err := h.addConnectionID(f.SequenceNumber, f.ConnectionID, f.StatelessResetToken); err != nil { return err } // Retire the active connection ID, if necessary. if h.activeSequenceNumber < f.RetirePriorTo { // The queue is guaranteed to have at least one element at this point. h.updateConnectionID() } return nil } func (h *connIDManager) addConnectionID(seq uint64, connID protocol.ConnectionID, resetToken protocol.StatelessResetToken) error { // insert a new element at the end if h.queue.Len() == 0 || h.queue.Back().Value.SequenceNumber < seq { h.queue.PushBack(newConnID{ SequenceNumber: seq, ConnectionID: connID, StatelessResetToken: resetToken, }) return nil } // insert a new element somewhere in the middle for el := h.queue.Front(); el != nil; el = el.Next() { if el.Value.SequenceNumber == seq { if el.Value.ConnectionID != connID { return fmt.Errorf("received conflicting connection IDs for sequence number %d", seq) } if el.Value.StatelessResetToken != resetToken { return fmt.Errorf("received conflicting stateless reset tokens for sequence number %d", seq) } break } if el.Value.SequenceNumber > seq { h.queue.InsertBefore(newConnID{ SequenceNumber: seq, ConnectionID: connID, StatelessResetToken: resetToken, }, el) break } } return nil } func (h *connIDManager) updateConnectionID() { h.queueControlFrame(&wire.RetireConnectionIDFrame{ SequenceNumber: h.activeSequenceNumber, }) h.highestRetired = max(h.highestRetired, h.activeSequenceNumber) if h.activeStatelessResetToken != nil { h.removeStatelessResetToken(*h.activeStatelessResetToken) } front := h.queue.Remove(h.queue.Front()) h.activeSequenceNumber = front.SequenceNumber h.activeConnectionID = front.ConnectionID h.activeStatelessResetToken = &front.StatelessResetToken h.packetsSinceLastChange = 0 h.packetsPerConnectionID = protocol.PacketsPerConnectionID/2 + uint32(h.rand.Int31n(protocol.PacketsPerConnectionID)) h.addStatelessResetToken(*h.activeStatelessResetToken) } func (h *connIDManager) Close() { if h.activeStatelessResetToken != nil { h.removeStatelessResetToken(*h.activeStatelessResetToken) } } // is called when the server performs a Retry // and when the server changes the connection ID in the first Initial sent func (h *connIDManager) ChangeInitialConnID(newConnID protocol.ConnectionID) { if h.activeSequenceNumber != 0 { panic("expected first connection ID to have sequence number 0") } h.activeConnectionID = newConnID } // is called when the server provides a stateless reset token in the transport parameters func (h *connIDManager) SetStatelessResetToken(token protocol.StatelessResetToken) { if h.activeSequenceNumber != 0 { panic("expected first connection ID to have sequence number 0") } h.activeStatelessResetToken = &token h.addStatelessResetToken(token) } func (h *connIDManager) SentPacket() { h.packetsSinceLastChange++ } func (h *connIDManager) shouldUpdateConnID() bool { if !h.handshakeComplete { return false } // initiate the first change as early as possible (after handshake completion) if h.queue.Len() > 0 && h.activeSequenceNumber == 0 { return true } // For later changes, only change if // 1. The queue of connection IDs is filled more than 50%. // 2. We sent at least PacketsPerConnectionID packets return 2*h.queue.Len() >= protocol.MaxActiveConnectionIDs && h.packetsSinceLastChange >= h.packetsPerConnectionID } func (h *connIDManager) Get() protocol.ConnectionID { if h.shouldUpdateConnID() { h.updateConnectionID() } return h.activeConnectionID } func (h *connIDManager) SetHandshakeComplete() { h.handshakeComplete = true } golang-github-lucas-clemente-quic-go-0.46.0/conn_id_manager_test.go000066400000000000000000000351111465664453100253010ustar00rootroot00000000000000package quic import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID Manager", func() { var ( m *connIDManager frameQueue []wire.Frame tokenAdded *protocol.StatelessResetToken removedTokens []protocol.StatelessResetToken ) initialConnID := protocol.ParseConnectionID([]byte{0, 0, 0, 0}) BeforeEach(func() { frameQueue = nil tokenAdded = nil removedTokens = nil m = newConnIDManager( initialConnID, func(token protocol.StatelessResetToken) { tokenAdded = &token }, func(token protocol.StatelessResetToken) { removedTokens = append(removedTokens, token) }, func(f wire.Frame, ) { frameQueue = append(frameQueue, f) }) }) get := func() (protocol.ConnectionID, protocol.StatelessResetToken) { if m.queue.Len() == 0 { return protocol.ConnectionID{}, protocol.StatelessResetToken{} } val := m.queue.Remove(m.queue.Front()) return val.ConnectionID, val.StatelessResetToken } It("returns the initial connection ID", func() { Expect(m.Get()).To(Equal(initialConnID)) }) It("changes the initial connection ID", func() { m.ChangeInitialConnID(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5})) Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5}))) }) It("sets the token for the first connection ID", func() { token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} m.SetStatelessResetToken(token) Expect(*m.activeStatelessResetToken).To(Equal(token)) Expect(*tokenAdded).To(Equal(token)) }) It("adds and gets connection IDs", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: protocol.ParseConnectionID([]byte{2, 3, 4, 5}), StatelessResetToken: protocol.StatelessResetToken{0xe, 0xd, 0xc, 0xb, 0xa, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, })).To(Succeed()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 4, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}, })).To(Succeed()) c1, rt1 := get() Expect(c1).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) Expect(rt1).To(Equal(protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe})) c2, rt2 := get() Expect(c2).To(Equal(protocol.ParseConnectionID([]byte{2, 3, 4, 5}))) Expect(rt2).To(Equal(protocol.StatelessResetToken{0xe, 0xd, 0xc, 0xb, 0xa, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0})) c3, _ := get() Expect(c3).To(BeZero()) }) It("accepts duplicates", func() { f1 := &wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}, } f2 := &wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}, } Expect(m.Add(f1)).To(Succeed()) Expect(m.Add(f2)).To(Succeed()) c1, rt1 := get() Expect(c1).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) Expect(rt1).To(Equal(protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe})) c2, _ := get() Expect(c2).To(BeZero()) }) It("ignores duplicates for the currently used connection ID", func() { f := &wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}, } m.SetHandshakeComplete() Expect(m.Add(f)).To(Succeed()) Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) c, _ := get() Expect(c).To(BeZero()) // Now send the same connection ID again. It should not be queued. Expect(m.Add(f)).To(Succeed()) c, _ = get() Expect(c).To(BeZero()) }) It("rejects duplicates with different connection IDs", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 42, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), })).To(Succeed()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 42, ConnectionID: protocol.ParseConnectionID([]byte{2, 3, 4, 5}), })).To(MatchError("received conflicting connection IDs for sequence number 42")) }) It("rejects duplicates with different connection IDs", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 42, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe}, })).To(Succeed()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 42, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{0xe, 0xd, 0xc, 0xb, 0xa, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, })).To(MatchError("received conflicting stateless reset tokens for sequence number 42")) }) It("retires connection IDs", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), })).To(Succeed()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 13, ConnectionID: protocol.ParseConnectionID([]byte{2, 3, 4, 5}), })).To(Succeed()) Expect(frameQueue).To(BeEmpty()) Expect(m.Add(&wire.NewConnectionIDFrame{ RetirePriorTo: 14, SequenceNumber: 17, ConnectionID: protocol.ParseConnectionID([]byte{3, 4, 5, 6}), })).To(Succeed()) Expect(frameQueue).To(HaveLen(3)) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(10)) Expect(frameQueue[1].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(13)) Expect(frameQueue[2].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero()) Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{3, 4, 5, 6}))) }) It("ignores reordered connection IDs, if their sequence number was already retired", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), RetirePriorTo: 5, })).To(Succeed()) Expect(frameQueue).To(HaveLen(1)) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero()) frameQueue = nil // If this NEW_CONNECTION_ID frame hadn't been reordered, we would have retired it before. // Make sure it gets retired immediately now. Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 4, ConnectionID: protocol.ParseConnectionID([]byte{4, 3, 2, 1}), })).To(Succeed()) Expect(frameQueue).To(HaveLen(1)) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(4)) }) It("ignores reordered connection IDs, if their sequence number was already retired or less than active", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), RetirePriorTo: 5, })).To(Succeed()) Expect(frameQueue).To(HaveLen(1)) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero()) frameQueue = nil Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}))) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 9, ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), RetirePriorTo: 5, })).To(Succeed()) Expect(frameQueue).To(HaveLen(1)) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(9)) }) It("accepts retransmissions for the connection ID that is in use", func() { connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: connID, })).To(Succeed()) m.SetHandshakeComplete() Expect(frameQueue).To(BeEmpty()) Expect(m.Get()).To(Equal(connID)) Expect(frameQueue).To(HaveLen(1)) Expect(frameQueue[0]).To(BeAssignableToTypeOf(&wire.RetireConnectionIDFrame{})) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeZero()) frameQueue = nil Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: connID, })).To(Succeed()) Expect(frameQueue).To(BeEmpty()) }) It("errors when the peer sends too connection IDs", func() { for i := uint8(1); i < protocol.MaxActiveConnectionIDs; i++ { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(i), ConnectionID: protocol.ParseConnectionID([]byte{i, i, i, i}), StatelessResetToken: protocol.StatelessResetToken{i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i}, })).To(Succeed()) } Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(9999), ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, })).To(MatchError(&qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError})) }) It("initiates the first connection ID update as soon as possible", func() { Expect(m.Get()).To(Equal(initialConnID)) m.SetHandshakeComplete() Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, })).To(Succeed()) Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) }) It("waits until handshake completion before initiating a connection ID update", func() { Expect(m.Get()).To(Equal(initialConnID)) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, })).To(Succeed()) Expect(m.Get()).To(Equal(initialConnID)) m.SetHandshakeComplete() Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) }) It("initiates subsequent updates when enough packets are sent", func() { var s uint8 for s = uint8(1); s < protocol.MaxActiveConnectionIDs; s++ { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(s), ConnectionID: protocol.ParseConnectionID([]byte{s, s, s, s}), StatelessResetToken: protocol.StatelessResetToken{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s}, })).To(Succeed()) } m.SetHandshakeComplete() lastConnID := m.Get() Expect(lastConnID).To(Equal(protocol.ParseConnectionID([]byte{1, 1, 1, 1}))) var counter int for i := 0; i < 50*protocol.PacketsPerConnectionID; i++ { m.SentPacket() connID := m.Get() if connID != lastConnID { counter++ lastConnID = connID Expect(removedTokens).To(HaveLen(1)) removedTokens = nil Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(s), ConnectionID: protocol.ParseConnectionID([]byte{s, s, s, s}), StatelessResetToken: protocol.StatelessResetToken{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s}, })).To(Succeed()) s++ } } Expect(counter).To(BeNumerically("~", 50, 10)) }) It("retires delayed connection IDs that arrive after a higher connection ID was already retired", func() { for s := uint8(10); s <= 10+protocol.MaxActiveConnectionIDs/2; s++ { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(s), ConnectionID: protocol.ParseConnectionID([]byte{s, s, s, s}), StatelessResetToken: protocol.StatelessResetToken{s, s, s, s, s, s, s, s, s, s, s, s, s, s, s, s}, })).To(Succeed()) } m.SetHandshakeComplete() Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{10, 10, 10, 10}))) for { m.SentPacket() if m.Get() == protocol.ParseConnectionID([]byte{11, 11, 11, 11}) { break } } // The active conn ID is now {11, 11, 11, 11} Expect(m.queue.Front().Value.ConnectionID).To(Equal(protocol.ParseConnectionID([]byte{12, 12, 12, 12}))) // Add a delayed connection ID. It should just be ignored now. frameQueue = nil Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(5), ConnectionID: protocol.ParseConnectionID([]byte{5, 5, 5, 5}), StatelessResetToken: protocol.StatelessResetToken{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}, })).To(Succeed()) Expect(m.queue.Front().Value.ConnectionID).To(Equal(protocol.ParseConnectionID([]byte{12, 12, 12, 12}))) Expect(frameQueue).To(HaveLen(1)) Expect(frameQueue[0].(*wire.RetireConnectionIDFrame).SequenceNumber).To(BeEquivalentTo(5)) }) It("only initiates subsequent updates when enough if enough connection IDs are queued", func() { for i := uint8(1); i <= protocol.MaxActiveConnectionIDs/2; i++ { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(i), ConnectionID: protocol.ParseConnectionID([]byte{i, i, i, i}), StatelessResetToken: protocol.StatelessResetToken{i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i}, })).To(Succeed()) } m.SetHandshakeComplete() Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 1, 1, 1}))) for i := 0; i < 2*protocol.PacketsPerConnectionID; i++ { m.SentPacket() } Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 1, 1, 1}))) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1337, ConnectionID: protocol.ParseConnectionID([]byte{1, 3, 3, 7}), })).To(Succeed()) Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{2, 2, 2, 2}))) Expect(removedTokens).To(HaveLen(1)) Expect(removedTokens[0]).To(Equal(protocol.StatelessResetToken{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1})) }) It("removes the currently active stateless reset token when it is closed", func() { m.Close() Expect(removedTokens).To(BeEmpty()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, })).To(Succeed()) m.SetHandshakeComplete() Expect(m.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) m.Close() Expect(removedTokens).To(HaveLen(1)) Expect(removedTokens[0]).To(Equal(protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1})) }) }) golang-github-lucas-clemente-quic-go-0.46.0/connection.go000066400000000000000000002274531465664453100233120ustar00rootroot00000000000000package quic import ( "bytes" "context" "crypto/tls" "errors" "fmt" "io" "net" "reflect" "sync" "sync/atomic" "time" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/flowcontrol" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" ) type unpacker interface { UnpackLongHeader(hdr *wire.Header, data []byte) (*unpackedPacket, error) UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) } type streamManager interface { GetOrOpenSendStream(protocol.StreamID) (sendStreamI, error) GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) OpenStream() (Stream, error) OpenUniStream() (SendStream, error) OpenStreamSync(context.Context) (Stream, error) OpenUniStreamSync(context.Context) (SendStream, error) AcceptStream(context.Context) (Stream, error) AcceptUniStream(context.Context) (ReceiveStream, error) DeleteStream(protocol.StreamID) error UpdateLimits(*wire.TransportParameters) HandleMaxStreamsFrame(*wire.MaxStreamsFrame) CloseWithError(error) ResetFor0RTT() UseResetMaps() } type cryptoStreamHandler interface { StartHandshake(context.Context) error ChangeConnectionID(protocol.ConnectionID) SetLargest1RTTAcked(protocol.PacketNumber) error SetHandshakeConfirmed() GetSessionTicket() ([]byte, error) NextEvent() handshake.Event DiscardInitialKeys() HandleMessage([]byte, protocol.EncryptionLevel) error io.Closer ConnectionState() handshake.ConnectionState } type receivedPacket struct { buffer *packetBuffer remoteAddr net.Addr rcvTime time.Time data []byte ecn protocol.ECN info packetInfo // only valid if the contained IP address is valid } func (p *receivedPacket) Size() protocol.ByteCount { return protocol.ByteCount(len(p.data)) } func (p *receivedPacket) Clone() *receivedPacket { return &receivedPacket{ remoteAddr: p.remoteAddr, rcvTime: p.rcvTime, data: p.data, buffer: p.buffer, ecn: p.ecn, info: p.info, } } type connRunner interface { Add(protocol.ConnectionID, packetHandler) bool GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken Retire(protocol.ConnectionID) Remove(protocol.ConnectionID) ReplaceWithClosed([]protocol.ConnectionID, []byte) AddResetToken(protocol.StatelessResetToken, packetHandler) RemoveResetToken(protocol.StatelessResetToken) } type closeError struct { err error remote bool immediate bool } type errCloseForRecreating struct { nextPacketNumber protocol.PacketNumber nextVersion protocol.Version } func (e *errCloseForRecreating) Error() string { return "closing connection in order to recreate it" } var connTracingID atomic.Uint64 // to be accessed atomically func nextConnTracingID() ConnectionTracingID { return ConnectionTracingID(connTracingID.Add(1)) } // A Connection is a QUIC connection type connection struct { // Destination connection ID used during the handshake. // Used to check source connection ID on incoming packets. handshakeDestConnID protocol.ConnectionID // Set for the client. Destination connection ID used on the first Initial sent. origDestConnID protocol.ConnectionID retrySrcConnID *protocol.ConnectionID // only set for the client (and if a Retry was performed) srcConnIDLen int perspective protocol.Perspective version protocol.Version config *Config conn sendConn sendQueue sender streamsMap streamManager connIDManager *connIDManager connIDGenerator *connIDGenerator rttStats *utils.RTTStats cryptoStreamManager *cryptoStreamManager sentPacketHandler ackhandler.SentPacketHandler receivedPacketHandler ackhandler.ReceivedPacketHandler retransmissionQueue *retransmissionQueue framer *framer connFlowController flowcontrol.ConnectionFlowController tokenStoreKey string // only set for the client tokenGenerator *handshake.TokenGenerator // only set for the server unpacker unpacker frameParser wire.FrameParser packer packer mtuDiscoverer mtuDiscoverer // initialized when the transport parameters are received maxPayloadSizeEstimate atomic.Uint32 initialStream *cryptoStream handshakeStream *cryptoStream oneRTTStream *cryptoStream // only set for the server cryptoStreamHandler cryptoStreamHandler receivedPackets chan receivedPacket sendingScheduled chan struct{} closeOnce sync.Once // closeChan is used to notify the run loop that it should terminate closeChan chan closeError ctx context.Context ctxCancel context.CancelCauseFunc handshakeCompleteChan chan struct{} undecryptablePackets []receivedPacket // undecryptable packets, waiting for a change in encryption level undecryptablePacketsToProcess []receivedPacket earlyConnReadyChan chan struct{} sentFirstPacket bool droppedInitialKeys bool handshakeComplete bool handshakeConfirmed bool receivedRetry bool versionNegotiated bool receivedFirstPacket bool // the minimum of the max_idle_timeout values advertised by both endpoints idleTimeout time.Duration creationTime time.Time // The idle timeout is set based on the max of the time we received the last packet... lastPacketReceivedTime time.Time // ... and the time we sent a new ack-eliciting packet after receiving a packet. firstAckElicitingPacketAfterIdleSentTime time.Time // pacingDeadline is the time when the next packet should be sent pacingDeadline time.Time peerParams *wire.TransportParameters timer connectionTimer // keepAlivePingSent stores whether a keep alive PING is in flight. // It is reset as soon as we receive a packet from the peer. keepAlivePingSent bool keepAliveInterval time.Duration datagramQueue *datagramQueue connStateMutex sync.Mutex connState ConnectionState logID string tracer *logging.ConnectionTracer logger utils.Logger } var ( _ Connection = &connection{} _ EarlyConnection = &connection{} _ streamSender = &connection{} ) var newConnection = func( ctx context.Context, ctxCancel context.CancelCauseFunc, conn sendConn, runner connRunner, origDestConnID protocol.ConnectionID, retrySrcConnID *protocol.ConnectionID, clientDestConnID protocol.ConnectionID, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, connIDGenerator ConnectionIDGenerator, statelessResetToken protocol.StatelessResetToken, conf *Config, tlsConf *tls.Config, tokenGenerator *handshake.TokenGenerator, clientAddressValidated bool, tracer *logging.ConnectionTracer, logger utils.Logger, v protocol.Version, ) quicConn { s := &connection{ ctx: ctx, ctxCancel: ctxCancel, conn: conn, config: conf, handshakeDestConnID: destConnID, srcConnIDLen: srcConnID.Len(), tokenGenerator: tokenGenerator, oneRTTStream: newCryptoStream(), perspective: protocol.PerspectiveServer, tracer: tracer, logger: logger, version: v, } if origDestConnID.Len() > 0 { s.logID = origDestConnID.String() } else { s.logID = destConnID.String() } s.connIDManager = newConnIDManager( destConnID, func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) }, runner.RemoveResetToken, s.queueControlFrame, ) s.connIDGenerator = newConnIDGenerator( srcConnID, &clientDestConnID, func(connID protocol.ConnectionID) { runner.Add(connID, s) }, runner.GetStatelessResetToken, runner.Remove, runner.Retire, runner.ReplaceWithClosed, s.queueControlFrame, connIDGenerator, ) s.preSetup() s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( 0, protocol.ByteCount(s.config.InitialPacketSize), s.rttStats, clientAddressValidated, s.conn.capabilities().ECN, s.perspective, s.tracer, s.logger, ) s.maxPayloadSizeEstimate.Store(uint32(estimateMaxPayloadSize(protocol.ByteCount(s.config.InitialPacketSize)))) params := &wire.TransportParameters{ InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxStreamDataUni: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxData: protocol.ByteCount(s.config.InitialConnectionReceiveWindow), MaxIdleTimeout: s.config.MaxIdleTimeout, MaxBidiStreamNum: protocol.StreamNum(s.config.MaxIncomingStreams), MaxUniStreamNum: protocol.StreamNum(s.config.MaxIncomingUniStreams), MaxAckDelay: protocol.MaxAckDelayInclGranularity, AckDelayExponent: protocol.AckDelayExponent, MaxUDPPayloadSize: protocol.MaxPacketBufferSize, DisableActiveMigration: true, StatelessResetToken: &statelessResetToken, OriginalDestinationConnectionID: origDestConnID, // For interoperability with quic-go versions before May 2023, this value must be set to a value // different from protocol.DefaultActiveConnectionIDLimit. // If set to the default value, it will be omitted from the transport parameters, which will make // old quic-go versions interpret it as 0, instead of the default value of 2. // See https://github.com/quic-go/quic-go/pull/3806. ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, RetrySourceConnectionID: retrySrcConnID, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = wire.MaxDatagramSize } else { params.MaxDatagramFrameSize = protocol.InvalidByteCount } if s.tracer != nil && s.tracer.SentTransportParameters != nil { s.tracer.SentTransportParameters(params) } cs := handshake.NewCryptoSetupServer( clientDestConnID, conn.LocalAddr(), conn.RemoteAddr(), params, tlsConf, conf.Allow0RTT, s.rttStats, tracer, logger, s.version, ) s.cryptoStreamHandler = cs s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) s.cryptoStreamManager = newCryptoStreamManager(s.initialStream, s.handshakeStream, s.oneRTTStream) return s } // declare this as a variable, such that we can it mock it in the tests var newClientConnection = func( ctx context.Context, conn sendConn, runner connRunner, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, connIDGenerator ConnectionIDGenerator, conf *Config, tlsConf *tls.Config, initialPacketNumber protocol.PacketNumber, enable0RTT bool, hasNegotiatedVersion bool, tracer *logging.ConnectionTracer, logger utils.Logger, v protocol.Version, ) quicConn { s := &connection{ conn: conn, config: conf, origDestConnID: destConnID, handshakeDestConnID: destConnID, srcConnIDLen: srcConnID.Len(), perspective: protocol.PerspectiveClient, logID: destConnID.String(), logger: logger, tracer: tracer, versionNegotiated: hasNegotiatedVersion, version: v, } s.connIDManager = newConnIDManager( destConnID, func(token protocol.StatelessResetToken) { runner.AddResetToken(token, s) }, runner.RemoveResetToken, s.queueControlFrame, ) s.connIDGenerator = newConnIDGenerator( srcConnID, nil, func(connID protocol.ConnectionID) { runner.Add(connID, s) }, runner.GetStatelessResetToken, runner.Remove, runner.Retire, runner.ReplaceWithClosed, s.queueControlFrame, connIDGenerator, ) s.ctx, s.ctxCancel = context.WithCancelCause(ctx) s.preSetup() s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( initialPacketNumber, protocol.ByteCount(s.config.InitialPacketSize), s.rttStats, false, // has no effect s.conn.capabilities().ECN, s.perspective, s.tracer, s.logger, ) s.maxPayloadSizeEstimate.Store(uint32(estimateMaxPayloadSize(protocol.ByteCount(s.config.InitialPacketSize)))) oneRTTStream := newCryptoStream() params := &wire.TransportParameters{ InitialMaxStreamDataBidiRemote: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxStreamDataBidiLocal: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxStreamDataUni: protocol.ByteCount(s.config.InitialStreamReceiveWindow), InitialMaxData: protocol.ByteCount(s.config.InitialConnectionReceiveWindow), MaxIdleTimeout: s.config.MaxIdleTimeout, MaxBidiStreamNum: protocol.StreamNum(s.config.MaxIncomingStreams), MaxUniStreamNum: protocol.StreamNum(s.config.MaxIncomingUniStreams), MaxAckDelay: protocol.MaxAckDelayInclGranularity, MaxUDPPayloadSize: protocol.MaxPacketBufferSize, AckDelayExponent: protocol.AckDelayExponent, DisableActiveMigration: true, // For interoperability with quic-go versions before May 2023, this value must be set to a value // different from protocol.DefaultActiveConnectionIDLimit. // If set to the default value, it will be omitted from the transport parameters, which will make // old quic-go versions interpret it as 0, instead of the default value of 2. // See https://github.com/quic-go/quic-go/pull/3806. ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = wire.MaxDatagramSize } else { params.MaxDatagramFrameSize = protocol.InvalidByteCount } if s.tracer != nil && s.tracer.SentTransportParameters != nil { s.tracer.SentTransportParameters(params) } cs := handshake.NewCryptoSetupClient( destConnID, params, tlsConf, enable0RTT, s.rttStats, tracer, logger, s.version, ) s.cryptoStreamHandler = cs s.cryptoStreamManager = newCryptoStreamManager(s.initialStream, s.handshakeStream, oneRTTStream) s.unpacker = newPacketUnpacker(cs, s.srcConnIDLen) s.packer = newPacketPacker(srcConnID, s.connIDManager.Get, s.initialStream, s.handshakeStream, s.sentPacketHandler, s.retransmissionQueue, cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective) if len(tlsConf.ServerName) > 0 { s.tokenStoreKey = tlsConf.ServerName } else { s.tokenStoreKey = conn.RemoteAddr().String() } if s.config.TokenStore != nil { if token := s.config.TokenStore.Pop(s.tokenStoreKey); token != nil { s.packer.SetToken(token.data) } } return s } func (s *connection) preSetup() { s.initialStream = newCryptoStream() s.handshakeStream = newCryptoStream() s.sendQueue = newSendQueue(s.conn) s.retransmissionQueue = newRetransmissionQueue() s.frameParser = *wire.NewFrameParser(s.config.EnableDatagrams) s.rttStats = &utils.RTTStats{} s.connFlowController = flowcontrol.NewConnectionFlowController( protocol.ByteCount(s.config.InitialConnectionReceiveWindow), protocol.ByteCount(s.config.MaxConnectionReceiveWindow), func(size protocol.ByteCount) bool { if s.config.AllowConnectionWindowIncrease == nil { return true } return s.config.AllowConnectionWindowIncrease(s, uint64(size)) }, s.rttStats, s.logger, ) s.earlyConnReadyChan = make(chan struct{}) s.streamsMap = newStreamsMap( s.ctx, s, s.queueControlFrame, s.newFlowController, uint64(s.config.MaxIncomingStreams), uint64(s.config.MaxIncomingUniStreams), s.perspective, ) s.framer = newFramer() s.receivedPackets = make(chan receivedPacket, protocol.MaxConnUnprocessedPackets) s.closeChan = make(chan closeError, 1) s.sendingScheduled = make(chan struct{}, 1) s.handshakeCompleteChan = make(chan struct{}) now := time.Now() s.lastPacketReceivedTime = now s.creationTime = now s.datagramQueue = newDatagramQueue(s.scheduleSending, s.logger) s.connState.Version = s.version } // run the connection main loop func (s *connection) run() error { var closeErr closeError defer func() { s.ctxCancel(closeErr.err) }() s.timer = *newTimer() if err := s.cryptoStreamHandler.StartHandshake(s.ctx); err != nil { return err } if err := s.handleHandshakeEvents(); err != nil { return err } go func() { if err := s.sendQueue.Run(); err != nil { s.destroyImpl(err) } }() if s.perspective == protocol.PerspectiveClient { s.scheduleSending() // so the ClientHello actually gets sent } var sendQueueAvailable <-chan struct{} runLoop: for { if s.framer.QueuedTooManyControlFrames() { s.closeLocal(&qerr.TransportError{ErrorCode: InternalError}) } // Close immediately if requested select { case closeErr = <-s.closeChan: break runLoop default: } s.maybeResetTimer() var processedUndecryptablePacket bool if len(s.undecryptablePacketsToProcess) > 0 { queue := s.undecryptablePacketsToProcess s.undecryptablePacketsToProcess = nil for _, p := range queue { if processed := s.handlePacketImpl(p); processed { processedUndecryptablePacket = true } // Don't set timers and send packets if the packet made us close the connection. select { case closeErr = <-s.closeChan: break runLoop default: } } } // If we processed any undecryptable packets, jump to the resetting of the timers directly. if !processedUndecryptablePacket { select { case closeErr = <-s.closeChan: break runLoop case <-s.timer.Chan(): s.timer.SetRead() // We do all the interesting stuff after the switch statement, so // nothing to see here. case <-s.sendingScheduled: // We do all the interesting stuff after the switch statement, so // nothing to see here. case <-sendQueueAvailable: case firstPacket := <-s.receivedPackets: wasProcessed := s.handlePacketImpl(firstPacket) // Don't set timers and send packets if the packet made us close the connection. select { case closeErr = <-s.closeChan: break runLoop default: } if s.handshakeComplete { // Now process all packets in the receivedPackets channel. // Limit the number of packets to the length of the receivedPackets channel, // so we eventually get a chance to send out an ACK when receiving a lot of packets. numPackets := len(s.receivedPackets) receiveLoop: for i := 0; i < numPackets; i++ { select { case p := <-s.receivedPackets: if processed := s.handlePacketImpl(p); processed { wasProcessed = true } select { case closeErr = <-s.closeChan: break runLoop default: } default: break receiveLoop } } } // Only reset the timers if this packet was actually processed. // This avoids modifying any state when handling undecryptable packets, // which could be injected by an attacker. if !wasProcessed { continue } } } now := time.Now() if timeout := s.sentPacketHandler.GetLossDetectionTimeout(); !timeout.IsZero() && timeout.Before(now) { // This could cause packets to be retransmitted. // Check it before trying to send packets. if err := s.sentPacketHandler.OnLossDetectionTimeout(); err != nil { s.closeLocal(err) } } if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() && !now.Before(keepAliveTime) { // send a PING frame since there is no activity in the connection s.logger.Debugf("Sending a keep-alive PING to keep the connection alive.") s.framer.QueueControlFrame(&wire.PingFrame{}) s.keepAlivePingSent = true } else if !s.handshakeComplete && now.Sub(s.creationTime) >= s.config.handshakeTimeout() { s.destroyImpl(qerr.ErrHandshakeTimeout) continue } else { idleTimeoutStartTime := s.idleTimeoutStartTime() if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) || (s.handshakeComplete && now.After(s.nextIdleTimeoutTime())) { s.destroyImpl(qerr.ErrIdleTimeout) continue } } if s.sendQueue.WouldBlock() { // The send queue is still busy sending out packets. // Wait until there's space to enqueue new packets. sendQueueAvailable = s.sendQueue.Available() continue } if err := s.triggerSending(now); err != nil { s.closeLocal(err) } if s.sendQueue.WouldBlock() { sendQueueAvailable = s.sendQueue.Available() } else { sendQueueAvailable = nil } } s.cryptoStreamHandler.Close() s.sendQueue.Close() // close the send queue before sending the CONNECTION_CLOSE s.handleCloseError(&closeErr) if s.tracer != nil && s.tracer.Close != nil { if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) { s.tracer.Close() } } s.logger.Infof("Connection %s closed.", s.logID) s.timer.Stop() return closeErr.err } // blocks until the early connection can be used func (s *connection) earlyConnReady() <-chan struct{} { return s.earlyConnReadyChan } func (s *connection) HandshakeComplete() <-chan struct{} { return s.handshakeCompleteChan } func (s *connection) Context() context.Context { return s.ctx } func (s *connection) supportsDatagrams() bool { return s.peerParams.MaxDatagramFrameSize > 0 } func (s *connection) ConnectionState() ConnectionState { s.connStateMutex.Lock() defer s.connStateMutex.Unlock() cs := s.cryptoStreamHandler.ConnectionState() s.connState.TLS = cs.ConnectionState s.connState.Used0RTT = cs.Used0RTT s.connState.GSO = s.conn.capabilities().GSO return s.connState } // Time when the connection should time out func (s *connection) nextIdleTimeoutTime() time.Time { idleTimeout := max(s.idleTimeout, s.rttStats.PTO(true)*3) return s.idleTimeoutStartTime().Add(idleTimeout) } // Time when the next keep-alive packet should be sent. // It returns a zero time if no keep-alive should be sent. func (s *connection) nextKeepAliveTime() time.Time { if s.config.KeepAlivePeriod == 0 || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() { return time.Time{} } keepAliveInterval := max(s.keepAliveInterval, s.rttStats.PTO(true)*3/2) return s.lastPacketReceivedTime.Add(keepAliveInterval) } func (s *connection) maybeResetTimer() { var deadline time.Time if !s.handshakeComplete { deadline = s.creationTime.Add(s.config.handshakeTimeout()) if t := s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout); t.Before(deadline) { deadline = t } } else { if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() { deadline = keepAliveTime } else { deadline = s.nextIdleTimeoutTime() } } s.timer.SetTimer( deadline, s.receivedPacketHandler.GetAlarmTimeout(), s.sentPacketHandler.GetLossDetectionTimeout(), s.pacingDeadline, ) } func (s *connection) idleTimeoutStartTime() time.Time { startTime := s.lastPacketReceivedTime if t := s.firstAckElicitingPacketAfterIdleSentTime; t.After(startTime) { startTime = t } return startTime } func (s *connection) handleHandshakeComplete() error { defer close(s.handshakeCompleteChan) // Once the handshake completes, we have derived 1-RTT keys. // There's no point in queueing undecryptable packets for later decryption anymore. s.undecryptablePackets = nil s.connIDManager.SetHandshakeComplete() s.connIDGenerator.SetHandshakeComplete() if s.tracer != nil && s.tracer.ChoseALPN != nil { s.tracer.ChoseALPN(s.cryptoStreamHandler.ConnectionState().NegotiatedProtocol) } // The server applies transport parameters right away, but the client side has to wait for handshake completion. // During a 0-RTT connection, the client is only allowed to use the new transport parameters for 1-RTT packets. if s.perspective == protocol.PerspectiveClient { s.applyTransportParameters() return nil } // All these only apply to the server side. if err := s.handleHandshakeConfirmed(); err != nil { return err } ticket, err := s.cryptoStreamHandler.GetSessionTicket() if err != nil { return err } if ticket != nil { // may be nil if session tickets are disabled via tls.Config.SessionTicketsDisabled s.oneRTTStream.Write(ticket) for s.oneRTTStream.HasData() { s.queueControlFrame(s.oneRTTStream.PopCryptoFrame(protocol.MaxPostHandshakeCryptoFrameSize)) } } token, err := s.tokenGenerator.NewToken(s.conn.RemoteAddr()) if err != nil { return err } s.queueControlFrame(&wire.NewTokenFrame{Token: token}) s.queueControlFrame(&wire.HandshakeDoneFrame{}) return nil } func (s *connection) handleHandshakeConfirmed() error { if err := s.dropEncryptionLevel(protocol.EncryptionHandshake); err != nil { return err } s.handshakeConfirmed = true s.sentPacketHandler.SetHandshakeConfirmed() s.cryptoStreamHandler.SetHandshakeConfirmed() if !s.config.DisablePathMTUDiscovery && s.conn.capabilities().DF { s.mtuDiscoverer.Start() } return nil } func (s *connection) handlePacketImpl(rp receivedPacket) bool { s.sentPacketHandler.ReceivedBytes(rp.Size()) if wire.IsVersionNegotiationPacket(rp.data) { s.handleVersionNegotiationPacket(rp) return false } var counter uint8 var lastConnID protocol.ConnectionID var processed bool data := rp.data p := rp for len(data) > 0 { if counter > 0 { p = *(p.Clone()) p.data = data destConnID, err := wire.ParseConnectionID(p.data, s.srcConnIDLen) if err != nil { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropHeaderParseError) } s.logger.Debugf("error parsing packet, couldn't parse connection ID: %s", err) break } if destConnID != lastConnID { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID) } s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", destConnID, lastConnID) break } } if wire.IsLongHeaderPacket(p.data[0]) { hdr, packetData, rest, err := wire.ParsePacket(p.data) if err != nil { if s.tracer != nil && s.tracer.DroppedPacket != nil { dropReason := logging.PacketDropHeaderParseError if err == wire.ErrUnsupportedVersion { dropReason = logging.PacketDropUnsupportedVersion } s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), dropReason) } s.logger.Debugf("error parsing packet: %s", err) break } lastConnID = hdr.DestConnectionID if hdr.Version != s.version { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion) } s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version) break } if counter > 0 { p.buffer.Split() } counter++ // only log if this actually a coalesced packet if s.logger.Debug() && (counter > 1 || len(rest) > 0) { s.logger.Debugf("Parsed a coalesced packet. Part %d: %d bytes. Remaining: %d bytes.", counter, len(packetData), len(rest)) } p.data = packetData if wasProcessed := s.handleLongHeaderPacket(p, hdr); wasProcessed { processed = true } data = rest } else { if counter > 0 { p.buffer.Split() } processed = s.handleShortHeaderPacket(p) break } } p.buffer.MaybeRelease() return processed } func (s *connection) handleShortHeaderPacket(p receivedPacket) bool { var wasQueued bool defer func() { // Put back the packet buffer if the packet wasn't queued for later decryption. if !wasQueued { p.buffer.Decrement() } }() destConnID, err := wire.ParseConnectionID(p.data, s.srcConnIDLen) if err != nil { s.tracer.DroppedPacket(logging.PacketType1RTT, protocol.InvalidPacketNumber, protocol.ByteCount(len(p.data)), logging.PacketDropHeaderParseError) return false } pn, pnLen, keyPhase, data, err := s.unpacker.UnpackShortHeader(p.rcvTime, p.data) if err != nil { wasQueued = s.handleUnpackError(err, p, logging.PacketType1RTT) return false } if s.logger.Debug() { s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, 1-RTT", pn, p.Size(), destConnID) wire.LogShortHeader(s.logger, destConnID, pn, pnLen, keyPhase) } if s.receivedPacketHandler.IsPotentiallyDuplicate(pn, protocol.Encryption1RTT) { s.logger.Debugf("Dropping (potentially) duplicate packet.") if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketType1RTT, pn, p.Size(), logging.PacketDropDuplicate) } return false } var log func([]logging.Frame) if s.tracer != nil && s.tracer.ReceivedShortHeaderPacket != nil { log = func(frames []logging.Frame) { s.tracer.ReceivedShortHeaderPacket( &logging.ShortHeader{ DestConnectionID: destConnID, PacketNumber: pn, PacketNumberLen: pnLen, KeyPhase: keyPhase, }, p.Size(), p.ecn, frames, ) } } if err := s.handleUnpackedShortHeaderPacket(destConnID, pn, data, p.ecn, p.rcvTime, log); err != nil { s.closeLocal(err) return false } return true } func (s *connection) handleLongHeaderPacket(p receivedPacket, hdr *wire.Header) bool /* was the packet successfully processed */ { var wasQueued bool defer func() { // Put back the packet buffer if the packet wasn't queued for later decryption. if !wasQueued { p.buffer.Decrement() } }() if hdr.Type == protocol.PacketTypeRetry { return s.handleRetryPacket(hdr, p.data, p.rcvTime) } // The server can change the source connection ID with the first Handshake packet. // After this, all packets with a different source connection have to be ignored. if s.receivedFirstPacket && hdr.Type == protocol.PacketTypeInitial && hdr.SrcConnectionID != s.handshakeDestConnID { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeInitial, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnknownConnectionID) } s.logger.Debugf("Dropping Initial packet (%d bytes) with unexpected source connection ID: %s (expected %s)", p.Size(), hdr.SrcConnectionID, s.handshakeDestConnID) return false } // drop 0-RTT packets, if we are a client if s.perspective == protocol.PerspectiveClient && hdr.Type == protocol.PacketType0RTT { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketType0RTT, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropKeyUnavailable) } return false } packet, err := s.unpacker.UnpackLongHeader(hdr, p.data) if err != nil { wasQueued = s.handleUnpackError(err, p, logging.PacketTypeFromHeader(hdr)) return false } if s.logger.Debug() { s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.hdr.PacketNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel) packet.hdr.Log(s.logger) } if pn := packet.hdr.PacketNumber; s.receivedPacketHandler.IsPotentiallyDuplicate(pn, packet.encryptionLevel) { s.logger.Debugf("Dropping (potentially) duplicate packet.") if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), pn, p.Size(), logging.PacketDropDuplicate) } return false } if err := s.handleUnpackedLongHeaderPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil { s.closeLocal(err) return false } return true } func (s *connection) handleUnpackError(err error, p receivedPacket, pt logging.PacketType) (wasQueued bool) { switch err { case handshake.ErrKeysDropped: if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropKeyUnavailable) } s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", pt, p.Size()) case handshake.ErrKeysNotYetAvailable: // Sealer for this encryption level not yet available. // Try again later. s.tryQueueingUndecryptablePacket(p, pt) return true case wire.ErrInvalidReservedBits: s.closeLocal(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: err.Error(), }) case handshake.ErrDecryptionFailed: // This might be a packet injected by an attacker. Drop it. if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropPayloadDecryptError) } s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", pt, p.Size(), err) default: var headerErr *headerParseError if errors.As(err, &headerErr) { // This might be a packet injected by an attacker. Drop it. if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", pt, p.Size(), err) } else { // This is an error returned by the AEAD (other than ErrDecryptionFailed). // For example, a PROTOCOL_VIOLATION due to key updates. s.closeLocal(err) } } return false } func (s *connection) handleRetryPacket(hdr *wire.Header, data []byte, rcvTime time.Time) bool /* was this a valid Retry */ { if s.perspective == protocol.PerspectiveServer { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry.") return false } if s.receivedFirstPacket { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry, since we already received a packet.") return false } destConnID := s.connIDManager.Get() if hdr.SrcConnectionID == destConnID { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry, since the server didn't change the Source Connection ID.") return false } // If a token is already set, this means that we already received a Retry from the server. // Ignore this Retry packet. if s.receivedRetry { s.logger.Debugf("Ignoring Retry, since a Retry was already received.") return false } tag := handshake.GetRetryIntegrityTag(data[:len(data)-16], destConnID, hdr.Version) if !bytes.Equal(data[len(data)-16:], tag[:]) { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, protocol.ByteCount(len(data)), logging.PacketDropPayloadDecryptError) } s.logger.Debugf("Ignoring spoofed Retry. Integrity Tag doesn't match.") return false } if s.logger.Debug() { s.logger.Debugf("<- Received Retry:") (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) s.logger.Debugf("Switching destination connection ID to: %s", hdr.SrcConnectionID) } if s.tracer != nil && s.tracer.ReceivedRetry != nil { s.tracer.ReceivedRetry(hdr) } newDestConnID := hdr.SrcConnectionID s.receivedRetry = true if err := s.sentPacketHandler.ResetForRetry(rcvTime); err != nil { s.closeLocal(err) return false } s.handshakeDestConnID = newDestConnID s.retrySrcConnID = &newDestConnID s.cryptoStreamHandler.ChangeConnectionID(newDestConnID) s.packer.SetToken(hdr.Token) s.connIDManager.ChangeInitialConnID(newDestConnID) s.scheduleSending() return true } func (s *connection) handleVersionNegotiationPacket(p receivedPacket) { if s.perspective == protocol.PerspectiveServer || // servers never receive version negotiation packets s.receivedFirstPacket || s.versionNegotiated { // ignore delayed / duplicated version negotiation packets if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) } return } src, dest, supportedVersions, err := wire.ParseVersionNegotiationPacket(p.data) if err != nil { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Error parsing Version Negotiation packet: %s", err) return } for _, v := range supportedVersions { if v == s.version { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedVersion) } // The Version Negotiation packet contains the version that we offered. // This might be a packet sent by an attacker, or it was corrupted. return } } s.logger.Infof("Received a Version Negotiation packet. Supported Versions: %s", supportedVersions) if s.tracer != nil && s.tracer.ReceivedVersionNegotiationPacket != nil { s.tracer.ReceivedVersionNegotiationPacket(dest, src, supportedVersions) } newVersion, ok := protocol.ChooseSupportedVersion(s.config.Versions, supportedVersions) if !ok { s.destroyImpl(&VersionNegotiationError{ Ours: s.config.Versions, Theirs: supportedVersions, }) s.logger.Infof("No compatible QUIC version found.") return } if s.tracer != nil && s.tracer.NegotiatedVersion != nil { s.tracer.NegotiatedVersion(newVersion, s.config.Versions, supportedVersions) } s.logger.Infof("Switching to QUIC version %s.", newVersion) nextPN, _ := s.sentPacketHandler.PeekPacketNumber(protocol.EncryptionInitial) s.destroyImpl(&errCloseForRecreating{ nextPacketNumber: nextPN, nextVersion: newVersion, }) } func (s *connection) handleUnpackedLongHeaderPacket( packet *unpackedPacket, ecn protocol.ECN, rcvTime time.Time, packetSize protocol.ByteCount, // only for logging ) error { if !s.receivedFirstPacket { s.receivedFirstPacket = true if !s.versionNegotiated && s.tracer != nil && s.tracer.NegotiatedVersion != nil { var clientVersions, serverVersions []protocol.Version switch s.perspective { case protocol.PerspectiveClient: clientVersions = s.config.Versions case protocol.PerspectiveServer: serverVersions = s.config.Versions } s.tracer.NegotiatedVersion(s.version, clientVersions, serverVersions) } // The server can change the source connection ID with the first Handshake packet. if s.perspective == protocol.PerspectiveClient && packet.hdr.SrcConnectionID != s.handshakeDestConnID { cid := packet.hdr.SrcConnectionID s.logger.Debugf("Received first packet. Switching destination connection ID to: %s", cid) s.handshakeDestConnID = cid s.connIDManager.ChangeInitialConnID(cid) } // We create the connection as soon as we receive the first packet from the client. // We do that before authenticating the packet. // That means that if the source connection ID was corrupted, // we might have created a connection with an incorrect source connection ID. // Once we authenticate the first packet, we need to update it. if s.perspective == protocol.PerspectiveServer { if packet.hdr.SrcConnectionID != s.handshakeDestConnID { s.handshakeDestConnID = packet.hdr.SrcConnectionID s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID) } if s.tracer != nil && s.tracer.StartedConnection != nil { s.tracer.StartedConnection( s.conn.LocalAddr(), s.conn.RemoteAddr(), packet.hdr.SrcConnectionID, packet.hdr.DestConnectionID, ) } } } if s.perspective == protocol.PerspectiveServer && packet.encryptionLevel == protocol.EncryptionHandshake && !s.droppedInitialKeys { // On the server side, Initial keys are dropped as soon as the first Handshake packet is received. // See Section 4.9.1 of RFC 9001. if err := s.dropEncryptionLevel(protocol.EncryptionInitial); err != nil { return err } } s.lastPacketReceivedTime = rcvTime s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} s.keepAlivePingSent = false var log func([]logging.Frame) if s.tracer != nil && s.tracer.ReceivedLongHeaderPacket != nil { log = func(frames []logging.Frame) { s.tracer.ReceivedLongHeaderPacket(packet.hdr, packetSize, ecn, frames) } } isAckEliciting, err := s.handleFrames(packet.data, packet.hdr.DestConnectionID, packet.encryptionLevel, log) if err != nil { return err } return s.receivedPacketHandler.ReceivedPacket(packet.hdr.PacketNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting) } func (s *connection) handleUnpackedShortHeaderPacket( destConnID protocol.ConnectionID, pn protocol.PacketNumber, data []byte, ecn protocol.ECN, rcvTime time.Time, log func([]logging.Frame), ) error { s.lastPacketReceivedTime = rcvTime s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} s.keepAlivePingSent = false isAckEliciting, err := s.handleFrames(data, destConnID, protocol.Encryption1RTT, log) if err != nil { return err } return s.receivedPacketHandler.ReceivedPacket(pn, ecn, protocol.Encryption1RTT, rcvTime, isAckEliciting) } func (s *connection) handleFrames( data []byte, destConnID protocol.ConnectionID, encLevel protocol.EncryptionLevel, log func([]logging.Frame), ) (isAckEliciting bool, _ error) { // Only used for tracing. // If we're not tracing, this slice will always remain empty. var frames []logging.Frame if log != nil { frames = make([]logging.Frame, 0, 4) } handshakeWasComplete := s.handshakeComplete var handleErr error for len(data) > 0 { l, frame, err := s.frameParser.ParseNext(data, encLevel, s.version) if err != nil { return false, err } data = data[l:] if frame == nil { break } if ackhandler.IsFrameAckEliciting(frame) { isAckEliciting = true } if log != nil { frames = append(frames, toLoggingFrame(frame)) } // An error occurred handling a previous frame. // Don't handle the current frame. if handleErr != nil { continue } if err := s.handleFrame(frame, encLevel, destConnID); err != nil { if log == nil { return false, err } // If we're logging, we need to keep parsing (but not handling) all frames. handleErr = err } } if log != nil { log(frames) if handleErr != nil { return false, handleErr } } // Handle completion of the handshake after processing all the frames. // This ensures that we correctly handle the following case on the server side: // We receive a Handshake packet that contains the CRYPTO frame that allows us to complete the handshake, // and an ACK serialized after that CRYPTO frame. In this case, we still want to process the ACK frame. if !handshakeWasComplete && s.handshakeComplete { if err := s.handleHandshakeComplete(); err != nil { return false, err } } return } func (s *connection) handleFrame(f wire.Frame, encLevel protocol.EncryptionLevel, destConnID protocol.ConnectionID) error { var err error wire.LogFrame(s.logger, f, false) switch frame := f.(type) { case *wire.CryptoFrame: err = s.handleCryptoFrame(frame, encLevel) case *wire.StreamFrame: err = s.handleStreamFrame(frame) case *wire.AckFrame: err = s.handleAckFrame(frame, encLevel) case *wire.ConnectionCloseFrame: s.handleConnectionCloseFrame(frame) case *wire.ResetStreamFrame: err = s.handleResetStreamFrame(frame) case *wire.MaxDataFrame: s.handleMaxDataFrame(frame) case *wire.MaxStreamDataFrame: err = s.handleMaxStreamDataFrame(frame) case *wire.MaxStreamsFrame: s.handleMaxStreamsFrame(frame) case *wire.DataBlockedFrame: case *wire.StreamDataBlockedFrame: case *wire.StreamsBlockedFrame: case *wire.StopSendingFrame: err = s.handleStopSendingFrame(frame) case *wire.PingFrame: case *wire.PathChallengeFrame: s.handlePathChallengeFrame(frame) case *wire.PathResponseFrame: // since we don't send PATH_CHALLENGEs, we don't expect PATH_RESPONSEs err = errors.New("unexpected PATH_RESPONSE frame") case *wire.NewTokenFrame: err = s.handleNewTokenFrame(frame) case *wire.NewConnectionIDFrame: err = s.handleNewConnectionIDFrame(frame) case *wire.RetireConnectionIDFrame: err = s.handleRetireConnectionIDFrame(frame, destConnID) case *wire.HandshakeDoneFrame: err = s.handleHandshakeDoneFrame() case *wire.DatagramFrame: err = s.handleDatagramFrame(frame) default: err = fmt.Errorf("unexpected frame type: %s", reflect.ValueOf(&frame).Elem().Type().Name()) } return err } // handlePacket is called by the server with a new packet func (s *connection) handlePacket(p receivedPacket) { // Discard packets once the amount of queued packets is larger than // the channel size, protocol.MaxConnUnprocessedPackets select { case s.receivedPackets <- p: default: if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropDOSPrevention) } } } func (s *connection) handleConnectionCloseFrame(frame *wire.ConnectionCloseFrame) { if frame.IsApplicationError { s.closeRemote(&qerr.ApplicationError{ Remote: true, ErrorCode: qerr.ApplicationErrorCode(frame.ErrorCode), ErrorMessage: frame.ReasonPhrase, }) return } s.closeRemote(&qerr.TransportError{ Remote: true, ErrorCode: qerr.TransportErrorCode(frame.ErrorCode), FrameType: frame.FrameType, ErrorMessage: frame.ReasonPhrase, }) } func (s *connection) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { if err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel); err != nil { return err } for { data := s.cryptoStreamManager.GetCryptoData(encLevel) if data == nil { break } if err := s.cryptoStreamHandler.HandleMessage(data, encLevel); err != nil { return err } } return s.handleHandshakeEvents() } func (s *connection) handleHandshakeEvents() error { for { ev := s.cryptoStreamHandler.NextEvent() var err error switch ev.Kind { case handshake.EventNoEvent: return nil case handshake.EventHandshakeComplete: // Don't call handleHandshakeComplete yet. // It's advantageous to process ACK frames that might be serialized after the CRYPTO frame first. s.handshakeComplete = true case handshake.EventReceivedTransportParameters: err = s.handleTransportParameters(ev.TransportParameters) case handshake.EventRestoredTransportParameters: s.restoreTransportParameters(ev.TransportParameters) close(s.earlyConnReadyChan) case handshake.EventReceivedReadKeys: // Queue all packets for decryption that have been undecryptable so far. s.undecryptablePacketsToProcess = s.undecryptablePackets s.undecryptablePackets = nil case handshake.EventDiscard0RTTKeys: err = s.dropEncryptionLevel(protocol.Encryption0RTT) case handshake.EventWriteInitialData: _, err = s.initialStream.Write(ev.Data) case handshake.EventWriteHandshakeData: _, err = s.handshakeStream.Write(ev.Data) } if err != nil { return err } } } func (s *connection) handleStreamFrame(frame *wire.StreamFrame) error { str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) if err != nil { return err } if str == nil { // Stream is closed and already garbage collected // ignore this StreamFrame return nil } return str.handleStreamFrame(frame) } func (s *connection) handleMaxDataFrame(frame *wire.MaxDataFrame) { s.connFlowController.UpdateSendWindow(frame.MaximumData) } func (s *connection) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) error { str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) if err != nil { return err } if str == nil { // stream is closed and already garbage collected return nil } str.updateSendWindow(frame.MaximumStreamData) return nil } func (s *connection) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) { s.streamsMap.HandleMaxStreamsFrame(frame) } func (s *connection) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { str, err := s.streamsMap.GetOrOpenReceiveStream(frame.StreamID) if err != nil { return err } if str == nil { // stream is closed and already garbage collected return nil } return str.handleResetStreamFrame(frame) } func (s *connection) handleStopSendingFrame(frame *wire.StopSendingFrame) error { str, err := s.streamsMap.GetOrOpenSendStream(frame.StreamID) if err != nil { return err } if str == nil { // stream is closed and already garbage collected return nil } str.handleStopSendingFrame(frame) return nil } func (s *connection) handlePathChallengeFrame(frame *wire.PathChallengeFrame) { s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data}) } func (s *connection) handleNewTokenFrame(frame *wire.NewTokenFrame) error { if s.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received NEW_TOKEN frame from the client", } } if s.config.TokenStore != nil { s.config.TokenStore.Put(s.tokenStoreKey, &ClientToken{data: frame.Token}) } return nil } func (s *connection) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error { return s.connIDManager.Add(f) } func (s *connection) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error { return s.connIDGenerator.Retire(f.SequenceNumber, destConnID) } func (s *connection) handleHandshakeDoneFrame() error { if s.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received a HANDSHAKE_DONE frame", } } if !s.handshakeConfirmed { return s.handleHandshakeConfirmed() } return nil } func (s *connection) handleAckFrame(frame *wire.AckFrame, encLevel protocol.EncryptionLevel) error { acked1RTTPacket, err := s.sentPacketHandler.ReceivedAck(frame, encLevel, s.lastPacketReceivedTime) if err != nil { return err } if !acked1RTTPacket { return nil } // On the client side: If the packet acknowledged a 1-RTT packet, this confirms the handshake. // This is only possible if the ACK was sent in a 1-RTT packet. // This is an optimization over simply waiting for a HANDSHAKE_DONE frame, see section 4.1.2 of RFC 9001. if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed { if err := s.handleHandshakeConfirmed(); err != nil { return err } } return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) } func (s *connection) handleDatagramFrame(f *wire.DatagramFrame) error { if f.Length(s.version) > wire.MaxDatagramSize { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "DATAGRAM frame too large", } } s.datagramQueue.HandleDatagramFrame(f) return nil } // closeLocal closes the connection and send a CONNECTION_CLOSE containing the error func (s *connection) closeLocal(e error) { s.closeOnce.Do(func() { if e == nil { s.logger.Infof("Closing connection.") } else { s.logger.Errorf("Closing connection with error: %s", e) } s.closeChan <- closeError{err: e, immediate: false, remote: false} }) } // destroy closes the connection without sending the error on the wire func (s *connection) destroy(e error) { s.destroyImpl(e) <-s.ctx.Done() } func (s *connection) destroyImpl(e error) { s.closeOnce.Do(func() { if nerr, ok := e.(net.Error); ok && nerr.Timeout() { s.logger.Errorf("Destroying connection: %s", e) } else { s.logger.Errorf("Destroying connection with error: %s", e) } s.closeChan <- closeError{err: e, immediate: true, remote: false} }) } func (s *connection) closeRemote(e error) { s.closeOnce.Do(func() { s.logger.Errorf("Peer closed connection with error: %s", e) s.closeChan <- closeError{err: e, immediate: true, remote: true} }) } func (s *connection) CloseWithError(code ApplicationErrorCode, desc string) error { s.closeLocal(&qerr.ApplicationError{ ErrorCode: code, ErrorMessage: desc, }) <-s.ctx.Done() return nil } func (s *connection) closeWithTransportError(code TransportErrorCode) { s.closeLocal(&qerr.TransportError{ErrorCode: code}) <-s.ctx.Done() } func (s *connection) handleCloseError(closeErr *closeError) { e := closeErr.err if e == nil { e = &qerr.ApplicationError{} } else { defer func() { closeErr.err = e }() } var ( statelessResetErr *StatelessResetError versionNegotiationErr *VersionNegotiationError recreateErr *errCloseForRecreating applicationErr *ApplicationError transportErr *TransportError ) switch { case errors.Is(e, qerr.ErrIdleTimeout), errors.Is(e, qerr.ErrHandshakeTimeout), errors.As(e, &statelessResetErr), errors.As(e, &versionNegotiationErr), errors.As(e, &recreateErr), errors.As(e, &applicationErr), errors.As(e, &transportErr): default: e = &qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: e.Error(), } } s.streamsMap.CloseWithError(e) s.connIDManager.Close() if s.datagramQueue != nil { s.datagramQueue.CloseWithError(e) } if s.tracer != nil && s.tracer.ClosedConnection != nil && !errors.As(e, &recreateErr) { s.tracer.ClosedConnection(e) } // If this is a remote close we're done here if closeErr.remote { s.connIDGenerator.ReplaceWithClosed(nil) return } if closeErr.immediate { s.connIDGenerator.RemoveAll() return } // Don't send out any CONNECTION_CLOSE if this is an error that occurred // before we even sent out the first packet. if s.perspective == protocol.PerspectiveClient && !s.sentFirstPacket { s.connIDGenerator.RemoveAll() return } connClosePacket, err := s.sendConnectionClose(e) if err != nil { s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) } s.connIDGenerator.ReplaceWithClosed(connClosePacket) } func (s *connection) dropEncryptionLevel(encLevel protocol.EncryptionLevel) error { if s.tracer != nil && s.tracer.DroppedEncryptionLevel != nil { s.tracer.DroppedEncryptionLevel(encLevel) } s.sentPacketHandler.DropPackets(encLevel) s.receivedPacketHandler.DropPackets(encLevel) //nolint:exhaustive // only Initial and 0-RTT need special treatment switch encLevel { case protocol.EncryptionInitial: s.droppedInitialKeys = true s.cryptoStreamHandler.DiscardInitialKeys() case protocol.Encryption0RTT: s.streamsMap.ResetFor0RTT() s.framer.Handle0RTTRejection() return s.connFlowController.Reset() } return s.cryptoStreamManager.Drop(encLevel) } // is called for the client, when restoring transport parameters saved for 0-RTT func (s *connection) restoreTransportParameters(params *wire.TransportParameters) { if s.logger.Debug() { s.logger.Debugf("Restoring Transport Parameters: %s", params) } s.peerParams = params s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) s.connFlowController.UpdateSendWindow(params.InitialMaxData) s.streamsMap.UpdateLimits(params) s.connStateMutex.Lock() s.connState.SupportsDatagrams = s.supportsDatagrams() s.connStateMutex.Unlock() } func (s *connection) handleTransportParameters(params *wire.TransportParameters) error { if s.tracer != nil && s.tracer.ReceivedTransportParameters != nil { s.tracer.ReceivedTransportParameters(params) } if err := s.checkTransportParameters(params); err != nil { return &qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), } } if s.perspective == protocol.PerspectiveClient && s.peerParams != nil && s.ConnectionState().Used0RTT && !params.ValidForUpdate(s.peerParams) { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "server sent reduced limits after accepting 0-RTT data", } } s.peerParams = params // On the client side we have to wait for handshake completion. // During a 0-RTT connection, we are only allowed to use the new transport parameters for 1-RTT packets. if s.perspective == protocol.PerspectiveServer { s.applyTransportParameters() // On the server side, the early connection is ready as soon as we processed // the client's transport parameters. close(s.earlyConnReadyChan) } s.connStateMutex.Lock() s.connState.SupportsDatagrams = s.supportsDatagrams() s.connStateMutex.Unlock() return nil } func (s *connection) checkTransportParameters(params *wire.TransportParameters) error { if s.logger.Debug() { s.logger.Debugf("Processed Transport Parameters: %s", params) } // check the initial_source_connection_id if params.InitialSourceConnectionID != s.handshakeDestConnID { return fmt.Errorf("expected initial_source_connection_id to equal %s, is %s", s.handshakeDestConnID, params.InitialSourceConnectionID) } if s.perspective == protocol.PerspectiveServer { return nil } // check the original_destination_connection_id if params.OriginalDestinationConnectionID != s.origDestConnID { return fmt.Errorf("expected original_destination_connection_id to equal %s, is %s", s.origDestConnID, params.OriginalDestinationConnectionID) } if s.retrySrcConnID != nil { // a Retry was performed if params.RetrySourceConnectionID == nil { return errors.New("missing retry_source_connection_id") } if *params.RetrySourceConnectionID != *s.retrySrcConnID { return fmt.Errorf("expected retry_source_connection_id to equal %s, is %s", s.retrySrcConnID, *params.RetrySourceConnectionID) } } else if params.RetrySourceConnectionID != nil { return errors.New("received retry_source_connection_id, although no Retry was performed") } return nil } func (s *connection) applyTransportParameters() { params := s.peerParams // Our local idle timeout will always be > 0. s.idleTimeout = s.config.MaxIdleTimeout if s.idleTimeout > 0 && params.MaxIdleTimeout < s.idleTimeout { s.idleTimeout = params.MaxIdleTimeout } s.keepAliveInterval = min(s.config.KeepAlivePeriod, min(s.idleTimeout/2, protocol.MaxKeepAliveInterval)) s.streamsMap.UpdateLimits(params) s.frameParser.SetAckDelayExponent(params.AckDelayExponent) s.connFlowController.UpdateSendWindow(params.InitialMaxData) s.rttStats.SetMaxAckDelay(params.MaxAckDelay) s.connIDGenerator.SetMaxActiveConnIDs(params.ActiveConnectionIDLimit) if params.StatelessResetToken != nil { s.connIDManager.SetStatelessResetToken(*params.StatelessResetToken) } // We don't support connection migration yet, so we don't have any use for the preferred_address. if params.PreferredAddress != nil { // Retire the connection ID. s.connIDManager.AddFromPreferredAddress(params.PreferredAddress.ConnectionID, params.PreferredAddress.StatelessResetToken) } maxPacketSize := protocol.ByteCount(protocol.MaxPacketBufferSize) if params.MaxUDPPayloadSize > 0 && params.MaxUDPPayloadSize < maxPacketSize { maxPacketSize = params.MaxUDPPayloadSize } s.mtuDiscoverer = newMTUDiscoverer( s.rttStats, protocol.ByteCount(s.config.InitialPacketSize), maxPacketSize, s.onMTUIncreased, s.tracer, ) } func (s *connection) triggerSending(now time.Time) error { s.pacingDeadline = time.Time{} sendMode := s.sentPacketHandler.SendMode(now) //nolint:exhaustive // No need to handle pacing limited here. switch sendMode { case ackhandler.SendAny: return s.sendPackets(now) case ackhandler.SendNone: return nil case ackhandler.SendPacingLimited: deadline := s.sentPacketHandler.TimeUntilSend() if deadline.IsZero() { deadline = deadlineSendImmediately } s.pacingDeadline = deadline // Allow sending of an ACK if we're pacing limit. // This makes sure that a peer that is mostly receiving data (and thus has an inaccurate cwnd estimate) // sends enough ACKs to allow its peer to utilize the bandwidth. fallthrough case ackhandler.SendAck: // We can at most send a single ACK only packet. // There will only be a new ACK after receiving new packets. // SendAck is only returned when we're congestion limited, so we don't need to set the pacinggs timer. return s.maybeSendAckOnlyPacket(now) case ackhandler.SendPTOInitial: if err := s.sendProbePacket(protocol.EncryptionInitial, now); err != nil { return err } if s.sendQueue.WouldBlock() { s.scheduleSending() return nil } return s.triggerSending(now) case ackhandler.SendPTOHandshake: if err := s.sendProbePacket(protocol.EncryptionHandshake, now); err != nil { return err } if s.sendQueue.WouldBlock() { s.scheduleSending() return nil } return s.triggerSending(now) case ackhandler.SendPTOAppData: if err := s.sendProbePacket(protocol.Encryption1RTT, now); err != nil { return err } if s.sendQueue.WouldBlock() { s.scheduleSending() return nil } return s.triggerSending(now) default: return fmt.Errorf("BUG: invalid send mode %d", sendMode) } } func (s *connection) sendPackets(now time.Time) error { // Path MTU Discovery // Can't use GSO, since we need to send a single packet that's larger than our current maximum size. // Performance-wise, this doesn't matter, since we only send a very small (<10) number of // MTU probe packets per connection. if s.handshakeConfirmed && s.mtuDiscoverer != nil && s.mtuDiscoverer.ShouldSendProbe(now) { ping, size := s.mtuDiscoverer.GetPing() p, buf, err := s.packer.PackMTUProbePacket(ping, size, s.version) if err != nil { return err } ecn := s.sentPacketHandler.ECNMode(true) s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) s.registerPackedShortHeaderPacket(p, ecn, now) s.sendQueue.Send(buf, 0, ecn) // This is kind of a hack. We need to trigger sending again somehow. s.pacingDeadline = deadlineSendImmediately return nil } if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked { s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset}) } if offset := s.connFlowController.GetWindowUpdate(); offset > 0 { s.framer.QueueControlFrame(&wire.MaxDataFrame{MaximumData: offset}) } if cf := s.cryptoStreamManager.GetPostHandshakeData(protocol.MaxPostHandshakeCryptoFrameSize); cf != nil { s.queueControlFrame(cf) } if !s.handshakeConfirmed { packet, err := s.packer.PackCoalescedPacket(false, s.maxPacketSize(), s.version) if err != nil || packet == nil { return err } s.sentFirstPacket = true if err := s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now); err != nil { return err } sendMode := s.sentPacketHandler.SendMode(now) if sendMode == ackhandler.SendPacingLimited { s.resetPacingDeadline() } else if sendMode == ackhandler.SendAny { s.pacingDeadline = deadlineSendImmediately } return nil } if s.conn.capabilities().GSO { return s.sendPacketsWithGSO(now) } return s.sendPacketsWithoutGSO(now) } func (s *connection) sendPacketsWithoutGSO(now time.Time) error { for { buf := getPacketBuffer() ecn := s.sentPacketHandler.ECNMode(true) if _, err := s.appendOneShortHeaderPacket(buf, s.maxPacketSize(), ecn, now); err != nil { if err == errNothingToPack { buf.Release() return nil } return err } s.sendQueue.Send(buf, 0, ecn) if s.sendQueue.WouldBlock() { return nil } sendMode := s.sentPacketHandler.SendMode(now) if sendMode == ackhandler.SendPacingLimited { s.resetPacingDeadline() return nil } if sendMode != ackhandler.SendAny { return nil } // Prioritize receiving of packets over sending out more packets. if len(s.receivedPackets) > 0 { s.pacingDeadline = deadlineSendImmediately return nil } } } func (s *connection) sendPacketsWithGSO(now time.Time) error { buf := getLargePacketBuffer() maxSize := s.maxPacketSize() ecn := s.sentPacketHandler.ECNMode(true) for { var dontSendMore bool size, err := s.appendOneShortHeaderPacket(buf, maxSize, ecn, now) if err != nil { if err != errNothingToPack { return err } if buf.Len() == 0 { buf.Release() return nil } dontSendMore = true } if !dontSendMore { sendMode := s.sentPacketHandler.SendMode(now) if sendMode == ackhandler.SendPacingLimited { s.resetPacingDeadline() } if sendMode != ackhandler.SendAny { dontSendMore = true } } // Don't send more packets in this batch if they require a different ECN marking than the previous ones. nextECN := s.sentPacketHandler.ECNMode(true) // Append another packet if // 1. The congestion controller and pacer allow sending more // 2. The last packet appended was a full-size packet // 3. The next packet will have the same ECN marking // 4. We still have enough space for another full-size packet in the buffer if !dontSendMore && size == maxSize && nextECN == ecn && buf.Len()+maxSize <= buf.Cap() { continue } s.sendQueue.Send(buf, uint16(maxSize), ecn) if dontSendMore { return nil } if s.sendQueue.WouldBlock() { return nil } // Prioritize receiving of packets over sending out more packets. if len(s.receivedPackets) > 0 { s.pacingDeadline = deadlineSendImmediately return nil } buf = getLargePacketBuffer() } } func (s *connection) resetPacingDeadline() { deadline := s.sentPacketHandler.TimeUntilSend() if deadline.IsZero() { deadline = deadlineSendImmediately } s.pacingDeadline = deadline } func (s *connection) maybeSendAckOnlyPacket(now time.Time) error { if !s.handshakeConfirmed { ecn := s.sentPacketHandler.ECNMode(false) packet, err := s.packer.PackCoalescedPacket(true, s.maxPacketSize(), s.version) if err != nil { return err } if packet == nil { return nil } return s.sendPackedCoalescedPacket(packet, ecn, now) } ecn := s.sentPacketHandler.ECNMode(true) p, buf, err := s.packer.PackAckOnlyPacket(s.maxPacketSize(), s.version) if err != nil { if err == errNothingToPack { return nil } return err } s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, buf.Len(), false) s.registerPackedShortHeaderPacket(p, ecn, now) s.sendQueue.Send(buf, 0, ecn) return nil } func (s *connection) sendProbePacket(encLevel protocol.EncryptionLevel, now time.Time) error { // Queue probe packets until we actually send out a packet, // or until there are no more packets to queue. var packet *coalescedPacket for { if wasQueued := s.sentPacketHandler.QueueProbePacket(encLevel); !wasQueued { break } var err error packet, err = s.packer.MaybePackProbePacket(encLevel, s.maxPacketSize(), s.version) if err != nil { return err } if packet != nil { break } } if packet == nil { s.retransmissionQueue.AddPing(encLevel) var err error packet, err = s.packer.MaybePackProbePacket(encLevel, s.maxPacketSize(), s.version) if err != nil { return err } } if packet == nil || (len(packet.longHdrPackets) == 0 && packet.shortHdrPacket == nil) { return fmt.Errorf("connection BUG: couldn't pack %s probe packet", encLevel) } return s.sendPackedCoalescedPacket(packet, s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()), now) } // appendOneShortHeaderPacket appends a new packet to the given packetBuffer. // If there was nothing to pack, the returned size is 0. func (s *connection) appendOneShortHeaderPacket(buf *packetBuffer, maxSize protocol.ByteCount, ecn protocol.ECN, now time.Time) (protocol.ByteCount, error) { startLen := buf.Len() p, err := s.packer.AppendPacket(buf, maxSize, s.version) if err != nil { return 0, err } size := buf.Len() - startLen s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, size, false) s.registerPackedShortHeaderPacket(p, ecn, now) return size, nil } func (s *connection) registerPackedShortHeaderPacket(p shortHeaderPacket, ecn protocol.ECN, now time.Time) { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && (len(p.StreamFrames) > 0 || ackhandler.HasAckElicitingFrames(p.Frames)) { s.firstAckElicitingPacketAfterIdleSentTime = now } largestAcked := protocol.InvalidPacketNumber if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) s.connIDManager.SentPacket() } func (s *connection) sendPackedCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN, now time.Time) error { s.logCoalescedPacket(packet, ecn) for _, p := range packet.longHdrPackets { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { s.firstAckElicitingPacketAfterIdleSentTime = now } largestAcked := protocol.InvalidPacketNumber if p.ack != nil { largestAcked = p.ack.LargestAcked() } s.sentPacketHandler.SentPacket(now, p.header.PacketNumber, largestAcked, p.streamFrames, p.frames, p.EncryptionLevel(), ecn, p.length, false) if s.perspective == protocol.PerspectiveClient && p.EncryptionLevel() == protocol.EncryptionHandshake && !s.droppedInitialKeys { // On the client side, Initial keys are dropped as soon as the first Handshake packet is sent. // See Section 4.9.1 of RFC 9001. if err := s.dropEncryptionLevel(protocol.EncryptionInitial); err != nil { return err } } } if p := packet.shortHdrPacket; p != nil { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { s.firstAckElicitingPacketAfterIdleSentTime = now } largestAcked := protocol.InvalidPacketNumber if p.Ack != nil { largestAcked = p.Ack.LargestAcked() } s.sentPacketHandler.SentPacket(now, p.PacketNumber, largestAcked, p.StreamFrames, p.Frames, protocol.Encryption1RTT, ecn, p.Length, p.IsPathMTUProbePacket) } s.connIDManager.SentPacket() s.sendQueue.Send(packet.buffer, 0, ecn) return nil } func (s *connection) sendConnectionClose(e error) ([]byte, error) { var packet *coalescedPacket var err error var transportErr *qerr.TransportError var applicationErr *qerr.ApplicationError if errors.As(e, &transportErr) { packet, err = s.packer.PackConnectionClose(transportErr, s.maxPacketSize(), s.version) } else if errors.As(e, &applicationErr) { packet, err = s.packer.PackApplicationClose(applicationErr, s.maxPacketSize(), s.version) } else { packet, err = s.packer.PackConnectionClose(&qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: fmt.Sprintf("connection BUG: unspecified error type (msg: %s)", e.Error()), }, s.maxPacketSize(), s.version) } if err != nil { return nil, err } ecn := s.sentPacketHandler.ECNMode(packet.IsOnlyShortHeaderPacket()) s.logCoalescedPacket(packet, ecn) return packet.buffer.Data, s.conn.Write(packet.buffer.Data, 0, ecn) } func (s *connection) maxPacketSize() protocol.ByteCount { if s.mtuDiscoverer == nil { // Use the configured packet size on the client side. // If the server sends a max_udp_payload_size that's smaller than this size, we can ignore this: // Apparently the server still processed the (fully padded) Initial packet anyway. if s.perspective == protocol.PerspectiveClient { return protocol.ByteCount(s.config.InitialPacketSize) } // On the server side, there's no downside to using 1200 bytes until we received the client's transport // parameters: // * If the first packet didn't contain the entire ClientHello, all we can do is ACK that packet. We don't // need a lot of bytes for that. // * If it did, we will have processed the transport parameters and initialized the MTU discoverer. return protocol.MinInitialPacketSize } return s.mtuDiscoverer.CurrentSize() } // AcceptStream returns the next stream openend by the peer func (s *connection) AcceptStream(ctx context.Context) (Stream, error) { return s.streamsMap.AcceptStream(ctx) } func (s *connection) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { return s.streamsMap.AcceptUniStream(ctx) } // OpenStream opens a stream func (s *connection) OpenStream() (Stream, error) { return s.streamsMap.OpenStream() } func (s *connection) OpenStreamSync(ctx context.Context) (Stream, error) { return s.streamsMap.OpenStreamSync(ctx) } func (s *connection) OpenUniStream() (SendStream, error) { return s.streamsMap.OpenUniStream() } func (s *connection) OpenUniStreamSync(ctx context.Context) (SendStream, error) { return s.streamsMap.OpenUniStreamSync(ctx) } func (s *connection) newFlowController(id protocol.StreamID) flowcontrol.StreamFlowController { initialSendWindow := s.peerParams.InitialMaxStreamDataUni if id.Type() == protocol.StreamTypeBidi { if id.InitiatedBy() == s.perspective { initialSendWindow = s.peerParams.InitialMaxStreamDataBidiRemote } else { initialSendWindow = s.peerParams.InitialMaxStreamDataBidiLocal } } return flowcontrol.NewStreamFlowController( id, s.connFlowController, protocol.ByteCount(s.config.InitialStreamReceiveWindow), protocol.ByteCount(s.config.MaxStreamReceiveWindow), initialSendWindow, s.rttStats, s.logger, ) } // scheduleSending signals that we have data for sending func (s *connection) scheduleSending() { select { case s.sendingScheduled <- struct{}{}: default: } } // tryQueueingUndecryptablePacket queues a packet for which we're missing the decryption keys. // The logging.PacketType is only used for logging purposes. func (s *connection) tryQueueingUndecryptablePacket(p receivedPacket, pt logging.PacketType) { if s.handshakeComplete { panic("shouldn't queue undecryptable packets after handshake completion") } if len(s.undecryptablePackets)+1 > protocol.MaxUndecryptablePackets { if s.tracer != nil && s.tracer.DroppedPacket != nil { s.tracer.DroppedPacket(pt, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropDOSPrevention) } s.logger.Infof("Dropping undecryptable packet (%d bytes). Undecryptable packet queue full.", p.Size()) return } s.logger.Infof("Queueing packet (%d bytes) for later decryption", p.Size()) if s.tracer != nil && s.tracer.BufferedPacket != nil { s.tracer.BufferedPacket(pt, p.Size()) } s.undecryptablePackets = append(s.undecryptablePackets, p) } func (s *connection) queueControlFrame(f wire.Frame) { s.framer.QueueControlFrame(f) s.scheduleSending() } func (s *connection) onHasStreamData(id protocol.StreamID, str sendStreamI) { s.framer.AddActiveStream(id, str) s.scheduleSending() } func (s *connection) onHasStreamControlFrame(id protocol.StreamID, str streamControlFrameGetter) { s.framer.AddStreamWithControlFrames(id, str) s.scheduleSending() } func (s *connection) onStreamCompleted(id protocol.StreamID) { if err := s.streamsMap.DeleteStream(id); err != nil { s.closeLocal(err) } s.framer.RemoveActiveStream(id) } func (s *connection) onMTUIncreased(mtu protocol.ByteCount) { s.maxPayloadSizeEstimate.Store(uint32(estimateMaxPayloadSize(mtu))) s.sentPacketHandler.SetMaxDatagramSize(mtu) } func (s *connection) SendDatagram(p []byte) error { if !s.supportsDatagrams() { return errors.New("datagram support disabled") } f := &wire.DatagramFrame{DataLenPresent: true} // The payload size estimate is conservative. // Under many circumstances we could send a few more bytes. maxDataLen := min( f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version), protocol.ByteCount(s.maxPayloadSizeEstimate.Load()), ) if protocol.ByteCount(len(p)) > maxDataLen { return &DatagramTooLargeError{MaxDatagramPayloadSize: int64(maxDataLen)} } f.Data = make([]byte, len(p)) copy(f.Data, p) return s.datagramQueue.Add(f) } func (s *connection) ReceiveDatagram(ctx context.Context) ([]byte, error) { if !s.config.EnableDatagrams { return nil, errors.New("datagram support disabled") } return s.datagramQueue.Receive(ctx) } func (s *connection) LocalAddr() net.Addr { return s.conn.LocalAddr() } func (s *connection) RemoteAddr() net.Addr { return s.conn.RemoteAddr() } func (s *connection) GetVersion() protocol.Version { return s.version } func (s *connection) NextConnection(ctx context.Context) (Connection, error) { // The handshake might fail after the server rejected 0-RTT. // This could happen if the Finished message is malformed or never received. select { case <-ctx.Done(): return nil, context.Cause(ctx) case <-s.Context().Done(): case <-s.HandshakeComplete(): s.streamsMap.UseResetMaps() } return s, nil } // estimateMaxPayloadSize estimates the maximum payload size for short header packets. // It is not very sophisticated: it just subtracts the size of header (assuming the maximum // connection ID length), and the size of the encryption tag. func estimateMaxPayloadSize(mtu protocol.ByteCount) protocol.ByteCount { return mtu - 1 /* type byte */ - 20 /* maximum connection ID length */ - 16 /* tag size */ } golang-github-lucas-clemente-quic-go-0.46.0/connection_logging.go000066400000000000000000000116721465664453100250120ustar00rootroot00000000000000package quic import ( "slices" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" ) // ConvertFrame converts a wire.Frame into a logging.Frame. // This makes it possible for external packages to access the frames. // Furthermore, it removes the data slices from CRYPTO and STREAM frames. func toLoggingFrame(frame wire.Frame) logging.Frame { switch f := frame.(type) { case *wire.AckFrame: // We use a pool for ACK frames. // Implementations of the tracer interface may hold on to frames, so we need to make a copy here. return toLoggingAckFrame(f) case *wire.CryptoFrame: return &logging.CryptoFrame{ Offset: f.Offset, Length: protocol.ByteCount(len(f.Data)), } case *wire.StreamFrame: return &logging.StreamFrame{ StreamID: f.StreamID, Offset: f.Offset, Length: f.DataLen(), Fin: f.Fin, } case *wire.DatagramFrame: return &logging.DatagramFrame{ Length: logging.ByteCount(len(f.Data)), } default: return logging.Frame(frame) } } func toLoggingAckFrame(f *wire.AckFrame) *logging.AckFrame { ack := &logging.AckFrame{ AckRanges: slices.Clone(f.AckRanges), DelayTime: f.DelayTime, ECNCE: f.ECNCE, ECT0: f.ECT0, ECT1: f.ECT1, } return ack } func (s *connection) logLongHeaderPacket(p *longHeaderPacket, ecn protocol.ECN) { // quic-go logging if s.logger.Debug() { p.header.Log(s.logger) if p.ack != nil { wire.LogFrame(s.logger, p.ack, true) } for _, frame := range p.frames { wire.LogFrame(s.logger, frame.Frame, true) } for _, frame := range p.streamFrames { wire.LogFrame(s.logger, frame.Frame, true) } } // tracing if s.tracer != nil && s.tracer.SentLongHeaderPacket != nil { frames := make([]logging.Frame, 0, len(p.frames)) for _, f := range p.frames { frames = append(frames, toLoggingFrame(f.Frame)) } for _, f := range p.streamFrames { frames = append(frames, toLoggingFrame(f.Frame)) } var ack *logging.AckFrame if p.ack != nil { ack = toLoggingAckFrame(p.ack) } s.tracer.SentLongHeaderPacket(p.header, p.length, ecn, ack, frames) } } func (s *connection) logShortHeaderPacket( destConnID protocol.ConnectionID, ackFrame *wire.AckFrame, frames []ackhandler.Frame, streamFrames []ackhandler.StreamFrame, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit, ecn protocol.ECN, size protocol.ByteCount, isCoalesced bool, ) { if s.logger.Debug() && !isCoalesced { s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)", pn, size, s.logID, ecn) } // quic-go logging if s.logger.Debug() { wire.LogShortHeader(s.logger, destConnID, pn, pnLen, kp) if ackFrame != nil { wire.LogFrame(s.logger, ackFrame, true) } for _, f := range frames { wire.LogFrame(s.logger, f.Frame, true) } for _, f := range streamFrames { wire.LogFrame(s.logger, f.Frame, true) } } // tracing if s.tracer != nil && s.tracer.SentShortHeaderPacket != nil { fs := make([]logging.Frame, 0, len(frames)+len(streamFrames)) for _, f := range frames { fs = append(fs, toLoggingFrame(f.Frame)) } for _, f := range streamFrames { fs = append(fs, toLoggingFrame(f.Frame)) } var ack *logging.AckFrame if ackFrame != nil { ack = toLoggingAckFrame(ackFrame) } s.tracer.SentShortHeaderPacket( &logging.ShortHeader{ DestConnectionID: destConnID, PacketNumber: pn, PacketNumberLen: pnLen, KeyPhase: kp, }, size, ecn, ack, fs, ) } } func (s *connection) logCoalescedPacket(packet *coalescedPacket, ecn protocol.ECN) { if s.logger.Debug() { // There's a short period between dropping both Initial and Handshake keys and completion of the handshake, // during which we might call PackCoalescedPacket but just pack a short header packet. if len(packet.longHdrPackets) == 0 && packet.shortHdrPacket != nil { s.logShortHeaderPacket( packet.shortHdrPacket.DestConnID, packet.shortHdrPacket.Ack, packet.shortHdrPacket.Frames, packet.shortHdrPacket.StreamFrames, packet.shortHdrPacket.PacketNumber, packet.shortHdrPacket.PacketNumberLen, packet.shortHdrPacket.KeyPhase, ecn, packet.shortHdrPacket.Length, false, ) return } if len(packet.longHdrPackets) > 1 { s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.longHdrPackets), packet.buffer.Len(), s.logID) } else { s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.longHdrPackets[0].header.PacketNumber, packet.buffer.Len(), s.logID, packet.longHdrPackets[0].EncryptionLevel()) } } for _, p := range packet.longHdrPackets { s.logLongHeaderPacket(p, ecn) } if p := packet.shortHdrPacket; p != nil { s.logShortHeaderPacket(p.DestConnID, p.Ack, p.Frames, p.StreamFrames, p.PacketNumber, p.PacketNumberLen, p.KeyPhase, ecn, p.Length, true) } } golang-github-lucas-clemente-quic-go-0.46.0/connection_logging_test.go000066400000000000000000000031031465664453100260370ustar00rootroot00000000000000package quic import ( "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("CRYPTO frame", func() { It("converts CRYPTO frames", func() { f := toLoggingFrame(&wire.CryptoFrame{ Offset: 1234, Data: []byte("foobar"), }) Expect(f).To(BeAssignableToTypeOf(&logging.CryptoFrame{})) cf := f.(*logging.CryptoFrame) Expect(cf.Offset).To(Equal(logging.ByteCount(1234))) Expect(cf.Length).To(Equal(logging.ByteCount(6))) }) It("converts STREAM frames", func() { f := toLoggingFrame(&wire.StreamFrame{ StreamID: 42, Offset: 1234, Data: []byte("foo"), Fin: true, }) Expect(f).To(BeAssignableToTypeOf(&logging.StreamFrame{})) sf := f.(*logging.StreamFrame) Expect(sf.StreamID).To(Equal(logging.StreamID(42))) Expect(sf.Offset).To(Equal(logging.ByteCount(1234))) Expect(sf.Length).To(Equal(logging.ByteCount(3))) Expect(sf.Fin).To(BeTrue()) }) It("converts DATAGRAM frames", func() { f := toLoggingFrame(&wire.DatagramFrame{Data: []byte("foobar")}) Expect(f).To(BeAssignableToTypeOf(&logging.DatagramFrame{})) df := f.(*logging.DatagramFrame) Expect(df.Length).To(Equal(logging.ByteCount(6))) }) It("converts other frames", func() { f := toLoggingFrame(&wire.MaxDataFrame{MaximumData: 1234}) Expect(f).To(BeAssignableToTypeOf(&logging.MaxDataFrame{})) Expect(f).ToNot(BeAssignableToTypeOf(&logging.MaxStreamDataFrame{})) mdf := f.(*logging.MaxDataFrame) Expect(mdf.MaximumData).To(Equal(logging.ByteCount(1234))) }) }) golang-github-lucas-clemente-quic-go-0.46.0/connection_test.go000066400000000000000000004215321465664453100243430ustar00rootroot00000000000000package quic import ( "bytes" "context" "crypto/rand" "crypto/tls" "errors" "fmt" "io" "net" "net/netip" "runtime/pprof" "strings" "time" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/mocks" mockackhandler "github.com/quic-go/quic-go/internal/mocks/ackhandler" mocklogging "github.com/quic-go/quic-go/internal/mocks/logging" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go/testutils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func areConnsRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "quic-go.(*connection).run") } var _ = Describe("Connection", func() { var ( conn *connection connRunner *MockConnRunner mconn *MockSendConn streamManager *MockStreamManager packer *MockPacker cryptoSetup *mocks.MockCryptoSetup tracer *mocklogging.MockConnectionTracer capabilities connCapabilities ) remoteAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} localAddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 7331} srcConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}) destConnID := protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}) clientDestConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) getCoalescedPacket := func(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) *coalescedPacket { buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("foobar")...) packet := &coalescedPacket{buffer: buffer} if encLevel != protocol.Encryption1RTT { var typ protocol.PacketType switch encLevel { case protocol.EncryptionInitial: typ = protocol.PacketTypeInitial case protocol.EncryptionHandshake: typ = protocol.PacketTypeHandshake } packet.longHdrPackets = []*longHeaderPacket{{ header: &wire.ExtendedHeader{ Header: wire.Header{Type: typ}, PacketNumber: pn, }, length: 6, // foobar }} } else { packet.shortHdrPacket = &shortHeaderPacket{ PacketNumber: pn, Length: 6, } } return packet } expectReplaceWithClosed := func() { connRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()).Do(func(connIDs []protocol.ConnectionID, _ []byte) { Expect(connIDs).To(ContainElement(srcConnID)) if len(connIDs) > 1 { Expect(connIDs).To(ContainElement(clientDestConnID)) } }) } expectAppendPacket := func(packer *MockPacker, p shortHeaderPacket, b []byte) *MockPackerAppendPacketCall { return packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), Version1).DoAndReturn(func(buf *packetBuffer, _ protocol.ByteCount, _ protocol.Version) (shortHeaderPacket, error) { buf.Data = append(buf.Data, b...) return p, nil }) } enableGSO := func() { capabilities = connCapabilities{GSO: true} } BeforeEach(func() { Eventually(areConnsRunning).Should(BeFalse()) connRunner = NewMockConnRunner(mockCtrl) mconn = NewMockSendConn(mockCtrl) mconn.EXPECT().capabilities().DoAndReturn(func() connCapabilities { return capabilities }).AnyTimes() mconn.EXPECT().RemoteAddr().Return(remoteAddr).AnyTimes() mconn.EXPECT().LocalAddr().Return(localAddr).AnyTimes() tokenGenerator := handshake.NewTokenGenerator([32]byte{0xa, 0xb, 0xc}) var tr *logging.ConnectionTracer tr, tracer = mocklogging.NewMockConnectionTracer(mockCtrl) tracer.EXPECT().NegotiatedVersion(gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) tracer.EXPECT().SentTransportParameters(gomock.Any()) tracer.EXPECT().UpdatedKeyFromTLS(gomock.Any(), gomock.Any()).AnyTimes() tracer.EXPECT().UpdatedCongestionState(gomock.Any()) ctx, cancel := context.WithCancelCause(context.Background()) conn = newConnection( ctx, cancel, mconn, connRunner, protocol.ConnectionID{}, nil, clientDestConnID, destConnID, srcConnID, &protocol.DefaultConnectionIDGenerator{}, protocol.StatelessResetToken{}, populateConfig(&Config{DisablePathMTUDiscovery: true}), &tls.Config{}, tokenGenerator, false, tr, utils.DefaultLogger, protocol.Version1, ).(*connection) streamManager = NewMockStreamManager(mockCtrl) conn.streamsMap = streamManager packer = NewMockPacker(mockCtrl) conn.packer = packer cryptoSetup = mocks.NewMockCryptoSetup(mockCtrl) conn.cryptoStreamHandler = cryptoSetup conn.handshakeComplete = true conn.idleTimeout = time.Hour }) AfterEach(func() { Eventually(areConnsRunning).Should(BeFalse()) capabilities = connCapabilities{} }) Context("frame handling", func() { Context("handling STREAM frames", func() { It("passes STREAM frames to the stream", func() { f := &wire.StreamFrame{ StreamID: 5, Data: []byte{0xde, 0xca, 0xfb, 0xad}, } str := NewMockReceiveStreamI(mockCtrl) str.EXPECT().handleStreamFrame(f) streamManager.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(5)).Return(str, nil) Expect(conn.handleStreamFrame(f)).To(Succeed()) }) It("returns errors", func() { testErr := errors.New("test err") f := &wire.StreamFrame{ StreamID: 5, Data: []byte{0xde, 0xca, 0xfb, 0xad}, } str := NewMockReceiveStreamI(mockCtrl) str.EXPECT().handleStreamFrame(f).Return(testErr) streamManager.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(5)).Return(str, nil) Expect(conn.handleStreamFrame(f)).To(MatchError(testErr)) }) It("ignores STREAM frames for closed streams", func() { streamManager.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(5)).Return(nil, nil) // for closed streams, the streamManager returns nil Expect(conn.handleStreamFrame(&wire.StreamFrame{ StreamID: 5, Data: []byte("foobar"), })).To(Succeed()) }) }) Context("handling ACK frames", func() { It("informs the SentPacketHandler about ACKs", func() { f := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 3}}} sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().ReceivedAck(f, protocol.EncryptionHandshake, gomock.Any()) conn.sentPacketHandler = sph err := conn.handleAckFrame(f, protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) }) }) Context("handling RESET_STREAM frames", func() { It("closes the streams for writing", func() { f := &wire.ResetStreamFrame{ StreamID: 555, ErrorCode: 42, FinalSize: 0x1337, } str := NewMockReceiveStreamI(mockCtrl) streamManager.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(555)).Return(str, nil) str.EXPECT().handleResetStreamFrame(f) err := conn.handleResetStreamFrame(f) Expect(err).ToNot(HaveOccurred()) }) It("returns errors", func() { f := &wire.ResetStreamFrame{ StreamID: 7, FinalSize: 0x1337, } testErr := errors.New("flow control violation") str := NewMockReceiveStreamI(mockCtrl) streamManager.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(7)).Return(str, nil) str.EXPECT().handleResetStreamFrame(f).Return(testErr) err := conn.handleResetStreamFrame(f) Expect(err).To(MatchError(testErr)) }) It("ignores RESET_STREAM frames for closed streams", func() { streamManager.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(3)).Return(nil, nil) Expect(conn.handleFrame(&wire.ResetStreamFrame{ StreamID: 3, ErrorCode: 42, }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) }) }) Context("handling MAX_DATA and MAX_STREAM_DATA frames", func() { var connFC *mocks.MockConnectionFlowController BeforeEach(func() { connFC = mocks.NewMockConnectionFlowController(mockCtrl) conn.connFlowController = connFC }) It("updates the flow control window of a stream", func() { f := &wire.MaxStreamDataFrame{ StreamID: 12345, MaximumStreamData: 0x1337, } str := NewMockSendStreamI(mockCtrl) streamManager.EXPECT().GetOrOpenSendStream(protocol.StreamID(12345)).Return(str, nil) str.EXPECT().updateSendWindow(protocol.ByteCount(0x1337)) Expect(conn.handleMaxStreamDataFrame(f)).To(Succeed()) }) It("updates the flow control window of the connection", func() { offset := protocol.ByteCount(0x800000) connFC.EXPECT().UpdateSendWindow(offset) conn.handleMaxDataFrame(&wire.MaxDataFrame{MaximumData: offset}) }) It("ignores MAX_STREAM_DATA frames for a closed stream", func() { streamManager.EXPECT().GetOrOpenSendStream(protocol.StreamID(10)).Return(nil, nil) Expect(conn.handleFrame(&wire.MaxStreamDataFrame{ StreamID: 10, MaximumStreamData: 1337, }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) }) }) Context("handling MAX_STREAM_ID frames", func() { It("passes the frame to the streamsMap", func() { f := &wire.MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: 10, } streamManager.EXPECT().HandleMaxStreamsFrame(f) conn.handleMaxStreamsFrame(f) }) }) Context("handling STOP_SENDING frames", func() { It("passes the frame to the stream", func() { f := &wire.StopSendingFrame{ StreamID: 5, ErrorCode: 10, } str := NewMockSendStreamI(mockCtrl) streamManager.EXPECT().GetOrOpenSendStream(protocol.StreamID(5)).Return(str, nil) str.EXPECT().handleStopSendingFrame(f) err := conn.handleStopSendingFrame(f) Expect(err).ToNot(HaveOccurred()) }) It("ignores STOP_SENDING frames for a closed stream", func() { streamManager.EXPECT().GetOrOpenSendStream(protocol.StreamID(3)).Return(nil, nil) Expect(conn.handleFrame(&wire.StopSendingFrame{ StreamID: 3, ErrorCode: 1337, }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) }) }) It("handles NEW_CONNECTION_ID frames", func() { connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) Expect(conn.handleFrame(&wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: connID, }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) Expect(conn.connIDManager.queue.Back().Value.ConnectionID).To(Equal(connID)) }) It("handles PING frames", func() { err := conn.handleFrame(&wire.PingFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("rejects PATH_RESPONSE frames", func() { err := conn.handleFrame(&wire.PathResponseFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).To(MatchError("unexpected PATH_RESPONSE frame")) }) It("handles PATH_CHALLENGE frames", func() { data := [8]byte{1, 2, 3, 4, 5, 6, 7, 8} err := conn.handleFrame(&wire.PathChallengeFrame{Data: data}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).ToNot(HaveOccurred()) frames, _ := conn.framer.AppendControlFrames(nil, 1000, protocol.Version1) Expect(frames).To(Equal([]ackhandler.Frame{{Frame: &wire.PathResponseFrame{Data: data}}})) }) It("rejects NEW_TOKEN frames", func() { err := conn.handleNewTokenFrame(&wire.NewTokenFrame{}) Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.ProtocolViolation)) }) It("handles BLOCKED frames", func() { err := conn.handleFrame(&wire.DataBlockedFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("handles STREAM_BLOCKED frames", func() { err := conn.handleFrame(&wire.StreamDataBlockedFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("handles STREAMS_BLOCKED frames", func() { err := conn.handleFrame(&wire.StreamsBlockedFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("handles CONNECTION_CLOSE frames, with a transport error code", func() { expectedErr := &qerr.TransportError{ Remote: true, ErrorCode: qerr.StreamLimitError, ErrorMessage: "foobar", } streamManager.EXPECT().CloseWithError(expectedErr) connRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()).Do(func(connIDs []protocol.ConnectionID, _ []byte) { Expect(connIDs).To(ConsistOf(clientDestConnID, srcConnID)) }) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(expectedErr), tracer.EXPECT().Close(), ) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) Expect(conn.run()).To(MatchError(expectedErr)) }() Expect(conn.handleFrame(&wire.ConnectionCloseFrame{ ErrorCode: uint64(qerr.StreamLimitError), ReasonPhrase: "foobar", }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("handles CONNECTION_CLOSE frames, with an application error code", func() { testErr := &qerr.ApplicationError{ Remote: true, ErrorCode: 0x1337, ErrorMessage: "foobar", } streamManager.EXPECT().CloseWithError(testErr) connRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()).Do(func(connIDs []protocol.ConnectionID, _ []byte) { Expect(connIDs).To(ConsistOf(clientDestConnID, srcConnID)) }) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(testErr), tracer.EXPECT().Close(), ) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) Expect(conn.run()).To(MatchError(testErr)) }() ccf := &wire.ConnectionCloseFrame{ ErrorCode: 0x1337, ReasonPhrase: "foobar", IsApplicationError: true, } Expect(conn.handleFrame(ccf, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) Eventually(conn.Context().Done()).Should(BeClosed()) Expect(context.Cause(conn.Context())).To(MatchError(testErr)) }) It("errors on HANDSHAKE_DONE frames", func() { Expect(conn.handleHandshakeDoneFrame()).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received a HANDSHAKE_DONE frame", })) }) }) It("tells its versions", func() { conn.version = 4242 Expect(conn.GetVersion()).To(Equal(protocol.Version(4242))) }) Context("closing", func() { var ( runErr chan error expectedRunErr error ) BeforeEach(func() { runErr = make(chan error, 1) expectedRunErr = nil }) AfterEach(func() { if expectedRunErr != nil { Eventually(runErr).Should(Receive(MatchError(expectedRunErr))) } else { Eventually(runErr).Should(Receive()) } }) runConn := func() { go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) runErr <- conn.run() }() Eventually(areConnsRunning).Should(BeTrue()) } It("shuts down without error", func() { conn.handshakeComplete = true runConn() streamManager.EXPECT().CloseWithError(&qerr.ApplicationError{}) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("connection close")...) packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).DoAndReturn(func(e *qerr.ApplicationError, _ protocol.ByteCount, _ protocol.Version) (*coalescedPacket, error) { Expect(e.ErrorCode).To(BeEquivalentTo(qerr.NoError)) Expect(e.ErrorMessage).To(BeEmpty()) return &coalescedPacket{buffer: buffer}, nil }) mconn.EXPECT().Write([]byte("connection close"), gomock.Any(), gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { var appErr *ApplicationError Expect(errors.As(e, &appErr)).To(BeTrue()) Expect(appErr.Remote).To(BeFalse()) Expect(appErr.ErrorCode).To(BeZero()) }), tracer.EXPECT().Close(), ) conn.CloseWithError(0, "") Eventually(areConnsRunning).Should(BeFalse()) Expect(conn.Context().Done()).To(BeClosed()) }) It("only closes once", func() { runConn() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") conn.CloseWithError(0, "") Eventually(areConnsRunning).Should(BeFalse()) Expect(conn.Context().Done()).To(BeClosed()) }) It("closes with an error", func() { runConn() expectedErr := &qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: "test error", } streamManager.EXPECT().CloseWithError(expectedErr) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackApplicationClose(expectedErr, gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(expectedErr), tracer.EXPECT().Close(), ) conn.CloseWithError(0x1337, "test error") Eventually(areConnsRunning).Should(BeFalse()) Expect(conn.Context().Done()).To(BeClosed()) Expect(context.Cause(conn.Context())).To(MatchError(expectedErr)) }) It("includes the frame type in transport-level close frames", func() { runConn() expectedErr := &qerr.TransportError{ ErrorCode: 0x1337, FrameType: 0x42, ErrorMessage: "test error", } streamManager.EXPECT().CloseWithError(expectedErr) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(expectedErr, gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(expectedErr), tracer.EXPECT().Close(), ) conn.closeLocal(expectedErr) Eventually(areConnsRunning).Should(BeFalse()) Expect(conn.Context().Done()).To(BeClosed()) }) It("destroys the connection", func() { runConn() testErr := errors.New("close") streamManager.EXPECT().CloseWithError(gomock.Any()) connRunner.EXPECT().Remove(gomock.Any()).AnyTimes() cryptoSetup.EXPECT().Close() // don't EXPECT any calls to mconn.Write() gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { var transportErr *TransportError Expect(errors.As(e, &transportErr)).To(BeTrue()) Expect(transportErr.Remote).To(BeFalse()) Expect(transportErr.ErrorCode).To(Equal(qerr.InternalError)) }), tracer.EXPECT().Close(), ) conn.destroy(testErr) Eventually(areConnsRunning).Should(BeFalse()) expectedRunErr = &qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: testErr.Error(), } }) It("doesn't send any more packets after receiving a CONNECTION_CLOSE", func() { unpacker := NewMockUnpacker(mockCtrl) conn.handshakeConfirmed = true conn.unpacker = unpacker runConn() cryptoSetup.EXPECT().Close() streamManager.EXPECT().CloseWithError(gomock.Any()) connRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()).AnyTimes() b, err := wire.AppendShortHeader(nil, srcConnID, 42, protocol.PacketNumberLen2, protocol.KeyPhaseOne) Expect(err).ToNot(HaveOccurred()) unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(time.Time, []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { b, err := (&wire.ConnectionCloseFrame{ErrorCode: uint64(qerr.StreamLimitError)}).Append(nil, conn.version) Expect(err).ToNot(HaveOccurred()) return 3, protocol.PacketNumberLen2, protocol.KeyPhaseOne, b, nil }) gomock.InOrder( tracer.EXPECT().ReceivedShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), tracer.EXPECT().ClosedConnection(gomock.Any()), tracer.EXPECT().Close(), ) // don't EXPECT any calls to packer.PackPacket() conn.handlePacket(receivedPacket{ rcvTime: time.Now(), remoteAddr: &net.UDPAddr{}, buffer: getPacketBuffer(), data: b, }) // Consistently(pack).ShouldNot(Receive()) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("closes when the sendQueue encounters an error", func() { conn.handshakeConfirmed = true sconn := NewMockSendConn(mockCtrl) sconn.EXPECT().capabilities().AnyTimes() sconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).Return(io.ErrClosedPipe).AnyTimes() conn.sendQueue = newSendQueue(sconn) sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().Return(time.Now().Add(time.Hour)).AnyTimes() sph.EXPECT().ECNMode(true).Return(protocol.ECT1).AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() // only expect a single SentPacket() call sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() streamManager.EXPECT().CloseWithError(gomock.Any()) connRunner.EXPECT().Remove(gomock.Any()).AnyTimes() cryptoSetup.EXPECT().Close() conn.sentPacketHandler = sph expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 1}, []byte("foobar")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack).AnyTimes() runConn() conn.queueControlFrame(&wire.PingFrame{}) conn.scheduleSending() Eventually(conn.Context().Done()).Should(BeClosed()) }) It("closes due to a stateless reset", func() { token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} runConn() gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { var srErr *StatelessResetError Expect(errors.As(e, &srErr)).To(BeTrue()) Expect(srErr.Token).To(Equal(token)) }), tracer.EXPECT().Close(), ) streamManager.EXPECT().CloseWithError(gomock.Any()) connRunner.EXPECT().Remove(gomock.Any()).AnyTimes() cryptoSetup.EXPECT().Close() conn.destroy(&StatelessResetError{Token: token}) }) }) Context("receiving packets", func() { var unpacker *MockUnpacker BeforeEach(func() { unpacker = NewMockUnpacker(mockCtrl) conn.unpacker = unpacker }) getShortHeaderPacket := func(connID protocol.ConnectionID, pn protocol.PacketNumber, data []byte) receivedPacket { b, err := wire.AppendShortHeader(nil, connID, pn, protocol.PacketNumberLen2, protocol.KeyPhaseOne) Expect(err).ToNot(HaveOccurred()) return receivedPacket{ data: append(b, data...), buffer: getPacketBuffer(), rcvTime: time.Now(), } } getLongHeaderPacket := func(extHdr *wire.ExtendedHeader, data []byte) receivedPacket { b, err := extHdr.Append(nil, conn.version) Expect(err).ToNot(HaveOccurred()) return receivedPacket{ data: append(b, data...), buffer: getPacketBuffer(), rcvTime: time.Now(), } } It("drops Retry packets", func() { p := getLongHeaderPacket(&wire.ExtendedHeader{Header: wire.Header{ Type: protocol.PacketTypeRetry, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Version: conn.version, Token: []byte("foobar"), }}, make([]byte, 16) /* Retry integrity tag */) tracer.EXPECT().DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("drops Version Negotiation packets", func() { b := wire.ComposeVersionNegotiation( protocol.ArbitraryLenConnectionID(srcConnID.Bytes()), protocol.ArbitraryLenConnectionID(destConnID.Bytes()), conn.config.Versions, ) tracer.EXPECT().DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, protocol.ByteCount(len(b)), logging.PacketDropUnexpectedPacket) Expect(conn.handlePacketImpl(receivedPacket{ data: b, buffer: getPacketBuffer(), })).To(BeFalse()) }) It("drops packets for which header decryption fails", func() { p := getLongHeaderPacket(&wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen2, }, nil) p.data[0] ^= 0x40 // unset the QUIC bit tracer.EXPECT().DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("drops packets for which the version is unsupported", func() { p := getLongHeaderPacket(&wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, Version: conn.version + 1, }, PacketNumberLen: protocol.PacketNumberLen2, }, nil) tracer.EXPECT().DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnsupportedVersion) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("drops packets with an unsupported version", func() { origSupportedVersions := make([]protocol.Version, len(protocol.SupportedVersions)) copy(origSupportedVersions, protocol.SupportedVersions) defer func() { protocol.SupportedVersions = origSupportedVersions }() protocol.SupportedVersions = append(protocol.SupportedVersions, conn.version+1) p := getLongHeaderPacket(&wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Version: conn.version + 1, }, PacketNumberLen: protocol.PacketNumberLen2, }, nil) tracer.EXPECT().DroppedPacket(logging.PacketTypeHandshake, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedVersion) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("informs the ReceivedPacketHandler about non-ack-eliciting packets", func() { hdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, DestConnectionID: srcConnID, Version: protocol.Version1, Length: 1, }, PacketNumber: 0x37, PacketNumberLen: protocol.PacketNumberLen1, } unpackedHdr := *hdr unpackedHdr.PacketNumber = 0x1337 packet := getLongHeaderPacket(hdr, nil) packet.ecn = protocol.ECNCE rcvTime := time.Now().Add(-10 * time.Second) unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return(&unpackedPacket{ encryptionLevel: protocol.EncryptionInitial, hdr: &unpackedHdr, data: []byte{0}, // one PADDING frame }, nil) rph := mockackhandler.NewMockReceivedPacketHandler(mockCtrl) gomock.InOrder( rph.EXPECT().IsPotentiallyDuplicate(protocol.PacketNumber(0x1337), protocol.EncryptionInitial), rph.EXPECT().ReceivedPacket(protocol.PacketNumber(0x1337), protocol.ECNCE, protocol.EncryptionInitial, rcvTime, false), ) conn.receivedPacketHandler = rph packet.rcvTime = rcvTime tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), gomock.Any(), logging.ECNCE, []logging.Frame{}) Expect(conn.handlePacketImpl(packet)).To(BeTrue()) }) It("informs the ReceivedPacketHandler about ack-eliciting packets", func() { rcvTime := time.Now().Add(-10 * time.Second) b, err := (&wire.PingFrame{}).Append(nil, conn.version) Expect(err).ToNot(HaveOccurred()) packet := getShortHeaderPacket(srcConnID, 0x37, nil) packet.ecn = protocol.ECT1 unpacker.EXPECT().UnpackShortHeader(rcvTime, gomock.Any()).Return(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2, protocol.KeyPhaseZero, b, nil) rph := mockackhandler.NewMockReceivedPacketHandler(mockCtrl) gomock.InOrder( rph.EXPECT().IsPotentiallyDuplicate(protocol.PacketNumber(0x1337), protocol.Encryption1RTT), rph.EXPECT().ReceivedPacket(protocol.PacketNumber(0x1337), protocol.ECT1, protocol.Encryption1RTT, rcvTime, true), ) conn.receivedPacketHandler = rph packet.rcvTime = rcvTime tracer.EXPECT().ReceivedShortHeaderPacket( &logging.ShortHeader{DestConnectionID: srcConnID, PacketNumber: 0x1337, PacketNumberLen: 2, KeyPhase: protocol.KeyPhaseZero}, protocol.ByteCount(len(packet.data)), logging.ECT1, []logging.Frame{&logging.PingFrame{}}, ) Expect(conn.handlePacketImpl(packet)).To(BeTrue()) }) It("drops duplicate packets", func() { packet := getShortHeaderPacket(srcConnID, 0x37, nil) unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2, protocol.KeyPhaseOne, []byte("foobar"), nil) rph := mockackhandler.NewMockReceivedPacketHandler(mockCtrl) rph.EXPECT().IsPotentiallyDuplicate(protocol.PacketNumber(0x1337), protocol.Encryption1RTT).Return(true) conn.receivedPacketHandler = rph tracer.EXPECT().DroppedPacket(logging.PacketType1RTT, protocol.PacketNumber(0x1337), protocol.ByteCount(len(packet.data)), logging.PacketDropDuplicate) Expect(conn.handlePacketImpl(packet)).To(BeFalse()) }) It("drops a packet when unpacking fails", func() { unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() expectReplaceWithClosed() p := getLongHeaderPacket(&wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: srcConnID, Version: conn.version, Length: 2 + 6, }, PacketNumber: 0x1337, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar")) tracer.EXPECT().DroppedPacket(logging.PacketTypeHandshake, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropPayloadDecryptError) conn.handlePacket(p) Consistently(conn.Context().Done()).ShouldNot(BeClosed()) // make the go routine return tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) conn.closeLocal(errors.New("close")) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("processes multiple received packets before sending one", func() { conn.creationTime = time.Now() var pn protocol.PacketNumber unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { pn++ return pn, protocol.PacketNumberLen2, protocol.KeyPhaseZero, []byte{0} /* PADDING frame */, nil }).Times(3) tracer.EXPECT().ReceivedShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version) // only expect a single call for i := 0; i < 3; i++ { conn.handlePacket(getShortHeaderPacket(srcConnID, 0x1337+protocol.PacketNumber(i), []byte("foobar"))) } go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() Consistently(conn.Context().Done()).ShouldNot(BeClosed()) // make the go routine return streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) conn.closeLocal(errors.New("close")) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("doesn't processes multiple received packets before sending one before handshake completion", func() { conn.handshakeComplete = false conn.creationTime = time.Now() var pn protocol.PacketNumber unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { pn++ return pn, protocol.PacketNumberLen4, protocol.KeyPhaseZero, []byte{0} /* PADDING frame */, nil }).Times(3) tracer.EXPECT().ReceivedShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).Times(3) for i := 0; i < 3; i++ { conn.handlePacket(getShortHeaderPacket(srcConnID, 0x1337+protocol.PacketNumber(i), []byte("foobar"))) } go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() Consistently(conn.Context().Done()).ShouldNot(BeClosed()) // make the go routine return streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) conn.closeLocal(errors.New("close")) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("closes the connection when unpacking fails because the reserved bits were incorrect", func() { unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(0), protocol.PacketNumberLen(0), protocol.KeyPhaseBit(0), nil, wire.ErrInvalidReservedBits) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) err := conn.run() Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.ProtocolViolation)) close(done) }() expectReplaceWithClosed() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) packet := getShortHeaderPacket(srcConnID, 0x42, nil) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.handlePacket(packet) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("ignores packets when unpacking the header fails", func() { testErr := &headerParseError{errors.New("test error")} unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(0), protocol.PacketNumberLen(0), protocol.KeyPhaseBit(0), nil, testErr) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() runErr := make(chan error) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) runErr <- conn.run() }() expectReplaceWithClosed() tracer.EXPECT().DroppedPacket(logging.PacketType1RTT, protocol.InvalidPacketNumber, gomock.Any(), logging.PacketDropHeaderParseError) conn.handlePacket(getShortHeaderPacket(srcConnID, 0x42, nil)) Consistently(runErr).ShouldNot(Receive()) // make the go routine return packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("closes the connection when unpacking fails because of an error other than a decryption error", func() { unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(0), protocol.PacketNumberLen(0), protocol.KeyPhaseBit(0), nil, &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) err := conn.run() Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.ConnectionIDLimitError)) close(done) }() expectReplaceWithClosed() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.handlePacket(getShortHeaderPacket(srcConnID, 0x42, nil)) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("ignores packets with a different source connection ID", func() { hdr1 := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Length: 1, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 1, } hdr2 := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), Length: 1, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 2, } Expect(srcConnID).ToNot(Equal(hdr2.SrcConnectionID)) // Send one packet, which might change the connection ID. // only EXPECT one call to the unpacker unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return(&unpackedPacket{ encryptionLevel: protocol.Encryption1RTT, hdr: hdr1, data: []byte{0}, // one PADDING frame }, nil) p1 := getLongHeaderPacket(hdr1, nil) tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), protocol.ByteCount(len(p1.data)), gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(p1)).To(BeTrue()) // The next packet has to be ignored, since the source connection ID doesn't match. p2 := getLongHeaderPacket(hdr2, nil) tracer.EXPECT().DroppedPacket(logging.PacketTypeInitial, protocol.InvalidPacketNumber, protocol.ByteCount(len(p2.data)), logging.PacketDropUnknownConnectionID) Expect(conn.handlePacketImpl(p2)).To(BeFalse()) }) It("queues undecryptable packets", func() { conn.handshakeComplete = false hdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Length: 1, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 1, } unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return(nil, handshake.ErrKeysNotYetAvailable) packet := getLongHeaderPacket(hdr, nil) tracer.EXPECT().BufferedPacket(logging.PacketTypeHandshake, packet.Size()) Expect(conn.handlePacketImpl(packet)).To(BeFalse()) Expect(conn.undecryptablePackets).To(Equal([]receivedPacket{packet})) }) Context("updating the remote address", func() { It("doesn't support connection migration", func() { unpacker.EXPECT().UnpackShortHeader(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(10), protocol.PacketNumberLen2, protocol.KeyPhaseZero, []byte{0} /* one PADDING frame */, nil) packet := getShortHeaderPacket(srcConnID, 0x42, nil) packet.remoteAddr = &net.IPAddr{IP: net.IPv4(192, 168, 0, 100)} tracer.EXPECT().ReceivedShortHeaderPacket(gomock.Any(), protocol.ByteCount(len(packet.data)), gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(packet)).To(BeTrue()) }) }) Context("coalesced packets", func() { BeforeEach(func() { tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) }) getPacketWithLength := func(connID protocol.ConnectionID, length protocol.ByteCount) (int /* header length */, receivedPacket) { hdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: connID, SrcConnectionID: destConnID, Version: protocol.Version1, Length: length, }, PacketNumberLen: protocol.PacketNumberLen3, } hdrLen := hdr.GetLength(conn.version) b := make([]byte, 1) rand.Read(b) packet := getLongHeaderPacket(hdr, bytes.Repeat(b, int(length)-3)) return int(hdrLen), packet } It("cuts packets to the right length", func() { hdrLen, packet := getPacketWithLength(srcConnID, 456) unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen + 456 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{Header: wire.Header{}}, }, nil }) cryptoSetup.EXPECT().DiscardInitialKeys() tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionInitial) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), protocol.ByteCount(len(packet.data)), gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(packet)).To(BeTrue()) }) It("handles coalesced packets", func() { hdrLen1, packet1 := getPacketWithLength(srcConnID, 456) packet1.ecn = protocol.ECT1 unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen1 + 456 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{ PacketNumber: 1, Header: wire.Header{SrcConnectionID: destConnID}, }, }, nil }) hdrLen2, packet2 := getPacketWithLength(srcConnID, 123) unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen2 + 123 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{ PacketNumber: 2, Header: wire.Header{SrcConnectionID: destConnID}, }, }, nil }) tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionInitial).AnyTimes() cryptoSetup.EXPECT().DiscardInitialKeys().AnyTimes() gomock.InOrder( tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), protocol.ByteCount(len(packet1.data)), logging.ECT1, gomock.Any()), tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), protocol.ByteCount(len(packet2.data)), logging.ECT1, gomock.Any()), ) packet1.data = append(packet1.data, packet2.data...) Expect(conn.handlePacketImpl(packet1)).To(BeTrue()) }) It("works with undecryptable packets", func() { conn.handshakeComplete = false hdrLen1, packet1 := getPacketWithLength(srcConnID, 456) hdrLen2, packet2 := getPacketWithLength(srcConnID, 123) gomock.InOrder( unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return(nil, handshake.ErrKeysNotYetAvailable), unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen2 + 123 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{Header: wire.Header{}}, }, nil }), ) tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionInitial).AnyTimes() cryptoSetup.EXPECT().DiscardInitialKeys().AnyTimes() gomock.InOrder( tracer.EXPECT().BufferedPacket(gomock.Any(), protocol.ByteCount(len(packet1.data))), tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), protocol.ByteCount(len(packet2.data)), gomock.Any(), gomock.Any()), ) packet1.data = append(packet1.data, packet2.data...) Expect(conn.handlePacketImpl(packet1)).To(BeTrue()) Expect(conn.undecryptablePackets).To(HaveLen(1)) Expect(conn.undecryptablePackets[0].data).To(HaveLen(hdrLen1 + 456 - 3)) }) It("ignores coalesced packet parts if the destination connection IDs don't match", func() { wrongConnID := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) Expect(srcConnID).ToNot(Equal(wrongConnID)) hdrLen1, packet1 := getPacketWithLength(srcConnID, 456) unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen1 + 456 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{Header: wire.Header{}}, }, nil }) _, packet2 := getPacketWithLength(wrongConnID, 123) tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionInitial).AnyTimes() cryptoSetup.EXPECT().DiscardInitialKeys().AnyTimes() // don't EXPECT any more calls to unpacker.UnpackLongHeader() gomock.InOrder( tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), protocol.ByteCount(len(packet1.data)), gomock.Any(), gomock.Any()), tracer.EXPECT().DroppedPacket(gomock.Any(), protocol.InvalidPacketNumber, protocol.ByteCount(len(packet2.data)), logging.PacketDropUnknownConnectionID), ) packet1.data = append(packet1.data, packet2.data...) Expect(conn.handlePacketImpl(packet1)).To(BeTrue()) }) }) }) Context("sending packets", func() { var ( connDone chan struct{} sender *MockSender sph *mockackhandler.MockSentPacketHandler ) BeforeEach(func() { sender = NewMockSender(mockCtrl) sender.EXPECT().Run() sender.EXPECT().WouldBlock().AnyTimes() conn.sendQueue = sender connDone = make(chan struct{}) sph = mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph }) AfterEach(func() { streamManager.EXPECT().CloseWithError(gomock.Any()) sph.EXPECT().ECNMode(gomock.Any()).Return(protocol.ECNCE).MaxTimes(1) packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sender.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) Eventually(connDone).Should(BeClosed()) }) runConn := func() { go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() close(connDone) }() } It("sends packets", func() { conn.handshakeConfirmed = true sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(true).Return(protocol.ECNNon).AnyTimes() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) runConn() p := shortHeaderPacket{ DestConnID: protocol.ParseConnectionID([]byte{1, 2, 3}), PacketNumber: 1337, PacketNumberLen: protocol.PacketNumberLen3, KeyPhase: protocol.KeyPhaseOne, } expectAppendPacket(packer, p, []byte("foobar")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack).AnyTimes() sent := make(chan struct{}) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(*packetBuffer, uint16, protocol.ECN) { close(sent) }) tracer.EXPECT().SentShortHeaderPacket(&logging.ShortHeader{ DestConnectionID: p.DestConnID, PacketNumber: p.PacketNumber, PacketNumberLen: p.PacketNumberLen, KeyPhase: p.KeyPhase, }, gomock.Any(), gomock.Any(), nil, []logging.Frame{}) conn.scheduleSending() Eventually(sent).Should(BeClosed()) }) It("doesn't send packets if there's nothing to send", func() { conn.handshakeConfirmed = true sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(true).AnyTimes() runConn() packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack).AnyTimes() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure there are no calls to mconn.Write() }) It("sends ACK only packets", func() { sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAck) sph.EXPECT().ECNMode(gomock.Any()).Return(protocol.ECT1).AnyTimes() done := make(chan struct{}) packer.EXPECT().PackCoalescedPacket(true, gomock.Any(), conn.version).Do(func(bool, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) { close(done) return nil, nil }) runConn() conn.scheduleSending() Eventually(done).Should(BeClosed()) }) It("adds a BLOCKED frame when it is connection-level flow control blocked", func() { conn.handshakeConfirmed = true sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) fc := mocks.NewMockConnectionFlowController(mockCtrl) fc.EXPECT().IsNewlyBlocked().Return(true, protocol.ByteCount(1337)) fc.EXPECT().GetWindowUpdate() expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 13}, []byte("foobar")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack).AnyTimes() conn.connFlowController = fc runConn() sent := make(chan struct{}) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(*packetBuffer, uint16, protocol.ECN) { close(sent) }) tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), nil, []logging.Frame{}) conn.scheduleSending() Eventually(sent).Should(BeClosed()) frames, _ := conn.framer.AppendControlFrames(nil, 1000, protocol.Version1) Expect(frames).To(Equal([]ackhandler.Frame{{Frame: &logging.DataBlockedFrame{MaximumData: 1337}}})) }) It("doesn't send when the SentPacketHandler doesn't allow it", func() { sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone).AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() runConn() conn.scheduleSending() time.Sleep(50 * time.Millisecond) }) for _, enc := range []protocol.EncryptionLevel{protocol.EncryptionInitial, protocol.EncryptionHandshake, protocol.Encryption1RTT} { encLevel := enc Context(fmt.Sprintf("sending %s probe packets", encLevel), func() { var sendMode ackhandler.SendMode var getFrame func(protocol.ByteCount, protocol.Version) wire.Frame BeforeEach(func() { switch encLevel { case protocol.EncryptionInitial: sendMode = ackhandler.SendPTOInitial getFrame = conn.retransmissionQueue.GetInitialFrame case protocol.EncryptionHandshake: sendMode = ackhandler.SendPTOHandshake getFrame = conn.retransmissionQueue.GetHandshakeFrame case protocol.Encryption1RTT: sendMode = ackhandler.SendPTOAppData getFrame = conn.retransmissionQueue.GetAppDataFrame } }) It("sends a probe packet", func() { sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(sendMode) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone) sph.EXPECT().QueueProbePacket(encLevel) sph.EXPECT().ECNMode(gomock.Any()) p := getCoalescedPacket(123, encLevel) packer.EXPECT().MaybePackProbePacket(encLevel, gomock.Any(), conn.version).Return(p, nil) sph.EXPECT().SentPacket(gomock.Any(), protocol.PacketNumber(123), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) conn.sentPacketHandler = sph runConn() sent := make(chan struct{}) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(*packetBuffer, uint16, protocol.ECN) { close(sent) }) if encLevel == protocol.Encryption1RTT { tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), p.shortHdrPacket.Length, gomock.Any(), gomock.Any(), gomock.Any()) } else { tracer.EXPECT().SentLongHeaderPacket(gomock.Any(), p.longHdrPackets[0].length, gomock.Any(), gomock.Any(), gomock.Any()) } conn.scheduleSending() Eventually(sent).Should(BeClosed()) }) It("sends a PING as a probe packet", func() { sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(sendMode) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone) sph.EXPECT().ECNMode(gomock.Any()).Return(protocol.ECT0) sph.EXPECT().QueueProbePacket(encLevel).Return(false) p := getCoalescedPacket(123, encLevel) packer.EXPECT().MaybePackProbePacket(encLevel, gomock.Any(), conn.version).Return(p, nil) sph.EXPECT().SentPacket(gomock.Any(), protocol.PacketNumber(123), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) runConn() sent := make(chan struct{}) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(*packetBuffer, uint16, protocol.ECN) { close(sent) }) if encLevel == protocol.Encryption1RTT { tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), p.shortHdrPacket.Length, logging.ECT0, gomock.Any(), gomock.Any()) } else { tracer.EXPECT().SentLongHeaderPacket(gomock.Any(), p.longHdrPackets[0].length, logging.ECT0, gomock.Any(), gomock.Any()) } conn.scheduleSending() Eventually(sent).Should(BeClosed()) // We're using a mock packet packer in this test. // We therefore need to test separately that the PING was actually queued. Expect(getFrame(1000, protocol.Version1)).To(BeAssignableToTypeOf(&wire.PingFrame{})) }) }) } }) Context("packet pacing", func() { var ( sph *mockackhandler.MockSentPacketHandler sender *MockSender ) BeforeEach(func() { tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sph = mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() conn.handshakeConfirmed = true conn.handshakeComplete = true conn.sentPacketHandler = sph sender = NewMockSender(mockCtrl) sender.EXPECT().Run() conn.sendQueue = sender streamManager.EXPECT().CloseWithError(gomock.Any()) }) AfterEach(func() { // make the go routine return sph.EXPECT().ECNMode(gomock.Any()).MaxTimes(1) packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sender.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("sends multiple packets one by one immediately", func() { sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(2) sph.EXPECT().ECNMode(gomock.Any()).Times(2) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited) sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 10}, []byte("packet10")) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 11}, []byte("packet11")) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal([]byte("packet10"))) }) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal([]byte("packet11"))) }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 2 packets are sent }) It("sends multiple packets one by one immediately, with GSO", func() { enableGSO() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) sph.EXPECT().ECNMode(true).Return(protocol.ECT1).Times(4) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(3) payload1 := make([]byte, conn.maxPacketSize()) rand.Read(payload1) payload2 := make([]byte, conn.maxPacketSize()) rand.Read(payload2) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 10}, payload1) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 11}, payload2) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), gomock.Any()).Return(shortHeaderPacket{}, errNothingToPack) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), uint16(conn.maxPacketSize()), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal(append(payload1, payload2...))) }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 2 packets are sent }) It("stops appending packets when a smaller packet is packed, with GSO", func() { enableGSO() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(3) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone) sph.EXPECT().ECNMode(true).Times(4) payload1 := make([]byte, conn.maxPacketSize()) rand.Read(payload1) payload2 := make([]byte, conn.maxPacketSize()-1) rand.Read(payload2) payload3 := make([]byte, conn.maxPacketSize()) rand.Read(payload3) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 10}, payload1) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 11}, payload2) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 12}, payload3) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), uint16(conn.maxPacketSize()), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal(append(payload1, payload2...))) }) sender.EXPECT().Send(gomock.Any(), uint16(conn.maxPacketSize()), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal(payload3)) }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 2 packets are sent }) It("stops appending packets when the ECN marking changes, with GSO", func() { enableGSO() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(3) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone) sph.EXPECT().ECNMode(true).Return(protocol.ECT1).Times(2) sph.EXPECT().ECNMode(true).Return(protocol.ECT0).Times(2) payload1 := make([]byte, conn.maxPacketSize()) rand.Read(payload1) payload2 := make([]byte, conn.maxPacketSize()) rand.Read(payload2) payload3 := make([]byte, conn.maxPacketSize()) rand.Read(payload3) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 10}, payload1) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 11}, payload2) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 11}, payload3) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), uint16(conn.maxPacketSize()), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal(append(payload1, payload2...))) }) sender.EXPECT().Send(gomock.Any(), uint16(conn.maxPacketSize()), gomock.Any()).Do(func(b *packetBuffer, _ uint16, _ protocol.ECN) { Expect(b.Data).To(Equal(payload3)) }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 2 packets are sent }) It("sends multiple packets, when the pacer allows immediate sending", func() { sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(2) sph.EXPECT().ECNMode(gomock.Any()).Times(2) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 10}, []byte("packet10")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 1 packet is sent }) It("allows an ACK to be sent when pacing limited", func() { sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited) sph.EXPECT().ECNMode(gomock.Any()) packer.EXPECT().PackAckOnlyPacket(gomock.Any(), conn.version).Return(shortHeaderPacket{PacketNumber: 123}, getPacketBuffer(), nil) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 1 packet is sent }) // when becoming congestion limited, at some point the SendMode will change from SendAny to SendAck // we shouldn't send the ACK in the same run It("doesn't send an ACK right after becoming congestion limited", func() { sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAck) sph.EXPECT().ECNMode(gomock.Any()).Times(2) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 100}, []byte("packet100")) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 1 packet is sent }) It("paces packets", func() { pacingDelay := scaleDuration(100 * time.Millisecond) gomock.InOrder( sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny), sph.EXPECT().ECNMode(gomock.Any()), expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 100}, []byte("packet100")), sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited), sph.EXPECT().TimeUntilSend().Return(time.Now().Add(pacingDelay)), sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny), sph.EXPECT().ECNMode(gomock.Any()), expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 101}, []byte("packet101")), sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited), sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)), ) written := make(chan struct{}, 2) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { written <- struct{}{} }).Times(2) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() Eventually(written).Should(HaveLen(1)) Consistently(written, pacingDelay/2).Should(HaveLen(1)) Eventually(written, 2*pacingDelay).Should(HaveLen(2)) }) It("sends multiple packets at once", func() { sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).Times(3) sph.EXPECT().ECNMode(gomock.Any()).Times(3) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendPacingLimited) sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)) for pn := protocol.PacketNumber(1000); pn < 1003; pn++ { expectAppendPacket(packer, shortHeaderPacket{PacketNumber: pn}, []byte("packet")) } written := make(chan struct{}, 3) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { written <- struct{}{} }).Times(3) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() Eventually(written).Should(HaveLen(3)) }) for _, withGSO := range []bool{false, true} { withGSO := withGSO It(fmt.Sprintf("doesn't try to send if the send queue is full: %t", withGSO), func() { if withGSO { enableGSO() } available := make(chan struct{}, 1) sender.EXPECT().WouldBlock().Return(true) sender.EXPECT().Available().Return(available) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() time.Sleep(scaleDuration(50 * time.Millisecond)) written := make(chan struct{}) sender.EXPECT().WouldBlock().AnyTimes() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 1000}, []byte("packet1000")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { close(written) }) available <- struct{}{} Eventually(written).Should(BeClosed()) }) } It("stops sending when there are new packets to receive", func() { sender.EXPECT().WouldBlock().AnyTimes() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() written := make(chan struct{}) sender.EXPECT().WouldBlock().AnyTimes() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool) { sph.EXPECT().ReceivedBytes(gomock.Any()) conn.handlePacket(receivedPacket{buffer: getPacketBuffer()}) }) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 10}, []byte("packet10")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { close(written) }) conn.scheduleSending() time.Sleep(scaleDuration(50 * time.Millisecond)) Eventually(written).Should(BeClosed()) }) It("stops sending when the send queue is full", func() { sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny) sph.EXPECT().ECNMode(gomock.Any()) expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 1000}, []byte("packet1000")) written := make(chan struct{}, 1) sender.EXPECT().WouldBlock() sender.EXPECT().WouldBlock().Return(true).Times(2) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { written <- struct{}{} }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() available := make(chan struct{}, 1) sender.EXPECT().Available().Return(available) conn.scheduleSending() Eventually(written).Should(Receive()) time.Sleep(scaleDuration(50 * time.Millisecond)) // now make room in the send queue sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() sender.EXPECT().WouldBlock().AnyTimes() expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 1001}, []byte("packet1001")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { written <- struct{}{} }) available <- struct{}{} Eventually(written).Should(Receive()) // The send queue is not full any more. Sending on the available channel should have no effect. available <- struct{}{} time.Sleep(scaleDuration(50 * time.Millisecond)) }) It("doesn't set a pacing timer when there is no data to send", func() { sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() sender.EXPECT().WouldBlock().AnyTimes() packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) // don't EXPECT any calls to mconn.Write() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() // no packet will get sent time.Sleep(50 * time.Millisecond) }) It("sends a Path MTU probe packet", func() { mtuDiscoverer := NewMockMTUDiscoverer(mockCtrl) conn.mtuDiscoverer = mtuDiscoverer conn.config.DisablePathMTUDiscovery = false sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny) sph.EXPECT().ECNMode(true) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendNone) written := make(chan struct{}, 1) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*packetBuffer, uint16, protocol.ECN) { written <- struct{}{} }) mtuDiscoverer.EXPECT().ShouldSendProbe(gomock.Any()).Return(true) ping := ackhandler.Frame{Frame: &wire.PingFrame{}} mtuDiscoverer.EXPECT().GetPing().Return(ping, protocol.ByteCount(1234)) packer.EXPECT().PackMTUProbePacket(ping, protocol.ByteCount(1234), conn.version).Return(shortHeaderPacket{PacketNumber: 1}, getPacketBuffer(), nil) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() Eventually(written).Should(Receive()) mtuDiscoverer.EXPECT().CurrentSize().Return(protocol.ByteCount(1234)) }) }) Context("scheduling sending", func() { var sender *MockSender BeforeEach(func() { sender = NewMockSender(mockCtrl) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Run() conn.sendQueue = sender conn.handshakeConfirmed = true }) AfterEach(func() { // make the go routine return expectReplaceWithClosed() streamManager.EXPECT().CloseWithError(gomock.Any()) packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) sender.EXPECT().Close() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("sends when scheduleSending is called", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) conn.sentPacketHandler = sph expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 1}, []byte("packet1")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() // don't EXPECT any calls to mconn.Write() time.Sleep(50 * time.Millisecond) // only EXPECT calls after scheduleSending is called written := make(chan struct{}) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(*packetBuffer, uint16, protocol.ECN) { close(written) }) tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() conn.scheduleSending() Eventually(written).Should(BeClosed()) }) It("sets the timer to the ack timer", func() { expectAppendPacket(packer, shortHeaderPacket{PacketNumber: 1234}, []byte("packet1234")) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack) sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() sph.EXPECT().SentPacket(gomock.Any(), protocol.PacketNumber(1234), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) conn.sentPacketHandler = sph rph := mockackhandler.NewMockReceivedPacketHandler(mockCtrl) rph.EXPECT().GetAlarmTimeout().Return(time.Now().Add(10 * time.Millisecond)) // make the run loop wait rph.EXPECT().GetAlarmTimeout().Return(time.Now().Add(time.Hour)).MaxTimes(1) conn.receivedPacketHandler = rph written := make(chan struct{}) sender.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(*packetBuffer, uint16, protocol.ECN) { close(written) }) tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() Eventually(written).Should(BeClosed()) }) }) It("sends coalesced packets before the handshake is confirmed", func() { conn.handshakeComplete = false conn.handshakeConfirmed = false sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("foobar")...) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).Return(&coalescedPacket{ buffer: buffer, longHdrPackets: []*longHeaderPacket{ { header: &wire.ExtendedHeader{ Header: wire.Header{Type: protocol.PacketTypeInitial}, PacketNumber: 13, }, length: 123, }, { header: &wire.ExtendedHeader{ Header: wire.Header{Type: protocol.PacketTypeHandshake}, PacketNumber: 37, }, length: 1234, }, }, }, nil) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(false).Return(protocol.ECT1).AnyTimes() sph.EXPECT().TimeUntilSend().Return(time.Now()).AnyTimes() gomock.InOrder( sph.EXPECT().SentPacket(gomock.Any(), protocol.PacketNumber(13), gomock.Any(), gomock.Any(), gomock.Any(), protocol.EncryptionInitial, protocol.ECT1, protocol.ByteCount(123), gomock.Any()), sph.EXPECT().SentPacket(gomock.Any(), protocol.PacketNumber(37), gomock.Any(), gomock.Any(), gomock.Any(), protocol.EncryptionHandshake, protocol.ECT1, protocol.ByteCount(1234), gomock.Any()), ) gomock.InOrder( tracer.EXPECT().SentLongHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(hdr *wire.ExtendedHeader, _ protocol.ByteCount, _ logging.ECN, _ *wire.AckFrame, _ []logging.Frame) { Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial)) }), tracer.EXPECT().SentLongHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(hdr *wire.ExtendedHeader, _ protocol.ByteCount, _ logging.ECN, _ *wire.AckFrame, _ []logging.Frame) { Expect(hdr.Type).To(Equal(protocol.PacketTypeHandshake)) }), ) sent := make(chan struct{}) mconn.EXPECT().Write([]byte("foobar"), uint16(0), protocol.ECT1).Do(func([]byte, uint16, protocol.ECN) error { close(sent); return nil }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() conn.scheduleSending() Eventually(sent).Should(BeClosed()) // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("cancels the HandshakeComplete context when the handshake completes", func() { packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake) tracer.EXPECT().ChoseALPN(gomock.Any()) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode(gomock.Any()).AnyTimes() sph.EXPECT().DropPackets(protocol.EncryptionHandshake) sph.EXPECT().SetHandshakeConfirmed() connRunner.EXPECT().Retire(clientDestConnID) cryptoSetup.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().GetSessionTicket() cryptoSetup.EXPECT().ConnectionState() handshakeCtx := conn.HandshakeComplete() Consistently(handshakeCtx).ShouldNot(BeClosed()) Expect(conn.handleHandshakeComplete()).To(Succeed()) Eventually(handshakeCtx).Should(BeClosed()) }) It("sends a session ticket when the handshake completes", func() { const size = protocol.MaxPostHandshakeCryptoFrameSize * 3 / 2 packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() connRunner.EXPECT().Retire(clientDestConnID) conn.sentPacketHandler.DropPackets(protocol.EncryptionInitial) tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake) tracer.EXPECT().ChoseALPN(gomock.Any()) cryptoSetup.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().GetSessionTicket().Return(make([]byte, size), nil) cryptoSetup.EXPECT().ConnectionState() handshakeCtx := conn.HandshakeComplete() Consistently(handshakeCtx).ShouldNot(BeClosed()) Expect(conn.handleHandshakeComplete()).To(Succeed()) var frames []ackhandler.Frame Eventually(func() []ackhandler.Frame { frames, _ = conn.framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) return frames }).ShouldNot(BeEmpty()) var count int var s int for _, f := range frames { if cf, ok := f.Frame.(*wire.CryptoFrame); ok { count++ s += len(cf.Data) Expect(f.Frame.Length(conn.version)).To(BeNumerically("<=", protocol.MaxPostHandshakeCryptoFrameSize)) } } Expect(size).To(BeEquivalentTo(s)) }) It("doesn't cancel the HandshakeComplete context when the handshake fails", func() { packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() handshakeCtx := conn.HandshakeComplete() Consistently(handshakeCtx).ShouldNot(BeClosed()) mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) conn.closeLocal(errors.New("handshake error")) Consistently(handshakeCtx).ShouldNot(BeClosed()) Eventually(conn.Context().Done()).Should(BeClosed()) }) It("sends a HANDSHAKE_DONE frame when the handshake completes", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().SendMode(gomock.Any()).Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().ECNMode(gomock.Any()).AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SetHandshakeConfirmed() sph.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().SentShortHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ChoseALPN(gomock.Any()) conn.sentPacketHandler = sph done := make(chan struct{}) connRunner.EXPECT().Retire(clientDestConnID) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).DoAndReturn(func(_ *packetBuffer, _ protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) { frames, _ := conn.framer.AppendControlFrames(nil, protocol.MaxByteCount, v) Expect(frames).ToNot(BeEmpty()) Expect(frames[0].Frame).To(BeEquivalentTo(&wire.HandshakeDoneFrame{})) defer close(done) return shortHeaderPacket{}, nil }) packer.EXPECT().AppendPacket(gomock.Any(), gomock.Any(), conn.version).Return(shortHeaderPacket{}, errNothingToPack).AnyTimes() tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake) sph.EXPECT().DropPackets(protocol.EncryptionHandshake) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete}) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) cryptoSetup.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().GetSessionTicket() cryptoSetup.EXPECT().ConnectionState() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) Expect(conn.handleHandshakeComplete()).To(Succeed()) conn.run() }() Eventually(done).Should(BeClosed()) // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("doesn't return a run error when closing", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) Expect(conn.run()).To(Succeed()) close(done) }() streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() connRunner.EXPECT().Remove(gomock.Any()).AnyTimes() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.destroy(nil) Eventually(done).Should(BeClosed()) Expect(context.Cause(conn.Context())).To(MatchError(context.Canceled)) }) It("passes errors to the connection runner", func() { testErr := errors.New("handshake error") expectedErr := &qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: testErr.Error(), } done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) err := conn.run() Expect(err).To(MatchError(expectedErr)) close(done) }() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() Expect(conn.CloseWithError(0x1337, testErr.Error())).To(Succeed()) Eventually(done).Should(BeClosed()) Expect(context.Cause(conn.Context())).To(MatchError(expectedErr)) }) Context("transport parameters", func() { It("processes transport parameters received from the client", func() { params := &wire.TransportParameters{ MaxIdleTimeout: 90 * time.Second, InitialMaxStreamDataBidiLocal: 0x5000, InitialMaxData: 0x5000, ActiveConnectionIDLimit: 3, // marshaling always sets it to this value MaxUDPPayloadSize: protocol.MaxPacketBufferSize, InitialSourceConnectionID: destConnID, } streamManager.EXPECT().UpdateLimits(params) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).MaxTimes(3) Expect(conn.earlyConnReady()).ToNot(BeClosed()) tracer.EXPECT().ReceivedTransportParameters(params) conn.handleTransportParameters(params) Expect(conn.earlyConnReady()).To(BeClosed()) }) }) Context("keep-alives", func() { setRemoteIdleTimeout := func(t time.Duration) { streamManager.EXPECT().UpdateLimits(gomock.Any()) tracer.EXPECT().ReceivedTransportParameters(gomock.Any()) conn.handleTransportParameters(&wire.TransportParameters{ MaxIdleTimeout: t, InitialSourceConnectionID: destConnID, }) } runConn := func() { go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() } BeforeEach(func() { conn.config.MaxIdleTimeout = 30 * time.Second conn.config.KeepAlivePeriod = 15 * time.Second conn.receivedPacketHandler.ReceivedPacket(0, protocol.ECNNon, protocol.EncryptionHandshake, time.Now(), true) }) AfterEach(func() { // make the go routine return expectReplaceWithClosed() streamManager.EXPECT().CloseWithError(gomock.Any()) packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("sends a PING as a keep-alive after half the idle timeout", func() { setRemoteIdleTimeout(5 * time.Second) conn.lastPacketReceivedTime = time.Now().Add(-5 * time.Second / 2) sent := make(chan struct{}) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).Do(func(bool, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) { close(sent) return nil, nil }) runConn() Eventually(sent).Should(BeClosed()) }) It("sends a PING after a maximum of protocol.MaxKeepAliveInterval", func() { conn.config.MaxIdleTimeout = time.Hour setRemoteIdleTimeout(time.Hour) conn.lastPacketReceivedTime = time.Now().Add(-protocol.MaxKeepAliveInterval).Add(-time.Millisecond) sent := make(chan struct{}) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).Do(func(bool, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) { close(sent) return nil, nil }) runConn() Eventually(sent).Should(BeClosed()) }) It("doesn't send a PING packet if keep-alive is disabled", func() { setRemoteIdleTimeout(5 * time.Second) conn.config.KeepAlivePeriod = 0 conn.lastPacketReceivedTime = time.Now().Add(-time.Second * 5 / 2) runConn() // don't EXPECT() any calls to mconn.Write() time.Sleep(50 * time.Millisecond) }) It("doesn't send a PING if the handshake isn't completed yet", func() { conn.config.HandshakeIdleTimeout = time.Hour conn.handshakeComplete = false // Needs to be shorter than our idle timeout. // Otherwise we'll try to send a CONNECTION_CLOSE. conn.lastPacketReceivedTime = time.Now().Add(-20 * time.Second) runConn() // don't EXPECT() any calls to mconn.Write() time.Sleep(50 * time.Millisecond) }) It("send PING as keep-alive earliest after 1.5 times the PTO", func() { conn.config.KeepAlivePeriod = time.Microsecond pto := conn.rttStats.PTO(true) conn.lastPacketReceivedTime = time.Now() sentPingTimeChan := make(chan time.Time) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).Do(func(bool, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) { sentPingTimeChan <- time.Now() return nil, nil }) runConn() sentPingTime := <-sentPingTimeChan Expect(sentPingTime.Sub(conn.lastPacketReceivedTime)).To(BeNumerically(">", pto*3/2)) }) }) Context("timeouts", func() { BeforeEach(func() { streamManager.EXPECT().CloseWithError(gomock.Any()) }) It("times out due to no network activity", func() { connRunner.EXPECT().Remove(gomock.Any()).Times(2) conn.lastPacketReceivedTime = time.Now().Add(-time.Hour) done := make(chan struct{}) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { Expect(e).To(MatchError(&qerr.IdleTimeoutError{})) }), tracer.EXPECT().Close(), ) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) err := conn.run() nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(err).To(MatchError(qerr.ErrIdleTimeout)) close(done) }() Eventually(done).Should(BeClosed()) }) It("times out due to non-completed handshake", func() { conn.handshakeComplete = false conn.creationTime = time.Now().Add(-2 * protocol.DefaultHandshakeIdleTimeout).Add(-time.Second) connRunner.EXPECT().Remove(gomock.Any()).Times(2) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { Expect(e).To(MatchError(&HandshakeTimeoutError{})) }), tracer.EXPECT().Close(), ) done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) err := conn.run() nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(err).To(MatchError(qerr.ErrHandshakeTimeout)) close(done) }() Eventually(done).Should(BeClosed()) }) It("does not use the idle timeout before the handshake complete", func() { conn.handshakeComplete = false conn.config.HandshakeIdleTimeout = 9999 * time.Second conn.config.MaxIdleTimeout = 9999 * time.Second conn.lastPacketReceivedTime = time.Now().Add(-time.Minute) packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).DoAndReturn(func(e *qerr.ApplicationError, _ protocol.ByteCount, _ protocol.Version) (*coalescedPacket, error) { Expect(e.ErrorCode).To(BeZero()) return &coalescedPacket{buffer: getPacketBuffer()}, nil }) gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { idleTimeout := &IdleTimeoutError{} handshakeTimeout := &HandshakeTimeoutError{} Expect(errors.As(e, &idleTimeout)).To(BeFalse()) Expect(errors.As(e, &handshakeTimeout)).To(BeFalse()) }), tracer.EXPECT().Close(), ) // the handshake timeout is irrelevant here, since it depends on the time the connection was created, // and not on the last network activity go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() Consistently(conn.Context().Done()).ShouldNot(BeClosed()) // make the go routine return expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("closes the connection due to the idle timeout before handshake", func() { conn.config.HandshakeIdleTimeout = scaleDuration(25 * time.Millisecond) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() connRunner.EXPECT().Remove(gomock.Any()).AnyTimes() cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { Expect(e).To(MatchError(&IdleTimeoutError{})) }), tracer.EXPECT().Close(), ) done := make(chan struct{}) conn.handshakeComplete = false go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1) err := conn.run() nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(err).To(MatchError(qerr.ErrIdleTimeout)) close(done) }() Eventually(done).Should(BeClosed()) }) It("closes the connection due to the idle timeout after handshake", func() { conn.sentPacketHandler.DropPackets(protocol.EncryptionInitial) packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() gomock.InOrder( connRunner.EXPECT().Retire(clientDestConnID), connRunner.EXPECT().Remove(gomock.Any()), ) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ChoseALPN(gomock.Any()), tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake), tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { Expect(e).To(MatchError(&IdleTimeoutError{})) }), tracer.EXPECT().Close(), ) conn.idleTimeout = 0 done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete}) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1) cryptoSetup.EXPECT().SetHandshakeConfirmed().MaxTimes(1) cryptoSetup.EXPECT().ConnectionState() Expect(conn.handleHandshakeComplete()).To(Succeed()) err := conn.run() nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(err).To(MatchError(qerr.ErrIdleTimeout)) close(done) }() Eventually(done).Should(BeClosed()) }) It("doesn't time out when it just sent a packet", func() { conn.lastPacketReceivedTime = time.Now().Add(-time.Hour) conn.firstAckElicitingPacketAfterIdleSentTime = time.Now().Add(-time.Second) conn.idleTimeout = 30 * time.Second go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() Consistently(conn.Context().Done()).ShouldNot(BeClosed()) // make the go routine return packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) }) It("times out earliest after 3 times the PTO", func() { packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).AnyTimes() connRunner.EXPECT().Retire(gomock.Any()).AnyTimes() connRunner.EXPECT().Remove(gomock.Any()).Times(2) cryptoSetup.EXPECT().Close() closeTimeChan := make(chan time.Time) tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { Expect(e).To(MatchError(&IdleTimeoutError{})) closeTimeChan <- time.Now() }) tracer.EXPECT().Close() conn.idleTimeout = time.Millisecond done := make(chan struct{}) pto := conn.rttStats.PTO(true) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1) cryptoSetup.EXPECT().SetHandshakeConfirmed().MaxTimes(1) conn.run() close(done) }() closeTime := <-closeTimeChan Expect(closeTime.Sub(conn.lastPacketReceivedTime)).To(BeNumerically(">", pto*3)) Eventually(done).Should(BeClosed()) }) }) It("stores up to MaxConnUnprocessedPackets packets", func() { done := make(chan struct{}) tracer.EXPECT().DroppedPacket(logging.PacketTypeNotDetermined, protocol.InvalidPacketNumber, logging.ByteCount(6), logging.PacketDropDOSPrevention).Do(func(logging.PacketType, logging.PacketNumber, logging.ByteCount, logging.PacketDropReason) { close(done) }) // Nothing here should block for i := protocol.PacketNumber(0); i < protocol.MaxConnUnprocessedPackets+1; i++ { conn.handlePacket(receivedPacket{data: []byte("foobar")}) } Eventually(done).Should(BeClosed()) }) Context("getting streams", func() { It("opens streams", func() { mstr := NewMockStreamI(mockCtrl) streamManager.EXPECT().OpenStream().Return(mstr, nil) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) It("opens streams synchronously", func() { mstr := NewMockStreamI(mockCtrl) streamManager.EXPECT().OpenStreamSync(context.Background()).Return(mstr, nil) str, err := conn.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) It("opens unidirectional streams", func() { mstr := NewMockSendStreamI(mockCtrl) streamManager.EXPECT().OpenUniStream().Return(mstr, nil) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) It("opens unidirectional streams synchronously", func() { mstr := NewMockSendStreamI(mockCtrl) streamManager.EXPECT().OpenUniStreamSync(context.Background()).Return(mstr, nil) str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) It("accepts streams", func() { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() mstr := NewMockStreamI(mockCtrl) streamManager.EXPECT().AcceptStream(ctx).Return(mstr, nil) str, err := conn.AcceptStream(ctx) Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) It("accepts unidirectional streams", func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() mstr := NewMockReceiveStreamI(mockCtrl) streamManager.EXPECT().AcceptUniStream(ctx).Return(mstr, nil) str, err := conn.AcceptUniStream(ctx) Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) }) Context("datagrams", func() { It("doesn't allow datagrams if the peer didn't enable support", func() { conn.peerParams = &wire.TransportParameters{MaxDatagramFrameSize: 0} Expect(conn.SendDatagram(make([]byte, 200))).To(MatchError("datagram support disabled")) }) It("sends a datagram", func() { conn.peerParams = &wire.TransportParameters{MaxDatagramFrameSize: 1000} Expect(conn.SendDatagram([]byte("foobar"))).To(Succeed()) f := conn.datagramQueue.Peek() Expect(f).ToNot(BeNil()) Expect(f.Data).To(Equal([]byte("foobar"))) }) It("says when a datagram is too big", func() { conn.peerParams = &wire.TransportParameters{MaxDatagramFrameSize: 1000} err := conn.SendDatagram(make([]byte, 2000)) Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(&DatagramTooLargeError{})) derr := err.(*DatagramTooLargeError) Expect(derr.MaxDatagramPayloadSize).To(BeNumerically("<", 1000)) Expect(conn.SendDatagram(make([]byte, derr.MaxDatagramPayloadSize))).To(Succeed()) }) It("receives datagrams", func() { conn.config.EnableDatagrams = true conn.datagramQueue.HandleDatagramFrame(&wire.DatagramFrame{Data: []byte("foobar")}) data, err := conn.ReceiveDatagram(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) }) }) It("returns the local address", func() { Expect(conn.LocalAddr()).To(Equal(localAddr)) }) It("returns the remote address", func() { Expect(conn.RemoteAddr()).To(Equal(remoteAddr)) }) }) var _ = Describe("Client Connection", func() { var ( conn *connection connRunner *MockConnRunner packer *MockPacker mconn *MockSendConn cryptoSetup *mocks.MockCryptoSetup tracer *mocklogging.MockConnectionTracer tlsConf *tls.Config quicConf *Config ) srcConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}) destConnID := protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}) getPacket := func(hdr *wire.ExtendedHeader, data []byte) receivedPacket { b, err := hdr.Append(nil, conn.version) Expect(err).ToNot(HaveOccurred()) return receivedPacket{ rcvTime: time.Now(), data: append(b, data...), buffer: getPacketBuffer(), } } BeforeEach(func() { quicConf = populateConfig(&Config{}) tlsConf = nil }) JustBeforeEach(func() { Eventually(areConnsRunning).Should(BeFalse()) mconn = NewMockSendConn(mockCtrl) mconn.EXPECT().capabilities().AnyTimes() mconn.EXPECT().RemoteAddr().Return(&net.UDPAddr{}).AnyTimes() mconn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes() mconn.EXPECT().capabilities().AnyTimes() if tlsConf == nil { tlsConf = &tls.Config{} } connRunner = NewMockConnRunner(mockCtrl) var tr *logging.ConnectionTracer tr, tracer = mocklogging.NewMockConnectionTracer(mockCtrl) tracer.EXPECT().NegotiatedVersion(gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) tracer.EXPECT().SentTransportParameters(gomock.Any()) tracer.EXPECT().UpdatedKeyFromTLS(gomock.Any(), gomock.Any()).AnyTimes() tracer.EXPECT().UpdatedCongestionState(gomock.Any()) conn = newClientConnection( context.Background(), mconn, connRunner, destConnID, protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), &protocol.DefaultConnectionIDGenerator{}, quicConf, tlsConf, 42, // initial packet number false, false, tr, utils.DefaultLogger, protocol.Version1, ).(*connection) packer = NewMockPacker(mockCtrl) conn.packer = packer cryptoSetup = mocks.NewMockCryptoSetup(mockCtrl) conn.cryptoStreamHandler = cryptoSetup conn.sentFirstPacket = true }) It("changes the connection ID when receiving the first packet from the server", func() { unpacker := NewMockUnpacker(mockCtrl) unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(hdr *wire.Header, data []byte) (*unpackedPacket, error) { return &unpackedPacket{ encryptionLevel: protocol.Encryption1RTT, hdr: &wire.ExtendedHeader{Header: *hdr}, data: []byte{0}, // one PADDING frame }, nil }) conn.unpacker = unpacker done := make(chan struct{}) packer.EXPECT().PackCoalescedPacket(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { close(done) return nil, nil }) newConnID := protocol.ParseConnectionID([]byte{1, 3, 3, 7, 1, 3, 3, 7}) p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, SrcConnectionID: newConnID, DestConnectionID: srcConnID, Length: 2 + 6, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar")) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), p.Size(), gomock.Any(), []logging.Frame{}) Expect(conn.handlePacketImpl(p)).To(BeTrue()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) conn.run() }() Eventually(done).Should(BeClosed()) // make sure the go routine returns packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() connRunner.EXPECT().ReplaceWithClosed([]protocol.ConnectionID{srcConnID}, gomock.Any()) mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) time.Sleep(200 * time.Millisecond) }) It("continues accepting Long Header packets after using a new connection ID", func() { unpacker := NewMockUnpacker(mockCtrl) conn.unpacker = unpacker connRunner.EXPECT().AddResetToken(gomock.Any(), gomock.Any()) conn.connIDManager.SetHandshakeComplete() conn.handleNewConnectionIDFrame(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5}), }) Expect(conn.connIDManager.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5}))) // now receive a packet with the original source connection ID unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).DoAndReturn(func(hdr *wire.Header, _ []byte) (*unpackedPacket, error) { return &unpackedPacket{ hdr: &wire.ExtendedHeader{Header: *hdr}, data: []byte{0}, encryptionLevel: protocol.EncryptionHandshake, }, nil }) hdr := &wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: srcConnID, SrcConnectionID: destConnID, } tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) Expect(conn.handleLongHeaderPacket(receivedPacket{buffer: getPacketBuffer()}, hdr)).To(BeTrue()) }) It("handles HANDSHAKE_DONE frames", func() { conn.peerParams = &wire.TransportParameters{} sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake) sph.EXPECT().DropPackets(protocol.EncryptionHandshake) sph.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().SetHandshakeConfirmed() Expect(conn.handleHandshakeDoneFrame()).To(Succeed()) }) It("interprets an ACK for 1-RTT packets as confirmation of the handshake", func() { conn.peerParams = &wire.TransportParameters{} sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 3}}} tracer.EXPECT().DroppedEncryptionLevel(protocol.EncryptionHandshake) sph.EXPECT().ReceivedAck(ack, protocol.Encryption1RTT, gomock.Any()).Return(true, nil) sph.EXPECT().DropPackets(protocol.EncryptionHandshake) sph.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().SetLargest1RTTAcked(protocol.PacketNumber(3)) cryptoSetup.EXPECT().SetHandshakeConfirmed() Expect(conn.handleAckFrame(ack, protocol.Encryption1RTT)).To(Succeed()) }) It("doesn't send a CONNECTION_CLOSE when no packet was sent", func() { conn.sentFirstPacket = false tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() running := make(chan struct{}) cryptoSetup.EXPECT().StartHandshake(gomock.Any()).Do(func(context.Context) error { close(running) conn.closeLocal(errors.New("early error")) return nil }) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) cryptoSetup.EXPECT().Close() connRunner.EXPECT().Remove(gomock.Any()) go func() { defer GinkgoRecover() conn.run() }() Eventually(running).Should(BeClosed()) Eventually(areConnsRunning).Should(BeFalse()) }) Context("handling tokens", func() { var tokenStore TokenStore BeforeEach(func() { tlsConf = &tls.Config{ServerName: "server"} tokenStore = NewLRUTokenStore(10, 10) quicConf.TokenStore = tokenStore }) It("handles NEW_TOKEN frames", func() { Expect(conn.handleNewTokenFrame(&wire.NewTokenFrame{Token: []byte("foobar")})).To(Succeed()) Expect(tokenStore.Pop("server")).To(Equal(&ClientToken{data: []byte("foobar")})) }) }) Context("handling Version Negotiation", func() { getVNP := func(versions ...protocol.Version) receivedPacket { b := wire.ComposeVersionNegotiation( protocol.ArbitraryLenConnectionID(srcConnID.Bytes()), protocol.ArbitraryLenConnectionID(destConnID.Bytes()), versions, ) return receivedPacket{ rcvTime: time.Now(), data: b, buffer: getPacketBuffer(), } } It("closes and returns the right error", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph sph.EXPECT().ReceivedBytes(gomock.Any()) sph.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(128), protocol.PacketNumberLen4) conn.config.Versions = []protocol.Version{1234, 4321} errChan := make(chan error, 1) start := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().DoAndReturn(func() handshake.Event { <-start return handshake.Event{Kind: handshake.EventNoEvent} }) errChan <- conn.run() }() connRunner.EXPECT().Remove(srcConnID) tracer.EXPECT().ReceivedVersionNegotiationPacket(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_, _ protocol.ArbitraryLenConnectionID, versions []logging.Version) { Expect(versions).To(And( ContainElement(protocol.Version(4321)), ContainElement(protocol.Version(1337)), )) }) cryptoSetup.EXPECT().Close() Expect(conn.handlePacketImpl(getVNP(4321, 1337))).To(BeFalse()) close(start) var err error Eventually(errChan).Should(Receive(&err)) Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(&errCloseForRecreating{})) recreateErr := err.(*errCloseForRecreating) Expect(recreateErr.nextVersion).To(Equal(protocol.Version(4321))) Expect(recreateErr.nextPacketNumber).To(Equal(protocol.PacketNumber(128))) }) It("closes when no matching version is found", func() { errChan := make(chan error, 1) packer.EXPECT().PackCoalescedPacket(gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}) errChan <- conn.run() }() connRunner.EXPECT().Remove(srcConnID).MaxTimes(1) gomock.InOrder( tracer.EXPECT().ReceivedVersionNegotiationPacket(gomock.Any(), gomock.Any(), gomock.Any()), tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { var vnErr *VersionNegotiationError Expect(errors.As(e, &vnErr)).To(BeTrue()) Expect(vnErr.Theirs).To(ContainElement(logging.Version(12345678))) }), tracer.EXPECT().Close(), ) cryptoSetup.EXPECT().Close() Expect(conn.handlePacketImpl(getVNP(12345678))).To(BeFalse()) var err error Eventually(errChan).Should(Receive(&err)) Expect(err).To(HaveOccurred()) Expect(err).ToNot(BeAssignableToTypeOf(errCloseForRecreating{})) Expect(err.Error()).To(ContainSubstring("no compatible QUIC version found")) }) It("ignores Version Negotiation packets that offer the current version", func() { p := getVNP(conn.version) tracer.EXPECT().DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedVersion) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("ignores unparseable Version Negotiation packets", func() { p := getVNP(conn.version) p.data = p.data[:len(p.data)-2] tracer.EXPECT().DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropHeaderParseError) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) }) Context("handling Retry", func() { origDestConnID := protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}) var retryHdr *wire.ExtendedHeader JustBeforeEach(func() { retryHdr = &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeRetry, SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), Token: []byte("foobar"), Version: conn.version, }, } }) getRetryTag := func(hdr *wire.ExtendedHeader) []byte { b, err := hdr.Append(nil, conn.version) Expect(err).ToNot(HaveOccurred()) return handshake.GetRetryIntegrityTag(b, origDestConnID, hdr.Version)[:] } It("handles Retry packets", func() { now := time.Now() sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph sph.EXPECT().ResetForRetry(now) sph.EXPECT().ReceivedBytes(gomock.Any()) cryptoSetup.EXPECT().ChangeConnectionID(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef})) packer.EXPECT().SetToken([]byte("foobar")) tracer.EXPECT().ReceivedRetry(gomock.Any()).Do(func(hdr *wire.Header) { Expect(hdr.DestConnectionID).To(Equal(retryHdr.DestConnectionID)) Expect(hdr.SrcConnectionID).To(Equal(retryHdr.SrcConnectionID)) Expect(hdr.Token).To(Equal(retryHdr.Token)) }) p := getPacket(retryHdr, getRetryTag(retryHdr)) p.rcvTime = now Expect(conn.handlePacketImpl(p)).To(BeTrue()) }) It("ignores Retry packets after receiving a regular packet", func() { conn.receivedFirstPacket = true p := getPacket(retryHdr, getRetryTag(retryHdr)) tracer.EXPECT().DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("ignores Retry packets if the server didn't change the connection ID", func() { retryHdr.SrcConnectionID = destConnID p := getPacket(retryHdr, getRetryTag(retryHdr)) tracer.EXPECT().DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropUnexpectedPacket) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) It("ignores Retry packets with the a wrong Integrity tag", func() { tag := getRetryTag(retryHdr) tag[0]++ p := getPacket(retryHdr, tag) tracer.EXPECT().DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, p.Size(), logging.PacketDropPayloadDecryptError) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) }) Context("transport parameters", func() { var ( closed bool errChan chan error paramsChan chan *wire.TransportParameters ) JustBeforeEach(func() { errChan = make(chan error, 1) paramsChan = make(chan *wire.TransportParameters, 1) closed = false packer.EXPECT().PackCoalescedPacket(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().StartHandshake(gomock.Any()).MaxTimes(1) // This is not 100% what would happen in reality. // The run loop calls NextEvent once when it starts up (to send out the ClientHello), // and then again every time a CRYPTO frame is handled. // Injecting a CRYPTO frame is not straightforward though, // so we inject the transport parameters on the first call to NextEvent. params := <-paramsChan cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{ Kind: handshake.EventReceivedTransportParameters, TransportParameters: params, }) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventHandshakeComplete}).MaxTimes(1) cryptoSetup.EXPECT().NextEvent().Return(handshake.Event{Kind: handshake.EventNoEvent}).MaxTimes(1).Do(func() { defer GinkgoRecover() Expect(conn.handleHandshakeComplete()).To(Succeed()) }) tracer.EXPECT().ChoseALPN(gomock.Any()).MaxTimes(1) cryptoSetup.EXPECT().ConnectionState().MaxTimes(1) errChan <- conn.run() close(errChan) }() }) expectClose := func(applicationClose, errored bool) { if !closed && !errored { connRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()) if applicationClose { packer.EXPECT().PackApplicationClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil).MaxTimes(1) } else { packer.EXPECT().PackConnectionClose(gomock.Any(), gomock.Any(), conn.version).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil).MaxTimes(1) } cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any(), gomock.Any(), gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()), tracer.EXPECT().Close(), ) } closed = true } AfterEach(func() { conn.CloseWithError(0, "") Eventually(conn.Context().Done()).Should(BeClosed()) Eventually(errChan).Should(BeClosed()) }) It("uses the preferred_address connection ID", func() { params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, PreferredAddress: &wire.PreferredAddress{ IPv4: netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 42), IPv6: netip.AddrPortFrom(netip.AddrFrom16([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 13), ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, }, } packer.EXPECT().PackCoalescedPacket(false, gomock.Any(), conn.version).MaxTimes(1) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) // make sure the connection ID is not retired cf, _ := conn.framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(cf).To(BeEmpty()) connRunner.EXPECT().AddResetToken(protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, conn) Expect(conn.connIDManager.Get()).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4}))) // shut down connRunner.EXPECT().RemoveResetToken(protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}) expectClose(true, false) }) It("uses the minimum of the peers' idle timeouts", func() { conn.config.MaxIdleTimeout = 19 * time.Second params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, MaxIdleTimeout: 18 * time.Second, } processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) // close first expectClose(true, false) conn.CloseWithError(0, "") // then check. Avoids race condition when accessing idleTimeout Expect(conn.idleTimeout).To(Equal(18 * time.Second)) }) It("errors if the transport parameters contain a wrong initial_source_connection_id", func() { conn.handshakeDestConnID = protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false, true) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "expected initial_source_connection_id to equal deadbeef, is decafbad", }))) }) It("errors if the transport parameters don't contain the retry_source_connection_id, if a Retry was performed", func() { rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) conn.retrySrcConnID = &rcid params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false, true) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "missing retry_source_connection_id", }))) }) It("errors if the transport parameters contain the wrong retry_source_connection_id, if a Retry was performed", func() { rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) rcid2 := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) conn.retrySrcConnID = &rcid params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, RetrySourceConnectionID: &rcid2, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false, true) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "expected retry_source_connection_id to equal deadbeef, is deadc0de", }))) }) It("errors if the transport parameters contain the retry_source_connection_id, if no Retry was performed", func() { rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, RetrySourceConnectionID: &rcid, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false, true) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "received retry_source_connection_id, although no Retry was performed", }))) }) It("errors if the transport parameters contain a wrong original_destination_connection_id", func() { conn.origDestConnID = protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) params := &wire.TransportParameters{ OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), InitialSourceConnectionID: conn.handshakeDestConnID, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false, true) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) paramsChan <- params Eventually(processed).Should(BeClosed()) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "expected original_destination_connection_id to equal deadbeef, is decafbad", }))) }) It("errors if the transport parameters contain reduced limits after knowing 0-RTT data is accepted by the server", func() { conn.perspective = protocol.PerspectiveClient conn.peerParams = &wire.TransportParameters{ ActiveConnectionIDLimit: 3, InitialMaxData: 0x5000, InitialMaxStreamDataBidiLocal: 0x5000, InitialMaxStreamDataBidiRemote: 1000, InitialMaxStreamDataUni: 1000, MaxBidiStreamNum: 500, MaxUniStreamNum: 500, } params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, ActiveConnectionIDLimit: 3, InitialMaxData: 0x5000, InitialMaxStreamDataBidiLocal: 0x5000, InitialMaxStreamDataBidiRemote: 1000, InitialMaxStreamDataUni: 1000, MaxBidiStreamNum: 300, MaxUniStreamNum: 300, } expectClose(false, true) processed := make(chan struct{}) tracer.EXPECT().ReceivedTransportParameters(params).Do(func(*wire.TransportParameters) { close(processed) }) cryptoSetup.EXPECT().ConnectionState().Return(handshake.ConnectionState{Used0RTT: true}) paramsChan <- params Eventually(processed).Should(BeClosed()) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "server sent reduced limits after accepting 0-RTT data", }))) }) }) Context("handling potentially injected packets", func() { var unpacker *MockUnpacker getPacket := func(extHdr *wire.ExtendedHeader, data []byte) receivedPacket { b, err := extHdr.Append(nil, conn.version) Expect(err).ToNot(HaveOccurred()) return receivedPacket{ data: append(b, data...), buffer: getPacketBuffer(), } } // Convert an already packed raw packet into a receivedPacket wrapPacket := func(packet []byte) receivedPacket { return receivedPacket{ data: packet, buffer: getPacketBuffer(), } } // Illustrates that attacker may inject an Initial packet with a different // source connection ID, causing endpoint to ignore a subsequent real Initial packets. It("ignores Initial packets with a different source connection ID", func() { // Modified from test "ignores packets with a different source connection ID" unpacker = NewMockUnpacker(mockCtrl) conn.unpacker = unpacker hdr1 := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Length: 1, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 1, } hdr2 := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), Length: 1, Version: conn.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 2, } Expect(hdr2.SrcConnectionID).ToNot(Equal(srcConnID)) // Send one packet, which might change the connection ID. // only EXPECT one call to the unpacker unpacker.EXPECT().UnpackLongHeader(gomock.Any(), gomock.Any()).Return(&unpackedPacket{ encryptionLevel: protocol.EncryptionInitial, hdr: hdr1, data: []byte{0}, // one PADDING frame }, nil) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(getPacket(hdr1, nil))).To(BeTrue()) // The next packet has to be ignored, since the source connection ID doesn't match. tracer.EXPECT().DroppedPacket(gomock.Any(), protocol.InvalidPacketNumber, gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(getPacket(hdr2, nil))).To(BeFalse()) }) It("ignores 0-RTT packets", func() { p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketType0RTT, DestConnectionID: srcConnID, Length: 2 + 6, Version: conn.version, }, PacketNumber: 0x42, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar")) tracer.EXPECT().DroppedPacket(logging.PacketType0RTT, protocol.InvalidPacketNumber, p.Size(), gomock.Any()) Expect(conn.handlePacketImpl(p)).To(BeFalse()) }) // Illustrates that an injected Initial with an ACK frame for an unsent packet causes // the connection to immediately break down It("fails on Initial-level ACK for unsent packet", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} initialPacket := testutils.ComposeInitialPacket(destConnID, srcConnID, destConnID, nil, []wire.Frame{ack}, protocol.PerspectiveServer, conn.version) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse()) }) // Illustrates that an injected Initial with a CONNECTION_CLOSE frame causes // the connection to immediately break down It("fails on Initial-level CONNECTION_CLOSE frame", func() { connCloseFrame := &wire.ConnectionCloseFrame{ IsApplicationError: true, ReasonPhrase: "mitm attacker", } initialPacket := testutils.ComposeInitialPacket(destConnID, srcConnID, destConnID, nil, []wire.Frame{connCloseFrame}, protocol.PerspectiveServer, conn.version) tracer.EXPECT().ReceivedLongHeaderPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(wrapPacket(initialPacket))).To(BeTrue()) }) // Illustrates that attacker who injects a Retry packet and changes the connection ID // can cause subsequent real Initial packets to be ignored It("ignores Initial packets which use original source id, after accepting a Retry", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) conn.sentPacketHandler = sph sph.EXPECT().ReceivedBytes(gomock.Any()).Times(2) sph.EXPECT().ResetForRetry(gomock.Any()) newSrcConnID := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) cryptoSetup.EXPECT().ChangeConnectionID(newSrcConnID) packer.EXPECT().SetToken([]byte("foobar")) tracer.EXPECT().ReceivedRetry(gomock.Any()) conn.handlePacketImpl(wrapPacket(testutils.ComposeRetryPacket(newSrcConnID, destConnID, destConnID, []byte("foobar"), conn.version))) initialPacket := testutils.ComposeInitialPacket(conn.connIDManager.Get(), srcConnID, conn.connIDManager.Get(), nil, nil, protocol.PerspectiveServer, conn.version) tracer.EXPECT().DroppedPacket(gomock.Any(), protocol.InvalidPacketNumber, gomock.Any(), gomock.Any()) Expect(conn.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/connection_timer.go000066400000000000000000000025651465664453100245050ustar00rootroot00000000000000package quic import ( "time" "github.com/quic-go/quic-go/internal/utils" ) var deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine type connectionTimer struct { timer *utils.Timer last time.Time } func newTimer() *connectionTimer { return &connectionTimer{timer: utils.NewTimer()} } func (t *connectionTimer) SetRead() { if deadline := t.timer.Deadline(); deadline != deadlineSendImmediately { t.last = deadline } t.timer.SetRead() } func (t *connectionTimer) Chan() <-chan time.Time { return t.timer.Chan() } // SetTimer resets the timer. // It makes sure that the deadline is strictly increasing. // This prevents busy-looping in cases where the timer fires, but we can't actually send out a packet. // This doesn't apply to the pacing deadline, which can be set multiple times to deadlineSendImmediately. func (t *connectionTimer) SetTimer(idleTimeoutOrKeepAlive, ackAlarm, lossTime, pacing time.Time) { deadline := idleTimeoutOrKeepAlive if !ackAlarm.IsZero() && ackAlarm.Before(deadline) && ackAlarm.After(t.last) { deadline = ackAlarm } if !lossTime.IsZero() && lossTime.Before(deadline) && lossTime.After(t.last) { deadline = lossTime } if !pacing.IsZero() && pacing.Before(deadline) { deadline = pacing } t.timer.Reset(deadline) } func (t *connectionTimer) Stop() { t.timer.Stop() } golang-github-lucas-clemente-quic-go-0.46.0/connection_timer_test.go000066400000000000000000000035341465664453100255410ustar00rootroot00000000000000package quic import ( "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func (t *connectionTimer) Deadline() time.Time { return t.timer.Deadline() } var _ = Describe("Timer", func() { It("sets an idle timeout", func() { now := time.Now() t := newTimer() t.SetTimer(now.Add(time.Hour), time.Time{}, time.Time{}, time.Time{}) Expect(t.Deadline()).To(Equal(now.Add(time.Hour))) }) It("sets an ACK timer", func() { now := time.Now() t := newTimer() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) Expect(t.Deadline()).To(Equal(now.Add(time.Minute))) }) It("sets a loss timer", func() { now := time.Now() t := newTimer() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), now.Add(time.Second), time.Time{}) Expect(t.Deadline()).To(Equal(now.Add(time.Second))) }) It("sets a pacing timer", func() { now := time.Now() t := newTimer() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), now.Add(time.Second), now.Add(time.Millisecond)) Expect(t.Deadline()).To(Equal(now.Add(time.Millisecond))) }) It("doesn't reset to an earlier time", func() { now := time.Now() t := newTimer() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) Expect(t.Deadline()).To(Equal(now.Add(time.Minute))) t.SetRead() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) Expect(t.Deadline()).To(Equal(now.Add(time.Hour))) }) It("allows the pacing timer to be set to send immediately", func() { now := time.Now() t := newTimer() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, time.Time{}) Expect(t.Deadline()).To(Equal(now.Add(time.Minute))) t.SetRead() t.SetTimer(now.Add(time.Hour), now.Add(time.Minute), time.Time{}, deadlineSendImmediately) Expect(t.Deadline()).To(Equal(deadlineSendImmediately)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/crypto_stream.go000066400000000000000000000044151465664453100240350ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" ) type cryptoStream struct { queue frameSorter highestOffset protocol.ByteCount finished bool writeOffset protocol.ByteCount writeBuf []byte } func newCryptoStream() *cryptoStream { return &cryptoStream{queue: *newFrameSorter()} } func (s *cryptoStream) HandleCryptoFrame(f *wire.CryptoFrame) error { highestOffset := f.Offset + protocol.ByteCount(len(f.Data)) if maxOffset := highestOffset; maxOffset > protocol.MaxCryptoStreamOffset { return &qerr.TransportError{ ErrorCode: qerr.CryptoBufferExceeded, ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", maxOffset, protocol.MaxCryptoStreamOffset), } } if s.finished { if highestOffset > s.highestOffset { // reject crypto data received after this stream was already finished return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received crypto data after change of encryption level", } } // ignore data with a smaller offset than the highest received // could e.g. be a retransmission return nil } s.highestOffset = max(s.highestOffset, highestOffset) return s.queue.Push(f.Data, f.Offset, nil) } // GetCryptoData retrieves data that was received in CRYPTO frames func (s *cryptoStream) GetCryptoData() []byte { _, data, _ := s.queue.Pop() return data } func (s *cryptoStream) Finish() error { if s.queue.HasMoreData() { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "encryption level changed, but crypto stream has more data to read", } } s.finished = true return nil } // Writes writes data that should be sent out in CRYPTO frames func (s *cryptoStream) Write(p []byte) (int, error) { s.writeBuf = append(s.writeBuf, p...) return len(p), nil } func (s *cryptoStream) HasData() bool { return len(s.writeBuf) > 0 } func (s *cryptoStream) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { f := &wire.CryptoFrame{Offset: s.writeOffset} n := min(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf))) f.Data = s.writeBuf[:n] s.writeBuf = s.writeBuf[n:] s.writeOffset += n return f } golang-github-lucas-clemente-quic-go-0.46.0/crypto_stream_manager.go000066400000000000000000000042101465664453100255200ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) type cryptoStreamManager struct { initialStream *cryptoStream handshakeStream *cryptoStream oneRTTStream *cryptoStream } func newCryptoStreamManager( initialStream *cryptoStream, handshakeStream *cryptoStream, oneRTTStream *cryptoStream, ) *cryptoStreamManager { return &cryptoStreamManager{ initialStream: initialStream, handshakeStream: handshakeStream, oneRTTStream: oneRTTStream, } } func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { var str *cryptoStream //nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets. switch encLevel { case protocol.EncryptionInitial: str = m.initialStream case protocol.EncryptionHandshake: str = m.handshakeStream case protocol.Encryption1RTT: str = m.oneRTTStream default: return fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel) } return str.HandleCryptoFrame(frame) } func (m *cryptoStreamManager) GetCryptoData(encLevel protocol.EncryptionLevel) []byte { var str *cryptoStream //nolint:exhaustive // CRYPTO frames cannot be sent in 0-RTT packets. switch encLevel { case protocol.EncryptionInitial: str = m.initialStream case protocol.EncryptionHandshake: str = m.handshakeStream case protocol.Encryption1RTT: str = m.oneRTTStream default: panic(fmt.Sprintf("received CRYPTO frame with unexpected encryption level: %s", encLevel)) } return str.GetCryptoData() } func (m *cryptoStreamManager) GetPostHandshakeData(maxSize protocol.ByteCount) *wire.CryptoFrame { if !m.oneRTTStream.HasData() { return nil } return m.oneRTTStream.PopCryptoFrame(maxSize) } func (m *cryptoStreamManager) Drop(encLevel protocol.EncryptionLevel) error { //nolint:exhaustive // 1-RTT keys should never get dropped. switch encLevel { case protocol.EncryptionInitial: return m.initialStream.Finish() case protocol.EncryptionHandshake: return m.handshakeStream.Finish() default: panic(fmt.Sprintf("dropped unexpected encryption level: %s", encLevel)) } } golang-github-lucas-clemente-quic-go-0.46.0/crypto_stream_manager_test.go000066400000000000000000000053171465664453100265700ustar00rootroot00000000000000package quic import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Crypto Stream Manager", func() { var ( csm *cryptoStreamManager initialStream *cryptoStream handshakeStream *cryptoStream oneRTTStream *cryptoStream ) BeforeEach(func() { initialStream = newCryptoStream() handshakeStream = newCryptoStream() oneRTTStream = newCryptoStream() csm = newCryptoStreamManager(initialStream, handshakeStream, oneRTTStream) }) It("passes messages to the initial stream", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} Expect(csm.HandleCryptoFrame(cf, protocol.EncryptionInitial)).To(Succeed()) Expect(csm.GetCryptoData(protocol.EncryptionInitial)).To(Equal([]byte("foobar"))) }) It("passes messages to the handshake stream", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} Expect(csm.HandleCryptoFrame(cf, protocol.EncryptionHandshake)).To(Succeed()) Expect(csm.GetCryptoData(protocol.EncryptionHandshake)).To(Equal([]byte("foobar"))) }) It("passes messages to the 1-RTT stream", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} Expect(csm.HandleCryptoFrame(cf, protocol.Encryption1RTT)).To(Succeed()) Expect(csm.GetCryptoData(protocol.Encryption1RTT)).To(Equal([]byte("foobar"))) }) It("processes all messages", func() { Expect(csm.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3}, protocol.EncryptionHandshake)).To(Succeed()) Expect(csm.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")}, protocol.EncryptionHandshake)).To(Succeed()) var data []byte for { b := csm.GetCryptoData(protocol.EncryptionHandshake) if len(b) == 0 { break } data = append(data, b...) } Expect(data).To(Equal([]byte("foobar"))) }) It("errors for unknown encryption levels", func() { err := csm.HandleCryptoFrame(&wire.CryptoFrame{}, 42) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("received CRYPTO frame with unexpected encryption level")) }) It("drops Initial", func() { Expect(initialStream.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})).To(Succeed()) err := csm.Drop(protocol.EncryptionInitial) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("encryption level changed, but crypto stream has more data to read")) }) It("drops Handshake", func() { Expect(handshakeStream.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})).To(Succeed()) err := csm.Drop(protocol.EncryptionHandshake) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("encryption level changed, but crypto stream has more data to read")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/crypto_stream_test.go000066400000000000000000000121241465664453100250700ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Crypto Stream", func() { var str *cryptoStream BeforeEach(func() { str = newCryptoStream() }) Context("handling incoming data", func() { It("handles in-order CRYPTO frames", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})).To(Succeed()) Expect(str.GetCryptoData()).To(Equal([]byte("foo"))) Expect(str.GetCryptoData()).To(BeNil()) Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("bar"), Offset: 3})).To(Succeed()) Expect(str.GetCryptoData()).To(Equal([]byte("bar"))) Expect(str.GetCryptoData()).To(BeNil()) }) It("errors if the frame exceeds the maximum offset", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Offset: protocol.MaxCryptoStreamOffset - 5, Data: []byte("foobar"), })).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.CryptoBufferExceeded, ErrorMessage: fmt.Sprintf("received invalid offset %d on crypto stream, maximum allowed %d", protocol.MaxCryptoStreamOffset+1, protocol.MaxCryptoStreamOffset), })) }) It("handles out-of-order CRYPTO frames", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Offset: 3, Data: []byte("bar")})).To(Succeed()) Expect(str.HandleCryptoFrame(&wire.CryptoFrame{Data: []byte("foo")})).To(Succeed()) var data []byte for { b := str.GetCryptoData() if b == nil { break } data = append(data, b...) } Expect(data).To(Equal([]byte("foobar"))) Expect(str.GetCryptoData()).To(BeNil()) }) Context("finishing", func() { It("errors if there's still data to read at the current offset after finishing", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Data: []byte("foo"), })).To(Succeed()) Expect(str.Finish()).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "encryption level changed, but crypto stream has more data to read", })) }) It("errors if there's still data to read at a higher offset after finishing", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Data: []byte("foobar"), Offset: 10, })).To(Succeed()) Expect(str.Finish()).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "encryption level changed, but crypto stream has more data to read", })) }) It("works with reordered data", func() { f1 := &wire.CryptoFrame{ Data: []byte("foo"), } f2 := &wire.CryptoFrame{ Offset: 3, Data: []byte("bar"), } Expect(str.HandleCryptoFrame(f2)).To(Succeed()) Expect(str.HandleCryptoFrame(f1)).To(Succeed()) Expect(str.GetCryptoData()).To(HaveLen(3)) Expect(str.GetCryptoData()).To(HaveLen(3)) Expect(str.Finish()).To(Succeed()) Expect(str.HandleCryptoFrame(f2)).To(Succeed()) }) It("rejects new crypto data after finishing", func() { Expect(str.Finish()).To(Succeed()) Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Data: []byte("foo"), })).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received crypto data after change of encryption level", })) }) It("ignores crypto data below the maximum offset received before finishing", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Data: []byte("foobar"), })).To(Succeed()) Expect(str.GetCryptoData()).To(Equal([]byte("foobar"))) Expect(str.Finish()).To(Succeed()) Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Offset: 2, Data: []byte("foo"), })).To(Succeed()) }) }) }) Context("writing data", func() { It("says if it has data", func() { Expect(str.HasData()).To(BeFalse()) _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(str.HasData()).To(BeTrue()) }) It("pops crypto frames", func() { _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) f := str.PopCryptoFrame(1000) Expect(f).ToNot(BeNil()) Expect(f.Offset).To(BeZero()) Expect(f.Data).To(Equal([]byte("foobar"))) }) It("coalesces multiple writes", func() { _, err := str.Write([]byte("foo")) Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("bar")) Expect(err).ToNot(HaveOccurred()) f := str.PopCryptoFrame(1000) Expect(f).ToNot(BeNil()) Expect(f.Offset).To(BeZero()) Expect(f.Data).To(Equal([]byte("foobar"))) }) It("respects the maximum size", func() { frameHeaderLen := (&wire.CryptoFrame{}).Length(protocol.Version1) _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) f := str.PopCryptoFrame(frameHeaderLen + 3) Expect(f).ToNot(BeNil()) Expect(f.Offset).To(BeZero()) Expect(f.Data).To(Equal([]byte("foo"))) f = str.PopCryptoFrame(frameHeaderLen + 3) Expect(f).ToNot(BeNil()) Expect(f.Offset).To(Equal(protocol.ByteCount(3))) Expect(f.Data).To(Equal([]byte("bar"))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/datagram_queue.go000066400000000000000000000056401465664453100241270ustar00rootroot00000000000000package quic import ( "context" "sync" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/utils/ringbuffer" "github.com/quic-go/quic-go/internal/wire" ) const ( maxDatagramSendQueueLen = 32 maxDatagramRcvQueueLen = 128 ) type datagramQueue struct { sendMx sync.Mutex sendQueue ringbuffer.RingBuffer[*wire.DatagramFrame] sent chan struct{} // used to notify Add that a datagram was dequeued rcvMx sync.Mutex rcvQueue [][]byte rcvd chan struct{} // used to notify Receive that a new datagram was received closeErr error closed chan struct{} hasData func() logger utils.Logger } func newDatagramQueue(hasData func(), logger utils.Logger) *datagramQueue { return &datagramQueue{ hasData: hasData, rcvd: make(chan struct{}, 1), sent: make(chan struct{}, 1), closed: make(chan struct{}), logger: logger, } } // Add queues a new DATAGRAM frame for sending. // Up to 32 DATAGRAM frames will be queued. // Once that limit is reached, Add blocks until the queue size has reduced. func (h *datagramQueue) Add(f *wire.DatagramFrame) error { h.sendMx.Lock() for { if h.sendQueue.Len() < maxDatagramSendQueueLen { h.sendQueue.PushBack(f) h.sendMx.Unlock() h.hasData() return nil } select { case <-h.sent: // drain the queue so we don't loop immediately default: } h.sendMx.Unlock() select { case <-h.closed: return h.closeErr case <-h.sent: } h.sendMx.Lock() } } // Peek gets the next DATAGRAM frame for sending. // If actually sent out, Pop needs to be called before the next call to Peek. func (h *datagramQueue) Peek() *wire.DatagramFrame { h.sendMx.Lock() defer h.sendMx.Unlock() if h.sendQueue.Empty() { return nil } return h.sendQueue.PeekFront() } func (h *datagramQueue) Pop() { h.sendMx.Lock() defer h.sendMx.Unlock() _ = h.sendQueue.PopFront() select { case h.sent <- struct{}{}: default: } } // HandleDatagramFrame handles a received DATAGRAM frame. func (h *datagramQueue) HandleDatagramFrame(f *wire.DatagramFrame) { data := make([]byte, len(f.Data)) copy(data, f.Data) var queued bool h.rcvMx.Lock() if len(h.rcvQueue) < maxDatagramRcvQueueLen { h.rcvQueue = append(h.rcvQueue, data) queued = true select { case h.rcvd <- struct{}{}: default: } } h.rcvMx.Unlock() if !queued && h.logger.Debug() { h.logger.Debugf("Discarding received DATAGRAM frame (%d bytes payload)", len(f.Data)) } } // Receive gets a received DATAGRAM frame. func (h *datagramQueue) Receive(ctx context.Context) ([]byte, error) { for { h.rcvMx.Lock() if len(h.rcvQueue) > 0 { data := h.rcvQueue[0] h.rcvQueue = h.rcvQueue[1:] h.rcvMx.Unlock() return data, nil } h.rcvMx.Unlock() select { case <-h.rcvd: continue case <-h.closed: return nil, h.closeErr case <-ctx.Done(): return nil, ctx.Err() } } } func (h *datagramQueue) CloseWithError(e error) { h.closeErr = e close(h.closed) } golang-github-lucas-clemente-quic-go-0.46.0/datagram_queue_test.go000066400000000000000000000102721465664453100251630ustar00rootroot00000000000000package quic import ( "context" "errors" "time" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Datagram Queue", func() { var queue *datagramQueue var queued chan struct{} BeforeEach(func() { queued = make(chan struct{}, 100) queue = newDatagramQueue(func() { queued <- struct{}{} }, utils.DefaultLogger) }) Context("sending", func() { It("returns nil when there's no datagram to send", func() { Expect(queue.Peek()).To(BeNil()) }) It("queues a datagram", func() { frame := &wire.DatagramFrame{Data: []byte("foobar")} Expect(queue.Add(frame)).To(Succeed()) Expect(queued).To(HaveLen(1)) f := queue.Peek() Expect(f.Data).To(Equal([]byte("foobar"))) queue.Pop() Expect(queue.Peek()).To(BeNil()) }) It("blocks when the maximum number of datagrams have been queued", func() { for i := 0; i < maxDatagramSendQueueLen; i++ { Expect(queue.Add(&wire.DatagramFrame{Data: []byte{0}})).To(Succeed()) } errChan := make(chan error, 1) go func() { defer GinkgoRecover() errChan <- queue.Add(&wire.DatagramFrame{Data: []byte("foobar")}) }() Consistently(errChan, 50*time.Millisecond).ShouldNot(Receive()) Expect(queue.Peek()).ToNot(BeNil()) Consistently(errChan, 50*time.Millisecond).ShouldNot(Receive()) queue.Pop() Eventually(errChan).Should(Receive(BeNil())) for i := 1; i < maxDatagramSendQueueLen; i++ { queue.Pop() } f := queue.Peek() Expect(f).ToNot(BeNil()) Expect(f.Data).To(Equal([]byte("foobar"))) }) It("returns the same datagram multiple times, when Pop isn't called", func() { Expect(queue.Add(&wire.DatagramFrame{Data: []byte("foo")})).To(Succeed()) Expect(queue.Add(&wire.DatagramFrame{Data: []byte("bar")})).To(Succeed()) Eventually(queued).Should(HaveLen(2)) f := queue.Peek() Expect(f.Data).To(Equal([]byte("foo"))) Expect(queue.Peek()).To(Equal(f)) Expect(queue.Peek()).To(Equal(f)) queue.Pop() f = queue.Peek() Expect(f).ToNot(BeNil()) Expect(f.Data).To(Equal([]byte("bar"))) }) It("closes", func() { for i := 0; i < maxDatagramSendQueueLen; i++ { Expect(queue.Add(&wire.DatagramFrame{Data: []byte("foo")})).To(Succeed()) } errChan := make(chan error, 1) go func() { defer GinkgoRecover() errChan <- queue.Add(&wire.DatagramFrame{Data: []byte("foo")}) }() Consistently(errChan, 25*time.Millisecond).ShouldNot(Receive()) testErr := errors.New("test error") queue.CloseWithError(testErr) Eventually(errChan).Should(Receive(MatchError(testErr))) }) }) Context("receiving", func() { It("receives DATAGRAM frames", func() { queue.HandleDatagramFrame(&wire.DatagramFrame{Data: []byte("foo")}) queue.HandleDatagramFrame(&wire.DatagramFrame{Data: []byte("bar")}) data, err := queue.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foo"))) data, err = queue.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("bar"))) }) It("blocks until a frame is received", func() { c := make(chan []byte, 1) go func() { defer GinkgoRecover() data, err := queue.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) c <- data }() Consistently(c).ShouldNot(Receive()) queue.HandleDatagramFrame(&wire.DatagramFrame{Data: []byte("foobar")}) Eventually(c).Should(Receive(Equal([]byte("foobar")))) }) It("blocks until context is done", func() { ctx, cancel := context.WithCancel(context.Background()) errChan := make(chan error) go func() { defer GinkgoRecover() _, err := queue.Receive(ctx) errChan <- err }() Consistently(errChan).ShouldNot(Receive()) cancel() Eventually(errChan).Should(Receive(Equal(context.Canceled))) }) It("closes", func() { errChan := make(chan error, 1) go func() { defer GinkgoRecover() _, err := queue.Receive(context.Background()) errChan <- err }() Consistently(errChan).ShouldNot(Receive()) queue.CloseWithError(errors.New("test error")) Eventually(errChan).Should(Receive(MatchError("test error"))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/docs/000077500000000000000000000000001465664453100215375ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/docs/quic.png000066400000000000000000000416231465664453100232140ustar00rootroot00000000000000‰PNG  IHDR^ø=ûè\CZIDATxìÖÌØPFáÙ¶mÛ¶mÛ¶mÛ¶Íh¶mÛ6¾ÙûmïIž¨nïmëE)¥”RJ)¥”RJ9.¿È…šè‚q‚æ(…¸pfJ)¥”RÊJc>Ãpí?§| ºa"Ö`?Öc&ú¡‚â?J)¥”R¥pæ ¯ÐþñßGi0·aŽðkQ>áÌT@DG2dAaTB´GO´A=”GdD"Do¸eJ)¥TBl†ý(xìx·DYKY¿™eéÚÏÒ·éb‰«Ô²¨Ùs›þ~ZP‘ADÄE*¤C|„G¸uá0æ§P HyG\”B,Ã9¼…¹À+Æ,´A>D€k¤”RJåÃ#Øg~C„´ÔMÚXù5›­Þ±+¿UkßiË9p”…O•îÇo×[4„kéQ}°ûq7ñÌ ßÕÛ8ÝXŽî(ð çÖ`0\„?«Yº°ãgZ©Ek­ðÔ–£ßpKÕ¸•…IšÂ¼xõú«sÝ„(ø”J†®Ø‰g0wt«QááÔ”RJ©zx ƒyóéË’Õj`5vŸ0~¬œ$gÿ‘æ7xˆ¿UmáÔ¼"!c2¶â6Ì<ÄfŒ@uDpä9„@ÌüE¬ø¼•ŽºU6î³ô­;›Ÿ ïØ; è(’î‹7– $DÐww‚»/îîn«ÖqY÷Å}qwwwww[ÇÞ¿ÞœjþõõŽôÔtÍDÞïœûI¦3­Ð—ªW÷ç>Ÿ¶L$cªÉoÆ &ˆ%zÍÍßp>œê ‚ ‚è%¾K²FB›5ÛÑH«ë¾Ó±T9ã;j˜æš¦®|vçÄ2äïØüv½aj18w>hú–†) uªÈ{\ubÇ´ÑË#sóøqy2^//õï@@…*jÚxe*ï[4ÓSv~™/(x޽åÒ ïEõ±ßß×±šLÏßd4Iï2Õ` Ò¬%‚© oª}Jñ?Á{{2^//Uäçç„ÙP¦WpÞ±mqØ5¦uL_óø§ªLEù{+”¯ÜtDr¾M.nD;ò0Ô5ÂÀŒOÕì·•Žîöy·]¥ÅRü™&)º@1|Ú²7Sˆþ}ÆtNѹ=eª«y2^//5d¦´L¯œuù: ôW¦vLż0#“ާÃf:îíóM”$‰Ý•ºªŽœ(n?)¶ÎgPpq®ð®ëaZì 0Ó—Lÿ(0–Ã4O!ãEñÊδ!Žk?HhS?ïµZü!S ;ÀXSÆ«ö7“¼i>þbZÉß±y4ß“•©]z¦úü³×¬ëô^”ê7TÜþ3Um òó °ïÑd¼Ù¦¿¼l¾˜ p£Ü” G1…“ñ"Èx‘ñ"bÃõ{“«A§/ù.»O@xÑ’ª›M¿ÃßYq‰$L¹W+UqøHG÷sÒÄŒ´—žÖ’gašÏ-1÷¾–©œ"ÓõŒi(¿Èq‘¼®ŠÍW!þ/€.îßßLË™š3ù“ñ"Èx‘ñ"|B¡½Ã,¯Ö«¶AšLYT-D[ÀT-ž„wæ…þ`•Štîi7R¢ô€wÄí–k’ø1}`œsF³ç„ %JCºÈø8;Ø9L%¹ƒ ´‡OoÆZ0=´Ø|UeÚçAáþŸÄVñ"ãEñ"¶¾©)ªUÿ?/ø†SæA²T©U˜.4 ù´øI+r7j¢ùê´ý0¦Û‹Û´Ô$`ÚfÜY¦Òåmm :ï<ößôÜ3· ãÖƒPñ£Qhƃ[1Êõ.Sb-~Ê´ÈÂÅ`6ðD£œ±TYÛæœõÞ‚ÐÂÅ y`³ï©KÆË«ñ"ÈxÅÅVy¦Í×ß³ØþGhÆl™öó”øNoqߊ떿e»7÷%²j-ñ³£LIej‰;)PHÈ®0§›ö㵕Çi¦¼Zü¦Ó¿V]³¤É“C®ú¡æ„ ë¾Óïg—ø.Ž\Úûžo™‘ñòd¼2^Ätýá?ŽÛ­ßÍWÌY¾ ­eëŒ1-´âîÐÍšÃTJs“4LgÄ/.÷Îp}HMJ©3f²âÙÌ”6½ÈxzÍ"ªÖdßGܾ_5ƇCß7•)1/‚Œ/ÂkqKwDóeqÑ(¾B1!ÓÅ’Enÿ;ù&ÁLqj œ¬áBéÒÓŠ‡dF|@²{·‘,U*œú•ºo]÷ž²·DyS"2^^ŒAÆ‹(® ~èÆÿL+9˜öZ8+—Zs“Vâ—ÔœøƒG¦«Á¯³­8‘Oµ„KÓ:®ç/ˆ9/R÷kö ´îhüÎ!d¼¼/‚Œñ¹Xïå¡æÛ’ßI™¾òðÚÞgŠ”‰6xªI¾m=2]íÖ키)RzÚï©›Fø3-ñäÈÓ¸…G÷²Pû®â÷=g*@Æ‹ ãEÆ‹PNU¦g¥ÍwÖ\A `Š‘4]…4 >Ñ¿$mD6èqø¢ô‹ÛæÈåî;Y!óµÙ“ëYaØçÒ÷³÷©¸úQbÅ/IÈxd¼ˆÒ…l_w+"‚häF{¿™nÉ^ßäb17ö}2ûR®÷ãtÛ2ÊtÙràÊG\^ÉþMO”qa/Þãˆì5Mœ4)´Z¾YÚ|µ]»WHŠßÙ†ŒAÆ‹Œ¡„,e;žbʤîRŠéž‹k»‘w}ɬIÒCÿ²€,¦W0ëÑOE€Û4ÍD_þ 2ÂQ+¼·ú(V“ÙK¡ò§cm¹lMç.wy¿Ë ~_ü¾}d¼2^d¼%õF»-x—îb Ôd!r;1_{yCsXe¦‘¨º?NSaºV¹œÂ"²3Ý•½Æhž°­AêðŒÿù,¼x) Æu¶ÒÑ8êU–ŒAÆ‹Œa)-J O¡žRˆé±áÚž°ÄЊCší6ì1e¼pZÑbÓuÊ­54÷ÿRæ:'aÆÉ?] ãUù B¯×Þ÷­:ˆÛÿHÆ‹ ãEÆ‹°´¾<ÔTKû%™þgíʰTŠàôæé7°Út=c*¨îð®ìõÆz<§…øÃG8¼÷oÍX(n{€ŒAÆ‹Œa ‘LO<|—.UtMTà}¦³iQ_¿iY+V5e¼°ÈbãÕW#Ü%ÓzÙkžÜɨWP®¼ï}÷ƒçŒ†9)/yÈxd¼~xøÝKÓ‹Já!î¯ÈjµL¯Î;Ž‚–(‘U¦ º;/iÈxd¼(:â8HêþçÏ>Db¦P¦"Üë”æ¾!Ô×ýŸÓ1Åð±[‰õ-—oÂFÚ²Ì\EÿÝ™¦3]2ñðâËpë'@3–F6b"EPzÿ¿_ê4Ðnýng÷ÝXBÆK 2^/b°‡ƒ]5B$¿&³˜®1E;¹v/˜ö0gjä ßp˜6¶6m¼x´€Œþvöšˆ§>Œx±—1U×ò†Dãœ"}4[°Úé=o±xqˆ;1/yÈxd¼,˜þdI-Ñ$5S?¦3ô³ü–)—æ%&è;ÏUÉ”é¼/>U%£áLFšðA°§(^¼8T­Zš4iÍ›7‡ *@Ž9 eJ§Í¸O3õˆ%KlË2}Àô+ÓfÁ‘?gzÄ ÄþÙ¦·˜ÂÜXå¸Wæ~¤ …|ÍZC×}§]Þ÷}‹¿;…â$2^Rñ"æz`î0[4W€©Ó$¦ƒü=|›gWEó¥“<”õK¦^L™bÉⲯùq‚=CÁ‚¡f͚ЪU+¨_¿>úÛÏüýý]Û×ü\#4Åä9Nžçòœç­æ²Ì¦ä†i²©Æí’³ Ï:ÀôéÓáÂ… àŒ×¯_ñcÇ૯¾‚† B û+öö3ñQøÚn²¤¯7bY]íK¦HW1¶^±Åå=Ç(‰”!aâï6"ãEñ’ƒŒÕVKê5Sm ö¿ˆ›ÙcØÁGšÂ|¥Ôé©ñ¸²fÍ =zô€¹sçÂÝ»wÁÏŸ?‡Ý»wÄ  **ÊÞ9þÁßGJùáMÀfdlãðÜzÕ6Oâ$šŒÂñóÌ™3Ø1càÑ£G ËãÇaüøñiÜ÷+¦±^*ª+Ä´ÕÙµH•*äÏŸòåËY²d   ðóó33…ZCsÌd™û’½f]w§–¯¢&ãEñ"ãE¸E2§Å¾‘®61%‡3H¡¡¡={v(\¸0DDD@’$IÀE­Ô8>åçA¢íÆc¨Q£,_¾bbb@–ÇCçÎí½ƒ©ìªÎç8V¤8ô8rÉþhWã²ÌVјˆ)½‰˜‘ëׯüóÏ?`ÑÑÑ0mÚ4n4Çz¾JRLß‹ú³Zª*UªÀ?ü{÷î…û÷ïƒ#®\¹óæÍƒAƒAÙ²e=ôx{#¡¼†ÜU›ÕÛÝéÍÙ€šdd¼Èxn3ÐÓuZò¼ í-À „ Àˆ#`ãÆðûᅢ=^½z—/_† 6Àû₩ÌÑôg{>"¥‚Rb{CTùòåáøñã`%§N²&ã¹­Sùç¤h2•‰‚λŽ%cΓìCSSw­âC1cFÛ UŃ }ûöÆc9À”^Á(×ãÞ÷ß÷îÝYnܸLJ°°0{C¡]­Š—(Ü¡›]ÓUëËŸŒõ|‹5„ŒAÆ‹Œá~<¿$UE"µ`ºñ{Ê”)3fÌÀé6Fˆú÷ïÉ’%³Šî¯YK-q@! ~üñG[™‘ ^¾| ~ø!‰çµPeÌÿ7Œ£o^ÂåßûXö9Ì„ñi*@…‡‡ÃÅ‹ÁL:’þ¯8Ëhá_zOĵ>úÈÒ<|fΜ‰ÌÕƒžE¦‰¶_šèyôÿG9{ŸºÁî÷GÆiå“L!d¼2^d¼·éîéZ¦¹Ga¦[âwT«VÍf˜¬äìÙ³P»vmã±náS›VPO|ŸáhÛ¥K—À  ¹3ž×ª0Gk€ÏY )CÃdš–LÈúϰ¦éĉàMV®\i,¾_fQzü?úwâ¼øÕ«WAXÿÖ¦Mãõ]a¨]›&sŸªŽœh3]Mf/…àÜùŒŸF Éxd¼ÌCÆ‹HÂtQò½t3æ ?ûTêÔ©á§Ÿ~R6B„ÌŸ?Äc>hÁû"BÐ(V¬Ÿ=òãÆ3.*ˆRÝû:X Kü¡+Æ£×tmß¾|ÁÖ­[#_C5y²‹µq¸:âéÓ§à -Zd|Ð M«óɬp È•«ÛûlØÞ‰ŒAÆ‹Œám˜@R_kæÉ%–ò-Z”¨gÛ¶m8 h¨§öhÂ~q@ã?þ_ЫW/ñœŽ¢ŸQj?K7K¨/¸Û­ÿ¬K—.àKpÕ£á_%$3Pvè߃íÿþû/x“µk×Waü,ßR LóLýõ42^/2^„T ÉûóÄôŠ5d… â ÞãÈ‘#Æm}49Æëß‘&M8þ<øLÁ,Qñœújªáz†d#Ï{|FÝ7qéÒÙ Þ} ¹bæ—pœ«5÷yçÍB„L™l7ǬX±ÂXàXIö…((†iOWæñ"Èx‘ñ"$hÄ’¢™çþ;UÄWÏ{Ÿ%K–;ÕäHõ®Ço¿ý>kÄÅsºÏG½”ó™äC3Š ™¡ÿ G›b8Wl¨÷*ª™'«ø`¬[·|ÉĉKŽõz¯ƒ÷쟪!ãEñ"ãEȱ_òÞÜpãþ”ÕG𽆱D¾ÐÅø#Ùn:5ޝkpÚÖÒ Ùz¯<|ÅÝïzžÕ;w ¶0`ÀñXhæù\ÿ½ž={‚ÁœœÏå&¤¿ä}ëIÆ‹ ãå1d¼ˆŠL ©wÝ(Ü?¦ÿÞèÑ£ÁÇà FE‰ç’[3GX7½ÿ~PŠüÇ$M1%$š}ÆaÖÊ•+C,3²Äiº¦p“ùM¾H€.úž={öˆ¹#x¡}°D´j/‚Œ—Çñ"&{д9©€ÂN(=5j”x>ã5s éc›_Þ±UwÁ)ùàô5N3b.†7xòä‰Í6nܪW¯Ž£R¶ÕŒöhÑ¢…1öÂõõí±ùf,‡eÅs©Ã„,‘\¾œ–ŒAÆ‹Œ!M ú!~§™g¶þ{ ,€ØÂíÛ·ÅÎ+÷M••ú¹` #Ïž=³Ec4oÞÜ–KÖ±cGXºt)x LË÷Útãi©:!¾Cè ÅçÕü`³L{ÇÕ·o_cZ/šAwÒøâ!Ç9æ‡Ú<`sPñ\¦1!™@BmÉx^7^/ŠˆaÊ©™#9ÓŸ<¯ËéÊzøë¯¿À‹±52‘ðT/K2¶0:w¤æš4iâ•nŸþ¹¸ßaš"òH>8‹…‡"š §ôlýU‚}yF—CÕ«WDNŸ>mLfwÅ%=þÅ‹ 4[¿Kjõã,Q¢„=—¨ÄOù_ªÉ„Wîh!/‚Œ/Bšu^H©oʃµíŽaE±ãIΜ9á»ï¾S¨êd0àSÍ9EŰT‘k×® ï@ƒ„ø ÕÓ¬sæÌ÷9_SÄ’Î[LHnýg9rä• ©+R¤ˆ™c3ŽT‰¯™\üë"†É¶/Õ`ȬX hTŸ>}Œ:q›òLÈ·÷ðo<_2^/2^„ÛdÔ$TY3‰Ø fñâÅ ‚ÙW8BälâñãÇ ìNãÆ¶>ú¶ƒvPFã\cÇŽU>¸#ö|ŽMË`ÿþ0—÷V=ÔìÙ³M#ö{)Y²¤q%¦#ŠèÛµnÝZuÜfž¹<úÀ‘âçÝ™(&P#2^/2^„Të=ÐIÍ=¶é¿‹FKìí[ @—ûÃg•`iÖy™<·úq}ýõ×bùéë—*U*Þ#Y ˜&ì€ÌL¯=&­mUŸbNb£ÍŠ+ŠŸU03gÿÙgŸJÚ¶mkêqë®_¿T#î롦€~’No&júÏëÔ©*Á¨ Ü¿¿?TªT ÿÛéqnÞ¼tjÕª%~VÅLZýÌ™3•Žvñe«¦4tèP1É^üìGáØçKÜËGLIÈxd¼LCÆ‹(Î2’Hyÿ“ {$Š ±„š.×Â÷ŸBŒï×p3ÑÂûkœæÌ™ÅpS˜4i¨[0 û¾­)@¶×_„½¿”q:O%˜ùûùùçŸùõ×_çŽ;ÄùcS#^b éäÉ“AË–-sëš—+Wt8 ~6S8öN’÷³/‚Œ/ByÓ!Í]ø¬Tž8ö’\éŸÏ˜1T‚Iõ¸???‡Çˆ7êàÁƒÀÁ©IñóŸ5çÔÓ·íß¿?¨âæÍ›oâ"8¸ô×á9 Ç‚ËiÅÏÞ±`ü./‚Œ—)Èx-¤[ìI öhĤx½<88Øá¾0[KïñˆSJƒ¿ƒ‚‚ô}?Ñœ“”÷uF£%¨:õÁzh½õߢE‹@%˜Î ì»±f1Ó$œyCl#íõmš6m *¹~ýºÓÜ+ÔðáÃåƒ4Õœªo›?~剿ø/\¥ˆf £ñ\ôôøñã 3pà@gqQ–|’ñ"Èx‰ñ"&IÞ‡!šb'¬ïÕ6l˜Ó8¥ùóçCçÎñÿÛþ·*Ž9"î{½æš%b—î4C}ÄA4x¢ßI©YÌE‰g‡fŸ@¦Wm`„ âþÞ×\ÓSß¾T©R €ù\vW8¢Â:0žÄ¯¼öšk¹f1á’ÎD­Äé3å ³xÓ?þøcœâ´W_†6ÍN3Š|ä|õ‚MB…¨âñ‹aqÇŽ??ba(î$2^/2^„’Úè[š<ùÅ–9"‡†ÈÈH‡û-[¶,ŸžTŸ6ÀUJsM*½n …+üœMª™ÚŸÓšgøó¨¿íõ`ÂAƒaû[×ñK—.Áü¡×7¡ÉÂV ØX0l”éè8·3åÓÁ(IjÀE>·Iž<9f¥XúÀ¿|ùÛ—¶¾ä­Ü!@ò¼+‘ñ"Èx‘ñ"þÓÔ¿;ÝŠ<š%î+gΜ–Î&!gÏž…ÚµkÏé>SY&‘¼ÜŒ¬„…Î|ö Dafì9‰µm7nĬP,òÇE¶iÉØ,¶lÙbëÏܪU+ s´¯óL 5/rKâ‚XÈk¿î™Ø'ÖT¹c’ª†;…¡Niµc2Òʸ*4}úô0dÈÛM—Á³×‡òš™"÷%ι/‚Œ/â¨&yíçkêHÂ^Šû¬X±"ŽI¯zÄ 0,pÇ^Â]alôí ¦k­ïÛu†…ƒy}8Þð¶€f´›©17®^#@A« üyoÀyº “Ð5^ WU3Â/EÜÆ­ÒìÌç±cì ‹bº5P………©¯r®k³ÛíT¼xq_ß½ÊsÑ¢é×½œƒÃÁ‹ƒã¹ß”„‰"o(ûÀ¼‰Ñ+uwØØXµa‘¹új!£EÙ0n8¬ÖŽëW+„Ö/mpPÙå<êmŸ¸¸¸`(ô‚`‡Á/ár¸ž‡çàN¸ ~ Ç–°Œ0­!IØTg#¡±úÚ#”›œ†èG$-LÚç!Ðݱ+¶ñ¹tˆòðcx’¤—á,_OÀÈnéñäàÅpðâàŸð$ £DÞá:ùQ(‘—ÂDoD*‘Ѩ·›´ÚXÉsyŒ”eJ•V?OU±ˆ´ÙšAa&½Ýå·’0&QQQV‡¢di ¦™Ð¢U +#ß`ÙÒeôŸ©uˆ ÑÑÑ¥¡ð&¨[Á±pƒnäîžnKˆS0 Ά=`Íhι•Äu_áàÅpðâàŸpNr7€@ñïQ>“áj¸fëjÑ-xî‚ à§°1 …^AŽhjW”¿Üëbh¡BòµÆ×®Bõž¨îQoQÛÇaA½ 3èõ H–¸)w`C†.»½ ç$Í—›Ö¥­?§›»¾¤ ¿Ì¢O’^¥"¡!8ÅH)}ÚÒŸøùiý©ÝËñnºéQ;€!"o© IÂB¼^¼‹¶ž÷Ý!Œ‡t-Š¶Ùªêg•b*;hpö”5o$\7…ž­_ý‘ûf”5œ~œÐS­×W³Òé‹”.T#.Ú£ÞV­Zµ$F×ëA*ÑyN RÙjmˆF¹ñ 84kxgº±3SmDM-€¥&w Z5¡1d±zs–,JMjÇQßN thÅxÏHMéJøN}O†ÂÈ‚0H*¼^¼•J$œ/ü<4êœþÓê_ý§jЮE£=êäò´Oé½–©V¬•‚ƒë%F°Ôúz`ÙX:²jFÅ&ëF|;mpr(.ƒs¡0²^‚m†{ÊM»¢|÷wèªF‡VNP*/L|¶®#ØAadÁ%‰6ƒÃÁ‹ƒ£Ò’„ý&xEXku/-¥kžÕÛ¯F÷pùªl³½…QõvPvóÔ¯Õut£NôëÏÉò +áü韸Ì=CadÁAéíC8x1¼8x1= I˜$L޶M“Vóœë¯.ÿ‘–§5wP÷v¤}¿3QFÔãÜÍ}¬¡‚—¢ŒÕ¡Ïû ò *éÙMÓõÓWÚ 4Á&éßÔ8x1¼8x1£ Iø24=vÅþšVóÞJhžç5÷âo_PMÝ‚{‡ÃQË4k¼@H~j°iÆÅZÌ™üQžwÌQë‡>ï™ xÍ‘hói¼^¼•o!Iø$4=•#líµš×ííWò´Þj¾›ØâïéFÅÖ #êqT$áÆñ²Î×ó¿yÞޝ¬ñú # fH´ù\^ /^ŒÊHV„¦Ç¡(Ïj5¯íKÿ•àտŸ¿ë®ÕÚ #êqÄC’ðUa °niˆÖØ_ËgƒiOW¬ÌèOÃz¿AImž£g×êÔ¬ª>÷ŸgëS¯w_£)“hÏâ1Us®¯nõ`ë(›­2FÔãx’„õ„ˆ²Z£´Nàtê ¤\lß’±4¤çëôdÕXõ\_>ߨ6ïû>íÎ¥CìZ8šªFE>8ß9 …•]ºƒÃÁ‹ƒ£’-qϳ…aW”%ú+úxy[­Ê@}˜øÐ5÷å&u(cD7ºðë,¯5÷Òï©jXÓm)±ÕTÛI€HV7࣭“u§îž{bÝuýÕeãÔ)HmhRÖzOÖ O:´¢C;Ñzã;ÚÑUtI±ÒÞ¤ndÁûm¾‡ƒÃÁ‹ƒ£rUâžð«àaGÝ»ÉéKL3‡uRkíÄþª3GU¢*ËÖ[u@Ë©oŠÁlj-ÿ}NŠÌôkª£ìözPU $ c ¼Ž‚“[ܯ0ȵa£•©Ub"e¤§S³¦MÉhQ_‚]«fMêÔ¡ÅFF=JG¹iµ> …ÑoH´ù^ /^ŒJ޽.H‡bûüQÂTýÚu¨OÏ^T84”‚,Ad±XhæŒ4lèPjT¿Çù¾j9¾¿FÖãx’„6a@ªU«Vë½ùœ>|öYZºd Ý¿Ÿ4Þ{ï½ׇùkrróæMZ´`!uîØ‘ªDÇäþ™ŠrÜ ¡K´–hó3‚ƒÃÁ‹ƒc$á/PøaøúÔùÖ–ÜêcÃzõiØ!”µ-‹œlÛ¶Íå¾ìÞ½›4®^¹J£Gò^ouâûîâ{;AaÆàÕÑŸÌpX­­Ð8«Ñ8w´Ñ¨Ç*„Ý+Y¼¸š°¯_¿NîôïßÿÁõ•-[–ܹ}û6mÞ´‰j?ý •)UšÊ—)K…‡/r~—YÞ”® ^•hó ‚ƒÃÁ‹ƒS’„넟§ÄU@¦èV¾üÉreÊPé’¥¨g÷tèàArgùòå.÷åüùóäÎôéÓ)$$„Ê–.MJ¥Ç/£†ÿ’]±fÃŒh›- 3èqô„$aÓtˆ¸¸`üÉipp0ycæÌ™Úµ©á,7:uêd¸m5$xN¢Í¯ ^ /^LYH.þÏzHN·lÙBÞøî»ï\îËÝ»wÉüQÎÏÎÁV­ZBa6=€!IXL˜‹ŠœV¨P¼ñóÏ?»\ã;wȽ{÷ÖŸ×]˜ÙmDî ^ /^Ìã$œ'üŸß 9ÍÊÊ"o¤¦¦’vN`` ycÕªUúû¶Fø!IXP˜ ÝF±±±±äE‹¹\ã•+WÈ}úôÑŸ÷_aNêB’°@>^ /^L$ ¿þÏHNwìØAÞ˜2e ùšBhóë‡R I ÌEmHNk×®í;aÃììlòFBB‚þ¼Öœ< IÂÐ|¼^¼˜$ÌþÏHNgÏžMÞ3f i焆†’7>,ñD½y+ÑyîóÑ’Ó† ’7V¯^ír.\ o`ÄL^5aNªB’°$/†ƒ/)øžOþÏHHN“““ÉãÆó¼N:¥¿o§„Ÿ1I¢óÜ0óo(5kÖ|¨5^999äÎéÓ§©@Ú9÷`ˆ0'1$,—σÃÁ‹ƒI ÂßÑmQÕ´iSòFFF†Ï5^{÷î•Ø¼Û<Œ‘è<9f^ AÞ=z´Kg8~ü8¹3tèPý}X*ÌK5H–ÈçÁ‹áàÅÁ‹©I©Âÿ©s бcÇÈ””Ÿë©7nܨ?g½ð3’¥Y›‹Âº¡M†¾xñ")ŠârÑÑÑtîÜ9Ò¸|ù2………éÏIæå)HÊçÁ‹áàÅÁ‹y’„i"0’Ó¤¤$Ò¡®û r¹/;w&wÒÓÓõçÌ…üT£I§Ø.Arн½(33SÝHuíÚµêNõno'§ááájp®ÿª[·®þœÓÐ’ï~cã§^¼˜XH~+òÍ!i~üñÇ´iÓ&êÕ«—¶TÇ÷ß~›Îž=«ŽõíÛ— .¬ÿÿ…_!¿Wqa>’ éôÖ rà xÝÇõ?'ÌMSÞÇ‹áàÅÁ‹‘ IÂÙ"ÿð•{‘gëajRÕí¼ÿ³w@®e[Ç¿ÖØ¶mÛ¶mÛ¶mÛžycÛ¶mÛ¾÷v½U]_åÛÉQþ¿ªUš™÷’µöI>gﯣÆTÅlgßd]N[EýÐ÷ô}ÔJ»ÿõ¾ü;½£6Wù-ÁÎõ¼^ÝìùMjCFÝÔ>\TÛš¨3êÜþôë…¨iUA›› hl•×HµO_®½—k7=ŽÞ—ƒP׋º¢vÆÖµ@VË3ÿ’à‚Á Îìù]j-QkD]S»Aþä¨Yô_ ÔŽSú5ªwÔ µŸQª¨ Ì4¡ªa¼¨6µžU™$‚^/ jöü!¡†ˆU-`MsM!”™tÞ&xõÁ‹à…n£çO +šíôB™mlÌü‚W¼^øÙèùKÂRæE;¯Pf;3†àÕ/‚¾â3+k\´+ ev˜}c(Á /‚>4zþ«€0yÑn.”ÙÙÆÌ/%xõÁ‹à…§Ì¾@ë0”¹xöÊìû€W‚^/\oö}J™ÀM‚' eö¨1ó=^=@ð"xáT³ï ï‹ç2¡ÌÞ2f¾Á«^/ìiö}-á1vàm9ß3_†àÕ/‚Ö3û¾³Là~Ÿç…²ÌüÀ˜àÕ/‚6û~ŒLà ·…²ÛüÀ˜€àÕ/‚¦0û~©€p¨±xzGu©´øiÔ¯ x¼0ŒÙ÷û„mÍ4±PFk³þQGðÁ‹à…Œ¾. ,i^¸‹«Œ°Ÿ1ëç^ÿ‚Á ¯š½™Öa"sñl­2ÂÅÆ¬ÿGðú¼^¸Åìýü´Q¿‹ç•7f}pƒ×ûÊfkñàEðÂáfï·`~ez³Ê_³^·€Áë3å ó¶xð"xa ³÷§ ׋çu• F4?(fWs­f¼¦o•',Úâä¼0eÊ“À‘Æâù=ªSe‚Ù z¢þrÆkúUyÂ2ÆÌÆVu¼Ðõ‹Ñû¯„Ì‹wBU[I|©æ[Ìx]ÝÊVái.‚ô¤ÙÿÑhæ1Ïòª:¾Ù|XÍ7Ÿ¹þU^°¶1¯¡‚'¿°zŒf.žCT&¸Ï˜ñyÞMåóêTµ¼°ÙÿÄOŒÅs‡ÊíæNË;¨ù¦-ÝF„ØÊ8f /žîí)=" \k,žo¢ÚT˜Êü€˜CÍ7©ùÚÆV^°“qìTÕ¼0¼Ùÿ^QCÒ>ìf. ITXß|ru05߸æÚ›ByÁþÆCUDðÂëæ ¦upop^Se€SÙ>¡lŒRº£7pZ³úPÕDðÂ)æ ¦u*ª·±xŽSà)c¶')Ø\««,ØtùMUÁ Ë›3xP@xÎX<©è0˜yçZÊF‡ú·W^ðX³zJÕDðÂpæç×oQƒÓ>œn,ž_*»xاmbeç#É.•÷êœÕ ª.‚1ç°ˆÀ ØF-&Ù!ÖŽõÙz"‡=Æ|øµÎY®ê"xás\Âäæâ9N(²gŒ™Þ\‚íLnÊòý¾ª.‚æ6çðETíÇÆâyU(ªÑ¢º™î\‚'ƒžÊò ÚFª.‚º¢~`[ Î4ÏxB­W’=²öä”ÿÒXÔ˜Õª6‚n4gq­ÃræâÙT(¢ËŒY¾S¢û GSÖ°ƒ1§éTm/lmÎâKÎ1ÅPQ¿‹ç¡h:¢¾2fy²²·ˆù¡µ€²†³9W³¯^ÜÖÑ›§]¸ÝX8ßGu© øP^BÙ›Ò|­[+kxÔ8zªMÕGðÂ-æ<ΤuØÖ\¡ 7tî«âÝ|ï³Ná~ûœ™‚×W7-msáܤ¬`˜¨oõ*àS¨Ïšëmn5 .4æq‘@ðÂ3ÎŒøò#8O›Õj*e;›ó¹ Bßܪ"ᔵ‚VM^Oˆo9pÙ¨óÕlÔüspwÔ”*žù͵öET§ «›kkTà…Ž¨7Â×BË+êwcÑô‰šFÍ„]Í úzÓ Í.!á Íg$¼P³aBðú,jd¡ec.œ»Õ,-ê{s.s¨¸®7ßÓ…j$ŒdþÂu˜‚jÚ£žJ_ר¥±9gsá,¯"aß®ûUl[˜ïë—¨Q”6M­ÕüÁëߘUwBøZWhYW™‹æÍ†_ì˜1!/¡bÙù¦¥V«0xÔçFÿ?ìëÉ xἄàõç ·®Rû.j$<`Îá•Õæûû&jh!ÕÖfÿR߀à…QkÊ _wó”#÷z9‰}T5VK¸xçoÍwRtE½g>Í8ú/lW›[Û ÜÐËn¼ôÿs³ÿW•ì†Ô÷Í÷ù?\’¬Ëƒ4/4EgÔ‹ Á«WÔBB^ŽˆZU9Ù%aál!Ú¢nM¸ñ||•Ëþ ël[90XÔ[fÏ×^°ö*¬£¾å,Ç\ýµX^{-½a.šŸ£&WÖøšú`•ÏxQÝ Ú]õ ÷Ö .¼00NN _ïpëN®§Ãü5§r°L¢y¦î‹ÓFýšð§·!UN·&¬³KTLõ›Ùë“T¼øfùÙÄðõ¿ì亽ѷQÓ*·',š£Tí)¡×k«¼æôÞ³ñ0î6{üº[^ìùcâçÛ•<éØTë à¯.ŸFM¬ŒM”°pº3ÙX•ý_î­À…ysÂû%jP ÖIèñ‰²àŃ,‰uVT»ÐhkFõˆþ¿5–2¶Q‚ùe€G×à „þ~S‘o"fLÜõùt¡&OxRùgî¥#x!É _ÿc® µS?s^ŽI»:aÁ|5©Ð7›%^Œ«qjŸµžÐ7ÃD½šÐ×#‚R õZÂ×mQC)Ú£Ž3ûÿDÖ›wõq⣠·Ì?¾æäÀè)¢ú$~»:½ðwmQ×&ôô‡†ü–‚¦‹ú¹á롨áäÀ QW$ökel‘Ä?=ý´ÈêωAv¾’ÿO½5¢„š}ûy€@ðB£,Õ«á빺¿ÈÀpQ÷%öý¼¼î§>6ñ…?,~0ÎõMBûDÍ¥j%ê‹Ä5öLÔÂŽ‰}|GÙ ^ÿgï€$׿8ŠWkmk°¶m<Û¶5žm­mÛ¶mÛ¶w÷ÒßœüÒé$÷T}Ên}'Íx/B 8ŽÛ ý÷*`+Ä‚ñ9y¨n)Ä‚íH„{ ×!|/÷(DÇ—CÿyHA]˜OÓ᥵†ŠoÆ¿N{ÓÀ)Þ¹HGË‹Ý Ž¡*üÔHX0~hÄ¢µ>_ ýûÒtxi?A YŠh-FC,Z‰¬ˆ‰Já Ä‚‹¸ ^/`èC6ÏG_¨¹q bÑf”„Š #Ä¢­HM‡—f_ô‡r@ãVØoè»07bª†N›ÝD[áÅòa‚¡7@ø©û!\ÀðrqX±(5¡éðÒì/Œ‰ƒ¦ ,üXVü€$C¿¹…“=TˆE‹Q^êAœ4te"üدRñBðZ·ãÄ€–ÐtxiÑ+‚þƒ’Ð ¹á‡‚xÇ!,sÃe*í!\Æp{Ù ~® üZc!†¬D x¡¼è…ˆ]áLš/íKˆaçÐxµúXmøˆa&¿ÜESQnìn0xЬ!ü^F¬€’‚îÈ7Â{8gøË&çÒtxi¯ bØn¼ìðJ1 bЄáªÞ@ŠÁC¥½‘7t –@ 9…j Òòa/Ä Óh‰lpCa<ŽMƒÖ! œOÓá¥Ý‰K\FwT‚ á!̃ö#\ÛS†ûMtGb±FX1èÊàïÓÊà4İ øqˆÅ²£@ ;ˆÂÐtxi±SU…Øh!žpÉÌ9Ñû!†%£9\ß½¸1èz£)‚p²,xs!†íB´]Y[„±x ÙàdpúÚø×ïVÄÁ«À7nfáZÑïñ‹}¿Ws 6»‚ÉxÅ+Åã-LÂUˆ £!Ö`'Žá ÄAûQ¾é^œPëQšµÞø¯huÎ_‡—/­vAþg*=¾+ !ð»nÈÍL¥°òOÔ<ÄÁéðÒá¥Eð.N@þ#µUáëxG!>´  ™/Œ¯ ßÝ@á½txéðÒ²â‹y3Ž:Œ§¡ýMYñ#n@|à^Gö¦UÆ4ˆAix?^:¼´üè‚$ˆÏ]Ç·È íßT Ó<þòr ºiͰ â# Qšÿ†—/­$úã*Äg.âw$Bû»Sê¡ÕÝe¡9WO`›nÔ¸š/^ZN¼ÍÛƒ iL+n¸q¡£øy¡ÅN4ÃHÜ„xÀ5 D=h:¼þ^Z} Ä5Þ4ô‚0”– íqâ«ð,ÒA‹íò£öB\h;Z¤áÉó:¼”/= 6×]|dÿkT‚iÔÆ·1vØ4 Ñe ¹³JhÅH†Ä ›˜‹–ú^3¦i™p~ÇÖ?ª?o"šCGsÌwà”Ñy Ç3z´Á“åÂÓèU¸qÀ,Eg<‚l°+MÓ´8¼‚aØ‚›œÅBtÆ}È-ÆŠ žÃO˜ièrIØŠø¢8‚ÐüS•ñ:bÖáÄ¢TÇjŒÇ/x傦išS…Q¢`%.B ¸ˆåèƒæ¸ … ¹¸‚8 Iƒ5HMûOe@ 4Ä]xO㼋x/ã)<ˆ;Q‰ˆ@Ó4ÍmeF”D54ƽxo 9ÞÀS¸ QÅé y´¯ i¤îýÙÞ=Iv`]olÛ¶mÛ¶Yˆm;)ĶmÛ¶mgcÝüA!~óºg§{öœª¯¬[¾¦€1ا>)Ðç4p“b¤T³¥R³]Sp*5z;õMT´f*5[;PQ¿ôn*5z7 —¨h—TjvBª€ÁÓ« ¼0>oª€åR©Ùói°T×¥R³ýSELš¾K¥Fß§ùREœJÍ>OÓ¥ *½JÍÞM$*X=•z)š¨àŠTè‘4Lú ›žK¥Hc§ÿÀdéóTèôHú,~N¥~J{¤žé?°{*Mèš4BúôL§Ò„ÞHk§^é0Tz*•&õdZ&ýÆK/¦ÒÄîIó¦¿`”ôP*MB,ð‡¡Ó©tB_¤³ÒÒ©_äôKç¥Ò‰}šNMK¤ÒÀ2XZ1ÍZ@¯tL*©7Ò•i¿´Rš8õlÒ8¦J›¦³Ó穤ãRKØ%•.j@z8Ý”.I§¦£Ó>i»´qZ5­–6H[¥ÒéÒôhúâ_ÿ›lA+¥÷SéFýO-`„tZ*ݨ¹RËX(½œJ7hÛÔÒ†H‡¦Siã.Hma†ôH*mÚk  môIë¦RiÃFMmg–tFú6•6i™жFN;§×SiñöOm¯wZ.ݘ~J¥»)u+äÅÒ¾éÖôu*]Ø÷é´êÖú¦YÓ¶éÒôA*Ø[颴mš3 –Y“¦åÓÖétNº3½˜ÞIŸþÃÁý¯Ó[éñtkº8J;¤•ÓX©z¥!ºdÕ ~ç×E´ûýpIEND®B`‚golang-github-lucas-clemente-quic-go-0.46.0/docs/quic.sketch000066400000000000000000002600001465664453100237010ustar00rootroot00000000000000SQLite format 3 @  -æ mµmFetablepayloadpayloadCREATE TABLE payload (name text, value blob)IgtablemetadatametadataCREATE TABLE metadata (name text, value blob) xšT°`ýÉzÂxH variant streamtypedè„@„„„NSString„„NSObject…„+ NONAPPSTORE†O autosaved streamtypedè„@„„„NSNumber„„NSValue„„NSObject…„*„„c—†e #8saveHistory streamtypedè„@„„„NSArray„„NSObject…„i’„„„NSString”„+NONAPPSTORE.28276††Mversion streamtypedè„@„„„NSNumber„„NSValue„„NSObject…„*„„q—R†‚1„Xcreated streamtypedè„@„„„ NSDictionary„„NSObject…„i’„„„NSString”„+app†’„–—com.bohemiancoding.sketch3†’„–—commit†’„–—(03fe9abc608b3dfa53ba3091b740ab38ca8a5006†’„–—build†’„„„NSNumber„„NSValue”„*„„q›tn†’„–— appVersion†’„–—3.7.2†’„–—variant†’„–— NONAPPSTORE†’„–—version†’„œšž›R††aAJKLMTWYabcipyŒ’•—šœ¤¥¦ª­±ÈÉÎÑÓÖØßàèëîøú "-58;OPUXZ]_fgmpu„…†‡Ž•–—˜Ÿ ¡¢©²¿ÒÓØÛÝàâéîñôùûþ!'+.5<=>?GHIJQRST[\]^i|}‚…‡ŠŒ“”•™œ ³´¹¼¾ÁÃÊÐÓÖÚÜßâçìïò  #*+,-4567>?@ATUZ]_bdkpsv{}€ƒ“”™œž¡£©­°·¾¿ÀÁÈÉÊËÒÓÔÕÜÝÞßñò÷úüÿ  '(-0257?@FILPRUX]behxy~ƒ†ˆ‘”™ ¡¢£ª«¬­´µ¶·ÊËÐÓÕØÚáæéìñóöù  #&-4567>?@AHIJKRSTUghmpruw€…ˆŒŸ ¥¨ª­¯¶¼¿ÂÆÈËÎÓØÛÞîïô÷ùüþ  "#$%,-./BCHKMPRY^adiknq‚‡ŠŒ‘—›ž¥¬­®¯¶·¸¹ÀÁÂÃÊËÌÍßàåèêíï÷øùý  !&)+.07=@CHJMPUWZ]mnsvx{}ƒ‡Š–—˜™ ¡¢£ª«¬­ÀÁÆÉËÎÐ×Ýàãèêíðõ÷úý       $ ( + 0 7 8 9 : A B C D K L M N a b g j l o q x ~ „ ˆ Š • — š ­ ® ³ ¶ ¸ » ½ Â Æ É Ô Û Ü Ý Þ å æ ç è ï ð ñ ò ù ú û ü        " # ( + - 0 2 9 : ? B E J L O S f g l o q t v } ~ ƒ † ‰ Ž “ – ¦ § ¬ ¯ ± ´ ¶ ¼ ½ Á Ä Ë Ó Ô Õ Ö Ý Þ ß à ç è é ê ñ ò ó ô ÿ      " ) . 1 4 9 ; > A Q R W Z \ _ a f j m t { | } ~ … † ‡ ˆ ‘ ’ ™ š › œ ® ¯ ´ · ¹ ¼ ¾ Å Ê Í Ð Õ × Ú Þ ñ ò ÷ ú ü ÿ        0 1 6 9 ; > @ G K N U \ ] ^ _ f g h i p q r s z { | } ‘ – ™ › ž   § ¬ ¯ ² · ¹ ¼ ¿ Ï Ð Õ Ø Ú Ý ß ä è ë ò ù ú û ü            - . 3 6 8 ; = D J M P T V Y \ a c f i y z  ‚ „ ‡ ‰ Ž ’ • œ £ ¤ ¥ ¦ ­ ® ¯ ° · ¸ ¹ À Á Â Ã Õ Ö Û Þ à ã å ì ñ ô ÷ ü þ!#&(/58;?DGJOTWZjkpsuxzƒ†‘˜™š›¢£¤¥¬­®¯¶·¸¹ÀÁÂÃÊËÌÍàáæéëîð÷üÿ   %(*-/48;BIJKLSTUV]^_`ghij|}‚…‡ŠŒ“”•™œ©¼½ÂÅÇÊÌÓÙÜßäæéìñóöù  #&+2345<=>?FGHI\]begjlsy|„†‰Œ‘“–™©ª¯²´·¹ÀÄÇÌÓÔÕÖÝÞßàçèéêýþ   $&),1369IJORTWY^bepwxyz‚ƒ„‹ŒŽ•–—˜Ÿ ¡¢©ª«¬¾¿ÄÇÉÌÎÕÚÝàåçêî   #(*-0@AFIKNPVZ]dklmnuvwx€‚‰Š‹ŒŸ ¥¨ª­¯¶»¾ÁÆÈËÎÞßäçéìîó÷ú   &'();<ADFIKRWZ]bdgk~„‡‰ŒŽ•š ¥§ª­½¾ÃÆÈËÍÔØÛâéêëìóôõöýþÿ  #&(+-49<?DFIL\]begjlqux†‡ˆ‰‘’“š›œ¤¥¦§º»ÀÃÅÈÊÑ×ÚÝáãæéîðóö ")0123:;<=DELMNOabgjloqx}€ƒˆŠ‘¤¥ª­¯²´»ÁÄÇËÐÓÖÛàãæö÷üÿ $%&'./0189:;BCDELMNOVWXYlmruwz|ƒˆ‹Ž“•˜›«¬±´¶¹»ÀÄÇÎÕÖרßàáâéêëìóôõö#&(0126:IJNTUVWX\]^_fjnpqxz|~€‡‹“—¢£§¨«±²³´µ¹ºÅÉÍÑÕÖáæéëòóöùûU$nullÝ  ![layerStylesVassets_enableSliceInteractionUpages_currentPageIndexV$class[cloudUserID_layerTextStyles_enableLayerInteraction\cloudShareID[do_objectID\layerSymbols]cloudShareURL€€ € €€ €€€€_$12582539-B9A4-49C0-A917-3CCC926395C4Õ%&'()*+,-YgradientsVcolors_imageCollectionVimages€€€ €€Ò/01Xarray_do€€Ñ3€Ò5678Z$classnameX$classes^NSMutableArray£79:WNSArrayXNSObjectÒ56<=_MSImmutableArray¤>?@:_MSImmutableArray_MSImmutableModelBase]MSModelObjectÒ/B1€ €Ñ3€Ò(GH€ € ÑJ€ Ò56LM_NSMutableDictionary£LN:\NSDictionaryÒ56PQ_MSImmutableImageCollection¥RSTU:_MSImmutableImageCollection__MSImmutableImageCollection_MSImmutableModelBase]MSModelObjectÒ/W1€€Ñ3€Ò56\]_MSImmutableAssetCollection¥^_`a:_MSImmutableAssetCollection__MSImmutableAssetCollection_MSImmutableModelBase]MSModelObjectÒcdeWobjects€€Ò/g1€€Ñ3€Ò56lm_MSImmutableSharedStyleContainer§nopqrs:_MSImmutableSharedStyleContainer_ _MSImmutableSharedStyleContainer_ MSImmutableSharedObjectContainer_!_MSImmutableSharedObjectContainer_MSImmutableModelBase]MSModelObjectÒcuv€€Ò/x1€€Ñ3€Ò56}~_MSImmutableSymbolContainer§€‚ƒ„:_MSImmutableSymbolContainer__MSImmutableSymbolContainer_ MSImmutableSharedObjectContainer_!_MSImmutableSharedObjectContainer_MSImmutableModelBase]MSModelObjectÒc†‡€€Ò/‰1€€Ñ3€Ò56Ž_#MSImmutableSharedTextStyleContainer©‘’“”•–—:_#MSImmutableSharedTextStyleContainer_$_MSImmutableSharedTextStyleContainer_MSImmutableSharedStyleContainer_ _MSImmutableSharedStyleContainer_ MSImmutableSharedObjectContainer_!_MSImmutableSharedObjectContainer_MSImmutableModelBase]MSModelObjectÒ/™1€€Òœ3ž[NS.object.0€€ß ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¹º»¼¾¿ÀÂÃĹ¹Çȹ͹_originalObjectID_isFlippedHorizontalUstyleXrotationUframe_hasClickThrough_horizontalRulerData]exportOptions_layerListExpandedType_includeInCloudUploadYzoomValue_verticalRulerData_isFlippedVertical[nameIsFixedTnameVlayersYisVisibleXuserInfoTgridXisLocked\scrollOriginVlayout_shouldBreakMaskChain^sharedObjectID€€*#€' €!€ #?ú ;Y|¶0 €)€, €€€€_$1C627570-5E7B-430D-8EBA-FCED2EA2E965ÕÓÔÕÖ¹ØÙÚZshouldTrim_includedLayerIds\layerOptions]exportFormats€$€&€"Ò/Ü1€#€Ñ3€Ò/á1€%€Ñ3€Ò56æç_MSImmutableExportOptions¥èéêë:_MSImmutableExportOptions__MSImmutableExportOptions_MSImmutableModelBase]MSModelObjectÖíîïðñ»ò¹ôô»Qy_constrainProportionsUwidthVheightQx€(#@rÀÒ56ö÷_MSImmutableRect¥øùúû:_MSImmutableRect__MSImmutableRect_MSImmutableModelBase]MSModelObjectVPage 1Öþ·ÿ_startDecorationTypeZmiterLimitYtextStyle_endDecorationType€+€ €Ò56_MSImmutableStyle¥    :_MSImmutableStyle__MSImmutableStyle_MSImmutableModelBase]MSModelObjectÒ/1€-€Ùœ3 [NS.object.2[NS.object.7[NS.object.4[NS.object.1[NS.object.6[NS.object.3[NS.object.5€­Æ€p€A΀.Ñß¶£§¤¬° "#¡³¨­®±¹»%&¹*¹,¹¹/1_backgroundColor_hasBackgroundColor€0€; €€>€@€/ €=€_$08363F15-7E31-4930-B61C-00A85AD6CA42ÖÓÔÕÖ¹67Ù9€1€9€&€2_$399C52A5-D8AD-408D-8006-01897988B9F6Ò/<1€3€Òœ3@€€4ÖB®CDEFGHI_visibleScaleTypeZfileFormatUscale€5€8€7€6#?ð_$4B244220-CAC9-40C5-BCD3-295D50403C0BSpngPÒ56NO_MSImmutableExportFormat¥PQRS:_MSImmutableExportFormat__MSImmutableExportFormat_MSImmutableModelBase]MSModelObjectÒ/U1€:€Ñ3€×ðíîïñò[\]¹_`€(#@o€<#@]#@‚ð#@~À_$D4DF30CB-788F-4834-804C-CA95720AD491TquicÕdefghIIIISredUalphaTblueUgreen€?Ò56jk_MSImmutableColor¥lmno:_MSImmutableColor__MSImmutableColor_MSImmutableModelBase]MSModelObjectÒ56qr_MSImmutableSliceLayer§stuvwx:_MSImmutableSliceLayer__MSImmutableSliceLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectß¶¢£¯§¤¬° ¥·¡³¨­®±¹{|}~¹¹…¹¹ˆ¹Š€K#@:€L€C€H €€€¬€B€J€_$DED53505-1438-4CD8-9A07-F77D2604562EÕÓÔÕÖ¹Ù‘€F€&€DÒ/“1€E€Ñ3€Ò/˜1€G€Ñ3€×ðíîïñòžŸ ¹¢£€(#@=óÎu¡€I#@]>Mé]y#@:uœ”[#@¬%)/"í_$A75F48A9-BA84-4479-8B04-41C1131C4D09\Group 4 CopyÖþ·ÿ€+€€Ò/«1€M€Óœ®3°€‚€€Nß ¡¢£¤¥¨§²¬³­¯°´®±³¶··»¸¹º»¼º½¹¹À¹Ã¹¹[windingRule_clippingMaskMode_hasClippingMask€ €W€U€P€O€€d €V€€_$3E333086-3BFB-49E7-94B8-1FA689649C02ÕÓÔÕÖ¹ËÙÍ€S€&€QÒ/Ï1€R€Ñ3€Ò/Ô1€T€Ñ3€ÖíîïðñÙò¹ÜÝÞ#?¶‘Õ’ _€(#@9¯ýƒ5"#@<ÛI•-æ#?’«¦³tUShapeØáþ·âÿãæWbordersUfills€X€€+€^€Ò/éê€Y€]Òœ3퀀ZÖïðñòóôõ÷YthicknessXfillTypeYisEnabledXpositionUcolor#@€\ €[Õdefgh»I»»€?Ò56ûü_MSImmutableStyleBorder©ýþÿ:_MSImmutableStyleBorder__MSImmutableStyleBorder_MSImmutableStyleBasicFill__MSImmutableStyleBasicFill_MSImmutableStylePart__MSImmutableStylePart_MSImmutableModelBase]MSModelObjectÒ56_ MSImmutableBorderStyleCollection¦    :_ MSImmutableBorderStyleCollection_MSImmutableStylePartCollection_MSImmutableArray_MSImmutableModelBase]MSModelObjectÒ/€_€cÒœ3€€`Ùóðñ»ºIUimage^noiseIntensity_patternFillType_patternTileScaleZnoiseIndex€b€a€ ÕdefghI !€?#?Úšššššš#?ì¼¼¼¼¼½#?êúúúúúûÒ56#$_MSImmutableStyleFill©%&'()*+,:_MSImmutableStyleFill__MSImmutableStyleFill_MSImmutableStyleBasicFill__MSImmutableStyleBasicFill_MSImmutableStylePart__MSImmutableStylePart_MSImmutableModelBase]MSModelObjectÒ56./_MSImmutableFillStyleCollection¦01234:_MSImmutableFillStyleCollection_MSImmutableStylePartCollection_MSImmutableArray_MSImmutableModelBase]MSModelObjectÒ/61€e€Òœ3:€€fß¶£§¤¬° <=>¡³¨­®±¹»@A¹EG¹I¹K¹M_booleanOperationVeditedTpath€h€m €ÿÿÿÿÿÿÿÿ €o€€€g€n€_$49690316-75E2-4F42-884B-871206CD4AD9ÕÓÔÕÖ¹RÙT€k€&€iÒ/V1€j€Ñ3€Ò/[1€l€Ñ3€Öíîïðñ`ò¹cde#<ñ]«÷Ù`®€(#@9¯ýƒ5 #@<ÛI•-ò#¼ë‰€VàTPathÓhijlVpointsXisClosed€p €Ò/n1€q€Ôœ3rst€€w€r€{Øvwxyz{|~€»‚¹\hasCurveFromYcurveModeUpointYcurveFrom\cornerRadiusWcurveToZhasCurveTo €v€u€s€t_+{-0.50421963253740165, 0.75653639823804564}_{0.46837120540447552, 1}_{0.46837120540447552, 1}Ò56ˆ‰_MSImmutableCurvePoint¥Š‹Œ:_MSImmutableCurvePoint__MSImmutableCurvePoint_MSImmutableModelBase]MSModelObjectØvwxyz{|¹~‘’»“€v€z€x€y _{1, 0.10814728142012979}_+{0.21898521695696799, -0.34487938166027993}_{1, 0.10814728142012979}Øvwxyz{|¹º›œ»¹€v€~€|€}_{0.46837120540447552, 1}_{0.46837120540447552, 1}_{0.46837120540447552, 1}Ò56£¤_MSImmutableShapePath¥¥¦§¨:_MSImmutableShapePath__MSImmutableShapePath_MSImmutableModelBase]MSModelObjectÒ56ª«_MSImmutableShapePathLayer§¬­®¯°±:_MSImmutableShapePathLayer__MSImmutableShapePathLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectÒ56³´_MSImmutableShapeGroup«µ¶·¸¹º»¼½¾:_MSImmutableShapeGroup__MSImmutableShapeGroup_MSImmutableLayerGroup__MSImmutableLayerGroup_MSImmutableStyledLayer__MSImmutableStyledLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectß ¡¢£¤¥¨§²¬³­¯°´®±³¶·»ùºÅƺ½¹¹Ê¹Ã¹¹€ €Š€‰€„€ƒ€€ €V€€_$68C3637C-D88F-42E8-AFD3-5D91D4C4FB14ÕÓÔÕÖ¹ÕÙ×€‡€&€…Ò/Ù1€†€Ñ3€Ò/Þ1€ˆ€Ñ3€Öíîïðñãò¹æçè#@#>|ч€(#@"»íÁðžÖ#@%> Í 3Ë#@ÌÍU ×þ·âÿì€+€€‹€Ò/€cÒœ3ó€€Ùóðñö»ºI€b€Ž€ Õdefgh»I»»€?Ò/ü1€€Òœ3€€‘ß¶£§¤¬° <=>¡³¨­®±¹»¹E ¹I¹ ¹M€“€˜ € €™€€€’€n€_$0C6505D6-9CC1-4D99-AFD0-68CD89849E01ÕÓÔÕÖ¹Ù€–€&€”Ò/1€•€Ñ3€Ò/1€—€Ñ3€Öíîïðñ»ò¹$%&€(#@"»íÁðžÙ#@%> Í 3Ö#<ñÚº ÿmÓhi(l€š €Ò/,1€›€Õœ/31234[NS.object.3€€ €œ€¨€¤Øvwxyz{|~89»:¹ €v€Ÿ€€ž_*{0.12151945042946087, 0.83584335277843125}_{0.47339824055200064, 1}_{0.47339824055200064, 1}Øvwxyz{|ACD»E €v€£€¡€¢ _,{0.27788544955996708, -0.078331283863736875}_+{-0.13662820754101609, 0.61289771616453714}_+{0.078612797202702145, 0.25396109280768026}Øvwxyz{|¹~MN»O€v€§€¥€¦ _{1, 0.12191017768291522}_+{0.64807697621433991, -0.04224646953865354}_{1, 0.12191017768291522}Øvwxyz{|¹ºWX»Y¹€v€«€©€ª_{0.47339824055200064, 1}_{0.47339824055200064, 1}_{0.47339824055200064, 1}Ò56_`_MSImmutableLayerGroup©abcdefgh:_MSImmutableLayerGroup__MSImmutableLayerGroup_MSImmutableStyledLayer__MSImmutableStyledLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectß¶¢£¯§¤¬° ¥·¡³¨­®±¹klmno¹¹…¹¹x¹z€·#À9€¸€¯€´ €€€¬€®€¶€_$126E4378-369B-43E9-BC3C-5654A33A33B8ÕÓÔÕÖ¹Ù€²€&€°Ò/ƒ1€±€Ñ3€Ò/ˆ1€³€Ñ3€×ðíîïñò޹|’€(#@>€µ#@]bªi°£ä#@Ž¢Å<2 è_$5C7F7BB0-F14F-48E3-9EB8-4B1C251A68F8\Group 3 CopyÖþ·ÿ€+€€Ò/š1€¹€Óœ3Ÿ€ä€€ºß ¡¢£¤¥¨§²¬³­¯°´®±³¶·£»¤¹º¦§º½¹¹«¹Ã¹¹€ €Â€Á€¼€»€€Ë €V€€_$14B9131B-7A27-4B00-A60A-09B928DB118BÕÓÔÕÖ¹¶Ù¸€¿€&€½Ò/º1€¾€Ñ3€Ò/¿1€À€Ñ3€ÖíîïðñÄò¹ÇÈÉ#?Ë›G@€(#@9xeo: )#@<Ö±ÞÐÇ#?ÐÈ*V›`Øáþ·âÿË΀À€+€Ç€Ò/Ñê€Ä€]Òœ3Õ€€ÅÖïðñòóôõÙ€\ €ÆÕdefgh»I»»€?Ò/݀ȀcÒœ3ကÉÙóðñ仺I€b€Ê€ ÕdefghéIêë€?#?Úšššššš#?ì¼¼¼¼¼½#?êúúúúúûÒ/í1€Ì€Òœ3ñ€€Íß¶£§¤¬° <=>¡³¨­®±¹»ôõ¹Eú¹I¹þ¹M€Ï€Ô € €Õ€€€Î€n€_$98396ABE-76FE-4E0C-9ED5-DEACBBEA9D18ÕÓÔÕÖ¹Ù€Ò€&€ÐÒ/ 1€Ñ€Ñ3€Ò/1€Ó€Ñ3€Öíîïðñ»ò¹»€(#@9xeo: *#@<Ö±ÞÐÈÓhil€Ö €Ò/1€×€Ôœ3 !"€€Ü€Ø€àØvwxyz{|~&'»(¹ €v€Û€Ù€Ú_+{0.77640045460466156, -0.36008348775197829}_{0, 0.12155767213889436}_{0, 0.12155767213889436}Øvwxyz{|¹~01»2€v€ß€Ý€Þ _{0.57567538893183645, 1}_){1.4780028014239563, 0.72658678302290047}_{0.57567538893183645, 1}Øvwxyz{|¹º:;»<¹€v€ã€á€â_{0, 0.12155767213889436}_{0, 0.12155767213889436}_{0, 0.12155767213889436}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·D»E¹ºGHº½¹¹L¹Ã¹¹€ €ì€ë€æ€å€€ñ €V€€_$1CBE06F4-6A86-42F1-BF10-3FF0EB872199ÕÓÔÕÖ¹WÙY€é€&€çÒ/[1€è€Ñ3€Ò/`1€ê€Ñ3€Öíîïðñeò¹hij#@"BC·ÅЀ(#@"»íÁðž¿#@%> Í 3Å#@'¯aß)`À×þ·âÿn€+€€í€Ò/q€î€cÒœ3u€€ïÙóðñx»ºI€b€ð€ Õdefgh»I»»€?Ò/~1€ò€Òœ3‚€€óß¶£§¤¬° <=>¡³¨­®±¹»…†¹E‹¹I¹¹M€õ€ú € €û€€€ô€n€_$C97612CB-8A3C-42D2-8146-6D4EC64E192FÕÓÔÕÖ¹–Ù˜€ø€&€öÒ/š1€÷€Ñ3€Ò/Ÿ1€ù€Ñ3€Öíîïðñ¤ò¹§¨»#<ñ]«÷Ù`¬€(#@"»íÁðžÁ#@%> Í 3ÉÓhiªl€ü €Ò/®1€ý€Õœ±3³´µ¶[NS.object.3€€þ Øvwxyz{|~º»»¼¹ €v€ÿ_*{0.87848054957053845, 0.83584335277843125}_{0.52660175944799892, 1}_{0.52660175944799892, 1}Øvwxyz{|AÄÅ»Æ €v _,{0.72211455044003325, -0.078331283863736875}_){1.1366282075410161, 0.61289771616453714}_*{0.92138720279729813, 0.25396109280768026}Øvwxyz{|¹~ÎϻЀv  _{0, 0.1219101776829155}_+{0.35192302378565882, -0.04224646953865354}_{0, 0.1219101776829155}Øvwxyz{|¹ºØÙ»Ú¹€v   _{0.52660175944799892, 1}_{0.52660175944799892, 1}_{0.52660175944799892, 1}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹á»âãä¹¹…¹¹í¹ï €€€¬€_$2191F97A-EC32-43B7-8472-B097CF4E6706ÕÓÔÕÖ¹ôÙö€&Ò/ø1€Ñ3€Ò/ý1€Ñ3€×ðíîïñò޹|€(#@a@#@~þX¯–6»_$F1C4ED70-5A03-4C28-B985-8241542281D3WGroup 3Öþ·ÿ€+€€Ò/1€Óœ3F€ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·»¹ºº½¹¹¹Ã¹¹€ $"€- €V€€_$70498B03-A1DD-48F2-8D28-2E3A2C4576DAÕÓÔÕÖ¹*Ù, €&Ò/.1€Ñ3€Ò/31!€Ñ3€×ðíîïñò9:;¹=>€(#@<Ö±ÞÐÇ##?Ë›G8#@9xeo: )#?ÐÈ*V›X_$9D878491-A0B2-4C0B-9BD1-60261D77183DØáþ·âÿAD%€€+)€Ò/Gê&€]Òœ3K€'ÖïðñòóôõO€\ (Õdefgh»I»»€?Ò/S*€cÒœ3W€+ÙóðñZ»ºI€b,€ Õdefgh_I`a€?#?Úšššššš#?ì¼¼¼¼¼½#?êúúúúúûÒ/c1.€Òœ3g€/ß¶£§¤¬° <=>¡³¨­®±¹»jk¹Ep¹I¹t¹M16 € 7€€0€n€_$DE5470A4-C251-421F-8057-C7B4C8FCE917ÕÓÔÕÖ¹{Ù}4€&2Ò/13€Ñ3€Ò/„15€Ñ3€Öíîïðñ»ò¹‹Œ»€(#@9xeo: *#@<Ö±ÞÐÈÓhiŽl8 €Ò/’19€Ôœ3–—˜€>:BØvwxyz{|~œ»ž¹ €v=;<_+{0.77640045460466156, -0.36008348775197829}_{0, 0.12155767213889436}_{0, 0.12155767213889436}Øvwxyz{|¹~¦§»¨€vA?@ _{0.57567538893183645, 1}_){1.4780028014239563, 0.72658678302290047}_{0.57567538893183645, 1}Øvwxyz{|¹º°±»²¹€vECD_{0, 0.12155767213889436}_{0, 0.12155767213889436}_{0, 0.12155767213889436}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·º»»¹º½¾º½¹¹Â¹Ã¹¹€ NMHG€S €V€€_$AFA9E950-B6F6-4C45-AA02-4DE02D0285B3ÕÓÔÕÖ¹ÍÙÏK€&IÒ/Ñ1J€Ñ3€Ò/Ö1L€Ñ3€ÖíîïðñÛò¹Þßà#@"BC·Å°€(#@"»íÁðž¿#@%> Í 3Å#@'¯aß)a×þ·âÿä€+€O€Ò/çP€cÒœ3ë€QÙóðñI€bR€ Õdefgh»I»»€?Ò/ô1T€Òœ3ø€Uß¶£§¤¬° <=>¡³¨­®±¹»ûü¹E¹I¹¹MW\ € ]€€V€n€_$92290A37-F0CF-4AD8-9934-CEE4F8809745ÕÓÔÕÖ¹ ÙZ€&XÒ/1Y€Ñ3€Ò/1[€Ñ3€Öíîïðñò¹»#<ñ]«÷Ù`¬€(#@"»íÁðžÁ#@%> Í 3ÉÓhi l^ €Ò/$1_€Õœ'3)*+,[NS.object.3€d`lhØvwxyz{|~01»2¹ €vcab_*{0.87848054957053845, 0.83584335277843125}_{0.52660175944799892, 1}_{0.52660175944799892, 1}Øvwxyz{|A:;»< €vgef _,{0.72211455044003325, -0.078331283863736875}_){1.1366282075410161, 0.61289771616453714}_*{0.92138720279729813, 0.25396109280768026}Øvwxyz{|¹~DE»F€vkij _{0, 0.1219101776829155}_+{0.35192302378565882, -0.04224646953865354}_{0, 0.1219101776829155}Øvwxyz{|¹ºNO»P¹€vomn_{0.52660175944799892, 1}_{0.52660175944799892, 1}_{0.52660175944799892, 1}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹W»XYZ¹¹…¹¹c¹ez{rw €€€¬qy€_$A208A3F0-59DD-43A0-BB5E-0D08816C1F11ÕÓÔÕÖ¹jÙlu€&sÒ/n1t€Ñ3€Ò/s1v€Ñ3€×ðíîïñòyz{¹}~€(#@=óÎu x#@a`#@:uœ”[#@ƒ_$9BF8781F-1371-4E36-9802-327258E5CBB0WGroup 4Öþ·ÿ€+€€Ò/†1|€Óœ‰3‹§€}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·»¹º’“º½¹¹—¹Ã¹¹€ …„~€Ž €V€€_$659B795E-701D-44E5-8F80-AC68A9BE8E5DÕÓÔÕÖ¹¢Ù¤‚€&€Ò/¦1€Ñ3€Ò/«1ƒ€Ñ3€Öíîïðñ°ò¹³´µ#?¶‘Õ’ _€(#@9¯ýƒ5"#@<ÛI•-ä#?’«¦³tØáþ·âÿ·º†€€+Š€Ò/½ê‡€]Òœ3Á€ˆÖïðñòóôõÅ€\ ‰Õdefgh»I»»€?Ò/É‹€cÒœ3Í€ŒÙóðñлºI€b€ ÕdefghÕIÖ×€?#?Úšššššš#?ì¼¼¼¼¼½#?êúúúúúûÒ/Ù1€Òœ3݀߶£§¤¬° <=>¡³¨­®±¹»àá¹Eæ¹I¹ê¹M’— € ˜€€‘€n€_$4B51068B-5774-4A9F-9DBD-7DFE275821CDÕÓÔÕÖ¹ñÙó•€&“Ò/õ1”€Ñ3€Ò/ú1–€Ñ3€Öíîïðñÿò¹#<ñ]«÷Ù`®€(#@9¯ýƒ5 #@<ÛI•-ð#¼ë‰€VàÓhil™ €Ò/ 1š€Ôœ3€Ÿ›£Øvwxyz{|~»¹ €vžœ_+{-0.50421963253740165, 0.75653639823804564}_{0.46837120540447552, 1}_{0.46837120540447552, 1}Øvwxyz{|¹~» €v¢ ¡ _{1, 0.10814728142012979}_+{0.21898521695696799, -0.34487938166027993}_{1, 0.10814728142012979}Øvwxyz{|¹º()»*¹€v¦¤¥_{0.46837120540447552, 1}_{0.46837120540447552, 1}_{0.46837120540447552, 1}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·2»3¹º56º½¹¹:¹Ã¹¹€ ¯®©¨€´ €V€€_$CBCB542F-159D-41C7-8FBB-43C61F29CB78ÕÓÔÕÖ¹EÙG¬€&ªÒ/I1«€Ñ3€Ò/N1­€Ñ3€ÖíîïðñSò¹VWX#@#>|ц€(#@"»íÁðžÖ#@%> Í 3É#@ÌÍU ×þ·âÿ\€+€°€Ò/_±€cÒœ3c€²Ùóðñf»ºI€b³€ Õdefgh»I»»€?Ò/l1µ€Òœ3p€¶ß¶£§¤¬° <=>¡³¨­®±¹»st¹Ey¹I¹}¹M¸½ € ¾€€·€n€_$39F50062-DAF4-41AA-A1FD-E2C2DB387054ÕÓÔÕÖ¹„Ù†»€&¹Ò/ˆ1º€Ñ3€Ò/1¼€Ñ3€Öíîïðñ»ò¹”•–€(#@"»íÁðžÙ#@%> Í 3Ó#<ñÚº ÿmÓhi˜l¿ €Ò/œ1À€ÕœŸ3¡¢£¤[NS.object.3€ÅÁÍÉØvwxyz{|~¨©»ª¹ €vÄÂÃ_*{0.12151945042946087, 0.83584335277843125}_{0.47339824055200064, 1}_{0.47339824055200064, 1}Øvwxyz{|A²³»´ €vÈÆÇ _,{0.27788544955996708, -0.078331283863736875}_+{-0.13662820754101609, 0.61289771616453714}_+{0.078612797202702145, 0.25396109280768026}Øvwxyz{|¹~¼½»¾€vÌÊË _{1, 0.12191017768291522}_+{0.64807697621433991, -0.04224646953865354}_{1, 0.12191017768291522}Øvwxyz{|¹ºÆÇ»ȹ€vÐÎÏ_{0.47339824055200064, 1}_{0.47339824055200064, 1}_{0.47339824055200064, 1}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹Ï»ÐÑÒ¹¹…¹¹Û¹ÝÛÜÓØ €€€¬ÒÚ€_$1EBB465E-86E9-4B52-B5FE-90DFFE975D8EÕÓÔÕÖ¹âÙäÖ€&ÔÒ/æ1Õ€Ñ3€Ò/ë1×€Ñ3€×ðíîïñòñòó¹õö€(#@P­XýíÙ#@c #@WhÿM d–#@€@_$29DFFDE5-0A57-4911-9835-5B3DCF9E95FBWGroup 2Öþ·ÿ€+€€Ò/þ1݀؜3    [NS.object.4[NS.object.6[NS.object.3[NS.object.52€Ï_hÞ2ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·»¹ºº½¹¹¹Ã¹¹€ æåà߀ï €V€€_$D1A0E293-7A19-4C91-A157-4E7D8BFD8C10ÕÓÔÕÖ¹#Ù%ã€&áÒ/'1â€Ñ3€Ò/,1ä€Ñ3€Öíîïðñ1ò¹456#?ÆA¼Diö€(#@DÖ <Á…k#@C&]Á§³#?šÜçtØáþ·âÿ8;瀀+ë€Ò/>êè€]Òœ3B€éÖïðñòóDõG#@CÉîËû€\ êÕdefgh»I»»€?Ò/Kì€cÒœ3O€íÙóðñR»ºI€bî€ ÕdefghIIII€?Ò/X1ð€Òœ3\€ñß¶£§¤¬° <=>¡³¨­®±¹»_`¹Ee¹I¹i¹Móø € ù€€ò€n€_$01012744-7A26-4B6C-A6EF-A4D03DCD0252ÕÓÔÕÖ¹pÙrö€&ôÒ/t1õ€Ñ3€Ò/y1÷€Ñ3€Öíîïðñ~ò¹‚»#¼ñ]«÷Ù`°€(#@DÖ <Á…k#@C&]Á§ªÓhi„¹lú€Ò/ˆ1û€Ôœ3ŒŽ€üØvwxyz{|~’“»”¹ €vÿýþ_){0.10880151056628272, 1.2521486755176012}_,{0.0010531150891241719, 0.57056196468876641}_,{0.0010531150891241719, 0.57056196468876641}Øvwxyz{|Aœ»ž €v _+{0.85371494259364389, -0.23190867082304195}_({1.1326399272316914, 1.0719370756098709}_*{0.98558195842765683, 0.38451928948086411}Øvwxyz{|¹~¦§»¨€v _,{0.0010531150891241719, 0.57056196468876641}_.{-0.034756971689684252, -0.061282103783498104}_,{0.0010531150891241719, 0.57056196468876641}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·°»±¹º³´º½¹¹¸¹Ã¹¹€   € €V€€_$DBF844F1-7CF2-4F00-8D46-81C8C93D0B0DÕÓÔÕÖ¹ÃÙÅ €& Ò/Ç1 €Ñ3€Ò/Ì1€Ñ3€ÖíîïðñÑò¹ÔÕÖ#@=øyÖ€(#@D¬óÛ‚E'#@C" T½#@IòÆáŒ¤½Øáþ·âÿØÛ€€+€Ò/Þê€]Òœ3â€Öïðñòóäõç#@’:)Çy§€\ Õdefgh»I»»€?Ò/ë€cÒœ3ï€Ùóðñò»ºI€b€ ÕdefghIIII€?Ò/ø1€Òœ3ü€ß¶£§¤¬° <=>¡³¨­®±¹»ÿ ¹E ¹I¹ ¹M" € #€€€n€_$B104B2C3-26B7-48B9-9AA6-1DE19625A0A0ÕÓÔÕÖ¹ Ù  €&Ò/ 1€Ñ3€Ò/ 1!€Ñ3€Öíîïðñ ò¹ ! " ##<ñ]«÷Ù`±€(#@D¬óÛ‚E-#@C" T½#¼ùâ‘÷‹ô;Óhi %¹l$€Ò/ )1%€Ôœ3 - . /€*&.Øvwxyz{|~ 3 4» 5¹ €v)'(_){0.15232636028064062, 1.2174111260163647}_+{0.011575448594345275, 0.62357305517230777}_+{0.011575448594345275, 0.62357305517230777}Øvwxyz{|A = >» ? €v-+, _+{0.95934701793892474, -0.24372284599771429}_'{1.032252523273123, 1.0653734226642573}_*{0.99908632994489355, 0.46969483273098456}Øvwxyz{|¹~ G H» I€v1/0 _+{0.011575448594345275, 0.62357305517230777}_,{-0.12299312433560032, -0.10606284470724603}_+{0.011575448594345275, 0.62357305517230777}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶· Q» R¹º T Uº½¹¹ Y¹Ã¹¹€ :943€C €V€€_$E52176D8-3720-4116-A9DD-191280257F20ÕÓÔÕÖ¹ dÙ f7€&5Ò/ h16€Ñ3€Ò/ m18€Ñ3€Öíîïðñ rò¹ u v w#@In2¿«â±€(#@" ýí>zè#@/ì„Y÷6#@Bû-ûNÇØáþ·âÿ y |;€€+?€Ò/ ê<€]Òœ3 ƒ€=Öïðñòóôõ ‡€\ >Õdefgh»I»»€?Ò/ ‹@€cÒœ3 €AÙóðñ ’»ºI€bB€ ÕdefghIIII€?Ò/ ˜1D€Òœ3 œ€Eß¶£§¤¬° <=>¡³¨­®±¹» Ÿ  ¹E ¥¹I¹ ©¹MGL € M€€F€n€_$A9633681-E887-4F5A-98C0-1A0A70C06730ÕÓÔÕÖ¹ °Ù ²J€&HÒ/ ´1I€Ñ3€Ò/ ¹1K€Ñ3€Öíîïðñ»ò¹ À Á»€(#@" ýí>zè#@/ì„Y÷KÓhi ÃlN €Ò/ Ç1O€× Ê Ëœ Ì3 Î Ï Ð Ñ Ò Ó[NS.object.4[NS.object.3[NS.object.5€X`T\PdØvwxyz{|~ × Ø» Ù¹ €vSQR_*{0.95909522322265617, 0.37294500761594601}_){0.95698207338687646, 0.1578339198487313}_){0.95698207338687646, 0.1578339198487313}Øvwxyz{|~ á â» ã €vWUV _){0.8752583003900799, 0.94708230474289579}_){1.0424727656561501, 0.61444928830295686}_){0.9713147418382545, 0.84329534114186633}Øvwxyz{|~ ë ì» í €v[YZ _*{0.29974653641938492, 0.97985713535374763}_({0.68682046068639224, 0.958059772046851}_{0.52438355157078176, 1}Øvwxyz{|~ õ ö» ÷ €v_]^ _+{-0.036887420028353601, 0.5142076789747364}_*{0.11084931631661361, 0.89553022742791089}_+{0.020764820056514043, 0.77540837228846071}Øvwxyz{|¹~ ÿ » €vcab _{0.056826181384498957, 0}_){0.042263822733579814, 0.261305740847734}_{0.056826181384498957, 0}Øvwxyz{|¹º » ¹€vgef_){0.95698207338687646, 0.1578339198487313}_){0.95698207338687646, 0.1578339198487313}_){0.95698207338687646, 0.1578339198487313}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹ »   ¹¹…¹ ¹ qvjo €€€¬ ip€_$6DE52FC2-DB9A-4E7D-9C64-B66360E102F3ÕÓÔÕÖ¹ %Ù 'm€&kÒ/ )1l€Ñ3€Ò/ .1n€Ñ3€Öíîïðñ 3ò¹ 6 7 8#@/}`yÀô€(#@)°bêï#@+X«\Bƒ #@SA!E¡-UGroup×þ·âÿ =€+€r€Ò/ @s€cÒœ3 D€tÙóðñ G»ºI€bu€ Õdefgh»I»»€?Ò/ M1w€Óœ P3 R¥€xß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹ V» W¹º Y Zº½¹¹ ^¹ a¹¹€zy€† €€€_$ADAB3D2E-8BA5-482B-AE24-CAB6D60FF596ÕÓÔÕÖ¹ iÙ k}€&{Ò/ m1|€Ñ3€Ò/ r1~€Ñ3€Öíîïðñ wò¹ z { |#?Â#ZbÍYD€(#@(“±GkpV#@*žÕ[d® #?Ê@¤‡ˆ_TOval×þ·âÿ €+€‚€Ò/ „ƒ€cÒœ3 ˆ€„Ùóðñ ‹»ºI€b…€ Õdefgh»I»»€?Ò/ ‘1‡€Òœ3 •€ˆß¶£§¤¬° <=>¡³¨­®±¹» ˜ ™¹E¹ ž¹  ¹ ¢¹ ¤Š €‘¤‰€_$582B1774-A97A-4358-AC6C-68052A5D71BFÕÓÔÕÖ¹ ©Ù «€&‹Ò/ ­1Œ€Ñ3€Ò/ ²1Ž€Ñ3€Öíîïðñ ·ò¹ º »»#¼ùâ‘÷‹ô3€(#@(“±GkpU#@*žÕ[d®YOval-pathÓhi ¾l’ €Ò/ Â1“€Õœ Å3 Ç È É Ê[NS.object.3€˜” œØvwxyz{| Í Ï Ð» Ñ €v—•– _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Í Ù Ú» Û €v›™š _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í ã ä» å €vŸž _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í í î» ï €v£¡¢ _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}Ò56 õ ö_MSImmutableOvalShape© ÷ ø ù ú û ü ý þ:_MSImmutableOvalShape__MSImmutableOvalShape_MSImmutableShapePathLayer__MSImmutableShapePathLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹ » ¹º  º½¹¹ ¹ a¹¹€­¬§¦€² €€€_$2BC9EEAD-D849-4F8C-A35B-84AA871E00A5ÕÓÔÕÖ¹ Ù ª€&¨Ò/ 1©€Ñ3€Ò/ 1«€Ñ3€Öíîïðñ #ò¹ & ' (#@n w8v¦€(#@-,aÍ€N#@ ±Tçä”#@È5@Ì£%×þ·âÿ ,€+€®€Ò/ /¯€cÒœ3 3€°Ùóðñ 6»ºI€b±€ ÕdefghIIII€?Ò/ <1³€Òœ3 @€´ß¶£§¤¬° <=>¡³¨­®±¹» C D¹E¹ I¹  ¹ M¹ ¤¶» €¼¤µ€_$D193661E-6988-4F15-9562-EEB7D68742B1ÕÓÔÕÖ¹ TÙ V¹€&·Ò/ X1¸€Ñ3€Ò/ ]1º€Ñ3€Öíîïðñ»ò¹ d e»€(#@-,aÍ€G#@ ±Tçä¦Óhi gl½ €Ò/ k1¾€Õœ n3 p q r s[NS.object.3€Ã¿ËÇØvwxyz{| Í w x» y €vÂÀÁ _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Í ‚» ƒ €vÆÄÅ _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í ‹ Œ»  €vÊÈÉ _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í • –» — €vÎÌÍ _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹ ž» Ÿ   ¡¹¹…¹ ª¹ ×ÜÑÖ €€€¬ Ðp€_$2D495404-4BBA-497E-A33E-0DFEC556D9ADÕÓÔÕÖ¹ ±Ù ³Ô€&ÒÒ/ µ1Ó€Ñ3€Ò/ º1Õ€Ñ3€Öíîïðñ ¿ò¹  à Ä#@,,Ï•[Ìá€(#@)°bêïj#@+X«\Bƒ#@8Ü>°ö¥µ×þ·âÿ È€+€Ø€Ò/ ËÙ€cÒœ3 Ï€ÚÙóðñ Ò»ºI€bÛ€ Õdefgh»I»»€?Ò/ Ø1Ý€Óœ Û3 Ý€Þß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹ Ỡ⹺ ä 庽¹¹ é¹ a¹¹€æåà߀ë €€€_$235C0558-DC56-4559-8B20-8E42A6A1C872ÕÓÔÕÖ¹ ôÙ öã€&áÒ/ ø1â€Ñ3€Ò/ ý1ä€Ñ3€Öíîïðñ ò¹   #?Â#ZbÍYH€(#@(*åM©#@*žÕ[d®#?Ú9Ú¤ö…×þ·âÿ €+€ç€Ò/ è€cÒœ3 €éÙóðñ »ºI€bê€ Õdefgh»I»»€?Ò/ 1ì€Òœ3 €íß¶£§¤¬° <=>¡³¨­®±¹» " #¹E¹ (¹  ¹ ,¹ ¤ïô €õ¤î€_$F640F3CB-E25A-4665-AE15-AC2833C59DE6ÕÓÔÕÖ¹ 3Ù 5ò€&ðÒ/ 71ñ€Ñ3€Ò/ <1ó€Ñ3€Öíîïðñ Aò¹ D E F#¼ùâ‘÷‹ô:€(#@(*åM«#@*žÕ[d®#=ÙYìJxÓhi Hlö €Ò/ L1÷€Õœ O3 Q R S T[NS.object.3€üøØvwxyz{| Í X Y» Z €vûùú _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Í b c» d €vÿýþ _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í l m» n €v _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í v w» x €v _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹ €» ¹º ƒ „º½¹¹ ˆ¹ a¹¹€  € €€€_$4B7C83DA-1C8E-4FF7-8F7E-A0E210A074E8ÕÓÔÕÖ¹ “Ù • €& Ò/ —1 €Ñ3€Ò/ œ1€Ñ3€Öíîïðñ ¡ò¹ ¤ ¥ ¦#@n w8vª€(#@ʽ]Ö?C#@ ±Tçäœ#@:*àÑ×þ·âÿ ª€+€€Ò/ ­€cÒœ3 ±€Ùóðñ ´»ºI€b€ ÕdefghIIII€?Ò/ º1€Òœ3 ¾€ß¶£§¤¬° <=>¡³¨­®±¹» Á ¹E¹ ǹ  ¹ ˹ ¤ €¤€_$51A1E7DC-CB66-4355-8C32-8DF4EE3A455BÕÓÔÕÖ¹ ÒÙ Ô€&Ò/ Ö1€Ñ3€Ò/ Û1€Ñ3€Öíîïðñ»ò¹ â 㻀(#@ʽ]Ö?B#@ ±Tçä©Óhi ål €Ò/ é1!€Õœ ì3 î ï ð ñ[NS.object.3€&".*Øvwxyz{| Í õ ö» ÷ €v%#$ _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Í ÿ »  €v)'( _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í »  €v-+, _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í  »  €v1/0 _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶· » ¹º !º½¹¹ %¹Ã¹¹€ :943€C €V€€_$611DEEC9-D39B-4BE7-8446-F313FDE9D93BÕÓÔÕÖ¹ 0Ù 27€&5Ò/ 416€Ñ3€Ò/ 918€Ñ3€Öíîïðñ >ò¹ A B C#@IS®8ˆ¹x€(#@#Ò=µ3„#@/ISõ5#@FŒÝBöØáþ·âÿ E H;€€+?€Ò/ Kê<€]Òœ3 O€=Öïðñòóôõ S€\ >Õdefgh»I»»€?Ò/ W@€cÒœ3 [€AÙóðñ ^»ºI€bB€ ÕdefghIIII€?Ò/ d1D€Òœ3 h€Eß¶£§¤¬° <=>¡³¨­®±¹» k l¹E q¹I¹ u¹MGL € M€€F€n€_$9C14EA11-F602-4DF5-A6B7-EC910D62597AÕÓÔÕÖ¹ |Ù ~J€&HÒ/ €1I€Ñ3€Ò/ …1K€Ñ3€Öíîïðñ»ò¹ Œ »€(#@#Ò=µ3„ #@/ISõ5%Óhi lN €Ò/ “1O€Õœ –3 ˜ ™ š ›[NS.object.3€TP[XØvwxyz{|~ Ÿ  » ¡¹ €vSQR_+{-0.18808645190953224, 0.56413207311514058}_+{0.082408281845646539, 0.14880711741763814}_+{0.082408281845646539, 0.14880711741763814}Øvwxyz{|~ © ª» « €vWUV _*{0.91471263417920201, 0.51862644340287622}_){0.23324985825811739, 1.3946495467053943}_*{0.96718108717189888, 0.78207172461252916}Øvwxyz{|¹~ ³ ³» µ€vYYZ V{1, 0}_*{0.98674701227196393, 0.26355123145345377}Øvwxyz{|¹º ¼ ½» ¾¹€v^\]_+{0.082408281845646539, 0.14880711741763814}_+{0.082408281845646539, 0.14880711741763814}_+{0.082408281845646539, 0.14880711741763814}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹ Å» Æ Ç È¹¹…¹ ѹ glaf €€€¬ `p€_$9A3FC255-A4A1-4A71-A9D0-8966BD680548ÕÓÔÕÖ¹ ØÙ Úd€&bÒ/ Ü1c€Ñ3€Ò/ á1e€Ñ3€Öíîïðñ æò¹ é ê ë#@@]Ëg³qc€(#@@ÇÝ„@>#@5õ¿é#{#@;X«\Bƒ×þ·âÿ ï€+€h€Ò/ òi€cÒœ3 ö€jÙóðñ ù»ºI€bk€ Õdefgh»I»»€?Ò/ ÿ1m€Óœ3¤€nß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹» ¹º  º½¹¹¹Ã¹¹€vupo€ €V€€_$BE8A44DB-C504-4FE6-AB89-BAD35CE57AF3ÕÓÔÕÖ¹Ùs€&qÒ/1r€Ñ3€Ò/$1t€Ñ3€Öíîïðñ)ò¹,-.#@Y\OméO€(#@@g²AÒÏ#@-|IÂáýW#?Ö>…‹R~ÆØáþ·âÿ03w€€+{€Ò/6êx€]Òœ3:€yÖïðñòóôõ>€\ zÕdefghAIBC€?#?Á‘‘‘‘‘’#?À#?¿Ò/E|€cÒœ3I€}ÙóðñL»ºI€b~€ ÕdefghQIRS€?#?îÞÞÞÞÞß#?äTTTTTT#?êZZZZZZÒ/U1€€Òœ3Y€ß¶£§¤¬° <=>¡³¨­®±¹»\]¹Eb¹I¹f¹Mƒˆ € ‰€€‚€n€_$7EF7E02B-2A31-4579-991A-384F3580FC67ÕÓÔÕÖ¹mÙo†€&„Ò/q1…€Ñ3€Ò/v1‡€Ñ3€Öíîïðñ»ò¹}~»€(#@@g²AÒÐ#@-|IÂáýmÓhi€lŠ €Ò/„1‹€×‡ˆœ‰3‹ŒŽ[NS.object.4[NS.object.3[NS.object.5€”œ˜Œ Øvwxyz{|~”•»–¹ €vŽ_,{0.069884524741148424, 0.072959499532503788}_+{0.22219138423366264, 0.044067987390972972}_+{0.22219138423366264, 0.044067987390972972}Øvwxyz{|AžŸ»  €v“‘’ _){0.12944826546315674, 1.2169565829187119}_,{-0.054610766246305988, 0.47592302198899411}_+{0.024580212194532047, 0.79471331597867045}Øvwxyz{|~¨©»ª €v—•– _){0.6771037704349544, 0.80806299328142051}_*{0.36352265197735933, 0.75736232411476478}_*{0.50931235917713447, 0.80041854648910904}Øvwxyz{|A²³»´ €v›™š _){1.0992283129309168, 0.50914264003499621}_){0.81458284544747761, 1.1952036350611777}_*{0.94934644033802651, 0.87039896013931584}Øvwxyz{|¹~¼½»¾€vŸž _{0.71724235567976824, 0}_*{0.88481905455585086, 0.15730135550209098}_{0.71724235567976824, 0}Øvwxyz{|¹ºÆÇ»ȹ€v£¡¢_+{0.22219138423366264, 0.044067987390972972}_+{0.22219138423366264, 0.044067987390972972}_+{0.22219138423366264, 0.044067987390972972}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹лѹºÓÔº½¹¹Ø¹Ã¹¹€¬«¦¥€± €V€€_$D577427B-044C-4B93-8168-0D77369CE644ÕÓÔÕÖ¹ãÙå©€&§Ò/ç1¨€Ñ3€Ò/ì1ª€Ñ3€Öíîïðññò¹ôõö#?Ș'Lð€(#@2‚Cõÿ"ì#@&‹Æ-Òh#@àÉ.œÕ×þ·âÿú€+€­€Ò/ý®€cÒœ3€¯Ùóðñ»ºI€b°€ Õdefgh»I»»€?Ò/ 1²€Òœ3€³ß¶£§¤¬° <=>¡³¨­®±¹»¹E¹I¹¹Mµº € »€€´€n€_$26F7E703-EABB-4780-B507-F2649DC79C57ÕÓÔÕÖ¹"Ù$¸€&¶Ò/&1·€Ñ3€Ò/+1¹€Ñ3€Öíîïðñ»ò¹23»€(#@2‚Cõÿ"ô#@&‹Æ-ÒhÓhi5l¼ €Ò/91½€Õœ<3>?@A[NS.object.3€Â¾ÊÆØvwxyz{|~EF»G¹ €vÁ¿À_.{-0.0044199887191483465, -0.12164886358549279}_+{0.01557069745 7438594, 0.64619967871756223}_+{0.015570697457438594, 0.64619967871756223}Øvwxyz{| ÍOP»Q €vÅÃÄ _({1.0975749815102411, 1.0663092397019061}_+{0.88723624991425998, -0.21765290324350761}_*{0.99253993163280996, 0.42508164556954758}Øvwxyz{|AYZ»[ €vÉÇÈ _,{-0.019463371821798241, 0.19179770999932655}_*{0.059469617493593856, 1.2152404681453497}_+{0.015570697457438594, 0.64619967871756223}Øvwxyz{|¹~cd»e€vÍËÌ _+{0.015570697457438594, 0.64619967871756223}_+{0.015570697457438594, 0.64619967871756223}_+{0.015570697457438594, 0.64619967871756223}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹l»mno¹¹…¹x¹zØÙÐÕ €€€¬ Ï×€_$F1C4F6BB-90C9-4A02-B2D0-78412AF1497CÕÓÔÕÖ¹ÙÓ€&ÑÒ/ƒ1Ò€Ñ3€Ò/ˆ1Ô€Ñ3€×ðíîïñòŽó¹‘’€(#@P­XýíÖ#@WhÿM d—#@_$ADEDDC75-FEC7-4982-B712-633BC1DAA4E0\Group 2 CopyÖþ·ÿ€+€€Ò/š1ڀ؞Ÿœ ¡3£¤¥¦§¨[NS.object.4[NS.object.6[NS.object.3[NS.object.5/€ÈWeÛ+ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¬»­¹º¯°º½¹¹´¹Ã¹¹€ ãâÝÜ€ì €V€€_$8708C2D0-623C-453C-8A43-CFE5AB6D4412ÕÓÔÕÖ¹¿ÙÁà€&ÞÒ/Ã1߀Ñ3€Ò/È1á€Ñ3€ÖíîïðñÍò¹ÐÑÒ#?ÆA¼Diö€(#@DÖ <Á…l#@C&]Á§³#?šÜçtØáþ·âÿÔ×䀀+è€Ò/Úêå€]Òœ3Þ€æÖïðñòóàõã#@CÉîËû€\ çÕdefgh»I»»€?Ò/çé€cÒœ3ë€êÙóðñI€bë€ ÕdefghIIII€?Ò/ô1í€Òœ3ø€îß¶£§¤¬° <=>¡³¨­®±¹»ûü¹E¹I¹¹Mðõ € ö€€ï€n€_$6A01B555-5302-42C6-95F7-C1F84C6FE8B7ÕÓÔÕÖ¹ Ùó€&ñÒ/1ò€Ñ3€Ò/1ô€Ñ3€Öíîïðñò¹»#¼ñ]«÷Ù`°€(#@DÖ <Á…l#@C&]Á§ªÓhi ¹l÷€Ò/$1ø€Ôœ3()*€ýùØvwxyz{|~./»0¹ €vüúû_){0.10880151056628272, 1.2521486755176012}_,{0.0010531150891241719, 0.57056196468876641}_,{0.0010531150891241719, 0.57056196468876641}Øvwxyz{|A89»: €vþÿ _+{0.85371494259364389, -0.23190867082304195}_({1.1326399272316914, 1.0719370756098709}_*{0.98558195842765683, 0.38451928948086411}Øvwxyz{|¹~BC»D€v _,{0.0010531150891241719, 0.57056196468876641}_.{-0.034756971689684252, -0.061282103783498104}_,{0.0010531150891241719, 0.57056196468876641}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·L»M¹ºOPº½¹¹T¹Ã¹¹€   € €V€€_$3D4930A8-8628-491F-88DC-2789FEAE3742ÕÓÔÕÖ¹_Ùa €&Ò/c1 €Ñ3€Ò/h1 €Ñ3€Öíîïðñmò¹pqr#@=øyÖ€(#@D¬óÛ‚E(#@C" T½#@IòÆáŒ¤¿Øáþ·âÿtw€€+€Ò/zê€]Òœ3~€Öïðñòó€õƒ#@’:)Çy§€\ Õdefgh»I»»€?Ò/‡€cÒœ3‹€ÙóðñŽ»ºI€b€ ÕdefghIIII€?Ò/”1€Òœ3˜€ß¶£§¤¬° <=>¡³¨­®±¹»›œ¹E¡¹I¹¥¹M €  €€€n€_$26A3A3C5-4473-4416-BE36-9EA01CA040EEÕÓÔÕÖ¹¬Ù®€&Ò/°1€Ñ3€Ò/µ1€Ñ3€Öíîïðñºò¹½¾¿#<ñ]«÷Ù`±€(#@D¬óÛ‚E.#@C" T½#¼ùâ‘÷‹ô;ÓhiÁ¹l!€Ò/Å1"€Ôœ3ÉÊË€'#+Øvwxyz{|~Ïлѹ €v&$%_){0.15232636028064062, 1.2174111260163647}_+{0.011575448594345275, 0.62357305517230777}_+{0.011575448594345275, 0.62357305517230777}Øvwxyz{|AÙÚ»Û €v*() _+{0.95934701793892474, -0.24372284599771429}_'{1.032252523273123, 1.0653734226642573}_*{0.99908632994489355, 0.46969483273098456}Øvwxyz{|¹~ãä»å€v.,- _+{0.011575448594345275, 0.62357305517230777}_,{-0.12299312433560032, -0.10606284470724603}_+{0.011575448594345275, 0.62357305517230777}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·í»î¹ºðñº½¹¹õ¹Ã¹¹€ 7610€@ €V€€_$C1438940-160F-4A48-A6A6-EE25162BF63FÕÓÔÕÖ¹Ù4€&2Ò/13€Ñ3€Ò/ 15€Ñ3€Öíîïðñò¹#@In2¿«â±€(#@" ýí>zé#@/ì„Y÷4#@Bû-ûNÈØáþ·âÿ8€€+<€Ò/ê9€]Òœ3€:Öïðñòóôõ#€\ ;Õdefgh»I»»€?Ò/'=€cÒœ3+€>Ùóðñ.»ºI€b?€ ÕdefghIIII€?Ò/41A€Òœ38€Bß¶£§¤¬° <=>¡³¨­®±¹»;<¹EA¹I¹E¹MDI € J€€C€n€_$8147B82F-32B2-475B-9B46-7C2533D7FDDFÕÓÔÕÖ¹LÙNG€&EÒ/P1F€Ñ3€Ò/U1H€Ñ3€Öíîïðñ»ò¹\]»€(#@" ýí>zé#@/ì„Y÷IÓhi_lK €Ò/c1L€×fgœh3jklmno[NS.object.4[NS.object.3[NS.object.5€U]QYMaØvwxyz{|~st»u¹ €vPNO_*{0.95909522322265617, 0.37294500761594601}_){0.95698207338687646, 0.1578339198487313}_){0.95698207338687646, 0.1578339198487313}Øvwxyz{|~}~» €vTRS _){0.8752583003900799, 0.94708230474289579}_){1.0424727656561501, 0.61444928830295686}_){0.9713147418382545, 0.84329534114186633}Øvwxyz{|~‡ˆ»‰ €vXVW _*{0.29974653641938492, 0.97985713535374763}_({0.68682046068639224, 0.958059772046851}_{0.52438355157078176, 1}Øvwxyz{|~‘’»“ €v\Z[ _+{-0.036887420028353601, 0.5142076789747364}_*{0.11084931631661361, 0.89553022742791089}_+{0.020764820056514043, 0.77540837228846071}Øvwxyz{|¹~›œ»€v`^_ _{0.056826181384498957, 0}_){0.042263822733579814, 0.261305740847734}_{0.056826181384498957, 0}Øvwxyz{|¹º¥¦»§¹€vdbc_){0.95698207338687646, 0.1578339198487313}_){0.95698207338687646, 0.1578339198487313}_){0.95698207338687646, 0.1578339198487313}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹®»¯°±¹¹…¹º¹ mrgl €€€¬ fp€_$D56CE297-66ED-4AE0-953F-6FBB5FBFA0CFÕÓÔÕÖ¹ÁÙÃj€&hÒ/Å1i€Ñ3€Ò/Ê1k€Ñ3€ÖíîïðñÏò¹ÒÓÔ#@/}`yÀô€(#@)°bêï’#@+X«\Bƒ #@SA!E¡.×þ·âÿØ€+€n€Ò/Ûo€cÒœ3߀pÙóðñ⻺I€bq€ Õdefgh»I»»€?Ò/è1s€Óœë3힀tß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹ñ»ò¹ºôõº½¹¹ù¹ a¹¹€|{vu€ €€€_$31C9E2D5-47FD-4CC4-BE5C-BB55DCFD3AC8ÕÓÔÕÖ¹Ùy€&wÒ/1x€Ñ3€Ò/ 1z€Ñ3€Öíîïðñò¹#?Â#ZbÍYD€(#@(“±GkpX#@*žÕ[d® #?Ê@¤‡ˆ_×þ·âÿ€+€}€Ò/~€cÒœ3"€Ùóðñ%»ºI€b€€ Õdefgh»I»»€?Ò/+1‚€Òœ3/€ƒß¶£§¤¬° <=>¡³¨­®±¹»23¹E¹8¹  ¹<¹ ¤…Š €‹¤„€_$324EC4B7-16AF-4DCA-8BC7-E519EB8C201AÕÓÔÕÖ¹CÙEˆ€&†Ò/G1‡€Ñ3€Ò/L1‰€Ñ3€ÖíîïðñQò¹TU»#¼ùâ‘÷‹ô3€(#@(“±GkpW#@*žÕ[d®ÓhiWlŒ €Ò/[1€Õœ^3`abc[NS.object.3€’Žš–Øvwxyz{| Ígh»i €v‘ _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Íqr»s €v•“” _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í{|»} €v™—˜ _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í…†»‡ €v›œ _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹»¹º’“º½¹¹—¹ a¹¹€¦¥ Ÿ€« €€€_$9A40C48C-A254-43B5-81F5-4876EE6155B2ÕÓÔÕÖ¹¢Ù¤£€&¡Ò/¦1¢€Ñ3€Ò/«1¤€Ñ3€Öíîïðñ°ò¹³´µ#@n w8v¦€(#@-,aÍ€O#@ ±Tçä”#@È5@Ì£'×þ·âÿ¹€+€§€Ò/¼¨€cÒœ3À€©ÙóðñûºI€bª€ ÕdefghIIII€?Ò/É1¬€Òœ3Í€­ß¶£§¤¬° <=>¡³¨­®±¹»ÐѹE¹Ö¹  ¹Ú¹ ¤ ¯´ €µ¤®€_$E28706FE-FF46-46F7-B859-62FA9CF47157ÕÓÔÕÖ¹áÙã²€&°Ò/å1±€Ñ3€Ò/ê1³€Ñ3€Öíîïðñ»ò¹ñò»€(#@-,aÍ€H#@ ±Tçä¦Óhiôl¶ €Ò/ø1·€Õœû3ýþÿ[NS.object.3€¼¸ÄÀØvwxyz{| Í» €v»¹º _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Í» €v¿½¾ _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í» €vÃÁ _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í"#»$ €vÇÅÆ _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹+»,-.¹¹…¹7¹ ÐÕÊÏ €€€¬ Ép€_$27F712CB-3433-427C-B64F-70F7749D5F53ÕÓÔÕÖ¹>Ù@Í€&ËÒ/B1Ì€Ñ3€Ò/G1΀Ñ3€ÖíîïðñLò¹OPQ#@,,Ï•[Ìá€(#@)°bêïl#@+X«\Bƒ#@8Ü>°ö¥·×þ·âÿU€+€Ñ€Ò/XÒ€cÒœ3\€ÓÙóðñ_»ºI€bÔ€ Õdefgh»I»»€?Ò/e1Ö€Óœh3j€×ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹n»o¹ºqrº½¹¹v¹ a¹¹€ßÞÙØ€ä €€€_$0A6A126D-934F-4B20-8368-16FE91C877A5ÕÓÔÕֹك܀&ÚÒ/…1Û€Ñ3€Ò/Š1Ý€Ñ3€Öíîïðñò¹’“”#?Â#ZbÍYH€(#@(*åM«#@*žÕ[d®#?Ú9Ú¤ö…×þ·âÿ˜€+€à€Ò/›á€cÒœ3Ÿ€âÙóðñ¢»ºI€b〠Õdefgh»I»»€?Ò/¨1å€Òœ3¬€æß¶£§¤¬° <=>¡³¨­®±¹»¯°¹E¹µ¹  ¹¹¹ ¤èí €î¤ç€_$7882A6A9-1618-4DB9-92C0-31AD3568028BÕÓÔÕÖ¹ÀÙÂë€&éÒ/Ä1ê€Ñ3€Ò/É1ì€Ñ3€ÖíîïðñÎò¹ÑÒÓ#¼ùâ‘÷‹ô:€(#@(*åM­#@*žÕ[d®#=ÙYìJxÓhiÕlï €Ò/Ù1ð€ÕœÜ3Þßàá[NS.object.3€õñýùØvwxyz{| Íåæ»ç €vôòó _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| Íïð»ñ €vøö÷ _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Íùú»û €vüúû _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í» €vþÿ _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹ »¹ºº½¹¹¹ a¹¹€ € €€€_$8DD87681-0A11-432D-8CF5-E7550A894CF1ÕÓÔÕÖ¹ Ù"€&Ò/$1€Ñ3€Ò/)1€Ñ3€Öíîïðñ.ò¹123#@n w8vª€(#@ʽ]Ö?C#@ ±Tçäœ#@:*àÓ×þ·âÿ7€+€ €Ò/: €cÒœ3>€ ÙóðñA»ºI€b € ÕdefghIIII€?Ò/G1€Òœ3K€ß¶£§¤¬° <=>¡³¨­®±¹»NO¹E¹T¹  ¹X¹ ¤ €¤€_$560ECFA3-167B-43CC-9C2C-64A111FDF6A4ÕÓÔÕÖ¹_Ùa€&Ò/c1€Ñ3€Ò/h1€Ñ3€Öíîïðñ»ò¹op»€(#@ʽ]Ö?B#@ ±Tçä©Óhirl €Ò/v1€Õœy3{|}~[NS.object.3€'#Øvwxyz{| Í‚ƒ»„ €v _{0.77614237490000004, 1}_{0.22385762510000001, 1}X{0.5, 1}Øvwxyz{| ÍŒ»Ž €v" ! _{1, 0.22385762510000001}_{1, 0.77614237490000004}X{1, 0.5}Øvwxyz{| Í–—»˜ €v&$% _{0.22385762510000001, 0}_{0.77614237490000004, 0}X{0.5, 0}Øvwxyz{| Í ¡»¢ €v*() _{0, 0.77614237490000004}_{0, 0.22385762510000001}X{0, 0.5}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·ª»«¹º­®º½¹¹²¹Ã¹¹€ 32-,€< €V€€_$90ED05E2-03D9-43D6-85F9-808A829BCE60ÕÓÔÕÖ¹½Ù¿0€&.Ò/Á1/€Ñ3€Ò/Æ11€Ñ3€ÖíîïðñËò¹ÎÏÐ#@IS®8ˆ¹x€(#@#Ò=µ3„ #@/ISõ5#@FŒÝB÷Øáþ·âÿÒÕ4€€+8€Ò/Øê5€]Òœ3Ü€6Öïðñòóôõà€\ 7Õdefgh»I»»€?Ò/ä9€cÒœ3è€:Ùóðñ뻺I€b;€ ÕdefghIIII€?Ò/ñ1=€Òœ3õ€>ß¶£§¤¬° <=>¡³¨­®±¹»øù¹Eþ¹I¹¹M@E € F€€?€n€_$E8FF48E0-6CA1-493A-BAE6-94B53FD72801ÕÓÔÕÖ¹ Ù C€&AÒ/ 1B€Ñ3€Ò/1D€Ñ3€Öíîïðñ»ò¹»€(#@#Ò=µ3„ #@/ISõ5$ÓhilG €Ò/ 1H€Õœ#3%&'([NS.object.3€MISQØvwxyz{|~,-».¹ €vLJK_+{-0.18808645190953224, 0.56413207311514058}_+{0.082408281845646539, 0.14880711741763814}_+{0.082408281845646539, 0.14880711741763814}Øvwxyz{|~67»8 €vPNO _*{0.91471263417920201, 0.51862644340287622}_){0.23324985825811739, 1.3946495467053943}_*{0.96718108717189888, 0.78207172461252916}Øvwxyz{|¹~ ³ ³»B€vYYR _*{0.98674701227196393, 0.26355123145345377}Øvwxyz{|¹ºHI»J¹€vVTU_+{0.082408281845646539, 0.14880711741763814}_+{0.082408281845646539, 0.14880711741763814}_+{0.082408281845646539, 0.14880711741763814}ß¶¢£¯§¤¬° ¥·¡³¨­®±¹Q»RST¹¹…¹]¹ _dY^ €€€¬ Xp€_$C676DDAD-AFCF-4003-AD6F-79C721A33012ÕÓÔÕÖ¹dÙf\€&ZÒ/h1[€Ñ3€Ò/m1]€Ñ3€Öíîïðñrò¹uvw#@@]Ëg³qc€(#@@ÇÝ„@?#@5õ¿é#z#@;X«\Bƒ×þ·âÿ{€+€`€Ò/~a€cÒœ3‚€bÙóðñ…»ºI€bc€ Õdefgh»I»»€?Ò/‹1e€ÓœŽ3œ€fß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹”»•¹º—˜º½¹¹œ¹Ã¹¹€nmhg€w €V€€_$890DE5ED-DCC6-4536-9D7E-8305DF460E10ÕÓÔÕÖ¹§Ù©k€&iÒ/«1j€Ñ3€Ò/°1l€Ñ3€Öíîïðñµò¹¸¹º#@Y\OméN€(#@@g²AÒÐ#@-|IÂáýU#?Ö>…‹R~ÆØáþ·âÿ¼¿o€€+s€Ò/Âêp€]Òœ3Æ€qÖïðñòóôõÊ€\ rÕdefghÍIÎÏ€?#?Á‘‘‘‘‘’#?À#?¿Ò/Ñt€cÒœ3Õ€uÙóðñØ»ºI€bv€ ÕdefghÝIÞ߀?#?îÞÞÞÞÞß#?äTTTTTT#?êZZZZZZÒ/á1x€Òœ3å€yß¶£§¤¬° <=>¡³¨­®±¹»èé¹Eî¹I¹ò¹M{€ € €€z€n€_$9B0DE56F-0033-4341-BD15-43AB28EE39B6ÕÓÔÕÖ¹ùÙû~€&|Ò/ý1}€Ñ3€Ò/1€Ñ3€Öíîïðñ»ò¹  »€(#@@g²AÒÑ#@-|IÂáýkÓhi l‚ €Ò/1ƒ€×œ3[NS.object.4[NS.object.3[NS.object.5€Œ”ˆ„˜Øvwxyz{|~ !»"¹ €v‡…†_,{0.069884524741148424, 0.072959499532503788}_+{0.22219138423366264, 0.044067987390972972}_+{0.22219138423366264, 0.044067987390972972}Øvwxyz{|A*+», €v‹‰Š _){0.12944826546315674, 1.2169565829187119}_,{-0.054610766246305988, 0.47592302198899411}_+{0.024580212194532047, 0.79471331597867045}Øvwxyz{|~45»6 €vŽ _){0.6771037704349544, 0.80806299328142051}_*{0.36352265197735933, 0.75736232411476478}_*{0.50931235917713447, 0.80041854648910904}Øvwxyz{|A>?»@ €v“‘’ _){1.0992283129309168, 0.50914264003499621}_){0.81458284544747761, 1.1952036350611777}_*{0.94934644033802651, 0.87039896013931584}Øvwxyz{|¹~HI»J€v—•– _{0.71724235567976824, 0}_*{0.88481905455585086, 0.15730135550209098}_{0.71724235567976824, 0}Øvwxyz{|¹ºRS»T¹€v›™š_+{0.22219138423366264, 0.044067987390972972}_+{0.22219138423366264, 0.044067987390972972}_+{0.22219138423366264, 0.044067987390972972}ß ¡¢£¤¥¨§²¬³­¯°´®±³¶·¹\»]¹º_`º½¹¹d¹Ã¹¹€¤£ž€© €V€€_$826A3EE6-F3CA-46BE-B894-3E887860A766ÕÓÔÕÖ¹oÙq¡€&ŸÒ/s1 €Ñ3€Ò/x1¢€Ñ3€Öíîïðñ}ò¹€‚#?Ș'Lð€(#@2‚Cõÿ"í#@&‹Æ-Òg#@àÉ.œ××þ·âÿ†€+€¥€Ò/‰¦€cÒœ3€§Ùóðñ»ºI€b¨€ Õdefgh»I»»€?Ò/–1ª€Òœ3š€«ß¶£§¤¬° <=>¡³¨­®±¹»ž¹ E£¹I¹§¹M­² € ³€€¬€n€_$7D21EC67-786C-4CA1-8FB7-24C320E2C796ÕÓÔÕÖ¹®Ù°°€&®Ò/²1¯€Ñ3€Ò/·1±€Ñ3€Öíîïðñ»ò¹¾¿»€(#@2‚Cõÿ"õ#@&‹Æ-ÒgÓhiÁl´ €Ò/Å1µ€ÕœÈ3ÊËÌÍ[NS.object.3€º¶Â¾Øvwxyz{|~ÑÒ»Ó¹ €v¹·¸_.{-0.0044199887191483465, -0.12164886358549279}_+{0.015570697457438594, 0.64619967871756223}_+{0.015570697457438594, 0.64619967871756223}Øvwxyz{| ÍÛÜ»Ý €v½»¼ _({1.0975749815102411, 1.0663092397019061}_+{0.88723624991425998, -0.21765290324350761}_*{0.99253993163280996, 0.42508164556954758}Øvwxyz{|Aåæ»ç €vÁ¿À _,{-0.019463371821798241, 0.19179770999932655}_*{0.059469617493593856, 1.2152404681453497}_+{0.015570697457438594, 0.64619967871756223}Øvwxyz{|¹~ïð»ñ€vÅÃÄ _+{0.015570697457438594, 0.64619967871756223}_+{0.015570697457438594, 0.64619967871756223}_+{0.015570697457438594, 0.64619967871756223}ß ¡¢£¤÷¨§øùúûü¬­®°±³ý¶þ¹»¹¹ Í ¹¹ ¹¹¹[glyphBounds_dontSynchroniseWithSymbol_heightIsClipped_lineSpacingBehaviourWpreview]textBehaviour_attributedString_!automaticallyDrawOnUnderlyingPath€ÑÎÿÈǀР€ê_$BB68C9D2-66B0-479B-AC67-CA0F556ADFABÕÓÔÕÖ¹ÙÌ€&ÉÒ/1Ê€Òœ3€ËÕB®CDFGHI€8€7€6Ò/$1Í€Ñ3€×ðíîïñò*+,¹./€(#@v Ï#@?#@ƒÀ#@~_$6C5D54FC-D648-48C5-9D79-A5BAED56A8EDTQUICÖþ·ÿ5€+€ÒÒ789_encodedAttributeséÓÙ;<=>œ?@ABCDEFGHXNS.key.2XNS.key.1XNS.key.0[NS.object.3XNS.key.3ßÞèÜÝÔäÕã_MSAttributedStringFontAttributeÒKLM_NSFontDescriptorAttributesÛÖÕ<=œOPQRJÙÚר€ _NSFontSizeAttribute#@r_NSFontNameAttribute_SourceCodePro-LightÒ56YZ_NSFontDescriptor¢[:_NSFontDescriptorVNSKern#À4WNSColorÔ`abcºdeUNSRGB\NSColorSpace_NSCustomColorSpaceF0 0 0àâÒghiTNSIDáÒ56kl\NSColorSpace¢m:\NSColorSpaceÒ56^o¢^:_NSParagraphStyleÔrst ÍvwZNSTabStops[NSAlignment\NSTextBlocks€åçÑyæÒ569{¢9:Ò56p}¢p:Ò56N¢N:Ò56‚_MSImmutableTextStyle¥ƒ„…†:_MSImmutableTextStyle__MSImmutableTextStyle_MSImmutableModelBase]MSModelObjectÒˆ‰Š_archivedAttributedStringþëÔŒŽ ‘’XNSString_NSAttributeInfo\NSAttributesÐûìýÓœ”3–ù€íÙ;<=˜œ?™šBœžD ¡[NS.object.3÷öèðñîÝïøWNSColorÔ`ab¤ºdeF0 0 0àâ_MSAttributedStringFontAttributeÒKLªÛòÕ<=œ¬­®RJôõóØ€ _NSFontSizeAttribute_NSFontNameAttribute_SourceCodePro-Light_NSParagraphStyleÔrst Ívw€åçVNSKernÙ;<=»œ?™šBœ¡ Ãž[NS.object.3÷öèðñøïúîÒÆÇÈXNS.bytesüDÒ56ÊË]NSMutableData£ÊÌ:VNSDataÒ56ÎÏ_NSAttributedString¢Ð:_NSAttributedStringÒ56ÒÓ_MSAttributedString¢Ô:_MSAttributedString_{{0, 0}, {616, 328}}Ò56ר_MSImmutableTextLayer©ÙÚÛÜÝÞßà:_MSImmutableTextLayer__MSImmutableTextLayer_MSImmutableStyledLayer__MSImmutableStyledLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectÓâãäåTbaseVguidesÒ/ç1€Ñ3€Ò56ìí_MSImmutableRulerData¥îïðñ:_MSImmutableRulerData__MSImmutableRulerData_MSImmutableModelBase]MSModelObject[{-637, 173}ÓâãôåÒ/÷1€Ñ3€Ò56üý_MSImmutablePage«þÿ:_MSImmutablePage__MSImmutablePage_MSImmutableLayerGroup__MSImmutableLayerGroup_MSImmutableStyledLayer__MSImmutableStyledLayer_MSImmutableLayer__MSImmutableLayer_MSImmutableModelBase]MSModelObjectÒ56  _MSImmutableDocumentData¥   :_MSImmutableDocumentData__MSImmutableDocumentData_MSImmutableModelBase]MSModelObjectZMSArchiverÑTroot€"+5:? Y _ ”   § À Æ Ù à ì þ  $ 0 = K M O P R T W Y [ \ ^ ` b d ‹   ª ± Ã Ê Ì Î Ð Ò Ô Ý æ è ê ï ñ ú$,5>QZm„’›Ÿ¤¦¯±³¸ºÃÙàíö;Yp~‡‰‹’›¸Ãàþ#,468ACEJLUw†¨Ëî)7@BDMOQVXa~ªÈë&4=?AJLNSU^„—½ä)Lp‡•ž ¢«·¹»&9OU^dvŒš²ÉÓçû &+4AH_npqs|~‚„†‡“–—˜šœŸ¡¢¥§¨ªÑæñ "$&/138:CEGLNWr}˜´ËÙòô &/AL^qˆ–¶Ì×áõ÷ùûý$7Kbpy{}¢®ºÆÒÞêöøûý  Tf{|~€‚„†‡‰Š‹Ž’¹ÒÓÕ×ÙÛ  5HSY[]_aj‘•–Ÿ¹ÄÞù')+02OQZ\efoxŸ¤¹½ÃÈÎÐÙì÷ 5CLds‹¤·Ëâð?@BKMOQRSUVXZ[\^_acŠŸ ¢¤¦¯±³¸ºÃÅÇÌÎëíöø ;Hacegprtƒ…‡âî!#$%'()+-./1Xmnprt}†ˆ‘“•šœµ¾ÀÁÊÓÜâ        $ & ( 1 3 5 N X a k t z ƒ … † ˆ Ÿ ¨ Á Ô í!!#!@!W!o!†!”!!À!Í!ð""$";"I"R"T"V"_"a"c"ˆ"Ž""¯"Â"Í"Ï"Ñ"Ó"Ô"é"ë"ô"ý### #P#h#„#¡#¸#Ð#ç#õ#þ$$,$M$n$$˜$¦$¯$±$³$¼$¾$À% %%%%*%+%-%/%0%1%3%<%=%?%@%B%C%E%F%H%J%q%†%‡%‰%‹%%–%˜%š%Ÿ%¡%ª%¬%®%³%µ%Î%×%Ù%Ú%ã%ì%õ%ú&&&&&&&%&'&)&:&<&>&@&B&c&p&z&€&Š&—&Ÿ&ª&«&­&¯&±&³&µ&¶&ä&ÿ''#';'F'^'w'Ž'œ'½'¾'À'Â'Ä'Æ'Ç'â((+(L(M(O(Q(S(U(V(q(Œ(§(°(Ç(Ò(é)))&)/)K)Z)v)“)¦)º)Ñ)ß)è***/*H*`*y*’*¬*¿*Ó*ê*ø+S+U+V+X+Z+[+]+_+a+b+c+e+f+g+i+k+l+m+o+–+«+¬+®+°+²+»+½+¿+Ä+Æ+Ï+Ñ+Ó+Ø+Ú+ó+ü+þ+ÿ,,,,7,9,;,=,?,H,J,L,U,W,Y,~,€,‚,„,…,š,œ,¥,§,©,²,´,¶------- - - --------?-T-U-W-Y-[-d-f-h-m-o-x-z-|--ƒ-œ-ž-Ÿ-¨-±-º-Ç-É-Ê-Ì-Õ-×-Ù-î-ú-ü-þ....%.&.(.*.,.../.\.w.’.³.´.¶.¸.º.¼.¾.¿.î//J/k/l/n/p/r/t/u//¾/Ù/ú/û/ý/ÿ00000:0U0^0v0‰0¡0º0Ó0í111+191ˆ1‰1‹1”1–1˜1š1›1œ1ž1Ÿ1¡1£1¤1¥1§1¨1ª1¬1Ó1è1é1ë1í1ï1ø1ú1ü222 222224262?2A2J2K2T2{2ˆ2¡2£2¥2§2°2²2´2Á2Ã2Å2Ç3"3$3%3'3)3*3,3.303132343536383:3;3<3>3e3z3{3}333Š3Œ3Ž3“3•3ž3 3¢3§3©3Â3Ë3Í3Î3×3à3é4 4 4444444!4*4,4.4G4I4J4L4a4c4l4n4p4y4{4}4¢4¤4¦4¨4©4¾4À4É4Ò4Û4ä4æ4è4ñ4ó4õ5@5A5C5E5F5G5I5J5L5M5O5P5R5S5U5W5~5“5”5–5˜5š5£5¥5§5¬5®5·5¹5»5À5Â5Û5Ý5Þ5ç5ð5ý5ÿ666 6 66 6"6$6&6(6I6J6L6N6P6R6S66œ6·6Ø6Ù6Û6Ý6ß6á6â6ý7)7D7e7f7h7j7l7n7o7Š7¥7À8888 8"8#8%8'8)8*8+8-8.8/81838485878^8s8t8v8x8z8ƒ8…8‡8Œ8Ž8—8™8›8 8¢8»8Ä8Æ8Ç8Ð8Ù8â8ÿ9999999999!9F9H9J9L9M9b9d9m9o9q9z9|9~9É9Ê9Ì9Î9Ï9Ð9Ò9Ó9Õ9Ö9Ø9Ù9Û9Ü9Þ9à:::::!:#:,:.:0:5:7:@:B:D:I:K:d:m:o:p:y:‚::‘:’:”::Ÿ:¡:¶:Â:Ä:Ç:É:Ì:Ï:ð:ñ:ó:ö:ø:û:ü;);D;_;€;;ƒ;†;‰;Œ;;¼;è<<6<7<9<<>> >">%>.>/>8>_>g>€>‚>„>†>>’>”>¡>¤>¦>©???? ? ???????????!?"?#?%?L?a?b?e?g?j?s?v?x?}??ˆ?‹??’?”?±?³?¼?¿?È?É?Ò?Û@@#@&@(@*@-@/@8@;@=@F@H@K@d@f@g@j@@@Š@@@˜@š@@Â@Ä@Ç@É@Ê@ß@á@ê@ó@üAAA AAAAcAdAgAjAkAlAnAoArAsAuAvAyAzA|A~A¥AºA»A¾AÀAÃAÌAÏAÑAÖAØAáAäAæAëAíBBB BBB(B+B,B.B7B:BNGNINJNSN\NeNrNuNvNxNN„N†N—N™NœNŸN¢NÃNÄNÆNÉNÌNÏNÐNþOO4OUOVOXO[O^OaObO}O«OÆOçOèOêOíOðOóOôPP*PEP P¢P£P¦P©PªP­P°P²P³P´P·P¸P¹P»P½P¾P¿PÁPèPýPþQQQQQQQQQ$Q'Q)Q.Q0QIQRQTQUQ^QgQpQQQ‘Q”Q–QŸQ¢Q¤Q­Q¯Q²Q×QÙQÜQÞQßQôQöQÿRRR RRR]R^RaRdReRfRhRiRlRmRoRpRsRtRvRxRŸR´RµR¸RºR½RÆRÉRËRÐRÒRÛRÞRàRåRçSSSS SSS+S.S/S1S:S=S?STS`SbSeShSkSnSSS’S•S˜S›SœSÉSäSÿT T!T#T&T)T,T-T\TŠT¸TÙTÚTÜTßTâTåTæUU/UJUkUlUnUqUtUwUxU“U®UÉVVVVV"V%V&V'V)V*V,V.V/V0V3V4V7V9V`VuVvVyV{V~V‡VŠVŒV‘V“VœVŸV¡V¦V¨VÅVÇVÐVÓVÜVÝVæVïWWW7W9W;W=WFWIWKWlWxW„WWœWŸW¡W¤W§WªW­W°W³XXXXXXXXX X!X"X%X&X'X)X+X,X-X/XVXkXlXoXqXtX}X€X‚X‡X‰X’X•X—XœXžX·XÀXÂXÃXÌXÕXÞXÿYYYY Y YYYY"Y$Y'Y@YIYKYLYOYdYfYoYrYtY}YY‚Y§Y©Y¬Y®Y¯YÄYÆYÏYÒYÔYÝYßYâZ-Z.Z1Z4Z5Z6Z8Z9Zb_b`bbbebhbkblbšbÉb÷cRcTcUcXc[c\c_cbcdcecfcicjckcmcocpcqcscšc¯c°c³cµc¸cÁcÄcÆcËcÍcÖcÙcÛcàcâcûdddddd"dCdFdHdJdMdOdXd[d]dfdhdkd„d†d‡dŠdŸd¡dªd­d¯d¸dºd½dâdädçdédêdÿee e eeeeeheieleoepeqesetewexeze{e~eeeƒeªe¿eÀeÃeÅeÈeÑeÔeÖeÛeÝeæeéeëeðeòf f fff f-f0f1f3f|?|Z|u|~|Ÿ| |¢|¥|¨|«|¬|Ç|â|ë} } }}}}}}4}O}X}y}z}|}}‚}…}†}¡}¼}Å~ ~"~#~&~)~*~-~0~2~3~4~7~8~9~<~>~?~@~B~i~~~~‚~„~‡~~“~•~š~œ~¥~¨~ª~¯~±~Ê~Ó~Õ~Ö~ß~è~ñ #%.03XZ]_`uw€ƒ…Ž“Þßâåæçéêíîñòõöùû€"€7€8€;€=€@€I€L€N€S€U€^€a€c€h€j€ƒ€…€†€€˜€¥€¨€©€«€´€·€¹€Î€Ú€Ü€ß€â€å€è 1LUvwy|‚ƒž¹Âãäæéìïð‚ ‚&‚/‚P‚Q‚S‚V‚Y‚\‚]‚x‚“‚œ‚÷‚ù‚ú‚ýƒƒƒƒƒ ƒ ƒ ƒƒƒƒƒƒƒƒƒ?ƒTƒUƒXƒZƒ]ƒfƒiƒkƒpƒrƒ{ƒ~ƒ€ƒ…ƒ‡ƒ ƒ©ƒ«ƒ¬ƒµƒ¾ƒÇƒèƒëƒíƒïƒòƒôƒý„„„ „ „„)„+„,„/„D„F„O„R„T„]„_„b„‡„‰„Œ„Ž„„¤„¦„¯„²„´„½„¿„Â… …………………………… …#…$…&…(…O…d…e…h…j…m…v…y…{…€…‚…‹…Ž……•…—…°…²…³…¼…Å…Ò…Õ…Ö…Ø…á…ä…æ…û†† † ††††6†7†9†<†?†B†C†q†Ÿ†Í†î†ï†ñ†ô†÷†ú†û‡(‡T‡‡¢‡£‡¥‡¨‡«‡®‡¯‡¶‡ãˆˆˆˆ ˆ ˆˆˆ?ˆmˆ›ˆêˆëˆîˆñˆôˆ÷ˆøˆùˆûˆüˆþ‰‰‰‰‰‰ ‰ ‰2‰G‰H‰K‰M‰P‰Y‰\‰^‰c‰e‰n‰q‰s‰x‰z‰“‰œ‰ž‰Ÿ‰¨‰±‰º‰×‰Ù‰Û‰Þ‰à‰é‰ì‰î‰÷‰ù‰üŠ!Š#Š&Š(Š)Š>Š@ŠIŠLŠNŠ[Š^Š`ŠcоŠÀŠÁŠÄŠÇŠÈŠËŠÎŠÐŠÑŠÒŠÕŠÖŠ×ŠÙŠÛŠÜŠÝŠß‹‹‹‹‹!‹$‹-‹0‹2‹7‹9‹B‹E‹G‹L‹N‹g‹p‹r‹s‹|‹…‹Ž‹¯‹²‹´‹¶‹¹‹»‹Ä‹Ç‹É‹Ò‹Ô‹×‹ð‹ò‹ó‹öŒ Œ ŒŒŒ(Œ1Œ4Œ6Œ?ŒAŒDŒiŒkŒnŒpŒqŒ†ŒˆŒ‘ŒšŒ£Œ¬Œ¯Œ±ŒºŒ¼Œ¿  !#%Labegjsvx}ˆ‹’”­¯°¹ÂÏÒÓÕÞá㎎ ŽŽ$Ž&Ž)Ž,Ž/Ž2Ž5Ž8ŽYŽZŽ\Ž_ŽbŽeŽfŽ•ŽÃŽñKz¨ÉÊÌÏÒÕÖ/\}~€ƒ†‰Š¶â‘‘0‘1‘3‘6‘9‘<‘=‘X‘…‘ ‘Á‘‘đǑʑ͑Αü’*’X’³’µ’¶’¹’¼’½’À’Ã’Å’Æ’Ç’Ê’Ë’Ì’Î’Ð’Ñ’Ò’Ô’û““““““"“%“'“,“.“7“:“<“A“C“\“e“g“h“q“z“ƒ“ “¢“¤“§“©“²“µ“·“À““œê“ì“ï“ñ“ò”” ”””” ”"”%”p”q”t”w”x”y”{”|””€”‚”ƒ”†”‡”‰”‹”²”ǔȔ˔͔Дٔܔޔã”å”î”ñ”ó”ø”ú•••••(•5•8•9•;•D•G•I•^•j•l•o•r•u•x•™•š•œ•Ÿ•¢•¥•¦•×––3–T–U–W–Z–]–`–a–Œ–º–ç—— — —————D—q—Ÿ—À—Á—×Ɨɗ̗͗û˜)˜W˜¦˜§˜ª˜­˜°˜³˜´˜µ˜·˜¸˜º˜¼˜½˜¾˜Á˜Â˜Å˜Ç˜î™™™™ ™ ™™™™™!™*™-™/™4™6™S™U™^™a™b™k™t™›™¨™Á™Ã™Å™Ç™Ð™Ó™Õ™öšššš&š)š+š.š1š4š7š:š=š˜ššš›šžš¡š¢š¥š¨šªš«š¬š¯š°š±š³šµš¶š·š¹šàšõšöšùšûšþ›› › ›››››!›&›(›A›J›L›M›V›_›h›‰›Œ›Ž››“›•›ž›¡›£›¬›®›±›Ê›Ó›Õ›Ö›Ù›î›ð›ù›ü›þœœ œ œ1œ3œ6œ8œ9œNœPœYœ\œ^œgœiœlœ·œ¸œ»œ¾œ¿œÀœÂœÃœÆœÇœÉœÊœÍœÎœÐœÒœù #%*,58:?AZcefox…ˆ‰‹”—™ª¬¯²µÖ×ÙÜßâãžž>žmžŽžž‘ž”ž—žšž›žÉžôŸ!ŸBŸCŸEŸHŸKŸNŸOŸ~Ÿ¯ŸÞ 9 ; < ? B C F I K L M P Q R T V W X Z  – — š œ Ÿ ¨ « ­ ² ´ ½ À Â Ç É â ë í î ÷¡¡ ¡*¡-¡/¡1¡4¡6¡?¡B¡D¡M¡O¡R¡k¡t¡v¡w¡z¡¡‘¡š¡¡Ÿ¡¨¡ª¡­¡Ò¡Ô¡×¡Ù¡Ú¡ï¡ñ¡ú¡ý¡ÿ¢¢ ¢ ¢X¢Y¢\¢_¢`¢a¢c¢d¢g¢h¢j¢k¢n¢o¢q¢s¢š¢¯¢°¢³¢µ¢¸¢Á¢Ä¢Æ¢Ë¢Í¢Ö¢Ù¢Û¢à¢â¢û££££££"£/£2£3£5£>£A£C£T£V£Y£\£_£€££ƒ£†£‰£Œ££¹£ç¤¤6¤7¤9¤<¤?¤B¤C¤q¤›¤È¤é¤ê¤ì¤ï¤ò¤õ¤ö¥$¥S¥¥Ü¥Þ¥ß¥â¥å¥æ¥é¥ì¥î¥ï¥ð¥ó¥ô¥õ¥÷¥ù¥ú¥û¥ý¦$¦9¦:¦=¦?¦B¦K¦N¦P¦U¦W¦`¦c¦e¦j¦l¦…¦Ž¦¦‘¦š¦£¦¬¦Í¦Ð¦Ò¦Ô¦×¦Ù¦â¦å¦ç¦ð¦ò¦õ§§§§§)§+§4§7§9§B§D§G§l§n§q§s§t§‰§‹§”§—§™§¢§¤§§§ò§ó§ö§ù§ú§û§ý§þ¨¨¨¨¨¨ ¨ ¨ ¨4¨I¨J¨M¨O¨R¨[¨^¨`¨e¨g¨p¨s¨u¨z¨|¨•¨—¨˜¨¡¨ª¨·¨º¨»¨½¨Æ¨É¨Ë¨è¨ô©© ©©©©©©© ©A©B©D©G©J©M©N©{©§©Ó©ô©õ©÷©ú©ýªªª-ªYª…ª¦ª§ª©ª¬ª¯ª²ª³ªà« «&«G«H«J«M«P«S«T«‚«¯«Ý«þ«ÿ¬¬¬¬ ¬ ¬'¬S¬o¬¬‘¬“¬–¬™¬œ¬¬É¬õ­!­p­q­t­w­z­}­~­­­‚­„­†­‡­ˆ­‹­Œ­­‘­¸­Í­Î­Ñ­Ó­Ö­ß­â­ä­é­ë­ô­÷­ù­þ®®®"®$®%®.®7®@®]®_®a®d®f®o®r®t®}®®‚®§®©®¬®®®¯®Ä®Æ®Ï®Ò®Ô®á®ä®æ®é¯D¯F¯G¯J¯M¯N¯Q¯T¯V¯W¯X¯[¯\¯]¯`¯b¯c¯d¯f¯¯¢¯£¯¦¯¨¯«¯´¯·¯¹¯¾¯À¯É¯Ì¯Î¯Ó¯Õ¯î¯÷¯ù¯ú°° °°2°4°6°9°;°D°G°I°R°T°W°|°~°°ƒ°„°™°›°¤°§°©°²°´°·±±±± ± ± ± ±±±±±±±±±±F±[±\±_±a±d±m±p±r±w±y±‚±…±‡±Œ±Ž±§±°±²±³±¼±Å±Ò±Õ±Ö±Ø±á±ä±æ±û²² ² ²²²²6²7²9²<²?²B²C²^²y²‚²£²¤²¦²©²¬²¯²°²Ë²æ²ï³³³³³³³³8³S³\³}³~³€³ƒ³†³‰³Š³¥³À³É´$´&´'´*´-´.´1´4´6´7´8´;´<´=´@´B´C´D´F´m´‚´ƒ´†´ˆ´‹´”´—´™´ž´ ´©´¬´®´³´µ´Î´×´Ù´Ú´ã´ì´õµµµµµµ$µ'µ)µ2µ4µ7µ\µ^µaµcµdµyµ{µ„µ‡µ‰µ’µ”µ—µâµãµæµéµêµëµíµîµñµòµõµöµùµúµýµÿ¶&¶;¶<¶?¶A¶D¶M¶P¶R¶W¶Y¶b¶e¶g¶l¶n¶‡¶‰¶Š¶“¶œ¶©¶¬¶­¶¯¶¸¶»¶½¶Ò¶Þ¶à¶ã¶æ¶é¶ì· ·······5·P·Y·z·{·}·€·ƒ·†·‡·¢·½·Æ·ç·è·ê·í·ð·ó·ô¸¸*¸3¸T¸U¸W¸Z¸]¸`¸a¸|¸—¸ ¸ï¸ð¸ó¸ö¸ù¸ü¸ý¸þ¹¹¹¹¹¹¹ ¹ ¹¹¹7¹L¹M¹P¹R¹U¹^¹a¹c¹h¹j¹s¹v¹x¹}¹¹˜¹¡¹£¹¤¹­¹¶¹¿¹Ü¹Þ¹à¹ã¹å¹î¹ñ¹ó¹ü¹þºº&º(º+º-º.ºCºEºNºQºSº`ºcºeºhºÃºÅºÆºÉºÌºÍºÐºÓºÕºÖº×ºÚºÛºÜºßºáºâºãºå» »!»"»%»'»*»3»6»8»=»?»H»K»M»R»T»m»v»x»y»‚»‹»”»±»³»µ»¸»º»Ã»Æ»È»Ñ»Ó»Ö»û»ý¼¼¼¼¼¼#¼&¼(¼1¼3¼6¼¼‚¼…¼ˆ¼‰¼Š¼Œ¼¼¼‘¼”¼•¼˜¼™¼œ¼ž¼Å¼Ú¼Û¼Þ¼à¼ã¼ì¼ï¼ñ¼ö¼ø½½½½ ½ ½&½/½1½2½;½D½M½Z½]½^½`½i½l½n½ƒ½½‘½”½—½š½½¾½¿½Á½Ä½Ç½Ê½Ë½æ¾¾ ¾+¾,¾.¾1¾4¾7¾8¾S¾n¾w¾˜¾™¾›¾ž¾¡¾¤¾¥¾À¾Û¾ä¿¿¿¿ ¿¿¿¿-¿H¿Q¿¬¿®¿¯¿²¿µ¿¶¿¹¿¼¿¾¿¿¿À¿Ã¿Ä¿Å¿È¿Ê¿Ë¿Ì¿Î¿õÀ À ÀÀÀÀÀÀ!À&À(À1À4À6À;À=ÀVÀ_ÀaÀbÀkÀtÀ}ÀšÀœÀžÀ¡À£À¬À¯À±ÀºÀ¼À¿ÀäÀæÀéÀëÀìÁÁÁ ÁÁÁÁÁÁjÁkÁnÁqÁrÁsÁuÁvÁyÁzÁ}Á~ÁÁ‚Á…Á‡Á®ÁÃÁÄÁÇÁÉÁÌÁÕÁØÁÚÁßÁáÁêÁíÁïÁôÁöÂÂÂÂÂ$Â1Â4Â5Â7Â@ÂCÂEÂZÂfÂhÂkÂnÂqÂtÂ•Â–Â˜Â›ÂžÂ¡Â¢Â½ÂØÂáÃÃÃÃà ÃÃÃ*ÃEÃNÃoÃpÃrÃuÃxÃ{Ã|×òûÃÜÃÝÃßÃâÃåÃèÃéÄÄÄ(ăąĆĉČÄÄēĕĖėĚěĜĞĠġĢĤÄËÄàÄáÄäÄæÄéÄòÄõÄ÷ÄüÄþÅÅ Å ÅÅÅ,Å5Å7Å8ÅAÅJÅSÅtÅwÅyÅ{Å~ŀʼnŌŎŗřŜŵŷŸŻÅÐÅÒÅÛÅÞÅàÅéÅëÅîÆÆÆÆÆÆ0Æ2Æ;Æ>Æ@ÆIÆKÆNÆ™ÆšÆÆ Æ¡Æ¢Æ¤Æ¥Æ¨Æ©Æ«Æ¬Æ¯Æ°Æ²Æ´ÆÛÆðÆñÆôÆöÆùÇÇÇÇ ÇÇÇÇÇ!Ç#Ç<Ç>Ç?ÇHÇQÇ^ÇaÇbÇdÇmÇpÇrLJǓǕǘǛǞǡÇÂÇÃÇÅÇÈÇËÇÎÇÏÇýÈ+ÈYÈzÈ{È}ȀȃȆȇȴÈàÉ É.É/É1É4É7É:É;ÉhɉɊɌÉɒɕɖÉÄÉòÊ ÊoÊpÊsÊvÊyÊ|Ê}Ê~Ê€ÊʃʅʆʇʊʋʎÊÊ·ÊÌÊÍÊÐÊÒÊÕÊÞÊáÊãÊèÊêÊóÊöÊøÊýÊÿËË!Ë#Ë$Ë-Ë6Ë?Ë\Ë^Ë`ËcËeËnËqËsË|Ë~Ë˦˨˫˭ˮËÃËÅËÎËÑËÓËàËãËåËèÌCÌEÌFÌIÌLÌMÌPÌSÌUÌVÌWÌZÌ[Ì\Ì^Ì`ÌaÌbÌd̵̷̡̠̤̦̩̲̼̋̾ÌÇÌÊÌÌÌÑÌÓÌìÌõÌ÷ÌøÍÍ ÍÍ4Í7Í9Í;Í>Í@ÍIÍLÍNÍWÍYÍ\ÍuÍwÍxÍ{Í͒͛ͤͭͶ͹ͻÍÄÍÆÍÉÍîÍðÍóÍõÍöÎ Î ÎÎÎ(Î1Î4Î6Î?ÎAÎDÎÎΓΖΗΘΚΛΞΟΡ΢ΥΦΨΪÎÑÎæÎçÎêÎìÎïÎøÎûÎýÏÏÏ ÏÏÏÏÏ2Ï4Ï5Ï>ÏGÏTÏWÏXÏZÏcÏfÏhÏ…Ï‘ÏϩϫϮϱϴϷϺϽÏÞÏßÏáÏäÏçÏêÏëÐÐHÐvЗИКÐРУФÐÐÐÿÑ-ÑNÑOÑQÑTÑWÑZÑ[чѴÑáÒÒÒÒÒ ÒÒÒ;ÒgÒ”ÒµÒ¶Ò¸Ò»Ò¾ÒÁÒÂÒÝÓ Ó%ÓFÓGÓIÓLÓOÓRÓSÓÓ¯ÓÝÔ8Ô:Ô;Ô>ÔAÔBÔEÔHÔJÔKÔLÔOÔPÔQÔSÔUÔVÔWÔYÔ€Ô•Ô–Ô™Ô›ÔžÔ§ÔªÔ¬Ô±Ô³Ô¼Ô¿ÔÁÔÆÔÈÔáÔêÔìÔíÔöÔÿÕÕ%Õ'Õ)Õ,Õ.Õ7Õ:Õ<ÕEÕGÕJÕoÕqÕtÕvÕwՌՎ՗՚՜եէժÕõÕöÕùÕüÕýÕþÖÖÖÖÖÖÖ Ö ÖÖÖ7ÖLÖMÖPÖRÖUÖ^ÖaÖcÖhÖjÖsÖvÖxÖ}ÖÖ˜ÖšÖ›Ö¤Ö­ÖºÖ½Ö¾ÖÀÖÉÖÌÖÎÖãÖïÖñÖôÖ÷ÖúÖý×××!×$×'×*×+×\׊׸×Ù×Ú×Ü×ß×â×å׿ØØ?ØlØØŽØØ“Ø–Ø™ØšØÉØöÙ$ÙEÙFÙHÙKÙNÙQÙRـٮÙÜÚ?ÚKÚgÚyÚÚ˜Ú¦Ú¹ÚÝÚßÚàÚãÚæÚéÚìÚïÚðÚñÚóÚöÚ÷ÚøÚûÚüÚþÚÿÛÛÛÛ+Û@ÛAÛDÛFÛIÛRÛUÛWÛ`ÛbÛeÛzÛ|Û~ۀۉیێۓە۲۴۽ÛÀÛÉÛÊÛÓÛÜÜÜÜ!Ü#Ü%Ü(Ü1ÜEÜHÜKÜpÜy܂܋ܗܠܣܦܩܬܯܸܻܲܵÜÝÜæÝÝÝ ÝÝ!Ý$Ý'Ý*Ý,ÝBÝKÝaÝw݀ݓݘݫݲݻÝÃÝÔÝÚÝçÝüÞÞÞ ÞÞÞÞÞ%Þ2Þ7ÞDÞMÞRÞeÞvÞÞޚޜޟޢާު޳޸ÞÁÞÆÞÏÞÔÞÝÞôÞÿßß.ßEßSß\ßwßzß}ߎߗߩ߶߹߼߿ßÂßÏßÒßÔß×ßüàà ààààààà à#à+à<àCàFàIàkàtàwàzàà’à•à˜à›àà³àÉàßàòáááá áá7áCáFáIáLáOáRáUáXá[á^ágápásáxááá–áá¦á»áÀáÕáÞáóáøâ â$â-âDâWânâ†âŸâ¹âÌâàâ÷ããããã!ã$ã-ã0ã2ã7ã9ãBãYãdã{ã“ãªã¸ãÄãÑãÔã×ãàãããåãêãìãõäää0äCä[ätää§äºäÎäåäóäüåå!å;åVåmå{å†å‹åå’golang-github-lucas-clemente-quic-go-0.46.0/docs/quic.svg000066400000000000000000000250721465664453100232270ustar00rootroot00000000000000 quic Created with Sketch. QUI C golang-github-lucas-clemente-quic-go-0.46.0/errors.go000066400000000000000000000044631465664453100224610ustar00rootroot00000000000000package quic import ( "fmt" "github.com/quic-go/quic-go/internal/qerr" ) type ( TransportError = qerr.TransportError ApplicationError = qerr.ApplicationError VersionNegotiationError = qerr.VersionNegotiationError StatelessResetError = qerr.StatelessResetError IdleTimeoutError = qerr.IdleTimeoutError HandshakeTimeoutError = qerr.HandshakeTimeoutError ) type ( TransportErrorCode = qerr.TransportErrorCode ApplicationErrorCode = qerr.ApplicationErrorCode StreamErrorCode = qerr.StreamErrorCode ) const ( NoError = qerr.NoError InternalError = qerr.InternalError ConnectionRefused = qerr.ConnectionRefused FlowControlError = qerr.FlowControlError StreamLimitError = qerr.StreamLimitError StreamStateError = qerr.StreamStateError FinalSizeError = qerr.FinalSizeError FrameEncodingError = qerr.FrameEncodingError TransportParameterError = qerr.TransportParameterError ConnectionIDLimitError = qerr.ConnectionIDLimitError ProtocolViolation = qerr.ProtocolViolation InvalidToken = qerr.InvalidToken ApplicationErrorErrorCode = qerr.ApplicationErrorErrorCode CryptoBufferExceeded = qerr.CryptoBufferExceeded KeyUpdateError = qerr.KeyUpdateError AEADLimitReached = qerr.AEADLimitReached NoViablePathError = qerr.NoViablePathError ) // A StreamError is used for Stream.CancelRead and Stream.CancelWrite. // It is also returned from Stream.Read and Stream.Write if the peer canceled reading or writing. type StreamError struct { StreamID StreamID ErrorCode StreamErrorCode Remote bool } func (e *StreamError) Is(target error) bool { _, ok := target.(*StreamError) return ok } func (e *StreamError) Error() string { pers := "local" if e.Remote { pers = "remote" } return fmt.Sprintf("stream %d canceled by %s with error code %d", e.StreamID, pers, e.ErrorCode) } // DatagramTooLargeError is returned from Connection.SendDatagram if the payload is too large to be sent. type DatagramTooLargeError struct { MaxDatagramPayloadSize int64 } func (e *DatagramTooLargeError) Is(target error) bool { _, ok := target.(*DatagramTooLargeError) return ok } func (e *DatagramTooLargeError) Error() string { return "DATAGRAM frame too large" } golang-github-lucas-clemente-quic-go-0.46.0/example/000077500000000000000000000000001465664453100222425ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/example/client/000077500000000000000000000000001465664453100235205ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/example/client/main.go000066400000000000000000000031611465664453100247740ustar00rootroot00000000000000package main import ( "bytes" "crypto/tls" "crypto/x509" "flag" "io" "log" "net/http" "os" "sync" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/internal/testdata" "github.com/quic-go/quic-go/qlog" ) func main() { quiet := flag.Bool("q", false, "don't print the data") keyLogFile := flag.String("keylog", "", "key log file") insecure := flag.Bool("insecure", false, "skip certificate verification") flag.Parse() urls := flag.Args() var keyLog io.Writer if len(*keyLogFile) > 0 { f, err := os.Create(*keyLogFile) if err != nil { log.Fatal(err) } defer f.Close() keyLog = f } pool, err := x509.SystemCertPool() if err != nil { log.Fatal(err) } testdata.AddRootCA(pool) roundTripper := &http3.RoundTripper{ TLSClientConfig: &tls.Config{ RootCAs: pool, InsecureSkipVerify: *insecure, KeyLogWriter: keyLog, }, QUICConfig: &quic.Config{ Tracer: qlog.DefaultConnectionTracer, }, } defer roundTripper.Close() hclient := &http.Client{ Transport: roundTripper, } var wg sync.WaitGroup wg.Add(len(urls)) for _, addr := range urls { log.Printf("GET %s", addr) go func(addr string) { rsp, err := hclient.Get(addr) if err != nil { log.Fatal(err) } log.Printf("Got response for %s: %#v", addr, rsp) body := &bytes.Buffer{} _, err = io.Copy(body, rsp.Body) if err != nil { log.Fatal(err) } if *quiet { log.Printf("Response Body: %d bytes", body.Len()) } else { log.Printf("Response Body (%d bytes):\n%s", body.Len(), body.Bytes()) } wg.Done() }(addr) } wg.Wait() } golang-github-lucas-clemente-quic-go-0.46.0/example/echo/000077500000000000000000000000001465664453100231605ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/example/echo/echo.go000066400000000000000000000051331465664453100244270ustar00rootroot00000000000000package main import ( "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io" "log" "math/big" "github.com/quic-go/quic-go" ) const addr = "localhost:4242" const message = "foobar" // We start a server echoing data on the first stream the client opens, // then connect with a client, send the message, and wait for its receipt. func main() { go func() { log.Fatal(echoServer()) }() err := clientMain() if err != nil { panic(err) } } // Start a server that echos all data on the first stream opened by the client func echoServer() error { listener, err := quic.ListenAddr(addr, generateTLSConfig(), nil) if err != nil { return err } defer listener.Close() conn, err := listener.Accept(context.Background()) if err != nil { return err } stream, err := conn.AcceptStream(context.Background()) if err != nil { panic(err) } defer stream.Close() // Echo through the loggingWriter _, err = io.Copy(loggingWriter{stream}, stream) return err } func clientMain() error { tlsConf := &tls.Config{ InsecureSkipVerify: true, NextProtos: []string{"quic-echo-example"}, } conn, err := quic.DialAddr(context.Background(), addr, tlsConf, nil) if err != nil { return err } defer conn.CloseWithError(0, "") stream, err := conn.OpenStreamSync(context.Background()) if err != nil { return err } defer stream.Close() fmt.Printf("Client: Sending '%s'\n", message) _, err = stream.Write([]byte(message)) if err != nil { return err } buf := make([]byte, len(message)) _, err = io.ReadFull(stream, buf) if err != nil { return err } fmt.Printf("Client: Got '%s'\n", buf) return nil } // A wrapper for io.Writer that also logs the message. type loggingWriter struct{ io.Writer } func (w loggingWriter) Write(b []byte) (int, error) { fmt.Printf("Server: Got '%s'\n", string(b)) return w.Writer.Write(b) } // Setup a bare-bones TLS config for the server func generateTLSConfig() *tls.Config { key, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { panic(err) } template := x509.Certificate{SerialNumber: big.NewInt(1)} certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key) if err != nil { panic(err) } keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}) certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER}) tlsCert, err := tls.X509KeyPair(certPEM, keyPEM) if err != nil { panic(err) } return &tls.Config{ Certificates: []tls.Certificate{tlsCert}, NextProtos: []string{"quic-echo-example"}, } } golang-github-lucas-clemente-quic-go-0.46.0/example/main.go000066400000000000000000000111631465664453100235170ustar00rootroot00000000000000package main import ( "crypto/md5" "errors" "flag" "fmt" "io" "log" "mime/multipart" "net/http" "strconv" "strings" "sync" _ "net/http/pprof" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/internal/testdata" "github.com/quic-go/quic-go/qlog" ) type binds []string func (b binds) String() string { return strings.Join(b, ",") } func (b *binds) Set(v string) error { *b = strings.Split(v, ",") return nil } // Size is needed by the /demo/upload handler to determine the size of the uploaded file type Size interface { Size() int64 } // See https://en.wikipedia.org/wiki/Lehmer_random_number_generator func generatePRData(l int) []byte { res := make([]byte, l) seed := uint64(1) for i := 0; i < l; i++ { seed = seed * 48271 % 2147483647 res[i] = byte(seed) } return res } func setupHandler(www string) http.Handler { mux := http.NewServeMux() if len(www) > 0 { mux.Handle("/", http.FileServer(http.Dir(www))) } else { mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Printf("%#v\n", r) const maxSize = 1 << 30 // 1 GB num, err := strconv.ParseInt(strings.ReplaceAll(r.RequestURI, "/", ""), 10, 64) if err != nil || num <= 0 || num > maxSize { w.WriteHeader(400) return } w.Write(generatePRData(int(num))) }) } mux.HandleFunc("/demo/tile", func(w http.ResponseWriter, r *http.Request) { // Small 40x40 png w.Write([]byte{ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x01, 0x03, 0x00, 0x00, 0x00, 0xb6, 0x30, 0x2a, 0x2e, 0x00, 0x00, 0x00, 0x03, 0x50, 0x4c, 0x54, 0x45, 0x5a, 0xc3, 0x5a, 0xad, 0x38, 0xaa, 0xdb, 0x00, 0x00, 0x00, 0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0x01, 0x63, 0x18, 0x61, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xe2, 0xb8, 0x75, 0x22, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, }) }) mux.HandleFunc("/demo/tiles", func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "") for i := 0; i < 200; i++ { fmt.Fprintf(w, ``, i) } io.WriteString(w, "") }) mux.HandleFunc("/demo/echo", func(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { fmt.Printf("error reading body while handling /echo: %s\n", err.Error()) } w.Write(body) }) // accept file uploads and return the MD5 of the uploaded file // maximum accepted file size is 1 GB mux.HandleFunc("/demo/upload", func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPost { err := r.ParseMultipartForm(1 << 30) // 1 GB if err == nil { var file multipart.File file, _, err = r.FormFile("uploadfile") if err == nil { var size int64 if sizeInterface, ok := file.(Size); ok { size = sizeInterface.Size() b := make([]byte, size) file.Read(b) md5 := md5.Sum(b) fmt.Fprintf(w, "%x", md5) return } err = errors.New("couldn't get uploaded file size") } } log.Printf("Error receiving upload: %#v", err) } io.WriteString(w, `

`) }) return mux } func main() { // defer profile.Start().Stop() go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() // runtime.SetBlockProfileRate(1) bs := binds{} flag.Var(&bs, "bind", "bind to") www := flag.String("www", "", "www data") tcp := flag.Bool("tcp", false, "also listen on TCP") key := flag.String("key", "", "TLS key (requires -cert option)") cert := flag.String("cert", "", "TLS certificate (requires -key option)") flag.Parse() if len(bs) == 0 { bs = binds{"localhost:6121"} } handler := setupHandler(*www) var wg sync.WaitGroup wg.Add(len(bs)) var certFile, keyFile string if *key != "" && *cert != "" { keyFile = *key certFile = *cert } else { certFile, keyFile = testdata.GetCertificatePaths() } for _, b := range bs { fmt.Println("listening on", b) bCap := b go func() { var err error if *tcp { err = http3.ListenAndServeTLS(bCap, certFile, keyFile, handler) } else { server := http3.Server{ Handler: handler, Addr: bCap, QUICConfig: &quic.Config{ Tracer: qlog.DefaultConnectionTracer, }, } err = server.ListenAndServeTLS(certFile, keyFile) } if err != nil { fmt.Println(err) } wg.Done() }() } wg.Wait() } golang-github-lucas-clemente-quic-go-0.46.0/frame_sorter.go000066400000000000000000000140651465664453100236340ustar00rootroot00000000000000package quic import ( "errors" "sync" "github.com/quic-go/quic-go/internal/protocol" list "github.com/quic-go/quic-go/internal/utils/linkedlist" ) // byteInterval is an interval from one ByteCount to the other type byteInterval struct { Start protocol.ByteCount End protocol.ByteCount } var byteIntervalElementPool sync.Pool func init() { byteIntervalElementPool = *list.NewPool[byteInterval]() } type frameSorterEntry struct { Data []byte DoneCb func() } type frameSorter struct { queue map[protocol.ByteCount]frameSorterEntry readPos protocol.ByteCount gaps *list.List[byteInterval] } var errDuplicateStreamData = errors.New("duplicate stream data") func newFrameSorter() *frameSorter { s := frameSorter{ gaps: list.NewWithPool[byteInterval](&byteIntervalElementPool), queue: make(map[protocol.ByteCount]frameSorterEntry), } s.gaps.PushFront(byteInterval{Start: 0, End: protocol.MaxByteCount}) return &s } func (s *frameSorter) Push(data []byte, offset protocol.ByteCount, doneCb func()) error { err := s.push(data, offset, doneCb) if err == errDuplicateStreamData { if doneCb != nil { doneCb() } return nil } return err } func (s *frameSorter) push(data []byte, offset protocol.ByteCount, doneCb func()) error { if len(data) == 0 { return errDuplicateStreamData } start := offset end := offset + protocol.ByteCount(len(data)) if end <= s.gaps.Front().Value.Start { return errDuplicateStreamData } startGap, startsInGap := s.findStartGap(start) endGap, endsInGap := s.findEndGap(startGap, end) startGapEqualsEndGap := startGap == endGap if (startGapEqualsEndGap && end <= startGap.Value.Start) || (!startGapEqualsEndGap && startGap.Value.End >= endGap.Value.Start && end <= startGap.Value.Start) { return errDuplicateStreamData } startGapNext := startGap.Next() startGapEnd := startGap.Value.End // save it, in case startGap is modified endGapStart := endGap.Value.Start // save it, in case endGap is modified endGapEnd := endGap.Value.End // save it, in case endGap is modified var adjustedStartGapEnd bool var wasCut bool pos := start var hasReplacedAtLeastOne bool for { oldEntry, ok := s.queue[pos] if !ok { break } oldEntryLen := protocol.ByteCount(len(oldEntry.Data)) if end-pos > oldEntryLen || (hasReplacedAtLeastOne && end-pos == oldEntryLen) { // The existing frame is shorter than the new frame. Replace it. delete(s.queue, pos) pos += oldEntryLen hasReplacedAtLeastOne = true if oldEntry.DoneCb != nil { oldEntry.DoneCb() } } else { if !hasReplacedAtLeastOne { return errDuplicateStreamData } // The existing frame is longer than the new frame. // Cut the new frame such that the end aligns with the start of the existing frame. data = data[:pos-start] end = pos wasCut = true break } } if !startsInGap && !hasReplacedAtLeastOne { // cut the frame, such that it starts at the start of the gap data = data[startGap.Value.Start-start:] start = startGap.Value.Start wasCut = true } if start <= startGap.Value.Start { if end >= startGap.Value.End { // The frame covers the whole startGap. Delete the gap. s.gaps.Remove(startGap) } else { startGap.Value.Start = end } } else if !hasReplacedAtLeastOne { startGap.Value.End = start adjustedStartGapEnd = true } if !startGapEqualsEndGap { s.deleteConsecutive(startGapEnd) var nextGap *list.Element[byteInterval] for gap := startGapNext; gap.Value.End < endGapStart; gap = nextGap { nextGap = gap.Next() s.deleteConsecutive(gap.Value.End) s.gaps.Remove(gap) } } if !endsInGap && start != endGapEnd && end > endGapEnd { // cut the frame, such that it ends at the end of the gap data = data[:endGapEnd-start] end = endGapEnd wasCut = true } if end == endGapEnd { if !startGapEqualsEndGap { // The frame covers the whole endGap. Delete the gap. s.gaps.Remove(endGap) } } else { if startGapEqualsEndGap && adjustedStartGapEnd { // The frame split the existing gap into two. s.gaps.InsertAfter(byteInterval{Start: end, End: startGapEnd}, startGap) } else if !startGapEqualsEndGap { endGap.Value.Start = end } } if wasCut && len(data) < protocol.MinStreamFrameBufferSize { newData := make([]byte, len(data)) copy(newData, data) data = newData if doneCb != nil { doneCb() doneCb = nil } } if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps { return errors.New("too many gaps in received data") } s.queue[start] = frameSorterEntry{Data: data, DoneCb: doneCb} return nil } func (s *frameSorter) findStartGap(offset protocol.ByteCount) (*list.Element[byteInterval], bool) { for gap := s.gaps.Front(); gap != nil; gap = gap.Next() { if offset >= gap.Value.Start && offset <= gap.Value.End { return gap, true } if offset < gap.Value.Start { return gap, false } } panic("no gap found") } func (s *frameSorter) findEndGap(startGap *list.Element[byteInterval], offset protocol.ByteCount) (*list.Element[byteInterval], bool) { for gap := startGap; gap != nil; gap = gap.Next() { if offset >= gap.Value.Start && offset < gap.Value.End { return gap, true } if offset < gap.Value.Start { return gap.Prev(), false } } panic("no gap found") } // deleteConsecutive deletes consecutive frames from the queue, starting at pos func (s *frameSorter) deleteConsecutive(pos protocol.ByteCount) { for { oldEntry, ok := s.queue[pos] if !ok { break } oldEntryLen := protocol.ByteCount(len(oldEntry.Data)) delete(s.queue, pos) if oldEntry.DoneCb != nil { oldEntry.DoneCb() } pos += oldEntryLen } } func (s *frameSorter) Pop() (protocol.ByteCount, []byte, func()) { entry, ok := s.queue[s.readPos] if !ok { return s.readPos, nil, nil } delete(s.queue, s.readPos) offset := s.readPos s.readPos += protocol.ByteCount(len(entry.Data)) if s.gaps.Front().Value.End <= s.readPos { panic("frame sorter BUG: read position higher than a gap") } return offset, entry.Data, entry.DoneCb } // HasMoreData says if there is any more data queued at *any* offset. func (s *frameSorter) HasMoreData() bool { return len(s.queue) > 0 } golang-github-lucas-clemente-quic-go-0.46.0/frame_sorter_test.go000066400000000000000000001162501465664453100246720ustar00rootroot00000000000000package quic import ( "bytes" "fmt" "math" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("frame sorter", func() { var s *frameSorter checkGaps := func(expectedGaps []byteInterval) { if s.gaps.Len() != len(expectedGaps) { fmt.Println("Gaps:") for gap := s.gaps.Front(); gap != nil; gap = gap.Next() { fmt.Printf("\t%d - %d\n", gap.Value.Start, gap.Value.End) } ExpectWithOffset(1, s.gaps.Len()).To(Equal(len(expectedGaps))) } var i int for gap := s.gaps.Front(); gap != nil; gap = gap.Next() { ExpectWithOffset(1, gap.Value).To(Equal(expectedGaps[i])) i++ } } type callbackTracker struct { called *bool cb func() } getCallback := func() (func(), callbackTracker) { var called bool cb := func() { if called { panic("double free") } called = true } return cb, callbackTracker{ cb: cb, called: &called, } } checkCallbackCalled := func(t callbackTracker) { ExpectWithOffset(1, *t.called).To(BeTrue()) } checkCallbackNotCalled := func(t callbackTracker) { ExpectWithOffset(1, *t.called).To(BeFalse()) t.cb() ExpectWithOffset(1, *t.called).To(BeTrue()) } BeforeEach(func() { s = newFrameSorter() }) It("returns nil when empty", func() { _, data, doneCb := s.Pop() Expect(data).To(BeNil()) Expect(doneCb).To(BeNil()) }) It("inserts and pops a single frame", func() { cb, t := getCallback() Expect(s.Push([]byte("foobar"), 0, cb)).To(Succeed()) offset, data, doneCb := s.Pop() Expect(offset).To(BeZero()) Expect(data).To(Equal([]byte("foobar"))) Expect(doneCb).ToNot(BeNil()) checkCallbackNotCalled(t) offset, data, doneCb = s.Pop() Expect(offset).To(Equal(protocol.ByteCount(6))) Expect(data).To(BeNil()) Expect(doneCb).To(BeNil()) }) It("inserts and pops two consecutive frame", func() { cb1, t1 := getCallback() cb2, t2 := getCallback() Expect(s.Push([]byte("bar"), 3, cb2)).To(Succeed()) Expect(s.Push([]byte("foo"), 0, cb1)).To(Succeed()) offset, data, doneCb := s.Pop() Expect(offset).To(BeZero()) Expect(data).To(Equal([]byte("foo"))) Expect(doneCb).ToNot(BeNil()) doneCb() checkCallbackCalled(t1) offset, data, doneCb = s.Pop() Expect(offset).To(Equal(protocol.ByteCount(3))) Expect(data).To(Equal([]byte("bar"))) Expect(doneCb).ToNot(BeNil()) doneCb() checkCallbackCalled(t2) offset, data, doneCb = s.Pop() Expect(offset).To(Equal(protocol.ByteCount(6))) Expect(data).To(BeNil()) Expect(doneCb).To(BeNil()) }) It("ignores empty frames", func() { Expect(s.Push(nil, 0, nil)).To(Succeed()) _, data, doneCb := s.Pop() Expect(data).To(BeNil()) Expect(doneCb).To(BeNil()) }) It("says if has more data", func() { Expect(s.HasMoreData()).To(BeFalse()) Expect(s.Push([]byte("foo"), 0, nil)).To(Succeed()) Expect(s.HasMoreData()).To(BeTrue()) _, data, _ := s.Pop() Expect(data).To(Equal([]byte("foo"))) Expect(s.HasMoreData()).To(BeFalse()) }) Context("Gap handling", func() { var dataCounter uint8 BeforeEach(func() { dataCounter = 0 }) checkQueue := func(m map[protocol.ByteCount][]byte) { ExpectWithOffset(1, s.queue).To(HaveLen(len(m))) for offset, data := range m { ExpectWithOffset(1, s.queue).To(HaveKey(offset)) ExpectWithOffset(1, s.queue[offset].Data).To(Equal(data)) } } getData := func(l protocol.ByteCount) []byte { dataCounter++ return bytes.Repeat([]byte{dataCounter}, int(l)) } // ---xxx-------------- // ++++++ // => // ---xxx++++++-------- It("case 1", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(5) cb2, t2 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 11, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) }) // ---xxx----------------- // +++++++ // => // ---xxx---+++++++-------- It("case 2", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(5) cb2, t2 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 -15 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 10: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 6, End: 10}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) }) // ---xxx----xxxxxx------- // ++++ // => // ---xxx++++xxxxx-------- It("case 3", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() f3 := getData(5) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f3, 10, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f2, 6, cb3)).To(Succeed()) // 6 - 10 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f2, 10: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ----xxxx------- // ++++ // => // ----xxxx++----- It("case 4", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 7 Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 9 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 7: f2[2:], }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 9, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) It("case 4, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2)) f1 := getData(4 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 7 Expect(s.Push(f2, 5*mult, cb2)).To(Succeed()) // 5 - 9 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f1, 7 * mult: f2[2*mult:], }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 9 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) }) // xxxx------- // ++++ // => // xxxx+++----- It("case 5", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() Expect(s.Push(f1, 0, cb1)).To(Succeed()) // 0 - 4 Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 7 checkQueue(map[protocol.ByteCount][]byte{ 0: f1, 4: f2[1:], }) checkGaps([]byteInterval{ {Start: 7, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) It("case 5, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2)) f1 := getData(4 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() Expect(s.Push(f1, 0, cb1)).To(Succeed()) // 0 - 4 Expect(s.Push(f2, 3*mult, cb2)).To(Succeed()) // 3 - 7 checkQueue(map[protocol.ByteCount][]byte{ 0: f1, 4 * mult: f2[mult:], }) checkGaps([]byteInterval{ {Start: 7 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) }) // ----xxxx------- // ++++ // => // --++xxxx------- It("case 6", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 9 Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 7 checkQueue(map[protocol.ByteCount][]byte{ 3: f2[:2], 5: f1, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 9, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) It("case 6, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2)) f1 := getData(4 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() Expect(s.Push(f1, 5*mult, cb1)).To(Succeed()) // 5 - 9 Expect(s.Push(f2, 3*mult, cb2)).To(Succeed()) // 3 - 7 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f2[:2*mult], 5 * mult: f1, }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 9 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) }) // ---xxx----xxxxxx------- // ++ // => // ---xxx++--xxxxx-------- It("case 7", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(2) cb2, t2 := getCallback() f3 := getData(5) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f3, 10, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f2, 6, cb3)).To(Succeed()) // 6 - 8 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f2, 10: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 8, End: 10}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx---------xxxxxx-- // ++ // => // ---xxx---++----xxxxx-- It("case 8", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(2) cb2, t2 := getCallback() f3 := getData(5) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f3, 15, cb2)).To(Succeed()) // 15 - 20 Expect(s.Push(f2, 10, cb3)).To(Succeed()) // 10 - 12 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 10: f2, 15: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 6, End: 10}, {Start: 12, End: 15}, {Start: 20, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx----xxxxxx------- // ++ // => // ---xxx--++xxxxx-------- It("case 9", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(2) cb2, t2 := getCallback() cb3, t3 := getCallback() f3 := getData(5) Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f3, 10, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f2, 8, cb3)).To(Succeed()) // 8 - 10 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 8: f2, 10: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 6, End: 8}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx----=====------- // +++++++ // => // ---xxx++++=====-------- It("case 10", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(5) cb2, t2 := getCallback() f3 := getData(6) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f3, 5, cb3)).To(Succeed()) // 5 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f3[1:5], 10: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) It("case 10, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 4)) f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(5 * mult) cb2, t2 := getCallback() f3 := getData(6 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 10*mult, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f3, 5*mult, cb3)).To(Succeed()) // 5 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f1, 6 * mult: f3[mult : 5*mult], 10 * mult: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 15 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxxx----=====------- // ++++++ // => // ---xxx++++=====-------- It("case 11", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(5) cb2, t2 := getCallback() f3 := getData(5) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 7 Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f3, 5, cb3)).To(Succeed()) // 5 - 10 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 7: f3[2:], 10: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) // ---xxxx----=====------- // ++++++ // => // ---xxx++++=====-------- It("case 11, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 3)) f1 := getData(4 * mult) cb1, t1 := getCallback() f2 := getData(5 * mult) cb2, t2 := getCallback() f3 := getData(5 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 7 Expect(s.Push(f2, 10*mult, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f3, 5*mult, cb3)).To(Succeed()) // 5 - 10 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f1, 7 * mult: f3[2*mult:], 10 * mult: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 15 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ----xxxx------- // +++++++ // => // ----+++++++----- It("case 12", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(7) cb2, t2 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 7 Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 10 checkQueue(map[protocol.ByteCount][]byte{ 3: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) }) // ----xxx===------- // +++++++ // => // ----+++++++----- It("case 13", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(7) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 10 checkQueue(map[protocol.ByteCount][]byte{ 3: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // ----xxx====------- // +++++ // => // ----+++====----- It("case 14", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() f3 := getData(5) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 10 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 8 checkQueue(map[protocol.ByteCount][]byte{ 3: f3[:3], 6: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) It("case 14, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 3)) f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() f3 := getData(5 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 10 Expect(s.Push(f3, 3*mult, cb3)).To(Succeed()) // 3 - 8 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f3[:3*mult], 6 * mult: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 10 * mult, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ----xxx===------- // ++++++ // => // ----++++++----- It("case 15", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(6) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 9 checkQueue(map[protocol.ByteCount][]byte{ 3: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 9, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // ---xxxx------- // ++++ // => // ---xxxx----- It("case 16", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 9 Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 9 checkQueue(map[protocol.ByteCount][]byte{ 5: f1, }) checkGaps([]byteInterval{ {Start: 0, End: 5}, {Start: 9, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) // ----xxx===------- // +++ // => // ----xxx===----- It("case 17", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(3) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 6 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 9, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) // ---xxxx------- // ++ // => // ---xxxx----- It("case 18", func() { f1 := getData(4) cb1, t1 := getCallback() f2 := getData(2) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 9 Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 7 checkQueue(map[protocol.ByteCount][]byte{ 5: f1, }) checkGaps([]byteInterval{ {Start: 0, End: 5}, {Start: 9, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) // ---xxxxx------ // ++ // => // ---xxxxx---- It("case 19", func() { f1 := getData(5) cb1, t1 := getCallback() f2 := getData(2) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 10 checkQueue(map[protocol.ByteCount][]byte{ 5: f1, }) Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 8 checkQueue(map[protocol.ByteCount][]byte{ 5: f1, }) checkGaps([]byteInterval{ {Start: 0, End: 5}, {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) // xxxxx------ // ++ // => // xxxxx------ It("case 20", func() { f1 := getData(10) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() Expect(s.Push(f1, 0, cb1)).To(Succeed()) // 0 - 10 Expect(s.Push(f2, 5, cb2)).To(Succeed()) // 5 - 9 checkQueue(map[protocol.ByteCount][]byte{ 0: f1, }) checkGaps([]byteInterval{ {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) // ---xxxxx--- // +++ // => // ---xxxxx--- It("case 21", func() { f1 := getData(5) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 10 Expect(s.Push(f2, 7, cb2)).To(Succeed()) // 7 - 10 checkGaps([]byteInterval{ {Start: 0, End: 5}, {Start: 10, End: protocol.MaxByteCount}, }) checkQueue(map[protocol.ByteCount][]byte{ 5: f1, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) }) // ----xxx------ // +++++ // => // --+++++---- It("case 22", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(5) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 8 Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 8 checkQueue(map[protocol.ByteCount][]byte{ 3: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 8, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) }) // ----xxx===------ // ++++++++ // => // --++++++++---- It("case 23", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(8) cb3, t3 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 8 Expect(s.Push(f2, 8, cb2)).To(Succeed()) // 8 - 11 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 11, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // --xxx---===--- // ++++++ // => // --xxx++++++---- It("case 24", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(6) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 6, cb3)).To(Succeed()) // 6 - 12 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 12, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // --xxx---===---### // +++++++++ // => // --xxx+++++++++### It("case 25", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(3) cb3, t3 := getCallback() f4 := getData(9) cb4, t4 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 15, cb3)).To(Succeed()) // 15 - 18 Expect(s.Push(f4, 6, cb4)).To(Succeed()) // 6 - 15 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f4, 15: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 18, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) checkCallbackNotCalled(t4) }) // ----xxx------ // +++++++ // => // --+++++++--- It("case 26", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(10) cb2, t2 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 8 Expect(s.Push(f2, 3, cb2)).To(Succeed()) // 3 - 13 checkQueue(map[protocol.ByteCount][]byte{ 3: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 13, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) }) // ---xxx====--- // ++++ // => // --+xxx====--- It("case 27", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() f3 := getData(4) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 10 Expect(s.Push(f3, 2, cb3)).To(Succeed()) // 2 - 6 checkQueue(map[protocol.ByteCount][]byte{ 2: f3[:1], 3: f1, 6: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 2}, {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) It("case 27, for long frames", func() { const mult = protocol.MinStreamFrameSize f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() f3 := getData(4 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 10 Expect(s.Push(f3, 2*mult, cb3)).To(Succeed()) // 2 - 6 checkQueue(map[protocol.ByteCount][]byte{ 2 * mult: f3[:mult], 3 * mult: f1, 6 * mult: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 2 * mult}, {Start: 10 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx====--- // ++++++ // => // --+xxx====--- It("case 28", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() f3 := getData(6) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 10 Expect(s.Push(f3, 2, cb3)).To(Succeed()) // 2 - 8 checkQueue(map[protocol.ByteCount][]byte{ 2: f3[:1], 3: f1, 6: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 2}, {Start: 10, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) It("case 28, for long frames", func() { const mult = protocol.MinStreamFrameSize f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() f3 := getData(6 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 10 Expect(s.Push(f3, 2*mult, cb3)).To(Succeed()) // 2 - 8 checkQueue(map[protocol.ByteCount][]byte{ 2 * mult: f3[:mult], 3 * mult: f1, 6 * mult: f2, }) checkGaps([]byteInterval{ {Start: 0, End: 2 * mult}, {Start: 10 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx===----- // +++++ // => // ---xxx+++++--- It("case 29", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(5) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9 Expect(s.Push(f3, 6, cb3)).To(Succeed()) // 6 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 11, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx===---- // ++++++ // => // ---xxx===++-- It("case 30", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(6) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6, cb2)).To(Succeed()) // 6 - 9 Expect(s.Push(f3, 5, cb3)).To(Succeed()) // 5 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f2, 9: f3[4:], }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 11, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) It("case 30, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 2)) f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(3 * mult) cb2, t2 := getCallback() f3 := getData(6 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 6*mult, cb2)).To(Succeed()) // 6 - 9 Expect(s.Push(f3, 5*mult, cb3)).To(Succeed()) // 5 - 11 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f1, 6 * mult: f2, 9 * mult: f3[4*mult:], }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 11 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx---===----- // ++++++++++ // => // ---xxx++++++++--- It("case 31", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(10) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 5, cb3)).To(Succeed()) // 5 - 15 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f3[1:], }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 15, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackCalled(t3) }) It("case 31, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 9)) f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(3 * mult) cb2, t2 := getCallback() f3 := getData(10 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9*mult, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 5*mult, cb3)).To(Succeed()) // 5 - 15 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f1, 6 * mult: f3[mult:], }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 15 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx---===----- // +++++++++ // => // ---+++++++++--- It("case 32", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(9) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 12 checkQueue(map[protocol.ByteCount][]byte{ 3: f3, }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 12, End: protocol.MaxByteCount}, }) checkCallbackCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) }) // ---xxx---===###----- // ++++++++++++ // => // ---xxx++++++++++--- It("case 33", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(3) cb2, t2 := getCallback() f3 := getData(3) cb3, t3 := getCallback() f4 := getData(12) cb4, t4 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 9, cb3)).To(Succeed()) // 12 - 15 Expect(s.Push(f4, 5, cb4)).To(Succeed()) // 5 - 17 checkQueue(map[protocol.ByteCount][]byte{ 3: f1, 6: f4[1:], }) checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 17, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackCalled(t3) checkCallbackCalled(t4) }) It("case 33, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 11)) f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(3 * mult) cb2, t2 := getCallback() f3 := getData(3 * mult) cb3, t3 := getCallback() f4 := getData(12 * mult) cb4, t4 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9*mult, cb2)).To(Succeed()) // 9 - 12 Expect(s.Push(f3, 9*mult, cb3)).To(Succeed()) // 12 - 15 Expect(s.Push(f4, 5*mult, cb4)).To(Succeed()) // 5 - 17 checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f1, 6 * mult: f4[mult:], }) checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 17 * mult, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackCalled(t3) checkCallbackNotCalled(t4) }) // ---xxx===---### // ++++++ // => // ---xxx++++++### It("case 34", func() { f1 := getData(5) cb1, t1 := getCallback() f2 := getData(5) cb2, t2 := getCallback() f3 := getData(10) cb3, t3 := getCallback() f4 := getData(5) cb4, t4 := getCallback() Expect(s.Push(f1, 5, cb1)).To(Succeed()) // 5 - 10 Expect(s.Push(f2, 10, cb2)).To(Succeed()) // 10 - 15 Expect(s.Push(f4, 20, cb3)).To(Succeed()) // 20 - 25 Expect(s.Push(f3, 10, cb4)).To(Succeed()) // 10 - 20 checkQueue(map[protocol.ByteCount][]byte{ 5: f1, 10: f3, 20: f4, }) checkGaps([]byteInterval{ {Start: 0, End: 5}, {Start: 25, End: protocol.MaxByteCount}, }) checkCallbackNotCalled(t1) checkCallbackCalled(t2) checkCallbackNotCalled(t3) checkCallbackNotCalled(t4) }) // ---xxx---####--- // ++++++++ // => // ---++++++####--- It("case 35", func() { f1 := getData(3) cb1, t1 := getCallback() f2 := getData(4) cb2, t2 := getCallback() f3 := getData(8) cb3, t3 := getCallback() Expect(s.Push(f1, 3, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9, cb2)).To(Succeed()) // 9 - 13 Expect(s.Push(f3, 3, cb3)).To(Succeed()) // 3 - 11 checkGaps([]byteInterval{ {Start: 0, End: 3}, {Start: 13, End: protocol.MaxByteCount}, }) checkQueue(map[protocol.ByteCount][]byte{ 3: f3[:6], 9: f2, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) checkCallbackCalled(t3) }) It("case 35, for long frames", func() { mult := protocol.ByteCount(math.Ceil(float64(protocol.MinStreamFrameSize) / 6)) f1 := getData(3 * mult) cb1, t1 := getCallback() f2 := getData(4 * mult) cb2, t2 := getCallback() f3 := getData(8 * mult) cb3, t3 := getCallback() Expect(s.Push(f1, 3*mult, cb1)).To(Succeed()) // 3 - 6 Expect(s.Push(f2, 9*mult, cb2)).To(Succeed()) // 9 - 13 Expect(s.Push(f3, 3*mult, cb3)).To(Succeed()) // 3 - 11 checkGaps([]byteInterval{ {Start: 0, End: 3 * mult}, {Start: 13 * mult, End: protocol.MaxByteCount}, }) checkQueue(map[protocol.ByteCount][]byte{ 3 * mult: f3[:6*mult], 9 * mult: f2, }) checkCallbackCalled(t1) checkCallbackNotCalled(t2) checkCallbackNotCalled(t3) }) Context("receiving data after reads", func() { It("ignores duplicate frames", func() { Expect(s.Push([]byte("foobar"), 0, nil)).To(Succeed()) offset, data, _ := s.Pop() Expect(offset).To(BeZero()) Expect(data).To(Equal([]byte("foobar"))) // now receive the duplicate Expect(s.Push([]byte("foobar"), 0, nil)).To(Succeed()) Expect(s.queue).To(BeEmpty()) checkGaps([]byteInterval{ {Start: 6, End: protocol.MaxByteCount}, }) }) It("ignores parts of frames that have already been read", func() { Expect(s.Push([]byte("foo"), 0, nil)).To(Succeed()) offset, data, _ := s.Pop() Expect(offset).To(BeZero()) Expect(data).To(Equal([]byte("foo"))) // now receive the duplicate Expect(s.Push([]byte("foobar"), 0, nil)).To(Succeed()) offset, data, _ = s.Pop() Expect(offset).To(Equal(protocol.ByteCount(3))) Expect(data).To(Equal([]byte("bar"))) Expect(s.queue).To(BeEmpty()) checkGaps([]byteInterval{ {Start: 6, End: protocol.MaxByteCount}, }) }) }) Context("DoS protection", func() { It("errors when too many gaps are created", func() { for i := 0; i < protocol.MaxStreamFrameSorterGaps; i++ { Expect(s.Push([]byte("foobar"), protocol.ByteCount(i*7), nil)).To(Succeed()) } Expect(s.gaps.Len()).To(Equal(protocol.MaxStreamFrameSorterGaps)) err := s.Push([]byte("foobar"), protocol.ByteCount(protocol.MaxStreamFrameSorterGaps*7)+100, nil) Expect(err).To(MatchError("too many gaps in received data")) }) }) }) Context("stress testing", func() { type frame struct { offset protocol.ByteCount data []byte } for _, lf := range []bool{true, false} { longFrames := lf const num = 1000 name := "short" if longFrames { name = "long" } Context(fmt.Sprintf("using %s frames", name), func() { var data []byte var dataLen protocol.ByteCount var callbacks []callbackTracker BeforeEach(func() { seed := time.Now().UnixNano() fmt.Fprintf(GinkgoWriter, "Seed: %d\n", seed) rand.Seed(uint64(seed)) callbacks = nil dataLen = 25 if longFrames { dataLen = 2 * protocol.MinStreamFrameSize } data = make([]byte, num*dataLen) for i := 0; i < num; i++ { for j := protocol.ByteCount(0); j < dataLen; j++ { data[protocol.ByteCount(i)*dataLen+j] = uint8(i) } } }) getRandomFrames := func() []frame { frames := make([]frame, num) for i := protocol.ByteCount(0); i < num; i++ { b := make([]byte, dataLen) Expect(copy(b, data[i*dataLen:])).To(BeEquivalentTo(dataLen)) frames[i] = frame{ offset: i * dataLen, data: b, } } rand.Shuffle(len(frames), func(i, j int) { frames[i], frames[j] = frames[j], frames[i] }) return frames } getData := func() []byte { var data []byte for { offset, b, cb := s.Pop() if b == nil { break } Expect(offset).To(BeEquivalentTo(len(data))) data = append(data, b...) if cb != nil { cb() } } return data } // push pushes data to the frame sorter // It creates a new callback and adds the push := func(data []byte, offset protocol.ByteCount) { cb, t := getCallback() ExpectWithOffset(1, s.Push(data, offset, cb)).To(Succeed()) callbacks = append(callbacks, t) } checkCallbacks := func() { ExpectWithOffset(1, callbacks).ToNot(BeEmpty()) for _, t := range callbacks { checkCallbackCalled(t) } } It("inserting frames in a random order", func() { frames := getRandomFrames() for _, f := range frames { push(f.data, f.offset) } checkGaps([]byteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}}) Expect(getData()).To(Equal(data)) Expect(s.queue).To(BeEmpty()) checkCallbacks() }) It("inserting frames in a random order, with some duplicates", func() { frames := getRandomFrames() for _, f := range frames { push(f.data, f.offset) if rand.Intn(10) < 5 { df := frames[rand.Intn(len(frames))] push(df.data, df.offset) } } checkGaps([]byteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}}) Expect(getData()).To(Equal(data)) Expect(s.queue).To(BeEmpty()) checkCallbacks() }) It("inserting frames in a random order, with randomly cut retransmissions", func() { frames := getRandomFrames() for _, f := range frames { push(f.data, f.offset) if rand.Intn(10) < 5 { length := protocol.ByteCount(1 + rand.Intn(int(4*dataLen))) if length >= num*dataLen { length = num*dataLen - 1 } b := make([]byte, length) offset := protocol.ByteCount(rand.Intn(int(num*dataLen - length))) Expect(copy(b, data[offset:offset+length])).To(BeEquivalentTo(length)) push(b, offset) } } checkGaps([]byteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}}) Expect(getData()).To(Equal(data)) Expect(s.queue).To(BeEmpty()) checkCallbacks() }) }) } }) }) golang-github-lucas-clemente-quic-go-0.46.0/framer.go000066400000000000000000000164201465664453100224150ustar00rootroot00000000000000package quic import ( "slices" "sync" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils/ringbuffer" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/quicvarint" ) const ( maxPathResponses = 256 maxControlFrames = 16 << 10 ) // This is the largest possible size of a stream-related control frame // (which is the RESET_STREAM frame). const maxStreamControlFrameSize = 25 type streamControlFrameGetter interface { getControlFrame() (_ ackhandler.Frame, ok, hasMore bool) } type framer struct { mutex sync.Mutex activeStreams map[protocol.StreamID]sendStreamI streamQueue ringbuffer.RingBuffer[protocol.StreamID] streamsWithControlFrames map[protocol.StreamID]streamControlFrameGetter controlFrameMutex sync.Mutex controlFrames []wire.Frame pathResponses []*wire.PathResponseFrame queuedTooManyControlFrames bool } func newFramer() *framer { return &framer{ activeStreams: make(map[protocol.StreamID]sendStreamI), streamsWithControlFrames: make(map[protocol.StreamID]streamControlFrameGetter), } } func (f *framer) HasData() bool { f.mutex.Lock() hasData := !f.streamQueue.Empty() f.mutex.Unlock() if hasData { return true } f.controlFrameMutex.Lock() defer f.controlFrameMutex.Unlock() return len(f.streamsWithControlFrames) > 0 || len(f.controlFrames) > 0 || len(f.pathResponses) > 0 } func (f *framer) QueueControlFrame(frame wire.Frame) { f.controlFrameMutex.Lock() defer f.controlFrameMutex.Unlock() if pr, ok := frame.(*wire.PathResponseFrame); ok { // Only queue up to maxPathResponses PATH_RESPONSE frames. // This limit should be high enough to never be hit in practice, // unless the peer is doing something malicious. if len(f.pathResponses) >= maxPathResponses { return } f.pathResponses = append(f.pathResponses, pr) return } // This is a hack. if len(f.controlFrames) >= maxControlFrames { f.queuedTooManyControlFrames = true return } f.controlFrames = append(f.controlFrames, frame) } func (f *framer) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) { f.controlFrameMutex.Lock() defer f.controlFrameMutex.Unlock() var length protocol.ByteCount // add a PATH_RESPONSE first, but only pack a single PATH_RESPONSE per packet if len(f.pathResponses) > 0 { frame := f.pathResponses[0] frameLen := frame.Length(v) if frameLen <= maxLen { frames = append(frames, ackhandler.Frame{Frame: frame}) length += frameLen f.pathResponses = f.pathResponses[1:] } } // add stream-related control frames for id, str := range f.streamsWithControlFrames { start: remainingLen := maxLen - length if remainingLen <= maxStreamControlFrameSize { break } fr, ok, hasMore := str.getControlFrame() if !hasMore { delete(f.streamsWithControlFrames, id) } if !ok { continue } frames = append(frames, fr) length += fr.Frame.Length(v) if hasMore { // It is rare that a stream has more than one control frame to queue. // We don't want to spawn another loop for just to cover that case. goto start } } for len(f.controlFrames) > 0 { frame := f.controlFrames[len(f.controlFrames)-1] frameLen := frame.Length(v) if length+frameLen > maxLen { break } frames = append(frames, ackhandler.Frame{Frame: frame}) length += frameLen f.controlFrames = f.controlFrames[:len(f.controlFrames)-1] } return frames, length } // QueuedTooManyControlFrames says if the control frame queue exceeded its maximum queue length. // This is a hack. // It is easier to implement than propagating an error return value in QueueControlFrame. // The correct solution would be to queue frames with their respective structs. // See https://github.com/quic-go/quic-go/issues/4271 for the queueing of stream-related control frames. func (f *framer) QueuedTooManyControlFrames() bool { return f.queuedTooManyControlFrames } func (f *framer) AddActiveStream(id protocol.StreamID, str sendStreamI) { f.mutex.Lock() if _, ok := f.activeStreams[id]; !ok { f.streamQueue.PushBack(id) f.activeStreams[id] = str } f.mutex.Unlock() } func (f *framer) AddStreamWithControlFrames(id protocol.StreamID, str streamControlFrameGetter) { f.controlFrameMutex.Lock() if _, ok := f.streamsWithControlFrames[id]; !ok { f.streamsWithControlFrames[id] = str } f.controlFrameMutex.Unlock() } // RemoveActiveStream is called when a stream completes. func (f *framer) RemoveActiveStream(id protocol.StreamID) { f.mutex.Lock() delete(f.activeStreams, id) // We don't delete the stream from the streamQueue, // since we'd have to iterate over the ringbuffer. // Instead, we check if the stream is still in activeStreams in AppendStreamFrames. f.mutex.Unlock() } func (f *framer) AppendStreamFrames(frames []ackhandler.StreamFrame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) { startLen := len(frames) var length protocol.ByteCount f.mutex.Lock() // pop STREAM frames, until less than 128 bytes are left in the packet numActiveStreams := f.streamQueue.Len() for i := 0; i < numActiveStreams; i++ { if protocol.MinStreamFrameSize+length > maxLen { break } id := f.streamQueue.PopFront() // This should never return an error. Better check it anyway. // The stream will only be in the streamQueue, if it enqueued itself there. str, ok := f.activeStreams[id] // The stream might have been removed after being enqueued. if !ok { continue } remainingLen := maxLen - length // For the last STREAM frame, we'll remove the DataLen field later. // Therefore, we can pretend to have more bytes available when popping // the STREAM frame (which will always have the DataLen set). remainingLen += protocol.ByteCount(quicvarint.Len(uint64(remainingLen))) frame, ok, hasMoreData := str.popStreamFrame(remainingLen, v) if hasMoreData { // put the stream back in the queue (at the end) f.streamQueue.PushBack(id) } else { // no more data to send. Stream is not active delete(f.activeStreams, id) } // The frame can be "nil" // * if the stream was canceled after it said it had data // * the remaining size doesn't allow us to add another STREAM frame if !ok { continue } frames = append(frames, frame) length += frame.Frame.Length(v) } f.mutex.Unlock() if len(frames) > startLen { l := frames[len(frames)-1].Frame.Length(v) // account for the smaller size of the last STREAM frame frames[len(frames)-1].Frame.DataLenPresent = false length += frames[len(frames)-1].Frame.Length(v) - l } return frames, length } func (f *framer) Handle0RTTRejection() { f.mutex.Lock() defer f.mutex.Unlock() f.controlFrameMutex.Lock() defer f.controlFrameMutex.Unlock() f.streamQueue.Clear() for id := range f.activeStreams { delete(f.activeStreams, id) } var j int for i, frame := range f.controlFrames { switch frame.(type) { case *wire.MaxDataFrame, *wire.MaxStreamDataFrame, *wire.MaxStreamsFrame, *wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame: continue default: f.controlFrames[j] = f.controlFrames[i] j++ } } f.controlFrames = slices.Delete(f.controlFrames, j, len(f.controlFrames)) } golang-github-lucas-clemente-quic-go-0.46.0/framer_test.go000066400000000000000000000477771465664453100234770ustar00rootroot00000000000000package quic import ( "bytes" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Framer", func() { const ( id1 = protocol.StreamID(10) id2 = protocol.StreamID(11) ) var ( framer *framer stream1, stream2 *MockSendStreamI version protocol.Version ) BeforeEach(func() { stream1 = NewMockSendStreamI(mockCtrl) stream1.EXPECT().StreamID().Return(protocol.StreamID(5)).AnyTimes() stream2 = NewMockSendStreamI(mockCtrl) stream2.EXPECT().StreamID().Return(protocol.StreamID(6)).AnyTimes() framer = newFramer() }) Context("handling control frames", func() { It("adds control frames", func() { pc := &wire.PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 6, 7, 8}} msf := &wire.MaxStreamsFrame{MaxStreamNum: 0x1337} framer.QueueControlFrame(pc) framer.QueueControlFrame(msf) frames, length := framer.AppendControlFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(2)) fs := []wire.Frame{frames[0].Frame, frames[1].Frame} Expect(fs).To(ContainElement(pc)) Expect(fs).To(ContainElement(msf)) Expect(length).To(Equal(pc.Length(version) + msf.Length(version))) }) It("says if it has data", func() { Expect(framer.HasData()).To(BeFalse()) f := &wire.MaxDataFrame{MaximumData: 0x42} framer.QueueControlFrame(f) Expect(framer.HasData()).To(BeTrue()) frames, _ := framer.AppendControlFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(framer.HasData()).To(BeFalse()) }) It("appends to the slice given", func() { ping := &wire.PingFrame{} pc := &wire.PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 6, 7, 8}} framer.QueueControlFrame(pc) frames, length := framer.AppendControlFrames([]ackhandler.Frame{{Frame: ping}}, 1000, protocol.Version1) Expect(frames).To(HaveLen(2)) Expect(frames[0].Frame).To(Equal(ping)) Expect(frames[1].Frame).To(Equal(pc)) Expect(length).To(Equal(pc.Length(version))) }) It("adds stream-related control frames", func() { ping := &wire.PingFrame{} framer.QueueControlFrame(ping) str := NewMockStreamControlFrameGetter(mockCtrl) framer.AddStreamWithControlFrames(10, str) mdf1 := &wire.MaxStreamDataFrame{MaximumStreamData: 1337} mdf2 := &wire.MaxStreamDataFrame{MaximumStreamData: 1338} str.EXPECT().getControlFrame().Return(ackhandler.Frame{Frame: mdf1}, true, true) str.EXPECT().getControlFrame().Return(ackhandler.Frame{Frame: mdf2}, true, false) frames, l := framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(HaveLen(3)) Expect(frames[0].Frame).To(Equal(mdf1)) Expect(frames[1].Frame).To(Equal(mdf2)) Expect(frames[2].Frame).To(Equal(ping)) Expect(l).To(Equal(ping.Length(protocol.Version1) + mdf1.Length(protocol.Version1) + mdf2.Length(protocol.Version1))) }) It("doesn't enqueue more stream-related control frames if there are less than 25 bytes left", func() { str := NewMockStreamControlFrameGetter(mockCtrl) framer.AddStreamWithControlFrames(10, str) mdf1 := &wire.MaxStreamDataFrame{MaximumStreamData: 1337} str.EXPECT().getControlFrame().Return(ackhandler.Frame{Frame: mdf1}, true, true).AnyTimes() frames, l := framer.AppendControlFrames(nil, 100, protocol.Version1) Expect(l).To(Equal(protocol.ByteCount(len(frames)) * mdf1.Length(protocol.Version1))) Expect(l).To(And( BeNumerically(">", 100-maxStreamControlFrameSize), BeNumerically("<=", 100), )) }) It("adds the right number of frames", func() { maxSize := protocol.ByteCount(1000) bf := &wire.DataBlockedFrame{MaximumData: 0x1337} bfLen := bf.Length(version) numFrames := int(maxSize / bfLen) // max number of frames that fit into maxSize for i := 0; i < numFrames+1; i++ { framer.QueueControlFrame(bf) } frames, length := framer.AppendControlFrames(nil, maxSize, protocol.Version1) Expect(frames).To(HaveLen(numFrames)) Expect(length).To(BeNumerically(">", maxSize-bfLen)) frames, length = framer.AppendControlFrames(nil, maxSize, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(length).To(Equal(bfLen)) }) It("drops *_BLOCKED frames when 0-RTT is rejected", func() { ping := &wire.PingFrame{} ncid := &wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), } frames := []wire.Frame{ &wire.DataBlockedFrame{MaximumData: 1337}, &wire.StreamDataBlockedFrame{StreamID: 42, MaximumStreamData: 1337}, &wire.StreamsBlockedFrame{StreamLimit: 13}, ping, ncid, } rand.Shuffle(len(frames), func(i, j int) { frames[i], frames[j] = frames[j], frames[i] }) for _, f := range frames { framer.QueueControlFrame(f) } framer.Handle0RTTRejection() fs, length := framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(fs).To(HaveLen(2)) Expect(length).To(Equal(ping.Length(version) + ncid.Length(version))) }) It("detects when too many frames are queued", func() { for i := 0; i < maxControlFrames-1; i++ { framer.QueueControlFrame(&wire.PingFrame{}) framer.QueueControlFrame(&wire.PingFrame{}) Expect(framer.QueuedTooManyControlFrames()).To(BeFalse()) frames, _ := framer.AppendControlFrames([]ackhandler.Frame{}, 1, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(framer.controlFrames).To(HaveLen(i + 1)) } framer.QueueControlFrame(&wire.PingFrame{}) Expect(framer.QueuedTooManyControlFrames()).To(BeFalse()) Expect(framer.controlFrames).To(HaveLen(maxControlFrames)) framer.QueueControlFrame(&wire.PingFrame{}) Expect(framer.QueuedTooManyControlFrames()).To(BeTrue()) Expect(framer.controlFrames).To(HaveLen(maxControlFrames)) }) }) Context("handling PATH_RESPONSE frames", func() { It("packs a single PATH_RESPONSE per packet", func() { f1 := &wire.PathResponseFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} f2 := &wire.PathResponseFrame{Data: [8]byte{2, 3, 4, 5, 6, 7, 8, 9}} cf1 := &wire.DataBlockedFrame{MaximumData: 1337} cf2 := &wire.HandshakeDoneFrame{} framer.QueueControlFrame(f1) framer.QueueControlFrame(f2) framer.QueueControlFrame(cf1) framer.QueueControlFrame(cf2) // the first packet should contain a single PATH_RESPONSE frame, but all the other control frames Expect(framer.HasData()).To(BeTrue()) frames, length := framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(HaveLen(3)) Expect(frames[0].Frame).To(Equal(f1)) Expect([]wire.Frame{frames[1].Frame, frames[2].Frame}).To(ContainElement(cf1)) Expect([]wire.Frame{frames[1].Frame, frames[2].Frame}).To(ContainElement(cf2)) Expect(length).To(Equal(f1.Length(protocol.Version1) + cf1.Length(protocol.Version1) + cf2.Length(protocol.Version1))) // the second packet should contain the other PATH_RESPONSE frame Expect(framer.HasData()).To(BeTrue()) frames, length = framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f2)) Expect(length).To(Equal(f2.Length(protocol.Version1))) Expect(framer.HasData()).To(BeFalse()) }) It("limits the number of queued PATH_RESPONSE frames", func() { var pathResponses []*wire.PathResponseFrame for i := 0; i < 2*maxPathResponses; i++ { var f wire.PathResponseFrame rand.Read(f.Data[:]) pathResponses = append(pathResponses, &f) framer.QueueControlFrame(&f) } for i := 0; i < maxPathResponses; i++ { Expect(framer.HasData()).To(BeTrue()) frames, length := framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(pathResponses[i])) Expect(length).To(Equal(pathResponses[i].Length(protocol.Version1))) } Expect(framer.HasData()).To(BeFalse()) frames, length := framer.AppendControlFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(BeEmpty()) Expect(length).To(BeZero()) }) }) Context("popping STREAM frames", func() { It("returns nil when popping an empty framer", func() { Expect(framer.AppendStreamFrames(nil, 1000, protocol.Version1)).To(BeEmpty()) }) It("returns STREAM frames", func() { f := &wire.StreamFrame{ StreamID: id1, Data: []byte("foobar"), Offset: 42, DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) framer.AddActiveStream(id1, stream1) fs, length := framer.AppendStreamFrames(nil, 1000, protocol.Version1) Expect(fs).To(HaveLen(1)) Expect(fs[0].Frame.DataLenPresent).To(BeFalse()) Expect(length).To(Equal(f.Length(version))) }) It("says if it has data", func() { Expect(framer.HasData()).To(BeFalse()) framer.AddActiveStream(id1, stream1) Expect(framer.HasData()).To(BeTrue()) f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foo")} f2 := &wire.StreamFrame{StreamID: id1, Data: []byte("bar")} stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, true) stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false) frames, _ := framer.AppendStreamFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f1)) Expect(framer.HasData()).To(BeTrue()) frames, _ = framer.AppendStreamFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f2)) Expect(framer.HasData()).To(BeFalse()) framer.AddStreamWithControlFrames(id1, nil) Expect(framer.HasData()).To(BeTrue()) }) It("appends to a frame slice", func() { f := &wire.StreamFrame{ StreamID: id1, Data: []byte("foobar"), DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) framer.AddActiveStream(id1, stream1) f0 := ackhandler.StreamFrame{Frame: &wire.StreamFrame{StreamID: 9999}} frames := []ackhandler.StreamFrame{f0} fs, length := framer.AppendStreamFrames(frames, 1000, protocol.Version1) Expect(fs).To(HaveLen(2)) Expect(fs[0]).To(Equal(f0)) Expect(fs[1].Frame.Data).To(Equal([]byte("foobar"))) Expect(fs[1].Frame.DataLenPresent).To(BeFalse()) Expect(length).To(Equal(f.Length(version))) }) It("skips a stream that was reported active, but was completed shortly after", func() { f := &wire.StreamFrame{ StreamID: id2, Data: []byte("foobar"), DataLenPresent: true, } stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) framer.AddActiveStream(id1, stream1) framer.AddActiveStream(id2, stream2) framer.RemoveActiveStream(id1) frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f)) }) It("skips a stream that was reported active, but doesn't have any data", func() { f := &wire.StreamFrame{ StreamID: id2, Data: []byte("foobar"), DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{}, false, false) stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) framer.AddActiveStream(id1, stream1) framer.AddActiveStream(id2, stream2) frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f)) }) It("pops from a stream multiple times, if it has enough data", func() { f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")} f2 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")} stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, true) stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false) framer.AddActiveStream(id1, stream1) // only add it once frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f1)) frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f2)) // no further calls to popStreamFrame, after popStreamFrame said there's no more data frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) Expect(frames).To(BeNil()) }) It("re-queues a stream at the end, if it has enough data", func() { f11 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")} f12 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")} f2 := &wire.StreamFrame{StreamID: id2, Data: []byte("raboof")} stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f11}, true, true) stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f12}, true, false) stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false) framer.AddActiveStream(id1, stream1) // only add it once framer.AddActiveStream(id2, stream2) // first a frame from stream 1 frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f11)) // then a frame from stream 2 frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f2)) // then another frame from stream 1 frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f12)) }) It("only dequeues data from each stream once per packet", func() { f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")} f2 := &wire.StreamFrame{StreamID: id2, Data: []byte("raboof")} // both streams have more data, and will be re-queued stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, true) stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, true) framer.AddActiveStream(id1, stream1) framer.AddActiveStream(id2, stream2) frames, length := framer.AppendStreamFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(2)) Expect(frames[0].Frame).To(Equal(f1)) Expect(frames[1].Frame).To(Equal(f2)) Expect(length).To(Equal(f1.Length(version) + f2.Length(version))) }) It("returns multiple normal frames in the order they were reported active", func() { f1 := &wire.StreamFrame{Data: []byte("foobar")} f2 := &wire.StreamFrame{Data: []byte("foobaz")} stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f1}, true, false) stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f2}, true, false) framer.AddActiveStream(id2, stream2) framer.AddActiveStream(id1, stream1) frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(2)) Expect(frames[0].Frame).To(Equal(f2)) Expect(frames[1].Frame).To(Equal(f1)) }) It("only asks a stream for data once, even if it was reported active multiple times", func() { f := &wire.StreamFrame{Data: []byte("foobar")} stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) // only one call to this function framer.AddActiveStream(id1, stream1) framer.AddActiveStream(id1, stream1) frames, _ := framer.AppendStreamFrames(nil, 1000, protocol.Version1) Expect(frames).To(HaveLen(1)) }) It("does not pop empty frames", func() { fs, length := framer.AppendStreamFrames(nil, 500, protocol.Version1) Expect(fs).To(BeEmpty()) Expect(length).To(BeZero()) }) It("pops maximum size STREAM frames", func() { for i := protocol.MinStreamFrameSize; i < 2000; i++ { stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).DoAndReturn(func(size protocol.ByteCount, v protocol.Version) (ackhandler.StreamFrame, bool, bool) { f := &wire.StreamFrame{ StreamID: id1, DataLenPresent: true, } f.Data = make([]byte, f.MaxDataLen(size, v)) Expect(f.Length(version)).To(Equal(size)) return ackhandler.StreamFrame{Frame: f}, true, false }) framer.AddActiveStream(id1, stream1) frames, _ := framer.AppendStreamFrames(nil, i, protocol.Version1) Expect(frames).To(HaveLen(1)) f := frames[0].Frame Expect(f.DataLenPresent).To(BeFalse()) Expect(f.Length(version)).To(Equal(i)) } }) It("pops multiple STREAM frames", func() { for i := 2 * protocol.MinStreamFrameSize; i < 2000; i++ { stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).DoAndReturn(func(size protocol.ByteCount, v protocol.Version) (ackhandler.StreamFrame, bool, bool) { f := &wire.StreamFrame{ StreamID: id2, DataLenPresent: true, } f.Data = make([]byte, f.MaxDataLen(protocol.MinStreamFrameSize, v)) return ackhandler.StreamFrame{Frame: f}, true, false }) stream2.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).DoAndReturn(func(size protocol.ByteCount, v protocol.Version) (ackhandler.StreamFrame, bool, bool) { f := &wire.StreamFrame{ StreamID: id2, DataLenPresent: true, } f.Data = make([]byte, f.MaxDataLen(size, v)) Expect(f.Length(version)).To(Equal(size)) return ackhandler.StreamFrame{Frame: f}, true, false }) framer.AddActiveStream(id1, stream1) framer.AddActiveStream(id2, stream2) frames, _ := framer.AppendStreamFrames(nil, i, protocol.Version1) Expect(frames).To(HaveLen(2)) f1 := frames[0].Frame f2 := frames[1].Frame Expect(f1.DataLenPresent).To(BeTrue()) Expect(f2.DataLenPresent).To(BeFalse()) Expect(f1.Length(version) + f2.Length(version)).To(Equal(i)) } }) It("pops frames that when asked for the the minimum STREAM frame size", func() { f := &wire.StreamFrame{Data: []byte("foobar")} stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) framer.AddActiveStream(id1, stream1) framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize, protocol.Version1) }) It("does not pop frames smaller than the minimum size", func() { // don't expect a call to PopStreamFrame() framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize-1, protocol.Version1) }) It("stops iterating when the remaining size is smaller than the minimum STREAM frame size", func() { // pop a frame such that the remaining size is one byte less than the minimum STREAM frame size f := &wire.StreamFrame{ StreamID: id1, Data: bytes.Repeat([]byte("f"), int(500-protocol.MinStreamFrameSize)), DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any(), protocol.Version1).Return(ackhandler.StreamFrame{Frame: f}, true, false) framer.AddActiveStream(id1, stream1) fs, length := framer.AppendStreamFrames(nil, 500, protocol.Version1) Expect(fs).To(HaveLen(1)) Expect(fs[0].Frame).To(Equal(f)) Expect(length).To(Equal(f.Length(version))) }) It("drops all STREAM frames when 0-RTT is rejected", func() { framer.AddActiveStream(id1, stream1) framer.Handle0RTTRejection() fs, length := framer.AppendStreamFrames(nil, protocol.MaxByteCount, protocol.Version1) Expect(fs).To(BeEmpty()) Expect(length).To(BeZero()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/000077500000000000000000000000001465664453100223035ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/frames/000077500000000000000000000000001465664453100235605ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/frames/cmd/000077500000000000000000000000001465664453100243235ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/frames/cmd/corpus.go000066400000000000000000000174511465664453100261750ustar00rootroot00000000000000package main import ( "log" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/fuzzing/internal/helper" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) const version = protocol.Version1 func getRandomData(l int) []byte { b := make([]byte, l) rand.Read(b) return b } func getRandomNumber() uint64 { switch 1 << uint8(rand.Intn(3)) { case 1: return uint64(rand.Int63n(64)) case 2: return uint64(rand.Int63n(16384)) case 4: return uint64(rand.Int63n(1073741824)) case 8: return uint64(rand.Int63n(4611686018427387904)) default: panic("unexpected length") } } func getRandomNumberLowerOrEqual(target uint64) uint64 { if target == 0 { return 0 } return uint64(rand.Int63n(int64(target))) } // returns a *maximum* number of num ACK ranges func getAckRanges(num int) []wire.AckRange { var ranges []wire.AckRange prevSmallest := uint64(rand.Int63n(4611686018427387904)) for i := 0; i < num; i++ { if prevSmallest <= 2 { break } largest := getRandomNumberLowerOrEqual(prevSmallest - 2) smallest := getRandomNumberLowerOrEqual(largest) ranges = append(ranges, wire.AckRange{ Smallest: protocol.PacketNumber(smallest), Largest: protocol.PacketNumber(largest), }) prevSmallest = smallest } return ranges } func getFrames() []wire.Frame { frames := []wire.Frame{ &wire.StreamFrame{ // STREAM frame at 0 offset, with FIN bit StreamID: protocol.StreamID(getRandomNumber()), Fin: true, }, &wire.StreamFrame{ // STREAM frame at 0 offset, with data and FIN bit StreamID: protocol.StreamID(getRandomNumber()), Fin: true, Data: getRandomData(100), }, &wire.StreamFrame{ // STREAM frame at non-zero offset, with data StreamID: protocol.StreamID(getRandomNumber()), Offset: protocol.ByteCount(getRandomNumber()), Data: getRandomData(50), }, &wire.StreamFrame{ // STREAM frame at non-zero offset, with data and FIN bit StreamID: protocol.StreamID(getRandomNumber()), Offset: protocol.ByteCount(getRandomNumber()), Data: getRandomData(50), Fin: true, }, &wire.StreamFrame{ // STREAM frame at non-zero offset, with data and FIN bit. Long enough to use the buffer. StreamID: protocol.StreamID(getRandomNumber()), Offset: protocol.ByteCount(getRandomNumber()), Data: getRandomData(2 * protocol.MinStreamFrameBufferSize), Fin: true, }, &wire.StreamFrame{ // STREAM frame at maximum offset, with FIN bit StreamID: protocol.StreamID(getRandomNumber()), Offset: protocol.MaxByteCount - 5, Data: getRandomData(5), Fin: true, }, &wire.StreamFrame{ // STREAM frame with data at maximum offset StreamID: protocol.StreamID(getRandomNumber()), Offset: protocol.MaxByteCount, Data: getRandomData(10), }, &wire.AckFrame{ AckRanges: getAckRanges(1), DelayTime: time.Duration(getRandomNumber()), }, &wire.AckFrame{ AckRanges: getAckRanges(5), DelayTime: time.Duration(getRandomNumber()), }, &wire.AckFrame{ AckRanges: getAckRanges(300), DelayTime: time.Duration(getRandomNumber()), }, &wire.AckFrame{ AckRanges: getAckRanges(3), DelayTime: time.Duration(getRandomNumber()), ECT0: getRandomNumber(), ECT1: getRandomNumber(), ECNCE: getRandomNumber(), }, &wire.PingFrame{}, &wire.ResetStreamFrame{ StreamID: protocol.StreamID(getRandomNumber()), ErrorCode: quic.StreamErrorCode(getRandomNumber()), FinalSize: protocol.ByteCount(getRandomNumber()), }, &wire.ResetStreamFrame{ // at maximum offset StreamID: protocol.StreamID(getRandomNumber()), ErrorCode: quic.StreamErrorCode(getRandomNumber()), FinalSize: protocol.MaxByteCount, }, &wire.StopSendingFrame{ StreamID: protocol.StreamID(getRandomNumber()), ErrorCode: quic.StreamErrorCode(getRandomNumber()), }, &wire.CryptoFrame{ Data: getRandomData(100), }, &wire.CryptoFrame{ Offset: protocol.ByteCount(getRandomNumber()), Data: getRandomData(50), }, &wire.NewTokenFrame{ Token: getRandomData(10), }, &wire.MaxDataFrame{ MaximumData: protocol.ByteCount(getRandomNumber()), }, &wire.MaxDataFrame{ MaximumData: protocol.MaxByteCount, }, &wire.MaxStreamDataFrame{ StreamID: protocol.StreamID(getRandomNumber()), MaximumStreamData: protocol.ByteCount(getRandomNumber()), }, &wire.MaxStreamDataFrame{ StreamID: protocol.StreamID(getRandomNumber()), MaximumStreamData: protocol.MaxByteCount, }, &wire.MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: protocol.StreamNum(getRandomNumber()), }, &wire.MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: protocol.StreamNum(getRandomNumber()), }, &wire.DataBlockedFrame{ MaximumData: protocol.ByteCount(getRandomNumber()), }, &wire.DataBlockedFrame{ MaximumData: protocol.MaxByteCount, }, &wire.StreamDataBlockedFrame{ StreamID: protocol.StreamID(getRandomNumber()), MaximumStreamData: protocol.ByteCount(getRandomNumber()), }, &wire.StreamDataBlockedFrame{ StreamID: protocol.StreamID(getRandomNumber()), MaximumStreamData: protocol.MaxByteCount, }, &wire.StreamsBlockedFrame{ Type: protocol.StreamTypeUni, StreamLimit: protocol.StreamNum(getRandomNumber()), }, &wire.StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: protocol.StreamNum(getRandomNumber()), }, &wire.RetireConnectionIDFrame{ SequenceNumber: getRandomNumber(), }, &wire.ConnectionCloseFrame{ // QUIC error with empty reason IsApplicationError: false, ErrorCode: getRandomNumber(), ReasonPhrase: "", }, &wire.ConnectionCloseFrame{ // QUIC error with reason IsApplicationError: false, // TODO: add frame type ErrorCode: getRandomNumber(), ReasonPhrase: string(getRandomData(100)), }, &wire.ConnectionCloseFrame{ // application error with empty reason IsApplicationError: true, ErrorCode: getRandomNumber(), ReasonPhrase: "", }, &wire.ConnectionCloseFrame{ // application error with reason IsApplicationError: true, ErrorCode: getRandomNumber(), ReasonPhrase: string(getRandomData(100)), }, } seq1 := getRandomNumber() seq2 := getRandomNumber() var token1, token2 protocol.StatelessResetToken copy(token1[:], getRandomData(16)) copy(token2[:], getRandomData(16)) frames = append(frames, []wire.Frame{ &wire.NewConnectionIDFrame{ SequenceNumber: seq1, RetirePriorTo: seq1 / 2, ConnectionID: protocol.ParseConnectionID(getRandomData(4)), StatelessResetToken: token1, }, &wire.NewConnectionIDFrame{ SequenceNumber: seq2, RetirePriorTo: seq2, ConnectionID: protocol.ParseConnectionID(getRandomData(17)), StatelessResetToken: token2, }, }...) var data1 [8]byte copy(data1[:], getRandomData(8)) frames = append(frames, &wire.PathChallengeFrame{ Data: data1, }) var data2 [8]byte copy(data2[:], getRandomData(8)) frames = append(frames, &wire.PathResponseFrame{ Data: data2, }) return frames } func main() { for _, f := range getFrames() { b, err := f.Append(nil, version) if err != nil { log.Fatal(err) } if err := helper.WriteCorpusFileWithPrefix("corpus", b, 1); err != nil { log.Fatal(err) } } for i := 0; i < 30; i++ { frames := getFrames() var b []byte for j := 0; j < rand.Intn(30)+2; j++ { if rand.Intn(10) == 0 { // write a PADDING frame b = append(b, 0) } f := frames[rand.Intn(len(frames))] var err error b, err = f.Append(b, version) if err != nil { log.Fatal(err) } if rand.Intn(10) == 0 { // write a PADDING frame b = append(b, 0) } } if err := helper.WriteCorpusFileWithPrefix("corpus", b, 1); err != nil { log.Fatal(err) } } } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/frames/fuzz.go000066400000000000000000000066461465664453100251210ustar00rootroot00000000000000package frames import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) const version = protocol.Version1 // PrefixLen is the number of bytes used for configuration const PrefixLen = 1 func toEncLevel(v uint8) protocol.EncryptionLevel { switch v % 3 { default: return protocol.EncryptionInitial case 1: return protocol.EncryptionHandshake case 2: return protocol.Encryption1RTT } } // Fuzz fuzzes the QUIC frames. // //go:generate go run ./cmd/corpus.go func Fuzz(data []byte) int { if len(data) < PrefixLen { return 0 } encLevel := toEncLevel(data[0]) data = data[PrefixLen:] parser := wire.NewFrameParser(true) parser.SetAckDelayExponent(protocol.DefaultAckDelayExponent) var numFrames int var b []byte for len(data) > 0 { initialLen := len(data) l, f, err := parser.ParseNext(data, encLevel, version) if err != nil { break } data = data[l:] numFrames++ if f == nil { // PADDING frame continue } // We accept empty STREAM frames, but we don't write them. if sf, ok := f.(*wire.StreamFrame); ok { if sf.DataLen() == 0 { sf.PutBack() continue } } validateFrame(f) startLen := len(b) parsedLen := initialLen - len(data) b, err = f.Append(b, version) if err != nil { panic(fmt.Sprintf("error writing frame %#v: %s", f, err)) } frameLen := protocol.ByteCount(len(b) - startLen) if f.Length(version) != frameLen { panic(fmt.Sprintf("inconsistent frame length for %#v: expected %d, got %d", f, frameLen, f.Length(version))) } if sf, ok := f.(*wire.StreamFrame); ok { sf.PutBack() } if frameLen > protocol.ByteCount(parsedLen) { panic(fmt.Sprintf("serialized length (%d) is longer than parsed length (%d)", len(b), parsedLen)) } } if numFrames == 0 { return 0 } return 1 } func validateFrame(frame wire.Frame) { switch f := frame.(type) { case *wire.StreamFrame: if protocol.ByteCount(len(f.Data)) != f.DataLen() { panic("STREAM frame: inconsistent data length") } case *wire.AckFrame: if f.DelayTime < 0 { panic(fmt.Sprintf("invalid ACK delay_time: %s", f.DelayTime)) } if f.LargestAcked() < f.LowestAcked() { panic("ACK: largest acknowledged is smaller than lowest acknowledged") } for _, r := range f.AckRanges { if r.Largest < 0 || r.Smallest < 0 { panic("ACK range contains a negative packet number") } } if !f.AcksPacket(f.LargestAcked()) { panic("ACK frame claims that largest acknowledged is not acknowledged") } if !f.AcksPacket(f.LowestAcked()) { panic("ACK frame claims that lowest acknowledged is not acknowledged") } _ = f.AcksPacket(100) _ = f.AcksPacket((f.LargestAcked() + f.LowestAcked()) / 2) case *wire.NewConnectionIDFrame: if f.ConnectionID.Len() < 1 || f.ConnectionID.Len() > 20 { panic(fmt.Sprintf("invalid NEW_CONNECTION_ID frame length: %s", f.ConnectionID)) } case *wire.NewTokenFrame: if len(f.Token) == 0 { panic("NEW_TOKEN frame with an empty token") } case *wire.MaxStreamsFrame: if f.MaxStreamNum > protocol.MaxStreamCount { panic("MAX_STREAMS frame with an invalid Maximum Streams value") } case *wire.StreamsBlockedFrame: if f.StreamLimit > protocol.MaxStreamCount { panic("STREAMS_BLOCKED frame with an invalid Maximum Streams value") } case *wire.ConnectionCloseFrame: if f.IsApplicationError && f.FrameType != 0 { panic("CONNECTION_CLOSE for an application error containing a frame type") } } } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/handshake/000077500000000000000000000000001465664453100242315ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/handshake/cmd/000077500000000000000000000000001465664453100247745ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/handshake/cmd/corpus.go000066400000000000000000000064141465664453100266430ustar00rootroot00000000000000package main import ( "context" "crypto/tls" "log" "net" fuzzhandshake "github.com/quic-go/quic-go/fuzzing/handshake" "github.com/quic-go/quic-go/fuzzing/internal/helper" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/testdata" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) const alpn = "fuzz" func main() { client := handshake.NewCryptoSetupClient( protocol.ConnectionID{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &tls.Config{ MinVersion: tls.VersionTLS13, ServerName: "localhost", NextProtos: []string{alpn}, RootCAs: testdata.GetRootCA(), ClientSessionCache: tls.NewLRUClientSessionCache(1), }, false, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("client"), protocol.Version1, ) config := testdata.GetTLSConfig() config.NextProtos = []string{alpn} server := handshake.NewCryptoSetupServer( protocol.ConnectionID{}, &net.UDPAddr{IP: net.IPv6loopback, Port: 1234}, &net.UDPAddr{IP: net.IPv6loopback, Port: 4321}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, config, false, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("server"), protocol.Version1, ) if err := client.StartHandshake(context.Background()); err != nil { log.Fatal(err) } if err := server.StartHandshake(context.Background()); err != nil { log.Fatal(err) } var clientHandshakeComplete, serverHandshakeComplete bool var messages [][]byte for { clientLoop: for { ev := client.NextEvent() //nolint:exhaustive // only need to process a few events switch ev.Kind { case handshake.EventNoEvent: break clientLoop case handshake.EventWriteInitialData: messages = append(messages, ev.Data) if err := server.HandleMessage(ev.Data, protocol.EncryptionInitial); err != nil { log.Fatal(err) } case handshake.EventWriteHandshakeData: messages = append(messages, ev.Data) if err := server.HandleMessage(ev.Data, protocol.EncryptionHandshake); err != nil { log.Fatal(err) } case handshake.EventHandshakeComplete: clientHandshakeComplete = true } } serverLoop: for { ev := server.NextEvent() //nolint:exhaustive // only need to process a few events switch ev.Kind { case handshake.EventNoEvent: break serverLoop case handshake.EventWriteInitialData: messages = append(messages, ev.Data) if err := client.HandleMessage(ev.Data, protocol.EncryptionInitial); err != nil { log.Fatal(err) } case handshake.EventWriteHandshakeData: messages = append(messages, ev.Data) if err := client.HandleMessage(ev.Data, protocol.EncryptionHandshake); err != nil { log.Fatal(err) } case handshake.EventHandshakeComplete: serverHandshakeComplete = true } } if serverHandshakeComplete && clientHandshakeComplete { break } } ticket, err := server.GetSessionTicket() if err != nil { log.Fatal(err) } if ticket == nil { log.Fatal("expected a session ticket") } messages = append(messages, ticket) for _, m := range messages { if err := helper.WriteCorpusFileWithPrefix("corpus", m, fuzzhandshake.PrefixLen); err != nil { log.Fatal(err) } } } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/handshake/fuzz.go000066400000000000000000000270221465664453100255610ustar00rootroot00000000000000package handshake import ( "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "errors" "fmt" "io" "log" "math" mrand "math/rand" "net" "time" "github.com/quic-go/quic-go/fuzzing/internal/helper" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) var ( cert, clientCert *tls.Certificate certPool, clientCertPool *x509.CertPool sessionTicketKey = [32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32} ) func init() { priv, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { log.Fatal(err) } cert, certPool, err = helper.GenerateCertificate(priv) if err != nil { log.Fatal(err) } privClient, err := rsa.GenerateKey(rand.Reader, 1024) if err != nil { log.Fatal(err) } clientCert, clientCertPool, err = helper.GenerateCertificate(privClient) if err != nil { log.Fatal(err) } } type messageType uint8 // TLS handshake message types. const ( typeClientHello messageType = 1 typeServerHello messageType = 2 typeNewSessionTicket messageType = 4 typeEncryptedExtensions messageType = 8 typeCertificate messageType = 11 typeCertificateRequest messageType = 13 typeCertificateVerify messageType = 15 typeFinished messageType = 20 ) func (m messageType) String() string { switch m { case typeClientHello: return "ClientHello" case typeServerHello: return "ServerHello" case typeNewSessionTicket: return "NewSessionTicket" case typeEncryptedExtensions: return "EncryptedExtensions" case typeCertificate: return "Certificate" case typeCertificateRequest: return "CertificateRequest" case typeCertificateVerify: return "CertificateVerify" case typeFinished: return "Finished" default: return fmt.Sprintf("unknown message type: %d", m) } } // consumes 3 bits func getClientAuth(rand uint8) tls.ClientAuthType { switch rand { default: return tls.NoClientCert case 0: return tls.RequestClientCert case 1: return tls.RequireAnyClientCert case 2: return tls.VerifyClientCertIfGiven case 3: return tls.RequireAndVerifyClientCert } } const ( alpn = "fuzzing" alpnWrong = "wrong" ) func toEncryptionLevel(n uint8) protocol.EncryptionLevel { switch n % 3 { default: return protocol.EncryptionInitial case 1: return protocol.EncryptionHandshake case 2: return protocol.Encryption1RTT } } func getTransportParameters(seed uint8) *wire.TransportParameters { const maxVarInt = math.MaxUint64 / 4 r := mrand.New(mrand.NewSource(int64(seed))) return &wire.TransportParameters{ ActiveConnectionIDLimit: 2, InitialMaxData: protocol.ByteCount(r.Int63n(maxVarInt)), InitialMaxStreamDataBidiLocal: protocol.ByteCount(r.Int63n(maxVarInt)), InitialMaxStreamDataBidiRemote: protocol.ByteCount(r.Int63n(maxVarInt)), InitialMaxStreamDataUni: protocol.ByteCount(r.Int63n(maxVarInt)), } } // PrefixLen is the number of bytes used for configuration const ( PrefixLen = 12 confLen = 5 ) // Fuzz fuzzes the TLS 1.3 handshake used by QUIC. // //go:generate go run ./cmd/corpus.go func Fuzz(data []byte) int { if len(data) < PrefixLen { return -1 } dataLen := len(data) var runConfig1, runConfig2 [confLen]byte copy(runConfig1[:], data) data = data[confLen:] messageConfig1 := data[0] data = data[1:] copy(runConfig2[:], data) data = data[confLen:] messageConfig2 := data[0] data = data[1:] if dataLen != len(data)+PrefixLen { panic("incorrect configuration") } clientConf := &tls.Config{ MinVersion: tls.VersionTLS13, ServerName: "localhost", NextProtos: []string{alpn}, RootCAs: certPool, } useSessionTicketCache := helper.NthBit(runConfig1[0], 2) if useSessionTicketCache { clientConf.ClientSessionCache = tls.NewLRUClientSessionCache(5) } if val := runHandshake(runConfig1, messageConfig1, clientConf, data); val != 1 { return val } return runHandshake(runConfig2, messageConfig2, clientConf, data) } func runHandshake(runConfig [confLen]byte, messageConfig uint8, clientConf *tls.Config, data []byte) int { serverConf := &tls.Config{ MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{*cert}, NextProtos: []string{alpn}, SessionTicketKey: sessionTicketKey, } // This sets the cipher suite for both client and server. // The way crypto/tls is designed doesn't allow us to set different cipher suites for client and server. resetCipherSuite := func() {} switch (runConfig[0] >> 6) % 4 { case 0: resetCipherSuite = qtls.SetCipherSuite(tls.TLS_AES_128_GCM_SHA256) case 1: resetCipherSuite = qtls.SetCipherSuite(tls.TLS_AES_256_GCM_SHA384) case 3: resetCipherSuite = qtls.SetCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256) default: } defer resetCipherSuite() enable0RTTClient := helper.NthBit(runConfig[0], 0) enable0RTTServer := helper.NthBit(runConfig[0], 1) sendPostHandshakeMessageToClient := helper.NthBit(runConfig[0], 3) sendPostHandshakeMessageToServer := helper.NthBit(runConfig[0], 4) sendSessionTicket := helper.NthBit(runConfig[0], 5) serverConf.ClientAuth = getClientAuth(runConfig[1] & 0b00000111) serverConf.SessionTicketsDisabled = helper.NthBit(runConfig[1], 3) if helper.NthBit(runConfig[2], 0) { clientConf.RootCAs = x509.NewCertPool() } if helper.NthBit(runConfig[2], 1) { serverConf.ClientCAs = clientCertPool } else { serverConf.ClientCAs = x509.NewCertPool() } if helper.NthBit(runConfig[2], 2) { serverConf.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) { if helper.NthBit(runConfig[2], 3) { return nil, errors.New("getting client config failed") } if helper.NthBit(runConfig[2], 4) { return nil, nil } return serverConf, nil } } if helper.NthBit(runConfig[2], 5) { serverConf.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) { if helper.NthBit(runConfig[2], 6) { return nil, errors.New("getting certificate failed") } if helper.NthBit(runConfig[2], 7) { return nil, nil } return clientCert, nil // this certificate will be invalid } } if helper.NthBit(runConfig[3], 0) { serverConf.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { if helper.NthBit(runConfig[3], 1) { return errors.New("certificate verification failed") } return nil } } if helper.NthBit(runConfig[3], 2) { clientConf.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { if helper.NthBit(runConfig[3], 3) { return errors.New("certificate verification failed") } return nil } } if helper.NthBit(runConfig[3], 4) { serverConf.NextProtos = []string{alpnWrong} } if helper.NthBit(runConfig[3], 5) { serverConf.NextProtos = []string{alpnWrong, alpn} } if helper.NthBit(runConfig[3], 6) { serverConf.KeyLogWriter = io.Discard } if helper.NthBit(runConfig[3], 7) { clientConf.KeyLogWriter = io.Discard } clientTP := getTransportParameters(runConfig[4] & 0x3) if helper.NthBit(runConfig[4], 3) { clientTP.MaxAckDelay = protocol.MaxMaxAckDelay + 5 } serverTP := getTransportParameters(runConfig[4] & 0b00011000) if helper.NthBit(runConfig[4], 3) { serverTP.MaxAckDelay = protocol.MaxMaxAckDelay + 5 } messageToReplace := messageConfig % 32 messageToReplaceEncLevel := toEncryptionLevel(messageConfig >> 6) if len(data) == 0 { return -1 } client := handshake.NewCryptoSetupClient( protocol.ConnectionID{}, clientTP, clientConf, enable0RTTClient, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("client"), protocol.Version1, ) if err := client.StartHandshake(context.Background()); err != nil { log.Fatal(err) } defer client.Close() server := handshake.NewCryptoSetupServer( protocol.ConnectionID{}, &net.UDPAddr{IP: net.IPv6loopback, Port: 1234}, &net.UDPAddr{IP: net.IPv6loopback, Port: 4321}, serverTP, serverConf, enable0RTTServer, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("server"), protocol.Version1, ) if err := server.StartHandshake(context.Background()); err != nil { log.Fatal(err) } defer server.Close() var clientHandshakeComplete, serverHandshakeComplete bool for { var processedEvent bool clientLoop: for { ev := client.NextEvent() //nolint:exhaustive // only need to process a few events switch ev.Kind { case handshake.EventNoEvent: if !processedEvent && !clientHandshakeComplete { // handshake stuck return 1 } break clientLoop case handshake.EventWriteInitialData, handshake.EventWriteHandshakeData: msg := ev.Data encLevel := protocol.EncryptionInitial if ev.Kind == handshake.EventWriteHandshakeData { encLevel = protocol.EncryptionHandshake } if msg[0] == messageToReplace { fmt.Printf("replacing %s message to the server with %s at %s\n", messageType(msg[0]), messageType(data[0]), messageToReplaceEncLevel) msg = data encLevel = messageToReplaceEncLevel } if err := server.HandleMessage(msg, encLevel); err != nil { return 1 } case handshake.EventHandshakeComplete: clientHandshakeComplete = true } processedEvent = true } processedEvent = false serverLoop: for { ev := server.NextEvent() //nolint:exhaustive // only need to process a few events switch ev.Kind { case handshake.EventNoEvent: if !processedEvent && !serverHandshakeComplete { // handshake stuck return 1 } break serverLoop case handshake.EventWriteInitialData, handshake.EventWriteHandshakeData: encLevel := protocol.EncryptionInitial if ev.Kind == handshake.EventWriteHandshakeData { encLevel = protocol.EncryptionHandshake } msg := ev.Data if msg[0] == messageToReplace { fmt.Printf("replacing %s message to the client with %s at %s\n", messageType(msg[0]), messageType(data[0]), messageToReplaceEncLevel) msg = data encLevel = messageToReplaceEncLevel } if err := client.HandleMessage(msg, encLevel); err != nil { return 1 } case handshake.EventHandshakeComplete: serverHandshakeComplete = true } processedEvent = true } if serverHandshakeComplete && clientHandshakeComplete { break } } _ = client.ConnectionState() _ = server.ConnectionState() sealer, err := client.Get1RTTSealer() if err != nil { panic("expected to get a 1-RTT sealer") } opener, err := server.Get1RTTOpener() if err != nil { panic("expected to get a 1-RTT opener") } const msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit." encrypted := sealer.Seal(nil, []byte(msg), 1337, []byte("foobar")) decrypted, err := opener.Open(nil, encrypted, time.Time{}, 1337, protocol.KeyPhaseZero, []byte("foobar")) if err != nil { panic(fmt.Sprintf("Decrypting message failed: %s", err.Error())) } if string(decrypted) != msg { panic("wrong message") } if sendSessionTicket && !serverConf.SessionTicketsDisabled { ticket, err := server.GetSessionTicket() if err != nil { panic(err) } if ticket == nil { panic("empty ticket") } client.HandleMessage(ticket, protocol.Encryption1RTT) } if sendPostHandshakeMessageToClient { fmt.Println("sending post handshake message to the client at", messageToReplaceEncLevel) client.HandleMessage(data, messageToReplaceEncLevel) } if sendPostHandshakeMessageToServer { fmt.Println("sending post handshake message to the server at", messageToReplaceEncLevel) server.HandleMessage(data, messageToReplaceEncLevel) } return 1 } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/header/000077500000000000000000000000001465664453100235335ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/header/cmd/000077500000000000000000000000001465664453100242765ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/header/cmd/corpus.go000066400000000000000000000105321465664453100261410ustar00rootroot00000000000000package main import ( "log" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/fuzzing/header" "github.com/quic-go/quic-go/fuzzing/internal/helper" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) const version = protocol.Version1 func getRandomData(l int) []byte { b := make([]byte, l) rand.Read(b) return b } func getVNP(src, dest protocol.ArbitraryLenConnectionID, numVersions int) []byte { versions := make([]protocol.Version, numVersions) for i := 0; i < numVersions; i++ { versions[i] = protocol.Version(rand.Uint32()) } return wire.ComposeVersionNegotiation(src, dest, versions) } func main() { headers := []wire.Header{ { // Initial without token SrcConnectionID: protocol.ParseConnectionID(getRandomData(3)), DestConnectionID: protocol.ParseConnectionID(getRandomData(8)), Type: protocol.PacketTypeInitial, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Initial without token, with zero-length src conn id DestConnectionID: protocol.ParseConnectionID(getRandomData(8)), Type: protocol.PacketTypeInitial, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Initial with Token SrcConnectionID: protocol.ParseConnectionID(getRandomData(10)), DestConnectionID: protocol.ParseConnectionID(getRandomData(19)), Type: protocol.PacketTypeInitial, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, Token: getRandomData(25), }, { // Handshake packet SrcConnectionID: protocol.ParseConnectionID(getRandomData(5)), DestConnectionID: protocol.ParseConnectionID(getRandomData(10)), Type: protocol.PacketTypeHandshake, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Handshake packet, with zero-length src conn id DestConnectionID: protocol.ParseConnectionID(getRandomData(12)), Type: protocol.PacketTypeHandshake, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // 0-RTT packet SrcConnectionID: protocol.ParseConnectionID(getRandomData(8)), DestConnectionID: protocol.ParseConnectionID(getRandomData(9)), Type: protocol.PacketType0RTT, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Retry Packet, with empty orig dest conn id SrcConnectionID: protocol.ParseConnectionID(getRandomData(8)), DestConnectionID: protocol.ParseConnectionID(getRandomData(9)), Type: protocol.PacketTypeRetry, Token: getRandomData(1000), Version: version, }, } for _, h := range headers { extHdr := &wire.ExtendedHeader{ Header: h, PacketNumberLen: protocol.PacketNumberLen(rand.Intn(4) + 1), PacketNumber: protocol.PacketNumber(rand.Uint64()), } b, err := extHdr.Append(nil, version) if err != nil { log.Fatal(err) } if h.Type == protocol.PacketTypeRetry { b = append(b, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}...) } if h.Length > 0 { b = append(b, make([]byte, h.Length)...) } if err := helper.WriteCorpusFileWithPrefix("corpus", b, header.PrefixLen); err != nil { log.Fatal(err) } } // short header b, err := wire.AppendShortHeader(nil, protocol.ParseConnectionID(getRandomData(8)), 1337, protocol.PacketNumberLen2, protocol.KeyPhaseOne) if err != nil { log.Fatal(err) } if err := helper.WriteCorpusFileWithPrefix("corpus", b, header.PrefixLen); err != nil { log.Fatal(err) } vnps := [][]byte{ getVNP( protocol.ArbitraryLenConnectionID(getRandomData(8)), protocol.ArbitraryLenConnectionID(getRandomData(10)), 4, ), getVNP( protocol.ArbitraryLenConnectionID(getRandomData(10)), protocol.ArbitraryLenConnectionID(getRandomData(5)), 0, ), getVNP( protocol.ArbitraryLenConnectionID(getRandomData(3)), protocol.ArbitraryLenConnectionID(getRandomData(19)), 100, ), getVNP( protocol.ArbitraryLenConnectionID(getRandomData(3)), nil, 20, ), getVNP( nil, protocol.ArbitraryLenConnectionID(getRandomData(10)), 5, ), } for _, vnp := range vnps { if err := helper.WriteCorpusFileWithPrefix("corpus", vnp, header.PrefixLen); err != nil { log.Fatal(err) } } } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/header/fuzz.go000066400000000000000000000050561465664453100250660ustar00rootroot00000000000000package header import ( "bytes" "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) const version = protocol.Version1 // PrefixLen is the number of bytes used for configuration const PrefixLen = 1 // Fuzz fuzzes the QUIC header. // //go:generate go run ./cmd/corpus.go func Fuzz(data []byte) int { if len(data) < PrefixLen { return 0 } connIDLen := int(data[0] % 21) data = data[PrefixLen:] if wire.IsVersionNegotiationPacket(data) { return fuzzVNP(data) } connID, err := wire.ParseConnectionID(data, connIDLen) if err != nil { return 0 } if !wire.IsLongHeaderPacket(data[0]) { wire.ParseShortHeader(data, connIDLen) return 1 } is0RTTPacket := wire.Is0RTTPacket(data) hdr, _, _, err := wire.ParsePacket(data) if err != nil { return 0 } if hdr.DestConnectionID != connID { panic(fmt.Sprintf("Expected connection IDs to match: %s vs %s", hdr.DestConnectionID, connID)) } if (hdr.Type == protocol.PacketType0RTT) != is0RTTPacket { panic("inconsistent 0-RTT packet detection") } var extHdr *wire.ExtendedHeader // Parse the extended header, if this is not a Retry packet. if hdr.Type == protocol.PacketTypeRetry { extHdr = &wire.ExtendedHeader{Header: *hdr} } else { var err error extHdr, err = hdr.ParseExtended(data) if err != nil { return 0 } } // We always use a 2-byte encoding for the Length field in Long Header packets. // Serializing the header will fail when using a higher value. if hdr.Length > 16383 { return 1 } b, err := extHdr.Append(nil, version) if err != nil { // We are able to parse packets with connection IDs longer than 20 bytes, // but in QUIC version 1, we don't write headers with longer connection IDs. if hdr.DestConnectionID.Len() <= protocol.MaxConnIDLen && hdr.SrcConnectionID.Len() <= protocol.MaxConnIDLen { panic(err) } return 0 } // GetLength is not implemented for Retry packets if hdr.Type != protocol.PacketTypeRetry { if expLen := extHdr.GetLength(version); expLen != protocol.ByteCount(len(b)) { panic(fmt.Sprintf("inconsistent header length: %#v. Expected %d, got %d", extHdr, expLen, len(b))) } } return 1 } func fuzzVNP(data []byte) int { connID, err := wire.ParseConnectionID(data, 0) if err != nil { return 0 } dest, src, versions, err := wire.ParseVersionNegotiationPacket(data) if err != nil { return 0 } if !bytes.Equal(dest, connID.Bytes()) { panic("connection IDs don't match") } if len(versions) == 0 { panic("no versions") } wire.ComposeVersionNegotiation(src, dest, versions) return 1 } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/internal/000077500000000000000000000000001465664453100241175ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/internal/helper/000077500000000000000000000000001465664453100253765ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/internal/helper/helper.go000066400000000000000000000043631465664453100272120ustar00rootroot00000000000000package helper import ( "crypto" "crypto/rand" "crypto/sha1" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/hex" "math/big" "os" "path/filepath" "time" ) // NthBit gets the n-th bit of a byte (counting starts at 0). func NthBit(val uint8, n int) bool { if n < 0 || n > 7 { panic("invalid value for n") } return val>>n&0x1 == 1 } // WriteCorpusFile writes data to a corpus file in directory path. // The filename is calculated from the SHA1 sum of the file contents. func WriteCorpusFile(path string, data []byte) error { // create the directory, if it doesn't exist yet if _, err := os.Stat(path); os.IsNotExist(err) { if err := os.MkdirAll(path, os.ModePerm); err != nil { return err } } hash := sha1.Sum(data) return os.WriteFile(filepath.Join(path, hex.EncodeToString(hash[:])), data, 0o644) } // WriteCorpusFileWithPrefix writes data to a corpus file in directory path. // In many fuzzers, the first n bytes are used to control. // This function prepends n zero-bytes to the data. func WriteCorpusFileWithPrefix(path string, data []byte, n int) error { return WriteCorpusFile(path, append(make([]byte, n), data...)) } // GenerateCertificate generates a self-signed certificate. // It returns the certificate and a x509.CertPool containing that certificate. func GenerateCertificate(priv crypto.Signer) (*tls.Certificate, *x509.CertPool, error) { template := x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{Organization: []string{"quic-go fuzzer"}}, NotBefore: time.Now().Add(-24 * time.Hour), NotAfter: time.Now().Add(30 * 24 * time.Hour), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, DNSNames: []string{"localhost"}, BasicConstraintsValid: true, } derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, priv.Public(), priv) if err != nil { return nil, nil, err } cert, err := x509.ParseCertificate(derBytes) if err != nil { return nil, nil, err } certPool := x509.NewCertPool() certPool.AddCert(cert) return &tls.Certificate{ Certificate: [][]byte{derBytes}, PrivateKey: priv, }, certPool, nil } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/internal/helper/helper_suite_test.go000066400000000000000000000002751465664453100314600ustar00rootroot00000000000000package helper import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestHelper(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Helper Suite") } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/internal/helper/helper_test.go000066400000000000000000000040761465664453100302520ustar00rootroot00000000000000package helper import ( "fmt" "os" "path/filepath" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("exporting", func() { var dir string BeforeEach(func() { var err error dir, err = os.MkdirTemp("", "fuzzing-helper") Expect(err).ToNot(HaveOccurred()) fmt.Fprintf(GinkgoWriter, "Created temporary directory %s", dir) }) AfterEach(func() { Expect(dir).ToNot(BeEmpty()) Expect(os.RemoveAll(dir)).To(Succeed()) }) It("writes a file", func() { const data = "lorem ipsum" // calculated by running sha1sum on the generated file const expectedShaSum = "bfb7759a67daeb65410490b4d98bb9da7d1ea2ce" Expect(WriteCorpusFile(dir, []byte("lorem ipsum"))).To(Succeed()) path := filepath.Join(dir, expectedShaSum) Expect(path).To(BeARegularFile()) b, err := os.ReadFile(path) Expect(err).ToNot(HaveOccurred()) Expect(string(b)).To(Equal(data)) }) It("writes a file and prepends data", func() { const data = "lorem ipsum" // calculated by running sha1sum on the generated file const expectedShaSum = "523f5cab80fab0c7889dbf50dd310ab8c8879f9c" const prefixLen = 7 Expect(WriteCorpusFileWithPrefix(dir, []byte("lorem ipsum"), prefixLen)).To(Succeed()) path := filepath.Join(dir, expectedShaSum) Expect(path).To(BeARegularFile()) b, err := os.ReadFile(path) Expect(err).ToNot(HaveOccurred()) Expect(b[:prefixLen]).To(Equal(make([]byte, prefixLen))) Expect(string(b[prefixLen:])).To(Equal(data)) }) It("creates the directory, if it doesn't yet", func() { subdir := filepath.Join(dir, "corpus") Expect(subdir).ToNot(BeADirectory()) Expect(WriteCorpusFile(subdir, []byte("lorem ipsum"))).To(Succeed()) Expect(subdir).To(BeADirectory()) }) It("gets the nth bit of a byte", func() { const val = 0b10010001 Expect(NthBit(val, 0)).To(BeTrue()) Expect(NthBit(val, 1)).To(BeFalse()) Expect(NthBit(val, 2)).To(BeFalse()) Expect(NthBit(val, 3)).To(BeFalse()) Expect(NthBit(val, 4)).To(BeTrue()) Expect(NthBit(val, 5)).To(BeFalse()) Expect(NthBit(val, 6)).To(BeFalse()) Expect(NthBit(val, 7)).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/tokens/000077500000000000000000000000001465664453100236065ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/tokens/fuzz.go000066400000000000000000000056431465664453100251430ustar00rootroot00000000000000package tokens import ( "encoding/binary" "net" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" ) func Fuzz(data []byte) int { if len(data) < 32 { return -1 } var key quic.TokenGeneratorKey copy(key[:], data[:32]) data = data[32:] tg := handshake.NewTokenGenerator(key) if len(data) < 1 { return -1 } s := data[0] % 3 data = data[1:] switch s { case 0: tg.DecodeToken(data) return 1 case 1: return newToken(tg, data) case 2: return newRetryToken(tg, data) } return -1 } func newToken(tg *handshake.TokenGenerator, data []byte) int { if len(data) < 1 { return -1 } usesUDPAddr := data[0]%2 == 0 data = data[1:] if len(data) != 18 { return -1 } var addr net.Addr if usesUDPAddr { addr = &net.UDPAddr{ Port: int(binary.BigEndian.Uint16(data[:2])), IP: net.IP(data[2:]), } } else { addr = &net.TCPAddr{ Port: int(binary.BigEndian.Uint16(data[:2])), IP: net.IP(data[2:]), } } start := time.Now() encrypted, err := tg.NewToken(addr) if err != nil { panic(err) } token, err := tg.DecodeToken(encrypted) if err != nil { panic(err) } if token.IsRetryToken { panic("didn't encode a Retry token") } if token.SentTime.Before(start) || token.SentTime.After(time.Now()) { panic("incorrect send time") } if token.OriginalDestConnectionID.Len() > 0 || token.RetrySrcConnectionID.Len() > 0 { panic("didn't expect connection IDs") } return 1 } func newRetryToken(tg *handshake.TokenGenerator, data []byte) int { if len(data) < 2 { return -1 } origDestConnIDLen := int(data[0] % 21) retrySrcConnIDLen := int(data[1] % 21) data = data[2:] if len(data) < origDestConnIDLen { return -1 } origDestConnID := protocol.ParseConnectionID(data[:origDestConnIDLen]) data = data[origDestConnIDLen:] if len(data) < retrySrcConnIDLen { return -1 } retrySrcConnID := protocol.ParseConnectionID(data[:retrySrcConnIDLen]) data = data[retrySrcConnIDLen:] if len(data) < 1 { return -1 } usesUDPAddr := data[0]%2 == 0 data = data[1:] if len(data) != 18 { return -1 } start := time.Now() var addr net.Addr if usesUDPAddr { addr = &net.UDPAddr{ Port: int(binary.BigEndian.Uint16(data[:2])), IP: net.IP(data[2:]), } } else { addr = &net.TCPAddr{ Port: int(binary.BigEndian.Uint16(data[:2])), IP: net.IP(data[2:]), } } encrypted, err := tg.NewRetryToken(addr, origDestConnID, retrySrcConnID) if err != nil { panic(err) } token, err := tg.DecodeToken(encrypted) if err != nil { panic(err) } if !token.IsRetryToken { panic("expected a Retry token") } if token.SentTime.Before(start) || token.SentTime.After(time.Now()) { panic("incorrect send time") } if token.OriginalDestConnectionID != origDestConnID { panic("orig dest conn ID doesn't match") } if token.RetrySrcConnectionID != retrySrcConnID { panic("retry src conn ID doesn't match") } return 1 } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/transportparameters/000077500000000000000000000000001465664453100264235ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/transportparameters/cmd/000077500000000000000000000000001465664453100271665ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/transportparameters/cmd/corpus.go000066400000000000000000000054711465664453100310370ustar00rootroot00000000000000package main import ( "log" "math" "net/netip" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/fuzzing/internal/helper" "github.com/quic-go/quic-go/fuzzing/transportparameters" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) func getRandomData(l int) []byte { b := make([]byte, l) rand.Read(b) return b } func getRandomValue() uint64 { maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4} return uint64(rand.Int63n(maxVals[int(rand.Int31n(4))])) } func main() { for i := 0; i < 30; i++ { tp := &wire.TransportParameters{ InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), InitialMaxData: protocol.ByteCount(getRandomValue()), MaxAckDelay: time.Duration(getRandomValue()), AckDelayExponent: uint8(getRandomValue()), DisableActiveMigration: getRandomValue()%2 == 0, MaxUDPPayloadSize: protocol.ByteCount(getRandomValue()), MaxUniStreamNum: protocol.StreamNum(getRandomValue()), MaxBidiStreamNum: protocol.StreamNum(getRandomValue()), MaxIdleTimeout: time.Duration(getRandomValue()), ActiveConnectionIDLimit: getRandomValue() + 2, } if rand.Int()%2 == 0 { tp.OriginalDestinationConnectionID = protocol.ParseConnectionID(getRandomData(rand.Intn(21))) } if rand.Int()%2 == 0 { tp.InitialSourceConnectionID = protocol.ParseConnectionID(getRandomData(rand.Intn(21))) } if rand.Int()%2 == 0 { connID := protocol.ParseConnectionID(getRandomData(rand.Intn(21))) tp.RetrySourceConnectionID = &connID } if rand.Int()%2 == 0 { var token protocol.StatelessResetToken rand.Read(token[:]) tp.StatelessResetToken = &token } if rand.Int()%2 == 0 { var token protocol.StatelessResetToken rand.Read(token[:]) var ip4 [4]byte rand.Read(ip4[:]) var ip6 [16]byte rand.Read(ip6[:]) tp.PreferredAddress = &wire.PreferredAddress{ IPv4: netip.AddrPortFrom(netip.AddrFrom4(ip4), uint16(rand.Int())), IPv6: netip.AddrPortFrom(netip.AddrFrom16(ip6), uint16(rand.Int())), ConnectionID: protocol.ParseConnectionID(getRandomData(rand.Intn(21))), StatelessResetToken: token, } } var data []byte if rand.Int()%2 == 0 { pers := protocol.PerspectiveServer if rand.Int()%2 == 0 { pers = protocol.PerspectiveClient } data = tp.Marshal(pers) } else { data = tp.MarshalForSessionTicket(nil) } if err := helper.WriteCorpusFileWithPrefix("corpus", data, transportparameters.PrefixLen); err != nil { log.Fatal(err) } } } golang-github-lucas-clemente-quic-go-0.46.0/fuzzing/transportparameters/fuzz.go000066400000000000000000000056531465664453100277610ustar00rootroot00000000000000package transportparameters import ( "errors" "fmt" "github.com/quic-go/quic-go/fuzzing/internal/helper" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) // PrefixLen is the number of bytes used for configuration const PrefixLen = 1 // Fuzz fuzzes the QUIC transport parameters. // //go:generate go run ./cmd/corpus.go func Fuzz(data []byte) int { if len(data) <= PrefixLen { return 0 } if helper.NthBit(data[0], 0) { return fuzzTransportParametersForSessionTicket(data[PrefixLen:]) } return fuzzTransportParameters(data[PrefixLen:], helper.NthBit(data[0], 1)) } func fuzzTransportParameters(data []byte, sentByServer bool) int { sentBy := protocol.PerspectiveClient if sentByServer { sentBy = protocol.PerspectiveServer } tp := &wire.TransportParameters{} if err := tp.Unmarshal(data, sentBy); err != nil { return 0 } _ = tp.String() if err := validateTransportParameters(tp, sentBy); err != nil { panic(err) } tp2 := &wire.TransportParameters{} if err := tp2.Unmarshal(tp.Marshal(sentBy), sentBy); err != nil { fmt.Printf("%#v\n", tp) panic(err) } if err := validateTransportParameters(tp2, sentBy); err != nil { panic(err) } return 1 } func fuzzTransportParametersForSessionTicket(data []byte) int { tp := &wire.TransportParameters{} if err := tp.UnmarshalFromSessionTicket(data); err != nil { return 0 } b := tp.MarshalForSessionTicket(nil) tp2 := &wire.TransportParameters{} if err := tp2.UnmarshalFromSessionTicket(b); err != nil { panic(err) } return 1 } func validateTransportParameters(tp *wire.TransportParameters, sentBy protocol.Perspective) error { if sentBy == protocol.PerspectiveClient && tp.StatelessResetToken != nil { return errors.New("client's transport parameters contained stateless reset token") } if tp.MaxIdleTimeout < 0 { return fmt.Errorf("negative max_idle_timeout: %s", tp.MaxIdleTimeout) } if tp.AckDelayExponent > 20 { return fmt.Errorf("invalid ack_delay_exponent: %d", tp.AckDelayExponent) } if tp.MaxUDPPayloadSize < 1200 { return fmt.Errorf("invalid max_udp_payload_size: %d", tp.MaxUDPPayloadSize) } if tp.ActiveConnectionIDLimit < 2 { return fmt.Errorf("invalid active_connection_id_limit: %d", tp.ActiveConnectionIDLimit) } if tp.OriginalDestinationConnectionID.Len() > 20 { return fmt.Errorf("invalid original_destination_connection_id length: %s", tp.InitialSourceConnectionID) } if tp.InitialSourceConnectionID.Len() > 20 { return fmt.Errorf("invalid initial_source_connection_id length: %s", tp.InitialSourceConnectionID) } if tp.RetrySourceConnectionID != nil && tp.RetrySourceConnectionID.Len() > 20 { return fmt.Errorf("invalid retry_source_connection_id length: %s", tp.RetrySourceConnectionID) } if tp.PreferredAddress != nil && tp.PreferredAddress.ConnectionID.Len() > 20 { return fmt.Errorf("invalid preferred_address connection ID length: %s", tp.PreferredAddress.ConnectionID) } return nil } golang-github-lucas-clemente-quic-go-0.46.0/go.mod000066400000000000000000000021771465664453100217240ustar00rootroot00000000000000module github.com/quic-go/quic-go go 1.21 require ( github.com/francoispqt/gojay v1.2.13 github.com/onsi/ginkgo/v2 v2.9.5 github.com/onsi/gomega v1.27.6 github.com/prometheus/client_golang v1.19.1 github.com/quic-go/qpack v0.4.0 go.uber.org/mock v0.4.0 golang.org/x/crypto v0.23.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.20.0 golang.org/x/time v0.5.0 ) require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/common v0.48.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) golang-github-lucas-clemente-quic-go-0.46.0/go.sum000066400000000000000000000553221465664453100217510ustar00rootroot00000000000000cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= golang-github-lucas-clemente-quic-go-0.46.0/http3/000077500000000000000000000000001465664453100216515ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/http3/README.md000066400000000000000000000012421465664453100231270ustar00rootroot00000000000000# HTTP/3 [![Documentation](https://img.shields.io/badge/docs-quic--go.net-red?style=flat)](https://quic-go.net/docs/) [![PkgGoDev](https://pkg.go.dev/badge/github.com/quic-go/quic-go/http3)](https://pkg.go.dev/github.com/quic-go/quic-go/http3) This package implements HTTP/3 ([RFC 9114](https://datatracker.ietf.org/doc/html/rfc9114)), including QPACK ([RFC 9204](https://datatracker.ietf.org/doc/html/rfc9204)) and HTTP Datagrams ([RFC 9297](https://datatracker.ietf.org/doc/html/rfc9297)). It aims to provide feature parity with the standard library's HTTP/1.1 and HTTP/2 implementation. Detailed documentation can be found on [quic-go.net](https://quic-go.net/docs/). golang-github-lucas-clemente-quic-go-0.46.0/http3/body.go000066400000000000000000000063121465664453100231370ustar00rootroot00000000000000package http3 import ( "context" "errors" "io" "github.com/quic-go/quic-go" ) // A Hijacker allows hijacking of the stream creating part of a quic.Session from a http.Response.Body. // It is used by WebTransport to create WebTransport streams after a session has been established. type Hijacker interface { Connection() Connection } var errTooMuchData = errors.New("peer sent too much data") // The body is used in the requestBody (for a http.Request) and the responseBody (for a http.Response). type body struct { str *stream remainingContentLength int64 violatedContentLength bool hasContentLength bool } func newBody(str *stream, contentLength int64) *body { b := &body{str: str} if contentLength >= 0 { b.hasContentLength = true b.remainingContentLength = contentLength } return b } func (r *body) StreamID() quic.StreamID { return r.str.StreamID() } func (r *body) checkContentLengthViolation() error { if !r.hasContentLength { return nil } if r.remainingContentLength < 0 || r.remainingContentLength == 0 && r.str.hasMoreData() { if !r.violatedContentLength { r.str.CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) r.str.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) r.violatedContentLength = true } return errTooMuchData } return nil } func (r *body) Read(b []byte) (int, error) { if err := r.checkContentLengthViolation(); err != nil { return 0, err } if r.hasContentLength { b = b[:min(int64(len(b)), r.remainingContentLength)] } n, err := r.str.Read(b) r.remainingContentLength -= int64(n) if err := r.checkContentLengthViolation(); err != nil { return n, err } return n, maybeReplaceError(err) } func (r *body) Close() error { r.str.CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)) return nil } type requestBody struct { body connCtx context.Context rcvdSettings <-chan struct{} getSettings func() *Settings } var _ io.ReadCloser = &requestBody{} func newRequestBody(str *stream, contentLength int64, connCtx context.Context, rcvdSettings <-chan struct{}, getSettings func() *Settings) *requestBody { return &requestBody{ body: *newBody(str, contentLength), connCtx: connCtx, rcvdSettings: rcvdSettings, getSettings: getSettings, } } type hijackableBody struct { body body // only set for the http.Response // The channel is closed when the user is done with this response: // either when Read() errors, or when Close() is called. reqDone chan<- struct{} reqDoneClosed bool } var _ io.ReadCloser = &hijackableBody{} func newResponseBody(str *stream, contentLength int64, done chan<- struct{}) *hijackableBody { return &hijackableBody{ body: *newBody(str, contentLength), reqDone: done, } } func (r *hijackableBody) Read(b []byte) (int, error) { n, err := r.body.Read(b) if err != nil { r.requestDone() } return n, maybeReplaceError(err) } func (r *hijackableBody) requestDone() { if r.reqDoneClosed || r.reqDone == nil { return } if r.reqDone != nil { close(r.reqDone) } r.reqDoneClosed = true } func (r *hijackableBody) Close() error { r.requestDone() // If the EOF was read, CancelRead() is a no-op. r.body.str.CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)) return nil } golang-github-lucas-clemente-quic-go-0.46.0/http3/body_test.go000066400000000000000000000067661465664453100242130ustar00rootroot00000000000000package http3 import ( "bytes" "errors" "io" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Response Body", func() { var reqDone chan struct{} BeforeEach(func() { reqDone = make(chan struct{}) }) It("closes the reqDone channel when Read errors", func() { str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).Return(0, errors.New("test error")) rb := newResponseBody(&stream{Stream: str}, -1, reqDone) _, err := rb.Read([]byte{0}) Expect(err).To(MatchError("test error")) Expect(reqDone).To(BeClosed()) }) It("allows multiple calls to Read, when Read errors", func() { str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).Return(0, errors.New("test error")).Times(2) rb := newResponseBody(&stream{Stream: str}, -1, reqDone) _, err := rb.Read([]byte{0}) Expect(err).To(HaveOccurred()) Expect(reqDone).To(BeClosed()) _, err = rb.Read([]byte{0}) Expect(err).To(HaveOccurred()) }) It("closes responses", func() { str := mockquic.NewMockStream(mockCtrl) rb := newResponseBody(&stream{Stream: str}, -1, reqDone) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)) Expect(rb.Close()).To(Succeed()) }) It("allows multiple calls to Close", func() { str := mockquic.NewMockStream(mockCtrl) rb := newResponseBody(&stream{Stream: str}, -1, reqDone) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)).MaxTimes(2) Expect(rb.Close()).To(Succeed()) Expect(reqDone).To(BeClosed()) Expect(rb.Close()).To(Succeed()) }) Context("length limiting", func() { It("reads all frames", func() { var buf bytes.Buffer buf.Write(getDataFrame([]byte("foobar"))) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() rb := newResponseBody(&stream{Stream: str}, 6, reqDone) data, err := io.ReadAll(rb) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) }) It("errors if more data than the maximum length is sent, in the middle of a frame", func() { var buf bytes.Buffer buf.Write(getDataFrame([]byte("foo"))) buf.Write(getDataFrame([]byte("bar"))) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() rb := newResponseBody(&stream{Stream: str}, 4, reqDone) data, err := io.ReadAll(rb) Expect(data).To(Equal([]byte("foob"))) Expect(err).To(MatchError(errTooMuchData)) // check that repeated calls to Read also return the right error n, err := rb.Read([]byte{0}) Expect(n).To(BeZero()) Expect(err).To(MatchError(errTooMuchData)) }) It("errors if more data than the maximum length is sent, as an additional frame", func() { var buf bytes.Buffer buf.Write(getDataFrame([]byte("foo"))) buf.Write(getDataFrame([]byte("bar"))) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() rb := newResponseBody(&stream{Stream: str}, 3, reqDone) data, err := io.ReadAll(rb) Expect(err).To(MatchError(errTooMuchData)) Expect(data).To(Equal([]byte("foo"))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/capsule.go000066400000000000000000000032411465664453100236340ustar00rootroot00000000000000package http3 import ( "io" "github.com/quic-go/quic-go/quicvarint" ) // CapsuleType is the type of the capsule. type CapsuleType uint64 type exactReader struct { R *io.LimitedReader } func (r *exactReader) Read(b []byte) (int, error) { n, err := r.R.Read(b) if r.R.N > 0 { return n, io.ErrUnexpectedEOF } return n, err } type countingByteReader struct { io.ByteReader Read int } func (r *countingByteReader) ReadByte() (byte, error) { b, err := r.ByteReader.ReadByte() if err == nil { r.Read++ } return b, err } // ParseCapsule parses the header of a Capsule. // It returns an io.LimitedReader that can be used to read the Capsule value. // The Capsule value must be read entirely (i.e. until the io.EOF) before using r again. func ParseCapsule(r quicvarint.Reader) (CapsuleType, io.Reader, error) { cbr := countingByteReader{ByteReader: r} ct, err := quicvarint.Read(&cbr) if err != nil { // If an io.EOF is returned without consuming any bytes, return it unmodified. // Otherwise, return an io.ErrUnexpectedEOF. if err == io.EOF && cbr.Read > 0 { return 0, nil, io.ErrUnexpectedEOF } return 0, nil, err } l, err := quicvarint.Read(r) if err != nil { if err == io.EOF { return 0, nil, io.ErrUnexpectedEOF } return 0, nil, err } return CapsuleType(ct), &exactReader{R: io.LimitReader(r, int64(l)).(*io.LimitedReader)}, nil } // WriteCapsule writes a capsule func WriteCapsule(w quicvarint.Writer, ct CapsuleType, value []byte) error { b := make([]byte, 0, 16) b = quicvarint.Append(b, uint64(ct)) b = quicvarint.Append(b, uint64(len(value))) if _, err := w.Write(b); err != nil { return err } _, err := w.Write(value) return err } golang-github-lucas-clemente-quic-go-0.46.0/http3/capsule_test.go000066400000000000000000000025561465664453100247030ustar00rootroot00000000000000package http3 import ( "bytes" "io" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Capsule", func() { It("parses Capsules", func() { b := quicvarint.Append(nil, 1337) b = quicvarint.Append(b, 6) b = append(b, []byte("foobar")...) ct, r, err := ParseCapsule(bytes.NewReader(b)) Expect(err).ToNot(HaveOccurred()) Expect(ct).To(BeEquivalentTo(1337)) val, err := io.ReadAll(r) Expect(err).ToNot(HaveOccurred()) Expect(string(val)).To(Equal("foobar")) }) It("writes capsules", func() { var buf bytes.Buffer Expect(WriteCapsule(&buf, 1337, []byte("foobar"))).To(Succeed()) ct, r, err := ParseCapsule(&buf) Expect(err).ToNot(HaveOccurred()) Expect(ct).To(BeEquivalentTo(1337)) val, err := io.ReadAll(r) Expect(err).ToNot(HaveOccurred()) Expect(string(val)).To(Equal("foobar")) }) It("errors on EOF", func() { b := quicvarint.Append(nil, 1337) b = quicvarint.Append(b, 6) b = append(b, []byte("foobar")...) for i := range b { ct, r, err := ParseCapsule(bytes.NewReader(b[:i])) if err != nil { if i == 0 { Expect(err).To(MatchError(io.EOF)) } else { Expect(err).To(MatchError(io.ErrUnexpectedEOF)) } continue } Expect(ct).To(BeEquivalentTo(1337)) _, err = io.ReadAll(r) Expect(err).To(Equal(io.ErrUnexpectedEOF)) } }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/client.go000066400000000000000000000242231465664453100234610ustar00rootroot00000000000000package http3 import ( "context" "errors" "fmt" "io" "log/slog" "net/http" "net/http/httptrace" "net/textproto" "sync" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" ) const ( // MethodGet0RTT allows a GET request to be sent using 0-RTT. // Note that 0-RTT doesn't provide replay protection and should only be used for idempotent requests. MethodGet0RTT = "GET_0RTT" // MethodHead0RTT allows a HEAD request to be sent using 0-RTT. // Note that 0-RTT doesn't provide replay protection and should only be used for idempotent requests. MethodHead0RTT = "HEAD_0RTT" ) const ( defaultUserAgent = "quic-go HTTP/3" defaultMaxResponseHeaderBytes = 10 * 1 << 20 // 10 MB ) var defaultQuicConfig = &quic.Config{ MaxIncomingStreams: -1, // don't allow the server to create bidirectional streams KeepAlivePeriod: 10 * time.Second, } // SingleDestinationRoundTripper is an HTTP/3 client doing requests to a single remote server. type SingleDestinationRoundTripper struct { Connection quic.Connection // Enable support for HTTP/3 datagrams (RFC 9297). // If a QUICConfig is set, datagram support also needs to be enabled on the QUIC layer by setting EnableDatagrams. EnableDatagrams bool // Additional HTTP/3 settings. // It is invalid to specify any settings defined by RFC 9114 (HTTP/3) and RFC 9297 (HTTP Datagrams). AdditionalSettings map[uint64]uint64 StreamHijacker func(FrameType, quic.ConnectionTracingID, quic.Stream, error) (hijacked bool, err error) UniStreamHijacker func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool) // MaxResponseHeaderBytes specifies a limit on how many response bytes are // allowed in the server's response header. // Zero means to use a default limit. MaxResponseHeaderBytes int64 // DisableCompression, if true, prevents the Transport from requesting compression with an // "Accept-Encoding: gzip" request header when the Request contains no existing Accept-Encoding value. // If the Transport requests gzip on its own and gets a gzipped response, it's transparently // decoded in the Response.Body. // However, if the user explicitly requested gzip it is not automatically uncompressed. DisableCompression bool Logger *slog.Logger initOnce sync.Once hconn *connection requestWriter *requestWriter decoder *qpack.Decoder } var _ http.RoundTripper = &SingleDestinationRoundTripper{} func (c *SingleDestinationRoundTripper) Start() Connection { c.initOnce.Do(func() { c.init() }) return c.hconn } func (c *SingleDestinationRoundTripper) init() { c.decoder = qpack.NewDecoder(func(hf qpack.HeaderField) {}) c.requestWriter = newRequestWriter() c.hconn = newConnection( c.Connection.Context(), c.Connection, c.EnableDatagrams, protocol.PerspectiveClient, c.Logger, 0, ) // send the SETTINGs frame, using 0-RTT data, if possible go func() { if err := c.setupConn(c.hconn); err != nil { if c.Logger != nil { c.Logger.Debug("Setting up connection failed", "error", err) } c.hconn.CloseWithError(quic.ApplicationErrorCode(ErrCodeInternalError), "") } }() if c.StreamHijacker != nil { go c.handleBidirectionalStreams() } go c.hconn.HandleUnidirectionalStreams(c.UniStreamHijacker) } func (c *SingleDestinationRoundTripper) setupConn(conn *connection) error { // open the control stream str, err := conn.OpenUniStream() if err != nil { return err } b := make([]byte, 0, 64) b = quicvarint.Append(b, streamTypeControlStream) // send the SETTINGS frame b = (&settingsFrame{Datagram: c.EnableDatagrams, Other: c.AdditionalSettings}).Append(b) _, err = str.Write(b) return err } func (c *SingleDestinationRoundTripper) handleBidirectionalStreams() { for { str, err := c.hconn.AcceptStream(context.Background()) if err != nil { if c.Logger != nil { c.Logger.Debug("accepting bidirectional stream failed", "error", err) } return } fp := &frameParser{ r: str, conn: c.hconn, unknownFrameHandler: func(ft FrameType, e error) (processed bool, err error) { id := c.hconn.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) return c.StreamHijacker(ft, id, str, e) }, } go func() { if _, err := fp.ParseNext(); err == errHijacked { return } if err != nil { if c.Logger != nil { c.Logger.Debug("error handling stream", "error", err) } } c.hconn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "received HTTP/3 frame on bidirectional stream") }() } } func (c *SingleDestinationRoundTripper) maxHeaderBytes() uint64 { if c.MaxResponseHeaderBytes <= 0 { return defaultMaxResponseHeaderBytes } return uint64(c.MaxResponseHeaderBytes) } // RoundTrip executes a request and returns a response func (c *SingleDestinationRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { c.initOnce.Do(func() { c.init() }) rsp, err := c.roundTrip(req) if err != nil && req.Context().Err() != nil { // if the context was canceled, return the context cancellation error err = req.Context().Err() } return rsp, err } func (c *SingleDestinationRoundTripper) roundTrip(req *http.Request) (*http.Response, error) { // Immediately send out this request, if this is a 0-RTT request. switch req.Method { case MethodGet0RTT: // don't modify the original request reqCopy := *req req = &reqCopy req.Method = http.MethodGet case MethodHead0RTT: // don't modify the original request reqCopy := *req req = &reqCopy req.Method = http.MethodHead default: // wait for the handshake to complete earlyConn, ok := c.Connection.(quic.EarlyConnection) if ok { select { case <-earlyConn.HandshakeComplete(): case <-req.Context().Done(): return nil, req.Context().Err() } } } // It is only possible to send an Extended CONNECT request once the SETTINGS were received. // See section 3 of RFC 8441. if isExtendedConnectRequest(req) { connCtx := c.Connection.Context() // wait for the server's SETTINGS frame to arrive select { case <-c.hconn.ReceivedSettings(): case <-connCtx.Done(): return nil, context.Cause(connCtx) } if !c.hconn.Settings().EnableExtendedConnect { return nil, errors.New("http3: server didn't enable Extended CONNECT") } } reqDone := make(chan struct{}) str, err := c.hconn.openRequestStream(req.Context(), c.requestWriter, reqDone, c.DisableCompression, c.maxHeaderBytes()) if err != nil { return nil, err } // Request Cancellation: // This go routine keeps running even after RoundTripOpt() returns. // It is shut down when the application is done processing the body. done := make(chan struct{}) go func() { defer close(done) select { case <-req.Context().Done(): str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) str.CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)) case <-reqDone: } }() rsp, err := c.doRequest(req, str) if err != nil { // if any error occurred close(reqDone) <-done return nil, maybeReplaceError(err) } return rsp, maybeReplaceError(err) } func (c *SingleDestinationRoundTripper) OpenRequestStream(ctx context.Context) (RequestStream, error) { c.initOnce.Do(func() { c.init() }) return c.hconn.openRequestStream(ctx, c.requestWriter, nil, c.DisableCompression, c.maxHeaderBytes()) } // cancelingReader reads from the io.Reader. // It cancels writing on the stream if any error other than io.EOF occurs. type cancelingReader struct { r io.Reader str Stream } func (r *cancelingReader) Read(b []byte) (int, error) { n, err := r.r.Read(b) if err != nil && err != io.EOF { r.str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) } return n, err } func (c *SingleDestinationRoundTripper) sendRequestBody(str Stream, body io.ReadCloser, contentLength int64) error { defer body.Close() buf := make([]byte, bodyCopyBufferSize) sr := &cancelingReader{str: str, r: body} if contentLength == -1 { _, err := io.CopyBuffer(str, sr, buf) return err } // make sure we don't send more bytes than the content length n, err := io.CopyBuffer(str, io.LimitReader(sr, contentLength), buf) if err != nil { return err } var extra int64 extra, err = io.CopyBuffer(io.Discard, sr, buf) n += extra if n > contentLength { str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) return fmt.Errorf("http: ContentLength=%d with Body length %d", contentLength, n) } return err } func (c *SingleDestinationRoundTripper) doRequest(req *http.Request, str *requestStream) (*http.Response, error) { if err := str.SendRequestHeader(req); err != nil { return nil, err } if req.Body == nil { str.Close() } else { // send the request body asynchronously go func() { contentLength := int64(-1) // According to the documentation for http.Request.ContentLength, // a value of 0 with a non-nil Body is also treated as unknown content length. if req.ContentLength > 0 { contentLength = req.ContentLength } if err := c.sendRequestBody(str, req.Body, contentLength); err != nil { if c.Logger != nil { c.Logger.Debug("error writing request", "error", err) } } str.Close() }() } // copy from net/http: support 1xx responses trace := httptrace.ContextClientTrace(req.Context()) num1xx := 0 // number of informational 1xx headers received const max1xxResponses = 5 // arbitrary bound on number of informational responses var res *http.Response for { var err error res, err = str.ReadResponse() if err != nil { return nil, err } resCode := res.StatusCode is1xx := 100 <= resCode && resCode <= 199 // treat 101 as a terminal status, see https://github.com/golang/go/issues/26161 is1xxNonTerminal := is1xx && resCode != http.StatusSwitchingProtocols if is1xxNonTerminal { num1xx++ if num1xx > max1xxResponses { return nil, errors.New("http: too many 1xx informational responses") } if trace != nil && trace.Got1xxResponse != nil { if err := trace.Got1xxResponse(resCode, textproto.MIMEHeader(res.Header)); err != nil { return nil, err } } continue } break } connState := c.hconn.ConnectionState().TLS res.TLS = &connState res.Request = req return res, nil } golang-github-lucas-clemente-quic-go-0.46.0/http3/client_test.go000066400000000000000000001101711465664453100245160ustar00rootroot00000000000000package http3 import ( "bytes" "compress/gzip" "context" "errors" "io" "net/http" "net/http/httptrace" "net/textproto" "sync" "time" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func encodeResponse(status int) []byte { buf := &bytes.Buffer{} rstr := mockquic.NewMockStream(mockCtrl) rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes() rw := newResponseWriter(newStream(rstr, nil, nil), nil, false, nil) if status == http.StatusEarlyHints { rw.header.Add("Link", "; rel=preload; as=style") rw.header.Add("Link", "; rel=preload; as=script") } rw.WriteHeader(status) rw.Flush() return buf.Bytes() } var _ = Describe("Client", func() { var handshakeChan <-chan struct{} // a closed chan BeforeEach(func() { ch := make(chan struct{}) close(ch) handshakeChan = ch }) Context("hijacking bidirectional streams", func() { var ( request *http.Request conn *mockquic.MockEarlyConnection settingsFrameWritten chan struct{} ) testDone := make(chan struct{}) BeforeEach(func() { testDone = make(chan struct{}) settingsFrameWritten = make(chan struct{}) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()).Do(func(b []byte) (int, error) { defer GinkgoRecover() close(settingsFrameWritten) return len(b), nil }) conn = mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().HandshakeComplete().Return(handshakeChan) conn.EXPECT().OpenStreamSync(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("done")).AnyTimes() var err error request, err = http.NewRequest("GET", "https://quic.clemente.io:1337/file1.dat", nil) Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { testDone <- struct{}{} Eventually(settingsFrameWritten).Should(BeClosed()) }) It("hijacks a bidirectional stream of unknown frame type", func() { id := quic.ConnectionTracingID(1234) frameTypeChan := make(chan FrameType, 1) rt := &SingleDestinationRoundTripper{ Connection: conn, StreamHijacker: func(ft FrameType, connTracingID quic.ConnectionTracingID, _ quic.Stream, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) Expect(connTracingID).To(Equal(id)) frameTypeChan <- ft return true, nil }, } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x41)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.Stream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, id) conn.EXPECT().Context().Return(ctx).AnyTimes() _, err := rt.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(frameTypeChan).Should(Receive(BeEquivalentTo(0x41))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("closes the connection when hijacker didn't hijack a bidirectional stream", func() { frameTypeChan := make(chan FrameType, 1) rt := &SingleDestinationRoundTripper{ Connection: conn, StreamHijacker: func(ft FrameType, _ quic.ConnectionTracingID, _ quic.Stream, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) frameTypeChan <- ft return false, nil }, } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x41)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.Stream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any()).Return(nil).AnyTimes() _, err := rt.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(frameTypeChan).Should(Receive(BeEquivalentTo(0x41))) }) It("closes the connection when hijacker returned error", func() { frameTypeChan := make(chan FrameType, 1) rt := &SingleDestinationRoundTripper{ Connection: conn, StreamHijacker: func(ft FrameType, _ quic.ConnectionTracingID, _ quic.Stream, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) frameTypeChan <- ft return false, errors.New("error in hijacker") }, } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x41)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.Stream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any()).Return(nil).AnyTimes() _, err := rt.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(frameTypeChan).Should(Receive(BeEquivalentTo(0x41))) }) It("handles errors that occur when reading the frame type", func() { testErr := errors.New("test error") unknownStr := mockquic.NewMockStream(mockCtrl) done := make(chan struct{}) rt := &SingleDestinationRoundTripper{ Connection: conn, StreamHijacker: func(ft FrameType, _ quic.ConnectionTracingID, str quic.Stream, e error) (hijacked bool, err error) { defer close(done) Expect(e).To(MatchError(testErr)) Expect(ft).To(BeZero()) Expect(str).To(Equal(unknownStr)) return false, nil }, } unknownStr.EXPECT().Read(gomock.Any()).Return(0, testErr).AnyTimes() conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.Stream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any()).Return(nil).AnyTimes() _, err := rt.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) }) Context("hijacking unidirectional streams", func() { var ( req *http.Request conn *mockquic.MockEarlyConnection settingsFrameWritten chan struct{} ) testDone := make(chan struct{}) BeforeEach(func() { testDone = make(chan struct{}) settingsFrameWritten = make(chan struct{}) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()).Do(func(b []byte) (int, error) { defer GinkgoRecover() close(settingsFrameWritten) return len(b), nil }) conn = mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().HandshakeComplete().Return(handshakeChan) conn.EXPECT().OpenStreamSync(gomock.Any()).Return(nil, errors.New("done")) var err error req, err = http.NewRequest("GET", "https://quic.clemente.io:1337/file1.dat", nil) Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { testDone <- struct{}{} Eventually(settingsFrameWritten).Should(BeClosed()) }) It("hijacks an unidirectional stream of unknown stream type", func() { id := quic.ConnectionTracingID(100) streamTypeChan := make(chan StreamType, 1) rt := &SingleDestinationRoundTripper{ Connection: conn, UniStreamHijacker: func(st StreamType, connTracingID quic.ConnectionTracingID, _ quic.ReceiveStream, err error) bool { Expect(connTracingID).To(Equal(id)) Expect(err).ToNot(HaveOccurred()) streamTypeChan <- st return true }, } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x54)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return unknownStr, nil }) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, id) conn.EXPECT().Context().Return(ctx).AnyTimes() _, err := rt.RoundTrip(req) Expect(err).To(MatchError("done")) Eventually(streamTypeChan).Should(Receive(BeEquivalentTo(0x54))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("handles errors that occur when reading the stream type", func() { testErr := errors.New("test error") done := make(chan struct{}) unknownStr := mockquic.NewMockStream(mockCtrl) rt := &SingleDestinationRoundTripper{ Connection: conn, UniStreamHijacker: func(st StreamType, _ quic.ConnectionTracingID, str quic.ReceiveStream, err error) bool { defer close(done) Expect(st).To(BeZero()) Expect(str).To(Equal(unknownStr)) Expect(err).To(MatchError(testErr)) return true }, } unknownStr.EXPECT().Read(gomock.Any()).Return(0, testErr) conn.EXPECT().AcceptUniStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() _, err := rt.RoundTrip(req) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("cancels reading when hijacker didn't hijack an unidirectional stream", func() { streamTypeChan := make(chan StreamType, 1) rt := &SingleDestinationRoundTripper{ Connection: conn, UniStreamHijacker: func(st StreamType, _ quic.ConnectionTracingID, _ quic.ReceiveStream, err error) bool { Expect(err).ToNot(HaveOccurred()) streamTypeChan <- st return false }, } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x54)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() unknownStr.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeStreamCreationError)) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return unknownStr, nil }) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() _, err := rt.RoundTrip(req) Expect(err).To(MatchError("done")) Eventually(streamTypeChan).Should(Receive(BeEquivalentTo(0x54))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) }) Context("SETTINGS handling", func() { sendSettings := func() { settingsFrameWritten := make(chan struct{}) controlStr := mockquic.NewMockStream(mockCtrl) var buf bytes.Buffer controlStr.EXPECT().Write(gomock.Any()).Do(func(b []byte) (int, error) { defer GinkgoRecover() buf.Write(b) close(settingsFrameWritten) return len(b), nil }) conn := mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().Context().Return(context.Background()) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().OpenStreamSync(gomock.Any()).DoAndReturn(func(context.Context) (quic.Stream, error) { <-settingsFrameWritten return nil, errors.New("test done") }) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-settingsFrameWritten return nil, errors.New("test done") }).AnyTimes() conn.EXPECT().HandshakeComplete().Return(handshakeChan) rt := &SingleDestinationRoundTripper{ Connection: conn, EnableDatagrams: true, } req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req) Expect(err).To(MatchError("test done")) t, err := quicvarint.Read(&buf) Expect(err).ToNot(HaveOccurred()) Expect(t).To(BeEquivalentTo(streamTypeControlStream)) settings, err := parseSettingsFrame(&buf, uint64(buf.Len())) Expect(err).ToNot(HaveOccurred()) Expect(settings.Datagram).To(BeTrue()) } It("receives SETTINGS", func() { sendSettings() done := make(chan struct{}) conn := mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().OpenUniStream().DoAndReturn(func() (quic.SendStream, error) { <-done return nil, errors.New("test done") }).MaxTimes(1) conn.EXPECT().Context().Return(context.Background()) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{ExtendedConnect: true}).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() conn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-done return nil, errors.New("test done") }) rt := &SingleDestinationRoundTripper{Connection: conn} hconn := rt.Start() Eventually(hconn.ReceivedSettings()).Should(BeClosed()) settings := hconn.Settings() Expect(settings.EnableExtendedConnect).To(BeTrue()) // test shutdown conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).MaxTimes(1) close(done) }) It("checks the server's SETTINGS before sending an Extended CONNECT request", func() { sendSettings() done := make(chan struct{}) var wg sync.WaitGroup wg.Add(2) conn := mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().OpenUniStream().DoAndReturn(func() (quic.SendStream, error) { <-done wg.Done() return nil, errors.New("test done") }).MaxTimes(1) conn.EXPECT().Context().Return(context.Background()) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{ExtendedConnect: true}).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() conn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-done wg.Done() return nil, errors.New("test done") }) conn.EXPECT().HandshakeComplete().Return(handshakeChan) conn.EXPECT().Context().Return(context.Background()) conn.EXPECT().OpenStreamSync(gomock.Any()).Return(nil, errors.New("test error")) rt := &SingleDestinationRoundTripper{Connection: conn} _, err := rt.RoundTrip(&http.Request{ Method: http.MethodConnect, Proto: "connect", Host: "localhost", }) Expect(err).To(MatchError("test error")) // test shutdown conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).MaxTimes(1) close(done) wg.Wait() }) It("rejects Extended CONNECT requests if the server doesn't enable it", func() { sendSettings() done := make(chan struct{}) var wg sync.WaitGroup wg.Add(2) conn := mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().Context().Return(context.Background()) conn.EXPECT().OpenUniStream().DoAndReturn(func() (quic.SendStream, error) { <-done wg.Done() return nil, errors.New("test done") }).MaxTimes(1) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{}).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() conn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-done wg.Done() return nil, errors.New("test done") }) conn.EXPECT().HandshakeComplete().Return(handshakeChan) conn.EXPECT().Context().Return(context.Background()) rt := &SingleDestinationRoundTripper{Connection: conn} _, err := rt.RoundTrip(&http.Request{ Method: http.MethodConnect, Proto: "connect", Host: "localhost", }) Expect(err).To(MatchError("http3: server didn't enable Extended CONNECT")) // test shutdown conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).MaxTimes(1) close(done) wg.Wait() }) }) Context("Doing requests", func() { var ( req *http.Request str *mockquic.MockStream conn *mockquic.MockEarlyConnection cl *SingleDestinationRoundTripper settingsFrameWritten chan struct{} ) testDone := make(chan struct{}) decodeHeader := func(str io.Reader) map[string]string { fields := make(map[string]string) decoder := qpack.NewDecoder(nil) fp := frameParser{r: str} frame, err := fp.ParseNext() ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, frame).To(BeAssignableToTypeOf(&headersFrame{})) headersFrame := frame.(*headersFrame) data := make([]byte, headersFrame.Length) _, err = io.ReadFull(str, data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) hfs, err := decoder.DecodeFull(data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) for _, p := range hfs { fields[p.Name] = p.Value } return fields } BeforeEach(func() { settingsFrameWritten = make(chan struct{}) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()).Do(func(b []byte) (int, error) { defer GinkgoRecover() r := bytes.NewReader(b) streamType, err := quicvarint.Read(r) Expect(err).ToNot(HaveOccurred()) Expect(streamType).To(BeEquivalentTo(streamTypeControlStream)) close(settingsFrameWritten) return len(b), nil }) // SETTINGS frame str = mockquic.NewMockStream(mockCtrl) str.EXPECT().Context().Return(context.Background()).AnyTimes() str.EXPECT().StreamID().AnyTimes() conn = mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().Context().Return(context.Background()) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) cl = &SingleDestinationRoundTripper{Connection: conn} var err error req, err = http.NewRequest("GET", "https://quic.clemente.io:1337/file1.dat", nil) Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { testDone <- struct{}{} Eventually(settingsFrameWritten).Should(BeClosed()) }) It("errors if it can't open a request stream", func() { testErr := errors.New("stream open error") conn.EXPECT().OpenStreamSync(context.Background()).Return(nil, testErr) conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).MaxTimes(1) conn.EXPECT().HandshakeComplete().Return(handshakeChan) _, err := cl.RoundTrip(req) Expect(err).To(MatchError(testErr)) }) DescribeTable( "performs a 0-RTT request", func(method, serialized string) { testErr := errors.New("stream open error") req.Method = method // don't EXPECT any calls to HandshakeComplete() conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) buf := &bytes.Buffer{} str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() str.EXPECT().Close() str.EXPECT().CancelWrite(gomock.Any()) str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { return 0, testErr }) _, err := cl.RoundTrip(req) Expect(err).To(MatchError(testErr)) Expect(decodeHeader(buf)).To(HaveKeyWithValue(":method", serialized)) // make sure the request wasn't modified Expect(req.Method).To(Equal(method)) }, Entry("GET", MethodGet0RTT, http.MethodGet), Entry("HEAD", MethodHead0RTT, http.MethodHead), ) It("returns a response", func() { rspBuf := bytes.NewBuffer(encodeResponse(418)) gomock.InOrder( conn.EXPECT().HandshakeComplete().Return(handshakeChan), conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil), conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}), ) str.EXPECT().Write(gomock.Any()).AnyTimes().DoAndReturn(func(p []byte) (int, error) { return len(p), nil }) str.EXPECT().Close() str.EXPECT().Read(gomock.Any()).DoAndReturn(rspBuf.Read).AnyTimes() rsp, err := cl.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Proto).To(Equal("HTTP/3.0")) Expect(rsp.ProtoMajor).To(Equal(3)) Expect(rsp.StatusCode).To(Equal(418)) Expect(rsp.Request).ToNot(BeNil()) }) Context("requests containing a Body", func() { var strBuf *bytes.Buffer BeforeEach(func() { strBuf = &bytes.Buffer{} gomock.InOrder( conn.EXPECT().HandshakeComplete().Return(handshakeChan), conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil), ) body := &mockBody{} body.SetData([]byte("request body")) var err error req, err = http.NewRequest("POST", "https://quic.clemente.io:1337/upload", body) Expect(err).ToNot(HaveOccurred()) str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes() }) It("sends a request", func() { done := make(chan struct{}) gomock.InOrder( str.EXPECT().Close().Do(func() error { close(done); return nil }), // when reading the response errors str.EXPECT().CancelRead(gomock.Any()).MaxTimes(1), str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1), ) // the response body is sent asynchronously, while already reading the response testErr := errors.New("test done") str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { <-done return 0, testErr }) _, err := cl.RoundTrip(req) Expect(err).To(MatchError(testErr)) hfs := decodeHeader(strBuf) Expect(hfs).To(HaveKeyWithValue(":method", "POST")) Expect(hfs).To(HaveKeyWithValue(":path", "/upload")) }) It("doesn't send more bytes than allowed by http.Request.ContentLength", func() { req.ContentLength = 7 var once sync.Once done := make(chan struct{}) str.EXPECT().CancelRead(gomock.Any()) gomock.InOrder( str.EXPECT().CancelWrite(gomock.Any()).Do(func(c quic.StreamErrorCode) { once.Do(func() { Expect(c).To(Equal(quic.StreamErrorCode(ErrCodeRequestCanceled))) close(done) }) }).AnyTimes(), str.EXPECT().Close().MaxTimes(1), str.EXPECT().CancelWrite(gomock.Any()).AnyTimes(), ) str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { <-done return 0, errors.New("done") }) cl.RoundTrip(req) Expect(strBuf.String()).To(ContainSubstring("request")) Expect(strBuf.String()).ToNot(ContainSubstring("request body")) }) It("returns the error that occurred when reading the body", func() { req.Body.(*mockBody).readErr = errors.New("testErr") done := make(chan struct{}) str.EXPECT().CancelRead(gomock.Any()) gomock.InOrder( str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)).Do(func(quic.StreamErrorCode) { close(done) }), str.EXPECT().CancelWrite(gomock.Any()), ) // the response body is sent asynchronously, while already reading the response testErr := errors.New("test done") str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { <-done return 0, testErr }) closed := make(chan struct{}) str.EXPECT().Close().Do(func() error { close(closed); return nil }) _, err := cl.RoundTrip(req) Expect(err).To(MatchError(testErr)) Eventually(closed).Should(BeClosed()) }) It("closes the connection when the first frame is not a HEADERS frame", func() { b := (&dataFrame{Length: 0x42}).Append(nil) conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any()) closed := make(chan struct{}) r := bytes.NewReader(b) str.EXPECT().Close().Do(func() error { close(closed); return nil }) str.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() _, err := cl.RoundTrip(req) Expect(err).To(MatchError("http3: expected first frame to be a HEADERS frame")) Eventually(closed).Should(BeClosed()) }) It("cancels the stream when parsing the headers fails", func() { headerBuf := &bytes.Buffer{} enc := qpack.NewEncoder(headerBuf) Expect(enc.WriteField(qpack.HeaderField{Name: ":method", Value: "GET"})).To(Succeed()) // not a valid response pseudo header Expect(enc.Close()).To(Succeed()) b := (&headersFrame{Length: uint64(headerBuf.Len())}).Append(nil) b = append(b, headerBuf.Bytes()...) r := bytes.NewReader(b) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) closed := make(chan struct{}) str.EXPECT().Close().Do(func() error { close(closed); return nil }) str.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() _, err := cl.RoundTrip(req) Expect(err).To(HaveOccurred()) Eventually(closed).Should(BeClosed()) }) It("cancels the stream when the HEADERS frame is too large", func() { cl.MaxResponseHeaderBytes = 1337 b := (&headersFrame{Length: 1338}).Append(nil) r := bytes.NewReader(b) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) closed := make(chan struct{}) str.EXPECT().Close().Do(func() error { close(closed); return nil }) str.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() _, err := cl.RoundTrip(req) Expect(err).To(MatchError("http3: HEADERS frame too large: 1338 bytes (max: 1337)")) Eventually(closed).Should(BeClosed()) }) It("opens a request stream", func() { cl.Connection.(quic.EarlyConnection).HandshakeComplete() str, err := cl.OpenRequestStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.SendRequestHeader(req)).To(Succeed()) str.Write([]byte("foobar")) d := dataFrame{Length: 6} data := d.Append([]byte{}) data = append(data, []byte("foobar")...) Expect(bytes.Contains(strBuf.Bytes(), data)).To(BeTrue()) }) }) Context("request cancellations", func() { It("cancels a request while waiting for the handshake to complete", func() { ctx, cancel := context.WithCancel(context.Background()) req := req.WithContext(ctx) conn.EXPECT().HandshakeComplete().Return(make(chan struct{})) errChan := make(chan error) go func() { _, err := cl.RoundTrip(req) errChan <- err }() Consistently(errChan).ShouldNot(Receive()) cancel() Eventually(errChan).Should(Receive(MatchError("context canceled"))) }) It("cancels a request while the request is still in flight", func() { ctx, cancel := context.WithCancel(context.Background()) req := req.WithContext(ctx) conn.EXPECT().HandshakeComplete().Return(handshakeChan) conn.EXPECT().OpenStreamSync(ctx).Return(str, nil) buf := &bytes.Buffer{} str.EXPECT().Close().MaxTimes(1) str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write) done := make(chan struct{}) canceled := make(chan struct{}) gomock.InOrder( str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)).Do(func(quic.StreamErrorCode) { close(canceled) }), str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)).Do(func(quic.StreamErrorCode) { close(done) }), ) str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1) str.EXPECT().CancelRead(gomock.Any()).MaxTimes(1) str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { cancel() <-canceled return 0, errors.New("test done") }) _, err := cl.RoundTrip(req) Expect(err).To(MatchError(context.Canceled)) Eventually(done).Should(BeClosed()) }) It("cancels a request after the response arrived", func() { rspBuf := bytes.NewBuffer(encodeResponse(404)) ctx, cancel := context.WithCancel(context.Background()) req := req.WithContext(ctx) conn.EXPECT().HandshakeComplete().Return(handshakeChan) conn.EXPECT().OpenStreamSync(ctx).Return(str, nil) conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}) buf := &bytes.Buffer{} str.EXPECT().Close().MaxTimes(1) done := make(chan struct{}) str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write) str.EXPECT().Read(gomock.Any()).DoAndReturn(rspBuf.Read).AnyTimes() str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeRequestCanceled)) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)).Do(func(quic.StreamErrorCode) { close(done) }) _, err := cl.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) cancel() Eventually(done).Should(BeClosed()) }) }) Context("gzip compression", func() { BeforeEach(func() { conn.EXPECT().HandshakeComplete().Return(handshakeChan) }) It("adds the gzip header to requests", func() { conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) buf := &bytes.Buffer{} str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write) gomock.InOrder( str.EXPECT().Close(), // when the Read errors str.EXPECT().CancelRead(gomock.Any()).MaxTimes(1), str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1), ) testErr := errors.New("test done") str.EXPECT().Read(gomock.Any()).Return(0, testErr) _, err := cl.RoundTrip(req) Expect(err).To(MatchError(testErr)) hfs := decodeHeader(buf) Expect(hfs).To(HaveKeyWithValue("accept-encoding", "gzip")) }) It("doesn't add gzip if the header disable it", func() { client := &SingleDestinationRoundTripper{ Connection: conn, DisableCompression: true, } conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) buf := &bytes.Buffer{} str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write) gomock.InOrder( str.EXPECT().Close(), // when the Read errors str.EXPECT().CancelRead(gomock.Any()).MaxTimes(1), str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1), ) testErr := errors.New("test done") str.EXPECT().Read(gomock.Any()).Return(0, testErr) _, err := client.RoundTrip(req) Expect(err).To(MatchError(testErr)) hfs := decodeHeader(buf) Expect(hfs).ToNot(HaveKey("accept-encoding")) }) It("decompresses the response", func() { conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}) buf := &bytes.Buffer{} rstr := mockquic.NewMockStream(mockCtrl) rstr.EXPECT().StreamID().AnyTimes() rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes() rw := newResponseWriter(newStream(rstr, nil, nil), nil, false, nil) rw.Header().Set("Content-Encoding", "gzip") gz := gzip.NewWriter(rw) gz.Write([]byte("gzipped response")) gz.Close() rw.Flush() str.EXPECT().Write(gomock.Any()).AnyTimes().DoAndReturn(func(p []byte) (int, error) { return len(p), nil }) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() str.EXPECT().Close() rsp, err := cl.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(rsp.ContentLength).To(BeEquivalentTo(-1)) Expect(string(data)).To(Equal("gzipped response")) Expect(rsp.Header.Get("Content-Encoding")).To(BeEmpty()) Expect(rsp.Uncompressed).To(BeTrue()) }) It("only decompresses the response if the response contains the right content-encoding header", func() { conn.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}) buf := &bytes.Buffer{} rstr := mockquic.NewMockStream(mockCtrl) rstr.EXPECT().StreamID().AnyTimes() rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes() rw := newResponseWriter(newStream(rstr, nil, nil), nil, false, nil) rw.Write([]byte("not gzipped")) rw.Flush() str.EXPECT().Write(gomock.Any()).AnyTimes().DoAndReturn(func(p []byte) (int, error) { return len(p), nil }) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() str.EXPECT().Close() rsp, err := cl.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(string(data)).To(Equal("not gzipped")) Expect(rsp.Header.Get("Content-Encoding")).To(BeEmpty()) }) }) Context("1xx status code", func() { It("continues to read next header if code is 103", func() { var ( cnt int status int hdr textproto.MIMEHeader ) header1 := "; rel=preload; as=style" header2 := "; rel=preload; as=script" ctx := httptrace.WithClientTrace(req.Context(), &httptrace.ClientTrace{ Got1xxResponse: func(code int, header textproto.MIMEHeader) error { cnt++ status = code hdr = header return nil }, }) req := req.WithContext(ctx) rspBuf := bytes.NewBuffer(encodeResponse(103)) gomock.InOrder( conn.EXPECT().HandshakeComplete().Return(handshakeChan), conn.EXPECT().OpenStreamSync(ctx).Return(str, nil), conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}), ) str.EXPECT().Write(gomock.Any()).AnyTimes().DoAndReturn(func(p []byte) (int, error) { return len(p), nil }) str.EXPECT().Close() str.EXPECT().Read(gomock.Any()).DoAndReturn(rspBuf.Read).AnyTimes() rsp, err := cl.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Proto).To(Equal("HTTP/3.0")) Expect(rsp.ProtoMajor).To(Equal(3)) Expect(rsp.StatusCode).To(Equal(200)) Expect(rsp.Header).To(HaveKeyWithValue("Link", []string{header1, header2})) Expect(status).To(Equal(103)) Expect(cnt).To(Equal(1)) Expect(hdr).To(HaveKeyWithValue("Link", []string{header1, header2})) Expect(rsp.Request).ToNot(BeNil()) }) It("doesn't continue to read next header if code is a terminal status", func() { cnt := 0 status := 0 ctx := httptrace.WithClientTrace(req.Context(), &httptrace.ClientTrace{ Got1xxResponse: func(code int, header textproto.MIMEHeader) error { cnt++ status = code return nil }, }) req := req.WithContext(ctx) rspBuf := bytes.NewBuffer(encodeResponse(101)) gomock.InOrder( conn.EXPECT().HandshakeComplete().Return(handshakeChan), conn.EXPECT().OpenStreamSync(ctx).Return(str, nil), conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}), ) str.EXPECT().Write(gomock.Any()).AnyTimes().DoAndReturn(func(p []byte) (int, error) { return len(p), nil }) str.EXPECT().Close() str.EXPECT().Read(gomock.Any()).DoAndReturn(rspBuf.Read).AnyTimes() rsp, err := cl.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Proto).To(Equal("HTTP/3.0")) Expect(rsp.ProtoMajor).To(Equal(3)) Expect(rsp.StatusCode).To(Equal(101)) Expect(status).To(Equal(0)) Expect(cnt).To(Equal(0)) Expect(rsp.Request).ToNot(BeNil()) }) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/conn.go000066400000000000000000000220761465664453100231440ustar00rootroot00000000000000package http3 import ( "context" "fmt" "log/slog" "net" "sync" "sync/atomic" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" ) // Connection is an HTTP/3 connection. // It has all methods from the quic.Connection expect for AcceptStream, AcceptUniStream, // SendDatagram and ReceiveDatagram. type Connection interface { OpenStream() (quic.Stream, error) OpenStreamSync(context.Context) (quic.Stream, error) OpenUniStream() (quic.SendStream, error) OpenUniStreamSync(context.Context) (quic.SendStream, error) LocalAddr() net.Addr RemoteAddr() net.Addr CloseWithError(quic.ApplicationErrorCode, string) error Context() context.Context ConnectionState() quic.ConnectionState // ReceivedSettings returns a channel that is closed once the client's SETTINGS frame was received. ReceivedSettings() <-chan struct{} // Settings returns the settings received on this connection. Settings() *Settings } type connection struct { quic.Connection ctx context.Context perspective protocol.Perspective logger *slog.Logger enableDatagrams bool decoder *qpack.Decoder streamMx sync.Mutex streams map[protocol.StreamID]*datagrammer settings *Settings receivedSettings chan struct{} idleTimeout time.Duration idleTimer *time.Timer } func newConnection( ctx context.Context, quicConn quic.Connection, enableDatagrams bool, perspective protocol.Perspective, logger *slog.Logger, idleTimeout time.Duration, ) *connection { c := &connection{ ctx: ctx, Connection: quicConn, perspective: perspective, logger: logger, idleTimeout: idleTimeout, enableDatagrams: enableDatagrams, decoder: qpack.NewDecoder(func(hf qpack.HeaderField) {}), receivedSettings: make(chan struct{}), streams: make(map[protocol.StreamID]*datagrammer), } if idleTimeout > 0 { c.idleTimer = time.AfterFunc(idleTimeout, c.onIdleTimer) } return c } func (c *connection) onIdleTimer() { c.CloseWithError(quic.ApplicationErrorCode(ErrCodeNoError), "idle timeout") } func (c *connection) clearStream(id quic.StreamID) { c.streamMx.Lock() defer c.streamMx.Unlock() delete(c.streams, id) if c.idleTimeout > 0 && len(c.streams) == 0 { c.idleTimer.Reset(c.idleTimeout) } } func (c *connection) openRequestStream( ctx context.Context, requestWriter *requestWriter, reqDone chan<- struct{}, disableCompression bool, maxHeaderBytes uint64, ) (*requestStream, error) { str, err := c.Connection.OpenStreamSync(ctx) if err != nil { return nil, err } datagrams := newDatagrammer(func(b []byte) error { return c.sendDatagram(str.StreamID(), b) }) c.streamMx.Lock() c.streams[str.StreamID()] = datagrams c.streamMx.Unlock() qstr := newStateTrackingStream(str, c, datagrams) hstr := newStream(qstr, c, datagrams) return newRequestStream(hstr, requestWriter, reqDone, c.decoder, disableCompression, maxHeaderBytes), nil } func (c *connection) acceptStream(ctx context.Context) (quic.Stream, *datagrammer, error) { str, err := c.AcceptStream(ctx) if err != nil { return nil, nil, err } datagrams := newDatagrammer(func(b []byte) error { return c.sendDatagram(str.StreamID(), b) }) if c.perspective == protocol.PerspectiveServer { strID := str.StreamID() c.streamMx.Lock() c.streams[strID] = datagrams if c.idleTimeout > 0 { if len(c.streams) == 1 { c.idleTimer.Stop() } } c.streamMx.Unlock() str = newStateTrackingStream(str, c, datagrams) } return str, datagrams, nil } func (c *connection) CloseWithError(code quic.ApplicationErrorCode, msg string) error { if c.idleTimer != nil { c.idleTimer.Stop() } return c.Connection.CloseWithError(code, msg) } func (c *connection) HandleUnidirectionalStreams(hijack func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool)) { var ( rcvdControlStr atomic.Bool rcvdQPACKEncoderStr atomic.Bool rcvdQPACKDecoderStr atomic.Bool ) for { str, err := c.Connection.AcceptUniStream(context.Background()) if err != nil { if c.logger != nil { c.logger.Debug("accepting unidirectional stream failed", "error", err) } return } go func(str quic.ReceiveStream) { streamType, err := quicvarint.Read(quicvarint.NewReader(str)) if err != nil { id := c.Connection.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) if hijack != nil && hijack(StreamType(streamType), id, str, err) { return } if c.logger != nil { c.logger.Debug("reading stream type on stream failed", "stream ID", str.StreamID(), "error", err) } return } // We're only interested in the control stream here. switch streamType { case streamTypeControlStream: case streamTypeQPACKEncoderStream: if isFirst := rcvdQPACKEncoderStr.CompareAndSwap(false, true); !isFirst { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate QPACK encoder stream") } // Our QPACK implementation doesn't use the dynamic table yet. return case streamTypeQPACKDecoderStream: if isFirst := rcvdQPACKDecoderStr.CompareAndSwap(false, true); !isFirst { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate QPACK decoder stream") } // Our QPACK implementation doesn't use the dynamic table yet. return case streamTypePushStream: switch c.perspective { case protocol.PerspectiveClient: // we never increased the Push ID, so we don't expect any push streams c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeIDError), "") case protocol.PerspectiveServer: // only the server can push c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "") } return default: if hijack != nil { if hijack( StreamType(streamType), c.Connection.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID), str, nil, ) { return } } str.CancelRead(quic.StreamErrorCode(ErrCodeStreamCreationError)) return } // Only a single control stream is allowed. if isFirstControlStr := rcvdControlStr.CompareAndSwap(false, true); !isFirstControlStr { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate control stream") return } fp := &frameParser{conn: c.Connection, r: str} f, err := fp.ParseNext() if err != nil { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameError), "") return } sf, ok := f.(*settingsFrame) if !ok { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeMissingSettings), "") return } c.settings = &Settings{ EnableDatagrams: sf.Datagram, EnableExtendedConnect: sf.ExtendedConnect, Other: sf.Other, } close(c.receivedSettings) if !sf.Datagram { return } // If datagram support was enabled on our side as well as on the server side, // we can expect it to have been negotiated both on the transport and on the HTTP/3 layer. // Note: ConnectionState() will block until the handshake is complete (relevant when using 0-RTT). if c.enableDatagrams && !c.Connection.ConnectionState().SupportsDatagrams { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeSettingsError), "missing QUIC Datagram support") return } go func() { if err := c.receiveDatagrams(); err != nil { if c.logger != nil { c.logger.Debug("receiving datagrams failed", "error", err) } } }() }(str) } } func (c *connection) sendDatagram(streamID protocol.StreamID, b []byte) error { // TODO: this creates a lot of garbage and an additional copy data := make([]byte, 0, len(b)+8) data = quicvarint.Append(data, uint64(streamID/4)) data = append(data, b...) return c.Connection.SendDatagram(data) } func (c *connection) receiveDatagrams() error { for { b, err := c.Connection.ReceiveDatagram(context.Background()) if err != nil { return err } quarterStreamID, n, err := quicvarint.Parse(b) if err != nil { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeDatagramError), "") return fmt.Errorf("could not read quarter stream id: %w", err) } if quarterStreamID > maxQuarterStreamID { c.Connection.CloseWithError(quic.ApplicationErrorCode(ErrCodeDatagramError), "") return fmt.Errorf("invalid quarter stream id: %w", err) } streamID := protocol.StreamID(4 * quarterStreamID) c.streamMx.Lock() dg, ok := c.streams[streamID] if !ok { c.streamMx.Unlock() return nil } c.streamMx.Unlock() dg.enqueue(b[n:]) } } // ReceivedSettings returns a channel that is closed once the peer's SETTINGS frame was received. func (c *connection) ReceivedSettings() <-chan struct{} { return c.receivedSettings } // Settings returns the settings received on this connection. // It is only valid to call this function after the channel returned by ReceivedSettings was closed. func (c *connection) Settings() *Settings { return c.settings } func (c *connection) Context() context.Context { return c.ctx } golang-github-lucas-clemente-quic-go-0.46.0/http3/conn_test.go000066400000000000000000000365711465664453100242100ustar00rootroot00000000000000package http3 import ( "bytes" "context" "errors" "fmt" "time" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Connection", func() { Context("control stream handling", func() { It("parses the SETTINGS frame", func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) qconn.EXPECT().ReceiveDatagram(gomock.Any()).Return(nil, errors.New("no datagrams")) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveServer, nil, 0, ) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{ Datagram: true, ExtendedConnect: true, Other: map[uint64]uint64{1337: 42}, }).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(conn.ReceivedSettings()).Should(BeClosed()) Expect(conn.Settings().EnableDatagrams).To(BeTrue()) Expect(conn.Settings().EnableExtendedConnect).To(BeTrue()) Expect(conn.Settings().Other).To(HaveKeyWithValue(uint64(1337), uint64(42))) Eventually(done).Should(BeClosed()) }) It("rejects duplicate control streams", func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveServer, nil, 0, ) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{}).Append(b) r1 := bytes.NewReader(b) controlStr1 := mockquic.NewMockStream(mockCtrl) controlStr1.EXPECT().Read(gomock.Any()).DoAndReturn(r1.Read).AnyTimes() r2 := bytes.NewReader(b) controlStr2 := mockquic.NewMockStream(mockCtrl) controlStr2.EXPECT().Read(gomock.Any()).DoAndReturn(r2.Read).AnyTimes() done := make(chan struct{}) closed := make(chan struct{}) qconn.EXPECT().CloseWithError(qerr.ApplicationErrorCode(ErrCodeStreamCreationError), "duplicate control stream").Do(func(qerr.ApplicationErrorCode, string) error { close(closed) return nil }) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr1, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr2, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(closed).Should(BeClosed()) Eventually(done).Should(BeClosed()) }) for _, t := range []uint64{streamTypeQPACKEncoderStream, streamTypeQPACKDecoderStream} { streamType := t name := "encoder" if streamType == streamTypeQPACKDecoderStream { name = "decoder" } It(fmt.Sprintf("ignores the QPACK %s streams", name), func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveClient, nil, 0, ) buf := bytes.NewBuffer(quicvarint.Append(nil, streamType)) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(str, nil) testDone := make(chan struct{}) qconn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to str.CancelRead close(testDone) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) }) It(fmt.Sprintf("rejects duplicate QPACK %s streams", name), func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveClient, nil, 0, ) buf := bytes.NewBuffer(quicvarint.Append(nil, streamType)) str1 := mockquic.NewMockStream(mockCtrl) str1.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() buf2 := bytes.NewBuffer(quicvarint.Append(nil, streamType)) str2 := mockquic.NewMockStream(mockCtrl) str2.EXPECT().Read(gomock.Any()).DoAndReturn(buf2.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(str1, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(str2, nil) testDone := make(chan struct{}) qconn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) qconn.EXPECT().CloseWithError(qerr.ApplicationErrorCode(ErrCodeStreamCreationError), gomock.Any()).Do(func(qerr.ApplicationErrorCode, string) error { close(testDone) return nil }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) }) } It("resets streams other than the control stream and the QPACK streams", func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveServer, nil, 0, ) buf := bytes.NewBuffer(quicvarint.Append(nil, 0x1337)) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() reset := make(chan struct{}) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeStreamCreationError)).Do(func(quic.StreamErrorCode) { close(reset) }) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(str, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) Eventually(reset).Should(BeClosed()) }) It("errors when the first frame on the control stream is not a SETTINGS frame", func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveServer, nil, 0, ) b := quicvarint.Append(nil, streamTypeControlStream) b = (&dataFrame{}).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) closed := make(chan struct{}) qconn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeMissingSettings), gomock.Any()).Do(func(quic.ApplicationErrorCode, string) error { close(closed) return nil }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) Eventually(closed).Should(BeClosed()) }) It("errors when parsing the frame on the control stream fails", func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, protocol.PerspectiveServer, nil, 0, ) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{}).Append(b) r := bytes.NewReader(b[:len(b)-1]) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) closed := make(chan struct{}) qconn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameError), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) error { close(closed) return nil }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) Eventually(closed).Should(BeClosed()) }) for _, pers := range []protocol.Perspective{protocol.PerspectiveServer, protocol.PerspectiveClient} { pers := pers expectedErr := ErrCodeIDError if pers == protocol.PerspectiveClient { expectedErr = ErrCodeStreamCreationError } It(fmt.Sprintf("errors when parsing the %s opens a push stream", pers), func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, false, pers.Opposite(), nil, 0, ) buf := bytes.NewBuffer(quicvarint.Append(nil, streamTypePushStream)) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) closed := make(chan struct{}) qconn.EXPECT().CloseWithError(quic.ApplicationErrorCode(expectedErr), gomock.Any()).Do(func(quic.ApplicationErrorCode, string) error { close(closed) return nil }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) Eventually(closed).Should(BeClosed()) }) } It("errors when the server advertises datagram support (and we enabled support for it)", func() { qconn := mockquic.NewMockEarlyConnection(mockCtrl) conn := newConnection( context.Background(), qconn, true, protocol.PerspectiveClient, nil, 0, ) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{Datagram: true}).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")) qconn.EXPECT().ConnectionState().Return(quic.ConnectionState{SupportsDatagrams: false}) closed := make(chan struct{}) qconn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeSettingsError), "missing QUIC Datagram support").Do(func(quic.ApplicationErrorCode, string) error { close(closed) return nil }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) Eventually(closed).Should(BeClosed()) }) }) Context("datagram handling", func() { var ( qconn *mockquic.MockEarlyConnection conn *connection ) BeforeEach(func() { qconn = mockquic.NewMockEarlyConnection(mockCtrl) conn = newConnection( context.Background(), qconn, true, protocol.PerspectiveClient, nil, 0, ) b := quicvarint.Append(nil, streamTypeControlStream) b = (&settingsFrame{Datagram: true}).Append(b) r := bytes.NewReader(b) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes() qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(controlStr, nil).MaxTimes(1) qconn.EXPECT().AcceptUniStream(gomock.Any()).Return(nil, errors.New("test done")).MaxTimes(1) qconn.EXPECT().ConnectionState().Return(quic.ConnectionState{SupportsDatagrams: true}).MaxTimes(1) }) It("closes the connection if it can't parse the quarter stream ID", func() { qconn.EXPECT().ReceiveDatagram(gomock.Any()).Return([]byte{128}, nil) // return an invalid varint done := make(chan struct{}) qconn.EXPECT().CloseWithError(qerr.ApplicationErrorCode(ErrCodeDatagramError), gomock.Any()).Do(func(qerr.ApplicationErrorCode, string) error { close(done) return nil }) go func() { defer GinkgoRecover() conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) }) It("closes the connection if the quarter stream ID is invalid", func() { b := quicvarint.Append([]byte{}, maxQuarterStreamID+1) qconn.EXPECT().ReceiveDatagram(gomock.Any()).Return(b, nil) done := make(chan struct{}) qconn.EXPECT().CloseWithError(qerr.ApplicationErrorCode(ErrCodeDatagramError), gomock.Any()).Do(func(qerr.ApplicationErrorCode, string) error { close(done) return nil }) go func() { defer GinkgoRecover() conn.HandleUnidirectionalStreams(nil) }() Eventually(done).Should(BeClosed()) }) It("drops datagrams for non-existent streams", func() { const strID = 4 // first deliver the datagram... b := quicvarint.Append([]byte{}, strID/4) b = append(b, []byte("foobar")...) delivered := make(chan struct{}) qconn.EXPECT().ReceiveDatagram(gomock.Any()).DoAndReturn(func(context.Context) ([]byte, error) { close(delivered) return b, nil }) go func() { defer GinkgoRecover() conn.HandleUnidirectionalStreams(nil) }() Eventually(delivered).Should(BeClosed()) // ... then open the stream qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().Return(strID).MinTimes(1) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() qconn.EXPECT().OpenStreamSync(gomock.Any()).Return(qstr, nil) str, err := conn.openRequestStream(context.Background(), nil, nil, true, 1000) Expect(err).ToNot(HaveOccurred()) ctx, cancel := context.WithCancel(context.Background()) cancel() _, err = str.ReceiveDatagram(ctx) Expect(err).To(MatchError(context.Canceled)) }) It("delivers datagrams for existing streams", func() { const strID = 4 // first open the stream... qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().Return(strID).MinTimes(1) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() qconn.EXPECT().OpenStreamSync(gomock.Any()).Return(qstr, nil) str, err := conn.openRequestStream(context.Background(), nil, nil, true, 1000) Expect(err).ToNot(HaveOccurred()) // ... then deliver the datagram b := quicvarint.Append([]byte{}, strID/4) b = append(b, []byte("foobar")...) qconn.EXPECT().ReceiveDatagram(gomock.Any()).Return(b, nil) qconn.EXPECT().ReceiveDatagram(gomock.Any()).Return(nil, errors.New("test done")) go func() { defer GinkgoRecover() conn.HandleUnidirectionalStreams(nil) }() data, err := str.ReceiveDatagram(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) }) It("sends datagrams", func() { const strID = 404 expected := quicvarint.Append([]byte{}, strID/4) expected = append(expected, []byte("foobar")...) testErr := errors.New("test error") qconn.EXPECT().SendDatagram(expected).Return(testErr) Expect(conn.sendDatagram(strID, []byte("foobar"))).To(MatchError(testErr)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/datagram.go000066400000000000000000000031071465664453100237610ustar00rootroot00000000000000package http3 import ( "context" "sync" ) const maxQuarterStreamID = 1<<60 - 1 const streamDatagramQueueLen = 32 type datagrammer struct { sendDatagram func([]byte) error hasData chan struct{} queue [][]byte // TODO: use a ring buffer mx sync.Mutex sendErr error receiveErr error } func newDatagrammer(sendDatagram func([]byte) error) *datagrammer { return &datagrammer{ sendDatagram: sendDatagram, hasData: make(chan struct{}, 1), } } func (d *datagrammer) SetReceiveError(err error) { d.mx.Lock() defer d.mx.Unlock() d.receiveErr = err d.signalHasData() } func (d *datagrammer) SetSendError(err error) { d.mx.Lock() defer d.mx.Unlock() d.sendErr = err } func (d *datagrammer) Send(b []byte) error { d.mx.Lock() sendErr := d.sendErr d.mx.Unlock() if sendErr != nil { return sendErr } return d.sendDatagram(b) } func (d *datagrammer) signalHasData() { select { case d.hasData <- struct{}{}: default: } } func (d *datagrammer) enqueue(data []byte) { d.mx.Lock() defer d.mx.Unlock() if d.receiveErr != nil { return } if len(d.queue) >= streamDatagramQueueLen { return } d.queue = append(d.queue, data) d.signalHasData() } func (d *datagrammer) Receive(ctx context.Context) ([]byte, error) { start: d.mx.Lock() if len(d.queue) >= 1 { data := d.queue[0] d.queue = d.queue[1:] d.mx.Unlock() return data, nil } if receiveErr := d.receiveErr; receiveErr != nil { d.mx.Unlock() return nil, receiveErr } d.mx.Unlock() select { case <-ctx.Done(): return nil, context.Cause(ctx) case <-d.hasData: } goto start } golang-github-lucas-clemente-quic-go-0.46.0/http3/datagram_test.go000066400000000000000000000037561465664453100250320ustar00rootroot00000000000000package http3 import ( "context" "errors" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Datagrams", func() { It("receives a datagram", func() { dg := newDatagrammer(nil) dg.enqueue([]byte("foobar")) data, err := dg.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) }) It("queues up to 32 datagrams", func() { dg := newDatagrammer(nil) for i := 0; i < streamDatagramQueueLen+1; i++ { dg.enqueue([]byte{uint8(i)}) } for i := 0; i < streamDatagramQueueLen; i++ { data, err := dg.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data[0]).To(BeEquivalentTo(i)) } ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := dg.Receive(ctx) Expect(err).To(MatchError(context.Canceled)) }) It("blocks until a new datagram is received", func() { dg := newDatagrammer(nil) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) data, err := dg.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) }() Consistently(done, 50*time.Millisecond).ShouldNot(BeClosed()) dg.enqueue([]byte("foobar")) Eventually(done).Should(BeClosed()) }) It("drops datagrams when the stream's receive side is closed", func() { dg := newDatagrammer(nil) dg.enqueue([]byte("foo")) testErr := errors.New("test error") dg.SetReceiveError(testErr) dg.enqueue([]byte("bar")) data, err := dg.Receive(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foo"))) _, err = dg.Receive(context.Background()) Expect(err).To(MatchError(testErr)) }) It("sends datagrams", func() { var sent []byte testErr := errors.New("test error") dg := newDatagrammer(func(b []byte) error { sent = b return testErr }) Expect(dg.Send([]byte("foobar"))).To(MatchError(testErr)) Expect(sent).To(Equal([]byte("foobar"))) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/error.go000066400000000000000000000021341465664453100233310ustar00rootroot00000000000000package http3 import ( "errors" "fmt" "github.com/quic-go/quic-go" ) // Error is returned from the round tripper (for HTTP clients) // and inside the HTTP handler (for HTTP servers) if an HTTP/3 error occurs. // See section 8 of RFC 9114. type Error struct { Remote bool ErrorCode ErrCode ErrorMessage string } var _ error = &Error{} func (e *Error) Error() string { s := e.ErrorCode.string() if s == "" { s = fmt.Sprintf("H3 error (%#x)", uint64(e.ErrorCode)) } // Usually errors are remote. Only make it explicit for local errors. if !e.Remote { s += " (local)" } if e.ErrorMessage != "" { s += ": " + e.ErrorMessage } return s } func maybeReplaceError(err error) error { if err == nil { return nil } var ( e Error strErr *quic.StreamError appErr *quic.ApplicationError ) switch { default: return err case errors.As(err, &strErr): e.Remote = strErr.Remote e.ErrorCode = ErrCode(strErr.ErrorCode) case errors.As(err, &appErr): e.Remote = appErr.Remote e.ErrorCode = ErrCode(appErr.ErrorCode) e.ErrorMessage = appErr.ErrorMessage } return &e } golang-github-lucas-clemente-quic-go-0.46.0/http3/error_codes.go000066400000000000000000000042341465664453100245110ustar00rootroot00000000000000package http3 import ( "fmt" "github.com/quic-go/quic-go" ) type ErrCode quic.ApplicationErrorCode const ( ErrCodeNoError ErrCode = 0x100 ErrCodeGeneralProtocolError ErrCode = 0x101 ErrCodeInternalError ErrCode = 0x102 ErrCodeStreamCreationError ErrCode = 0x103 ErrCodeClosedCriticalStream ErrCode = 0x104 ErrCodeFrameUnexpected ErrCode = 0x105 ErrCodeFrameError ErrCode = 0x106 ErrCodeExcessiveLoad ErrCode = 0x107 ErrCodeIDError ErrCode = 0x108 ErrCodeSettingsError ErrCode = 0x109 ErrCodeMissingSettings ErrCode = 0x10a ErrCodeRequestRejected ErrCode = 0x10b ErrCodeRequestCanceled ErrCode = 0x10c ErrCodeRequestIncomplete ErrCode = 0x10d ErrCodeMessageError ErrCode = 0x10e ErrCodeConnectError ErrCode = 0x10f ErrCodeVersionFallback ErrCode = 0x110 ErrCodeDatagramError ErrCode = 0x33 ) func (e ErrCode) String() string { s := e.string() if s != "" { return s } return fmt.Sprintf("unknown error code: %#x", uint16(e)) } func (e ErrCode) string() string { switch e { case ErrCodeNoError: return "H3_NO_ERROR" case ErrCodeGeneralProtocolError: return "H3_GENERAL_PROTOCOL_ERROR" case ErrCodeInternalError: return "H3_INTERNAL_ERROR" case ErrCodeStreamCreationError: return "H3_STREAM_CREATION_ERROR" case ErrCodeClosedCriticalStream: return "H3_CLOSED_CRITICAL_STREAM" case ErrCodeFrameUnexpected: return "H3_FRAME_UNEXPECTED" case ErrCodeFrameError: return "H3_FRAME_ERROR" case ErrCodeExcessiveLoad: return "H3_EXCESSIVE_LOAD" case ErrCodeIDError: return "H3_ID_ERROR" case ErrCodeSettingsError: return "H3_SETTINGS_ERROR" case ErrCodeMissingSettings: return "H3_MISSING_SETTINGS" case ErrCodeRequestRejected: return "H3_REQUEST_REJECTED" case ErrCodeRequestCanceled: return "H3_REQUEST_CANCELLED" case ErrCodeRequestIncomplete: return "H3_INCOMPLETE_REQUEST" case ErrCodeMessageError: return "H3_MESSAGE_ERROR" case ErrCodeConnectError: return "H3_CONNECT_ERROR" case ErrCodeVersionFallback: return "H3_VERSION_FALLBACK" case ErrCodeDatagramError: return "H3_DATAGRAM_ERROR" default: return "" } } golang-github-lucas-clemente-quic-go-0.46.0/http3/error_codes_test.go000066400000000000000000000022461465664453100255510ustar00rootroot00000000000000package http3 import ( "go/ast" "go/parser" "go/token" "path" "runtime" "strconv" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("error codes", func() { It("has a string representation for every error code", func() { // We parse the error code file, extract all constants, and verify that // each of them has a string version. Go FTW! _, thisfile, _, ok := runtime.Caller(0) if !ok { panic("Failed to get current frame") } filename := path.Join(path.Dir(thisfile), "error_codes.go") fileAst, err := parser.ParseFile(token.NewFileSet(), filename, nil, 0) Expect(err).NotTo(HaveOccurred()) constSpecs := fileAst.Decls[2].(*ast.GenDecl).Specs Expect(len(constSpecs)).To(BeNumerically(">", 4)) // at time of writing for _, c := range constSpecs { valString := c.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value val, err := strconv.ParseInt(valString, 0, 64) Expect(err).NotTo(HaveOccurred()) Expect(ErrCode(val).String()).ToNot(Equal("unknown error code")) } }) It("has a string representation for unknown error codes", func() { Expect(ErrCode(0x1337).String()).To(Equal("unknown error code: 0x1337")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/error_test.go000066400000000000000000000024161465664453100243730ustar00rootroot00000000000000package http3 import ( "errors" "github.com/quic-go/quic-go" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("HTTP/3 errors", func() { It("converts", func() { Expect(maybeReplaceError(nil)).To(BeNil()) Expect(maybeReplaceError(errors.New("foobar"))).To(MatchError("foobar")) Expect(maybeReplaceError(&quic.StreamError{ ErrorCode: 1337, Remote: true, })).To(Equal(&Error{ Remote: true, ErrorCode: 1337, })) Expect(maybeReplaceError(&quic.ApplicationError{ ErrorCode: 42, Remote: true, ErrorMessage: "foobar", })).To(Equal(&Error{ Remote: true, ErrorCode: 42, ErrorMessage: "foobar", })) }) It("has a string representation", func() { Expect((&Error{ErrorCode: 0x10c, Remote: true}).Error()).To(Equal("H3_REQUEST_CANCELLED")) Expect((&Error{ErrorCode: 0x10c, Remote: true, ErrorMessage: "foobar"}).Error()).To(Equal("H3_REQUEST_CANCELLED: foobar")) Expect((&Error{ErrorCode: 0x10c, Remote: false}).Error()).To(Equal("H3_REQUEST_CANCELLED (local)")) Expect((&Error{ErrorCode: 0x10c, Remote: false, ErrorMessage: "foobar"}).Error()).To(Equal("H3_REQUEST_CANCELLED (local): foobar")) Expect((&Error{ErrorCode: 0x1337, Remote: true}).Error()).To(Equal("H3 error (0x1337)")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/frames.go000066400000000000000000000112271465664453100234600ustar00rootroot00000000000000package http3 import ( "bytes" "errors" "fmt" "io" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/quicvarint" ) // FrameType is the frame type of a HTTP/3 frame type FrameType uint64 type unknownFrameHandlerFunc func(FrameType, error) (processed bool, err error) type frame interface{} var errHijacked = errors.New("hijacked") type frameParser struct { r io.Reader conn quic.Connection unknownFrameHandler unknownFrameHandlerFunc } func (p *frameParser) ParseNext() (frame, error) { qr := quicvarint.NewReader(p.r) for { t, err := quicvarint.Read(qr) if err != nil { if p.unknownFrameHandler != nil { hijacked, err := p.unknownFrameHandler(0, err) if err != nil { return nil, err } if hijacked { return nil, errHijacked } } return nil, err } // Call the unknownFrameHandler for frames not defined in the HTTP/3 spec if t > 0xd && p.unknownFrameHandler != nil { hijacked, err := p.unknownFrameHandler(FrameType(t), nil) if err != nil { return nil, err } if hijacked { return nil, errHijacked } // If the unknownFrameHandler didn't process the frame, it is our responsibility to skip it. } l, err := quicvarint.Read(qr) if err != nil { return nil, err } switch t { case 0x0: return &dataFrame{Length: l}, nil case 0x1: return &headersFrame{Length: l}, nil case 0x4: return parseSettingsFrame(p.r, l) case 0x3: // CANCEL_PUSH case 0x5: // PUSH_PROMISE case 0x7: // GOAWAY case 0xd: // MAX_PUSH_ID case 0x2, 0x6, 0x8, 0x9: p.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") return nil, fmt.Errorf("http3: reserved frame type: %d", t) } // skip over unknown frames if _, err := io.CopyN(io.Discard, qr, int64(l)); err != nil { return nil, err } } } type dataFrame struct { Length uint64 } func (f *dataFrame) Append(b []byte) []byte { b = quicvarint.Append(b, 0x0) return quicvarint.Append(b, f.Length) } type headersFrame struct { Length uint64 } func (f *headersFrame) Append(b []byte) []byte { b = quicvarint.Append(b, 0x1) return quicvarint.Append(b, f.Length) } const ( // Extended CONNECT, RFC 9220 settingExtendedConnect = 0x8 // HTTP Datagrams, RFC 9297 settingDatagram = 0x33 ) type settingsFrame struct { Datagram bool // HTTP Datagrams, RFC 9297 ExtendedConnect bool // Extended CONNECT, RFC 9220 Other map[uint64]uint64 // all settings that we don't explicitly recognize } func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) { if l > 8*(1<<10) { return nil, fmt.Errorf("unexpected size for SETTINGS frame: %d", l) } buf := make([]byte, l) if _, err := io.ReadFull(r, buf); err != nil { if err == io.ErrUnexpectedEOF { return nil, io.EOF } return nil, err } frame := &settingsFrame{} b := bytes.NewReader(buf) var readDatagram, readExtendedConnect bool for b.Len() > 0 { id, err := quicvarint.Read(b) if err != nil { // should not happen. We allocated the whole frame already. return nil, err } val, err := quicvarint.Read(b) if err != nil { // should not happen. We allocated the whole frame already. return nil, err } switch id { case settingExtendedConnect: if readExtendedConnect { return nil, fmt.Errorf("duplicate setting: %d", id) } readExtendedConnect = true if val != 0 && val != 1 { return nil, fmt.Errorf("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: %d", val) } frame.ExtendedConnect = val == 1 case settingDatagram: if readDatagram { return nil, fmt.Errorf("duplicate setting: %d", id) } readDatagram = true if val != 0 && val != 1 { return nil, fmt.Errorf("invalid value for SETTINGS_H3_DATAGRAM: %d", val) } frame.Datagram = val == 1 default: if _, ok := frame.Other[id]; ok { return nil, fmt.Errorf("duplicate setting: %d", id) } if frame.Other == nil { frame.Other = make(map[uint64]uint64) } frame.Other[id] = val } } return frame, nil } func (f *settingsFrame) Append(b []byte) []byte { b = quicvarint.Append(b, 0x4) var l int for id, val := range f.Other { l += quicvarint.Len(id) + quicvarint.Len(val) } if f.Datagram { l += quicvarint.Len(settingDatagram) + quicvarint.Len(1) } if f.ExtendedConnect { l += quicvarint.Len(settingExtendedConnect) + quicvarint.Len(1) } b = quicvarint.Append(b, uint64(l)) if f.Datagram { b = quicvarint.Append(b, settingDatagram) b = quicvarint.Append(b, 1) } if f.ExtendedConnect { b = quicvarint.Append(b, settingExtendedConnect) b = quicvarint.Append(b, 1) } for id, val := range f.Other { b = quicvarint.Append(b, id) b = quicvarint.Append(b, val) } return b } golang-github-lucas-clemente-quic-go-0.46.0/http3/frames_test.go000066400000000000000000000253741465664453100245270ustar00rootroot00000000000000package http3 import ( "bytes" "errors" "fmt" "io" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) type errReader struct{ err error } func (e errReader) Read([]byte) (int, error) { return 0, e.err } var _ = Describe("Frames", func() { It("skips unknown frames", func() { data := quicvarint.Append(nil, 0xdeadbeef) // type byte data = quicvarint.Append(data, 0x42) data = append(data, make([]byte, 0x42)...) data = (&dataFrame{Length: 0x1234}).Append(data) fp := frameParser{r: bytes.NewReader(data)} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1234))) }) It("closes the connection when encountering a reserved frame type", func() { conn := mockquic.NewMockEarlyConnection(mockCtrl) for _, ft := range []uint64{0x2, 0x6, 0x8, 0x9} { data := quicvarint.Append(nil, ft) data = quicvarint.Append(data, 6) data = append(data, []byte("foobar")...) conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any()) fp := frameParser{ r: bytes.NewReader(data), conn: conn, } _, err := fp.ParseNext() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("http3: reserved frame type")) } }) Context("DATA frames", func() { It("parses", func() { data := quicvarint.Append(nil, 0) // type byte data = quicvarint.Append(data, 0x1337) fp := frameParser{r: bytes.NewReader(data)} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1337))) }) It("writes", func() { fp := frameParser{r: bytes.NewReader((&dataFrame{Length: 0xdeadbeef}).Append(nil))} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(Equal(uint64(0xdeadbeef))) }) }) Context("HEADERS frames", func() { It("parses", func() { data := quicvarint.Append(nil, 1) // type byte data = quicvarint.Append(data, 0x1337) fp := frameParser{r: bytes.NewReader(data)} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&headersFrame{})) Expect(frame.(*headersFrame).Length).To(Equal(uint64(0x1337))) }) It("writes", func() { data := (&headersFrame{Length: 0xdeadbeef}).Append(nil) fp := frameParser{r: bytes.NewReader(data)} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&headersFrame{})) Expect(frame.(*headersFrame).Length).To(Equal(uint64(0xdeadbeef))) }) }) Context("SETTINGS frames", func() { It("parses", func() { settings := quicvarint.Append(nil, 13) settings = quicvarint.Append(settings, 37) settings = quicvarint.Append(settings, 0xdead) settings = quicvarint.Append(settings, 0xbeef) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&settingsFrame{})) sf := frame.(*settingsFrame) Expect(sf.Other).To(HaveKeyWithValue(uint64(13), uint64(37))) Expect(sf.Other).To(HaveKeyWithValue(uint64(0xdead), uint64(0xbeef))) }) It("rejects duplicate settings", func() { settings := quicvarint.Append(nil, 13) settings = quicvarint.Append(settings, 37) settings = quicvarint.Append(settings, 13) settings = quicvarint.Append(settings, 38) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} _, err := fp.ParseNext() Expect(err).To(MatchError("duplicate setting: 13")) }) It("writes", func() { sf := &settingsFrame{Other: map[uint64]uint64{ 1: 2, 99: 999, 13: 37, }} fp := frameParser{r: bytes.NewReader(sf.Append(nil))} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(sf)) }) It("errors on EOF", func() { sf := &settingsFrame{Other: map[uint64]uint64{ 13: 37, 0xdeadbeef: 0xdecafbad, }} data := sf.Append(nil) fp := frameParser{r: bytes.NewReader(data)} _, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) for i := range data { b := make([]byte, i) copy(b, data[:i]) fp := frameParser{r: bytes.NewReader(b)} _, err := fp.ParseNext() Expect(err).To(MatchError(io.EOF)) } }) Context("HTTP Datagrams", func() { It("reads the SETTINGS_H3_DATAGRAM value", func() { settings := quicvarint.Append(nil, settingDatagram) settings = quicvarint.Append(settings, 1) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} f, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeAssignableToTypeOf(&settingsFrame{})) sf := f.(*settingsFrame) Expect(sf.Datagram).To(BeTrue()) }) It("rejects duplicate SETTINGS_H3_DATAGRAM entries", func() { settings := quicvarint.Append(nil, settingDatagram) settings = quicvarint.Append(settings, 1) settings = quicvarint.Append(settings, settingDatagram) settings = quicvarint.Append(settings, 1) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} _, err := fp.ParseNext() Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingDatagram))) }) It("rejects invalid values for the SETTINGS_H3_DATAGRAM entry", func() { settings := quicvarint.Append(nil, settingDatagram) settings = quicvarint.Append(settings, 1337) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} _, err := fp.ParseNext() Expect(err).To(MatchError("invalid value for SETTINGS_H3_DATAGRAM: 1337")) }) It("writes the SETTINGS_H3_DATAGRAM setting", func() { sf := &settingsFrame{Datagram: true} fp := frameParser{r: bytes.NewReader(sf.Append(nil))} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(sf)) }) }) Context("Extended Connect", func() { It("reads the SETTINGS_ENABLE_CONNECT_PROTOCOL value", func() { settings := quicvarint.Append(nil, settingExtendedConnect) settings = quicvarint.Append(settings, 1) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} f, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeAssignableToTypeOf(&settingsFrame{})) sf := f.(*settingsFrame) Expect(sf.ExtendedConnect).To(BeTrue()) }) It("rejects duplicate SETTINGS_ENABLE_CONNECT_PROTOCOL entries", func() { settings := quicvarint.Append(nil, settingExtendedConnect) settings = quicvarint.Append(settings, 1) settings = quicvarint.Append(settings, settingExtendedConnect) settings = quicvarint.Append(settings, 1) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} _, err := fp.ParseNext() Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingExtendedConnect))) }) It("rejects invalid values for the SETTINGS_ENABLE_CONNECT_PROTOCOL entry", func() { settings := quicvarint.Append(nil, settingExtendedConnect) settings = quicvarint.Append(settings, 1337) data := quicvarint.Append(nil, 4) // type byte data = quicvarint.Append(data, uint64(len(settings))) data = append(data, settings...) fp := frameParser{r: bytes.NewReader(data)} _, err := fp.ParseNext() Expect(err).To(MatchError("invalid value for SETTINGS_ENABLE_CONNECT_PROTOCOL: 1337")) }) It("writes the SETTINGS_ENABLE_CONNECT_PROTOCOL setting", func() { sf := &settingsFrame{ExtendedConnect: true} fp := frameParser{r: bytes.NewReader(sf.Append(nil))} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(sf)) }) }) }) Context("hijacking", func() { It("reads a frame without hijacking the stream", func() { buf := bytes.NewBuffer(quicvarint.Append(nil, 1337)) customFrameContents := []byte("foobar") buf.Write(customFrameContents) var called bool fp := frameParser{ r: buf, unknownFrameHandler: func(ft FrameType, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) Expect(ft).To(BeEquivalentTo(1337)) called = true b := make([]byte, 3) _, err = io.ReadFull(buf, b) Expect(err).ToNot(HaveOccurred()) Expect(string(b)).To(Equal("foo")) return true, nil }, } _, err := fp.ParseNext() Expect(err).To(MatchError(errHijacked)) Expect(called).To(BeTrue()) }) It("passes on errors that occur when reading the frame type", func() { testErr := errors.New("test error") var called bool fp := frameParser{ r: errReader{err: testErr}, unknownFrameHandler: func(ft FrameType, e error) (hijacked bool, err error) { Expect(e).To(MatchError(testErr)) Expect(ft).To(BeZero()) called = true return true, nil }, } _, err := fp.ParseNext() Expect(err).To(MatchError(errHijacked)) Expect(called).To(BeTrue()) }) It("reads a frame without hijacking the stream", func() { b := quicvarint.Append(nil, 1337) customFrameContents := []byte("custom frame") b = quicvarint.Append(b, uint64(len(customFrameContents))) b = append(b, customFrameContents...) b = (&dataFrame{Length: 6}).Append(b) b = append(b, []byte("foobar")...) var called bool fp := frameParser{ r: bytes.NewReader(b), unknownFrameHandler: func(ft FrameType, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) Expect(ft).To(BeEquivalentTo(1337)) called = true return false, nil }, } frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(&dataFrame{Length: 6})) Expect(called).To(BeTrue()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/gzip_reader.go000066400000000000000000000014361465664453100244770ustar00rootroot00000000000000package http3 // copied from net/transport.go // gzipReader wraps a response body so it can lazily // call gzip.NewReader on the first call to Read import ( "compress/gzip" "io" ) // call gzip.NewReader on the first call to Read type gzipReader struct { body io.ReadCloser // underlying Response.Body zr *gzip.Reader // lazily-initialized gzip reader zerr error // sticky error } func newGzipReader(body io.ReadCloser) io.ReadCloser { return &gzipReader{body: body} } func (gz *gzipReader) Read(p []byte) (n int, err error) { if gz.zerr != nil { return 0, gz.zerr } if gz.zr == nil { gz.zr, err = gzip.NewReader(gz.body) if err != nil { gz.zerr = err return 0, err } } return gz.zr.Read(p) } func (gz *gzipReader) Close() error { return gz.body.Close() } golang-github-lucas-clemente-quic-go-0.46.0/http3/headers.go000066400000000000000000000131201465664453100236100ustar00rootroot00000000000000package http3 import ( "errors" "fmt" "net/http" "net/url" "strconv" "strings" "golang.org/x/net/http/httpguts" "github.com/quic-go/qpack" ) type header struct { // Pseudo header fields defined in RFC 9114 Path string Method string Authority string Scheme string Status string // for Extended connect Protocol string // parsed and deduplicated ContentLength int64 // all non-pseudo headers Headers http.Header } func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) { hdr := header{Headers: make(http.Header, len(headers))} var readFirstRegularHeader, readContentLength bool var contentLengthStr string for _, h := range headers { // field names need to be lowercase, see section 4.2 of RFC 9114 if strings.ToLower(h.Name) != h.Name { return header{}, fmt.Errorf("header field is not lower-case: %s", h.Name) } if !httpguts.ValidHeaderFieldValue(h.Value) { return header{}, fmt.Errorf("invalid header field value for %s: %q", h.Name, h.Value) } if h.IsPseudo() { if readFirstRegularHeader { // all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114 return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name) } var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses switch h.Name { case ":path": hdr.Path = h.Value case ":method": hdr.Method = h.Value case ":authority": hdr.Authority = h.Value case ":protocol": hdr.Protocol = h.Value case ":scheme": hdr.Scheme = h.Value case ":status": hdr.Status = h.Value isResponsePseudoHeader = true default: return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name) } if isRequest && isResponsePseudoHeader { return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name) } if !isRequest && !isResponsePseudoHeader { return header{}, fmt.Errorf("invalid response pseudo header: %s", h.Name) } } else { if !httpguts.ValidHeaderFieldName(h.Name) { return header{}, fmt.Errorf("invalid header field name: %q", h.Name) } readFirstRegularHeader = true switch h.Name { case "content-length": // Ignore duplicate Content-Length headers. // Fail if the duplicates differ. if !readContentLength { readContentLength = true contentLengthStr = h.Value } else if contentLengthStr != h.Value { return header{}, fmt.Errorf("contradicting content lengths (%s and %s)", contentLengthStr, h.Value) } default: hdr.Headers.Add(h.Name, h.Value) } } } if len(contentLengthStr) > 0 { // use ParseUint instead of ParseInt, so that parsing fails on negative values cl, err := strconv.ParseUint(contentLengthStr, 10, 63) if err != nil { return header{}, fmt.Errorf("invalid content length: %w", err) } hdr.Headers.Set("Content-Length", contentLengthStr) hdr.ContentLength = int64(cl) } return hdr, nil } func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) { hdr, err := parseHeaders(headerFields, true) if err != nil { return nil, err } // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4 if len(hdr.Headers["Cookie"]) > 0 { hdr.Headers.Set("Cookie", strings.Join(hdr.Headers["Cookie"], "; ")) } isConnect := hdr.Method == http.MethodConnect // Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4 isExtendedConnected := isConnect && hdr.Protocol != "" if isExtendedConnected { if hdr.Scheme == "" || hdr.Path == "" || hdr.Authority == "" { return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty") } } else if isConnect { if hdr.Path != "" || hdr.Authority == "" { // normal CONNECT return nil, errors.New(":path must be empty and :authority must not be empty") } } else if len(hdr.Path) == 0 || len(hdr.Authority) == 0 || len(hdr.Method) == 0 { return nil, errors.New(":path, :authority and :method must not be empty") } if !isExtendedConnected && len(hdr.Protocol) > 0 { return nil, errors.New(":protocol must be empty") } var u *url.URL var requestURI string protocol := "HTTP/3.0" if isConnect { u = &url.URL{} if isExtendedConnected { u, err = url.ParseRequestURI(hdr.Path) if err != nil { return nil, err } protocol = hdr.Protocol } else { u.Path = hdr.Path } u.Scheme = hdr.Scheme u.Host = hdr.Authority requestURI = hdr.Authority } else { u, err = url.ParseRequestURI(hdr.Path) if err != nil { return nil, fmt.Errorf("invalid content length: %w", err) } requestURI = hdr.Path } return &http.Request{ Method: hdr.Method, URL: u, Proto: protocol, ProtoMajor: 3, ProtoMinor: 0, Header: hdr.Headers, Body: nil, ContentLength: hdr.ContentLength, Host: hdr.Authority, RequestURI: requestURI, }, nil } func hostnameFromURL(url *url.URL) string { if url != nil { return url.Host } return "" } func responseFromHeaders(headerFields []qpack.HeaderField) (*http.Response, error) { hdr, err := parseHeaders(headerFields, false) if err != nil { return nil, err } if hdr.Status == "" { return nil, errors.New("missing status field") } rsp := &http.Response{ Proto: "HTTP/3.0", ProtoMajor: 3, Header: hdr.Headers, ContentLength: hdr.ContentLength, } status, err := strconv.Atoi(hdr.Status) if err != nil { return nil, fmt.Errorf("invalid status code: %w", err) } rsp.StatusCode = status rsp.Status = hdr.Status + " " + http.StatusText(status) return rsp, nil } golang-github-lucas-clemente-quic-go-0.46.0/http3/headers_test.go000066400000000000000000000271361465664453100246630ustar00rootroot00000000000000package http3 import ( "net/http" "net/url" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/quic-go/qpack" ) var _ = Describe("Request", func() { It("populates requests", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "content-length", Value: "42"}, } req, err := requestFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(req.Method).To(Equal("GET")) Expect(req.URL.Path).To(Equal("/foo")) Expect(req.URL.Host).To(BeEmpty()) Expect(req.Proto).To(Equal("HTTP/3.0")) Expect(req.ProtoMajor).To(Equal(3)) Expect(req.ProtoMinor).To(BeZero()) Expect(req.ContentLength).To(Equal(int64(42))) Expect(req.Header).To(HaveLen(1)) Expect(req.Header.Get("Content-Length")).To(Equal("42")) Expect(req.Body).To(BeNil()) Expect(req.Host).To(Equal("quic.clemente.io")) Expect(req.RequestURI).To(Equal("/foo")) }) It("rejects upper-case fields", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "Content-Length", Value: "42"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError("header field is not lower-case: Content-Length")) }) It("rejects unknown pseudo headers", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: ":foo", Value: "bar"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError("unknown pseudo header: :foo")) }) It("rejects invalid field names", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "@", Value: "42"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(`invalid header field name: "@"`)) }) It("rejects invalid field values", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "content", Value: "\n"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(`invalid header field value for content: "\n"`)) }) It("rejects pseudo header fields after regular header fields", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: "content-length", Value: "42"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError("received pseudo header :authority after a regular header field")) }) It("rejects negative Content-Length values", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "content-length", Value: "-42"}, } _, err := requestFromHeaders(headers) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("invalid content length")) }) It("rejects multiple Content-Length headers, if they differ", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "content-length", Value: "42"}, {Name: "content-length", Value: "1337"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError("contradicting content lengths (42 and 1337)")) }) It("deduplicates multiple Content-Length headers, if they're the same", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "content-length", Value: "42"}, {Name: "content-length", Value: "42"}, } req, err := requestFromHeaders(headers) Expect(err).ToNot(HaveOccurred()) Expect(req.ContentLength).To(Equal(int64(42))) Expect(req.Header.Get("Content-Length")).To(Equal("42")) }) It("rejects pseudo header fields defined for responses", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: ":status", Value: "404"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError("invalid request pseudo header: :status")) }) It("parses path with leading double slashes", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "//foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, } req, err := requestFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(req.Header).To(BeEmpty()) Expect(req.Body).To(BeNil()) Expect(req.URL.Path).To(Equal("//foo")) Expect(req.URL.Host).To(BeEmpty()) Expect(req.Host).To(Equal("quic.clemente.io")) Expect(req.RequestURI).To(Equal("//foo")) }) It("concatenates the cookie headers", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "cookie", Value: "cookie1=foobar1"}, {Name: "cookie", Value: "cookie2=foobar2"}, } req, err := requestFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(req.Header).To(Equal(http.Header{ "Cookie": []string{"cookie1=foobar1; cookie2=foobar2"}, })) }) It("handles Other headers", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: "cache-control", Value: "max-age=0"}, {Name: "duplicate-header", Value: "1"}, {Name: "duplicate-header", Value: "2"}, } req, err := requestFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(req.Header).To(Equal(http.Header{ "Cache-Control": []string{"max-age=0"}, "Duplicate-Header": []string{"1", "2"}, })) }) It("errors with missing path", func() { headers := []qpack.HeaderField{ {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(":path, :authority and :method must not be empty")) }) It("errors with missing method", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(":path, :authority and :method must not be empty")) }) It("errors with missing authority", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":method", Value: "GET"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(":path, :authority and :method must not be empty")) }) It("errors with invalid protocol", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: "GET"}, {Name: ":protocol", Value: "connect-udp"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(":protocol must be empty")) }) Context("regular HTTP CONNECT", func() { It("handles CONNECT method", func() { headers := []qpack.HeaderField{ {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: http.MethodConnect}, } req, err := requestFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(req.Method).To(Equal(http.MethodConnect)) Expect(req.Proto).To(Equal("HTTP/3.0")) Expect(req.RequestURI).To(Equal("quic.clemente.io")) }) It("errors with missing authority in CONNECT method", func() { headers := []qpack.HeaderField{ {Name: ":method", Value: http.MethodConnect}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(":path must be empty and :authority must not be empty")) }) It("errors with extra path in CONNECT method", func() { headers := []qpack.HeaderField{ {Name: ":path", Value: "/foo"}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":method", Value: http.MethodConnect}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError(":path must be empty and :authority must not be empty")) }) }) Context("Extended CONNECT", func() { It("handles Extended CONNECT method", func() { headers := []qpack.HeaderField{ {Name: ":protocol", Value: "webtransport"}, {Name: ":scheme", Value: "ftp"}, {Name: ":method", Value: http.MethodConnect}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":path", Value: "/foo?val=1337"}, } req, err := requestFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(req.Method).To(Equal(http.MethodConnect)) Expect(req.Proto).To(Equal("webtransport")) Expect(req.URL.String()).To(Equal("ftp://quic.clemente.io/foo?val=1337")) Expect(req.URL.Query().Get("val")).To(Equal("1337")) }) It("errors with missing scheme", func() { headers := []qpack.HeaderField{ {Name: ":protocol", Value: "webtransport"}, {Name: ":method", Value: http.MethodConnect}, {Name: ":authority", Value: "quic.clemente.io"}, {Name: ":path", Value: "/foo"}, } _, err := requestFromHeaders(headers) Expect(err).To(MatchError("extended CONNECT: :scheme, :path and :authority must not be empty")) }) }) Context("extracting the hostname from a request", func() { var url *url.URL BeforeEach(func() { var err error url, err = url.Parse("https://quic.clemente.io:1337") Expect(err).ToNot(HaveOccurred()) }) It("uses URL.Host", func() { Expect(hostnameFromURL(url)).To(Equal("quic.clemente.io:1337")) }) It("returns an empty hostname if nothing is set", func() { Expect(hostnameFromURL(nil)).To(BeEmpty()) }) }) }) var _ = Describe("Response", func() { It("populates responses", func() { headers := []qpack.HeaderField{ {Name: ":status", Value: "200"}, {Name: "content-length", Value: "42"}, } rsp, err := responseFromHeaders(headers) Expect(err).NotTo(HaveOccurred()) Expect(rsp.Proto).To(Equal("HTTP/3.0")) Expect(rsp.ProtoMajor).To(Equal(3)) Expect(rsp.ProtoMinor).To(BeZero()) Expect(rsp.ContentLength).To(Equal(int64(42))) Expect(rsp.Header).To(HaveLen(1)) Expect(rsp.Header.Get("Content-Length")).To(Equal("42")) Expect(rsp.Body).To(BeNil()) Expect(rsp.StatusCode).To(BeEquivalentTo(200)) Expect(rsp.Status).To(Equal("200 OK")) }) It("rejects pseudo header fields after regular header fields", func() { headers := []qpack.HeaderField{ {Name: "content-length", Value: "42"}, {Name: ":status", Value: "200"}, } _, err := responseFromHeaders(headers) Expect(err).To(MatchError("received pseudo header :status after a regular header field")) }) It("rejects response with no status field", func() { headers := []qpack.HeaderField{ {Name: "content-length", Value: "42"}, } _, err := responseFromHeaders(headers) Expect(err).To(MatchError("missing status field")) }) It("rejects invalid status codes", func() { headers := []qpack.HeaderField{ {Name: ":status", Value: "foobar"}, {Name: "content-length", Value: "42"}, } _, err := responseFromHeaders(headers) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("invalid status code")) }) It("rejects pseudo header fields defined for requests", func() { headers := []qpack.HeaderField{ {Name: ":status", Value: "404"}, {Name: ":method", Value: "GET"}, } _, err := responseFromHeaders(headers) Expect(err).To(MatchError("invalid response pseudo header: :method")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/http3_suite_test.go000066400000000000000000000013201465664453100255060ustar00rootroot00000000000000package http3 import ( "os" "strconv" "testing" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func TestHttp3(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "HTTP/3 Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) //nolint:unparam func scaleDuration(t time.Duration) time.Duration { scaleFactor := 1 if f, err := strconv.Atoi(os.Getenv("TIMESCALE_FACTOR")); err == nil { // parsing "" errors, so this works fine if the env is not set scaleFactor = f } Expect(scaleFactor).ToNot(BeZero()) return time.Duration(scaleFactor) * t } golang-github-lucas-clemente-quic-go-0.46.0/http3/http_stream.go000066400000000000000000000173441465664453100245430ustar00rootroot00000000000000package http3 import ( "context" "errors" "fmt" "io" "net/http" "strconv" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/qpack" ) // A Stream is an HTTP/3 request stream. // When writing to and reading from the stream, data is framed in HTTP/3 DATA frames. type Stream interface { quic.Stream SendDatagram([]byte) error ReceiveDatagram(context.Context) ([]byte, error) } // A RequestStream is an HTTP/3 request stream. // When writing to and reading from the stream, data is framed in HTTP/3 DATA frames. type RequestStream interface { Stream // SendRequestHeader sends the HTTP request. // It is invalid to call it more than once. // It is invalid to call it after Write has been called. SendRequestHeader(req *http.Request) error // ReadResponse reads the HTTP response from the stream. // It is invalid to call it more than once. // It doesn't set Response.Request and Response.TLS. // It is invalid to call it after Read has been called. ReadResponse() (*http.Response, error) } type stream struct { quic.Stream conn *connection buf []byte // used as a temporary buffer when writing the HTTP/3 frame headers bytesRemainingInFrame uint64 datagrams *datagrammer } var _ Stream = &stream{} func newStream(str quic.Stream, conn *connection, datagrams *datagrammer) *stream { return &stream{ Stream: str, conn: conn, buf: make([]byte, 16), datagrams: datagrams, } } func (s *stream) Read(b []byte) (int, error) { fp := &frameParser{ r: s.Stream, conn: s.conn, } if s.bytesRemainingInFrame == 0 { parseLoop: for { frame, err := fp.ParseNext() if err != nil { return 0, err } switch f := frame.(type) { case *headersFrame: // skip HEADERS frames continue case *dataFrame: s.bytesRemainingInFrame = f.Length break parseLoop default: s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "") // parseNextFrame skips over unknown frame types // Therefore, this condition is only entered when we parsed another known frame type. return 0, fmt.Errorf("peer sent an unexpected frame: %T", f) } } } var n int var err error if s.bytesRemainingInFrame < uint64(len(b)) { n, err = s.Stream.Read(b[:s.bytesRemainingInFrame]) } else { n, err = s.Stream.Read(b) } s.bytesRemainingInFrame -= uint64(n) return n, err } func (s *stream) hasMoreData() bool { return s.bytesRemainingInFrame > 0 } func (s *stream) Write(b []byte) (int, error) { s.buf = s.buf[:0] s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf) if _, err := s.Stream.Write(s.buf); err != nil { return 0, err } return s.Stream.Write(b) } func (s *stream) writeUnframed(b []byte) (int, error) { return s.Stream.Write(b) } func (s *stream) StreamID() protocol.StreamID { return s.Stream.StreamID() } // The stream conforms to the quic.Stream interface, but instead of writing to and reading directly // from the QUIC stream, it writes to and reads from the HTTP stream. type requestStream struct { *stream responseBody io.ReadCloser // set by ReadResponse decoder *qpack.Decoder requestWriter *requestWriter maxHeaderBytes uint64 reqDone chan<- struct{} disableCompression bool sentRequest bool requestedGzip bool isConnect bool } var _ RequestStream = &requestStream{} func newRequestStream( str *stream, requestWriter *requestWriter, reqDone chan<- struct{}, decoder *qpack.Decoder, disableCompression bool, maxHeaderBytes uint64, ) *requestStream { return &requestStream{ stream: str, requestWriter: requestWriter, reqDone: reqDone, decoder: decoder, disableCompression: disableCompression, maxHeaderBytes: maxHeaderBytes, } } func (s *requestStream) Read(b []byte) (int, error) { if s.responseBody == nil { return 0, errors.New("http3: invalid use of RequestStream.Read: need to call ReadResponse first") } return s.responseBody.Read(b) } func (s *requestStream) SendRequestHeader(req *http.Request) error { if s.sentRequest { return errors.New("http3: invalid duplicate use of SendRequestHeader") } if !s.disableCompression && req.Method != http.MethodHead && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" { s.requestedGzip = true } s.isConnect = req.Method == http.MethodConnect s.sentRequest = true return s.requestWriter.WriteRequestHeader(s.Stream, req, s.requestedGzip) } func (s *requestStream) ReadResponse() (*http.Response, error) { fp := &frameParser{ r: s.Stream, conn: s.conn, } frame, err := fp.ParseNext() if err != nil { s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) return nil, fmt.Errorf("http3: parsing frame failed: %w", err) } hf, ok := frame.(*headersFrame) if !ok { s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame") return nil, errors.New("http3: expected first frame to be a HEADERS frame") } if hf.Length > s.maxHeaderBytes { s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes) } headerBlock := make([]byte, hf.Length) if _, err := io.ReadFull(s.Stream, headerBlock); err != nil { s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) return nil, fmt.Errorf("http3: failed to read response headers: %w", err) } hfs, err := s.decoder.DecodeFull(headerBlock) if err != nil { // TODO: use the right error code s.conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeGeneralProtocolError), "") return nil, fmt.Errorf("http3: failed to decode response headers: %w", err) } res, err := responseFromHeaders(hfs) if err != nil { s.Stream.CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) s.Stream.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) return nil, fmt.Errorf("http3: invalid response: %w", err) } // Check that the server doesn't send more data in DATA frames than indicated by the Content-Length header (if set). // See section 4.1.2 of RFC 9114. contentLength := int64(-1) if _, ok := res.Header["Content-Length"]; ok && res.ContentLength >= 0 { contentLength = res.ContentLength } respBody := newResponseBody(s.stream, contentLength, s.reqDone) // Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2. _, hasTransferEncoding := res.Header["Transfer-Encoding"] isInformational := res.StatusCode >= 100 && res.StatusCode < 200 isNoContent := res.StatusCode == http.StatusNoContent isSuccessfulConnect := s.isConnect && res.StatusCode >= 200 && res.StatusCode < 300 if !hasTransferEncoding && !isInformational && !isNoContent && !isSuccessfulConnect { res.ContentLength = -1 if clens, ok := res.Header["Content-Length"]; ok && len(clens) == 1 { if clen64, err := strconv.ParseInt(clens[0], 10, 64); err == nil { res.ContentLength = clen64 } } } if s.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" { res.Header.Del("Content-Encoding") res.Header.Del("Content-Length") res.ContentLength = -1 s.responseBody = newGzipReader(respBody) res.Uncompressed = true } else { s.responseBody = respBody } res.Body = s.responseBody return res, nil } func (s *stream) SendDatagram(b []byte) error { // TODO: reject if datagrams are not negotiated (yet) return s.datagrams.Send(b) } func (s *stream) ReceiveDatagram(ctx context.Context) ([]byte, error) { // TODO: reject if datagrams are not negotiated (yet) return s.datagrams.Receive(ctx) } golang-github-lucas-clemente-quic-go-0.46.0/http3/http_stream_test.go000066400000000000000000000145561465664453100256040ustar00rootroot00000000000000package http3 import ( "bytes" "context" "io" "math" "net/http" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/qpack" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func getDataFrame(data []byte) []byte { b := (&dataFrame{Length: uint64(len(data))}).Append(nil) return append(b, data...) } var _ = Describe("Stream", func() { Context("reading", func() { var ( str Stream qstr *mockquic.MockStream buf *bytes.Buffer errorCbCalled bool ) BeforeEach(func() { buf = &bytes.Buffer{} errorCbCalled = false qstr = mockquic.NewMockStream(mockCtrl) qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() conn := mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(qerr.ApplicationErrorCode, string) error { errorCbCalled = true return nil }).AnyTimes() str = newStream(qstr, newConnection(context.Background(), conn, false, protocol.PerspectiveClient, nil, 0), nil) }) It("reads DATA frames in a single run", func() { buf.Write(getDataFrame([]byte("foobar"))) b := make([]byte, 6) n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) Expect(b).To(Equal([]byte("foobar"))) }) It("reads DATA frames in multiple runs", func() { buf.Write(getDataFrame([]byte("foobar"))) b := make([]byte, 3) n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b).To(Equal([]byte("foo"))) n, err = str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b).To(Equal([]byte("bar"))) }) It("reads DATA frames into too large buffers", func() { buf.Write(getDataFrame([]byte("foobar"))) b := make([]byte, 10) n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) Expect(b[:n]).To(Equal([]byte("foobar"))) }) It("reads DATA frames into too large buffers, in multiple runs", func() { buf.Write(getDataFrame([]byte("foobar"))) b := make([]byte, 4) n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte("foob"))) n, err = str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(2)) Expect(b[:n]).To(Equal([]byte("ar"))) }) It("reads multiple DATA frames", func() { buf.Write(getDataFrame([]byte("foo"))) buf.Write(getDataFrame([]byte("bar"))) b := make([]byte, 6) n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b[:n]).To(Equal([]byte("foo"))) n, err = str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b[:n]).To(Equal([]byte("bar"))) }) It("skips HEADERS frames", func() { b := getDataFrame([]byte("foo")) b = (&headersFrame{Length: 10}).Append(b) b = append(b, make([]byte, 10)...) b = append(b, getDataFrame([]byte("bar"))...) buf.Write(b) r := make([]byte, 6) n, err := io.ReadFull(str, r) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) Expect(r).To(Equal([]byte("foobar"))) }) It("errors when it can't parse the frame", func() { buf.Write([]byte("invalid")) _, err := str.Read([]byte{0}) Expect(err).To(HaveOccurred()) }) It("errors on unexpected frames, and calls the error callback", func() { b := (&settingsFrame{}).Append(nil) buf.Write(b) _, err := str.Read([]byte{0}) Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame")) Expect(errorCbCalled).To(BeTrue()) }) }) Context("writing", func() { It("writes data frames", func() { buf := &bytes.Buffer{} qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() str := newStream(qstr, nil, nil) str.Write([]byte("foo")) str.Write([]byte("foobar")) fp := frameParser{r: buf} f, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(f).To(Equal(&dataFrame{Length: 3})) b := make([]byte, 3) _, err = io.ReadFull(buf, b) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte("foo"))) fp = frameParser{r: buf} f, err = fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(f).To(Equal(&dataFrame{Length: 6})) b = make([]byte, 6) _, err = io.ReadFull(buf, b) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte("foobar"))) }) }) }) var _ = Describe("Request Stream", func() { var str *requestStream var qstr *mockquic.MockStream BeforeEach(func() { qstr = mockquic.NewMockStream(mockCtrl) requestWriter := newRequestWriter() conn := mockquic.NewMockEarlyConnection(mockCtrl) str = newRequestStream( newStream(qstr, newConnection(context.Background(), conn, false, protocol.PerspectiveClient, nil, 0), nil), requestWriter, make(chan struct{}), qpack.NewDecoder(func(qpack.HeaderField) {}), true, math.MaxUint64, ) }) It("refuses to read before having read the response", func() { _, err := str.Read(make([]byte, 100)) Expect(err).To(MatchError("http3: invalid use of RequestStream.Read: need to call ReadResponse first")) }) It("prevents duplicate calls to SendRequestHeader", func() { req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil) Expect(err).ToNot(HaveOccurred()) qstr.EXPECT().Write(gomock.Any()).AnyTimes() Expect(str.SendRequestHeader(req)).To(Succeed()) Expect(str.SendRequestHeader(req)).To(MatchError("http3: invalid duplicate use of SendRequestHeader")) }) It("reads after the response", func() { req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil) Expect(err).ToNot(HaveOccurred()) qstr.EXPECT().Write(gomock.Any()).AnyTimes() Expect(str.SendRequestHeader(req)).To(Succeed()) buf := bytes.NewBuffer(encodeResponse(200)) buf.Write((&dataFrame{Length: 6}).Append(nil)) buf.Write([]byte("foobar")) qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() rsp, err := str.ReadResponse() Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(200)) b := make([]byte, 10) n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) Expect(b[:n]).To(Equal([]byte("foobar"))) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/mock_quic_early_listener_test.go000066400000000000000000000115071465664453100303160ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/http3 (interfaces: QUICEarlyListener) // // Generated by this command: // // mockgen -typed -package http3 -destination mock_quic_early_listener_test.go github.com/quic-go/quic-go/http3 QUICEarlyListener // // Package http3 is a generated GoMock package. package http3 import ( context "context" net "net" reflect "reflect" quic "github.com/quic-go/quic-go" gomock "go.uber.org/mock/gomock" ) // MockQUICEarlyListener is a mock of QUICEarlyListener interface. type MockQUICEarlyListener struct { ctrl *gomock.Controller recorder *MockQUICEarlyListenerMockRecorder } // MockQUICEarlyListenerMockRecorder is the mock recorder for MockQUICEarlyListener. type MockQUICEarlyListenerMockRecorder struct { mock *MockQUICEarlyListener } // NewMockQUICEarlyListener creates a new mock instance. func NewMockQUICEarlyListener(ctrl *gomock.Controller) *MockQUICEarlyListener { mock := &MockQUICEarlyListener{ctrl: ctrl} mock.recorder = &MockQUICEarlyListenerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQUICEarlyListener) EXPECT() *MockQUICEarlyListenerMockRecorder { return m.recorder } // Accept mocks base method. func (m *MockQUICEarlyListener) Accept(arg0 context.Context) (quic.EarlyConnection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Accept", arg0) ret0, _ := ret[0].(quic.EarlyConnection) ret1, _ := ret[1].(error) return ret0, ret1 } // Accept indicates an expected call of Accept. func (mr *MockQUICEarlyListenerMockRecorder) Accept(arg0 any) *MockQUICEarlyListenerAcceptCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockQUICEarlyListener)(nil).Accept), arg0) return &MockQUICEarlyListenerAcceptCall{Call: call} } // MockQUICEarlyListenerAcceptCall wrap *gomock.Call type MockQUICEarlyListenerAcceptCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICEarlyListenerAcceptCall) Return(arg0 quic.EarlyConnection, arg1 error) *MockQUICEarlyListenerAcceptCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICEarlyListenerAcceptCall) Do(f func(context.Context) (quic.EarlyConnection, error)) *MockQUICEarlyListenerAcceptCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICEarlyListenerAcceptCall) DoAndReturn(f func(context.Context) (quic.EarlyConnection, error)) *MockQUICEarlyListenerAcceptCall { c.Call = c.Call.DoAndReturn(f) return c } // Addr mocks base method. func (m *MockQUICEarlyListener) Addr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Addr") ret0, _ := ret[0].(net.Addr) return ret0 } // Addr indicates an expected call of Addr. func (mr *MockQUICEarlyListenerMockRecorder) Addr() *MockQUICEarlyListenerAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Addr", reflect.TypeOf((*MockQUICEarlyListener)(nil).Addr)) return &MockQUICEarlyListenerAddrCall{Call: call} } // MockQUICEarlyListenerAddrCall wrap *gomock.Call type MockQUICEarlyListenerAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICEarlyListenerAddrCall) Return(arg0 net.Addr) *MockQUICEarlyListenerAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICEarlyListenerAddrCall) Do(f func() net.Addr) *MockQUICEarlyListenerAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICEarlyListenerAddrCall) DoAndReturn(f func() net.Addr) *MockQUICEarlyListenerAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockQUICEarlyListener) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockQUICEarlyListenerMockRecorder) Close() *MockQUICEarlyListenerCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockQUICEarlyListener)(nil).Close)) return &MockQUICEarlyListenerCloseCall{Call: call} } // MockQUICEarlyListenerCloseCall wrap *gomock.Call type MockQUICEarlyListenerCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICEarlyListenerCloseCall) Return(arg0 error) *MockQUICEarlyListenerCloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICEarlyListenerCloseCall) Do(f func() error) *MockQUICEarlyListenerCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICEarlyListenerCloseCall) DoAndReturn(f func() error) *MockQUICEarlyListenerCloseCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/http3/mock_singleroundtripper_test.go000066400000000000000000000102351465664453100302100ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/http3 (interfaces: SingleRoundTripper) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package http3 -destination mock_singleroundtripper_test.go github.com/quic-go/quic-go/http3 SingleRoundTripper // // Package http3 is a generated GoMock package. package http3 import ( context "context" http "net/http" reflect "reflect" gomock "go.uber.org/mock/gomock" ) // MockSingleRoundTripper is a mock of SingleRoundTripper interface. type MockSingleRoundTripper struct { ctrl *gomock.Controller recorder *MockSingleRoundTripperMockRecorder } // MockSingleRoundTripperMockRecorder is the mock recorder for MockSingleRoundTripper. type MockSingleRoundTripperMockRecorder struct { mock *MockSingleRoundTripper } // NewMockSingleRoundTripper creates a new mock instance. func NewMockSingleRoundTripper(ctrl *gomock.Controller) *MockSingleRoundTripper { mock := &MockSingleRoundTripper{ctrl: ctrl} mock.recorder = &MockSingleRoundTripperMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSingleRoundTripper) EXPECT() *MockSingleRoundTripperMockRecorder { return m.recorder } // OpenRequestStream mocks base method. func (m *MockSingleRoundTripper) OpenRequestStream(arg0 context.Context) (RequestStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenRequestStream", arg0) ret0, _ := ret[0].(RequestStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenRequestStream indicates an expected call of OpenRequestStream. func (mr *MockSingleRoundTripperMockRecorder) OpenRequestStream(arg0 any) *MockSingleRoundTripperOpenRequestStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenRequestStream", reflect.TypeOf((*MockSingleRoundTripper)(nil).OpenRequestStream), arg0) return &MockSingleRoundTripperOpenRequestStreamCall{Call: call} } // MockSingleRoundTripperOpenRequestStreamCall wrap *gomock.Call type MockSingleRoundTripperOpenRequestStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSingleRoundTripperOpenRequestStreamCall) Return(arg0 RequestStream, arg1 error) *MockSingleRoundTripperOpenRequestStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSingleRoundTripperOpenRequestStreamCall) Do(f func(context.Context) (RequestStream, error)) *MockSingleRoundTripperOpenRequestStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSingleRoundTripperOpenRequestStreamCall) DoAndReturn(f func(context.Context) (RequestStream, error)) *MockSingleRoundTripperOpenRequestStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // RoundTrip mocks base method. func (m *MockSingleRoundTripper) RoundTrip(arg0 *http.Request) (*http.Response, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RoundTrip", arg0) ret0, _ := ret[0].(*http.Response) ret1, _ := ret[1].(error) return ret0, ret1 } // RoundTrip indicates an expected call of RoundTrip. func (mr *MockSingleRoundTripperMockRecorder) RoundTrip(arg0 any) *MockSingleRoundTripperRoundTripCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RoundTrip", reflect.TypeOf((*MockSingleRoundTripper)(nil).RoundTrip), arg0) return &MockSingleRoundTripperRoundTripCall{Call: call} } // MockSingleRoundTripperRoundTripCall wrap *gomock.Call type MockSingleRoundTripperRoundTripCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSingleRoundTripperRoundTripCall) Return(arg0 *http.Response, arg1 error) *MockSingleRoundTripperRoundTripCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSingleRoundTripperRoundTripCall) Do(f func(*http.Request) (*http.Response, error)) *MockSingleRoundTripperRoundTripCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSingleRoundTripperRoundTripCall) DoAndReturn(f func(*http.Request) (*http.Response, error)) *MockSingleRoundTripperRoundTripCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/http3/mockgen.go000066400000000000000000000007251465664453100236270ustar00rootroot00000000000000//go:build gomock || generate package http3 //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package http3 -destination mock_singleroundtripper_test.go github.com/quic-go/quic-go/http3 SingleRoundTripper" type SingleRoundTripper = singleRoundTripper //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package http3 -destination mock_quic_early_listener_test.go github.com/quic-go/quic-go/http3 QUICEarlyListener" golang-github-lucas-clemente-quic-go-0.46.0/http3/request_writer.go000066400000000000000000000176661465664453100253040ustar00rootroot00000000000000package http3 import ( "bytes" "errors" "fmt" "io" "net" "net/http" "strconv" "strings" "sync" "golang.org/x/net/http/httpguts" "golang.org/x/net/http2/hpack" "golang.org/x/net/idna" "github.com/quic-go/qpack" "github.com/quic-go/quic-go" ) const bodyCopyBufferSize = 8 * 1024 type requestWriter struct { mutex sync.Mutex encoder *qpack.Encoder headerBuf *bytes.Buffer } func newRequestWriter() *requestWriter { headerBuf := &bytes.Buffer{} encoder := qpack.NewEncoder(headerBuf) return &requestWriter{ encoder: encoder, headerBuf: headerBuf, } } func (w *requestWriter) WriteRequestHeader(str quic.Stream, req *http.Request, gzip bool) error { // TODO: figure out how to add support for trailers buf := &bytes.Buffer{} if err := w.writeHeaders(buf, req, gzip); err != nil { return err } _, err := str.Write(buf.Bytes()) return err } func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool) error { w.mutex.Lock() defer w.mutex.Unlock() defer w.encoder.Close() defer w.headerBuf.Reset() if err := w.encodeHeaders(req, gzip, "", actualContentLength(req)); err != nil { return err } b := make([]byte, 0, 128) b = (&headersFrame{Length: uint64(w.headerBuf.Len())}).Append(b) if _, err := wr.Write(b); err != nil { return err } _, err := wr.Write(w.headerBuf.Bytes()) return err } func isExtendedConnectRequest(req *http.Request) bool { return req.Method == http.MethodConnect && req.Proto != "" && req.Proto != "HTTP/1.1" } // copied from net/transport.go // Modified to support Extended CONNECT: // Contrary to what the godoc for the http.Request says, // we do respect the Proto field if the method is CONNECT. func (w *requestWriter) encodeHeaders(req *http.Request, addGzipHeader bool, trailers string, contentLength int64) error { host := req.Host if host == "" { host = req.URL.Host } host, err := httpguts.PunycodeHostPort(host) if err != nil { return err } if !httpguts.ValidHostHeader(host) { return errors.New("http3: invalid Host header") } // http.NewRequest sets this field to HTTP/1.1 isExtendedConnect := isExtendedConnectRequest(req) var path string if req.Method != http.MethodConnect || isExtendedConnect { path = req.URL.RequestURI() if !validPseudoPath(path) { orig := path path = strings.TrimPrefix(path, req.URL.Scheme+"://"+host) if !validPseudoPath(path) { if req.URL.Opaque != "" { return fmt.Errorf("invalid request :path %q from URL.Opaque = %q", orig, req.URL.Opaque) } else { return fmt.Errorf("invalid request :path %q", orig) } } } } // Check for any invalid headers and return an error before we // potentially pollute our hpack state. (We want to be able to // continue to reuse the hpack encoder for future requests) for k, vv := range req.Header { if !httpguts.ValidHeaderFieldName(k) { return fmt.Errorf("invalid HTTP header name %q", k) } for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { return fmt.Errorf("invalid HTTP header value %q for header %q", v, k) } } } enumerateHeaders := func(f func(name, value string)) { // 8.1.2.3 Request Pseudo-Header Fields // The :path pseudo-header field includes the path and query parts of the // target URI (the path-absolute production and optionally a '?' character // followed by the query production (see Sections 3.3 and 3.4 of // [RFC3986]). f(":authority", host) f(":method", req.Method) if req.Method != http.MethodConnect || isExtendedConnect { f(":path", path) f(":scheme", req.URL.Scheme) } if isExtendedConnect { f(":protocol", req.Proto) } if trailers != "" { f("trailer", trailers) } var didUA bool for k, vv := range req.Header { if strings.EqualFold(k, "host") || strings.EqualFold(k, "content-length") { // Host is :authority, already sent. // Content-Length is automatic, set below. continue } else if strings.EqualFold(k, "connection") || strings.EqualFold(k, "proxy-connection") || strings.EqualFold(k, "transfer-encoding") || strings.EqualFold(k, "upgrade") || strings.EqualFold(k, "keep-alive") { // Per 8.1.2.2 Connection-Specific Header // Fields, don't send connection-specific // fields. We have already checked if any // are error-worthy so just ignore the rest. continue } else if strings.EqualFold(k, "user-agent") { // Match Go's http1 behavior: at most one // User-Agent. If set to nil or empty string, // then omit it. Otherwise if not mentioned, // include the default (below). didUA = true if len(vv) < 1 { continue } vv = vv[:1] if vv[0] == "" { continue } } for _, v := range vv { f(k, v) } } if shouldSendReqContentLength(req.Method, contentLength) { f("content-length", strconv.FormatInt(contentLength, 10)) } if addGzipHeader { f("accept-encoding", "gzip") } if !didUA { f("user-agent", defaultUserAgent) } } // Do a first pass over the headers counting bytes to ensure // we don't exceed cc.peerMaxHeaderListSize. This is done as a // separate pass before encoding the headers to prevent // modifying the hpack state. hlSize := uint64(0) enumerateHeaders(func(name, value string) { hf := hpack.HeaderField{Name: name, Value: value} hlSize += uint64(hf.Size()) }) // TODO: check maximum header list size // if hlSize > cc.peerMaxHeaderListSize { // return errRequestHeaderListSize // } // trace := httptrace.ContextClientTrace(req.Context()) // traceHeaders := traceHasWroteHeaderField(trace) // Header list size is ok. Write the headers. enumerateHeaders(func(name, value string) { name = strings.ToLower(name) w.encoder.WriteField(qpack.HeaderField{Name: name, Value: value}) // if traceHeaders { // traceWroteHeaderField(trace, name, value) // } }) return nil } // authorityAddr returns a given authority (a host/IP, or host:port / ip:port) // and returns a host:port. The port 443 is added if needed. func authorityAddr(authority string) (addr string) { host, port, err := net.SplitHostPort(authority) if err != nil { // authority didn't have a port port = "443" host = authority } if a, err := idna.ToASCII(host); err == nil { host = a } // IPv6 address literal, without a port: if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { return host + ":" + port } return net.JoinHostPort(host, port) } // validPseudoPath reports whether v is a valid :path pseudo-header // value. It must be either: // // *) a non-empty string starting with '/' // *) the string '*', for OPTIONS requests. // // For now this is only used a quick check for deciding when to clean // up Opaque URLs before sending requests from the Transport. // See golang.org/issue/16847 // // We used to enforce that the path also didn't start with "//", but // Google's GFE accepts such paths and Chrome sends them, so ignore // that part of the spec. See golang.org/issue/19103. func validPseudoPath(v string) bool { return (len(v) > 0 && v[0] == '/') || v == "*" } // actualContentLength returns a sanitized version of // req.ContentLength, where 0 actually means zero (not unknown) and -1 // means unknown. func actualContentLength(req *http.Request) int64 { if req.Body == nil { return 0 } if req.ContentLength != 0 { return req.ContentLength } return -1 } // shouldSendReqContentLength reports whether the http2.Transport should send // a "content-length" request header. This logic is basically a copy of the net/http // transferWriter.shouldSendContentLength. // The contentLength is the corrected contentLength (so 0 means actually 0, not unknown). // -1 means unknown. func shouldSendReqContentLength(method string, contentLength int64) bool { if contentLength > 0 { return true } if contentLength < 0 { return false } // For zero bodies, whether we send a content-length depends on the method. // It also kinda doesn't matter for http2 either way, with END_STREAM. switch method { case "POST", "PUT", "PATCH": return true default: return false } } golang-github-lucas-clemente-quic-go-0.46.0/http3/request_writer_test.go000066400000000000000000000101611465664453100263220ustar00rootroot00000000000000package http3 import ( "bytes" "io" "net/http" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/qpack" "go.uber.org/mock/gomock" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Request Writer", func() { var ( rw *requestWriter str *mockquic.MockStream strBuf *bytes.Buffer ) decode := func(str io.Reader) map[string]string { fp := frameParser{r: str} frame, err := fp.ParseNext() ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, frame).To(BeAssignableToTypeOf(&headersFrame{})) headersFrame := frame.(*headersFrame) data := make([]byte, headersFrame.Length) _, err = io.ReadFull(str, data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) decoder := qpack.NewDecoder(nil) hfs, err := decoder.DecodeFull(data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) values := make(map[string]string) for _, hf := range hfs { values[hf.Name] = hf.Value } return values } BeforeEach(func() { rw = newRequestWriter() strBuf = &bytes.Buffer{} str = mockquic.NewMockStream(mockCtrl) str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes() }) It("writes a GET request", func() { req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io")) Expect(headerFields).To(HaveKeyWithValue(":method", "GET")) Expect(headerFields).To(HaveKeyWithValue(":path", "/index.html?foo=bar")) Expect(headerFields).To(HaveKeyWithValue(":scheme", "https")) Expect(headerFields).ToNot(HaveKey("accept-encoding")) }) It("rejects invalid host headers", func() { req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil) Expect(err).ToNot(HaveOccurred()) req.Host = "foo@bar" // @ is invalid Expect(rw.WriteRequestHeader(str, req, false)).To(MatchError("http3: invalid Host header")) }) It("sends cookies", func() { req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil) Expect(err).ToNot(HaveOccurred()) cookie1 := &http.Cookie{ Name: "Cookie #1", Value: "Value #1", } cookie2 := &http.Cookie{ Name: "Cookie #2", Value: "Value #2", } req.AddCookie(cookie1) req.AddCookie(cookie2) Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue("cookie", `Cookie #1="Value #1"; Cookie #2="Value #2"`)) }) It("adds the header for gzip support", func() { req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequestHeader(str, req, true)).To(Succeed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue("accept-encoding", "gzip")) }) It("writes a CONNECT request", func() { req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/", nil) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT")) Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io")) Expect(headerFields).ToNot(HaveKey(":path")) Expect(headerFields).ToNot(HaveKey(":scheme")) Expect(headerFields).ToNot(HaveKey(":protocol")) }) It("writes an Extended CONNECT request", func() { req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/foobar", nil) Expect(err).ToNot(HaveOccurred()) req.Proto = "webtransport" Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io")) Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT")) Expect(headerFields).To(HaveKeyWithValue(":path", "/foobar")) Expect(headerFields).To(HaveKeyWithValue(":scheme", "https")) Expect(headerFields).To(HaveKeyWithValue(":protocol", "webtransport")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/response_writer.go000066400000000000000000000154361465664453100254430ustar00rootroot00000000000000package http3 import ( "bytes" "fmt" "log/slog" "net/http" "strconv" "strings" "time" "github.com/quic-go/qpack" ) // The HTTPStreamer allows taking over a HTTP/3 stream. The interface is implemented the http.Response.Body. // On the client side, the stream will be closed for writing, unless the DontCloseRequestStream RoundTripOpt was set. // When a stream is taken over, it's the caller's responsibility to close the stream. type HTTPStreamer interface { HTTPStream() Stream } // The maximum length of an encoded HTTP/3 frame header is 16: // The frame has a type and length field, both QUIC varints (maximum 8 bytes in length) const frameHeaderLen = 16 const maxSmallResponseSize = 4096 type responseWriter struct { str *stream conn Connection header http.Header buf []byte status int // status code passed to WriteHeader // for responses smaller than maxSmallResponseSize, we buffer calls to Write, // and automatically add the Content-Length header smallResponseBuf []byte contentLen int64 // if handler set valid Content-Length header numWritten int64 // bytes written headerComplete bool // set once WriteHeader is called with a status code >= 200 headerWritten bool // set once the response header has been serialized to the stream isHead bool hijacked bool // set on HTTPStream is called logger *slog.Logger } var ( _ http.ResponseWriter = &responseWriter{} _ http.Flusher = &responseWriter{} _ Hijacker = &responseWriter{} _ HTTPStreamer = &responseWriter{} ) func newResponseWriter(str *stream, conn Connection, isHead bool, logger *slog.Logger) *responseWriter { return &responseWriter{ str: str, conn: conn, header: http.Header{}, buf: make([]byte, frameHeaderLen), isHead: isHead, logger: logger, } } func (w *responseWriter) Header() http.Header { return w.header } func (w *responseWriter) WriteHeader(status int) { if w.headerComplete { return } // http status must be 3 digits if status < 100 || status > 999 { panic(fmt.Sprintf("invalid WriteHeader code %v", status)) } w.status = status // immediately write 1xx headers if status < 200 { w.writeHeader(status) return } // We're done with headers once we write a status >= 200. w.headerComplete = true // Add Date header. // This is what the standard library does. // Can be disabled by setting the Date header to nil. if _, ok := w.header["Date"]; !ok { w.header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) } // Content-Length checking // use ParseUint instead of ParseInt, as negative values are invalid if clen := w.header.Get("Content-Length"); clen != "" { if cl, err := strconv.ParseUint(clen, 10, 63); err == nil { w.contentLen = int64(cl) } else { // emit a warning for malformed Content-Length and remove it logger := w.logger if logger == nil { logger = slog.Default() } logger.Error("Malformed Content-Length", "value", clen) w.header.Del("Content-Length") } } } func (w *responseWriter) sniffContentType(p []byte) { // If no content type, apply sniffing algorithm to body. // We can't use `w.header.Get` here since if the Content-Type was set to nil, we shouldn't do sniffing. _, haveType := w.header["Content-Type"] // If the Transfer-Encoding or Content-Encoding was set and is non-blank, // we shouldn't sniff the body. hasTE := w.header.Get("Transfer-Encoding") != "" hasCE := w.header.Get("Content-Encoding") != "" if !hasCE && !haveType && !hasTE && len(p) > 0 { w.header.Set("Content-Type", http.DetectContentType(p)) } } func (w *responseWriter) Write(p []byte) (int, error) { bodyAllowed := bodyAllowedForStatus(w.status) if !w.headerComplete { w.sniffContentType(p) w.WriteHeader(http.StatusOK) bodyAllowed = true } if !bodyAllowed { return 0, http.ErrBodyNotAllowed } w.numWritten += int64(len(p)) if w.contentLen != 0 && w.numWritten > w.contentLen { return 0, http.ErrContentLength } if w.isHead { return len(p), nil } if !w.headerWritten { // Buffer small responses. // This allows us to automatically set the Content-Length field. if len(w.smallResponseBuf)+len(p) < maxSmallResponseSize { w.smallResponseBuf = append(w.smallResponseBuf, p...) return len(p), nil } } return w.doWrite(p) } func (w *responseWriter) doWrite(p []byte) (int, error) { if !w.headerWritten { w.sniffContentType(w.smallResponseBuf) if err := w.writeHeader(w.status); err != nil { return 0, maybeReplaceError(err) } w.headerWritten = true } l := uint64(len(w.smallResponseBuf) + len(p)) if l == 0 { return 0, nil } df := &dataFrame{Length: l} w.buf = w.buf[:0] w.buf = df.Append(w.buf) if _, err := w.str.writeUnframed(w.buf); err != nil { return 0, maybeReplaceError(err) } if len(w.smallResponseBuf) > 0 { if _, err := w.str.writeUnframed(w.smallResponseBuf); err != nil { return 0, maybeReplaceError(err) } w.smallResponseBuf = nil } var n int if len(p) > 0 { var err error n, err = w.str.writeUnframed(p) if err != nil { return n, maybeReplaceError(err) } } return n, nil } func (w *responseWriter) writeHeader(status int) error { var headers bytes.Buffer enc := qpack.NewEncoder(&headers) if err := enc.WriteField(qpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)}); err != nil { return err } for k, v := range w.header { for index := range v { if err := enc.WriteField(qpack.HeaderField{Name: strings.ToLower(k), Value: v[index]}); err != nil { return err } } } buf := make([]byte, 0, frameHeaderLen+headers.Len()) buf = (&headersFrame{Length: uint64(headers.Len())}).Append(buf) buf = append(buf, headers.Bytes()...) _, err := w.str.writeUnframed(buf) return err } func (w *responseWriter) FlushError() error { if !w.headerComplete { w.WriteHeader(http.StatusOK) } _, err := w.doWrite(nil) return err } func (w *responseWriter) Flush() { if err := w.FlushError(); err != nil { if w.logger != nil { w.logger.Debug("could not flush to stream", "error", err) } } } func (w *responseWriter) HTTPStream() Stream { w.hijacked = true w.Flush() return w.str } func (w *responseWriter) wasStreamHijacked() bool { return w.hijacked } func (w *responseWriter) Connection() Connection { return w.conn } func (w *responseWriter) SetReadDeadline(deadline time.Time) error { return w.str.SetReadDeadline(deadline) } func (w *responseWriter) SetWriteDeadline(deadline time.Time) error { return w.str.SetWriteDeadline(deadline) } // copied from http2/http2.go // bodyAllowedForStatus reports whether a given response status code // permits a body. See RFC 2616, section 4.4. func bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: return false case status == http.StatusNoContent: return false case status == http.StatusNotModified: return false } return true } golang-github-lucas-clemente-quic-go-0.46.0/http3/response_writer_test.go000066400000000000000000000137601465664453100265000ustar00rootroot00000000000000package http3 import ( "bytes" "io" "net/http" "time" "github.com/quic-go/qpack" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Response Writer", func() { var ( rw *responseWriter strBuf *bytes.Buffer ) BeforeEach(func() { strBuf = &bytes.Buffer{} str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes() str.EXPECT().SetReadDeadline(gomock.Any()).Return(nil).AnyTimes() str.EXPECT().SetWriteDeadline(gomock.Any()).Return(nil).AnyTimes() rw = newResponseWriter(newStream(str, nil, nil), nil, false, nil) }) decodeHeader := func(str io.Reader) map[string][]string { rw.Flush() fields := make(map[string][]string) decoder := qpack.NewDecoder(nil) fp := frameParser{r: str} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&headersFrame{})) headersFrame := frame.(*headersFrame) data := make([]byte, headersFrame.Length) _, err = io.ReadFull(str, data) Expect(err).ToNot(HaveOccurred()) hfs, err := decoder.DecodeFull(data) Expect(err).ToNot(HaveOccurred()) for _, p := range hfs { fields[p.Name] = append(fields[p.Name], p.Value) } return fields } getData := func(str io.Reader) []byte { fp := frameParser{r: str} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) df := frame.(*dataFrame) data := make([]byte, df.Length) _, err = io.ReadFull(str, data) Expect(err).ToNot(HaveOccurred()) return data } It("writes status", func() { rw.WriteHeader(http.StatusTeapot) fields := decodeHeader(strBuf) Expect(fields).To(HaveLen(2)) Expect(fields).To(HaveKeyWithValue(":status", []string{"418"})) Expect(fields).To(HaveKey("date")) }) It("writes headers", func() { rw.Header().Add("content-length", "42") rw.WriteHeader(http.StatusTeapot) fields := decodeHeader(strBuf) Expect(fields).To(HaveKeyWithValue("content-length", []string{"42"})) }) It("writes multiple headers with the same name", func() { const cookie1 = "test1=1; Max-Age=7200; path=/" const cookie2 = "test2=2; Max-Age=7200; path=/" rw.Header().Add("set-cookie", cookie1) rw.Header().Add("set-cookie", cookie2) rw.WriteHeader(http.StatusTeapot) fields := decodeHeader(strBuf) Expect(fields).To(HaveKey("set-cookie")) cookies := fields["set-cookie"] Expect(cookies).To(ContainElement(cookie1)) Expect(cookies).To(ContainElement(cookie2)) }) It("writes data", func() { n, err := rw.Write([]byte("foobar")) Expect(n).To(Equal(6)) Expect(err).ToNot(HaveOccurred()) // Should have written 200 on the header stream fields := decodeHeader(strBuf) Expect(fields).To(HaveKeyWithValue(":status", []string{"200"})) // And foobar on the data stream Expect(getData(strBuf)).To(Equal([]byte("foobar"))) }) It("writes data after WriteHeader is called", func() { rw.WriteHeader(http.StatusTeapot) n, err := rw.Write([]byte("foobar")) Expect(n).To(Equal(6)) Expect(err).ToNot(HaveOccurred()) // Should have written 418 on the header stream fields := decodeHeader(strBuf) Expect(fields).To(HaveKeyWithValue(":status", []string{"418"})) // And foobar on the data stream Expect(getData(strBuf)).To(Equal([]byte("foobar"))) }) It("does not WriteHeader() twice", func() { rw.WriteHeader(http.StatusOK) rw.WriteHeader(http.StatusInternalServerError) fields := decodeHeader(strBuf) Expect(fields).To(HaveLen(2)) Expect(fields).To(HaveKeyWithValue(":status", []string{"200"})) Expect(fields).To(HaveKey("date")) }) It("allows calling WriteHeader() several times when using the 103 status code", func() { rw.Header().Add("Link", "; rel=preload; as=style") rw.Header().Add("Link", "; rel=preload; as=script") rw.WriteHeader(http.StatusEarlyHints) n, err := rw.Write([]byte("foobar")) Expect(n).To(Equal(6)) Expect(err).ToNot(HaveOccurred()) // Early Hints must have been received fields := decodeHeader(strBuf) Expect(fields).To(HaveLen(2)) Expect(fields).To(HaveKeyWithValue(":status", []string{"103"})) Expect(fields).To(HaveKeyWithValue("link", []string{"; rel=preload; as=style", "; rel=preload; as=script"})) // According to the spec, headers sent in the informational response must also be included in the final response fields = decodeHeader(strBuf) Expect(fields).To(HaveLen(4)) Expect(fields).To(HaveKeyWithValue(":status", []string{"200"})) Expect(fields).To(HaveKey("date")) Expect(fields).To(HaveKey("content-type")) Expect(fields).To(HaveKeyWithValue("link", []string{"; rel=preload; as=style", "; rel=preload; as=script"})) Expect(getData(strBuf)).To(Equal([]byte("foobar"))) }) It("doesn't allow writes if the status code doesn't allow a body", func() { rw.WriteHeader(304) n, err := rw.Write([]byte("foobar")) Expect(n).To(BeZero()) Expect(err).To(MatchError(http.ErrBodyNotAllowed)) }) It("first call to Write sniffs if Content-Type is not set", func() { n, err := rw.Write([]byte("")) Expect(n).To(Equal(13)) Expect(err).ToNot(HaveOccurred()) fields := decodeHeader(strBuf) Expect(fields).To(HaveKeyWithValue("content-type", []string{"text/html; charset=utf-8"})) }) It(`is compatible with "net/http".ResponseController`, func() { Expect(rw.SetReadDeadline(time.Now().Add(1 * time.Second))).To(BeNil()) Expect(rw.SetWriteDeadline(time.Now().Add(1 * time.Second))).To(BeNil()) }) It(`checks Content-Length header`, func() { rw.Header().Set("Content-Length", "6") n, err := rw.Write([]byte("foobar")) Expect(n).To(Equal(6)) Expect(err).To(BeNil()) n, err = rw.Write([]byte("foobar")) Expect(n).To(Equal(0)) Expect(err).To(Equal(http.ErrContentLength)) }) It(`panics when writing invalid status`, func() { Expect(func() { rw.WriteHeader(99) }).To(Panic()) Expect(func() { rw.WriteHeader(1000) }).To(Panic()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/roundtrip.go000066400000000000000000000253041465664453100242320ustar00rootroot00000000000000package http3 import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "net/http" "strings" "sync" "sync/atomic" "golang.org/x/net/http/httpguts" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" ) // Settings are HTTP/3 settings that apply to the underlying connection. type Settings struct { // Support for HTTP/3 datagrams (RFC 9297) EnableDatagrams bool // Extended CONNECT, RFC 9220 EnableExtendedConnect bool // Other settings, defined by the application Other map[uint64]uint64 } // RoundTripOpt are options for the Transport.RoundTripOpt method. type RoundTripOpt struct { // OnlyCachedConn controls whether the RoundTripper may create a new QUIC connection. // If set true and no cached connection is available, RoundTripOpt will return ErrNoCachedConn. OnlyCachedConn bool } type singleRoundTripper interface { OpenRequestStream(context.Context) (RequestStream, error) RoundTrip(*http.Request) (*http.Response, error) } type roundTripperWithCount struct { cancel context.CancelFunc dialing chan struct{} // closed as soon as quic.Dial(Early) returned dialErr error conn quic.EarlyConnection rt singleRoundTripper useCount atomic.Int64 } func (r *roundTripperWithCount) Close() error { r.cancel() <-r.dialing if r.conn != nil { return r.conn.CloseWithError(0, "") } return nil } // RoundTripper implements the http.RoundTripper interface type RoundTripper struct { mutex sync.Mutex // TLSClientConfig specifies the TLS configuration to use with // tls.Client. If nil, the default configuration is used. TLSClientConfig *tls.Config // QUICConfig is the quic.Config used for dialing new connections. // If nil, reasonable default values will be used. QUICConfig *quic.Config // Dial specifies an optional dial function for creating QUIC // connections for requests. // If Dial is nil, a UDPConn will be created at the first request // and will be reused for subsequent connections to other servers. Dial func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) // Enable support for HTTP/3 datagrams (RFC 9297). // If a QUICConfig is set, datagram support also needs to be enabled on the QUIC layer by setting EnableDatagrams. EnableDatagrams bool // Additional HTTP/3 settings. // It is invalid to specify any settings defined by RFC 9114 (HTTP/3) and RFC 9297 (HTTP Datagrams). AdditionalSettings map[uint64]uint64 // MaxResponseHeaderBytes specifies a limit on how many response bytes are // allowed in the server's response header. // Zero means to use a default limit. MaxResponseHeaderBytes int64 // DisableCompression, if true, prevents the Transport from requesting compression with an // "Accept-Encoding: gzip" request header when the Request contains no existing Accept-Encoding value. // If the Transport requests gzip on its own and gets a gzipped response, it's transparently // decoded in the Response.Body. // However, if the user explicitly requested gzip it is not automatically uncompressed. DisableCompression bool initOnce sync.Once initErr error newClient func(quic.EarlyConnection) singleRoundTripper clients map[string]*roundTripperWithCount transport *quic.Transport } var ( _ http.RoundTripper = &RoundTripper{} _ io.Closer = &RoundTripper{} ) // ErrNoCachedConn is returned when RoundTripper.OnlyCachedConn is set var ErrNoCachedConn = errors.New("http3: no cached connection was available") // RoundTripOpt is like RoundTrip, but takes options. func (r *RoundTripper) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) { r.initOnce.Do(func() { r.initErr = r.init() }) if r.initErr != nil { return nil, r.initErr } if req.URL == nil { closeRequestBody(req) return nil, errors.New("http3: nil Request.URL") } if req.URL.Scheme != "https" { closeRequestBody(req) return nil, fmt.Errorf("http3: unsupported protocol scheme: %s", req.URL.Scheme) } if req.URL.Host == "" { closeRequestBody(req) return nil, errors.New("http3: no Host in request URL") } if req.Header == nil { closeRequestBody(req) return nil, errors.New("http3: nil Request.Header") } for k, vv := range req.Header { if !httpguts.ValidHeaderFieldName(k) { return nil, fmt.Errorf("http3: invalid http header field name %q", k) } for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { return nil, fmt.Errorf("http3: invalid http header field value %q for key %v", v, k) } } } if req.Method != "" && !validMethod(req.Method) { closeRequestBody(req) return nil, fmt.Errorf("http3: invalid method %q", req.Method) } hostname := authorityAddr(hostnameFromURL(req.URL)) cl, isReused, err := r.getClient(req.Context(), hostname, opt.OnlyCachedConn) if err != nil { return nil, err } select { case <-cl.dialing: case <-req.Context().Done(): return nil, context.Cause(req.Context()) } if cl.dialErr != nil { r.removeClient(hostname) return nil, cl.dialErr } defer cl.useCount.Add(-1) rsp, err := cl.rt.RoundTrip(req) if err != nil { // non-nil errors on roundtrip are likely due to a problem with the connection // so we remove the client from the cache so that subsequent trips reconnect // context cancelation is excluded as is does not signify a connection error if !errors.Is(err, context.Canceled) { r.removeClient(hostname) } if isReused { if nerr, ok := err.(net.Error); ok && nerr.Timeout() { return r.RoundTripOpt(req, opt) } } } return rsp, err } // RoundTrip does a round trip. func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return r.RoundTripOpt(req, RoundTripOpt{}) } func (r *RoundTripper) init() error { if r.newClient == nil { r.newClient = func(conn quic.EarlyConnection) singleRoundTripper { return &SingleDestinationRoundTripper{ Connection: conn, EnableDatagrams: r.EnableDatagrams, DisableCompression: r.DisableCompression, AdditionalSettings: r.AdditionalSettings, MaxResponseHeaderBytes: r.MaxResponseHeaderBytes, } } } if r.QUICConfig == nil { r.QUICConfig = defaultQuicConfig.Clone() r.QUICConfig.EnableDatagrams = r.EnableDatagrams } if r.EnableDatagrams && !r.QUICConfig.EnableDatagrams { return errors.New("HTTP Datagrams enabled, but QUIC Datagrams disabled") } if len(r.QUICConfig.Versions) == 0 { r.QUICConfig = r.QUICConfig.Clone() r.QUICConfig.Versions = []quic.Version{protocol.SupportedVersions[0]} } if len(r.QUICConfig.Versions) != 1 { return errors.New("can only use a single QUIC version for dialing a HTTP/3 connection") } if r.QUICConfig.MaxIncomingStreams == 0 { r.QUICConfig.MaxIncomingStreams = -1 // don't allow any bidirectional streams } return nil } func (r *RoundTripper) getClient(ctx context.Context, hostname string, onlyCached bool) (rtc *roundTripperWithCount, isReused bool, err error) { r.mutex.Lock() defer r.mutex.Unlock() if r.clients == nil { r.clients = make(map[string]*roundTripperWithCount) } cl, ok := r.clients[hostname] if !ok { if onlyCached { return nil, false, ErrNoCachedConn } ctx, cancel := context.WithCancel(ctx) cl = &roundTripperWithCount{ dialing: make(chan struct{}), cancel: cancel, } go func() { defer close(cl.dialing) defer cancel() conn, rt, err := r.dial(ctx, hostname) if err != nil { cl.dialErr = err return } cl.conn = conn cl.rt = rt }() r.clients[hostname] = cl } select { case <-cl.dialing: if cl.dialErr != nil { delete(r.clients, hostname) return nil, false, cl.dialErr } select { case <-cl.conn.HandshakeComplete(): isReused = true default: } default: } cl.useCount.Add(1) return cl, isReused, nil } func (r *RoundTripper) dial(ctx context.Context, hostname string) (quic.EarlyConnection, singleRoundTripper, error) { var tlsConf *tls.Config if r.TLSClientConfig == nil { tlsConf = &tls.Config{} } else { tlsConf = r.TLSClientConfig.Clone() } if tlsConf.ServerName == "" { sni, _, err := net.SplitHostPort(hostname) if err != nil { // It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port. sni = hostname } tlsConf.ServerName = sni } // Replace existing ALPNs by H3 tlsConf.NextProtos = []string{versionToALPN(r.QUICConfig.Versions[0])} dial := r.Dial if dial == nil { if r.transport == nil { udpConn, err := net.ListenUDP("udp", nil) if err != nil { return nil, nil, err } r.transport = &quic.Transport{Conn: udpConn} } dial = func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err } return r.transport.DialEarly(ctx, udpAddr, tlsCfg, cfg) } } conn, err := dial(ctx, hostname, tlsConf, r.QUICConfig) if err != nil { return nil, nil, err } return conn, r.newClient(conn), nil } func (r *RoundTripper) removeClient(hostname string) { r.mutex.Lock() defer r.mutex.Unlock() if r.clients == nil { return } delete(r.clients, hostname) } // Close closes the QUIC connections that this RoundTripper has used. // It also closes the underlying UDPConn if it is not nil. func (r *RoundTripper) Close() error { r.mutex.Lock() defer r.mutex.Unlock() for _, cl := range r.clients { if err := cl.Close(); err != nil { return err } } r.clients = nil if r.transport != nil { if err := r.transport.Close(); err != nil { return err } if err := r.transport.Conn.Close(); err != nil { return err } r.transport = nil } return nil } func closeRequestBody(req *http.Request) { if req.Body != nil { req.Body.Close() } } func validMethod(method string) bool { /* Method = "OPTIONS" ; Section 9.2 | "GET" ; Section 9.3 | "HEAD" ; Section 9.4 | "POST" ; Section 9.5 | "PUT" ; Section 9.6 | "DELETE" ; Section 9.7 | "TRACE" ; Section 9.8 | "CONNECT" ; Section 9.9 | extension-method extension-method = token token = 1* */ return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1 } // copied from net/http/http.go func isNotToken(r rune) bool { return !httpguts.IsTokenRune(r) } func (r *RoundTripper) CloseIdleConnections() { r.mutex.Lock() defer r.mutex.Unlock() for hostname, cl := range r.clients { if cl.useCount.Load() == 0 { cl.Close() delete(r.clients, hostname) } } } golang-github-lucas-clemente-quic-go-0.46.0/http3/roundtrip_test.go000066400000000000000000000515141465664453100252730ustar00rootroot00000000000000package http3 import ( "bytes" "context" "crypto/tls" "errors" "io" "net/http" "time" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) type mockBody struct { reader bytes.Reader readErr error closeErr error closed bool } // make sure the mockBody can be used as a http.Request.Body var _ io.ReadCloser = &mockBody{} func (m *mockBody) Read(p []byte) (int, error) { if m.readErr != nil { return 0, m.readErr } return m.reader.Read(p) } func (m *mockBody) SetData(data []byte) { m.reader = *bytes.NewReader(data) } func (m *mockBody) Close() error { m.closed = true return m.closeErr } var _ = Describe("RoundTripper", func() { var req *http.Request BeforeEach(func() { var err error req, err = http.NewRequest("GET", "https://www.example.org/file1.html", nil) Expect(err).ToNot(HaveOccurred()) }) It("rejects quic.Configs that allow multiple QUIC versions", func() { qconf := &quic.Config{ Versions: []quic.Version{protocol.Version2, protocol.Version1}, } rt := &RoundTripper{QUICConfig: qconf} _, err := rt.RoundTrip(req) Expect(err).To(MatchError("can only use a single QUIC version for dialing a HTTP/3 connection")) }) It("uses the default QUIC and TLS config if none is give", func() { var dialAddrCalled bool rt := &RoundTripper{ Dial: func(_ context.Context, _ string, tlsConf *tls.Config, quicConf *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Expect(quicConf.MaxIncomingStreams).To(Equal(defaultQuicConfig.MaxIncomingStreams)) Expect(tlsConf.NextProtos).To(Equal([]string{NextProtoH3})) Expect(quicConf.Versions).To(Equal([]protocol.Version{protocol.Version1})) dialAddrCalled = true return nil, errors.New("test done") }, } _, err := rt.RoundTripOpt(req, RoundTripOpt{}) Expect(err).To(MatchError("test done")) Expect(dialAddrCalled).To(BeTrue()) }) It("adds the port to the hostname, if none is given", func() { var dialAddrCalled bool rt := &RoundTripper{ Dial: func(_ context.Context, hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Expect(hostname).To(Equal("quic.clemente.io:443")) dialAddrCalled = true return nil, errors.New("test done") }, } req, err := http.NewRequest("GET", "https://quic.clemente.io:443", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTripOpt(req, RoundTripOpt{}) Expect(err).To(MatchError("test done")) Expect(dialAddrCalled).To(BeTrue()) }) It("sets the ServerName in the tls.Config, if not set", func() { const host = "foo.bar" var dialCalled bool rt := &RoundTripper{ Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Expect(tlsCfg.ServerName).To(Equal(host)) dialCalled = true return nil, errors.New("test done") }, } req, err := http.NewRequest("GET", "https://foo.bar", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTripOpt(req, RoundTripOpt{}) Expect(err).To(MatchError("test done")) Expect(dialCalled).To(BeTrue()) }) It("uses the TLS config and QUIC config", func() { tlsConf := &tls.Config{ ServerName: "foo.bar", NextProtos: []string{"proto foo", "proto bar"}, } quicConf := &quic.Config{MaxIdleTimeout: 3 * time.Nanosecond} var dialAddrCalled bool rt := &RoundTripper{ Dial: func(_ context.Context, host string, tlsConfP *tls.Config, quicConfP *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Expect(host).To(Equal("www.example.org:443")) Expect(tlsConfP.ServerName).To(Equal(tlsConf.ServerName)) Expect(tlsConfP.NextProtos).To(Equal([]string{NextProtoH3})) Expect(quicConfP.MaxIdleTimeout).To(Equal(quicConf.MaxIdleTimeout)) dialAddrCalled = true return nil, errors.New("test done") }, QUICConfig: quicConf, TLSClientConfig: tlsConf, } _, err := rt.RoundTripOpt(req, RoundTripOpt{}) Expect(err).To(MatchError("test done")) Expect(dialAddrCalled).To(BeTrue()) // make sure the original tls.Config was not modified Expect(tlsConf.NextProtos).To(Equal([]string{"proto foo", "proto bar"})) }) It("uses the custom dialer, if provided", func() { testErr := errors.New("test done") tlsConf := &tls.Config{ServerName: "foo.bar"} quicConf := &quic.Config{MaxIdleTimeout: 1337 * time.Second} // nolint:staticcheck // This is a test. ctx := context.WithValue(context.Background(), "foo", "bar") var dialerCalled bool rt := &RoundTripper{ Dial: func(ctxP context.Context, address string, tlsConfP *tls.Config, quicConfP *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Expect(ctx.Value("foo").(string)).To(Equal("bar")) Expect(address).To(Equal("www.example.org:443")) Expect(tlsConfP.ServerName).To(Equal("foo.bar")) Expect(quicConfP.MaxIdleTimeout).To(Equal(quicConf.MaxIdleTimeout)) dialerCalled = true return nil, testErr }, TLSClientConfig: tlsConf, QUICConfig: quicConf, } _, err := rt.RoundTripOpt(req.WithContext(ctx), RoundTripOpt{}) Expect(err).To(MatchError(testErr)) Expect(dialerCalled).To(BeTrue()) }) It("enables HTTP/3 Datagrams", func() { testErr := errors.New("handshake error") rt := &RoundTripper{ EnableDatagrams: true, Dial: func(_ context.Context, _ string, _ *tls.Config, quicConf *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Expect(quicConf.EnableDatagrams).To(BeTrue()) return nil, testErr }, } _, err := rt.RoundTripOpt(req, RoundTripOpt{}) Expect(err).To(MatchError(testErr)) }) It("requires quic.Config.EnableDatagrams if HTTP/3 datagrams are enabled", func() { rt := &RoundTripper{ QUICConfig: &quic.Config{EnableDatagrams: false}, EnableDatagrams: true, Dial: func(_ context.Context, _ string, _ *tls.Config, config *quic.Config) (quic.EarlyConnection, error) { return nil, errors.New("handshake error") }, } _, err := rt.RoundTrip(req) Expect(err).To(MatchError("HTTP Datagrams enabled, but QUIC Datagrams disabled")) }) It("creates new clients", func() { testErr := errors.New("test err") req1, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) req2, err := http.NewRequest("GET", "https://example.com/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) var hostsDialed []string rt := &RoundTripper{ Dial: func(_ context.Context, host string, _ *tls.Config, quicConf *quic.Config) (quic.EarlyConnection, error) { hostsDialed = append(hostsDialed, host) return nil, testErr }, } _, err = rt.RoundTrip(req1) Expect(err).To(MatchError(testErr)) _, err = rt.RoundTrip(req2) Expect(err).To(MatchError(testErr)) Expect(hostsDialed).To(Equal([]string{"quic-go.net:443", "example.com:443"})) }) Context("reusing clients", func() { var ( rt *RoundTripper req1, req2 *http.Request clientChan chan *MockSingleRoundTripper ) BeforeEach(func() { clientChan = make(chan *MockSingleRoundTripper, 16) rt = &RoundTripper{ newClient: func(quic.EarlyConnection) singleRoundTripper { select { case c := <-clientChan: return c default: Fail("no client") return nil } }, } var err error req1, err = http.NewRequest("GET", "https://quic-go.net/file1.html", nil) Expect(err).ToNot(HaveOccurred()) req2, err = http.NewRequest("GET", "https://quic-go.net/file2.html", nil) Expect(err).ToNot(HaveOccurred()) Expect(req1.URL).ToNot(Equal(req2.URL)) }) It("reuses existing clients", func() { cl := NewMockSingleRoundTripper(mockCtrl) clientChan <- cl conn := mockquic.NewMockEarlyConnection(mockCtrl) handshakeChan := make(chan struct{}) close(handshakeChan) conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2) cl.EXPECT().RoundTrip(req1).Return(&http.Response{Request: req1}, nil) cl.EXPECT().RoundTrip(req2).Return(&http.Response{Request: req2}, nil) var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ return conn, nil } rsp, err := rt.RoundTrip(req1) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Request).To(Equal(req1)) rsp, err = rt.RoundTrip(req2) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Request).To(Equal(req2)) Expect(count).To(Equal(1)) }) It("redials a connection if dialing failed", func() { cl1 := NewMockSingleRoundTripper(mockCtrl) clientChan <- cl1 req1, err := http.NewRequest("GET", "https://quic-go.net/foo.html", nil) Expect(err).ToNot(HaveOccurred()) req2, err := http.NewRequest("GET", "https://quic-go.net/bar.html", nil) Expect(err).ToNot(HaveOccurred()) testErr := errors.New("handshake error") conn := mockquic.NewMockEarlyConnection(mockCtrl) var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ if count == 1 { return nil, testErr } return conn, nil } handshakeChan := make(chan struct{}) close(handshakeChan) conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2) cl1.EXPECT().RoundTrip(req2).Return(&http.Response{Request: req2}, nil) _, err = rt.RoundTrip(req1) Expect(err).To(MatchError(testErr)) rsp, err := rt.RoundTrip(req2) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Request).To(Equal(req2)) Expect(count).To(Equal(2)) }) It("immediately removes a clients when a request errored", func() { cl1 := NewMockSingleRoundTripper(mockCtrl) clientChan <- cl1 cl2 := NewMockSingleRoundTripper(mockCtrl) clientChan <- cl2 req1, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) req2, err := http.NewRequest("GET", "https://quic-go.net/bar.html", nil) Expect(err).ToNot(HaveOccurred()) conn := mockquic.NewMockEarlyConnection(mockCtrl) var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ return conn, nil } testErr := errors.New("test err") handshakeChan := make(chan struct{}) close(handshakeChan) conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2) cl1.EXPECT().RoundTrip(req1).Return(nil, testErr) cl2.EXPECT().RoundTrip(req2).Return(&http.Response{Request: req2}, nil) _, err = rt.RoundTrip(req1) Expect(err).To(MatchError(testErr)) rsp, err := rt.RoundTrip(req2) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Request).To(Equal(req2)) Expect(count).To(Equal(2)) }) It("does not remove a client when a request returns context canceled error", func() { cl1 := NewMockSingleRoundTripper(mockCtrl) clientChan <- cl1 cl2 := NewMockSingleRoundTripper(mockCtrl) clientChan <- cl2 req1, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) req2, err := http.NewRequest("GET", "https://quic-go.net/bar.html", nil) Expect(err).ToNot(HaveOccurred()) conn := mockquic.NewMockEarlyConnection(mockCtrl) var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ return conn, nil } testErr := context.Canceled handshakeChan := make(chan struct{}) close(handshakeChan) conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2) cl1.EXPECT().RoundTrip(req1).Return(nil, testErr) cl1.EXPECT().RoundTrip(req2).Return(&http.Response{Request: req2}, nil) _, err = rt.RoundTrip(req1) Expect(err).To(MatchError(testErr)) rsp, err := rt.RoundTrip(req2) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Request).To(Equal(req2)) Expect(count).To(Equal(1)) }) It("recreates a client when a request times out", func() { var reqCount int cl1 := NewMockSingleRoundTripper(mockCtrl) cl1.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { reqCount++ if reqCount == 1 { // the first request is successful... Expect(req.URL).To(Equal(req1.URL)) return &http.Response{Request: req}, nil } // ... after that, the connection timed out in the background Expect(req.URL).To(Equal(req2.URL)) return nil, &qerr.IdleTimeoutError{} }).Times(2) cl2 := NewMockSingleRoundTripper(mockCtrl) cl2.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { return &http.Response{Request: req}, nil }) clientChan <- cl1 clientChan <- cl2 conn := mockquic.NewMockEarlyConnection(mockCtrl) handshakeChan := make(chan struct{}) close(handshakeChan) conn.EXPECT().HandshakeComplete().Return(handshakeChan).MaxTimes(2) var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ return conn, nil } rsp1, err := rt.RoundTrip(req1) Expect(err).ToNot(HaveOccurred()) Expect(rsp1.Request.RemoteAddr).To(Equal(req1.RemoteAddr)) rsp2, err := rt.RoundTrip(req2) Expect(err).ToNot(HaveOccurred()) Expect(rsp2.Request.RemoteAddr).To(Equal(req2.RemoteAddr)) }) It("only issues a request once, even if a timeout error occurs", func() { var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ return mockquic.NewMockEarlyConnection(mockCtrl), nil } rt.newClient = func(quic.EarlyConnection) singleRoundTripper { cl := NewMockSingleRoundTripper(mockCtrl) cl.EXPECT().RoundTrip(gomock.Any()).Return(nil, &qerr.IdleTimeoutError{}) return cl } _, err := rt.RoundTrip(req1) Expect(err).To(MatchError(&qerr.IdleTimeoutError{})) Expect(count).To(Equal(1)) }) It("handles a burst of requests", func() { wait := make(chan struct{}) reqs := make(chan struct{}, 2) cl := NewMockSingleRoundTripper(mockCtrl) cl.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(req *http.Request) (*http.Response, error) { reqs <- struct{}{} <-wait return nil, &qerr.IdleTimeoutError{} }).Times(2) clientChan <- cl conn := mockquic.NewMockEarlyConnection(mockCtrl) conn.EXPECT().HandshakeComplete().Return(wait).AnyTimes() var count int rt.Dial = func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { count++ return conn, nil } done := make(chan struct{}, 2) go func() { defer GinkgoRecover() defer func() { done <- struct{}{} }() _, err := rt.RoundTrip(req1) Expect(err).To(MatchError(&qerr.IdleTimeoutError{})) }() // wait for the first requests to be issued Eventually(reqs).Should(Receive()) go func() { defer GinkgoRecover() defer func() { done <- struct{}{} }() _, err := rt.RoundTrip(req2) Expect(err).To(MatchError(&qerr.IdleTimeoutError{})) }() Eventually(reqs).Should(Receive()) close(wait) // now return the requests Eventually(done).Should(Receive()) Eventually(done).Should(Receive()) Expect(count).To(Equal(1)) }) It("doesn't create new clients if RoundTripOpt.OnlyCachedConn is set", func() { req, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTripOpt(req, RoundTripOpt{OnlyCachedConn: true}) Expect(err).To(MatchError(ErrNoCachedConn)) }) }) Context("validating request", func() { var rt RoundTripper It("rejects plain HTTP requests", func() { req, err := http.NewRequest("GET", "http://www.example.org/", nil) req.Body = &mockBody{} Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req) Expect(err).To(MatchError("http3: unsupported protocol scheme: http")) Expect(req.Body.(*mockBody).closed).To(BeTrue()) }) It("rejects requests without a URL", func() { req.URL = nil req.Body = &mockBody{} _, err := rt.RoundTrip(req) Expect(err).To(MatchError("http3: nil Request.URL")) Expect(req.Body.(*mockBody).closed).To(BeTrue()) }) It("rejects request without a URL Host", func() { req.URL.Host = "" req.Body = &mockBody{} _, err := rt.RoundTrip(req) Expect(err).To(MatchError("http3: no Host in request URL")) Expect(req.Body.(*mockBody).closed).To(BeTrue()) }) It("doesn't try to close the body if the request doesn't have one", func() { req.URL = nil Expect(req.Body).To(BeNil()) _, err := rt.RoundTrip(req) Expect(err).To(MatchError("http3: nil Request.URL")) }) It("rejects requests without a header", func() { req.Header = nil req.Body = &mockBody{} _, err := rt.RoundTrip(req) Expect(err).To(MatchError("http3: nil Request.Header")) Expect(req.Body.(*mockBody).closed).To(BeTrue()) }) It("rejects requests with invalid header name fields", func() { req.Header.Add("foobär", "value") _, err := rt.RoundTrip(req) Expect(err).To(MatchError("http3: invalid http header field name \"foobär\"")) }) It("rejects requests with invalid header name values", func() { req.Header.Add("foo", string([]byte{0x7})) _, err := rt.RoundTrip(req) Expect(err.Error()).To(ContainSubstring("http3: invalid http header field value")) }) It("rejects requests with an invalid request method", func() { req.Method = "foobär" req.Body = &mockBody{} _, err := rt.RoundTrip(req) Expect(err).To(MatchError("http3: invalid method \"foobär\"")) Expect(req.Body.(*mockBody).closed).To(BeTrue()) }) }) Context("closing", func() { It("closes", func() { conn := mockquic.NewMockEarlyConnection(mockCtrl) rt := &RoundTripper{ Dial: func(context.Context, string, *tls.Config, *quic.Config) (quic.EarlyConnection, error) { return conn, nil }, newClient: func(quic.EarlyConnection) singleRoundTripper { cl := NewMockSingleRoundTripper(mockCtrl) cl.EXPECT().RoundTrip(gomock.Any()).Return(&http.Response{}, nil) return cl }, } req, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(0), "") Expect(rt.Close()).To(Succeed()) }) It("closes while dialing", func() { rt := &RoundTripper{ Dial: func(ctx context.Context, _ string, _ *tls.Config, _ *quic.Config) (quic.EarlyConnection, error) { defer GinkgoRecover() Eventually(ctx.Done()).Should(BeClosed()) return nil, errors.New("cancelled") }, } req, err := http.NewRequest("GET", "https://quic-go.net/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) errChan := make(chan error, 1) go func() { defer GinkgoRecover() _, err := rt.RoundTrip(req) errChan <- err }() Consistently(errChan, scaleDuration(30*time.Millisecond)).ShouldNot(Receive()) Expect(rt.Close()).To(Succeed()) var rtErr error Eventually(errChan).Should(Receive(&rtErr)) Expect(rtErr).To(MatchError("cancelled")) }) It("closes idle connections", func() { conn1 := mockquic.NewMockEarlyConnection(mockCtrl) conn2 := mockquic.NewMockEarlyConnection(mockCtrl) rt := &RoundTripper{ Dial: func(_ context.Context, hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlyConnection, error) { switch hostname { case "site1.com:443": return conn1, nil case "site2.com:443": return conn2, nil default: Fail("unexpected hostname") return nil, errors.New("unexpected hostname") } }, } req1, err := http.NewRequest("GET", "https://site1.com", nil) Expect(err).ToNot(HaveOccurred()) req2, err := http.NewRequest("GET", "https://site2.com", nil) Expect(err).ToNot(HaveOccurred()) Expect(req1.Host).ToNot(Equal(req2.Host)) ctx1, cancel1 := context.WithCancel(context.Background()) ctx2, cancel2 := context.WithCancel(context.Background()) req1 = req1.WithContext(ctx1) req2 = req2.WithContext(ctx2) roundTripCalled := make(chan struct{}) reqFinished := make(chan struct{}) rt.newClient = func(quic.EarlyConnection) singleRoundTripper { cl := NewMockSingleRoundTripper(mockCtrl) cl.EXPECT().RoundTrip(gomock.Any()).DoAndReturn(func(r *http.Request) (*http.Response, error) { roundTripCalled <- struct{}{} <-r.Context().Done() return nil, nil }) return cl } go func() { rt.RoundTrip(req1) reqFinished <- struct{}{} }() go func() { rt.RoundTrip(req2) reqFinished <- struct{}{} }() <-roundTripCalled <-roundTripCalled // Both two requests are started. cancel1() <-reqFinished // req1 is finished conn1.EXPECT().CloseWithError(gomock.Any(), gomock.Any()) rt.CloseIdleConnections() cancel2() <-reqFinished // all requests are finished conn2.EXPECT().CloseWithError(gomock.Any(), gomock.Any()) rt.CloseIdleConnections() }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/server.go000066400000000000000000000561561465664453100235230ustar00rootroot00000000000000package http3 import ( "context" "crypto/tls" "errors" "fmt" "io" "log/slog" "net" "net/http" "runtime" "strconv" "strings" "sync" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" ) // allows mocking of quic.Listen and quic.ListenAddr var ( quicListen = func(conn net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { return quic.ListenEarly(conn, tlsConf, config) } quicListenAddr = func(addr string, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { return quic.ListenAddrEarly(addr, tlsConf, config) } ) // NextProtoH3 is the ALPN protocol negotiated during the TLS handshake, for QUIC v1 and v2. const NextProtoH3 = "h3" // StreamType is the stream type of a unidirectional stream. type StreamType uint64 const ( streamTypeControlStream = 0 streamTypePushStream = 1 streamTypeQPACKEncoderStream = 2 streamTypeQPACKDecoderStream = 3 ) // A QUICEarlyListener listens for incoming QUIC connections. type QUICEarlyListener interface { Accept(context.Context) (quic.EarlyConnection, error) Addr() net.Addr io.Closer } var _ QUICEarlyListener = &quic.EarlyListener{} func versionToALPN(v protocol.Version) string { //nolint:exhaustive // These are all the versions we care about. switch v { case protocol.Version1, protocol.Version2: return NextProtoH3 default: return "" } } // ConfigureTLSConfig creates a new tls.Config which can be used // to create a quic.Listener meant for serving http3. The created // tls.Config adds the functionality of detecting the used QUIC version // in order to set the correct ALPN value for the http3 connection. func ConfigureTLSConfig(tlsConf *tls.Config) *tls.Config { // The tls.Config used to setup the quic.Listener needs to have the GetConfigForClient callback set. // That way, we can get the QUIC version and set the correct ALPN value. return &tls.Config{ GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { // determine the ALPN from the QUIC version used proto := NextProtoH3 val := ch.Context().Value(quic.QUICVersionContextKey) if v, ok := val.(quic.Version); ok { proto = versionToALPN(v) } config := tlsConf if tlsConf.GetConfigForClient != nil { getConfigForClient := tlsConf.GetConfigForClient var err error conf, err := getConfigForClient(ch) if err != nil { return nil, err } if conf != nil { config = conf } } if config == nil { return nil, nil } // Workaround for https://github.com/golang/go/issues/60506. // This initializes the session tickets _before_ cloning the config. _, _ = config.DecryptTicket(nil, tls.ConnectionState{}) config = config.Clone() config.NextProtos = []string{proto} return config, nil }, } } // contextKey is a value for use with context.WithValue. It's used as // a pointer so it fits in an interface{} without allocation. type contextKey struct { name string } func (k *contextKey) String() string { return "quic-go/http3 context value " + k.name } // ServerContextKey is a context key. It can be used in HTTP // handlers with Context.Value to access the server that // started the handler. The associated value will be of // type *http3.Server. var ServerContextKey = &contextKey{"http3-server"} // RemoteAddrContextKey is a context key. It can be used in // HTTP handlers with Context.Value to access the remote // address of the connection. The associated value will be of // type net.Addr. // // Use this value instead of [http.Request.RemoteAddr] if you // require access to the remote address of the connection rather // than its string representation. var RemoteAddrContextKey = &contextKey{"remote-addr"} // listenerInfo contains info about specific listener added with addListener type listenerInfo struct { port int // 0 means that no info about port is available } // Server is a HTTP/3 server. type Server struct { // Addr optionally specifies the UDP address for the server to listen on, // in the form "host:port". // // When used by ListenAndServe and ListenAndServeTLS methods, if empty, // ":https" (port 443) is used. See net.Dial for details of the address // format. // // Otherwise, if Port is not set and underlying QUIC listeners do not // have valid port numbers, the port part is used in Alt-Svc headers set // with SetQUICHeaders. Addr string // Port is used in Alt-Svc response headers set with SetQUICHeaders. If // needed Port can be manually set when the Server is created. // // This is useful when a Layer 4 firewall is redirecting UDP traffic and // clients must use a port different from the port the Server is // listening on. Port int // TLSConfig provides a TLS configuration for use by server. It must be // set for ListenAndServe and Serve methods. TLSConfig *tls.Config // QUICConfig provides the parameters for QUIC connection created with Serve. // If nil, it uses reasonable default values. // // Configured versions are also used in Alt-Svc response header set with SetQUICHeaders. QUICConfig *quic.Config // Handler is the HTTP request handler to use. If not set, defaults to // http.NotFound. Handler http.Handler // EnableDatagrams enables support for HTTP/3 datagrams (RFC 9297). // If set to true, QUICConfig.EnableDatagrams will be set. EnableDatagrams bool // MaxHeaderBytes controls the maximum number of bytes the server will // read parsing the request HEADERS frame. It does not limit the size of // the request body. If zero or negative, http.DefaultMaxHeaderBytes is // used. MaxHeaderBytes int // AdditionalSettings specifies additional HTTP/3 settings. // It is invalid to specify any settings defined by RFC 9114 (HTTP/3) and RFC 9297 (HTTP Datagrams). AdditionalSettings map[uint64]uint64 // StreamHijacker, when set, is called for the first unknown frame parsed on a bidirectional stream. // It is called right after parsing the frame type. // If parsing the frame type fails, the error is passed to the callback. // In that case, the frame type will not be set. // Callers can either ignore the frame and return control of the stream back to HTTP/3 // (by returning hijacked false). // Alternatively, callers can take over the QUIC stream (by returning hijacked true). StreamHijacker func(FrameType, quic.ConnectionTracingID, quic.Stream, error) (hijacked bool, err error) // UniStreamHijacker, when set, is called for unknown unidirectional stream of unknown stream type. // If parsing the stream type fails, the error is passed to the callback. // In that case, the stream type will not be set. UniStreamHijacker func(StreamType, quic.ConnectionTracingID, quic.ReceiveStream, error) (hijacked bool) // IdleTimeout specifies how long until idle clients connection should be // closed. Idle refers only to the HTTP/3 layer, activity at the QUIC layer // like PING frames are not considered. // If zero or negative, there is no timeout. IdleTimeout time.Duration // ConnContext optionally specifies a function that modifies the context used for a new connection c. // The provided ctx has a ServerContextKey value. ConnContext func(ctx context.Context, c quic.Connection) context.Context Logger *slog.Logger mutex sync.RWMutex listeners map[*QUICEarlyListener]listenerInfo closed bool altSvcHeader string } // ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. // // If s.Addr is blank, ":https" is used. func (s *Server) ListenAndServe() error { ln, err := s.setupListenerForConn(s.TLSConfig, nil) if err != nil { return err } defer s.removeListener(&ln) return s.serveListener(ln) } // ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. // // If s.Addr is blank, ":https" is used. func (s *Server) ListenAndServeTLS(certFile, keyFile string) error { var err error certs := make([]tls.Certificate, 1) certs[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return err } // We currently only use the cert-related stuff from tls.Config, // so we don't need to make a full copy. ln, err := s.setupListenerForConn(&tls.Config{Certificates: certs}, nil) if err != nil { return err } defer s.removeListener(&ln) return s.serveListener(ln) } // Serve an existing UDP connection. // It is possible to reuse the same connection for outgoing connections. // Closing the server does not close the connection. func (s *Server) Serve(conn net.PacketConn) error { ln, err := s.setupListenerForConn(s.TLSConfig, conn) if err != nil { return err } defer s.removeListener(&ln) return s.serveListener(ln) } // ServeQUICConn serves a single QUIC connection. func (s *Server) ServeQUICConn(conn quic.Connection) error { return s.handleConn(conn) } // ServeListener serves an existing QUIC listener. // Make sure you use http3.ConfigureTLSConfig to configure a tls.Config // and use it to construct a http3-friendly QUIC listener. // Closing the server does close the listener. // ServeListener always returns a non-nil error. After Shutdown or Close, the returned error is http.ErrServerClosed. func (s *Server) ServeListener(ln QUICEarlyListener) error { s.mutex.Lock() if err := s.addListener(&ln); err != nil { s.mutex.Unlock() return err } s.mutex.Unlock() defer s.removeListener(&ln) return s.serveListener(ln) } func (s *Server) serveListener(ln QUICEarlyListener) error { for { conn, err := ln.Accept(context.Background()) if err == quic.ErrServerClosed { return http.ErrServerClosed } if err != nil { return err } go func() { if err := s.handleConn(conn); err != nil { if s.Logger != nil { s.Logger.Debug("handling connection failed", "error", err) } } }() } } var errServerWithoutTLSConfig = errors.New("use of http3.Server without TLSConfig") func (s *Server) setupListenerForConn(tlsConf *tls.Config, conn net.PacketConn) (QUICEarlyListener, error) { if tlsConf == nil { return nil, errServerWithoutTLSConfig } baseConf := ConfigureTLSConfig(tlsConf) quicConf := s.QUICConfig if quicConf == nil { quicConf = &quic.Config{Allow0RTT: true} } else { quicConf = s.QUICConfig.Clone() } if s.EnableDatagrams { quicConf.EnableDatagrams = true } s.mutex.Lock() defer s.mutex.Unlock() closed := s.closed if closed { return nil, http.ErrServerClosed } var ln QUICEarlyListener var err error if conn == nil { addr := s.Addr if addr == "" { addr = ":https" } ln, err = quicListenAddr(addr, baseConf, quicConf) } else { ln, err = quicListen(conn, baseConf, quicConf) } if err != nil { return nil, err } if err := s.addListener(&ln); err != nil { return nil, err } return ln, nil } func extractPort(addr string) (int, error) { _, portStr, err := net.SplitHostPort(addr) if err != nil { return 0, err } portInt, err := net.LookupPort("tcp", portStr) if err != nil { return 0, err } return portInt, nil } func (s *Server) generateAltSvcHeader() { if len(s.listeners) == 0 { // Don't announce any ports since no one is listening for connections s.altSvcHeader = "" return } // This code assumes that we will use protocol.SupportedVersions if no quic.Config is passed. supportedVersions := protocol.SupportedVersions if s.QUICConfig != nil && len(s.QUICConfig.Versions) > 0 { supportedVersions = s.QUICConfig.Versions } // keep track of which have been seen so we don't yield duplicate values seen := make(map[string]struct{}, len(supportedVersions)) var versionStrings []string for _, version := range supportedVersions { if v := versionToALPN(version); len(v) > 0 { if _, ok := seen[v]; !ok { versionStrings = append(versionStrings, v) seen[v] = struct{}{} } } } var altSvc []string addPort := func(port int) { for _, v := range versionStrings { altSvc = append(altSvc, fmt.Sprintf(`%s=":%d"; ma=2592000`, v, port)) } } if s.Port != 0 { // if Port is specified, we must use it instead of the // listener addresses since there's a reason it's specified. addPort(s.Port) } else { // if we have some listeners assigned, try to find ports // which we can announce, otherwise nothing should be announced validPortsFound := false for _, info := range s.listeners { if info.port != 0 { addPort(info.port) validPortsFound = true } } if !validPortsFound { if port, err := extractPort(s.Addr); err == nil { addPort(port) } } } s.altSvcHeader = strings.Join(altSvc, ",") } // We store a pointer to interface in the map set. This is safe because we only // call trackListener via Serve and can track+defer untrack the same pointer to // local variable there. We never need to compare a Listener from another caller. func (s *Server) addListener(l *QUICEarlyListener) error { if s.closed { return http.ErrServerClosed } if s.listeners == nil { s.listeners = make(map[*QUICEarlyListener]listenerInfo) } laddr := (*l).Addr() if port, err := extractPort(laddr.String()); err == nil { s.listeners[l] = listenerInfo{port} } else { logger := s.Logger if logger == nil { logger = slog.Default() } logger.Error("Unable to extract port from listener, will not be announced using SetQUICHeaders", "local addr", laddr, "error", err) s.listeners[l] = listenerInfo{} } s.generateAltSvcHeader() return nil } func (s *Server) removeListener(l *QUICEarlyListener) { s.mutex.Lock() defer s.mutex.Unlock() delete(s.listeners, l) s.generateAltSvcHeader() } func (s *Server) handleConn(conn quic.Connection) error { // send a SETTINGS frame str, err := conn.OpenUniStream() if err != nil { return fmt.Errorf("opening the control stream failed: %w", err) } b := make([]byte, 0, 64) b = quicvarint.Append(b, streamTypeControlStream) // stream type b = (&settingsFrame{ Datagram: s.EnableDatagrams, ExtendedConnect: true, Other: s.AdditionalSettings, }).Append(b) str.Write(b) ctx := conn.Context() ctx = context.WithValue(ctx, ServerContextKey, s) ctx = context.WithValue(ctx, http.LocalAddrContextKey, conn.LocalAddr()) ctx = context.WithValue(ctx, RemoteAddrContextKey, conn.RemoteAddr()) if s.ConnContext != nil { ctx = s.ConnContext(ctx, conn) if ctx == nil { panic("http3: ConnContext returned nil") } } hconn := newConnection( ctx, conn, s.EnableDatagrams, protocol.PerspectiveServer, s.Logger, s.IdleTimeout, ) go hconn.HandleUnidirectionalStreams(s.UniStreamHijacker) // Process all requests immediately. // It's the client's responsibility to decide which requests are eligible for 0-RTT. for { str, datagrams, err := hconn.acceptStream(context.Background()) if err != nil { var appErr *quic.ApplicationError if errors.As(err, &appErr) && appErr.ErrorCode == quic.ApplicationErrorCode(ErrCodeNoError) { return nil } return fmt.Errorf("accepting stream failed: %w", err) } go s.handleRequest(hconn, str, datagrams, hconn.decoder) } } func (s *Server) maxHeaderBytes() uint64 { if s.MaxHeaderBytes <= 0 { return http.DefaultMaxHeaderBytes } return uint64(s.MaxHeaderBytes) } func (s *Server) handleRequest(conn *connection, str quic.Stream, datagrams *datagrammer, decoder *qpack.Decoder) { var ufh unknownFrameHandlerFunc if s.StreamHijacker != nil { ufh = func(ft FrameType, e error) (processed bool, err error) { return s.StreamHijacker( ft, conn.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID), str, e, ) } } fp := &frameParser{conn: conn, r: str, unknownFrameHandler: ufh} frame, err := fp.ParseNext() if err != nil { if !errors.Is(err, errHijacked) { str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) } return } hf, ok := frame.(*headersFrame) if !ok { conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), "expected first frame to be a HEADERS frame") return } if hf.Length > s.maxHeaderBytes() { str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)) return } headerBlock := make([]byte, hf.Length) if _, err := io.ReadFull(str, headerBlock); err != nil { str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) return } hfs, err := decoder.DecodeFull(headerBlock) if err != nil { // TODO: use the right error code conn.CloseWithError(quic.ApplicationErrorCode(ErrCodeGeneralProtocolError), "expected first frame to be a HEADERS frame") return } req, err := requestFromHeaders(hfs) if err != nil { str.CancelRead(quic.StreamErrorCode(ErrCodeMessageError)) str.CancelWrite(quic.StreamErrorCode(ErrCodeMessageError)) return } connState := conn.ConnectionState().TLS req.TLS = &connState req.RemoteAddr = conn.RemoteAddr().String() // Check that the client doesn't send more data in DATA frames than indicated by the Content-Length header (if set). // See section 4.1.2 of RFC 9114. contentLength := int64(-1) if _, ok := req.Header["Content-Length"]; ok && req.ContentLength >= 0 { contentLength = req.ContentLength } hstr := newStream(str, conn, datagrams) body := newRequestBody(hstr, contentLength, conn.Context(), conn.ReceivedSettings(), conn.Settings) req.Body = body if s.Logger != nil { s.Logger.Debug("handling request", "method", req.Method, "host", req.Host, "uri", req.RequestURI) } ctx, cancel := context.WithCancel(conn.Context()) req = req.WithContext(ctx) context.AfterFunc(str.Context(), cancel) r := newResponseWriter(hstr, conn, req.Method == http.MethodHead, s.Logger) handler := s.Handler if handler == nil { handler = http.DefaultServeMux } // It's the client's responsibility to decide which requests are eligible for 0-RTT. var panicked bool func() { defer func() { if p := recover(); p != nil { panicked = true if p == http.ErrAbortHandler { return } // Copied from net/http/server.go const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] logger := s.Logger if logger == nil { logger = slog.Default() } logger.Error("http: panic serving", "arg", p, "trace", buf) } }() handler.ServeHTTP(r, req) }() if r.wasStreamHijacked() { return } // only write response when there is no panic if !panicked { // response not written to the client yet, set Content-Length if !r.headerWritten { if _, haveCL := r.header["Content-Length"]; !haveCL { r.header.Set("Content-Length", strconv.FormatInt(r.numWritten, 10)) } } r.Flush() } // abort the stream when there is a panic if panicked { str.CancelRead(quic.StreamErrorCode(ErrCodeInternalError)) str.CancelWrite(quic.StreamErrorCode(ErrCodeInternalError)) return } // If the EOF was read by the handler, CancelRead() is a no-op. str.CancelRead(quic.StreamErrorCode(ErrCodeNoError)) str.Close() } // Close the server immediately, aborting requests and sending CONNECTION_CLOSE frames to connected clients. // Close in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. func (s *Server) Close() error { s.mutex.Lock() defer s.mutex.Unlock() s.closed = true var err error for ln := range s.listeners { if cerr := (*ln).Close(); cerr != nil && err == nil { err = cerr } } return err } // CloseGracefully shuts down the server gracefully. The server sends a GOAWAY frame first, then waits for either timeout to trigger, or for all running requests to complete. // CloseGracefully in combination with ListenAndServe() (instead of Serve()) may race if it is called before a UDP socket is established. func (s *Server) CloseGracefully(timeout time.Duration) error { // TODO: implement return nil } // ErrNoAltSvcPort is the error returned by SetQUICHeaders when no port was found // for Alt-Svc to announce. This can happen if listening on a PacketConn without a port // (UNIX socket, for example) and no port is specified in Server.Port or Server.Addr. var ErrNoAltSvcPort = errors.New("no port can be announced, specify it explicitly using Server.Port or Server.Addr") // SetQUICHeaders can be used to set the proper headers that announce that this server supports HTTP/3. // The values set by default advertise all the ports the server is listening on, but can be // changed to a specific port by setting Server.Port before launching the server. // If no listener's Addr().String() returns an address with a valid port, Server.Addr will be used // to extract the port, if specified. // For example, a server launched using ListenAndServe on an address with port 443 would set: // // Alt-Svc: h3=":443"; ma=2592000 func (s *Server) SetQUICHeaders(hdr http.Header) error { s.mutex.RLock() defer s.mutex.RUnlock() if s.altSvcHeader == "" { return ErrNoAltSvcPort } // use the map directly to avoid constant canonicalization since the key is already canonicalized hdr["Alt-Svc"] = append(hdr["Alt-Svc"], s.altSvcHeader) return nil } // Deprecated: use SetQUICHeaders instead. func (s *Server) SetQuicHeaders(hdr http.Header) error { return s.SetQUICHeaders(hdr) } // ListenAndServeQUIC listens on the UDP network address addr and calls the // handler for HTTP/3 requests on incoming connections. http.DefaultServeMux is // used when handler is nil. func ListenAndServeQUIC(addr, certFile, keyFile string, handler http.Handler) error { server := &Server{ Addr: addr, Handler: handler, } return server.ListenAndServeTLS(certFile, keyFile) } // Deprecated: use ListenAndServeTLS instead. func ListenAndServe(addr, certFile, keyFile string, handler http.Handler) error { return ListenAndServeTLS(addr, certFile, keyFile, handler) } // ListenAndServeTLS listens on the given network address for both TLS/TCP and QUIC // connections in parallel. It returns if one of the two returns an error. // http.DefaultServeMux is used when handler is nil. // The correct Alt-Svc headers for QUIC are set. func ListenAndServeTLS(addr, certFile, keyFile string, handler http.Handler) error { // Load certs var err error certs := make([]tls.Certificate, 1) certs[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return err } // We currently only use the cert-related stuff from tls.Config, // so we don't need to make a full copy. config := &tls.Config{ Certificates: certs, } if addr == "" { addr = ":https" } // Open the listeners udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return err } udpConn, err := net.ListenUDP("udp", udpAddr) if err != nil { return err } defer udpConn.Close() if handler == nil { handler = http.DefaultServeMux } // Start the servers quicServer := &Server{ TLSConfig: config, Handler: handler, } hErr := make(chan error, 1) qErr := make(chan error, 1) go func() { hErr <- http.ListenAndServeTLS(addr, certFile, keyFile, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { quicServer.SetQUICHeaders(w.Header()) handler.ServeHTTP(w, r) })) }() go func() { qErr <- quicServer.Serve(udpConn) }() select { case err := <-hErr: quicServer.Close() return err case err := <-qErr: // Cannot close the HTTP server or wait for requests to complete properly :/ return err } } golang-github-lucas-clemente-quic-go-0.46.0/http3/server_test.go000066400000000000000000001272161465664453100245560ustar00rootroot00000000000000package http3 import ( "bytes" "context" "crypto/tls" "errors" "io" "log/slog" "net" "net/http" "runtime" "sync/atomic" "time" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/testdata" "github.com/quic-go/quic-go/quicvarint" "github.com/quic-go/qpack" "go.uber.org/mock/gomock" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" gmtypes "github.com/onsi/gomega/types" ) type mockAddr struct{ addr string } func (ma *mockAddr) Network() string { return "udp" } func (ma *mockAddr) String() string { return ma.addr } type mockAddrListener struct { *MockQUICEarlyListener addr *mockAddr } func (m *mockAddrListener) Addr() net.Addr { _ = m.MockQUICEarlyListener.Addr() return m.addr } func newMockAddrListener(addr string) *mockAddrListener { return &mockAddrListener{ MockQUICEarlyListener: NewMockQUICEarlyListener(mockCtrl), addr: &mockAddr{addr: addr}, } } type noPortListener struct { *mockAddrListener } func (m *noPortListener) Addr() net.Addr { _ = m.mockAddrListener.Addr() return &net.UnixAddr{ Net: "unix", Name: "/tmp/quic.sock", } } var _ = Describe("Server", func() { var ( s *Server origQuicListenAddr = quicListenAddr ) type testConnContextKey string BeforeEach(func() { s = &Server{ TLSConfig: testdata.GetTLSConfig(), ConnContext: func(ctx context.Context, c quic.Connection) context.Context { return context.WithValue(ctx, testConnContextKey("test"), c) }, } origQuicListenAddr = quicListenAddr }) AfterEach(func() { quicListenAddr = origQuicListenAddr }) Context("handling requests", func() { var ( qpackDecoder *qpack.Decoder str *mockquic.MockStream conn *connection exampleGetRequest *http.Request examplePostRequest *http.Request ) reqContext, reqContextCancel := context.WithCancel(context.Background()) decodeHeader := func(str io.Reader) map[string][]string { fields := make(map[string][]string) decoder := qpack.NewDecoder(nil) fp := frameParser{r: str} frame, err := fp.ParseNext() ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, frame).To(BeAssignableToTypeOf(&headersFrame{})) headersFrame := frame.(*headersFrame) data := make([]byte, headersFrame.Length) _, err = io.ReadFull(str, data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) hfs, err := decoder.DecodeFull(data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) for _, p := range hfs { fields[p.Name] = append(fields[p.Name], p.Value) } return fields } encodeRequest := func(req *http.Request) []byte { buf := &bytes.Buffer{} str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() rw := newRequestWriter() Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed()) return buf.Bytes() } setRequest := func(data []byte) { buf := bytes.NewBuffer(data) str.EXPECT().Read(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { if buf.Len() == 0 { return 0, io.EOF } return buf.Read(p) }).AnyTimes() } BeforeEach(func() { var err error exampleGetRequest, err = http.NewRequest("GET", "https://www.example.com", nil) Expect(err).ToNot(HaveOccurred()) examplePostRequest, err = http.NewRequest("POST", "https://www.example.com", bytes.NewReader([]byte("foobar"))) Expect(err).ToNot(HaveOccurred()) qpackDecoder = qpack.NewDecoder(nil) str = mockquic.NewMockStream(mockCtrl) str.EXPECT().Context().Return(reqContext).AnyTimes() str.EXPECT().StreamID().AnyTimes() qconn := mockquic.NewMockEarlyConnection(mockCtrl) addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} qconn.EXPECT().RemoteAddr().Return(addr).AnyTimes() qconn.EXPECT().LocalAddr().AnyTimes() qconn.EXPECT().ConnectionState().Return(quic.ConnectionState{}).AnyTimes() qconn.EXPECT().Context().Return(context.Background()).AnyTimes() conn = newConnection(context.Background(), qconn, false, protocol.PerspectiveServer, nil, 0) }) It("calls the HTTP handler function", func() { requestChan := make(chan *http.Request, 1) s.Handler = http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) { requestChan <- r }) setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) var req *http.Request Eventually(requestChan).Should(Receive(&req)) Expect(req.Host).To(Equal("www.example.com")) Expect(req.RemoteAddr).To(Equal("127.0.0.1:1337")) }) It("returns 200 with an empty handler", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) }) It("sets Content-Length when the handler doesn't flush to the client", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("foobar")) }) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) Expect(hfs).To(HaveKeyWithValue("content-length", []string{"6"})) // status, content-length, date, content-type Expect(hfs).To(HaveLen(4)) }) It("sets Content-Type when WriteHeader is called but response is not flushed", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) w.Write([]byte("")) }) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"404"})) Expect(hfs).To(HaveKeyWithValue("content-length", []string{"13"})) Expect(hfs).To(HaveKeyWithValue("content-type", []string{"text/html; charset=utf-8"})) }) It("not sets Content-Length when the handler flushes to the client", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("foobar")) // force flush w.(http.Flusher).Flush() }) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) // status, date, content-type Expect(hfs).To(HaveLen(3)) }) It("ignores calls to Write for responses to HEAD requests", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("foobar")) }) headRequest, err := http.NewRequest(http.MethodHead, "https://www.example.com", nil) Expect(err).ToNot(HaveOccurred()) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(headRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) Expect(responseBuf.Bytes()).To(BeEmpty()) }) It("response to HEAD request should also do content sniffing", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("")) }) headRequest, err := http.NewRequest(http.MethodHead, "https://www.example.com", nil) Expect(err).ToNot(HaveOccurred()) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(headRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) Expect(hfs).To(HaveKeyWithValue("content-length", []string{"13"})) Expect(hfs).To(HaveKeyWithValue("content-type", []string{"text/html; charset=utf-8"})) }) It("handles an aborting handler", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic(http.ErrAbortHandler) }) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeInternalError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeInternalError)) s.handleRequest(conn, str, nil, qpackDecoder) Expect(responseBuf.Bytes()).To(HaveLen(0)) }) It("handles a panicking handler", func() { var logBuf bytes.Buffer s.Logger = slog.New(slog.NewTextHandler(&logBuf, nil)) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("foobar") }) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeInternalError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeInternalError)) s.handleRequest(conn, str, nil, qpackDecoder) Expect(responseBuf.Bytes()).To(HaveLen(0)) Expect(logBuf.String()).To(ContainSubstring("http: panic serving")) Expect(logBuf.String()).To(ContainSubstring("foobar")) }) Context("hijacking bidirectional streams", func() { var conn *mockquic.MockEarlyConnection testDone := make(chan struct{}) BeforeEach(func() { testDone = make(chan struct{}) conn = mockquic.NewMockEarlyConnection(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().RemoteAddr().Return(&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337}).AnyTimes() conn.EXPECT().LocalAddr().AnyTimes() }) AfterEach(func() { testDone <- struct{}{} }) It("hijacks a bidirectional stream of unknown frame type", func() { id := quic.ConnectionTracingID(1337) frameTypeChan := make(chan FrameType, 1) s.StreamHijacker = func(ft FrameType, connTracingID quic.ConnectionTracingID, _ quic.Stream, e error) (hijacked bool, err error) { defer GinkgoRecover() Expect(e).ToNot(HaveOccurred()) Expect(connTracingID).To(Equal(id)) frameTypeChan <- ft return true, nil } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x41)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Context().Return(context.Background()).AnyTimes() unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() unknownStr.EXPECT().StreamID().AnyTimes() conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, id) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(frameTypeChan).Should(Receive(BeEquivalentTo(0x41))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("cancels writing when hijacker didn't hijack a bidirectional stream", func() { frameTypeChan := make(chan FrameType, 1) s.StreamHijacker = func(ft FrameType, _ quic.ConnectionTracingID, _ quic.Stream, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) frameTypeChan <- ft return false, nil } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x41)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Context().Return(context.Background()).AnyTimes() unknownStr.EXPECT().StreamID().AnyTimes() unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() unknownStr.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) unknownStr.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(frameTypeChan).Should(Receive(BeEquivalentTo(0x41))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("cancels writing when hijacker returned error", func() { frameTypeChan := make(chan FrameType, 1) s.StreamHijacker = func(ft FrameType, _ quic.ConnectionTracingID, _ quic.Stream, e error) (hijacked bool, err error) { Expect(e).ToNot(HaveOccurred()) frameTypeChan <- ft return false, errors.New("error in hijacker") } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x41)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Context().Return(context.Background()).AnyTimes() unknownStr.EXPECT().StreamID().AnyTimes() unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() unknownStr.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) unknownStr.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)) conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(frameTypeChan).Should(Receive(BeEquivalentTo(0x41))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("handles errors that occur when reading the stream type", func() { const strID = protocol.StreamID(1234 * 4) testErr := errors.New("test error") done := make(chan struct{}) s.StreamHijacker = func(ft FrameType, _ quic.ConnectionTracingID, str quic.Stream, err error) (bool, error) { defer close(done) Expect(ft).To(BeZero()) Expect(str.StreamID()).To(Equal(strID)) Expect(err).To(MatchError(testErr)) return true, nil } unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Context().Return(context.Background()).AnyTimes() unknownStr.EXPECT().StreamID().Return(strID).AnyTimes() unknownStr.EXPECT().Read(gomock.Any()).Return(0, testErr).AnyTimes() conn.EXPECT().AcceptStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(done).Should(BeClosed()) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) }) Context("hijacking unidirectional streams", func() { var conn *mockquic.MockEarlyConnection testDone := make(chan struct{}) BeforeEach(func() { testDone = make(chan struct{}) conn = mockquic.NewMockEarlyConnection(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().RemoteAddr().Return(&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337}).AnyTimes() conn.EXPECT().LocalAddr().AnyTimes() }) AfterEach(func() { testDone <- struct{}{} }) It("hijacks an unidirectional stream of unknown stream type", func() { id := quic.ConnectionTracingID(42) streamTypeChan := make(chan StreamType, 1) s.UniStreamHijacker = func(st StreamType, connTracingID quic.ConnectionTracingID, _ quic.ReceiveStream, err error) bool { Expect(err).ToNot(HaveOccurred()) Expect(connTracingID).To(Equal(id)) streamTypeChan <- st return true } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x54)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return unknownStr, nil }) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, id) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(streamTypeChan).Should(Receive(BeEquivalentTo(0x54))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("handles errors that occur when reading the stream type", func() { testErr := errors.New("test error") done := make(chan struct{}) unknownStr := mockquic.NewMockStream(mockCtrl) s.UniStreamHijacker = func(st StreamType, _ quic.ConnectionTracingID, str quic.ReceiveStream, err error) bool { defer close(done) Expect(st).To(BeZero()) Expect(str).To(Equal(unknownStr)) Expect(err).To(MatchError(testErr)) return true } unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { return 0, testErr }) conn.EXPECT().AcceptUniStream(gomock.Any()).Return(unknownStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(done).Should(BeClosed()) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) It("cancels reading when hijacker didn't hijack an unidirectional stream", func() { streamTypeChan := make(chan StreamType, 1) s.UniStreamHijacker = func(st StreamType, _ quic.ConnectionTracingID, _ quic.ReceiveStream, err error) bool { Expect(err).ToNot(HaveOccurred()) streamTypeChan <- st return false } buf := bytes.NewBuffer(quicvarint.Append(nil, 0x54)) unknownStr := mockquic.NewMockStream(mockCtrl) unknownStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() unknownStr.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeStreamCreationError)) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return unknownStr, nil }) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) ctx := context.WithValue(context.Background(), quic.ConnectionTracingKey, quic.ConnectionTracingID(1234)) conn.EXPECT().Context().Return(ctx).AnyTimes() s.handleConn(conn) Eventually(streamTypeChan).Should(Receive(BeEquivalentTo(0x54))) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to conn.CloseWithError }) }) Context("stream- and connection-level errors", func() { var conn *mockquic.MockEarlyConnection testDone := make(chan struct{}) BeforeEach(func() { testDone = make(chan struct{}) addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} conn = mockquic.NewMockEarlyConnection(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()) conn.EXPECT().Context().Return(context.Background()) conn.EXPECT().OpenUniStream().Return(controlStr, nil) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) conn.EXPECT().AcceptStream(gomock.Any()).Return(str, nil) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) conn.EXPECT().RemoteAddr().Return(addr).AnyTimes() conn.EXPECT().LocalAddr().AnyTimes() conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}).AnyTimes() conn.EXPECT().Context().Return(context.Background()).AnyTimes() }) AfterEach(func() { testDone <- struct{}{} }) It("cancels reading when client sends a body in GET request", func() { var handlerCalled bool s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerCalled = true }) requestData := encodeRequest(exampleGetRequest) b := (&dataFrame{Length: 6}).Append(nil) // add a body b = append(b, []byte("foobar")...) responseBuf := &bytes.Buffer{} setRequest(append(requestData, b...)) done := make(chan struct{}) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeNoError)) str.EXPECT().Close().Do(func() error { close(done); return nil }) s.handleConn(conn) Eventually(done).Should(BeClosed()) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) Expect(handlerCalled).To(BeTrue()) }) It("doesn't close the stream if the stream was hijacked (via HTTPStream)", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer close(handlerCalled) w.(HTTPStreamer).HTTPStream() str.Write([]byte("foobar")) }) requestData := encodeRequest(exampleGetRequest) b := (&dataFrame{Length: 6}).Append(nil) // add a body b = append(b, []byte("foobar")...) setRequest(append(requestData, b...)) var buf bytes.Buffer str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes() s.handleConn(conn) Eventually(handlerCalled).Should(BeClosed()) // The buffer is expected to contain: // 1. The response header (in a HEADERS frame) // 2. the "foobar" (unframed) fp := frameParser{r: &buf} frame, err := fp.ParseNext() Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&headersFrame{})) df := frame.(*headersFrame) data := make([]byte, df.Length) _, err = io.ReadFull(&buf, data) Expect(err).ToNot(HaveOccurred()) hdrs, err := qpackDecoder.DecodeFull(data) Expect(err).ToNot(HaveOccurred()) Expect(hdrs).To(ContainElement(qpack.HeaderField{Name: ":status", Value: "200"})) Expect(buf.Bytes()).To(Equal([]byte("foobar"))) }) It("errors when the client sends a too large header frame", func() { s.MaxHeaderBytes = 20 s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { Fail("Handler should not be called.") }) requestData := encodeRequest(exampleGetRequest) b := (&dataFrame{Length: 6}).Append(nil) // add a body b = append(b, []byte("foobar")...) responseBuf := &bytes.Buffer{} setRequest(append(requestData, b...)) done := make(chan struct{}) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)).Do(func(quic.StreamErrorCode) { close(done) }) s.handleConn(conn) Eventually(done).Should(BeClosed()) }) It("handles a request for which the client immediately resets the stream", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { close(handlerCalled) }) testErr := errors.New("stream reset") done := make(chan struct{}) str.EXPECT().Read(gomock.Any()).Return(0, testErr) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete)).Do(func(quic.StreamErrorCode) { close(done) }) s.handleConn(conn) Consistently(handlerCalled).ShouldNot(BeClosed()) }) It("closes the connection when the first frame is not a HEADERS frame", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { close(handlerCalled) }) b := (&dataFrame{}).Append(nil) setRequest(b) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() done := make(chan struct{}) conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(ErrCodeFrameUnexpected), gomock.Any()).Do(func(quic.ApplicationErrorCode, string) error { close(done) return nil }) s.handleConn(conn) Eventually(done).Should(BeClosed()) }) It("rejects a request that has too large request headers", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { close(handlerCalled) }) // use 2*DefaultMaxHeaderBytes here. qpack will compress the request, // but the request will still end up larger than DefaultMaxHeaderBytes. url := bytes.Repeat([]byte{'a'}, http.DefaultMaxHeaderBytes*2) req, err := http.NewRequest(http.MethodGet, "https://"+string(url), nil) Expect(err).ToNot(HaveOccurred()) setRequest(encodeRequest(req)) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() done := make(chan struct{}) str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeFrameError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeFrameError)).Do(func(quic.StreamErrorCode) { close(done) }) s.handleConn(conn) Eventually(done).Should(BeClosed()) }) }) It("resets the stream when the body of POST request is not read, and the request handler replaces the request.Body", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r.Body = struct { io.Reader io.Closer }{} close(handlerCalled) }) setRequest(encodeRequest(examplePostRequest)) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeNoError)) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) Eventually(handlerCalled).Should(BeClosed()) }) It("cancels the request context when the stream is closed", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() // The context is canceled via context.AfterFunc, // which performs the cancellation in a new Go routine. Eventually(r.Context().Done()).Should(BeClosed()) Expect(r.Context().Err()).To(MatchError(context.Canceled)) close(handlerCalled) }) setRequest(encodeRequest(examplePostRequest)) reqContextCancel() str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeNoError)) str.EXPECT().Close() s.handleRequest(conn, str, nil, qpackDecoder) Eventually(handlerCalled).Should(BeClosed()) }) }) Context("setting http headers", func() { BeforeEach(func() { s.QUICConfig = &quic.Config{Versions: []protocol.Version{protocol.Version1}} }) var ln1 QUICEarlyListener var ln2 QUICEarlyListener expected := http.Header{ "Alt-Svc": {`h3=":443"; ma=2592000`}, } addListener := func(addr string, ln *QUICEarlyListener) { mln := newMockAddrListener(addr) mln.EXPECT().Addr() *ln = mln s.addListener(ln) } removeListener := func(ln *QUICEarlyListener) { s.removeListener(ln) } checkSetHeaders := func(expected gmtypes.GomegaMatcher) { hdr := http.Header{} Expect(s.SetQUICHeaders(hdr)).To(Succeed()) Expect(hdr).To(expected) } checkSetHeaderError := func() { hdr := http.Header{} Expect(s.SetQUICHeaders(hdr)).To(Equal(ErrNoAltSvcPort)) } It("sets proper headers with numeric port", func() { addListener(":443", &ln1) checkSetHeaders(Equal(expected)) removeListener(&ln1) checkSetHeaderError() }) It("sets proper headers with full addr", func() { addListener("127.0.0.1:443", &ln1) checkSetHeaders(Equal(expected)) removeListener(&ln1) checkSetHeaderError() }) It("sets proper headers with string port", func() { addListener(":https", &ln1) checkSetHeaders(Equal(expected)) removeListener(&ln1) checkSetHeaderError() }) It("works multiple times", func() { addListener(":https", &ln1) checkSetHeaders(Equal(expected)) checkSetHeaders(Equal(expected)) removeListener(&ln1) checkSetHeaderError() }) It("works if the quic.Config sets QUIC versions", func() { s.QUICConfig.Versions = []quic.Version{quic.Version1, quic.Version2} addListener(":443", &ln1) checkSetHeaders(Equal(http.Header{"Alt-Svc": {`h3=":443"; ma=2592000`}})) removeListener(&ln1) checkSetHeaderError() }) It("uses s.Port if set to a non-zero value", func() { s.Port = 8443 addListener(":443", &ln1) checkSetHeaders(Equal(http.Header{"Alt-Svc": {`h3=":8443"; ma=2592000`}})) removeListener(&ln1) checkSetHeaderError() }) It("uses s.Addr if listeners don't have ports available", func() { s.Addr = ":443" var logBuf bytes.Buffer s.Logger = slog.New(slog.NewTextHandler(&logBuf, nil)) mln := &noPortListener{newMockAddrListener("")} mln.EXPECT().Addr() ln1 = mln s.addListener(&ln1) checkSetHeaders(Equal(expected)) s.removeListener(&ln1) checkSetHeaderError() Expect(logBuf.String()).To(ContainSubstring("Unable to extract port from listener, will not be announced using SetQUICHeaders")) }) It("properly announces multiple listeners", func() { addListener(":443", &ln1) addListener(":8443", &ln2) checkSetHeaders(Or( Equal(http.Header{"Alt-Svc": {`h3=":443"; ma=2592000,h3=":8443"; ma=2592000`}}), Equal(http.Header{"Alt-Svc": {`h3=":8443"; ma=2592000,h3=":443"; ma=2592000`}}), )) removeListener(&ln1) removeListener(&ln2) checkSetHeaderError() }) It("doesn't duplicate Alt-Svc values", func() { s.QUICConfig.Versions = []quic.Version{quic.Version1, quic.Version1} addListener(":443", &ln1) checkSetHeaders(Equal(http.Header{"Alt-Svc": {`h3=":443"; ma=2592000`}})) removeListener(&ln1) checkSetHeaderError() }) }) It("errors when ListenAndServe is called with s.TLSConfig nil", func() { Expect((&Server{}).ListenAndServe()).To(MatchError(errServerWithoutTLSConfig)) }) It("should nop-Close() when s.server is nil", func() { Expect((&Server{}).Close()).To(Succeed()) }) It("errors when ListenAndServeTLS is called after Close", func() { serv := &Server{} Expect(serv.Close()).To(Succeed()) Expect(serv.ListenAndServeTLS(testdata.GetCertificatePaths())).To(MatchError(http.ErrServerClosed)) }) It("handles concurrent Serve and Close", func() { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) c, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) s.Serve(c) }() runtime.Gosched() s.Close() Eventually(done).Should(BeClosed()) }) Context("ConfigureTLSConfig", func() { It("advertises v1 by default", func() { conf := ConfigureTLSConfig(testdata.GetTLSConfig()) ln, err := quic.ListenAddr("localhost:0", conf, &quic.Config{Versions: []quic.Version{quic.Version1}}) Expect(err).ToNot(HaveOccurred()) defer ln.Close() c, err := quic.DialAddr(context.Background(), ln.Addr().String(), &tls.Config{InsecureSkipVerify: true, NextProtos: []string{NextProtoH3}}, nil) Expect(err).ToNot(HaveOccurred()) defer c.CloseWithError(0, "") Expect(c.ConnectionState().TLS.NegotiatedProtocol).To(Equal(NextProtoH3)) }) It("sets the GetConfigForClient callback if no tls.Config is given", func() { var receivedConf *tls.Config quicListenAddr = func(addr string, tlsConf *tls.Config, _ *quic.Config) (QUICEarlyListener, error) { receivedConf = tlsConf return nil, errors.New("listen err") } Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf).ToNot(BeNil()) }) It("sets the ALPN for tls.Configs returned by the tls.GetConfigForClient", func() { tlsConf := &tls.Config{ GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { c := testdata.GetTLSConfig() c.NextProtos = []string{"foo", "bar"} return c, nil }, } ln, err := quic.ListenAddr("localhost:0", ConfigureTLSConfig(tlsConf), &quic.Config{Versions: []quic.Version{quic.Version1}}) Expect(err).ToNot(HaveOccurred()) defer ln.Close() c, err := quic.DialAddr(context.Background(), ln.Addr().String(), &tls.Config{InsecureSkipVerify: true, NextProtos: []string{NextProtoH3}}, nil) Expect(err).ToNot(HaveOccurred()) defer c.CloseWithError(0, "") Expect(c.ConnectionState().TLS.NegotiatedProtocol).To(Equal(NextProtoH3)) }) It("works if GetConfigForClient returns a nil tls.Config", func() { tlsConf := testdata.GetTLSConfig() tlsConf.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) { return nil, nil } ln, err := quic.ListenAddr("localhost:0", ConfigureTLSConfig(tlsConf), &quic.Config{Versions: []quic.Version{quic.Version1}}) Expect(err).ToNot(HaveOccurred()) defer ln.Close() c, err := quic.DialAddr(context.Background(), ln.Addr().String(), &tls.Config{InsecureSkipVerify: true, NextProtos: []string{NextProtoH3}}, nil) Expect(err).ToNot(HaveOccurred()) defer c.CloseWithError(0, "") Expect(c.ConnectionState().TLS.NegotiatedProtocol).To(Equal(NextProtoH3)) }) It("sets the ALPN for tls.Configs returned by the tls.GetConfigForClient, if it returns a static tls.Config", func() { tlsClientConf := testdata.GetTLSConfig() tlsClientConf.NextProtos = []string{"foo", "bar"} tlsConf := &tls.Config{ GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { return tlsClientConf, nil }, } ln, err := quic.ListenAddr("localhost:0", ConfigureTLSConfig(tlsConf), &quic.Config{Versions: []quic.Version{quic.Version1}}) Expect(err).ToNot(HaveOccurred()) defer ln.Close() c, err := quic.DialAddr(context.Background(), ln.Addr().String(), &tls.Config{InsecureSkipVerify: true, NextProtos: []string{NextProtoH3}}, nil) Expect(err).ToNot(HaveOccurred()) defer c.CloseWithError(0, "") Expect(c.ConnectionState().TLS.NegotiatedProtocol).To(Equal(NextProtoH3)) // check that the original config was not modified Expect(tlsClientConf.NextProtos).To(Equal([]string{"foo", "bar"})) }) }) Context("Serve", func() { origQuicListen := quicListen AfterEach(func() { quicListen = origQuicListen }) It("serves a packet conn", func() { ln := newMockAddrListener(":443") conn := &net.UDPConn{} quicListen = func(c net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { Expect(c).To(Equal(conn)) return ln, nil } s := &Server{ TLSConfig: &tls.Config{}, } stopAccept := make(chan struct{}) ln.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.EarlyConnection, error) { <-stopAccept return nil, errors.New("closed") }) ln.EXPECT().Addr() // generate alt-svc headers done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) s.Serve(conn) }() Consistently(done).ShouldNot(BeClosed()) ln.EXPECT().Close().Do(func() error { close(stopAccept); return nil }) Expect(s.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("serves two packet conns", func() { ln1 := newMockAddrListener(":443") ln2 := newMockAddrListener(":8443") lns := make(chan QUICEarlyListener, 2) lns <- ln1 lns <- ln2 conn1 := &net.UDPConn{} conn2 := &net.UDPConn{} quicListen = func(c net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { return <-lns, nil } s := &Server{ TLSConfig: &tls.Config{}, } stopAccept1 := make(chan struct{}) ln1.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.EarlyConnection, error) { <-stopAccept1 return nil, errors.New("closed") }) ln1.EXPECT().Addr() // generate alt-svc headers stopAccept2 := make(chan struct{}) ln2.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.EarlyConnection, error) { <-stopAccept2 return nil, errors.New("closed") }) ln2.EXPECT().Addr() done1 := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done1) s.Serve(conn1) }() done2 := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done2) s.Serve(conn2) }() Consistently(done1).ShouldNot(BeClosed()) Expect(done2).ToNot(BeClosed()) ln1.EXPECT().Close().Do(func() error { close(stopAccept1); return nil }) ln2.EXPECT().Close().Do(func() error { close(stopAccept2); return nil }) Expect(s.Close()).To(Succeed()) Eventually(done1).Should(BeClosed()) Eventually(done2).Should(BeClosed()) }) }) Context("ServeListener", func() { origQuicListen := quicListen AfterEach(func() { quicListen = origQuicListen }) It("serves a listener", func() { var called int32 ln := newMockAddrListener(":443") quicListen = func(conn net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { atomic.StoreInt32(&called, 1) return ln, nil } s := &Server{} stopAccept := make(chan struct{}) ln.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.EarlyConnection, error) { <-stopAccept return nil, errors.New("closed") }) ln.EXPECT().Addr() // generate alt-svc headers done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) s.ServeListener(ln) }() Consistently(func() int32 { return atomic.LoadInt32(&called) }).Should(Equal(int32(0))) Consistently(done).ShouldNot(BeClosed()) ln.EXPECT().Close().Do(func() error { close(stopAccept); return nil }) Expect(s.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("serves two listeners", func() { var called int32 ln1 := newMockAddrListener(":443") ln2 := newMockAddrListener(":8443") lns := make(chan QUICEarlyListener, 2) lns <- ln1 lns <- ln2 quicListen = func(c net.PacketConn, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { atomic.StoreInt32(&called, 1) return <-lns, nil } s := &Server{} stopAccept1 := make(chan struct{}) ln1.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.EarlyConnection, error) { <-stopAccept1 return nil, errors.New("closed") }) ln1.EXPECT().Addr() // generate alt-svc headers stopAccept2 := make(chan struct{}) ln2.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.EarlyConnection, error) { <-stopAccept2 return nil, errors.New("closed") }) ln2.EXPECT().Addr() done1 := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done1) s.ServeListener(ln1) }() done2 := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done2) s.ServeListener(ln2) }() Consistently(func() int32 { return atomic.LoadInt32(&called) }).Should(Equal(int32(0))) Consistently(done1).ShouldNot(BeClosed()) Expect(done2).ToNot(BeClosed()) ln1.EXPECT().Close().Do(func() error { close(stopAccept1); return nil }) ln2.EXPECT().Close().Do(func() error { close(stopAccept2); return nil }) Expect(s.Close()).To(Succeed()) Eventually(done1).Should(BeClosed()) Eventually(done2).Should(BeClosed()) }) }) Context("ServeQUICConn", func() { It("serves a QUIC connection", func() { mux := http.NewServeMux() mux.HandleFunc("/hello", func(w http.ResponseWriter, _ *http.Request) { w.Write([]byte("foobar")) }) s.Handler = mux tlsConf := testdata.GetTLSConfig() tlsConf.NextProtos = []string{NextProtoH3} conn := mockquic.NewMockEarlyConnection(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()) conn.EXPECT().LocalAddr() conn.EXPECT().RemoteAddr() conn.EXPECT().Context().Return(context.Background()) conn.EXPECT().OpenUniStream().Return(controlStr, nil) testDone := make(chan struct{}) conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }).MaxTimes(1) conn.EXPECT().AcceptStream(gomock.Any()).Return(nil, &quic.ApplicationError{ErrorCode: quic.ApplicationErrorCode(ErrCodeNoError)}) s.ServeQUICConn(conn) close(testDone) }) }) Context("ListenAndServe", func() { BeforeEach(func() { s.Addr = "localhost:0" }) AfterEach(func() { Expect(s.Close()).To(Succeed()) }) It("uses the quic.Config to start the QUIC server", func() { conf := &quic.Config{HandshakeIdleTimeout: time.Nanosecond} var receivedConf *quic.Config quicListenAddr = func(addr string, _ *tls.Config, config *quic.Config) (QUICEarlyListener, error) { receivedConf = config return nil, errors.New("listen err") } s.QUICConfig = conf Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf).To(Equal(conf)) }) }) It("closes gracefully", func() { Expect(s.CloseGracefully(0)).To(Succeed()) }) It("errors when listening fails", func() { testErr := errors.New("listen error") quicListenAddr = func(addr string, tlsConf *tls.Config, config *quic.Config) (QUICEarlyListener, error) { return nil, testErr } fullpem, privkey := testdata.GetCertificatePaths() Expect(ListenAndServeQUIC("", fullpem, privkey, nil)).To(MatchError(testErr)) }) It("supports H3_DATAGRAM", func() { s.EnableDatagrams = true var receivedConf *quic.Config quicListenAddr = func(addr string, _ *tls.Config, config *quic.Config) (QUICEarlyListener, error) { receivedConf = config return nil, errors.New("listen err") } Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf.EnableDatagrams).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/http3/state_tracking_stream.go000066400000000000000000000051121465664453100265540ustar00rootroot00000000000000package http3 import ( "context" "errors" "os" "sync" "github.com/quic-go/quic-go" ) var _ quic.Stream = &stateTrackingStream{} // stateTrackingStream is an implementation of quic.Stream that delegates // to an underlying stream // it takes care of proxying send and receive errors onto an implementation of // the errorSetter interface (intended to be occupied by a datagrammer) // it is also responsible for clearing the stream based on its ID from its // parent connection, this is done through the streamClearer interface when // both the send and receive sides are closed type stateTrackingStream struct { quic.Stream mx sync.Mutex sendErr error recvErr error clearer streamClearer setter errorSetter } type streamClearer interface { clearStream(quic.StreamID) } type errorSetter interface { SetSendError(error) SetReceiveError(error) } func newStateTrackingStream(s quic.Stream, clearer streamClearer, setter errorSetter) *stateTrackingStream { t := &stateTrackingStream{ Stream: s, clearer: clearer, setter: setter, } context.AfterFunc(s.Context(), func() { t.closeSend(context.Cause(s.Context())) }) return t } func (s *stateTrackingStream) closeSend(e error) { s.mx.Lock() defer s.mx.Unlock() // clear the stream the first time both the send // and receive are finished if s.sendErr == nil { if s.recvErr != nil { s.clearer.clearStream(s.StreamID()) } s.setter.SetSendError(e) s.sendErr = e } } func (s *stateTrackingStream) closeReceive(e error) { s.mx.Lock() defer s.mx.Unlock() // clear the stream the first time both the send // and receive are finished if s.recvErr == nil { if s.sendErr != nil { s.clearer.clearStream(s.StreamID()) } s.setter.SetReceiveError(e) s.recvErr = e } } func (s *stateTrackingStream) Close() error { s.closeSend(errors.New("write on closed stream")) return s.Stream.Close() } func (s *stateTrackingStream) CancelWrite(e quic.StreamErrorCode) { s.closeSend(&quic.StreamError{StreamID: s.Stream.StreamID(), ErrorCode: e}) s.Stream.CancelWrite(e) } func (s *stateTrackingStream) Write(b []byte) (int, error) { n, err := s.Stream.Write(b) if err != nil && !errors.Is(err, os.ErrDeadlineExceeded) { s.closeSend(err) } return n, err } func (s *stateTrackingStream) CancelRead(e quic.StreamErrorCode) { s.closeReceive(&quic.StreamError{StreamID: s.Stream.StreamID(), ErrorCode: e}) s.Stream.CancelRead(e) } func (s *stateTrackingStream) Read(b []byte) (int, error) { n, err := s.Stream.Read(b) if err != nil && !errors.Is(err, os.ErrDeadlineExceeded) { s.closeReceive(err) } return n, err } golang-github-lucas-clemente-quic-go-0.46.0/http3/state_tracking_stream_test.go000066400000000000000000000224601465664453100276200ustar00rootroot00000000000000package http3 import ( "bytes" "context" "errors" "io" "os" "github.com/quic-go/quic-go" mockquic "github.com/quic-go/quic-go/internal/mocks/quic" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var someStreamID = quic.StreamID(12) var _ = Describe("State Tracking Stream", func() { It("recognizes when the receive side is closed", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) buf := bytes.NewBuffer([]byte("foobar")) qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() for i := 0; i < 3; i++ { _, err := str.Read([]byte{0}) Expect(err).ToNot(HaveOccurred()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) } _, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(HaveLen(1)) Expect(setter.recvErrs[0]).To(Equal(io.EOF)) Expect(setter.sendErrs).To(BeEmpty()) }) It("recognizes local read cancellations", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) buf := bytes.NewBuffer([]byte("foobar")) qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() qstr.EXPECT().CancelRead(quic.StreamErrorCode(1337)) _, err := str.Read(make([]byte, 3)) Expect(err).ToNot(HaveOccurred()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) str.CancelRead(1337) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(HaveLen(1)) Expect(setter.recvErrs[0]).To(Equal(&quic.StreamError{StreamID: someStreamID, ErrorCode: 1337})) Expect(setter.sendErrs).To(BeEmpty()) }) It("recognizes remote cancellations", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) testErr := errors.New("test error") qstr.EXPECT().Read(gomock.Any()).Return(0, testErr) _, err := str.Read(make([]byte, 3)) Expect(err).To(MatchError(testErr)) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(HaveLen(1)) Expect(setter.recvErrs[0]).To(Equal(testErr)) Expect(setter.sendErrs).To(BeEmpty()) }) It("doesn't misinterpret read deadline errors", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) qstr.EXPECT().Read(gomock.Any()).Return(0, os.ErrDeadlineExceeded) _, err := str.Read(make([]byte, 3)) Expect(err).To(MatchError(os.ErrDeadlineExceeded)) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) }) It("recognizes when the send side is closed, when write errors", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) testErr := errors.New("test error") qstr.EXPECT().Write([]byte("foo")).Return(3, nil) qstr.EXPECT().Write([]byte("bar")).Return(0, testErr) _, err := str.Write([]byte("foo")) Expect(err).ToNot(HaveOccurred()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) _, err = str.Write([]byte("bar")) Expect(err).To(MatchError(testErr)) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(HaveLen(1)) Expect(setter.sendErrs[0]).To(Equal(testErr)) }) It("recognizes when the send side is closed, when write errors", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) qstr.EXPECT().Write([]byte("foo")).Return(0, os.ErrDeadlineExceeded) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) _, err := str.Write([]byte("foo")) Expect(err).To(MatchError(os.ErrDeadlineExceeded)) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) }) It("recognizes when the send side is closed, when CancelWrite is called", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) qstr.EXPECT().Write(gomock.Any()) qstr.EXPECT().CancelWrite(quic.StreamErrorCode(1337)) _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) str.CancelWrite(1337) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(HaveLen(1)) Expect(setter.sendErrs[0]).To(Equal(&quic.StreamError{StreamID: someStreamID, ErrorCode: 1337})) }) It("recognizes when the send side is closed, when the stream context is canceled", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes() ctx, cancel := context.WithCancelCause(context.Background()) qstr.EXPECT().Context().Return(ctx).AnyTimes() var ( clearer mockStreamClearer setter = mockErrorSetter{ sendSent: make(chan struct{}), } ) _ = newStateTrackingStream(qstr, &clearer, &setter) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(BeEmpty()) testErr := errors.New("test error") cancel(testErr) Eventually(setter.sendSent).Should(BeClosed()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(BeEmpty()) Expect(setter.sendErrs).To(HaveLen(1)) Expect(setter.sendErrs[0]).To(Equal(testErr)) }) It("clears the stream when receive is closed followed by send is closed", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) buf := bytes.NewBuffer([]byte("foobar")) qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() _, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(clearer.cleared).To(BeNil()) Expect(setter.recvErrs).To(HaveLen(1)) Expect(setter.recvErrs[0]).To(Equal(io.EOF)) testErr := errors.New("test error") qstr.EXPECT().Write([]byte("bar")).Return(0, testErr) _, err = str.Write([]byte("bar")) Expect(err).To(MatchError(testErr)) Expect(setter.sendErrs).To(HaveLen(1)) Expect(setter.sendErrs[0]).To(Equal(testErr)) Expect(clearer.cleared).To(Equal(&someStreamID)) }) It("clears the stream when send is closed followed by receive is closed", func() { qstr := mockquic.NewMockStream(mockCtrl) qstr.EXPECT().StreamID().AnyTimes().Return(someStreamID) qstr.EXPECT().Context().Return(context.Background()).AnyTimes() var ( clearer mockStreamClearer setter mockErrorSetter str = newStateTrackingStream(qstr, &clearer, &setter) ) testErr := errors.New("test error") qstr.EXPECT().Write([]byte("bar")).Return(0, testErr) _, err := str.Write([]byte("bar")) Expect(err).To(MatchError(testErr)) Expect(clearer.cleared).To(BeNil()) Expect(setter.sendErrs).To(HaveLen(1)) Expect(setter.sendErrs[0]).To(Equal(testErr)) buf := bytes.NewBuffer([]byte("foobar")) qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() _, err = io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(setter.recvErrs).To(HaveLen(1)) Expect(setter.recvErrs[0]).To(Equal(io.EOF)) Expect(clearer.cleared).To(Equal(&someStreamID)) }) }) type mockStreamClearer struct { cleared *quic.StreamID } func (s *mockStreamClearer) clearStream(id quic.StreamID) { s.cleared = &id } type mockErrorSetter struct { sendErrs []error recvErrs []error sendSent chan struct{} } func (e *mockErrorSetter) SetSendError(err error) { e.sendErrs = append(e.sendErrs, err) if e.sendSent != nil { close(e.sendSent) } } func (e *mockErrorSetter) SetReceiveError(err error) { e.recvErrs = append(e.recvErrs, err) } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/000077500000000000000000000000001465664453100242155ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/gomodvendor/000077500000000000000000000000001465664453100265405ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/gomodvendor/.gitignore000066400000000000000000000000101465664453100305170ustar00rootroot00000000000000vendor/ golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/gomodvendor/go.mod000066400000000000000000000014431465664453100276500ustar00rootroot00000000000000module test go 1.21 // The version doesn't matter here, as we're replacing it with the currently checked out code anyway. require github.com/quic-go/quic-go v0.21.0 require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/quic-go/qpack v0.4.0 // indirect go.uber.org/mock v0.4.0 // indirect golang.org/x/crypto v0.23.0 // indirect golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.21.0 // indirect ) replace github.com/quic-go/quic-go => ../../ golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/gomodvendor/go.sum000066400000000000000000000113451465664453100276770ustar00rootroot00000000000000github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/gomodvendor/main.go000066400000000000000000000003011465664453100300050ustar00rootroot00000000000000package main import "github.com/quic-go/quic-go/http3" // The contents of this script don't matter. // We just need to make sure that quic-go is imported. func main() { _ = http3.Server{} } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/000077500000000000000000000000001465664453100251465ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/benchmark_test.go000066400000000000000000000032511465664453100304670ustar00rootroot00000000000000package self_test import ( "context" "fmt" "net" "testing" "github.com/quic-go/quic-go" ) func BenchmarkHandshake(b *testing.B) { b.ReportAllocs() ln, err := quic.ListenAddr("localhost:0", tlsConfig, nil) if err != nil { b.Fatal(err) } defer ln.Close() connChan := make(chan quic.Connection, 1) go func() { for { conn, err := ln.Accept(context.Background()) if err != nil { return } connChan <- conn } }() conn, err := net.ListenUDP("udp", nil) if err != nil { b.Fatal(err) } defer conn.Close() tr := &quic.Transport{Conn: conn} defer tr.Close() b.ResetTimer() for i := 0; i < b.N; i++ { c, err := tr.Dial(context.Background(), ln.Addr(), tlsClientConfig, nil) if err != nil { b.Fatal(err) } <-connChan c.CloseWithError(0, "") } } func BenchmarkStreamChurn(b *testing.B) { b.ReportAllocs() ln, err := quic.ListenAddr("localhost:0", tlsConfig, &quic.Config{MaxIncomingStreams: 1e10}) if err != nil { b.Fatal(err) } defer ln.Close() errChan := make(chan error, 1) go func() { conn, err := ln.Accept(context.Background()) if err != nil { errChan <- err return } close(errChan) for { str, err := conn.AcceptStream(context.Background()) if err != nil { return } str.Close() } }() c, err := quic.DialAddr(context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), tlsClientConfig, nil) if err != nil { b.Fatal(err) } if err := <-errChan; err != nil { b.Fatal(err) } b.ResetTimer() for i := 0; i < b.N; i++ { str, err := c.OpenStreamSync(context.Background()) if err != nil { b.Fatal(err) } if err := str.Close(); err != nil { b.Fatal(err) } } } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/cancelation_test.go000066400000000000000000000544761465664453100310340ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "math/rand" "net" "sync" "sync/atomic" "time" "github.com/quic-go/quic-go" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Stream Cancellations", func() { const numStreams = 80 Context("canceling the read side", func() { var server *quic.Listener // The server accepts a single connection, and then opens numStreams unidirectional streams. // On each of these streams, it (tries to) write PRData. // When done, it sends the number of canceled streams on the channel. runServer := func(data []byte) <-chan int32 { numCanceledStreamsChan := make(chan int32) var err error server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) var canceledCounter atomic.Int32 go func() { defer GinkgoRecover() var wg sync.WaitGroup wg.Add(numStreams) conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) if _, err := str.Write(data); err != nil { Expect(err).To(Equal(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), Remote: true, })) canceledCounter.Add(1) return } if err := str.Close(); err != nil { Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) canceledCounter.Add(1) return } }() } wg.Wait() numCanceledStreamsChan <- canceledCounter.Load() }() return numCanceledStreamsChan } AfterEach(func() { Expect(server.Close()).To(Succeed()) }) It("downloads when the client immediately cancels most streams", func() { serverCanceledCounterChan := runServer(PRData) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var canceledCounter atomic.Int32 var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) // cancel around 2/3 of the streams if rand.Int31()%3 != 0 { canceledCounter.Add(1) resetErr := quic.StreamErrorCode(str.StreamID()) str.CancelRead(resetErr) _, err := str.Read([]byte{0}) Expect(err).To(Equal(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: resetErr, Remote: false, })) return } data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) }() } wg.Wait() var serverCanceledCounter int32 Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter)) Expect(conn.CloseWithError(0, "")).To(Succeed()) clientCanceledCounter := canceledCounter.Load() // The server will only count a stream as being reset if learns about the cancelation before it finished writing all data. Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter)) fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams) Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10)) Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10)) }) It("downloads when the client cancels streams after reading from them for a bit", func() { serverCanceledCounterChan := runServer(PRData) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var canceledCounter atomic.Int32 var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) // only read some data from about 1/3 of the streams if rand.Int31()%3 != 0 { length := int(rand.Int31n(int32(len(PRData) - 1))) data, err := io.ReadAll(io.LimitReader(str, int64(length))) Expect(err).ToNot(HaveOccurred()) str.CancelRead(quic.StreamErrorCode(str.StreamID())) Expect(data).To(Equal(PRData[:length])) canceledCounter.Add(1) return } data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) }() } wg.Wait() var serverCanceledCounter int32 Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter)) Expect(conn.CloseWithError(0, "")).To(Succeed()) clientCanceledCounter := canceledCounter.Load() // The server will only count a stream as being reset if learns about the cancelation before it finished writing all data. Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter)) fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams) Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10)) Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10)) }) It("allows concurrent Read and CancelRead calls", func() { // This test is especially valuable when run with race detector, // see https://github.com/quic-go/quic-go/issues/3239. serverCanceledCounterChan := runServer(make([]byte, 100)) // make sure the FIN is sent with the STREAM frame conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var wg sync.WaitGroup wg.Add(numStreams) var counter atomic.Int32 for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) b := make([]byte, 32) if _, err := str.Read(b); err != nil { counter.Add(1) Expect(err).To(Equal(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: 1234, Remote: false, })) return } }() go str.CancelRead(1234) Eventually(done).Should(BeClosed()) }() } wg.Wait() Expect(conn.CloseWithError(0, "")).To(Succeed()) numCanceled := counter.Load() fmt.Fprintf(GinkgoWriter, "canceled %d out of %d streams", numCanceled, numStreams) Expect(numCanceled).ToNot(BeZero()) Eventually(serverCanceledCounterChan).Should(Receive()) }) }) Context("canceling the write side", func() { runClient := func(server *quic.Listener) int32 /* number of canceled streams */ { conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var wg sync.WaitGroup var counter atomic.Int32 wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) if err != nil { counter.Add(1) Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) return } Expect(data).To(Equal(PRData)) }() } wg.Wait() streamCount := counter.Load() fmt.Fprintf(GinkgoWriter, "Canceled writing on %d of %d streams\n", streamCount, numStreams) Expect(streamCount).To(BeNumerically(">", numStreams/10)) Expect(numStreams - streamCount).To(BeNumerically(">", numStreams/10)) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(server.Close()).To(Succeed()) return streamCount } It("downloads when the server cancels some streams immediately", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) var canceledCounter atomic.Int32 go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) // cancel about 2/3 of the streams if rand.Int31()%3 != 0 { str.CancelWrite(quic.StreamErrorCode(str.StreamID())) canceledCounter.Add(1) return } _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } }() clientCanceledStreams := runClient(server) Expect(clientCanceledStreams).To(Equal(canceledCounter.Load())) }) It("downloads when the server cancels some streams after sending some data", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) var canceledCounter atomic.Int32 go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) // only write some data from about 1/3 of the streams, then cancel if rand.Int31()%3 != 0 { length := int(rand.Int31n(int32(len(PRData) - 1))) _, err = str.Write(PRData[:length]) Expect(err).ToNot(HaveOccurred()) str.CancelWrite(quic.StreamErrorCode(str.StreamID())) canceledCounter.Add(1) return } _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } }() clientCanceledStreams := runClient(server) Expect(clientCanceledStreams).To(Equal(canceledCounter.Load())) }) }) Context("canceling both read and write side", func() { It("downloads data when both sides cancel streams immediately", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() var wg sync.WaitGroup wg.Add(numStreams) conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) // cancel about half of the streams if rand.Int31()%2 == 0 { str.CancelWrite(quic.StreamErrorCode(str.StreamID())) return } if _, err = str.Write(PRData); err != nil { Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) return } if err := str.Close(); err != nil { Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) return } }() } wg.Wait() close(done) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var wg sync.WaitGroup var counter atomic.Int32 wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) // cancel around half of the streams if rand.Int31()%2 == 0 { str.CancelRead(quic.StreamErrorCode(str.StreamID())) return } data, err := io.ReadAll(str) if err != nil { Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) return } counter.Add(1) Expect(data).To(Equal(PRData)) }() } wg.Wait() count := counter.Load() Expect(count).To(BeNumerically(">", numStreams/15)) fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams) Expect(conn.CloseWithError(0, "")).To(Succeed()) Eventually(done).Should(BeClosed()) Expect(server.Close()).To(Succeed()) }) It("downloads data when both sides cancel streams after a while", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) // cancel about half of the streams length := len(PRData) if rand.Int31()%2 == 0 { length = int(rand.Int31n(int32(len(PRData) - 1))) } if _, err = str.Write(PRData[:length]); err != nil { Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) return } if length < len(PRData) { str.CancelWrite(quic.StreamErrorCode(str.StreamID())) } else if err := str.Close(); err != nil { Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) return } }() } wg.Wait() }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var wg sync.WaitGroup var counter atomic.Int32 wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) r := io.Reader(str) length := len(PRData) // cancel around half of the streams if rand.Int31()%2 == 0 { length = int(rand.Int31n(int32(len(PRData) - 1))) r = io.LimitReader(str, int64(length)) } data, err := io.ReadAll(r) if err != nil { Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) return } Expect(data).To(Equal(PRData[:length])) if length < len(PRData) { str.CancelRead(quic.StreamErrorCode(str.StreamID())) return } counter.Add(1) Expect(data).To(Equal(PRData)) }() } wg.Wait() Eventually(done).Should(BeClosed()) count := counter.Load() Expect(count).To(BeNumerically(">", numStreams/15)) fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(server.Close()).To(Succeed()) }) }) Context("canceling the context", func() { It("downloads data when the receiving peer cancels the context for accepting streams", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) ticker := time.NewTicker(5 * time.Millisecond) for i := 0; i < numStreams; i++ { <-ticker.C go func() { defer GinkgoRecover() str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 3}), ) Expect(err).ToNot(HaveOccurred()) var numToAccept int var counter atomic.Int32 var wg sync.WaitGroup wg.Add(numStreams) for numToAccept < numStreams { ctx, cancel := context.WithCancel(context.Background()) // cancel accepting half of the streams if rand.Int31()%2 == 0 { cancel() } else { numToAccept++ defer cancel() } go func() { defer GinkgoRecover() str, err := conn.AcceptUniStream(ctx) if err != nil { if err.Error() == "context canceled" { counter.Add(1) } return } data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) wg.Done() }() } wg.Wait() count := counter.Load() fmt.Fprintf(GinkgoWriter, "Canceled AcceptStream %d times\n", count) Expect(count).To(BeNumerically(">", numStreams/2)) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(server.Close()).To(Succeed()) }) It("downloads data when the sending peer cancels the context for opening streams", func() { const ( numStreams = 15 maxIncomingStreams = 5 ) server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) msg := make(chan struct{}, 1) var numCanceled atomic.Int32 go func() { defer GinkgoRecover() defer close(msg) conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) var numOpened int for numOpened < numStreams { ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(20*time.Millisecond)) defer cancel() str, err := conn.OpenUniStreamSync(ctx) if err != nil { Expect(err).To(MatchError(context.DeadlineExceeded)) numCanceled.Add(1) select { case msg <- struct{}{}: default: } continue } numOpened++ go func(str quic.SendStream) { defer GinkgoRecover() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }(str) } }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: maxIncomingStreams}), ) Expect(err).ToNot(HaveOccurred()) var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { <-msg str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) go func(str quic.ReceiveStream) { defer GinkgoRecover() data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) wg.Done() }(str) } wg.Wait() count := numCanceled.Load() fmt.Fprintf(GinkgoWriter, "Canceled OpenStreamSync %d times\n", count) Expect(count).To(BeNumerically(">=", numStreams-maxIncomingStreams)) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(server.Close()).To(Succeed()) }) }) It("doesn't run into any errors when streams are canceled all the time", func() { const maxIncomingStreams = 1000 server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{MaxIncomingStreams: maxIncomingStreams, MaxIdleTimeout: 10 * time.Second}), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() var wg sync.WaitGroup wg.Add(2 * 4 * maxIncomingStreams) handleStream := func(str quic.Stream) { str.SetDeadline(time.Now().Add(time.Second)) go func() { defer wg.Done() if rand.Int31()%2 == 0 { defer GinkgoRecover() io.ReadAll(str) } }() go func() { defer wg.Done() if rand.Int31()%2 == 0 { str.Write([]byte("foobar")) if rand.Int31()%2 == 0 { str.Close() } } }() go func() { defer wg.Done() // Make sure we at least send out *something* for the last stream, // otherwise the peer might never receive this anything for this stream. if rand.Int31()%2 == 0 || str.StreamID() == 4*(maxIncomingStreams-1) { str.CancelWrite(1234) } }() go func() { defer wg.Done() if rand.Int31()%2 == 0 { str.CancelRead(1234) } }() } serverRunning := make(chan struct{}) go func() { defer GinkgoRecover() defer close(serverRunning) conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for { str, err := conn.AcceptStream(context.Background()) if err != nil { // Make sure the connection is closed regularly. Expect(err).To(BeAssignableToTypeOf(&quic.ApplicationError{})) return } handleStream(str) } }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{}), ) Expect(err).ToNot(HaveOccurred()) for i := 0; i < maxIncomingStreams; i++ { str, err := conn.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) handleStream(str) } // We don't expect to accept any stream here. // We're just making sure the connection stays open and there's no error. ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() _, err = conn.AcceptStream(ctx) Expect(err).To(MatchError(context.DeadlineExceeded)) wg.Wait() Expect(conn.CloseWithError(0, "")).To(Succeed()) Eventually(serverRunning).Should(BeClosed()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/close_test.go000066400000000000000000000041141465664453100276410ustar00rootroot00000000000000package self_test import ( "context" "fmt" "net" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID lengths tests", func() { It("retransmits the CONNECTION_CLOSE packet", func() { server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{ DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() var drop atomic.Bool dropped := make(chan []byte, 100) proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), DelayPacket: func(dir quicproxy.Direction, _ []byte) time.Duration { return 5 * time.Millisecond // 10ms RTT }, DropPacket: func(dir quicproxy.Direction, b []byte) bool { if drop := drop.Load(); drop && dir == quicproxy.DirectionOutgoing { dropped <- b return true } return false }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") sconn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) time.Sleep(100 * time.Millisecond) drop.Store(true) sconn.CloseWithError(1337, "closing") // send 100 packets for i := 0; i < 100; i++ { str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) time.Sleep(time.Millisecond) } // Expect retransmissions of the CONNECTION_CLOSE for the // 1st, 2nd, 4th, 8th, 16th, 32th, 64th packet: 7 in total (+1 for the original packet) Eventually(dropped).Should(HaveLen(8)) first := <-dropped for len(dropped) > 0 { Expect(<-dropped).To(Equal(first)) // these packets are all identical } }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/conn_id_test.go000066400000000000000000000075311465664453100301530ustar00rootroot00000000000000package self_test import ( "context" "crypto/rand" "fmt" "io" mrand "math/rand" "net" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type connIDGenerator struct { length int } func (c *connIDGenerator) GenerateConnectionID() (quic.ConnectionID, error) { b := make([]byte, c.length) if _, err := rand.Read(b); err != nil { fmt.Fprintf(GinkgoWriter, "generating conn ID failed: %s", err) } return protocol.ParseConnectionID(b), nil } func (c *connIDGenerator) ConnectionIDLen() int { return c.length } var _ = Describe("Connection ID lengths tests", func() { randomConnIDLen := func() int { return 4 + int(mrand.Int31n(15)) } // connIDLen is ignored when connIDGenerator is set runServer := func(connIDLen int, connIDGenerator quic.ConnectionIDGenerator) (*quic.Listener, func()) { if connIDGenerator != nil { GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID generator for the server\n", connIDGenerator.ConnectionIDLen()))) } else { GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID for the server\n", connIDLen))) } addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) tr := &quic.Transport{ Conn: conn, ConnectionIDLength: connIDLen, ConnectionIDGenerator: connIDGenerator, } addTracer(tr) ln, err := tr.Listen(getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() for { conn, err := ln.Accept(context.Background()) if err != nil { return } go func() { defer GinkgoRecover() str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) }() } }() return ln, func() { ln.Close() tr.Close() } } // connIDLen is ignored when connIDGenerator is set runClient := func(addr net.Addr, connIDLen int, connIDGenerator quic.ConnectionIDGenerator) { if connIDGenerator != nil { GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID generator for the client\n", connIDGenerator.ConnectionIDLen()))) } else { GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID for the client\n", connIDLen))) } laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{ Conn: conn, ConnectionIDLength: connIDLen, ConnectionIDGenerator: connIDGenerator, } addTracer(tr) defer tr.Close() cl, err := tr.Dial( context.Background(), &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: addr.(*net.UDPAddr).Port}, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer cl.CloseWithError(0, "") str, err := cl.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) } It("downloads a file using a 0-byte connection ID for the client", func() { ln, closeFn := runServer(randomConnIDLen(), nil) defer closeFn() runClient(ln.Addr(), 0, nil) }) It("downloads a file when both client and server use a random connection ID length", func() { ln, closeFn := runServer(randomConnIDLen(), nil) defer closeFn() runClient(ln.Addr(), randomConnIDLen(), nil) }) It("downloads a file when both client and server use a custom connection ID generator", func() { ln, closeFn := runServer(0, &connIDGenerator{length: randomConnIDLen()}) defer closeFn() runClient(ln.Addr(), 0, &connIDGenerator{length: randomConnIDLen()}) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/datagram_test.go000066400000000000000000000125041465664453100303160ustar00rootroot00000000000000package self_test import ( "context" "encoding/binary" "fmt" mrand "math/rand" "net" "sync" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Datagram test", func() { const concurrentSends = 100 const maxDatagramSize = 250 var ( serverConn, clientConn *net.UDPConn dropped, total atomic.Int32 ) startServerAndProxy := func(enableDatagram, expectDatagramSupport bool) (port int, closeFn func()) { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) serverConn, err = net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) ln, err := quic.Listen( serverConn, getTLSConfig(), getQuicConfig(&quic.Config{EnableDatagrams: enableDatagram}), ) Expect(err).ToNot(HaveOccurred()) accepted := make(chan struct{}) go func() { defer GinkgoRecover() defer close(accepted) conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) if expectDatagramSupport { Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue()) if enableDatagram { f := &wire.DatagramFrame{DataLenPresent: true} var wg sync.WaitGroup wg.Add(concurrentSends) for i := 0; i < concurrentSends; i++ { go func(i int) { defer GinkgoRecover() defer wg.Done() b := make([]byte, 8) binary.BigEndian.PutUint64(b, uint64(i)) Expect(conn.SendDatagram(b)).To(Succeed()) }(i) } maxDatagramMessageSize := f.MaxDataLen(maxDatagramSize, conn.ConnectionState().Version) b := make([]byte, maxDatagramMessageSize+1) Expect(conn.SendDatagram(b)).To(MatchError(&quic.DatagramTooLargeError{ MaxDatagramPayloadSize: int64(maxDatagramMessageSize), })) wg.Wait() } } else { Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse()) } }() serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), // drop 10% of Short Header packets sent from the server DropPacket: func(dir quicproxy.Direction, packet []byte) bool { if dir == quicproxy.DirectionIncoming { return false } // don't drop Long Header packets if wire.IsLongHeaderPacket(packet[0]) { return false } drop := mrand.Int()%10 == 0 if drop { dropped.Add(1) } total.Add(1) return drop }, }) Expect(err).ToNot(HaveOccurred()) return proxy.LocalPort(), func() { Eventually(accepted).Should(BeClosed()) proxy.Close() ln.Close() } } BeforeEach(func() { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) clientConn, err = net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) }) It("sends datagrams", func() { oldMaxDatagramSize := wire.MaxDatagramSize wire.MaxDatagramSize = maxDatagramSize proxyPort, close := startServerAndProxy(true, true) defer close() raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort)) Expect(err).ToNot(HaveOccurred()) conn, err := quic.Dial( context.Background(), clientConn, raddr, getTLSClientConfig(), getQuicConfig(&quic.Config{EnableDatagrams: true}), ) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue()) var counter int for { // Close the connection if no message is received for 100 ms. timer := time.AfterFunc(scaleDuration(100*time.Millisecond), func() { conn.CloseWithError(0, "") }) if _, err := conn.ReceiveDatagram(context.Background()); err != nil { break } timer.Stop() counter++ } numDropped := int(dropped.Load()) expVal := concurrentSends - numDropped fmt.Fprintf(GinkgoWriter, "Dropped %d out of %d packets.\n", numDropped, total.Load()) fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, concurrentSends) Expect(counter).To(And( BeNumerically(">", expVal*9/10), BeNumerically("<", concurrentSends), )) Eventually(conn.Context().Done).Should(BeClosed()) wire.MaxDatagramSize = oldMaxDatagramSize }) It("server can disable datagram", func() { proxyPort, close := startServerAndProxy(false, true) raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort)) Expect(err).ToNot(HaveOccurred()) conn, err := quic.Dial( context.Background(), clientConn, raddr, getTLSClientConfig(), getQuicConfig(&quic.Config{EnableDatagrams: true}), ) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse()) close() conn.CloseWithError(0, "") }) It("client can disable datagram", func() { proxyPort, close := startServerAndProxy(false, true) raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort)) Expect(err).ToNot(HaveOccurred()) conn, err := quic.Dial( context.Background(), clientConn, raddr, getTLSClientConfig(), getQuicConfig(&quic.Config{EnableDatagrams: true}), ) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse()) Expect(conn.SendDatagram([]byte{0})).To(HaveOccurred()) close() conn.CloseWithError(0, "") }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/deadline_test.go000066400000000000000000000140301465664453100302770ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "time" "github.com/quic-go/quic-go" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Stream deadline tests", func() { setup := func() (serverStr, clientStr quic.Stream, close func()) { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) strChan := make(chan quic.SendStream) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) _, err = str.Read([]byte{0}) Expect(err).ToNot(HaveOccurred()) strChan <- str }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) clientStr, err = conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = clientStr.Write([]byte{0}) // need to write one byte so the server learns about the stream Expect(err).ToNot(HaveOccurred()) Eventually(strChan).Should(Receive(&serverStr)) return serverStr, clientStr, func() { Expect(server.Close()).To(Succeed()) Expect(conn.CloseWithError(0, "")).To(Succeed()) } } Context("read deadlines", func() { It("completes a transfer when the deadline is set", func() { serverStr, clientStr, closeFn := setup() defer closeFn() const timeout = time.Millisecond done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := serverStr.Write(PRDataLong) Expect(err).ToNot(HaveOccurred()) close(done) }() var bytesRead int var timeoutCounter int buf := make([]byte, 1<<10) data := make([]byte, len(PRDataLong)) clientStr.SetReadDeadline(time.Now().Add(timeout)) for bytesRead < len(PRDataLong) { n, err := clientStr.Read(buf) if nerr, ok := err.(net.Error); ok && nerr.Timeout() { timeoutCounter++ clientStr.SetReadDeadline(time.Now().Add(timeout)) } else { Expect(err).ToNot(HaveOccurred()) } copy(data[bytesRead:], buf[:n]) bytesRead += n } Expect(data).To(Equal(PRDataLong)) // make sure the test actually worked and Read actually ran into the deadline a few times Expect(timeoutCounter).To(BeNumerically(">=", 10)) Eventually(done).Should(BeClosed()) }) It("completes a transfer when the deadline is set concurrently", func() { serverStr, clientStr, closeFn := setup() defer closeFn() const timeout = time.Millisecond go func() { defer GinkgoRecover() _, err := serverStr.Write(PRDataLong) Expect(err).ToNot(HaveOccurred()) }() var bytesRead int var timeoutCounter int buf := make([]byte, 1<<10) data := make([]byte, len(PRDataLong)) clientStr.SetReadDeadline(time.Now().Add(timeout)) deadlineDone := make(chan struct{}) received := make(chan struct{}) go func() { defer close(deadlineDone) for { select { case <-received: return default: time.Sleep(timeout) } clientStr.SetReadDeadline(time.Now().Add(timeout)) } }() for bytesRead < len(PRDataLong) { n, err := clientStr.Read(buf) if nerr, ok := err.(net.Error); ok && nerr.Timeout() { timeoutCounter++ } else { Expect(err).ToNot(HaveOccurred()) } copy(data[bytesRead:], buf[:n]) bytesRead += n } close(received) Expect(data).To(Equal(PRDataLong)) // make sure the test actually worked an Read actually ran into the deadline a few times Expect(timeoutCounter).To(BeNumerically(">=", 10)) Eventually(deadlineDone).Should(BeClosed()) }) }) Context("write deadlines", func() { It("completes a transfer when the deadline is set", func() { serverStr, clientStr, closeFn := setup() defer closeFn() const timeout = time.Millisecond done := make(chan struct{}) go func() { defer GinkgoRecover() data, err := io.ReadAll(serverStr) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRDataLong)) close(done) }() var bytesWritten int var timeoutCounter int clientStr.SetWriteDeadline(time.Now().Add(timeout)) for bytesWritten < len(PRDataLong) { n, err := clientStr.Write(PRDataLong[bytesWritten:]) if nerr, ok := err.(net.Error); ok && nerr.Timeout() { timeoutCounter++ clientStr.SetWriteDeadline(time.Now().Add(timeout)) } else { Expect(err).ToNot(HaveOccurred()) } bytesWritten += n } clientStr.Close() // make sure the test actually worked an Read actually ran into the deadline a few times Expect(timeoutCounter).To(BeNumerically(">=", 10)) Eventually(done).Should(BeClosed()) }) It("completes a transfer when the deadline is set concurrently", func() { serverStr, clientStr, closeFn := setup() defer closeFn() const timeout = time.Millisecond readDone := make(chan struct{}) go func() { defer GinkgoRecover() data, err := io.ReadAll(serverStr) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRDataLong)) close(readDone) }() clientStr.SetWriteDeadline(time.Now().Add(timeout)) deadlineDone := make(chan struct{}) go func() { defer close(deadlineDone) for { select { case <-readDone: return default: time.Sleep(timeout) } clientStr.SetWriteDeadline(time.Now().Add(timeout)) } }() var bytesWritten int var timeoutCounter int clientStr.SetWriteDeadline(time.Now().Add(timeout)) for bytesWritten < len(PRDataLong) { n, err := clientStr.Write(PRDataLong[bytesWritten:]) if nerr, ok := err.(net.Error); ok && nerr.Timeout() { timeoutCounter++ } else { Expect(err).ToNot(HaveOccurred()) } bytesWritten += n } clientStr.Close() // make sure the test actually worked an Read actually ran into the deadline a few times Expect(timeoutCounter).To(BeNumerically(">=", 10)) Eventually(readDone).Should(BeClosed()) Eventually(deadlineDone).Should(BeClosed()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/drop_test.go000066400000000000000000000072461465664453100275110ustar00rootroot00000000000000package self_test import ( "context" "fmt" "math/rand" "net" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func randomDuration(min, max time.Duration) time.Duration { return min + time.Duration(rand.Int63n(int64(max-min))) } var _ = Describe("Drop Tests", func() { var ( proxy *quicproxy.QuicProxy ln *quic.Listener ) startListenerAndProxy := func(dropCallback quicproxy.DropCallback) { var err error ln, err = quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err = quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(dir quicproxy.Direction, _ []byte) time.Duration { return 5 * time.Millisecond // 10ms RTT }, DropPacket: dropCallback, }, ) Expect(err).ToNot(HaveOccurred()) } AfterEach(func() { Expect(proxy.Close()).To(Succeed()) Expect(ln.Close()).To(Succeed()) }) for _, d := range directions { direction := d // The purpose of this test is to create a lot of tails, by sending 1 byte messages. // The interval, the length of the drop period, and the time when the drop period starts are randomized. // To cover different scenarios, repeat this test a few times. for rep := 0; rep < 3; rep++ { It(fmt.Sprintf("sends short messages, dropping packets in %s direction", direction), func() { const numMessages = 15 messageInterval := randomDuration(10*time.Millisecond, 100*time.Millisecond) dropDuration := randomDuration(messageInterval*3/2, 2*time.Second) dropDelay := randomDuration(25*time.Millisecond, numMessages*messageInterval/2) // makes sure we don't interfere with the handshake fmt.Fprintf(GinkgoWriter, "Sending a message every %s, %d times.\n", messageInterval, numMessages) fmt.Fprintf(GinkgoWriter, "Dropping packets for %s, after a delay of %s\n", dropDuration, dropDelay) startTime := time.Now() var numDroppedPackets atomic.Int32 startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { if !d.Is(direction) { return false } drop := time.Now().After(startTime.Add(dropDelay)) && time.Now().Before(startTime.Add(dropDelay).Add(dropDuration)) if drop { numDroppedPackets.Add(1) } return drop }) done := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) for i := uint8(1); i <= numMessages; i++ { n, err := str.Write([]byte{i}) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(1)) time.Sleep(messageInterval) } <-done Expect(conn.CloseWithError(0, "")).To(Succeed()) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := uint8(1); i <= numMessages; i++ { b := []byte{0} n, err := str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(1)) Expect(b[0]).To(Equal(i)) } close(done) numDropped := numDroppedPackets.Load() fmt.Fprintf(GinkgoWriter, "Dropped %d packets.\n", numDropped) Expect(numDropped).To(BeNumerically(">", 0)) }) } } }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/early_data_test.go000066400000000000000000000034661465664453100306520ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("early data", func() { const rtt = 80 * time.Millisecond It("sends 0.5-RTT data", func() { ln, err := quic.ListenAddrEarly( "localhost:0", getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("early data")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) // make sure the Write finished before the handshake completed Expect(conn.HandshakeComplete()).ToNot(BeClosed()) Eventually(conn.Context().Done()).Should(BeClosed()) }() serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(quicproxy.Direction, []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("early data"))) conn.CloseWithError(0, "") Eventually(done).Should(BeClosed()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/handshake_drop_test.go000066400000000000000000000215571465664453100315200ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "io" mrand "math/rand" "net" "sync" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) var directions = []quicproxy.Direction{quicproxy.DirectionIncoming, quicproxy.DirectionOutgoing, quicproxy.DirectionBoth} type applicationProtocol struct { name string run func(ln *quic.Listener, port int) } var _ = Describe("Handshake drop tests", func() { data := GeneratePRData(5000) const timeout = 2 * time.Minute startListenerAndProxy := func(dropCallback quicproxy.DropCallback, doRetry bool, longCertChain bool) (ln *quic.Listener, proxyPort int, closeFn func()) { conf := getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, }) var tlsConf *tls.Config if longCertChain { tlsConf = getTLSConfigWithLongCertChain() } else { tlsConf = getTLSConfig() } laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) tr := &quic.Transport{Conn: conn} if doRetry { tr.VerifySourceAddress = func(net.Addr) bool { return true } } ln, err = tr.Listen(tlsConf, conf) Expect(err).ToNot(HaveOccurred()) serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DropPacket: dropCallback, DelayPacket: func(dir quicproxy.Direction, packet []byte) time.Duration { return 10 * time.Millisecond }, }) Expect(err).ToNot(HaveOccurred()) return ln, proxy.LocalPort(), func() { ln.Close() tr.Close() conn.Close() proxy.Close() } } clientSpeaksFirst := &applicationProtocol{ name: "client speaks first", run: func(ln *quic.Listener, port int) { serverConnChan := make(chan quic.Connection) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) b, err := io.ReadAll(gbytes.TimeoutReader(str, timeout)) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal(data)) serverConnChan <- conn }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, }), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) var serverConn quic.Connection Eventually(serverConnChan, timeout).Should(Receive(&serverConn)) conn.CloseWithError(0, "") serverConn.CloseWithError(0, "") }, } serverSpeaksFirst := &applicationProtocol{ name: "server speaks first", run: func(ln *quic.Listener, port int) { serverConnChan := make(chan quic.Connection) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) serverConnChan <- conn }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, }), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) b, err := io.ReadAll(gbytes.TimeoutReader(str, timeout)) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal(data)) var serverConn quic.Connection Eventually(serverConnChan, timeout).Should(Receive(&serverConn)) conn.CloseWithError(0, "") serverConn.CloseWithError(0, "") }, } nobodySpeaks := &applicationProtocol{ name: "nobody speaks", run: func(ln *quic.Listener, port int) { serverConnChan := make(chan quic.Connection) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) serverConnChan <- conn }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, }), ) Expect(err).ToNot(HaveOccurred()) var serverConn quic.Connection Eventually(serverConnChan, timeout).Should(Receive(&serverConn)) // both server and client accepted a connection. Close now. conn.CloseWithError(0, "") serverConn.CloseWithError(0, "") }, } for _, d := range directions { direction := d for _, dr := range []bool{true, false} { doRetry := dr desc := "when using Retry" if !dr { desc = "when not using Retry" } Context(desc, func() { for _, lcc := range []bool{false, true} { longCertChain := lcc Context(fmt.Sprintf("using a long certificate chain: %t", longCertChain), func() { for _, a := range []*applicationProtocol{clientSpeaksFirst, serverSpeaksFirst, nobodySpeaks} { app := a Context(app.name, func() { It(fmt.Sprintf("establishes a connection when the first packet is lost in %s direction", direction), func() { var incoming, outgoing atomic.Int32 ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { var p int32 switch d { case quicproxy.DirectionIncoming: p = incoming.Add(1) case quicproxy.DirectionOutgoing: p = outgoing.Add(1) } return p == 1 && d.Is(direction) }, doRetry, longCertChain) defer closeFn() app.run(ln, proxyPort) }) It(fmt.Sprintf("establishes a connection when the second packet is lost in %s direction", direction), func() { var incoming, outgoing atomic.Int32 ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { var p int32 switch d { case quicproxy.DirectionIncoming: p = incoming.Add(1) case quicproxy.DirectionOutgoing: p = outgoing.Add(1) } return p == 2 && d.Is(direction) }, doRetry, longCertChain) defer closeFn() app.run(ln, proxyPort) }) It(fmt.Sprintf("establishes a connection when 1/3 of the packets are lost in %s direction", direction), func() { const maxSequentiallyDropped = 10 var mx sync.Mutex var incoming, outgoing int ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { drop := mrand.Int63n(int64(3)) == 0 mx.Lock() defer mx.Unlock() // never drop more than 10 consecutive packets if d.Is(quicproxy.DirectionIncoming) { if drop { incoming++ if incoming > maxSequentiallyDropped { drop = false } } if !drop { incoming = 0 } } if d.Is(quicproxy.DirectionOutgoing) { if drop { outgoing++ if outgoing > maxSequentiallyDropped { drop = false } } if !drop { outgoing = 0 } } return drop }, doRetry, longCertChain) defer closeFn() app.run(ln, proxyPort) }) }) } }) } }) } It("establishes a connection when the ClientHello is larger than 1 MTU (e.g. post-quantum)", func() { origAdditionalTransportParametersClient := wire.AdditionalTransportParametersClient defer func() { wire.AdditionalTransportParametersClient = origAdditionalTransportParametersClient }() b := make([]byte, 2500) // the ClientHello will now span across 3 packets mrand.New(mrand.NewSource(GinkgoRandomSeed())).Read(b) wire.AdditionalTransportParametersClient = map[uint64][]byte{ // Avoid random collisions with the greased transport parameters. uint64(27+31*(1000+mrand.Int63()/31)) % quicvarint.Max: b, } ln, proxyPort, closeFn := startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { if d == quicproxy.DirectionOutgoing { return false } return mrand.Intn(3) == 0 }, false, false) defer closeFn() clientSpeaksFirst.run(ln, proxyPort) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/handshake_rtt_test.go000066400000000000000000000136211465664453100313560ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "io" "net" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Handshake RTT tests", func() { var ( proxy *quicproxy.QuicProxy serverConfig *quic.Config serverTLSConfig *tls.Config ) const rtt = 400 * time.Millisecond BeforeEach(func() { serverConfig = getQuicConfig(nil) serverTLSConfig = getTLSConfig() }) AfterEach(func() { Expect(proxy.Close()).To(Succeed()) }) runProxy := func(serverAddr net.Addr) { var err error // start the proxy proxy, err = quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: serverAddr.String(), DelayPacket: func(_ quicproxy.Direction, _ []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) } expectDurationInRTTs := func(startTime time.Time, num int) { testDuration := time.Since(startTime) rtts := float32(testDuration) / float32(rtt) Expect(rtts).To(SatisfyAll( BeNumerically(">=", num), BeNumerically("<", num+1), )) } // 1 RTT for verifying the source address // 1 RTT for the TLS handshake It("is forward-secure after 2 RTTs with Retry", func() { laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) defer udpConn.Close() tr := &quic.Transport{ Conn: udpConn, VerifySourceAddress: func(net.Addr) bool { return true }, } addTracer(tr) defer tr.Close() ln, err := tr.Listen(serverTLSConfig, serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() runProxy(ln.Addr()) startTime := time.Now() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{GetConfigForClient: func(info *quic.ClientHelloInfo) (*quic.Config, error) { Expect(info.AddrVerified).To(BeTrue()) return nil, nil }}), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") expectDurationInRTTs(startTime, 2) }) It("establishes a connection in 1 RTT when the server doesn't require a token", func() { ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() runProxy(ln.Addr()) startTime := time.Now() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{GetConfigForClient: func(info *quic.ClientHelloInfo) (*quic.Config, error) { Expect(info.AddrVerified).To(BeFalse()) return nil, nil }}), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") expectDurationInRTTs(startTime, 1) }) It("establishes a connection in 2 RTTs if a HelloRetryRequest is performed", func() { serverTLSConfig.CurvePreferences = []tls.CurveID{tls.CurveP384} ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() runProxy(ln.Addr()) startTime := time.Now() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") expectDurationInRTTs(startTime, 2) }) It("receives the first message from the server after 2 RTTs, when the server uses ListenAddr", func() { ln, err := quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() defer ln.Close() runProxy(ln.Addr()) startTime := time.Now() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) expectDurationInRTTs(startTime, 2) }) It("receives the first message from the server after 1 RTT, when the server uses ListenAddrEarly", func() { ln, err := quic.ListenAddrEarly("localhost:0", serverTLSConfig, serverConfig) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) // Check the ALPN now. This is probably what an application would do. // It makes sure that ConnectionState does not block until the handshake completes. Expect(conn.ConnectionState().TLS.NegotiatedProtocol).To(Equal(alpn)) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() defer ln.Close() runProxy(ln.Addr()) startTime := time.Now() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) expectDurationInRTTs(startTime, 1) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/handshake_test.go000066400000000000000000000714521465664453100304730ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type tokenStore struct { store quic.TokenStore gets chan<- string puts chan<- string } var _ quic.TokenStore = &tokenStore{} func newTokenStore(gets, puts chan<- string) quic.TokenStore { return &tokenStore{ store: quic.NewLRUTokenStore(10, 4), gets: gets, puts: puts, } } func (c *tokenStore) Put(key string, token *quic.ClientToken) { c.puts <- key c.store.Put(key, token) } func (c *tokenStore) Pop(key string) *quic.ClientToken { c.gets <- key return c.store.Pop(key) } var _ = Describe("Handshake tests", func() { var ( server *quic.Listener serverConfig *quic.Config acceptStopped chan struct{} ) BeforeEach(func() { server = nil acceptStopped = make(chan struct{}) serverConfig = getQuicConfig(nil) }) AfterEach(func() { if server != nil { server.Close() <-acceptStopped } }) runServer := func(tlsConf *tls.Config) { var err error // start the server server, err = quic.ListenAddr("localhost:0", tlsConf, serverConfig) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() defer close(acceptStopped) for { if _, err := server.Accept(context.Background()); err != nil { return } } }() } It("returns the context cancellation error on timeouts", func() { ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(20*time.Millisecond)) defer cancel() errChan := make(chan error, 1) go func() { _, err := quic.DialAddr( ctx, "localhost:1234", // nobody is listening on this port, but we're going to cancel this dial anyway getTLSClientConfig(), getQuicConfig(nil), ) errChan <- err }() var err error Eventually(errChan).Should(Receive(&err)) Expect(err).To(HaveOccurred()) Expect(err).To(MatchError(context.DeadlineExceeded)) }) It("returns the cancellation reason when a dial is canceled", func() { ctx, cancel := context.WithCancelCause(context.Background()) errChan := make(chan error, 1) go func() { _, err := quic.DialAddr( ctx, "localhost:1234", // nobody is listening on this port, but we're going to cancel this dial anyway getTLSClientConfig(), getQuicConfig(nil), ) errChan <- err }() cancel(errors.New("application cancelled")) var err error Eventually(errChan).Should(Receive(&err)) Expect(err).To(HaveOccurred()) Expect(err).To(MatchError("application cancelled")) }) It("uses the context everywhere, on the server side", func() { tlsGetConfigForClientContextChan := make(chan context.Context, 1) tlsGetCertificateContextChan := make(chan context.Context, 1) tracerContextChan := make(chan context.Context, 1) connContextChan := make(chan context.Context, 1) streamContextChan := make(chan context.Context, 1) conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{ Conn: conn, ConnContext: func(ctx context.Context) context.Context { //nolint:staticcheck return context.WithValue(ctx, "foo", "bar") }, } defer tr.Close() server, err := tr.Listen( &tls.Config{ GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { tlsGetConfigForClientContextChan <- info.Context() tlsConf := getTLSConfig() tlsConf.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { tlsGetCertificateContextChan <- info.Context() return &tlsConf.Certificates[0], nil } return tlsConf, nil }, }, getQuicConfig(&quic.Config{ Tracer: func(ctx context.Context, _ logging.Perspective, _ quic.ConnectionID) *logging.ConnectionTracer { tracerContextChan <- ctx return nil }, }), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() go func() { defer GinkgoRecover() defer close(acceptStopped) for { conn, err := server.Accept(context.Background()) if err != nil { return } connContextChan <- conn.Context() str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) streamContextChan <- str.Context() str.Write([]byte{1, 2, 3}) } }() c, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) _, err = c.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) c.CloseWithError(1337, "bye") checkContext := func(c <-chan context.Context, checkCancellationCause bool) { var ctx context.Context EventuallyWithOffset(1, c).Should(Receive(&ctx)) val := ctx.Value("foo") ExpectWithOffset(1, val).ToNot(BeNil()) v := val.(string) ExpectWithOffset(1, v).To(Equal("bar")) EventuallyWithOffset(1, ctx.Done).Should(BeClosed()) if !checkCancellationCause { return } ctxErr := context.Cause(ctx) var appErr *quic.ApplicationError ExpectWithOffset(1, errors.As(ctxErr, &appErr)).To(BeTrue()) ExpectWithOffset(1, appErr.ErrorCode).To(BeEquivalentTo(1337)) } checkContext(connContextChan, true) checkContext(tracerContextChan, true) checkContext(streamContextChan, true) // crypto/tls cancels the context when the TLS handshake completes. checkContext(tlsGetConfigForClientContextChan, false) checkContext(tlsGetCertificateContextChan, false) }) It("correctly handles a fresh context returned from ConnContext", func() { conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{ Conn: conn, ConnContext: func(ctx context.Context) context.Context { return context.Background() }, } server, err := tr.Listen(getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := server.Accept(context.Background()) if err != nil { return } Eventually(conn.Context().Done).Should(BeClosed()) }() c, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) c.CloseWithError(1337, "bye") }) It("uses the context everywhere, on the client side", func() { tlsServerConf := getTLSConfig() tlsServerConf.ClientAuth = tls.RequestClientCert runServer(tlsServerConf) tlsContextChan := make(chan context.Context, 1) tracerContextChan := make(chan context.Context, 1) tlsConf := getTLSClientConfig() tlsConf.GetClientCertificate = func(info *tls.CertificateRequestInfo) (*tls.Certificate, error) { tlsContextChan <- info.Context() return &tlsServerConf.Certificates[0], nil } //nolint:staticcheck ctx, cancel := context.WithCancel(context.WithValue(context.Background(), "foo", "bar")) conn, err := quic.DialAddr( ctx, fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(&quic.Config{ Tracer: func(ctx context.Context, _ logging.Perspective, _ quic.ConnectionID) *logging.ConnectionTracer { tracerContextChan <- ctx return nil }, }), ) Expect(err).ToNot(HaveOccurred()) cancel() // make sure the connection context is not cancelled (even though derived from the ctx passed to Dial) Expect(ctx.Done()).ToNot(Receive()) checkContext := func(ctx context.Context, checkCancellationCause bool) { val := ctx.Value("foo") ExpectWithOffset(2, val).ToNot(BeNil()) v := val.(string) ExpectWithOffset(2, v).To(Equal("bar")) if !checkCancellationCause { return } ctxErr := context.Cause(ctx) var appErr *quic.ApplicationError ExpectWithOffset(1, errors.As(ctxErr, &appErr)).To(BeTrue()) ExpectWithOffset(1, appErr.ErrorCode).To(BeEquivalentTo(1337)) } checkContextFromChan := func(c <-chan context.Context, checkCancellationCause bool) { var ctx context.Context EventuallyWithOffset(1, c).Should(Receive(&ctx)) checkContext(ctx, checkCancellationCause) } str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) conn.CloseWithError(1337, "bye") checkContext(conn.Context(), true) checkContext(str.Context(), true) // crypto/tls cancels the context when the TLS handshake completes. checkContextFromChan(tlsContextChan, false) checkContextFromChan(tracerContextChan, false) }) It("fails the handshake when tls.Config.GetConfigForClient errors", func() { laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) tr := &quic.Transport{Conn: udpConn} addTracer(tr) defer tr.Close() tlsConf := &tls.Config{} tlsConf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { return nil, errors.New("nope") } ln, err := tr.Listen(tlsConf, getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer ln.Close() _, err = quic.DialAddr(context.Background(), ln.Addr().String(), getTLSClientConfig(), getQuicConfig(nil)) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode.IsCryptoError()).To(BeTrue()) }) Context("using different cipher suites", func() { for n, id := range map[string]uint16{ "TLS_AES_128_GCM_SHA256": tls.TLS_AES_128_GCM_SHA256, "TLS_AES_256_GCM_SHA384": tls.TLS_AES_256_GCM_SHA384, "TLS_CHACHA20_POLY1305_SHA256": tls.TLS_CHACHA20_POLY1305_SHA256, } { name := n suiteID := id It(fmt.Sprintf("using %s", name), func() { reset := qtls.SetCipherSuite(suiteID) defer reset() tlsConf := getTLSConfig() ln, err := quic.ListenAddr("localhost:0", tlsConf, serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) Expect(conn.ConnectionState().TLS.CipherSuite).To(Equal(suiteID)) Expect(conn.CloseWithError(0, "")).To(Succeed()) }) } }) Context("Certificate validation", func() { It("accepts the certificate", func() { runServer(getTLSConfig()) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) conn.CloseWithError(0, "") }) It("has the right local and remote address on the tls.Config.GetConfigForClient ClientHelloInfo.Conn", func() { var local, remote net.Addr var local2, remote2 net.Addr done := make(chan struct{}) tlsConf := &tls.Config{ GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { local = info.Conn.LocalAddr() remote = info.Conn.RemoteAddr() conf := getTLSConfig() conf.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { defer close(done) local2 = info.Conn.LocalAddr() remote2 = info.Conn.RemoteAddr() return &(conf.Certificates[0]), nil } return conf, nil }, } runServer(tlsConf) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") Eventually(done).Should(BeClosed()) Expect(server.Addr()).To(Equal(local)) Expect(conn.LocalAddr().(*net.UDPAddr).Port).To(Equal(remote.(*net.UDPAddr).Port)) Expect(local).To(Equal(local2)) Expect(remote).To(Equal(remote2)) }) It("works with a long certificate chain", func() { runServer(getTLSConfigWithLongCertChain()) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) conn.CloseWithError(0, "") }) It("errors if the server name doesn't match", func() { runServer(getTLSConfig()) conn, err := net.ListenUDP("udp", nil) Expect(err).ToNot(HaveOccurred()) conf := getTLSClientConfig() conf.ServerName = "foo.bar" _, err = quic.Dial( context.Background(), conn, server.Addr(), conf, getQuicConfig(nil), ) Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode.IsCryptoError()).To(BeTrue()) Expect(transportErr.Error()).To(ContainSubstring("x509: certificate is valid for localhost, not foo.bar")) var certErr *tls.CertificateVerificationError Expect(errors.As(transportErr, &certErr)).To(BeTrue()) }) It("fails the handshake if the client fails to provide the requested client cert", func() { tlsConf := getTLSConfig() tlsConf.ClientAuth = tls.RequireAndVerifyClientCert runServer(tlsConf) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) // Usually, the error will occur after the client already finished the handshake. // However, there's a race condition here. The server's CONNECTION_CLOSE might be // received before the connection is returned, so we might already get the error while dialing. if err == nil { errChan := make(chan error) go func() { defer GinkgoRecover() _, err := conn.AcceptStream(context.Background()) errChan <- err }() Eventually(errChan).Should(Receive(&err)) } Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode.IsCryptoError()).To(BeTrue()) Expect(transportErr.Error()).To(Or( ContainSubstring("tls: certificate required"), ContainSubstring("tls: bad certificate"), )) }) It("uses the ServerName in the tls.Config", func() { runServer(getTLSConfig()) tlsConf := getTLSClientConfig() tlsConf.ServerName = "foo.bar" _, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode.IsCryptoError()).To(BeTrue()) Expect(transportErr.Error()).To(ContainSubstring("x509: certificate is valid for localhost, not foo.bar")) }) }) Context("queuening and accepting connections", func() { var ( server *quic.Listener pconn net.PacketConn dialer *quic.Transport ) dial := func() (quic.Connection, error) { remoteAddr := fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) raddr, err := net.ResolveUDPAddr("udp", remoteAddr) Expect(err).ToNot(HaveOccurred()) return dialer.Dial(context.Background(), raddr, getTLSClientConfig(), getQuicConfig(nil)) } BeforeEach(func() { var err error // start the server, but don't call Accept server, err = quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) // prepare a (single) packet conn for dialing to the server laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) pconn, err = net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) dialer = &quic.Transport{ Conn: pconn, ConnectionIDLength: 4, } }) AfterEach(func() { Expect(server.Close()).To(Succeed()) Expect(pconn.Close()).To(Succeed()) Expect(dialer.Close()).To(Succeed()) }) It("rejects new connection attempts if connections don't get accepted", func() { for i := 0; i < protocol.MaxAcceptQueueSize; i++ { conn, err := dial() Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") } time.Sleep(25 * time.Millisecond) // wait a bit for the connection to be queued conn, err := dial() Expect(err).ToNot(HaveOccurred()) ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() _, err = conn.AcceptStream(ctx) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) // now accept one connection, freeing one spot in the queue _, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) // dial again, and expect that this dial succeeds conn2, err := dial() Expect(err).ToNot(HaveOccurred()) defer conn2.CloseWithError(0, "") time.Sleep(25 * time.Millisecond) // wait a bit for the connection to be queued conn3, err := dial() Expect(err).ToNot(HaveOccurred()) ctx, cancel = context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() _, err = conn3.AcceptStream(ctx) Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) }) It("also returns closed connections from the accept queue", func() { firstConn, err := dial() Expect(err).ToNot(HaveOccurred()) for i := 1; i < protocol.MaxAcceptQueueSize; i++ { conn, err := dial() Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") } time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the connection to be queued conn, err := dial() Expect(err).ToNot(HaveOccurred()) ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() _, err = conn.AcceptStream(ctx) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) // Now close the one of the connection that are waiting to be accepted. const appErrCode quic.ApplicationErrorCode = 12345 Expect(firstConn.CloseWithError(appErrCode, "")) Eventually(firstConn.Context().Done()).Should(BeClosed()) time.Sleep(scaleDuration(200 * time.Millisecond)) // dial again, and expect that this fails again conn2, err := dial() Expect(err).ToNot(HaveOccurred()) ctx, cancel = context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() _, err = conn2.AcceptStream(ctx) Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) // now accept all connections var closedConn quic.Connection for i := 0; i < protocol.MaxAcceptQueueSize; i++ { conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) if conn.Context().Err() != nil { if closedConn != nil { Fail("only expected a single closed connection") } closedConn = conn } } Expect(closedConn).ToNot(BeNil()) // there should be exactly one closed connection _, err = closedConn.AcceptStream(context.Background()) var appErr *quic.ApplicationError Expect(errors.As(err, &appErr)).To(BeTrue()) Expect(appErr.ErrorCode).To(Equal(appErrCode)) }) It("closes handshaking connections when the server is closed", func() { laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) tr := &quic.Transport{Conn: udpConn} addTracer(tr) defer tr.Close() tlsConf := &tls.Config{} done := make(chan struct{}) tlsConf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { <-done return nil, errors.New("closed") } ln, err := tr.Listen(tlsConf, getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) errChan := make(chan error, 1) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() go func() { defer GinkgoRecover() _, err := quic.DialAddr(ctx, ln.Addr().String(), getTLSClientConfig(), getQuicConfig(nil)) errChan <- err }() time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the connection to be queued Expect(ln.Close()).To(Succeed()) close(done) err = <-errChan var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) }) }) Context("ALPN", func() { It("negotiates an application protocol", func() { ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) cs := conn.ConnectionState() Expect(cs.TLS.NegotiatedProtocol).To(Equal(alpn)) close(done) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), nil, ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") cs := conn.ConnectionState() Expect(cs.TLS.NegotiatedProtocol).To(Equal(alpn)) Eventually(done).Should(BeClosed()) Expect(ln.Close()).To(Succeed()) }) It("errors if application protocol negotiation fails", func() { runServer(getTLSConfig()) tlsConf := getTLSClientConfig() tlsConf.NextProtos = []string{"foobar"} _, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, nil, ) Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode.IsCryptoError()).To(BeTrue()) Expect(transportErr.Error()).To(ContainSubstring("no application protocol")) }) }) Context("using tokens", func() { It("uses tokens provided in NEW_TOKEN frames", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) defer server.Close() // dial the first connection and receive the token go func() { defer GinkgoRecover() _, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) }() gets := make(chan string, 100) puts := make(chan string, 100) tokenStore := newTokenStore(gets, puts) quicConf := getQuicConfig(&quic.Config{TokenStore: tokenStore}) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), quicConf, ) Expect(err).ToNot(HaveOccurred()) Expect(gets).To(Receive()) Eventually(puts).Should(Receive()) // received a token. Close this connection. Expect(conn.CloseWithError(0, "")).To(Succeed()) // dial the second connection and verify that the token was used done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) _, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) }() conn, err = quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), quicConf, ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") Expect(gets).To(Receive()) Eventually(done).Should(BeClosed()) }) It("rejects invalid Retry token with the INVALID_TOKEN error", func() { const rtt = 10 * time.Millisecond // The validity period of the retry token is the handshake timeout, // which is twice the handshake idle timeout. // By setting the handshake timeout shorter than the RTT, the token will have expired by the time // it reaches the server. serverConfig.HandshakeIdleTimeout = rtt / 5 laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) defer udpConn.Close() tr := &quic.Transport{ Conn: udpConn, VerifySourceAddress: func(net.Addr) bool { return true }, } addTracer(tr) defer tr.Close() server, err := tr.Listen(getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) defer server.Close() serverPort := server.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(quicproxy.Direction, []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() _, err = quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), nil, ) Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.InvalidToken)) }) }) Context("GetConfigForClient", func() { It("uses the quic.Config returned by GetConfigForClient", func() { serverConfig.EnableDatagrams = false var calledFrom net.Addr serverConfig.GetConfigForClient = func(info *quic.ClientHelloInfo) (*quic.Config, error) { conf := serverConfig.Clone() conf.EnableDatagrams = true calledFrom = info.RemoteAddr return getQuicConfig(conf), nil } ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) close(done) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{EnableDatagrams: true}), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") cs := conn.ConnectionState() Expect(cs.SupportsDatagrams).To(BeTrue()) Eventually(done).Should(BeClosed()) Expect(ln.Close()).To(Succeed()) Expect(calledFrom.(*net.UDPAddr).Port).To(Equal(conn.LocalAddr().(*net.UDPAddr).Port)) }) It("rejects the connection attempt if GetConfigForClient errors", func() { serverConfig.EnableDatagrams = false serverConfig.GetConfigForClient = func(info *quic.ClientHelloInfo) (*quic.Config, error) { return nil, errors.New("rejected") } ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := ln.Accept(context.Background()) Expect(err).To(HaveOccurred()) // we don't expect to accept any connection close(done) }() _, err = quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{EnableDatagrams: true}), ) Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(qerr.ConnectionRefused)) }) }) It("doesn't send any packets when generating the ClientHello fails", func() { ln, err := net.ListenUDP("udp", nil) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) packetChan := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) for { _, _, err := ln.ReadFromUDP(make([]byte, protocol.MaxPacketBufferSize)) if err != nil { return } packetChan <- struct{}{} } }() tlsConf := getTLSClientConfig() tlsConf.NextProtos = []string{""} _, err = quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.LocalAddr().(*net.UDPAddr).Port), tlsConf, nil, ) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode.IsCryptoError()).To(BeTrue()) Expect(err.Error()).To(ContainSubstring("tls: invalid NextProtos value")) Consistently(packetChan).ShouldNot(Receive()) ln.Close() Eventually(done).Should(BeClosed()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/hotswap_test.go000066400000000000000000000113221465664453100302200ustar00rootroot00000000000000package self_test import ( "context" "io" "net" "net/http" "strconv" "sync/atomic" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) type listenerWrapper struct { http3.QUICEarlyListener listenerClosed bool count atomic.Int32 } func (ln *listenerWrapper) Close() error { ln.listenerClosed = true return ln.QUICEarlyListener.Close() } func (ln *listenerWrapper) Faker() *fakeClosingListener { ln.count.Add(1) ctx, cancel := context.WithCancel(context.Background()) return &fakeClosingListener{ listenerWrapper: ln, ctx: ctx, cancel: cancel, } } type fakeClosingListener struct { *listenerWrapper closed atomic.Bool ctx context.Context cancel context.CancelFunc } func (ln *fakeClosingListener) Accept(ctx context.Context) (quic.EarlyConnection, error) { Expect(ctx).To(Equal(context.Background())) return ln.listenerWrapper.Accept(ln.ctx) } func (ln *fakeClosingListener) Close() error { if ln.closed.CompareAndSwap(false, true) { ln.cancel() if ln.listenerWrapper.count.Add(-1) == 0 { ln.listenerWrapper.Close() } } return nil } var _ = Describe("HTTP3 Server hotswap test", func() { var ( mux1 *http.ServeMux mux2 *http.ServeMux client *http.Client rt *http3.RoundTripper server1 *http3.Server server2 *http3.Server ln *listenerWrapper port string ) BeforeEach(func() { mux1 = http.NewServeMux() mux1.HandleFunc("/hello1", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() io.WriteString(w, "Hello, World 1!\n") // don't check the error here. Stream may be reset. }) mux2 = http.NewServeMux() mux2.HandleFunc("/hello2", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() io.WriteString(w, "Hello, World 2!\n") // don't check the error here. Stream may be reset. }) server1 = &http3.Server{ Handler: mux1, QUICConfig: getQuicConfig(nil), } server2 = &http3.Server{ Handler: mux2, QUICConfig: getQuicConfig(nil), } tlsConf := http3.ConfigureTLSConfig(getTLSConfig()) quicln, err := quic.ListenAddrEarly("0.0.0.0:0", tlsConf, getQuicConfig(nil)) ln = &listenerWrapper{QUICEarlyListener: quicln} Expect(err).NotTo(HaveOccurred()) port = strconv.Itoa(ln.Addr().(*net.UDPAddr).Port) }) AfterEach(func() { Expect(rt.Close()).NotTo(HaveOccurred()) Expect(ln.Close()).NotTo(HaveOccurred()) }) BeforeEach(func() { rt = &http3.RoundTripper{ TLSClientConfig: getTLSClientConfig(), DisableCompression: true, QUICConfig: getQuicConfig(&quic.Config{MaxIdleTimeout: 10 * time.Second}), } client = &http.Client{Transport: rt} }) It("hotswap works", func() { // open first server and make single request to it fake1 := ln.Faker() stoppedServing1 := make(chan struct{}) go func() { defer GinkgoRecover() server1.ServeListener(fake1) close(stoppedServing1) }() resp, err := client.Get("https://localhost:" + port + "/hello1") Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal("Hello, World 1!\n")) // open second server with same underlying listener, // make sure it opened and both servers are currently running fake2 := ln.Faker() stoppedServing2 := make(chan struct{}) go func() { defer GinkgoRecover() server2.ServeListener(fake2) close(stoppedServing2) }() Consistently(stoppedServing1).ShouldNot(BeClosed()) Consistently(stoppedServing2).ShouldNot(BeClosed()) // now close first server, no errors should occur here // and only the fake listener should be closed Expect(server1.Close()).NotTo(HaveOccurred()) Eventually(stoppedServing1).Should(BeClosed()) Expect(fake1.closed.Load()).To(BeTrue()) Expect(fake2.closed.Load()).To(BeFalse()) Expect(ln.listenerClosed).ToNot(BeTrue()) Expect(client.Transport.(*http3.RoundTripper).Close()).NotTo(HaveOccurred()) // verify that new connections are being initiated from the second server now resp, err = client.Get("https://localhost:" + port + "/hello2") Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err = io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal("Hello, World 2!\n")) // close the other server - both the fake and the actual listeners must close now Expect(server2.Close()).NotTo(HaveOccurred()) Eventually(stoppedServing2).Should(BeClosed()) Expect(fake2.closed.Load()).To(BeTrue()) Expect(ln.listenerClosed).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/http_test.go000066400000000000000000001010301465664453100275060ustar00rootroot00000000000000package self_test import ( "bufio" "bytes" "compress/gzip" "context" "crypto/tls" "encoding/binary" "errors" "fmt" "io" "net" "net/http" "net/http/httptrace" "net/textproto" "net/url" "os" "strconv" "sync/atomic" "time" "golang.org/x/sync/errgroup" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) type neverEnding byte func (b neverEnding) Read(p []byte) (n int, err error) { for i := range p { p[i] = byte(b) } return len(p), nil } const deadlineDelay = 250 * time.Millisecond var _ = Describe("HTTP tests", func() { var ( mux *http.ServeMux client *http.Client rt *http3.RoundTripper server *http3.Server stoppedServing chan struct{} port int ) BeforeEach(func() { mux = http.NewServeMux() mux.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() io.WriteString(w, "Hello, World!\n") // don't check the error here. Stream may be reset. }) mux.HandleFunc("/prdata", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() sl := r.URL.Query().Get("len") if sl != "" { var err error l, err := strconv.Atoi(sl) Expect(err).NotTo(HaveOccurred()) w.Write(GeneratePRData(l)) // don't check the error here. Stream may be reset. } else { w.Write(PRData) // don't check the error here. Stream may be reset. } }) mux.HandleFunc("/prdatalong", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() w.Write(PRDataLong) // don't check the error here. Stream may be reset. }) mux.HandleFunc("/echo", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() body, err := io.ReadAll(r.Body) Expect(err).NotTo(HaveOccurred()) w.Write(body) // don't check the error here. Stream may be reset. }) mux.HandleFunc("/remoteAddr", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() w.Header().Set("X-RemoteAddr", r.RemoteAddr) w.WriteHeader(http.StatusOK) }) server = &http3.Server{ Handler: mux, TLSConfig: getTLSConfig(), QUICConfig: getQuicConfig(&quic.Config{Allow0RTT: true, EnableDatagrams: true}), } addr, err := net.ResolveUDPAddr("udp", "0.0.0.0:0") Expect(err).NotTo(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).NotTo(HaveOccurred()) port = conn.LocalAddr().(*net.UDPAddr).Port stoppedServing = make(chan struct{}) go func() { defer GinkgoRecover() server.Serve(conn) close(stoppedServing) }() }) AfterEach(func() { Expect(rt.Close()).NotTo(HaveOccurred()) Expect(server.Close()).NotTo(HaveOccurred()) Eventually(stoppedServing).Should(BeClosed()) }) BeforeEach(func() { rt = &http3.RoundTripper{ TLSClientConfig: getTLSClientConfigWithoutServerName(), QUICConfig: getQuicConfig(&quic.Config{ MaxIdleTimeout: 10 * time.Second, }), DisableCompression: true, } client = &http.Client{Transport: rt} }) It("closes the connection after idle timeout", func() { server.IdleTimeout = 100 * time.Millisecond _, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", port)) Expect(err).ToNot(HaveOccurred()) time.Sleep(150 * time.Millisecond) _, err = client.Get(fmt.Sprintf("https://localhost:%d/hello", port)) Expect(err).ToNot(MatchError("idle timeout")) server.IdleTimeout = 0 }) It("downloads a hello", func() { resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal("Hello, World!\n")) }) It("sets content-length for small response", func() { mux.HandleFunc("/small", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() w.Write([]byte("foo")) w.Write([]byte("bar")) }) resp, err := client.Get(fmt.Sprintf("https://localhost:%d/small", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) Expect(resp.Header.Get("Content-Length")).To(Equal("6")) }) It("re-establishes a QUIC connection after a dial error", func() { var dialCounter int testErr := errors.New("test error") cl := http.Client{ Transport: &http3.RoundTripper{ TLSClientConfig: getTLSClientConfig(), Dial: func(ctx context.Context, addr string, tlsConf *tls.Config, conf *quic.Config) (quic.EarlyConnection, error) { dialCounter++ if dialCounter == 1 { // make the first dial fail return nil, testErr } return quic.DialAddrEarly(ctx, addr, tlsConf, conf) }, }, } defer cl.Transport.(io.Closer).Close() _, err := cl.Get(fmt.Sprintf("https://localhost:%d/hello", port)) Expect(err).To(MatchError(testErr)) resp, err := cl.Get(fmt.Sprintf("https://localhost:%d/hello", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) }) It("detects stream errors when server panics when writing response", func() { respChan := make(chan struct{}) mux.HandleFunc("/writing_and_panicking", func(w http.ResponseWriter, r *http.Request) { // no recover here as it will interfere with the handler w.Write([]byte("foobar")) w.(http.Flusher).Flush() // wait for the client to receive the response <-respChan panic(http.ErrAbortHandler) }) resp, err := client.Get(fmt.Sprintf("https://localhost:%d/writing_and_panicking", port)) close(respChan) Expect(err).ToNot(HaveOccurred()) body, err := io.ReadAll(resp.Body) Expect(err).To(HaveOccurred()) // the body will be a prefix of what's written Expect(bytes.HasPrefix([]byte("foobar"), body)).To(BeTrue()) }) It("requests to different servers with the same udpconn", func() { resp, err := client.Get(fmt.Sprintf("https://localhost:%d/remoteAddr", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) addr1 := resp.Header.Get("X-RemoteAddr") Expect(addr1).ToNot(Equal("")) resp, err = client.Get(fmt.Sprintf("https://127.0.0.1:%d/remoteAddr", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) addr2 := resp.Header.Get("X-RemoteAddr") Expect(addr2).ToNot(Equal("")) Expect(addr1).To(Equal(addr2)) }) It("downloads concurrently", func() { group, ctx := errgroup.WithContext(context.Background()) for i := 0; i < 2; i++ { group.Go(func() error { defer GinkgoRecover() req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/hello", port), nil) Expect(err).ToNot(HaveOccurred()) resp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal("Hello, World!\n")) return nil }) } err := group.Wait() Expect(err).ToNot(HaveOccurred()) }) It("sets and gets request headers", func() { handlerCalled := make(chan struct{}) mux.HandleFunc("/headers/request", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() Expect(r.Header.Get("foo")).To(Equal("bar")) Expect(r.Header.Get("lorem")).To(Equal("ipsum")) close(handlerCalled) }) req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/headers/request", port), nil) Expect(err).ToNot(HaveOccurred()) req.Header.Set("foo", "bar") req.Header.Set("lorem", "ipsum") resp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) Eventually(handlerCalled).Should(BeClosed()) }) It("sets and gets response headers", func() { mux.HandleFunc("/headers/response", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() w.Header().Set("foo", "bar") w.Header().Set("lorem", "ipsum") }) resp, err := client.Get(fmt.Sprintf("https://localhost:%d/headers/response", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) Expect(resp.Header.Get("foo")).To(Equal("bar")) Expect(resp.Header.Get("lorem")).To(Equal("ipsum")) }) It("downloads a small file", func() { resp, err := client.Get(fmt.Sprintf("https://localhost:%d/prdata", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 5*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(body).To(Equal(PRData)) }) It("downloads a large file", func() { resp, err := client.Get(fmt.Sprintf("https://localhost:%d/prdatalong", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 20*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(body).To(Equal(PRDataLong)) }) It("downloads many hellos", func() { const num = 150 for i := 0; i < num; i++ { resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal("Hello, World!\n")) } }) It("downloads many files, if the response is not read", func() { const num = 150 for i := 0; i < num; i++ { resp, err := client.Get(fmt.Sprintf("https://localhost:%d/prdata", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) Expect(resp.Body.Close()).To(Succeed()) } }) It("posts a small message", func() { resp, err := client.Post( fmt.Sprintf("https://localhost:%d/echo", port), "text/plain", bytes.NewReader([]byte("Hello, world!")), ) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 5*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(body).To(Equal([]byte("Hello, world!"))) }) It("uploads a file", func() { resp, err := client.Post( fmt.Sprintf("https://localhost:%d/echo", port), "text/plain", bytes.NewReader(PRData), ) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 5*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(body).To(Equal(PRData)) }) It("uses gzip compression", func() { mux.HandleFunc("/gzipped/hello", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() Expect(r.Header.Get("Accept-Encoding")).To(Equal("gzip")) w.Header().Set("Content-Encoding", "gzip") w.Header().Set("foo", "bar") gw := gzip.NewWriter(w) defer gw.Close() gw.Write([]byte("Hello, World!\n")) }) client.Transport.(*http3.RoundTripper).DisableCompression = false resp, err := client.Get(fmt.Sprintf("https://localhost:%d/gzipped/hello", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) Expect(resp.Uncompressed).To(BeTrue()) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 3*time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal("Hello, World!\n")) }) It("handles context cancellations", func() { mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) { <-r.Context().Done() }) ctx, cancel := context.WithCancel(context.Background()) req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil) Expect(err).ToNot(HaveOccurred()) time.AfterFunc(50*time.Millisecond, cancel) _, err = client.Do(req) Expect(err).To(HaveOccurred()) Expect(err).To(MatchError(context.Canceled)) }) It("cancels requests", func() { handlerCalled := make(chan struct{}) mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() defer close(handlerCalled) // TODO(4508): check for request context cancellations for { if _, err := w.Write([]byte("foobar")); err != nil { var http3Err *http3.Error Expect(errors.As(err, &http3Err)).To(BeTrue()) Expect(http3Err.ErrorCode).To(Equal(http3.ErrCode(0x10c))) Expect(http3Err.Error()).To(Equal("H3_REQUEST_CANCELLED")) return } } }) req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil) Expect(err).ToNot(HaveOccurred()) ctx, cancel := context.WithCancel(context.Background()) req = req.WithContext(ctx) resp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) cancel() Eventually(handlerCalled).Should(BeClosed()) _, err = resp.Body.Read([]byte{0}) var http3Err *http3.Error Expect(errors.As(err, &http3Err)).To(BeTrue()) Expect(http3Err.ErrorCode).To(Equal(http3.ErrCode(0x10c))) Expect(http3Err.Error()).To(Equal("H3_REQUEST_CANCELLED (local)")) }) It("allows streamed HTTP requests", func() { done := make(chan struct{}) mux.HandleFunc("/echoline", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() defer close(done) w.WriteHeader(200) w.(http.Flusher).Flush() reader := bufio.NewReader(r.Body) for { msg, err := reader.ReadString('\n') if err != nil { return } _, err = w.Write([]byte(msg)) Expect(err).ToNot(HaveOccurred()) w.(http.Flusher).Flush() } }) r, w := io.Pipe() req, err := http.NewRequest(http.MethodPut, fmt.Sprintf("https://localhost:%d/echoline", port), r) Expect(err).ToNot(HaveOccurred()) rsp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(200)) reader := bufio.NewReader(rsp.Body) for i := 0; i < 5; i++ { msg := fmt.Sprintf("Hello world, %d!\n", i) fmt.Fprint(w, msg) msgRcvd, err := reader.ReadString('\n') Expect(err).ToNot(HaveOccurred()) Expect(msgRcvd).To(Equal(msg)) } Expect(req.Body.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("allows taking over the stream", func() { handlerCalled := make(chan struct{}) mux.HandleFunc("/httpstreamer", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() close(handlerCalled) w.WriteHeader(http.StatusOK) str := w.(http3.HTTPStreamer).HTTPStream() str.Write([]byte("foobar")) // Do this in a Go routine, so that the handler returns early. // This way, we can also check that the HTTP/3 doesn't close the stream. go func() { defer GinkgoRecover() _, err := io.Copy(str, str) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() }) req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/httpstreamer", port), nil) Expect(err).ToNot(HaveOccurred()) tlsConf := getTLSClientConfigWithoutServerName() tlsConf.NextProtos = []string{http3.NextProtoH3} conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") rt := http3.SingleDestinationRoundTripper{Connection: conn} str, err := rt.OpenRequestStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.SendRequestHeader(req)).To(Succeed()) // make sure the request is received (and not stuck in some buffer, for example) Eventually(handlerCalled).Should(BeClosed()) rsp, err := str.ReadResponse() Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(200)) b := make([]byte, 6) _, err = io.ReadFull(str, b) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte("foobar"))) data := GeneratePRData(8 * 1024) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) repl, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(repl).To(Equal(data)) }) It("serves QUIC connections", func() { tlsConf := getTLSConfig() tlsConf.NextProtos = []string{http3.NextProtoH3} ln, err := quic.ListenAddr("localhost:0", tlsConf, getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer ln.Close() done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) server.ServeQUICConn(conn) // returns once the client closes }() resp, err := client.Get(fmt.Sprintf("https://localhost:%d/hello", ln.Addr().(*net.UDPAddr).Port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) client.Transport.(io.Closer).Close() Eventually(done).Should(BeClosed()) }) It("supports read deadlines", func() { mux.HandleFunc("/read-deadline", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() rc := http.NewResponseController(w) Expect(rc.SetReadDeadline(time.Now().Add(deadlineDelay))).To(Succeed()) body, err := io.ReadAll(r.Body) Expect(err).To(MatchError(os.ErrDeadlineExceeded)) Expect(body).To(ContainSubstring("aa")) w.Write([]byte("ok")) }) expectedEnd := time.Now().Add(deadlineDelay) resp, err := client.Post( fmt.Sprintf("https://localhost:%d/read-deadline", port), "text/plain", neverEnding('a'), ) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay)) Expect(err).ToNot(HaveOccurred()) Expect(time.Now().After(expectedEnd)).To(BeTrue()) Expect(string(body)).To(Equal("ok")) }) It("supports write deadlines", func() { mux.HandleFunc("/write-deadline", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() rc := http.NewResponseController(w) Expect(rc.SetWriteDeadline(time.Now().Add(deadlineDelay))).To(Succeed()) _, err := io.Copy(w, neverEnding('a')) Expect(err).To(MatchError(os.ErrDeadlineExceeded)) }) expectedEnd := time.Now().Add(deadlineDelay) resp, err := client.Get(fmt.Sprintf("https://localhost:%d/write-deadline", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) body, err := io.ReadAll(gbytes.TimeoutReader(resp.Body, 2*deadlineDelay)) Expect(err).ToNot(HaveOccurred()) Expect(time.Now().After(expectedEnd)).To(BeTrue()) Expect(string(body)).To(ContainSubstring("aa")) }) It("sets remote address", func() { mux.HandleFunc("/remote-addr", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() _, ok := r.Context().Value(http3.RemoteAddrContextKey).(net.Addr) Expect(ok).To(BeTrue()) }) resp, err := client.Get(fmt.Sprintf("https://localhost:%d/remote-addr", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(200)) }) It("sets conn context", func() { type ctxKey int var tracingID quic.ConnectionTracingID server.ConnContext = func(ctx context.Context, c quic.Connection) context.Context { serv, ok := ctx.Value(http3.ServerContextKey).(*http3.Server) Expect(ok).To(BeTrue()) Expect(serv).To(Equal(server)) ctx = context.WithValue(ctx, ctxKey(0), "Hello") ctx = context.WithValue(ctx, ctxKey(1), c) tracingID = c.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) return ctx } mux.HandleFunc("/http3-conn-context", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() v, ok := r.Context().Value(ctxKey(0)).(string) Expect(ok).To(BeTrue()) Expect(v).To(Equal("Hello")) c, ok := r.Context().Value(ctxKey(1)).(quic.Connection) Expect(ok).To(BeTrue()) Expect(c).ToNot(BeNil()) serv, ok := r.Context().Value(http3.ServerContextKey).(*http3.Server) Expect(ok).To(BeTrue()) Expect(serv).To(Equal(server)) id, ok := r.Context().Value(quic.ConnectionTracingKey).(quic.ConnectionTracingID) Expect(ok).To(BeTrue()) Expect(id).To(Equal(tracingID)) }) resp, err := client.Get(fmt.Sprintf("https://localhost:%d/http3-conn-context", port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) }) It("uses the QUIC connection context", func() { conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{ Conn: conn, ConnContext: func(ctx context.Context) context.Context { //nolint:staticcheck return context.WithValue(ctx, "foo", "bar") }, } defer tr.Close() tlsConf := getTLSConfig() tlsConf.NextProtos = []string{http3.NextProtoH3} ln, err := tr.Listen(tlsConf, getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer ln.Close() mux.HandleFunc("/quic-conn-context", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() v, ok := r.Context().Value("foo").(string) Expect(ok).To(BeTrue()) Expect(v).To(Equal("bar")) }) go func() { defer GinkgoRecover() c, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) server.ServeQUICConn(c) }() resp, err := client.Get(fmt.Sprintf("https://localhost:%d/quic-conn-context", conn.LocalAddr().(*net.UDPAddr).Port)) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) }) It("checks the server's settings", func() { tlsConf := tlsClientConfigWithoutServerName.Clone() tlsConf.NextProtos = []string{http3.NextProtoH3} conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") rt := http3.SingleDestinationRoundTripper{Connection: conn} hconn := rt.Start() Eventually(hconn.ReceivedSettings(), 5*time.Second, 10*time.Millisecond).Should(BeClosed()) settings := hconn.Settings() Expect(settings.EnableExtendedConnect).To(BeTrue()) Expect(settings.EnableDatagrams).To(BeFalse()) Expect(settings.Other).To(BeEmpty()) }) It("receives the client's settings", func() { settingsChan := make(chan *http3.Settings, 1) mux.HandleFunc("/settings", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() conn := w.(http3.Hijacker).Connection() Eventually(conn.ReceivedSettings(), 5*time.Second, 10*time.Millisecond).Should(BeClosed()) settingsChan <- conn.Settings() w.WriteHeader(http.StatusOK) }) rt = &http3.RoundTripper{ TLSClientConfig: getTLSClientConfigWithoutServerName(), QUICConfig: getQuicConfig(&quic.Config{ MaxIdleTimeout: 10 * time.Second, EnableDatagrams: true, }), EnableDatagrams: true, AdditionalSettings: map[uint64]uint64{1337: 42}, } req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("https://localhost:%d/settings", port), nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) var settings *http3.Settings Expect(settingsChan).To(Receive(&settings)) Expect(settings).ToNot(BeNil()) Expect(settings.EnableDatagrams).To(BeTrue()) Expect(settings.EnableExtendedConnect).To(BeFalse()) Expect(settings.Other).To(HaveKeyWithValue(uint64(1337), uint64(42))) }) It("processes 1xx response", func() { header1 := "; rel=preload; as=style" header2 := "; rel=preload; as=script" data := "1xx-test-data" mux.HandleFunc("/103-early-data", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() w.Header().Add("Link", header1) w.Header().Add("Link", header2) w.WriteHeader(http.StatusEarlyHints) n, err := w.Write([]byte(data)) Expect(err).NotTo(HaveOccurred()) Expect(n).To(Equal(len(data))) w.WriteHeader(http.StatusOK) }) var ( cnt int status int hdr textproto.MIMEHeader ) ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ Got1xxResponse: func(code int, header textproto.MIMEHeader) error { hdr = header status = code cnt++ return nil }, }) req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/103-early-data", port), nil) Expect(err).ToNot(HaveOccurred()) resp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusOK)) body, err := io.ReadAll(resp.Body) Expect(err).ToNot(HaveOccurred()) Expect(string(body)).To(Equal(data)) Expect(status).To(Equal(http.StatusEarlyHints)) Expect(hdr).To(HaveKeyWithValue("Link", []string{header1, header2})) Expect(cnt).To(Equal(1)) Expect(resp.Header).To(HaveKeyWithValue("Link", []string{header1, header2})) Expect(resp.Body.Close()).To(Succeed()) }) It("processes 1xx terminal response", func() { mux.HandleFunc("/101-switch-protocols", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() w.Header().Add("Connection", "upgrade") w.Header().Add("Upgrade", "proto") w.WriteHeader(http.StatusSwitchingProtocols) }) var ( cnt int status int ) ctx := httptrace.WithClientTrace(context.Background(), &httptrace.ClientTrace{ Got1xxResponse: func(code int, header textproto.MIMEHeader) error { status = code cnt++ return nil }, }) req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/101-switch-protocols", port), nil) Expect(err).ToNot(HaveOccurred()) resp, err := client.Do(req) Expect(err).ToNot(HaveOccurred()) Expect(resp.StatusCode).To(Equal(http.StatusSwitchingProtocols)) Expect(resp.Header).To(HaveKeyWithValue("Connection", []string{"upgrade"})) Expect(resp.Header).To(HaveKeyWithValue("Upgrade", []string{"proto"})) Expect(status).To(Equal(0)) Expect(cnt).To(Equal(0)) }) Context("HTTP datagrams", func() { openDatagramStream := func(h string) (_ http3.RequestStream, closeFn func()) { tlsConf := getTLSClientConfigWithoutServerName() tlsConf.NextProtos = []string{http3.NextProtoH3} conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), tlsConf, getQuicConfig(&quic.Config{EnableDatagrams: true}), ) Expect(err).ToNot(HaveOccurred()) rt := &http3.SingleDestinationRoundTripper{ Connection: conn, EnableDatagrams: true, } str, err := rt.OpenRequestStream(context.Background()) Expect(err).ToNot(HaveOccurred()) u, err := url.Parse(h) Expect(err).ToNot(HaveOccurred()) req := &http.Request{ Method: http.MethodConnect, Proto: "datagrams", Host: u.Host, URL: u, } Expect(str.SendRequestHeader(req)).To(Succeed()) rsp, err := str.ReadResponse() Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(Equal(http.StatusOK)) return str, func() { conn.CloseWithError(0, "") } } It("sends an receives HTTP datagrams", func() { errChan := make(chan error, 1) const num = 5 datagramChan := make(chan struct{}, num) mux.HandleFunc("/datagrams", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() Expect(r.Method).To(Equal(http.MethodConnect)) conn := w.(http3.Hijacker).Connection() Eventually(conn.ReceivedSettings()).Should(BeClosed()) Expect(conn.Settings().EnableDatagrams).To(BeTrue()) w.WriteHeader(http.StatusOK) str := w.(http3.HTTPStreamer).HTTPStream() go str.Read([]byte{0}) // need to continue reading from stream to observe state transitions for { if _, err := str.ReceiveDatagram(context.Background()); err != nil { errChan <- err return } datagramChan <- struct{}{} } }) str, closeFn := openDatagramStream(fmt.Sprintf("https://localhost:%d/datagrams", port)) defer closeFn() for i := 0; i < num; i++ { b := make([]byte, 8) binary.BigEndian.PutUint64(b, uint64(i)) Expect(str.SendDatagram(bytes.Repeat(b, 100))).To(Succeed()) } var count int loop: for { select { case <-datagramChan: count++ if count >= num*4/5 { break loop } case err := <-errChan: Fail(fmt.Sprintf("receiving datagrams failed: %s", err)) } } str.CancelWrite(42) var resetErr error Eventually(errChan).Should(Receive(&resetErr)) Expect(resetErr.(*quic.StreamError).ErrorCode).To(BeEquivalentTo(42)) }) It("closes the send direction", func() { errChan := make(chan error, 1) datagramChan := make(chan []byte, 1) mux.HandleFunc("/datagrams", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() conn := w.(http3.Hijacker).Connection() Eventually(conn.ReceivedSettings()).Should(BeClosed()) Expect(conn.Settings().EnableDatagrams).To(BeTrue()) w.WriteHeader(http.StatusOK) str := w.(http3.HTTPStreamer).HTTPStream() go str.Read([]byte{0}) // need to continue reading from stream to observe state transitions for { data, err := str.ReceiveDatagram(context.Background()) if err != nil { errChan <- err return } datagramChan <- data } }) str, closeFn := openDatagramStream(fmt.Sprintf("https://localhost:%d/datagrams", port)) defer closeFn() go str.Read([]byte{0}) Expect(str.SendDatagram([]byte("foo"))).To(Succeed()) Eventually(datagramChan).Should(Receive(Equal([]byte("foo")))) // signal that we're done sending str.Close() var resetErr error Eventually(errChan).Should(Receive(&resetErr)) Expect(resetErr).To(Equal(io.EOF)) // make sure we can't send anymore Expect(str.SendDatagram([]byte("foo"))).ToNot(Succeed()) }) It("detecting a stream reset from the server", func() { errChan := make(chan error, 1) datagramChan := make(chan []byte, 1) mux.HandleFunc("/datagrams", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() conn := w.(http3.Hijacker).Connection() Eventually(conn.ReceivedSettings()).Should(BeClosed()) Expect(conn.Settings().EnableDatagrams).To(BeTrue()) w.WriteHeader(http.StatusOK) str := w.(http3.HTTPStreamer).HTTPStream() go str.Read([]byte{0}) // need to continue reading from stream to observe state transitions for { data, err := str.ReceiveDatagram(context.Background()) if err != nil { errChan <- err return } str.CancelRead(42) datagramChan <- data } }) str, closeFn := openDatagramStream(fmt.Sprintf("https://localhost:%d/datagrams", port)) defer closeFn() go str.Read([]byte{0}) Expect(str.SendDatagram([]byte("foo"))).To(Succeed()) Eventually(datagramChan).Should(Receive(Equal([]byte("foo")))) // signal that we're done sending var resetErr error Eventually(errChan).Should(Receive(&resetErr)) Expect(resetErr).To(Equal(&quic.StreamError{ErrorCode: 42, Remote: false})) // make sure we can't send anymore Expect(str.SendDatagram([]byte("foo"))).To(Equal(&quic.StreamError{ErrorCode: 42, Remote: true})) }) }) Context("0-RTT", func() { runCountingProxy := func(serverPort int, rtt time.Duration) (*quicproxy.QuicProxy, *atomic.Uint32) { var num0RTTPackets atomic.Uint32 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { if contains0RTTPacket(data) { num0RTTPackets.Add(1) } return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) return proxy, &num0RTTPackets } It("sends 0-RTT GET requests", func() { proxy, num0RTTPackets := runCountingProxy(port, scaleDuration(50*time.Millisecond)) defer proxy.Close() tlsConf := getTLSClientConfigWithoutServerName() puts := make(chan string, 10) tlsConf.ClientSessionCache = newClientSessionCache(tls.NewLRUClientSessionCache(10), nil, puts) rt := &http3.RoundTripper{ TLSClientConfig: tlsConf, QUICConfig: getQuicConfig(&quic.Config{ MaxIdleTimeout: 10 * time.Second, }), DisableCompression: true, } defer rt.Close() mux.HandleFunc("/0rtt", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(strconv.FormatBool(!r.TLS.HandshakeComplete))) }) req, err := http.NewRequest(http3.MethodGet0RTT, fmt.Sprintf("https://localhost:%d/0rtt", proxy.LocalPort()), nil) Expect(err).ToNot(HaveOccurred()) rsp, err := rt.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(BeEquivalentTo(200)) data, err := io.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(string(data)).To(Equal("false")) Expect(num0RTTPackets.Load()).To(BeZero()) Eventually(puts).Should(Receive()) rt2 := &http3.RoundTripper{ TLSClientConfig: rt.TLSClientConfig, QUICConfig: rt.QUICConfig, DisableCompression: true, } defer rt2.Close() rsp, err = rt2.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) Expect(rsp.StatusCode).To(BeEquivalentTo(200)) data, err = io.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(string(data)).To(Equal("true")) Expect(num0RTTPackets.Load()).To(BeNumerically(">", 0)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/key_update_test.go000066400000000000000000000053611465664453100306730ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var ( sentHeaders []*logging.ShortHeader receivedHeaders []*logging.ShortHeader ) func countKeyPhases() (sent, received int) { lastKeyPhase := protocol.KeyPhaseOne for _, hdr := range sentHeaders { if hdr.KeyPhase != lastKeyPhase { sent++ lastKeyPhase = hdr.KeyPhase } } lastKeyPhase = protocol.KeyPhaseOne for _, hdr := range receivedHeaders { if hdr.KeyPhase != lastKeyPhase { received++ lastKeyPhase = hdr.KeyPhase } } return } var keyUpdateConnTracer = &logging.ConnectionTracer{ SentShortHeaderPacket: func(hdr *logging.ShortHeader, _ logging.ByteCount, _ logging.ECN, _ *logging.AckFrame, _ []logging.Frame) { sentHeaders = append(sentHeaders, hdr) }, ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, _ logging.ByteCount, _ logging.ECN, _ []logging.Frame) { receivedHeaders = append(receivedHeaders, hdr) }, } var _ = Describe("Key Update tests", func() { It("downloads a large file", func() { origKeyUpdateInterval := handshake.KeyUpdateInterval defer func() { handshake.KeyUpdateInterval = origKeyUpdateInterval }() handshake.KeyUpdateInterval = 1 // update keys as frequently as possible server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) defer server.Close() go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRDataLong) Expect(err).ToNot(HaveOccurred()) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{Tracer: func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return keyUpdateConnTracer }}), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRDataLong)) Expect(conn.CloseWithError(0, "")).To(Succeed()) keyPhasesSent, keyPhasesReceived := countKeyPhases() fmt.Fprintf(GinkgoWriter, "Used %d key phases on outgoing and %d key phases on incoming packets.\n", keyPhasesSent, keyPhasesReceived) Expect(keyPhasesReceived).To(BeNumerically(">", 10)) Expect(keyPhasesReceived).To(BeNumerically("~", keyPhasesSent, 2)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/mitm_test.go000066400000000000000000000376461465664453100275220ustar00rootroot00000000000000package self_test import ( "context" "errors" "fmt" "io" "math" "net" "sync/atomic" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/testutils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("MITM test", func() { const connIDLen = 6 // explicitly set the connection ID length, so the proxy can parse it var ( clientUDPConn net.PacketConn serverTransport, clientTransport *quic.Transport serverConn quic.Connection serverConfig *quic.Config ) startServerAndProxy := func(delayCb quicproxy.DelayCallback, dropCb quicproxy.DropCallback, forceAddressValidation bool) (proxyPort int, closeFn func()) { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) c, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) serverTransport = &quic.Transport{ Conn: c, ConnectionIDLength: connIDLen, } addTracer(serverTransport) if forceAddressValidation { serverTransport.VerifySourceAddress = func(net.Addr) bool { return true } } ln, err := serverTransport.Listen(getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) var err error serverConn, err = ln.Accept(context.Background()) if err != nil { return } str, err := serverConn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: delayCb, DropPacket: dropCb, }) Expect(err).ToNot(HaveOccurred()) return proxy.LocalPort(), func() { proxy.Close() ln.Close() serverTransport.Close() <-done } } BeforeEach(func() { serverConfig = getQuicConfig(nil) addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) clientUDPConn, err = net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) clientTransport = &quic.Transport{ Conn: clientUDPConn, ConnectionIDLength: connIDLen, } addTracer(clientTransport) }) Context("unsuccessful attacks", func() { AfterEach(func() { Eventually(serverConn.Context().Done()).Should(BeClosed()) // Test shutdown is tricky due to the proxy. Just wait for a bit. time.Sleep(50 * time.Millisecond) Expect(clientUDPConn.Close()).To(Succeed()) Expect(clientTransport.Close()).To(Succeed()) }) Context("injecting invalid packets", func() { const rtt = 20 * time.Millisecond sendRandomPacketsOfSameType := func(conn *quic.Transport, remoteAddr net.Addr, raw []byte) { defer GinkgoRecover() const numPackets = 10 ticker := time.NewTicker(rtt / numPackets) defer ticker.Stop() if wire.IsLongHeaderPacket(raw[0]) { hdr, _, _, err := wire.ParsePacket(raw) Expect(err).ToNot(HaveOccurred()) replyHdr := &wire.ExtendedHeader{ Header: wire.Header{ DestConnectionID: hdr.DestConnectionID, SrcConnectionID: hdr.SrcConnectionID, Type: hdr.Type, Version: hdr.Version, }, PacketNumber: protocol.PacketNumber(rand.Int31n(math.MaxInt32 / 4)), PacketNumberLen: protocol.PacketNumberLen(rand.Int31n(4) + 1), } for i := 0; i < numPackets; i++ { payloadLen := rand.Int31n(100) replyHdr.Length = protocol.ByteCount(rand.Int31n(payloadLen + 1)) b, err := replyHdr.Append(nil, hdr.Version) Expect(err).ToNot(HaveOccurred()) r := make([]byte, payloadLen) rand.Read(r) b = append(b, r...) if _, err := conn.WriteTo(b, remoteAddr); err != nil { return } <-ticker.C } } else { connID, err := wire.ParseConnectionID(raw, connIDLen) Expect(err).ToNot(HaveOccurred()) _, pn, pnLen, _, err := wire.ParseShortHeader(raw, connIDLen) if err != nil { // normally, ParseShortHeader is called after decrypting the header Expect(err).To(MatchError(wire.ErrInvalidReservedBits)) } for i := 0; i < numPackets; i++ { b, err := wire.AppendShortHeader(nil, connID, pn, pnLen, protocol.KeyPhaseBit(rand.Intn(2))) Expect(err).ToNot(HaveOccurred()) payloadLen := rand.Int31n(100) r := make([]byte, payloadLen) rand.Read(r) b = append(b, r...) if _, err := conn.WriteTo(b, remoteAddr); err != nil { return } <-ticker.C } } } runTest := func(delayCb quicproxy.DelayCallback) { proxyPort, closeFn := startServerAndProxy(delayCb, nil, false) defer closeFn() raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort)) Expect(err).ToNot(HaveOccurred()) conn, err := clientTransport.Dial( context.Background(), raddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) Expect(conn.CloseWithError(0, "")).To(Succeed()) } It("downloads a message when the packets are injected towards the server", func() { delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { defer GinkgoRecover() go sendRandomPacketsOfSameType(clientTransport, serverTransport.Conn.LocalAddr(), raw) } return rtt / 2 } runTest(delayCb) }) It("downloads a message when the packets are injected towards the client", func() { delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionOutgoing { defer GinkgoRecover() go sendRandomPacketsOfSameType(serverTransport, clientTransport.Conn.LocalAddr(), raw) } return rtt / 2 } runTest(delayCb) }) }) runTest := func(dropCb quicproxy.DropCallback) { proxyPort, closeFn := startServerAndProxy(nil, dropCb, false) defer closeFn() raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort)) Expect(err).ToNot(HaveOccurred()) conn, err := clientTransport.Dial( context.Background(), raddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) Expect(conn.CloseWithError(0, "")).To(Succeed()) } Context("duplicating packets", func() { It("downloads a message when packets are duplicated towards the server", func() { dropCb := func(dir quicproxy.Direction, raw []byte) bool { defer GinkgoRecover() if dir == quicproxy.DirectionIncoming { _, err := clientTransport.WriteTo(raw, serverTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) } return false } runTest(dropCb) }) It("downloads a message when packets are duplicated towards the client", func() { dropCb := func(dir quicproxy.Direction, raw []byte) bool { defer GinkgoRecover() if dir == quicproxy.DirectionOutgoing { _, err := serverTransport.WriteTo(raw, clientTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) } return false } runTest(dropCb) }) }) Context("corrupting packets", func() { const idleTimeout = time.Second var numCorrupted, numPackets atomic.Int32 BeforeEach(func() { numCorrupted.Store(0) numPackets.Store(0) serverConfig.MaxIdleTimeout = idleTimeout }) AfterEach(func() { num := numCorrupted.Load() fmt.Fprintf(GinkgoWriter, "Corrupted %d of %d packets.", num, numPackets.Load()) Expect(num).To(BeNumerically(">=", 1)) // If the packet containing the CONNECTION_CLOSE is corrupted, // we have to wait for the connection to time out. Eventually(serverConn.Context().Done(), 3*idleTimeout).Should(BeClosed()) }) It("downloads a message when packet are corrupted towards the server", func() { const interval = 4 // corrupt every 4th packet (stochastically) dropCb := func(dir quicproxy.Direction, raw []byte) bool { defer GinkgoRecover() if dir == quicproxy.DirectionIncoming { numPackets.Add(1) if rand.Intn(interval) == 0 { pos := rand.Intn(len(raw)) raw[pos] = byte(rand.Intn(256)) _, err := clientTransport.WriteTo(raw, serverTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) numCorrupted.Add(1) return true } } return false } runTest(dropCb) }) It("downloads a message when packet are corrupted towards the client", func() { const interval = 10 // corrupt every 10th packet (stochastically) dropCb := func(dir quicproxy.Direction, raw []byte) bool { defer GinkgoRecover() if dir == quicproxy.DirectionOutgoing { numPackets.Add(1) if rand.Intn(interval) == 0 { pos := rand.Intn(len(raw)) raw[pos] = byte(rand.Intn(256)) _, err := serverTransport.WriteTo(raw, clientTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) numCorrupted.Add(1) return true } } return false } runTest(dropCb) }) }) }) Context("successful injection attacks", func() { // These tests demonstrate that the QUIC protocol is vulnerable to injection attacks before the handshake // finishes. In particular, an adversary who can intercept packets coming from one endpoint and send a reply // that arrives before the real reply can tear down the connection in multiple ways. const rtt = 20 * time.Millisecond runTest := func(proxyPort int) (closeFn func(), err error) { raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxyPort)) Expect(err).ToNot(HaveOccurred()) _, err = clientTransport.Dial( context.Background(), raddr, getTLSClientConfig(), getQuicConfig(&quic.Config{HandshakeIdleTimeout: scaleDuration(200 * time.Millisecond)}), ) return func() { clientTransport.Close() }, err } // fails immediately because client connection closes when it can't find compatible version It("fails when a forged version negotiation packet is sent to client", func() { done := make(chan struct{}) delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial { return 0 } // Create fake version negotiation packet with no supported versions versions := []protocol.Version{} packet := wire.ComposeVersionNegotiation( protocol.ArbitraryLenConnectionID(hdr.SrcConnectionID.Bytes()), protocol.ArbitraryLenConnectionID(hdr.DestConnectionID.Bytes()), versions, ) // Send the packet _, err = serverTransport.WriteTo(packet, clientTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) close(done) } return rtt / 2 } proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, false) defer serverCloseFn() closeFn, err := runTest(proxyPort) defer closeFn() Expect(err).To(HaveOccurred()) vnErr := &quic.VersionNegotiationError{} Expect(errors.As(err, &vnErr)).To(BeTrue()) Eventually(done).Should(BeClosed()) }) // times out, because client doesn't accept subsequent real retry packets from server // as it has already accepted a retry. // TODO: determine behavior when server does not send Retry packets It("fails when a forged Retry packet with modified Source Connection ID is sent to client", func() { var initialPacketIntercepted bool done := make(chan struct{}) delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming && !initialPacketIntercepted { defer GinkgoRecover() defer close(done) hdr, _, _, err := wire.ParsePacket(raw) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial { return 0 } initialPacketIntercepted = true fakeSrcConnID := protocol.ParseConnectionID([]byte{0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12}) retryPacket := testutils.ComposeRetryPacket(fakeSrcConnID, hdr.SrcConnectionID, hdr.DestConnectionID, []byte("token"), hdr.Version) _, err = serverTransport.WriteTo(retryPacket, clientTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) } return rtt / 2 } proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, true) defer serverCloseFn() closeFn, err := runTest(proxyPort) defer closeFn() Expect(err).To(HaveOccurred()) Expect(err.(net.Error).Timeout()).To(BeTrue()) Eventually(done).Should(BeClosed()) }) // times out, because client doesn't accept real retry packets from server because // it has already accepted an initial. // TODO: determine behavior when server does not send Retry packets It("fails when a forged initial packet is sent to client", func() { done := make(chan struct{}) var injected bool delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial || injected { return 0 } defer close(done) injected = true initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.DestConnectionID, nil, nil, protocol.PerspectiveServer, hdr.Version) _, err = serverTransport.WriteTo(initialPacket, clientTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) } return rtt } proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, false) defer serverCloseFn() closeFn, err := runTest(proxyPort) defer closeFn() Expect(err).To(HaveOccurred()) Expect(err.(net.Error).Timeout()).To(BeTrue()) Eventually(done).Should(BeClosed()) }) // client connection closes immediately on receiving ack for unsent packet It("fails when a forged initial packet with ack for unsent packet is sent to client", func() { done := make(chan struct{}) var injected bool delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial || injected { return 0 } defer close(done) injected = true // Fake Initial with ACK for packet 2 (unsent) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.DestConnectionID, nil, []wire.Frame{ack}, protocol.PerspectiveServer, hdr.Version) _, err = serverTransport.WriteTo(initialPacket, clientTransport.Conn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) } return rtt } proxyPort, serverCloseFn := startServerAndProxy(delayCb, nil, false) defer serverCloseFn() closeFn, err := runTest(proxyPort) defer closeFn() Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ProtocolViolation)) Expect(transportErr.ErrorMessage).To(ContainSubstring("received ACK for an unsent packet")) Eventually(done).Should(BeClosed()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/mtu_test.go000066400000000000000000000127401465664453100273450ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "sync" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("DPLPMTUD", func() { It("discovers the MTU", func() { rtt := scaleDuration(5 * time.Millisecond) const mtu = 1400 ln, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{ InitialPacketSize: 1234, DisablePathMTUDiscovery: true, EnableDatagrams: true, }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) _, err = io.Copy(str, str) Expect(err).ToNot(HaveOccurred()) str.Close() }() var mx sync.Mutex var maxPacketSizeServer int var clientPacketSizes []int serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(quicproxy.Direction, []byte) time.Duration { return rtt / 2 }, DropPacket: func(dir quicproxy.Direction, packet []byte) bool { if len(packet) > mtu { return true } mx.Lock() defer mx.Unlock() switch dir { case quicproxy.DirectionIncoming: clientPacketSizes = append(clientPacketSizes, len(packet)) case quicproxy.DirectionOutgoing: if len(packet) > maxPacketSizeServer { maxPacketSizeServer = len(packet) } } return false }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() // Make sure to use v4-only socket here. // We can't reliably set the DF bit on dual-stack sockets on macOS. udpConn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) Expect(err).ToNot(HaveOccurred()) defer udpConn.Close() tr := &quic.Transport{Conn: udpConn} defer tr.Close() var mtus []logging.ByteCount mtuTracer := &logging.ConnectionTracer{ UpdatedMTU: func(mtu logging.ByteCount, _ bool) { mtus = append(mtus, mtu) }, } conn, err := tr.Dial( context.Background(), proxy.LocalAddr(), getTLSClientConfig(), getQuicConfig(&quic.Config{ InitialPacketSize: protocol.MinInitialPacketSize, EnableDatagrams: true, Tracer: func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return mtuTracer }, }), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer close(done) defer GinkgoRecover() data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRDataLong)) }() err = conn.SendDatagram(make([]byte, 2000)) Expect(err).To(BeAssignableToTypeOf(&quic.DatagramTooLargeError{})) initialMaxDatagramSize := err.(*quic.DatagramTooLargeError).MaxDatagramPayloadSize _, err = str.Write(PRDataLong) Expect(err).ToNot(HaveOccurred()) str.Close() Eventually(done, 20*time.Second).Should(BeClosed()) err = conn.SendDatagram(make([]byte, 2000)) Expect(err).To(BeAssignableToTypeOf(&quic.DatagramTooLargeError{})) finalMaxDatagramSize := err.(*quic.DatagramTooLargeError).MaxDatagramPayloadSize mx.Lock() defer mx.Unlock() Expect(mtus).ToNot(BeEmpty()) maxPacketSizeClient := int(mtus[len(mtus)-1]) fmt.Fprintf(GinkgoWriter, "max client packet size: %d, MTU: %d\n", maxPacketSizeClient, mtu) fmt.Fprintf(GinkgoWriter, "max datagram size: initial: %d, final: %d\n", initialMaxDatagramSize, finalMaxDatagramSize) fmt.Fprintf(GinkgoWriter, "max server packet size: %d, MTU: %d\n", maxPacketSizeServer, mtu) Expect(maxPacketSizeClient).To(BeNumerically(">=", mtu-25)) const maxDiff = 40 // this includes the 21 bytes for the short header, 16 bytes for the encryption tag, and framing overhead Expect(initialMaxDatagramSize).To(BeNumerically(">=", protocol.MinInitialPacketSize-maxDiff)) Expect(finalMaxDatagramSize).To(BeNumerically(">=", maxPacketSizeClient-maxDiff)) // MTU discovery was disabled on the server side Expect(maxPacketSizeServer).To(Equal(1234)) var numPacketsLargerThanDiscoveredMTU int for _, s := range clientPacketSizes { if s > maxPacketSizeClient { numPacketsLargerThanDiscoveredMTU++ } } // The client shouldn't have sent any packets larger than the MTU it discovered, // except for at most one MTU probe packet. Expect(numPacketsLargerThanDiscoveredMTU).To(BeNumerically("<=", 1)) }) It("uses the initial packet size", func() { c, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) Expect(err).ToNot(HaveOccurred()) defer c.Close() cconn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0}) Expect(err).ToNot(HaveOccurred()) defer cconn.Close() ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { defer close(done) quic.Dial(ctx, cconn, c.LocalAddr(), getTLSClientConfig(), getQuicConfig(&quic.Config{InitialPacketSize: 1337})) }() b := make([]byte, 2000) n, _, err := c.ReadFrom(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(1337)) cancel() Eventually(done).Should(BeClosed()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/multiplex_test.go000066400000000000000000000171121465664453100305610ustar00rootroot00000000000000package self_test import ( "context" "crypto/rand" "io" "net" "runtime" "sync/atomic" "time" "github.com/quic-go/quic-go" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Multiplexing", func() { runServer := func(ln *quic.Listener) { go func() { defer GinkgoRecover() for { conn, err := ln.Accept(context.Background()) if err != nil { return } go func() { defer GinkgoRecover() str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) }() } }() } dial := func(tr *quic.Transport, addr net.Addr) { conn, err := tr.Dial( context.Background(), addr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) } Context("multiplexing clients on the same conn", func() { getListener := func() *quic.Listener { ln, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) return ln } It("multiplexes connections to the same server", func() { server := getListener() runServer(server) defer server.Close() addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{Conn: conn} addTracer(tr) done1 := make(chan struct{}) done2 := make(chan struct{}) go func() { defer GinkgoRecover() dial(tr, server.Addr()) close(done1) }() go func() { defer GinkgoRecover() dial(tr, server.Addr()) close(done2) }() timeout := 30 * time.Second if debugLog() { timeout = time.Minute } Eventually(done1, timeout).Should(BeClosed()) Eventually(done2, timeout).Should(BeClosed()) }) It("multiplexes connections to different servers", func() { server1 := getListener() runServer(server1) defer server1.Close() server2 := getListener() runServer(server2) defer server2.Close() addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{Conn: conn} addTracer(tr) done1 := make(chan struct{}) done2 := make(chan struct{}) go func() { defer GinkgoRecover() dial(tr, server1.Addr()) close(done1) }() go func() { defer GinkgoRecover() dial(tr, server2.Addr()) close(done2) }() timeout := 30 * time.Second if debugLog() { timeout = time.Minute } Eventually(done1, timeout).Should(BeClosed()) Eventually(done2, timeout).Should(BeClosed()) }) }) Context("multiplexing server and client on the same conn", func() { It("connects to itself", func() { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) defer conn.Close() tr := &quic.Transport{Conn: conn} addTracer(tr) server, err := tr.Listen( getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runServer(server) done := make(chan struct{}) go func() { defer GinkgoRecover() dial(tr, server.Addr()) close(done) }() timeout := 30 * time.Second if debugLog() { timeout = time.Minute } Eventually(done, timeout).Should(BeClosed()) }) // This test would require setting of iptables rules, see https://stackoverflow.com/questions/23859164/linux-udp-socket-sendto-operation-not-permitted. if runtime.GOOS != "linux" { It("runs a server and client on the same conn", func() { addr1, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn1, err := net.ListenUDP("udp", addr1) Expect(err).ToNot(HaveOccurred()) defer conn1.Close() tr1 := &quic.Transport{Conn: conn1} addTracer(tr1) addr2, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn2, err := net.ListenUDP("udp", addr2) Expect(err).ToNot(HaveOccurred()) defer conn2.Close() tr2 := &quic.Transport{Conn: conn2} addTracer(tr2) server1, err := tr1.Listen( getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runServer(server1) defer server1.Close() server2, err := tr2.Listen( getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runServer(server2) defer server2.Close() done1 := make(chan struct{}) done2 := make(chan struct{}) go func() { defer GinkgoRecover() dial(tr2, server1.Addr()) close(done1) }() go func() { defer GinkgoRecover() dial(tr1, server2.Addr()) close(done2) }() timeout := 30 * time.Second if debugLog() { timeout = time.Minute } Eventually(done1, timeout).Should(BeClosed()) Eventually(done2, timeout).Should(BeClosed()) }) } }) It("sends and receives non-QUIC packets", func() { addr1, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn1, err := net.ListenUDP("udp", addr1) Expect(err).ToNot(HaveOccurred()) defer conn1.Close() tr1 := &quic.Transport{Conn: conn1} addTracer(tr1) addr2, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn2, err := net.ListenUDP("udp", addr2) Expect(err).ToNot(HaveOccurred()) defer conn2.Close() tr2 := &quic.Transport{Conn: conn2} addTracer(tr2) server, err := tr1.Listen(getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) runServer(server) defer server.Close() ctx, cancel := context.WithCancel(context.Background()) defer cancel() var sentPackets, rcvdPackets atomic.Int64 const packetLen = 128 // send a non-QUIC packet every 100µs go func() { defer GinkgoRecover() ticker := time.NewTicker(time.Millisecond / 10) defer ticker.Stop() var wroteFirstPacket bool for { select { case <-ticker.C: case <-ctx.Done(): return } b := make([]byte, packetLen) rand.Read(b[1:]) // keep the first byte set to 0, so it's not classified as a QUIC packet _, err := tr1.WriteTo(b, tr2.Conn.LocalAddr()) // The first sendmsg call on a new UDP socket sometimes errors on Linux. // It's not clear why this happens. // See https://github.com/golang/go/issues/63322. if err != nil && !wroteFirstPacket && isPermissionError(err) { _, err = tr1.WriteTo(b, tr2.Conn.LocalAddr()) } if ctx.Err() != nil { // ctx canceled while Read was executing return } Expect(err).ToNot(HaveOccurred()) sentPackets.Add(1) wroteFirstPacket = true } }() // receive and count non-QUIC packets go func() { defer GinkgoRecover() for { b := make([]byte, 1024) n, addr, err := tr2.ReadNonQUICPacket(ctx, b) if err != nil { Expect(err).To(MatchError(context.Canceled)) return } Expect(addr).To(Equal(tr1.Conn.LocalAddr())) Expect(n).To(Equal(packetLen)) rcvdPackets.Add(1) } }() dial(tr2, server.Addr()) Eventually(func() int64 { return sentPackets.Load() }).Should(BeNumerically(">", 10)) Eventually(func() int64 { return rcvdPackets.Load() }).Should(BeNumerically(">=", sentPackets.Load()*4/5)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/packetization_test.go000066400000000000000000000067301465664453100314070ustar00rootroot00000000000000package self_test import ( "context" "fmt" "net" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Packetization", func() { // In this test, the client sends 100 small messages. The server echoes these messages. // This means that every endpoint will send 100 ack-eliciting packets in short succession. // This test then tests that no more than 110 packets are sent in every direction, making sure that ACK are bundled. It("bundles ACKs", func() { const numMsg = 100 serverCounter, serverTracer := newPacketTracer() server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{ DisablePathMTUDiscovery: true, Tracer: newTracer(serverTracer), }), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() serverAddr := fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: serverAddr, DelayPacket: func(dir quicproxy.Direction, _ []byte) time.Duration { return 5 * time.Millisecond }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() clientCounter, clientTracer := newPacketTracer() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ DisablePathMTUDiscovery: true, Tracer: newTracer(clientTracer), }), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 1) // Echo every byte received from the client. for { if _, err := str.Read(b); err != nil { break } _, err = str.Write(b) Expect(err).ToNot(HaveOccurred()) } }() str, err := conn.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 1) // Send numMsg 1-byte messages. for i := 0; i < numMsg; i++ { _, err = str.Write([]byte{uint8(i)}) Expect(err).ToNot(HaveOccurred()) _, err = str.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(b[0]).To(Equal(uint8(i))) } Expect(conn.CloseWithError(0, "")).To(Succeed()) countBundledPackets := func(packets []shortHeaderPacket) (numBundled int) { for _, p := range packets { var hasAck, hasStreamFrame bool for _, f := range p.frames { switch f.(type) { case *logging.AckFrame: hasAck = true case *logging.StreamFrame: hasStreamFrame = true } } if hasAck && hasStreamFrame { numBundled++ } } return } numBundledIncoming := countBundledPackets(clientCounter.getRcvdShortHeaderPackets()) numBundledOutgoing := countBundledPackets(serverCounter.getRcvdShortHeaderPackets()) fmt.Fprintf(GinkgoWriter, "bundled incoming packets: %d / %d\n", numBundledIncoming, numMsg) fmt.Fprintf(GinkgoWriter, "bundled outgoing packets: %d / %d\n", numBundledOutgoing, numMsg) Expect(numBundledIncoming).To(And( BeNumerically("<=", numMsg), BeNumerically(">", numMsg*9/10), )) Expect(numBundledOutgoing).To(And( BeNumerically("<=", numMsg), BeNumerically(">", numMsg*9/10), )) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/qlog_dir_test.go000066400000000000000000000043241465664453100303370ustar00rootroot00000000000000package self_test import ( "context" "os" "path" "regexp" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/qlog" ) var _ = Describe("qlog dir tests", Serial, func() { var originalQlogDirValue string var tempTestDirPath string BeforeEach(func() { originalQlogDirValue = os.Getenv("QLOGDIR") var err error tempTestDirPath, err = os.MkdirTemp("", "temp_test_dir") Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { err := os.Setenv("QLOGDIR", originalQlogDirValue) Expect(err).ToNot(HaveOccurred()) err = os.RemoveAll(tempTestDirPath) Expect(err).ToNot(HaveOccurred()) }) handshake := func() { serverStopped := make(chan struct{}) server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), &quic.Config{ Tracer: qlog.DefaultConnectionTracer, }, ) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() defer close(serverStopped) for { if _, err := server.Accept(context.Background()); err != nil { return } } }() conn, err := quic.DialAddr( context.Background(), server.Addr().String(), getTLSClientConfig(), &quic.Config{ Tracer: qlog.DefaultConnectionTracer, }, ) Expect(err).ToNot(HaveOccurred()) conn.CloseWithError(0, "") server.Close() <-serverStopped } It("environment variable is set", func() { qlogDir := path.Join(tempTestDirPath, "qlogs") err := os.Setenv("QLOGDIR", qlogDir) Expect(err).ToNot(HaveOccurred()) handshake() _, err = os.Stat(tempTestDirPath) qlogDirCreated := !os.IsNotExist(err) Expect(qlogDirCreated).To(BeTrue()) childs, err := os.ReadDir(qlogDir) Expect(err).ToNot(HaveOccurred()) Expect(len(childs)).To(Equal(2)) odcids := make([]string, 0) vantagePoints := make([]string, 0) qlogFileNameRegexp := regexp.MustCompile(`^([0-f]+)_(client|server).sqlog$`) for _, child := range childs { matches := qlogFileNameRegexp.FindStringSubmatch(child.Name()) Expect(matches).To(HaveLen(3)) odcids = append(odcids, matches[1]) vantagePoints = append(vantagePoints, matches[2]) } Expect(odcids[0]).To(Equal(odcids[1])) Expect(vantagePoints).To(ContainElements("client", "server")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/resumption_test.go000066400000000000000000000126741465664453100307530ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "net" "time" "github.com/quic-go/quic-go" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type clientSessionCache struct { cache tls.ClientSessionCache gets chan<- string puts chan<- string } func newClientSessionCache(cache tls.ClientSessionCache, gets, puts chan<- string) *clientSessionCache { return &clientSessionCache{ cache: cache, gets: gets, puts: puts, } } var _ tls.ClientSessionCache = &clientSessionCache{} func (c *clientSessionCache) Get(sessionKey string) (*tls.ClientSessionState, bool) { session, ok := c.cache.Get(sessionKey) if c.gets != nil { c.gets <- sessionKey } return session, ok } func (c *clientSessionCache) Put(sessionKey string, cs *tls.ClientSessionState) { c.cache.Put(sessionKey, cs) if c.puts != nil { c.puts <- sessionKey } } var _ = Describe("TLS session resumption", func() { It("uses session resumption", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer server.Close() gets := make(chan string, 100) puts := make(chan string, 100) cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts) tlsConf := getTLSClientConfig() tlsConf.ClientSessionCache = cache conn1, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn1.CloseWithError(0, "") var sessionKey string Eventually(puts).Should(Receive(&sessionKey)) Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse()) serverConn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) conn2, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) Expect(gets).To(Receive(Equal(sessionKey))) Expect(conn2.ConnectionState().TLS.DidResume).To(BeTrue()) serverConn, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue()) conn2.CloseWithError(0, "") }) It("doesn't use session resumption, if the config disables it", func() { sConf := getTLSConfig() sConf.SessionTicketsDisabled = true server, err := quic.ListenAddr("localhost:0", sConf, getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer server.Close() gets := make(chan string, 100) puts := make(chan string, 100) cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts) tlsConf := getTLSClientConfig() tlsConf.ClientSessionCache = cache conn1, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn1.CloseWithError(0, "") Consistently(puts).ShouldNot(Receive()) Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse()) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() serverConn, err := server.Accept(ctx) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) conn2, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) Expect(conn2.ConnectionState().TLS.DidResume).To(BeFalse()) defer conn2.CloseWithError(0, "") serverConn, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) }) It("doesn't use session resumption, if the config returned by GetConfigForClient disables it", func() { sConf := &tls.Config{ GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { conf := getTLSConfig() conf.SessionTicketsDisabled = true return conf, nil }, } server, err := quic.ListenAddr("localhost:0", sConf, getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer server.Close() gets := make(chan string, 100) puts := make(chan string, 100) cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts) tlsConf := getTLSClientConfig() tlsConf.ClientSessionCache = cache conn1, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) Consistently(puts).ShouldNot(Receive()) Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse()) defer conn1.CloseWithError(0, "") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() serverConn, err := server.Accept(ctx) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) conn2, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) Expect(conn2.ConnectionState().TLS.DidResume).To(BeFalse()) defer conn2.CloseWithError(0, "") serverConn, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/rtt_test.go000066400000000000000000000054471465664453100273570ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("non-zero RTT", func() { runServer := func() *quic.Listener { ln, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) str.Close() }() return ln } downloadFile := func(port int) { conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) conn.CloseWithError(0, "") } for _, r := range [...]time.Duration{ 10 * time.Millisecond, 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond, } { rtt := r It(fmt.Sprintf("downloads a message with %s RTT", rtt), func() { ln := runServer() defer ln.Close() serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(quicproxy.Direction, []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) conn.CloseWithError(0, "") }) } for _, r := range [...]time.Duration{ 10 * time.Millisecond, 40 * time.Millisecond, } { rtt := r It(fmt.Sprintf("downloads a message with %s RTT, with reordering", rtt), func() { ln := runServer() defer ln.Close() serverPort := ln.Addr().(*net.UDPAddr).Port proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(quicproxy.Direction, []byte) time.Duration { return randomDuration(rtt/2, rtt*3/2) / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() downloadFile(proxy.LocalPort()) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/self_suite_linux_test.go000066400000000000000000000006521465664453100321200ustar00rootroot00000000000000//go:build linux package self_test import ( "errors" "os" "golang.org/x/sys/unix" ) // The first sendmsg call on a new UDP socket sometimes errors on Linux. // It's not clear why this happens. // See https://github.com/golang/go/issues/63322. func isPermissionError(err error) bool { var serr *os.SyscallError if errors.As(err, &serr) { return serr.Syscall == "sendmsg" && serr.Err == unix.EPERM } return false } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/self_suite_others_test.go000066400000000000000000000001371465664453100322630ustar00rootroot00000000000000//go:build !linux package self_test func isPermissionError(err error) bool { return false } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/self_suite_test.go000066400000000000000000000202271465664453100307010ustar00rootroot00000000000000package self_test import ( "bytes" "context" "crypto/tls" "crypto/x509" "flag" "fmt" "log" "os" "runtime/pprof" "strconv" "strings" "sync" "testing" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/integrationtests/tools" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) const alpn = tools.ALPN const ( dataLen = 500 * 1024 // 500 KB dataLenLong = 50 * 1024 * 1024 // 50 MB ) var ( // PRData contains dataLen bytes of pseudo-random data. PRData = GeneratePRData(dataLen) // PRDataLong contains dataLenLong bytes of pseudo-random data. PRDataLong = GeneratePRData(dataLenLong) ) // See https://en.wikipedia.org/wiki/Lehmer_random_number_generator func GeneratePRData(l int) []byte { res := make([]byte, l) seed := uint64(1) for i := 0; i < l; i++ { seed = seed * 48271 % 2147483647 res[i] = byte(seed) } return res } const logBufSize = 100 * 1 << 20 // initial size of the log buffer: 100 MB type syncedBuffer struct { mutex sync.Mutex *bytes.Buffer } func (b *syncedBuffer) Write(p []byte) (int, error) { b.mutex.Lock() n, err := b.Buffer.Write(p) b.mutex.Unlock() return n, err } func (b *syncedBuffer) Bytes() []byte { b.mutex.Lock() p := b.Buffer.Bytes() b.mutex.Unlock() return p } func (b *syncedBuffer) Reset() { b.mutex.Lock() b.Buffer.Reset() b.mutex.Unlock() } var ( logFileName string // the log file set in the ginkgo flags logBufOnce sync.Once logBuf *syncedBuffer versionParam string enableQlog bool version quic.Version tlsConfig *tls.Config tlsConfigLongChain *tls.Config tlsClientConfig *tls.Config tlsClientConfigWithoutServerName *tls.Config ) // read the logfile command line flag // to set call ginkgo -- -logfile=log.txt func init() { flag.StringVar(&logFileName, "logfile", "", "log file") flag.StringVar(&versionParam, "version", "1", "QUIC version") flag.BoolVar(&enableQlog, "qlog", false, "enable qlog") ca, caPrivateKey, err := tools.GenerateCA() if err != nil { panic(err) } leafCert, leafPrivateKey, err := tools.GenerateLeafCert(ca, caPrivateKey) if err != nil { panic(err) } tlsConfig = &tls.Config{ Certificates: []tls.Certificate{{ Certificate: [][]byte{leafCert.Raw}, PrivateKey: leafPrivateKey, }}, NextProtos: []string{alpn}, } tlsConfLongChain, err := tools.GenerateTLSConfigWithLongCertChain(ca, caPrivateKey) if err != nil { panic(err) } tlsConfigLongChain = tlsConfLongChain root := x509.NewCertPool() root.AddCert(ca) tlsClientConfig = &tls.Config{ ServerName: "localhost", RootCAs: root, NextProtos: []string{alpn}, } tlsClientConfigWithoutServerName = &tls.Config{ RootCAs: root, NextProtos: []string{alpn}, } } var _ = BeforeSuite(func() { switch versionParam { case "1": version = quic.Version1 case "2": version = quic.Version2 default: Fail(fmt.Sprintf("unknown QUIC version: %s", versionParam)) } fmt.Printf("Using QUIC version: %s\n", version) protocol.SupportedVersions = []quic.Version{version} }) func getTLSConfig() *tls.Config { return tlsConfig.Clone() } func getTLSConfigWithLongCertChain() *tls.Config { return tlsConfigLongChain.Clone() } func getTLSClientConfig() *tls.Config { return tlsClientConfig.Clone() } func getTLSClientConfigWithoutServerName() *tls.Config { return tlsClientConfigWithoutServerName.Clone() } func getQuicConfig(conf *quic.Config) *quic.Config { if conf == nil { conf = &quic.Config{} } else { conf = conf.Clone() } if !enableQlog { return conf } if conf.Tracer == nil { conf.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { return logging.NewMultiplexedConnectionTracer( tools.NewQlogConnectionTracer(GinkgoWriter)(ctx, p, connID), // multiplex it with an empty tracer to check that we're correctly ignoring unset callbacks everywhere &logging.ConnectionTracer{}, ) } return conf } origTracer := conf.Tracer conf.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { tr := origTracer(ctx, p, connID) qlogger := tools.NewQlogConnectionTracer(GinkgoWriter)(ctx, p, connID) if tr == nil { return qlogger } return logging.NewMultiplexedConnectionTracer(qlogger, tr) } return conf } func addTracer(tr *quic.Transport) { if !enableQlog { return } if tr.Tracer == nil { tr.Tracer = logging.NewMultiplexedTracer( tools.QlogTracer(GinkgoWriter), // multiplex it with an empty tracer to check that we're correctly ignoring unset callbacks everywhere &logging.Tracer{}, ) return } origTracer := tr.Tracer tr.Tracer = logging.NewMultiplexedTracer( tools.QlogTracer(GinkgoWriter), origTracer, ) } var _ = BeforeEach(func() { log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds) if debugLog() { logBufOnce.Do(func() { logBuf = &syncedBuffer{Buffer: bytes.NewBuffer(make([]byte, 0, logBufSize))} }) utils.DefaultLogger.SetLogLevel(utils.LogLevelDebug) log.SetOutput(logBuf) } }) func areHandshakesRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "RunHandshake") } func areTransportsRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "quic-go.(*Transport).listen") } var _ = AfterEach(func() { Expect(areHandshakesRunning()).To(BeFalse()) Eventually(areTransportsRunning).Should(BeFalse()) if debugLog() { logFile, err := os.Create(logFileName) Expect(err).ToNot(HaveOccurred()) logFile.Write(logBuf.Bytes()) logFile.Close() logBuf.Reset() } }) // Debug says if this test is being logged func debugLog() bool { return len(logFileName) > 0 } func scaleDuration(d time.Duration) time.Duration { scaleFactor := 1 if f, err := strconv.Atoi(os.Getenv("TIMESCALE_FACTOR")); err == nil { // parsing "" errors, so this works fine if the env is not set scaleFactor = f } Expect(scaleFactor).ToNot(BeZero()) return time.Duration(scaleFactor) * d } func newTracer(tracer *logging.ConnectionTracer) func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return tracer } } type packet struct { time time.Time hdr *logging.ExtendedHeader frames []logging.Frame } type shortHeaderPacket struct { time time.Time hdr *logging.ShortHeader frames []logging.Frame } type packetCounter struct { closed chan struct{} sentShortHdr, rcvdShortHdr []shortHeaderPacket rcvdLongHdr []packet } func (t *packetCounter) getSentShortHeaderPackets() []shortHeaderPacket { <-t.closed return t.sentShortHdr } func (t *packetCounter) getRcvdLongHeaderPackets() []packet { <-t.closed return t.rcvdLongHdr } func (t *packetCounter) getRcvdShortHeaderPackets() []shortHeaderPacket { <-t.closed return t.rcvdShortHdr } func newPacketTracer() (*packetCounter, *logging.ConnectionTracer) { c := &packetCounter{closed: make(chan struct{})} return c, &logging.ConnectionTracer{ ReceivedLongHeaderPacket: func(hdr *logging.ExtendedHeader, _ logging.ByteCount, _ logging.ECN, frames []logging.Frame) { c.rcvdLongHdr = append(c.rcvdLongHdr, packet{time: time.Now(), hdr: hdr, frames: frames}) }, ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, _ logging.ByteCount, _ logging.ECN, frames []logging.Frame) { c.rcvdShortHdr = append(c.rcvdShortHdr, shortHeaderPacket{time: time.Now(), hdr: hdr, frames: frames}) }, SentShortHeaderPacket: func(hdr *logging.ShortHeader, _ logging.ByteCount, _ logging.ECN, ack *wire.AckFrame, frames []logging.Frame) { if ack != nil { frames = append(frames, ack) } c.sentShortHdr = append(c.sentShortHdr, shortHeaderPacket{time: time.Now(), hdr: hdr, frames: frames}) }, Close: func() { close(c.closed) }, } } func TestSelf(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Self integration tests") } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/stateless_reset_test.go000066400000000000000000000072531465664453100317540ustar00rootroot00000000000000package self_test import ( "context" "crypto/rand" "fmt" "net" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Stateless Resets", func() { connIDLens := []int{0, 10} for i := range connIDLens { connIDLen := connIDLens[i] It(fmt.Sprintf("sends and recognizes stateless resets, for %d byte connection IDs", connIDLen), func() { var statelessResetKey quic.StatelessResetKey rand.Read(statelessResetKey[:]) c, err := net.ListenUDP("udp", nil) Expect(err).ToNot(HaveOccurred()) tr := &quic.Transport{ Conn: c, StatelessResetKey: &statelessResetKey, ConnectionIDLength: connIDLen, } defer tr.Close() ln, err := tr.Listen(getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) serverPort := ln.Addr().(*net.UDPAddr).Port closeServer := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) <-closeServer Expect(ln.Close()).To(Succeed()) Expect(tr.Close()).To(Succeed()) }() var drop atomic.Bool proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DropPacket: func(quicproxy.Direction, []byte) bool { return drop.Load() }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) defer udpConn.Close() cl := &quic.Transport{ Conn: udpConn, ConnectionIDLength: connIDLen, } defer cl.Close() conn, err := cl.Dial( context.Background(), &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxy.LocalPort()}, getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIdleTimeout: 2 * time.Second}), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data := make([]byte, 6) _, err = str.Read(data) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) // make sure that the CONNECTION_CLOSE is dropped drop.Store(true) close(closeServer) time.Sleep(100 * time.Millisecond) // We need to create a new Transport here, since the old one is still sending out // CONNECTION_CLOSE packets for (recently) closed connections). tr2 := &quic.Transport{ Conn: c, ConnectionIDLength: connIDLen, StatelessResetKey: &statelessResetKey, } defer tr2.Close() ln2, err := tr2.Listen(getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) drop.Store(false) acceptStopped := make(chan struct{}) go func() { defer GinkgoRecover() _, err := ln2.Accept(context.Background()) Expect(err).To(HaveOccurred()) close(acceptStopped) }() // Trigger something (not too small) to be sent, so that we receive the stateless reset. // If the client already sent another packet, it might already have received a packet. _, serr := str.Write([]byte("Lorem ipsum dolor sit amet.")) if serr == nil { _, serr = str.Read([]byte{0}) } Expect(serr).To(HaveOccurred()) Expect(serr).To(BeAssignableToTypeOf(&quic.StatelessResetError{})) Expect(ln2.Close()).To(Succeed()) Eventually(acceptStopped).Should(BeClosed()) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/stream_test.go000066400000000000000000000071001465664453100300250ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "sync" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/quic-go/quic-go" ) var _ = Describe("Bidirectional streams", func() { const numStreams = 300 var ( server *quic.Listener serverAddr string ) BeforeEach(func() { var err error server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) serverAddr = fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) }) AfterEach(func() { server.Close() }) runSendingPeer := func(conn quic.Connection) { var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { str, err := conn.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) data := GeneratePRData(25 * i) go func() { defer GinkgoRecover() _, err := str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() go func() { defer GinkgoRecover() defer wg.Done() dataRead, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(dataRead).To(Equal(data)) }() } wg.Wait() } runReceivingPeer := func(conn quic.Connection) { var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() defer wg.Done() // shouldn't use io.Copy here // we should read from the stream as early as possible, to free flow control credit data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } wg.Wait() } It(fmt.Sprintf("client opening %d streams to a server", numStreams), func() { var conn quic.Connection go func() { defer GinkgoRecover() var err error conn, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(conn) }() client, err := quic.DialAddr( context.Background(), serverAddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runSendingPeer(client) client.CloseWithError(0, "") <-conn.Context().Done() }) It(fmt.Sprintf("server opening %d streams to a client", numStreams), func() { go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runSendingPeer(conn) conn.CloseWithError(0, "") }() client, err := quic.DialAddr( context.Background(), serverAddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(client) Eventually(client.Context().Done()).Should(BeClosed()) }) It(fmt.Sprintf("client and server opening %d each and sending data to the peer", numStreams), func() { done1 := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() runReceivingPeer(conn) close(done) }() runSendingPeer(conn) <-done close(done1) }() client, err := quic.DialAddr( context.Background(), serverAddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) done2 := make(chan struct{}) go func() { defer GinkgoRecover() runSendingPeer(client) close(done2) }() runReceivingPeer(client) <-done1 <-done2 client.CloseWithError(0, "") }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/timeout_test.go000066400000000000000000000362261465664453100302330ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" mrand "math/rand" "net" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type faultyConn struct { net.PacketConn MaxPackets int32 counter atomic.Int32 } func (c *faultyConn) ReadFrom(p []byte) (int, net.Addr, error) { n, addr, err := c.PacketConn.ReadFrom(p) counter := c.counter.Add(1) if counter <= c.MaxPackets { return n, addr, err } return 0, nil, io.ErrClosedPipe } func (c *faultyConn) WriteTo(p []byte, addr net.Addr) (int, error) { counter := c.counter.Add(1) if counter <= c.MaxPackets { return c.PacketConn.WriteTo(p, addr) } return 0, io.ErrClosedPipe } var _ = Describe("Timeout tests", func() { checkTimeoutError := func(err error) { ExpectWithOffset(1, err).To(MatchError(&quic.IdleTimeoutError{})) nerr, ok := err.(net.Error) ExpectWithOffset(1, ok).To(BeTrue()) ExpectWithOffset(1, nerr.Timeout()).To(BeTrue()) } It("returns net.Error timeout errors when dialing", func() { errChan := make(chan error) go func() { _, err := quic.DialAddr( context.Background(), "localhost:12345", getTLSClientConfig(), getQuicConfig(&quic.Config{HandshakeIdleTimeout: scaleDuration(50 * time.Millisecond)}), ) errChan <- err }() var err error Eventually(errChan).Should(Receive(&err)) checkTimeoutError(err) }) It("returns the context error when the context expires", func() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() errChan := make(chan error) go func() { _, err := quic.DialAddr( ctx, "localhost:12345", getTLSClientConfig(), getQuicConfig(nil), ) errChan <- err }() var err error Eventually(errChan).Should(Receive(&err)) // This is not a net.Error timeout error Expect(err).To(MatchError(context.DeadlineExceeded)) }) It("returns the context error when the context expires with 0RTT enabled", func() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() errChan := make(chan error) go func() { _, err := quic.DialAddrEarly( ctx, "localhost:12345", getTLSClientConfig(), getQuicConfig(nil), ) errChan <- err }() var err error Eventually(errChan).Should(Receive(&err)) // This is not a net.Error timeout error Expect(err).To(MatchError(context.DeadlineExceeded)) }) It("returns net.Error timeout errors when an idle timeout occurs", func() { const idleTimeout = 500 * time.Millisecond server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) }() var drop atomic.Bool proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), DropPacket: func(quicproxy.Direction, []byte) bool { return drop.Load() }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true, MaxIdleTimeout: idleTimeout}), ) Expect(err).ToNot(HaveOccurred()) strIn, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) strOut, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = strIn.Read(make([]byte, 6)) Expect(err).ToNot(HaveOccurred()) drop.Store(true) time.Sleep(2 * idleTimeout) _, err = strIn.Write([]byte("test")) checkTimeoutError(err) _, err = strIn.Read([]byte{0}) checkTimeoutError(err) _, err = strOut.Write([]byte("test")) checkTimeoutError(err) _, err = strOut.Read([]byte{0}) checkTimeoutError(err) _, err = conn.OpenStream() checkTimeoutError(err) _, err = conn.OpenUniStream() checkTimeoutError(err) _, err = conn.AcceptStream(context.Background()) checkTimeoutError(err) _, err = conn.AcceptUniStream(context.Background()) checkTimeoutError(err) }) Context("timing out at the right time", func() { var idleTimeout time.Duration BeforeEach(func() { idleTimeout = scaleDuration(500 * time.Millisecond) }) It("times out after inactivity", func() { server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() serverConnChan := make(chan quic.Connection, 1) serverConnClosed := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) serverConnChan <- conn conn.AcceptStream(context.Background()) // blocks until the connection is closed close(serverConnClosed) }() counter, tr := newPacketTracer() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: idleTimeout, Tracer: newTracer(tr), DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := conn.AcceptStream(context.Background()) checkTimeoutError(err) close(done) }() Eventually(done, 2*idleTimeout).Should(BeClosed()) var lastAckElicitingPacketSentAt time.Time for _, p := range counter.getSentShortHeaderPackets() { var hasAckElicitingFrame bool for _, f := range p.frames { if _, ok := f.(*logging.AckFrame); ok { continue } hasAckElicitingFrame = true break } if hasAckElicitingFrame { lastAckElicitingPacketSentAt = p.time } } rcvdPackets := counter.getRcvdShortHeaderPackets() lastPacketRcvdAt := rcvdPackets[len(rcvdPackets)-1].time // We're ignoring here that only the first ack-eliciting packet sent resets the idle timeout. // This is ok since we're dealing with a lossless connection here, // and we'd expect to receive an ACK for additional other ack-eliciting packet sent. Expect(max(time.Since(lastAckElicitingPacketSentAt), time.Since(lastPacketRcvdAt))).To(And( BeNumerically(">=", idleTimeout), BeNumerically("<", idleTimeout*6/5), )) Consistently(serverConnClosed).ShouldNot(BeClosed()) // make the go routine return (<-serverConnChan).CloseWithError(0, "") Eventually(serverConnClosed).Should(BeClosed()) }) It("times out after sending a packet", func() { server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() var drop atomic.Bool proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), DropPacket: func(dir quicproxy.Direction, _ []byte) bool { if dir == quicproxy.DirectionOutgoing { return drop.Load() } return false }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() serverConnChan := make(chan quic.Connection, 1) serverConnClosed := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) serverConnChan <- conn <-conn.Context().Done() // block until the connection is closed close(serverConnClosed) }() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIdleTimeout: idleTimeout, DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) // wait half the idle timeout, then send a packet time.Sleep(idleTimeout / 2) drop.Store(true) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) // now make sure that the idle timeout is based on this packet startTime := time.Now() done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := conn.AcceptStream(context.Background()) checkTimeoutError(err) close(done) }() Eventually(done, 2*idleTimeout).Should(BeClosed()) dur := time.Since(startTime) Expect(dur).To(And( BeNumerically(">=", idleTimeout), BeNumerically("<", idleTimeout*12/10), )) Consistently(serverConnClosed).ShouldNot(BeClosed()) // make the go routine return (<-serverConnChan).CloseWithError(0, "") Eventually(serverConnClosed).Should(BeClosed()) }) }) It("does not time out if keepalive is set", func() { const idleTimeout = 500 * time.Millisecond server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() serverConnChan := make(chan quic.Connection, 1) serverConnClosed := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) serverConnChan <- conn conn.AcceptStream(context.Background()) // blocks until the connection is closed close(serverConnClosed) }() var drop atomic.Bool proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), DropPacket: func(quicproxy.Direction, []byte) bool { return drop.Load() }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: idleTimeout, KeepAlivePeriod: idleTimeout / 2, DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) // wait longer than the idle timeout time.Sleep(3 * idleTimeout) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Consistently(serverConnClosed).ShouldNot(BeClosed()) // idle timeout will still kick in if pings are dropped drop.Store(true) time.Sleep(2 * idleTimeout) _, err = str.Write([]byte("foobar")) checkTimeoutError(err) (<-serverConnChan).CloseWithError(0, "") Eventually(serverConnClosed).Should(BeClosed()) }) Context("faulty packet conns", func() { const handshakeTimeout = time.Second / 2 runServer := func(ln *quic.Listener) error { conn, err := ln.Accept(context.Background()) if err != nil { return err } str, err := conn.OpenUniStream() if err != nil { return err } defer str.Close() _, err = str.Write(PRData) return err } runClient := func(conn quic.Connection) error { str, err := conn.AcceptUniStream(context.Background()) if err != nil { return err } data, err := io.ReadAll(str) if err != nil { return err } Expect(data).To(Equal(PRData)) return conn.CloseWithError(0, "done") } It("deals with an erroring packet conn, on the server side", func() { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) maxPackets := mrand.Int31n(25) fmt.Fprintf(GinkgoWriter, "blocking connection after %d packets\n", maxPackets) ln, err := quic.Listen( &faultyConn{PacketConn: conn, MaxPackets: maxPackets}, getTLSConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) serverErrChan := make(chan error, 1) go func() { defer GinkgoRecover() serverErrChan <- runServer(ln) }() clientErrChan := make(chan error, 1) go func() { defer GinkgoRecover() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{ HandshakeIdleTimeout: handshakeTimeout, MaxIdleTimeout: handshakeTimeout, DisablePathMTUDiscovery: true, }), ) if err != nil { clientErrChan <- err return } clientErrChan <- runClient(conn) }() var clientErr error Eventually(clientErrChan, 5*handshakeTimeout).Should(Receive(&clientErr)) Expect(clientErr).To(HaveOccurred()) nErr, ok := clientErr.(net.Error) Expect(ok).To(BeTrue()) Expect(nErr.Timeout()).To(BeTrue()) select { case serverErr := <-serverErrChan: Expect(serverErr).To(HaveOccurred()) Expect(serverErr.Error()).To(ContainSubstring(io.ErrClosedPipe.Error())) defer ln.Close() default: Expect(ln.Close()).To(Succeed()) Eventually(serverErrChan).Should(Receive()) } }) It("deals with an erroring packet conn, on the client side", func() { ln, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{ HandshakeIdleTimeout: handshakeTimeout, MaxIdleTimeout: handshakeTimeout, KeepAlivePeriod: handshakeTimeout / 2, DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() serverErrChan := make(chan error, 1) go func() { defer GinkgoRecover() serverErrChan <- runServer(ln) }() addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) maxPackets := mrand.Int31n(25) fmt.Fprintf(GinkgoWriter, "blocking connection after %d packets\n", maxPackets) clientErrChan := make(chan error, 1) go func() { defer GinkgoRecover() conn, err := quic.Dial( context.Background(), &faultyConn{PacketConn: conn, MaxPackets: maxPackets}, ln.Addr(), getTLSClientConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) if err != nil { clientErrChan <- err return } clientErrChan <- runClient(conn) }() var clientErr error Eventually(clientErrChan, 5*handshakeTimeout).Should(Receive(&clientErr)) Expect(clientErr).To(HaveOccurred()) Expect(clientErr.Error()).To(ContainSubstring(io.ErrClosedPipe.Error())) Eventually(areHandshakesRunning, 5*handshakeTimeout).Should(BeFalse()) select { case serverErr := <-serverErrChan: // The handshake completed on the server side. Expect(serverErr).To(HaveOccurred()) nErr, ok := serverErr.(net.Error) Expect(ok).To(BeTrue()) Expect(nErr.Timeout()).To(BeTrue()) default: // The handshake didn't complete Expect(ln.Close()).To(Succeed()) Eventually(serverErrChan).Should(Receive()) } }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/tracer_test.go000066400000000000000000000074271465664453100300260ustar00rootroot00000000000000package self_test import ( "bufio" "bytes" "context" "fmt" "io" mrand "math/rand" "net" "sync" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go/metrics" "github.com/quic-go/quic-go/qlog" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Tracer tests", func() { addTracers := func(pers protocol.Perspective, conf *quic.Config) *quic.Config { enableQlog := mrand.Int()%2 != 0 enableMetrcis := mrand.Int()%2 != 0 enableCustomTracer := mrand.Int()%2 != 0 fmt.Fprintf(GinkgoWriter, "%s using qlog: %t, metrics: %t, custom: %t\n", pers, enableQlog, enableMetrcis, enableCustomTracer) var tracerConstructors []func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer if enableQlog { tracerConstructors = append(tracerConstructors, func(_ context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { if mrand.Int()%2 == 0 { // simulate that a qlog collector might only want to log some connections fmt.Fprintf(GinkgoWriter, "%s qlog tracer deciding to not trace connection %s\n", p, connID) return nil } fmt.Fprintf(GinkgoWriter, "%s qlog tracing connection %s\n", p, connID) return qlog.NewConnectionTracer(utils.NewBufferedWriteCloser(bufio.NewWriter(&bytes.Buffer{}), io.NopCloser(nil)), p, connID) }) } if enableMetrcis { tracerConstructors = append(tracerConstructors, metrics.DefaultConnectionTracer) } if enableCustomTracer { tracerConstructors = append(tracerConstructors, func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return &logging.ConnectionTracer{} }) } c := conf.Clone() c.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { tracers := make([]*logging.ConnectionTracer, 0, len(tracerConstructors)) for _, c := range tracerConstructors { if tr := c(ctx, p, connID); tr != nil { tracers = append(tracers, tr) } } return logging.NewMultiplexedConnectionTracer(tracers...) } return c } for i := 0; i < 3; i++ { It("handshakes with a random combination of tracers", func() { if enableQlog { Skip("This test sets tracers and won't produce any qlogs.") } quicClientConf := addTracers(protocol.PerspectiveClient, getQuicConfig(nil)) quicServerConf := addTracers(protocol.PerspectiveServer, getQuicConfig(nil)) serverChan := make(chan *quic.Listener) serverDone := make(chan struct{}) go func() { defer GinkgoRecover() defer close(serverDone) ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), quicServerConf) Expect(err).ToNot(HaveOccurred()) serverChan <- ln for { conn, err := ln.Accept(context.Background()) if err != nil { return } str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) } }() ln := <-serverChan var wg sync.WaitGroup wg.Add(3) for i := 0; i < 3; i++ { go func() { defer wg.Done() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), quicClientConf, ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) }() } wg.Wait() ln.Close() Eventually(serverDone).Should(BeClosed()) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/uni_stream_test.go000066400000000000000000000064551465664453100307140ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "sync" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Unidirectional Streams", func() { const numStreams = 500 var ( server *quic.Listener serverAddr string ) BeforeEach(func() { var err error server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) serverAddr = fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) }) AfterEach(func() { server.Close() }) dataForStream := func(id protocol.StreamID) []byte { return GeneratePRData(10 * int(id)) } runSendingPeer := func(conn quic.Connection) { for i := 0; i < numStreams; i++ { str, err := conn.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() _, err := str.Write(dataForStream(str.StreamID())) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } } runReceivingPeer := func(conn quic.Connection) { var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() defer wg.Done() data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(dataForStream(str.StreamID()))) }() } wg.Wait() } It(fmt.Sprintf("client opening %d streams to a server", numStreams), func() { go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(conn) conn.CloseWithError(0, "") }() client, err := quic.DialAddr( context.Background(), serverAddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runSendingPeer(client) <-client.Context().Done() }) It(fmt.Sprintf("server opening %d streams to a client", numStreams), func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runSendingPeer(conn) <-conn.Context().Done() }() client, err := quic.DialAddr( context.Background(), serverAddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(client) client.CloseWithError(0, "") }) It(fmt.Sprintf("client and server opening %d streams each and sending data to the peer", numStreams), func() { done1 := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() runReceivingPeer(conn) close(done) }() runSendingPeer(conn) <-done close(done1) }() client, err := quic.DialAddr( context.Background(), serverAddr, getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) done2 := make(chan struct{}) go func() { defer GinkgoRecover() runSendingPeer(client) close(done2) }() runReceivingPeer(client) <-done1 <-done2 client.CloseWithError(0, "") }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/self/zero_rtt_test.go000066400000000000000000001057261465664453100304170ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "io" "net" "sync" "sync/atomic" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type metadataClientSessionCache struct { toAdd []byte restored func([]byte) cache tls.ClientSessionCache } func (m metadataClientSessionCache) Get(key string) (*tls.ClientSessionState, bool) { session, ok := m.cache.Get(key) if !ok || session == nil { return session, ok } ticket, state, err := session.ResumptionState() Expect(err).ToNot(HaveOccurred()) Expect(state.Extra).To(HaveLen(2)) // ours, and the quic-go's m.restored(state.Extra[1]) session, err = tls.NewResumptionState(ticket, state) Expect(err).ToNot(HaveOccurred()) return session, true } func (m metadataClientSessionCache) Put(key string, session *tls.ClientSessionState) { ticket, state, err := session.ResumptionState() Expect(err).ToNot(HaveOccurred()) state.Extra = append(state.Extra, m.toAdd) session, err = tls.NewResumptionState(ticket, state) Expect(err).ToNot(HaveOccurred()) m.cache.Put(key, session) } // contains0RTTPacket says if a packet contains a 0-RTT long header packet. // It correctly handles coalesced packets. func contains0RTTPacket(data []byte) bool { for len(data) > 0 { if !wire.IsLongHeaderPacket(data[0]) { return false } hdr, _, rest, err := wire.ParsePacket(data) Expect(err).ToNot(HaveOccurred()) if hdr.Type == protocol.PacketType0RTT { return true } data = rest } return false } var _ = Describe("0-RTT", func() { rtt := scaleDuration(5 * time.Millisecond) runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *atomic.Uint32) { var num0RTTPackets atomic.Uint32 proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { if contains0RTTPacket(data) { num0RTTPackets.Add(1) } return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) return proxy, &num0RTTPackets } dialAndReceiveSessionTicket := func(serverTLSConf *tls.Config, serverConf *quic.Config, clientTLSConf *tls.Config) { if serverConf == nil { serverConf = getQuicConfig(nil) } serverConf.Allow0RTT = true ln, err := quic.ListenAddrEarly( "localhost:0", serverTLSConf, serverConf, ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() // dial the first connection in order to receive a session ticket done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) <-conn.Context().Done() }() puts := make(chan string, 100) cache := clientTLSConf.ClientSessionCache if cache == nil { cache = tls.NewLRUClientSessionCache(100) } clientTLSConf.ClientSessionCache = newClientSessionCache(cache, make(chan string, 100), puts) conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientTLSConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) Eventually(puts).Should(Receive()) // received the session ticket. We're done here. Expect(conn.CloseWithError(0, "")).To(Succeed()) Eventually(done).Should(BeClosed()) } transfer0RTTData := func( ln *quic.EarlyListener, proxyPort int, connIDLen int, clientTLSConf *tls.Config, clientConf *quic.Config, testdata []byte, // data to transfer ) { // accept the second connection, and receive the data sent in 0-RTT done := make(chan struct{}) go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(testdata)) Expect(str.Close()).To(Succeed()) Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) <-conn.Context().Done() close(done) }() if clientConf == nil { clientConf = getQuicConfig(nil) } var conn quic.EarlyConnection if connIDLen == 0 { var err error conn, err = quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxyPort), clientTLSConf, clientConf, ) Expect(err).ToNot(HaveOccurred()) } else { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) defer udpConn.Close() tr := &quic.Transport{ Conn: udpConn, ConnectionIDLength: connIDLen, } addTracer(tr) defer tr.Close() conn, err = tr.DialEarly( context.Background(), &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: proxyPort}, clientTLSConf, clientConf, ) Expect(err).ToNot(HaveOccurred()) } defer conn.CloseWithError(0, "") str, err := conn.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(testdata) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) <-conn.HandshakeComplete() Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) io.ReadAll(str) // wait for the EOF from the server to arrive before closing the conn conn.CloseWithError(0, "") Eventually(done).Should(BeClosed()) Eventually(conn.Context().Done()).Should(BeClosed()) } check0RTTRejected := func( ln *quic.EarlyListener, proxyPort int, clientConf *tls.Config, ) { conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxyPort), clientConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(make([]byte, 3000)) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) // make sure the server doesn't process the data ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(50*time.Millisecond)) defer cancel() serverConn, err := ln.Accept(ctx) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse()) _, err = serverConn.AcceptUniStream(ctx) Expect(err).To(Equal(context.DeadlineExceeded)) Expect(serverConn.CloseWithError(0, "")).To(Succeed()) Eventually(conn.Context().Done()).Should(BeClosed()) } // can be used to extract 0-RTT from a packetCounter get0RTTPackets := func(packets []packet) []protocol.PacketNumber { var zeroRTTPackets []protocol.PacketNumber for _, p := range packets { if p.hdr.Type == protocol.PacketType0RTT { zeroRTTPackets = append(zeroRTTPackets, p.hdr.PacketNumber) } } return zeroRTTPackets } for _, l := range []int{0, 15} { connIDLen := l It(fmt.Sprintf("transfers 0-RTT data, with %d byte connection IDs", connIDLen), func() { tlsConf := getTLSConfig() clientTLSConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf) counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() transfer0RTTData( ln, proxy.LocalPort(), connIDLen, clientTLSConf, getQuicConfig(nil), PRData, ) var numNewConnIDs int for _, p := range counter.getRcvdLongHeaderPackets() { for _, f := range p.frames { if _, ok := f.(*logging.NewConnectionIDFrame); ok { numNewConnIDs++ } } } if connIDLen == 0 { Expect(numNewConnIDs).To(BeZero()) } else { Expect(numNewConnIDs).ToNot(BeZero()) } num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10)) Expect(zeroRTTPackets).To(ContainElement(protocol.PacketNumber(0))) }) } // Test that data intended to be sent with 1-RTT protection is not sent in 0-RTT packets. It("waits for a connection until the handshake is done", func() { tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) zeroRTTData := GeneratePRData(5 << 10) oneRTTData := PRData counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() // now accept the second connection, and receive the 0-RTT data go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(zeroRTTData)) str, err = conn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err = io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(oneRTTData)) Expect(conn.CloseWithError(0, "")).To(Succeed()) }() proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) firstStr, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = firstStr.Write(zeroRTTData) Expect(err).ToNot(HaveOccurred()) Expect(firstStr.Close()).To(Succeed()) // wait for the handshake to complete Eventually(conn.HandshakeComplete()).Should(BeClosed()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) <-conn.Context().Done() // check that 0-RTT packets only contain STREAM frames for the first stream var num0RTT int for _, p := range counter.getRcvdLongHeaderPackets() { if p.hdr.Header.Type != protocol.PacketType0RTT { continue } for _, f := range p.frames { sf, ok := f.(*logging.StreamFrame) if !ok { continue } num0RTT++ Expect(sf.StreamID).To(Equal(firstStr.StreamID())) } } fmt.Fprintf(GinkgoWriter, "received %d STREAM frames in 0-RTT packets\n", num0RTT) Expect(num0RTT).ToNot(BeZero()) }) It("transfers 0-RTT data, when 0-RTT packets are lost", func() { var num0RTTPackets, numDropped atomic.Uint32 tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { return rtt / 2 }, DropPacket: func(_ quicproxy.Direction, data []byte) bool { if !wire.IsLongHeaderPacket(data[0]) { return false } hdr, _, _, err := wire.ParsePacket(data) Expect(err).ToNot(HaveOccurred()) if hdr.Type == protocol.PacketType0RTT { count := num0RTTPackets.Add(1) // drop 25% of the 0-RTT packets drop := count%4 == 0 if drop { numDropped.Add(1) } return drop } return false }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData) num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped.Load()) Expect(numDropped.Load()).ToNot(BeZero()) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).ToNot(BeEmpty()) }) It("retransmits all 0-RTT data when the server performs a Retry", func() { var mutex sync.Mutex var firstConnID, secondConnID *protocol.ConnectionID var firstCounter, secondCounter protocol.ByteCount tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) countZeroRTTBytes := func(data []byte) (n protocol.ByteCount) { for len(data) > 0 { hdr, _, rest, err := wire.ParsePacket(data) if err != nil { return } data = rest if hdr.Type == protocol.PacketType0RTT { n += hdr.Length - 16 /* AEAD tag */ } } return } counter, tracer := newPacketTracer() laddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", laddr) Expect(err).ToNot(HaveOccurred()) defer udpConn.Close() tr := &quic.Transport{ Conn: udpConn, VerifySourceAddress: func(net.Addr) bool { return true }, } addTracer(tr) defer tr.Close() ln, err := tr.ListenEarly( tlsConf, getQuicConfig(&quic.Config{Allow0RTT: true, Tracer: newTracer(tracer)}), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration { connID, err := wire.ParseConnectionID(data, 0) Expect(err).ToNot(HaveOccurred()) mutex.Lock() defer mutex.Unlock() if zeroRTTBytes := countZeroRTTBytes(data); zeroRTTBytes > 0 { if firstConnID == nil { firstConnID = &connID firstCounter += zeroRTTBytes } else if firstConnID != nil && *firstConnID == connID { Expect(secondConnID).To(BeNil()) firstCounter += zeroRTTBytes } else if secondConnID == nil { secondConnID = &connID secondCounter += zeroRTTBytes } else if secondConnID != nil && *secondConnID == connID { secondCounter += zeroRTTBytes } else { Fail("received 3 connection IDs on 0-RTT packets") } } return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, GeneratePRData(5000)) // ~5 packets mutex.Lock() defer mutex.Unlock() Expect(firstCounter).To(BeNumerically("~", 5000+100 /* framing overhead */, 100)) // the FIN bit might be sent extra Expect(secondCounter).To(BeNumerically("~", firstCounter, 20)) zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) Expect(len(zeroRTTPackets)).To(BeNumerically(">=", 5)) Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5))) }) It("doesn't use 0-RTT when Dial is used for the resumed connection", func() { tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, getQuicConfig(nil), clientConf) ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{Allow0RTT: true}), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn.CloseWithError(0, "") Expect(conn.ConnectionState().TLS.DidResume).To(BeTrue()) Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) Expect(num0RTTPackets.Load()).To(BeZero()) serverConn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue()) Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse()) }) It("doesn't reject 0-RTT when the server's transport stream limit increased", func() { const maxStreams = 1 tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ MaxIncomingUniStreams: maxStreams, }), clientConf) ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ MaxIncomingUniStreams: maxStreams + 1, Allow0RTT: true, }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) // The client remembers the old limit and refuses to open a new stream. _, err = conn.OpenUniStream() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("too many open streams")) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() _, err = conn.OpenUniStreamSync(ctx) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) Expect(conn.CloseWithError(0, "")).To(Succeed()) }) It("rejects 0-RTT when the server's stream limit decreased", func() { const maxStreams = 42 tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ MaxIncomingStreams: maxStreams, }), clientConf) counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ MaxIncomingStreams: maxStreams - 1, Allow0RTT: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() check0RTTRejected(ln, proxy.LocalPort(), clientConf) // The client should send 0-RTT packets, but the server doesn't process them. num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) }) It("rejects 0-RTT when the ALPN changed", func() { tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) // switch to different ALPN on the server side tlsConf.NextProtos = []string{"new-alpn"} // Append to the client's ALPN. // crypto/tls will attempt to resume with the ALPN from the original connection clientConf.NextProtos = append(clientConf.NextProtos, "new-alpn") counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() check0RTTRejected(ln, proxy.LocalPort(), clientConf) // The client should send 0-RTT packets, but the server doesn't process them. num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) }) It("rejects 0-RTT when the application doesn't allow it", func() { tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) // now close the listener and dial new connection with a different ALPN counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: false, // application rejects 0-RTT Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() check0RTTRejected(ln, proxy.LocalPort(), clientConf) // The client should send 0-RTT packets, but the server doesn't process them. num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) }) It("doesn't use 0-RTT, if the server didn't enable it", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) defer server.Close() gets := make(chan string, 100) puts := make(chan string, 100) cache := newClientSessionCache(tls.NewLRUClientSessionCache(10), gets, puts) tlsConf := getTLSClientConfig() tlsConf.ClientSessionCache = cache conn1, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) defer conn1.CloseWithError(0, "") var sessionKey string Eventually(puts).Should(Receive(&sessionKey)) Expect(conn1.ConnectionState().TLS.DidResume).To(BeFalse()) serverConn, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeFalse()) conn2, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) Expect(gets).To(Receive(Equal(sessionKey))) Expect(conn2.ConnectionState().TLS.DidResume).To(BeTrue()) serverConn, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverConn.ConnectionState().TLS.DidResume).To(BeTrue()) Expect(serverConn.ConnectionState().Used0RTT).To(BeFalse()) conn2.CloseWithError(0, "") }) DescribeTable("flow control limits", func(addFlowControlLimit func(*quic.Config, uint64)) { counter, tracer := newPacketTracer() firstConf := getQuicConfig(&quic.Config{Allow0RTT: true}) addFlowControlLimit(firstConf, 3) tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, firstConf, clientConf) secondConf := getQuicConfig(&quic.Config{ Allow0RTT: true, Tracer: newTracer(tracer), }) addFlowControlLimit(secondConf, 100) ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, secondConf, ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) str, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) written := make(chan struct{}) go func() { defer GinkgoRecover() defer close(written) _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() Eventually(written).Should(BeClosed()) serverConn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) rstr, err := serverConn.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(rstr) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) Expect(serverConn.ConnectionState().Used0RTT).To(BeTrue()) Expect(serverConn.CloseWithError(0, "")).To(Succeed()) Eventually(conn.Context().Done()).Should(BeClosed()) var processedFirst bool for _, p := range counter.getRcvdLongHeaderPackets() { for _, f := range p.frames { if sf, ok := f.(*logging.StreamFrame); ok { if !processedFirst { // The first STREAM should have been sent in a 0-RTT packet. // Due to the flow control limit, the STREAM frame was limit to the first 3 bytes. Expect(p.hdr.Type).To(Equal(protocol.PacketType0RTT)) Expect(sf.Length).To(BeEquivalentTo(3)) processedFirst = true } else { Fail("STREAM was shouldn't have been sent in 0-RTT") } } } } }, Entry("doesn't reject 0-RTT when the server's transport stream flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialStreamReceiveWindow = limit }), Entry("doesn't reject 0-RTT when the server's transport connection flow control limit increased", func(c *quic.Config, limit uint64) { c.InitialConnectionReceiveWindow = limit }), ) test0RTTRejection := func(tlsConf *tls.Config) { clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) // now dial new connection with different transport parameters counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ MaxIncomingUniStreams: 1, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(&quic.Config{}), ) Expect(err).ToNot(HaveOccurred()) // The client remembers that it was allowed to open 2 uni-directional streams. firstStr, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) written := make(chan struct{}, 2) go func() { defer GinkgoRecover() defer func() { written <- struct{}{} }() _, err := firstStr.Write([]byte("first flight")) Expect(err).ToNot(HaveOccurred()) }() secondStr, err := conn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() defer func() { written <- struct{}{} }() _, err := secondStr.Write([]byte("first flight")) Expect(err).ToNot(HaveOccurred()) }() ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() _, err = conn.AcceptStream(ctx) Expect(err).To(MatchError(quic.Err0RTTRejected)) Eventually(written).Should(Receive()) Eventually(written).Should(Receive()) _, err = firstStr.Write([]byte("foobar")) Expect(err).To(MatchError(quic.Err0RTTRejected)) _, err = conn.OpenUniStream() Expect(err).To(MatchError(quic.Err0RTTRejected)) _, err = conn.AcceptStream(ctx) Expect(err).To(Equal(quic.Err0RTTRejected)) newConn, err := conn.NextConnection(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := newConn.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = newConn.OpenUniStream() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("too many open streams")) _, err = str.Write([]byte("second flight")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) Expect(conn.CloseWithError(0, "")).To(Succeed()) // The client should send 0-RTT packets, but the server doesn't process them. num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) } It("correctly deals with 0-RTT rejections", func() { test0RTTRejection(getTLSConfig()) }) It("correctly deals with 0-RTT rejections, when the server uses GetConfigForClient", func() { tlsConf := getTLSConfig() test0RTTRejection(&tls.Config{ GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { return tlsConf, nil }, }) }) It("queues 0-RTT packets, if the Initial is delayed", func() { tlsConf := getTLSConfig() clientConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientConf) counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: ln.Addr().String(), DelayPacket: func(dir quicproxy.Direction, data []byte) time.Duration { if dir == quicproxy.DirectionIncoming && wire.IsLongHeaderPacket(data[0]) && data[0]&0x30>>4 == 0 { // Initial packet from client return rtt/2 + rtt } return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() transfer0RTTData(ln, proxy.LocalPort(), protocol.DefaultConnectionIDLength, clientConf, nil, PRData) Expect(counter.getRcvdLongHeaderPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial)) zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10)) Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0))) }) It("allows the application to attach data to the session ticket, for the server", func() { tlsConf := getTLSConfig() tlsConf.WrapSession = func(cs tls.ConnectionState, ss *tls.SessionState) ([]byte, error) { ss.Extra = append(ss.Extra, []byte("foobar")) return tlsConf.EncryptTicket(cs, ss) } var unwrapped bool tlsConf.UnwrapSession = func(identity []byte, cs tls.ConnectionState) (*tls.SessionState, error) { defer GinkgoRecover() state, err := tlsConf.DecryptTicket(identity, cs) if err != nil { return nil, err } Expect(state.Extra).To(HaveLen(2)) Expect(state.Extra[1]).To(Equal([]byte("foobar"))) unwrapped = true return state, nil } clientTLSConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf) ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{Allow0RTT: true}), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() transfer0RTTData( ln, ln.Addr().(*net.UDPAddr).Port, 10, clientTLSConf, getQuicConfig(nil), PRData, ) Expect(unwrapped).To(BeTrue()) }) It("allows the application to attach data to the session ticket, for the client", func() { tlsConf := getTLSConfig() clientTLSConf := getTLSClientConfig() var restored bool clientTLSConf.ClientSessionCache = &metadataClientSessionCache{ toAdd: []byte("foobar"), restored: func(b []byte) { defer GinkgoRecover() Expect(b).To(Equal([]byte("foobar"))) restored = true }, cache: tls.NewLRUClientSessionCache(100), } dialAndReceiveSessionTicket(tlsConf, nil, clientTLSConf) ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{Allow0RTT: true}), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() transfer0RTTData( ln, ln.Addr().(*net.UDPAddr).Port, 10, clientTLSConf, getQuicConfig(nil), PRData, ) Expect(restored).To(BeTrue()) }) It("sends 0-RTT datagrams", func() { tlsConf := getTLSConfig() clientTLSConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ EnableDatagrams: true, }), clientTLSConf) counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, EnableDatagrams: true, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() // second connection sentMessage := GeneratePRData(100) var receivedMessage []byte received := make(chan struct{}) go func() { defer GinkgoRecover() defer close(received) conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) receivedMessage, err = conn.ReceiveDatagram(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) }() conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientTLSConf, getQuicConfig(&quic.Config{ EnableDatagrams: true, }), ) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue()) Expect(conn.SendDatagram(sentMessage)).To(Succeed()) <-conn.HandshakeComplete() <-received Expect(conn.ConnectionState().Used0RTT).To(BeTrue()) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(receivedMessage).To(Equal(sentMessage)) num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) zeroRTTPackets := get0RTTPackets(counter.getRcvdLongHeaderPackets()) Expect(zeroRTTPackets).To(HaveLen(1)) }) It("rejects 0-RTT datagrams when the server doesn't support datagrams anymore", func() { tlsConf := getTLSConfig() clientTLSConf := getTLSClientConfig() dialAndReceiveSessionTicket(tlsConf, getQuicConfig(&quic.Config{ EnableDatagrams: true, }), clientTLSConf) counter, tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Allow0RTT: true, EnableDatagrams: false, Tracer: newTracer(tracer), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() // second connection go func() { defer GinkgoRecover() conn, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) _, err = conn.ReceiveDatagram(context.Background()) Expect(err.Error()).To(Equal("datagram support disabled")) <-conn.HandshakeComplete() Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) }() conn, err := quic.DialAddrEarly( context.Background(), fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientTLSConf, getQuicConfig(&quic.Config{ EnableDatagrams: true, }), ) Expect(err).ToNot(HaveOccurred()) // the client can temporarily send datagrams but the server doesn't process them. Expect(conn.ConnectionState().SupportsDatagrams).To(BeTrue()) Expect(conn.SendDatagram(make([]byte, 100))).To(Succeed()) <-conn.HandshakeComplete() Expect(conn.ConnectionState().SupportsDatagrams).To(BeFalse()) Expect(conn.ConnectionState().Used0RTT).To(BeFalse()) Expect(conn.CloseWithError(0, "")).To(Succeed()) num0RTT := num0RTTPackets.Load() fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(counter.getRcvdLongHeaderPackets())).To(BeEmpty()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/000077500000000000000000000000001465664453100253555ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/crypto.go000066400000000000000000000067621465664453100272370ustar00rootroot00000000000000package tools import ( "crypto" "crypto/ed25519" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "math/big" "net" "time" ) const ALPN = "quic-go integration tests" func GenerateCA() (*x509.Certificate, crypto.PrivateKey, error) { certTempl := &x509.Certificate{ SerialNumber: big.NewInt(2019), Subject: pkix.Name{}, NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, } pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, nil, err } caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, certTempl, pub, priv) if err != nil { return nil, nil, err } ca, err := x509.ParseCertificate(caBytes) if err != nil { return nil, nil, err } return ca, priv, nil } func GenerateLeafCert(ca *x509.Certificate, caPriv crypto.PrivateKey) (*x509.Certificate, crypto.PrivateKey, error) { certTempl := &x509.Certificate{ SerialNumber: big.NewInt(1), DNSNames: []string{"localhost"}, IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1)}, NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature, } pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { return nil, nil, err } certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, ca, pub, caPriv) if err != nil { return nil, nil, err } cert, err := x509.ParseCertificate(certBytes) if err != nil { return nil, nil, err } return cert, priv, nil } // GenerateTLSConfigWithLongCertChain generates a tls.Config that uses a long certificate chain. // The Root CA used is the same as for the config returned from getTLSConfig(). func GenerateTLSConfigWithLongCertChain(ca *x509.Certificate, caPrivateKey crypto.PrivateKey) (*tls.Config, error) { const chainLen = 7 certTempl := &x509.Certificate{ SerialNumber: big.NewInt(2019), Subject: pkix.Name{}, NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), IsCA: true, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, BasicConstraintsValid: true, } lastCA := ca lastCAPrivKey := caPrivateKey privKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, err } certs := make([]*x509.Certificate, chainLen) for i := 0; i < chainLen; i++ { caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, lastCA, &privKey.PublicKey, lastCAPrivKey) if err != nil { return nil, err } ca, err := x509.ParseCertificate(caBytes) if err != nil { return nil, err } certs[i] = ca lastCA = ca lastCAPrivKey = privKey } leafCert, leafPrivateKey, err := GenerateLeafCert(lastCA, lastCAPrivKey) if err != nil { return nil, err } rawCerts := make([][]byte, chainLen+1) for i, cert := range certs { rawCerts[chainLen-i] = cert.Raw } rawCerts[0] = leafCert.Raw return &tls.Config{ Certificates: []tls.Certificate{{ Certificate: rawCerts, PrivateKey: leafPrivateKey, }}, NextProtos: []string{ALPN}, }, nil } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/israce/000077500000000000000000000000001465664453100266235ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/israce/norace.go000066400000000000000000000001541465664453100304210ustar00rootroot00000000000000//go:build !race package israce // Enabled reports if the race detector is enabled. const Enabled = false golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/israce/race.go000066400000000000000000000001521465664453100300620ustar00rootroot00000000000000//go:build race package israce // Enabled reports if the race detector is enabled. const Enabled = true golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/proxy/000077500000000000000000000000001465664453100265365ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/proxy/proxy.go000066400000000000000000000214631465664453100302540ustar00rootroot00000000000000package quicproxy import ( "net" "sort" "sync" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) // Connection is a UDP connection type connection struct { ClientAddr *net.UDPAddr // Address of the client ServerConn *net.UDPConn // UDP connection to server incomingPackets chan packetEntry Incoming *queue Outgoing *queue } func (c *connection) queuePacket(t time.Time, b []byte) { c.incomingPackets <- packetEntry{Time: t, Raw: b} } // Direction is the direction a packet is sent. type Direction int const ( // DirectionIncoming is the direction from the client to the server. DirectionIncoming Direction = iota // DirectionOutgoing is the direction from the server to the client. DirectionOutgoing // DirectionBoth is both incoming and outgoing DirectionBoth ) type packetEntry struct { Time time.Time Raw []byte } type packetEntries []packetEntry func (e packetEntries) Len() int { return len(e) } func (e packetEntries) Less(i, j int) bool { return e[i].Time.Before(e[j].Time) } func (e packetEntries) Swap(i, j int) { e[i], e[j] = e[j], e[i] } type queue struct { sync.Mutex timer *utils.Timer Packets packetEntries } func newQueue() *queue { return &queue{timer: utils.NewTimer()} } func (q *queue) Add(e packetEntry) { q.Lock() q.Packets = append(q.Packets, e) if len(q.Packets) > 1 { lastIndex := len(q.Packets) - 1 if q.Packets[lastIndex].Time.Before(q.Packets[lastIndex-1].Time) { sort.Stable(q.Packets) } } q.timer.Reset(q.Packets[0].Time) q.Unlock() } func (q *queue) Get() []byte { q.Lock() raw := q.Packets[0].Raw q.Packets = q.Packets[1:] if len(q.Packets) > 0 { q.timer.Reset(q.Packets[0].Time) } q.Unlock() return raw } func (q *queue) Timer() <-chan time.Time { return q.timer.Chan() } func (q *queue) SetTimerRead() { q.timer.SetRead() } func (q *queue) Close() { q.timer.Stop() } func (d Direction) String() string { switch d { case DirectionIncoming: return "Incoming" case DirectionOutgoing: return "Outgoing" case DirectionBoth: return "both" default: panic("unknown direction") } } // Is says if one direction matches another direction. // For example, incoming matches both incoming and both, but not outgoing. func (d Direction) Is(dir Direction) bool { if d == DirectionBoth || dir == DirectionBoth { return true } return d == dir } // DropCallback is a callback that determines which packet gets dropped. type DropCallback func(dir Direction, packet []byte) bool // NoDropper doesn't drop packets. var NoDropper DropCallback = func(Direction, []byte) bool { return false } // DelayCallback is a callback that determines how much delay to apply to a packet. type DelayCallback func(dir Direction, packet []byte) time.Duration // NoDelay doesn't apply a delay. var NoDelay DelayCallback = func(Direction, []byte) time.Duration { return 0 } // Opts are proxy options. type Opts struct { // The address this proxy proxies packets to. RemoteAddr string // DropPacket determines whether a packet gets dropped. DropPacket DropCallback // DelayPacket determines how long a packet gets delayed. This allows // simulating a connection with non-zero RTTs. // Note that the RTT is the sum of the delay for the incoming and the outgoing packet. DelayPacket DelayCallback } // QuicProxy is a QUIC proxy that can drop and delay packets. type QuicProxy struct { mutex sync.Mutex closeChan chan struct{} conn *net.UDPConn serverAddr *net.UDPAddr dropPacket DropCallback delayPacket DelayCallback // Mapping from client addresses (as host:port) to connection clientDict map[string]*connection logger utils.Logger } // NewQuicProxy creates a new UDP proxy func NewQuicProxy(local string, opts *Opts) (*QuicProxy, error) { if opts == nil { opts = &Opts{} } laddr, err := net.ResolveUDPAddr("udp", local) if err != nil { return nil, err } conn, err := net.ListenUDP("udp", laddr) if err != nil { return nil, err } if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { return nil, err } if err := conn.SetWriteBuffer(protocol.DesiredSendBufferSize); err != nil { return nil, err } raddr, err := net.ResolveUDPAddr("udp", opts.RemoteAddr) if err != nil { return nil, err } packetDropper := NoDropper if opts.DropPacket != nil { packetDropper = opts.DropPacket } packetDelayer := NoDelay if opts.DelayPacket != nil { packetDelayer = opts.DelayPacket } p := QuicProxy{ clientDict: make(map[string]*connection), conn: conn, closeChan: make(chan struct{}), serverAddr: raddr, dropPacket: packetDropper, delayPacket: packetDelayer, logger: utils.DefaultLogger.WithPrefix("proxy"), } p.logger.Debugf("Starting UDP Proxy %s <-> %s", conn.LocalAddr(), raddr) go p.runProxy() return &p, nil } // Close stops the UDP Proxy func (p *QuicProxy) Close() error { p.mutex.Lock() defer p.mutex.Unlock() close(p.closeChan) for _, c := range p.clientDict { if err := c.ServerConn.Close(); err != nil { return err } c.Incoming.Close() c.Outgoing.Close() } return p.conn.Close() } // LocalAddr is the address the proxy is listening on. func (p *QuicProxy) LocalAddr() net.Addr { return p.conn.LocalAddr() } // LocalPort is the UDP port number the proxy is listening on. func (p *QuicProxy) LocalPort() int { return p.conn.LocalAddr().(*net.UDPAddr).Port } func (p *QuicProxy) newConnection(cliAddr *net.UDPAddr) (*connection, error) { conn, err := net.DialUDP("udp", nil, p.serverAddr) if err != nil { return nil, err } if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { return nil, err } if err := conn.SetWriteBuffer(protocol.DesiredSendBufferSize); err != nil { return nil, err } return &connection{ ClientAddr: cliAddr, ServerConn: conn, incomingPackets: make(chan packetEntry, 10), Incoming: newQueue(), Outgoing: newQueue(), }, nil } // runProxy listens on the proxy address and handles incoming packets. func (p *QuicProxy) runProxy() error { for { buffer := make([]byte, protocol.MaxPacketBufferSize) n, cliaddr, err := p.conn.ReadFromUDP(buffer) if err != nil { return err } raw := buffer[0:n] saddr := cliaddr.String() p.mutex.Lock() conn, ok := p.clientDict[saddr] if !ok { conn, err = p.newConnection(cliaddr) if err != nil { p.mutex.Unlock() return err } p.clientDict[saddr] = conn go p.runIncomingConnection(conn) go p.runOutgoingConnection(conn) } p.mutex.Unlock() if p.dropPacket(DirectionIncoming, raw) { if p.logger.Debug() { p.logger.Debugf("dropping incoming packet(%d bytes)", n) } continue } delay := p.delayPacket(DirectionIncoming, raw) if delay == 0 { if p.logger.Debug() { p.logger.Debugf("forwarding incoming packet (%d bytes) to %s", len(raw), conn.ServerConn.RemoteAddr()) } if _, err := conn.ServerConn.Write(raw); err != nil { return err } } else { now := time.Now() if p.logger.Debug() { p.logger.Debugf("delaying incoming packet (%d bytes) to %s by %s", len(raw), conn.ServerConn.RemoteAddr(), delay) } conn.queuePacket(now.Add(delay), raw) } } } // runConnection handles packets from server to a single client func (p *QuicProxy) runOutgoingConnection(conn *connection) error { outgoingPackets := make(chan packetEntry, 10) go func() { for { buffer := make([]byte, protocol.MaxPacketBufferSize) n, err := conn.ServerConn.Read(buffer) if err != nil { return } raw := buffer[0:n] if p.dropPacket(DirectionOutgoing, raw) { if p.logger.Debug() { p.logger.Debugf("dropping outgoing packet(%d bytes)", n) } continue } delay := p.delayPacket(DirectionOutgoing, raw) if delay == 0 { if p.logger.Debug() { p.logger.Debugf("forwarding outgoing packet (%d bytes) to %s", len(raw), conn.ClientAddr) } if _, err := p.conn.WriteToUDP(raw, conn.ClientAddr); err != nil { return } } else { now := time.Now() if p.logger.Debug() { p.logger.Debugf("delaying outgoing packet (%d bytes) to %s by %s", len(raw), conn.ClientAddr, delay) } outgoingPackets <- packetEntry{Time: now.Add(delay), Raw: raw} } } }() for { select { case <-p.closeChan: return nil case e := <-outgoingPackets: conn.Outgoing.Add(e) case <-conn.Outgoing.Timer(): conn.Outgoing.SetTimerRead() if _, err := p.conn.WriteTo(conn.Outgoing.Get(), conn.ClientAddr); err != nil { return err } } } } func (p *QuicProxy) runIncomingConnection(conn *connection) error { for { select { case <-p.closeChan: return nil case e := <-conn.incomingPackets: // Send the packet to the server conn.Incoming.Add(e) case <-conn.Incoming.Timer(): conn.Incoming.SetTimerRead() if _, err := conn.ServerConn.Write(conn.Incoming.Get()); err != nil { return err } } } } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/proxy/proxy_suite_test.go000066400000000000000000000002761465664453100325230ustar00rootroot00000000000000package quicproxy import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestQuicGo(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "QUIC Proxy") } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/proxy/proxy_test.go000066400000000000000000000356251465664453100313200ustar00rootroot00000000000000package quicproxy import ( "bytes" "fmt" "net" "runtime" "runtime/pprof" "strconv" "strings" "sync/atomic" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type packetData []byte func isProxyRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "proxy.(*QuicProxy).runIncomingConnection") || strings.Contains(b.String(), "proxy.(*QuicProxy).runOutgoingConnection") } var _ = Describe("QUIC Proxy", func() { makePacket := func(p protocol.PacketNumber, payload []byte) []byte { hdr := wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, Version: protocol.Version1, Length: 4 + protocol.ByteCount(len(payload)), DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0, 0, 0x13, 0x37}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0, 0, 0x13, 0x37}), }, PacketNumber: p, PacketNumberLen: protocol.PacketNumberLen4, } b, err := hdr.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) b = append(b, payload...) return b } readPacketNumber := func(b []byte) protocol.PacketNumber { hdr, data, _, err := wire.ParsePacket(b) ExpectWithOffset(1, err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial)) extHdr, err := hdr.ParseExtended(data) ExpectWithOffset(1, err).ToNot(HaveOccurred()) return extHdr.PacketNumber } AfterEach(func() { Eventually(isProxyRunning).Should(BeFalse()) }) Context("Proxy setup and teardown", func() { It("sets up the UDPProxy", func() { proxy, err := NewQuicProxy("localhost:0", nil) Expect(err).ToNot(HaveOccurred()) Expect(proxy.clientDict).To(HaveLen(0)) // check that the proxy port is in use addr, err := net.ResolveUDPAddr("udp", "localhost:"+strconv.Itoa(proxy.LocalPort())) Expect(err).ToNot(HaveOccurred()) _, err = net.ListenUDP("udp", addr) if runtime.GOOS == "windows" { Expect(err).To(MatchError(fmt.Sprintf("listen udp 127.0.0.1:%d: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.", proxy.LocalPort()))) } else { Expect(err).To(MatchError(fmt.Sprintf("listen udp 127.0.0.1:%d: bind: address already in use", proxy.LocalPort()))) } Expect(proxy.Close()).To(Succeed()) // stopping is tested in the next test }) It("stops the UDPProxy", func() { isProxyRunning := func() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "proxy.(*QuicProxy).runProxy") } proxy, err := NewQuicProxy("localhost:0", nil) Expect(err).ToNot(HaveOccurred()) port := proxy.LocalPort() Eventually(isProxyRunning).Should(BeTrue()) err = proxy.Close() Expect(err).ToNot(HaveOccurred()) // check that the proxy port is not in use anymore addr, err := net.ResolveUDPAddr("udp", "localhost:"+strconv.Itoa(port)) Expect(err).ToNot(HaveOccurred()) // sometimes it takes a while for the OS to free the port Eventually(func() error { ln, err := net.ListenUDP("udp", addr) if err != nil { return err } ln.Close() return nil }).ShouldNot(HaveOccurred()) Eventually(isProxyRunning).Should(BeFalse()) }) It("stops listening for proxied connections", func() { serverAddr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) serverConn, err := net.ListenUDP("udp", serverAddr) Expect(err).ToNot(HaveOccurred()) defer serverConn.Close() proxy, err := NewQuicProxy("localhost:0", &Opts{RemoteAddr: serverConn.LocalAddr().String()}) Expect(err).ToNot(HaveOccurred()) Expect(isProxyRunning()).To(BeFalse()) // check that the proxy port is not in use anymore conn, err := net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr)) Expect(err).ToNot(HaveOccurred()) _, err = conn.Write(makePacket(1, []byte("foobar"))) Expect(err).ToNot(HaveOccurred()) Eventually(isProxyRunning).Should(BeTrue()) Expect(proxy.Close()).To(Succeed()) Eventually(isProxyRunning).Should(BeFalse()) }) It("has the correct LocalAddr and LocalPort", func() { proxy, err := NewQuicProxy("localhost:0", nil) Expect(err).ToNot(HaveOccurred()) Expect(proxy.LocalAddr().String()).To(Equal("127.0.0.1:" + strconv.Itoa(proxy.LocalPort()))) Expect(proxy.LocalPort()).ToNot(BeZero()) Expect(proxy.Close()).To(Succeed()) }) }) Context("Proxy tests", func() { var ( serverConn *net.UDPConn serverNumPacketsSent atomic.Int32 serverReceivedPackets chan packetData clientConn *net.UDPConn proxy *QuicProxy stoppedReading chan struct{} ) startProxy := func(opts *Opts) { var err error proxy, err = NewQuicProxy("localhost:0", opts) Expect(err).ToNot(HaveOccurred()) clientConn, err = net.DialUDP("udp", nil, proxy.LocalAddr().(*net.UDPAddr)) Expect(err).ToNot(HaveOccurred()) } BeforeEach(func() { stoppedReading = make(chan struct{}) serverReceivedPackets = make(chan packetData, 100) serverNumPacketsSent.Store(0) // set up a dump UDP server // in production this would be a QUIC server raddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0") Expect(err).ToNot(HaveOccurred()) serverConn, err = net.ListenUDP("udp", raddr) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() defer close(stoppedReading) for { buf := make([]byte, protocol.MaxPacketBufferSize) // the ReadFromUDP will error as soon as the UDP conn is closed n, addr, err2 := serverConn.ReadFromUDP(buf) if err2 != nil { return } data := buf[0:n] serverReceivedPackets <- packetData(data) // echo the packet serverNumPacketsSent.Add(1) serverConn.WriteToUDP(data, addr) } }() }) AfterEach(func() { Expect(proxy.Close()).To(Succeed()) Expect(serverConn.Close()).To(Succeed()) Expect(clientConn.Close()).To(Succeed()) Eventually(stoppedReading).Should(BeClosed()) }) Context("no packet drop", func() { It("relays packets from the client to the server", func() { startProxy(&Opts{RemoteAddr: serverConn.LocalAddr().String()}) // send the first packet _, err := clientConn.Write(makePacket(1, []byte("foobar"))) Expect(err).ToNot(HaveOccurred()) // send the second packet _, err = clientConn.Write(makePacket(2, []byte("decafbad"))) Expect(err).ToNot(HaveOccurred()) Eventually(serverReceivedPackets).Should(HaveLen(2)) Expect(string(<-serverReceivedPackets)).To(ContainSubstring("foobar")) Expect(string(<-serverReceivedPackets)).To(ContainSubstring("decafbad")) }) It("relays packets from the server to the client", func() { startProxy(&Opts{RemoteAddr: serverConn.LocalAddr().String()}) // send the first packet _, err := clientConn.Write(makePacket(1, []byte("foobar"))) Expect(err).ToNot(HaveOccurred()) // send the second packet _, err = clientConn.Write(makePacket(2, []byte("decafbad"))) Expect(err).ToNot(HaveOccurred()) clientReceivedPackets := make(chan packetData, 2) // receive the packets echoed by the server on client side go func() { for { buf := make([]byte, protocol.MaxPacketBufferSize) // the ReadFromUDP will error as soon as the UDP conn is closed n, _, err2 := clientConn.ReadFromUDP(buf) if err2 != nil { return } data := buf[0:n] clientReceivedPackets <- packetData(data) } }() Eventually(serverReceivedPackets).Should(HaveLen(2)) Expect(serverNumPacketsSent.Load()).To(BeEquivalentTo(2)) Eventually(clientReceivedPackets).Should(HaveLen(2)) Expect(string(<-clientReceivedPackets)).To(ContainSubstring("foobar")) Expect(string(<-clientReceivedPackets)).To(ContainSubstring("decafbad")) }) }) Context("Drop Callbacks", func() { It("drops incoming packets", func() { var counter atomic.Int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), DropPacket: func(d Direction, _ []byte) bool { if d != DirectionIncoming { return false } return counter.Add(1)%2 == 1 }, } startProxy(opts) for i := 1; i <= 6; i++ { _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) Expect(err).ToNot(HaveOccurred()) } Eventually(serverReceivedPackets).Should(HaveLen(3)) Consistently(serverReceivedPackets).Should(HaveLen(3)) }) It("drops outgoing packets", func() { const numPackets = 6 var counter atomic.Int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), DropPacket: func(d Direction, _ []byte) bool { if d != DirectionOutgoing { return false } return counter.Add(1)%2 == 1 }, } startProxy(opts) clientReceivedPackets := make(chan packetData, numPackets) // receive the packets echoed by the server on client side go func() { for { buf := make([]byte, protocol.MaxPacketBufferSize) // the ReadFromUDP will error as soon as the UDP conn is closed n, _, err2 := clientConn.ReadFromUDP(buf) if err2 != nil { return } data := buf[0:n] clientReceivedPackets <- packetData(data) } }() for i := 1; i <= numPackets; i++ { _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) Expect(err).ToNot(HaveOccurred()) } Eventually(clientReceivedPackets).Should(HaveLen(numPackets / 2)) Consistently(clientReceivedPackets).Should(HaveLen(numPackets / 2)) }) }) Context("Delay Callback", func() { const delay = 200 * time.Millisecond expectDelay := func(startTime time.Time, numRTTs int) { expectedReceiveTime := startTime.Add(time.Duration(numRTTs) * delay) Expect(time.Now()).To(SatisfyAll( BeTemporally(">=", expectedReceiveTime), BeTemporally("<", expectedReceiveTime.Add(delay/2)), )) } It("delays incoming packets", func() { var counter atomic.Int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), // delay packet 1 by 200 ms // delay packet 2 by 400 ms // ... DelayPacket: func(d Direction, _ []byte) time.Duration { if d == DirectionOutgoing { return 0 } p := counter.Add(1) return time.Duration(p) * delay }, } startProxy(opts) // send 3 packets start := time.Now() for i := 1; i <= 3; i++ { _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) Expect(err).ToNot(HaveOccurred()) } Eventually(serverReceivedPackets).Should(HaveLen(1)) expectDelay(start, 1) Eventually(serverReceivedPackets).Should(HaveLen(2)) expectDelay(start, 2) Eventually(serverReceivedPackets).Should(HaveLen(3)) expectDelay(start, 3) Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(1))) Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(2))) Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(3))) }) It("handles reordered packets", func() { var counter atomic.Int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), // delay packet 1 by 600 ms // delay packet 2 by 400 ms // delay packet 3 by 200 ms DelayPacket: func(d Direction, _ []byte) time.Duration { if d == DirectionOutgoing { return 0 } p := counter.Add(1) return 600*time.Millisecond - time.Duration(p-1)*delay }, } startProxy(opts) // send 3 packets start := time.Now() for i := 1; i <= 3; i++ { _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) Expect(err).ToNot(HaveOccurred()) } Eventually(serverReceivedPackets).Should(HaveLen(1)) expectDelay(start, 1) Eventually(serverReceivedPackets).Should(HaveLen(2)) expectDelay(start, 2) Eventually(serverReceivedPackets).Should(HaveLen(3)) expectDelay(start, 3) Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(3))) Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(2))) Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(1))) }) It("doesn't reorder packets when a constant delay is used", func() { opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), DelayPacket: func(d Direction, _ []byte) time.Duration { if d == DirectionOutgoing { return 0 } return 100 * time.Millisecond }, } startProxy(opts) // send 100 packets for i := 0; i < 100; i++ { _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) Expect(err).ToNot(HaveOccurred()) } Eventually(serverReceivedPackets).Should(HaveLen(100)) for i := 0; i < 100; i++ { Expect(readPacketNumber(<-serverReceivedPackets)).To(Equal(protocol.PacketNumber(i))) } }) It("delays outgoing packets", func() { const numPackets = 3 var counter atomic.Int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), // delay packet 1 by 200 ms // delay packet 2 by 400 ms // ... DelayPacket: func(d Direction, _ []byte) time.Duration { if d == DirectionIncoming { return 0 } p := counter.Add(1) return time.Duration(p) * delay }, } startProxy(opts) clientReceivedPackets := make(chan packetData, numPackets) // receive the packets echoed by the server on client side go func() { for { buf := make([]byte, protocol.MaxPacketBufferSize) // the ReadFromUDP will error as soon as the UDP conn is closed n, _, err2 := clientConn.ReadFromUDP(buf) if err2 != nil { return } data := buf[0:n] clientReceivedPackets <- packetData(data) } }() start := time.Now() for i := 1; i <= numPackets; i++ { _, err := clientConn.Write(makePacket(protocol.PacketNumber(i), []byte("foobar"+strconv.Itoa(i)))) Expect(err).ToNot(HaveOccurred()) } // the packets should have arrived immediately at the server Eventually(serverReceivedPackets).Should(HaveLen(3)) expectDelay(start, 0) Eventually(clientReceivedPackets).Should(HaveLen(1)) expectDelay(start, 1) Eventually(clientReceivedPackets).Should(HaveLen(2)) expectDelay(start, 2) Eventually(clientReceivedPackets).Should(HaveLen(3)) expectDelay(start, 3) Expect(readPacketNumber(<-clientReceivedPackets)).To(Equal(protocol.PacketNumber(1))) Expect(readPacketNumber(<-clientReceivedPackets)).To(Equal(protocol.PacketNumber(2))) Expect(readPacketNumber(<-clientReceivedPackets)).To(Equal(protocol.PacketNumber(3))) }) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/tools/qlog.go000066400000000000000000000023071465664453100266500ustar00rootroot00000000000000package tools import ( "bufio" "context" "fmt" "io" "log" "os" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go/qlog" ) func QlogTracer(logger io.Writer) *logging.Tracer { filename := fmt.Sprintf("log_%s_transport.qlog", time.Now().Format("2006-01-02T15:04:05")) fmt.Fprintf(logger, "Creating %s.\n", filename) f, err := os.Create(filename) if err != nil { log.Fatalf("failed to create qlog file: %s", err) return nil } bw := bufio.NewWriter(f) return qlog.NewTracer(utils.NewBufferedWriteCloser(bw, f)) } func NewQlogConnectionTracer(logger io.Writer) func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return func(_ context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { filename := fmt.Sprintf("log_%s_%s.qlog", connID, p.String()) fmt.Fprintf(logger, "Creating %s.\n", filename) f, err := os.Create(filename) if err != nil { log.Fatalf("failed to create qlog file: %s", err) return nil } bw := bufio.NewWriter(f) return qlog.NewConnectionTracer(utils.NewBufferedWriteCloser(bw, f), p, connID) } } golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/versionnegotiation/000077500000000000000000000000001465664453100301435ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/versionnegotiation/handshake_test.go000066400000000000000000000161341465664453100334640ustar00rootroot00000000000000package versionnegotiation import ( "context" "crypto/tls" "errors" "fmt" "net" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/integrationtests/tools/israce" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type versioner interface { GetVersion() protocol.Version } type result struct { loggedVersions bool receivedVersionNegotiation bool chosen logging.Version clientVersions, serverVersions []logging.Version } func newVersionNegotiationTracer() (*result, *logging.ConnectionTracer) { r := &result{} return r, &logging.ConnectionTracer{ NegotiatedVersion: func(chosen logging.Version, clientVersions, serverVersions []logging.Version) { if r.loggedVersions { Fail("only expected one call to NegotiatedVersions") } r.loggedVersions = true r.chosen = chosen r.clientVersions = clientVersions r.serverVersions = serverVersions }, ReceivedVersionNegotiationPacket: func(dest, src logging.ArbitraryLenConnectionID, _ []logging.Version) { r.receivedVersionNegotiation = true }, } } var _ = Describe("Handshake tests", func() { startServer := func(tlsConf *tls.Config, conf *quic.Config) (*quic.Listener, func()) { server, err := quic.ListenAddr("localhost:0", tlsConf, conf) Expect(err).ToNot(HaveOccurred()) acceptStopped := make(chan struct{}) go func() { defer GinkgoRecover() defer close(acceptStopped) for { if _, err := server.Accept(context.Background()); err != nil { return } } }() return server, func() { server.Close() <-acceptStopped } } var supportedVersions []protocol.Version BeforeEach(func() { supportedVersions = append([]quic.Version{}, protocol.SupportedVersions...) protocol.SupportedVersions = append(protocol.SupportedVersions, []protocol.Version{7, 8, 9, 10}...) }) AfterEach(func() { protocol.SupportedVersions = supportedVersions }) if !israce.Enabled { It("when the server supports more versions than the client", func() { expectedVersion := protocol.SupportedVersions[0] // the server doesn't support the highest supported version, which is the first one the client will try // but it supports a bunch of versions that the client doesn't speak serverConfig := &quic.Config{} serverConfig.Versions = []protocol.Version{7, 8, protocol.SupportedVersions[0], 9} serverResult, serverTracer := newVersionNegotiationTracer() serverConfig.Tracer = func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return serverTracer } server, cl := startServer(getTLSConfig(), serverConfig) defer cl() clientResult, clientTracer := newVersionNegotiationTracer() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), maybeAddQLOGTracer(&quic.Config{Tracer: func(ctx context.Context, perspective logging.Perspective, id quic.ConnectionID) *logging.ConnectionTracer { return clientTracer }}), ) Expect(err).ToNot(HaveOccurred()) Expect(conn.(versioner).GetVersion()).To(Equal(expectedVersion)) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(clientResult.chosen).To(Equal(expectedVersion)) Expect(clientResult.receivedVersionNegotiation).To(BeFalse()) Expect(clientResult.clientVersions).To(Equal(protocol.SupportedVersions)) Expect(clientResult.serverVersions).To(BeEmpty()) Expect(serverResult.chosen).To(Equal(expectedVersion)) Expect(serverResult.serverVersions).To(Equal(serverConfig.Versions)) Expect(serverResult.clientVersions).To(BeEmpty()) }) It("when the client supports more versions than the server supports", func() { expectedVersion := protocol.SupportedVersions[0] // The server doesn't support the highest supported version, which is the first one the client will try, // but it supports a bunch of versions that the client doesn't speak serverResult, serverTracer := newVersionNegotiationTracer() serverConfig := &quic.Config{} serverConfig.Versions = supportedVersions serverConfig.Tracer = func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return serverTracer } server, cl := startServer(getTLSConfig(), serverConfig) defer cl() clientVersions := []protocol.Version{7, 8, 9, protocol.SupportedVersions[0], 10} clientResult, clientTracer := newVersionNegotiationTracer() conn, err := quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), maybeAddQLOGTracer(&quic.Config{ Versions: clientVersions, Tracer: func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return clientTracer }, }), ) Expect(err).ToNot(HaveOccurred()) Expect(conn.(versioner).GetVersion()).To(Equal(protocol.SupportedVersions[0])) Expect(conn.CloseWithError(0, "")).To(Succeed()) Expect(clientResult.chosen).To(Equal(expectedVersion)) Expect(clientResult.receivedVersionNegotiation).To(BeTrue()) Expect(clientResult.clientVersions).To(Equal(clientVersions)) Expect(clientResult.serverVersions).To(ContainElements(supportedVersions)) // may contain greased versions Expect(serverResult.chosen).To(Equal(expectedVersion)) Expect(serverResult.serverVersions).To(Equal(serverConfig.Versions)) Expect(serverResult.clientVersions).To(BeEmpty()) }) It("fails if the server disables version negotiation", func() { // The server doesn't support the highest supported version, which is the first one the client will try, // but it supports a bunch of versions that the client doesn't speak _, serverTracer := newVersionNegotiationTracer() serverConfig := &quic.Config{} serverConfig.Versions = supportedVersions serverConfig.Tracer = func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return serverTracer } conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)}) Expect(err).ToNot(HaveOccurred()) tr := &quic.Transport{ Conn: conn, DisableVersionNegotiationPackets: true, } ln, err := tr.Listen(getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() clientVersions := []protocol.Version{7, 8, 9, protocol.SupportedVersions[0], 10} clientResult, clientTracer := newVersionNegotiationTracer() _, err = quic.DialAddr( context.Background(), fmt.Sprintf("localhost:%d", conn.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), maybeAddQLOGTracer(&quic.Config{ Versions: clientVersions, Tracer: func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer { return clientTracer }, HandshakeIdleTimeout: 100 * time.Millisecond, }), ) Expect(err).To(HaveOccurred()) var nerr net.Error Expect(errors.As(err, &nerr)).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(clientResult.receivedVersionNegotiation).To(BeFalse()) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/versionnegotiation/rtt_test.go000066400000000000000000000030111465664453100323350ustar00rootroot00000000000000package versionnegotiation import ( "context" "time" "github.com/quic-go/quic-go" quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Handshake RTT tests", func() { const rtt = 400 * time.Millisecond expectDurationInRTTs := func(startTime time.Time, num int) { testDuration := time.Since(startTime) rtts := float32(testDuration) / float32(rtt) Expect(rtts).To(SatisfyAll( BeNumerically(">=", num), BeNumerically("<", num+1), )) } It("fails when there's no matching version, after 1 RTT", func() { if len(protocol.SupportedVersions) == 1 { Skip("Test requires at least 2 supported versions.") } serverConfig := &quic.Config{} serverConfig.Versions = protocol.SupportedVersions[:1] ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() // start the proxy proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: ln.Addr().String(), DelayPacket: func(_ quicproxy.Direction, _ []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) startTime := time.Now() _, err = quic.DialAddr( context.Background(), proxy.LocalAddr().String(), getTLSClientConfig(), maybeAddQLOGTracer(&quic.Config{Versions: protocol.SupportedVersions[1:2]}), ) Expect(err).To(HaveOccurred()) expectDurationInRTTs(startTime, 1) }) }) versionnegotiation_suite_test.go000066400000000000000000000033261465664453100366150ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/integrationtests/versionnegotiationpackage versionnegotiation import ( "context" "crypto/tls" "crypto/x509" "flag" "testing" "github.com/quic-go/quic-go/integrationtests/tools" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var ( enableQlog bool tlsConfig *tls.Config tlsClientConfig *tls.Config ) func init() { flag.BoolVar(&enableQlog, "qlog", false, "enable qlog") ca, caPrivateKey, err := tools.GenerateCA() if err != nil { panic(err) } leafCert, leafPrivateKey, err := tools.GenerateLeafCert(ca, caPrivateKey) if err != nil { panic(err) } tlsConfig = &tls.Config{ Certificates: []tls.Certificate{{ Certificate: [][]byte{leafCert.Raw}, PrivateKey: leafPrivateKey, }}, NextProtos: []string{tools.ALPN}, } root := x509.NewCertPool() root.AddCert(ca) tlsClientConfig = &tls.Config{ ServerName: "localhost", RootCAs: root, NextProtos: []string{tools.ALPN}, } } func getTLSConfig() *tls.Config { return tlsConfig } func getTLSClientConfig() *tls.Config { return tlsClientConfig } func TestQuicVersionNegotiation(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Version Negotiation Suite") } func maybeAddQLOGTracer(c *quic.Config) *quic.Config { if c == nil { c = &quic.Config{} } if !enableQlog { return c } qlogger := tools.NewQlogConnectionTracer(GinkgoWriter) if c.Tracer == nil { c.Tracer = qlogger } else if qlogger != nil { origTracer := c.Tracer c.Tracer = func(ctx context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { return logging.NewMultiplexedConnectionTracer( qlogger(ctx, p, connID), origTracer(ctx, p, connID), ) } } return c } golang-github-lucas-clemente-quic-go-0.46.0/interface.go000066400000000000000000000447241465664453100231110ustar00rootroot00000000000000package quic import ( "context" "crypto/tls" "errors" "io" "net" "time" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" ) // The StreamID is the ID of a QUIC stream. type StreamID = protocol.StreamID // A Version is a QUIC version number. type Version = protocol.Version // A VersionNumber is a QUIC version number. // Deprecated: VersionNumber was renamed to Version. type VersionNumber = Version const ( // Version1 is RFC 9000 Version1 = protocol.Version1 // Version2 is RFC 9369 Version2 = protocol.Version2 ) // A ClientToken is a token received by the client. // It can be used to skip address validation on future connection attempts. type ClientToken struct { data []byte } type TokenStore interface { // Pop searches for a ClientToken associated with the given key. // Since tokens are not supposed to be reused, it must remove the token from the cache. // It returns nil when no token is found. Pop(key string) (token *ClientToken) // Put adds a token to the cache with the given key. It might get called // multiple times in a connection. Put(key string, token *ClientToken) } // Err0RTTRejected is the returned from: // * Open{Uni}Stream{Sync} // * Accept{Uni}Stream // * Stream.Read and Stream.Write // when the server rejects a 0-RTT connection attempt. var Err0RTTRejected = errors.New("0-RTT rejected") // ConnectionTracingKey can be used to associate a ConnectionTracer with a Connection. // It is set on the Connection.Context() context, // as well as on the context passed to logging.Tracer.NewConnectionTracer. // Deprecated: Applications can set their own tracing key using Transport.ConnContext. var ConnectionTracingKey = connTracingCtxKey{} // ConnectionTracingID is the type of the context value saved under the ConnectionTracingKey. // Deprecated: Applications can set their own tracing key using Transport.ConnContext. type ConnectionTracingID uint64 type connTracingCtxKey struct{} // QUICVersionContextKey can be used to find out the QUIC version of a TLS handshake from the // context returned by tls.Config.ClientHelloInfo.Context. var QUICVersionContextKey = handshake.QUICVersionContextKey // Stream is the interface implemented by QUIC streams // In addition to the errors listed on the Connection, // calls to stream functions can return a StreamError if the stream is canceled. type Stream interface { ReceiveStream SendStream // SetDeadline sets the read and write deadlines associated // with the connection. It is equivalent to calling both // SetReadDeadline and SetWriteDeadline. SetDeadline(t time.Time) error } // A ReceiveStream is a unidirectional Receive Stream. type ReceiveStream interface { // StreamID returns the stream ID. StreamID() StreamID // Read reads data from the stream. // Read can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetReadDeadline. // If the stream was canceled by the peer, the error is a StreamError and // Remote == true. // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. io.Reader // CancelRead aborts receiving on this stream. // It will ask the peer to stop transmitting stream data. // Read will unblock immediately, and future Read calls will fail. // When called multiple times or after reading the io.EOF it is a no-op. CancelRead(StreamErrorCode) // SetReadDeadline sets the deadline for future Read calls and // any currently-blocked Read call. // A zero value for t means Read will not time out. SetReadDeadline(t time.Time) error } // A SendStream is a unidirectional Send Stream. type SendStream interface { // StreamID returns the stream ID. StreamID() StreamID // Write writes data to the stream. // Write can be made to time out and return a net.Error with Timeout() == true // after a fixed time limit; see SetDeadline and SetWriteDeadline. // If the stream was canceled by the peer, the error is a StreamError and // Remote == true. // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. io.Writer // Close closes the write-direction of the stream. // Future calls to Write are not permitted after calling Close. // It must not be called concurrently with Write. // It must not be called after calling CancelWrite. io.Closer // CancelWrite aborts sending on this stream. // Data already written, but not yet delivered to the peer is not guaranteed to be delivered reliably. // Write will unblock immediately, and future calls to Write will fail. // When called multiple times it is a no-op. // When called after Close, it aborts delivery. Note that there is no guarantee if // the peer will receive the FIN or the reset first. CancelWrite(StreamErrorCode) // The Context is canceled as soon as the write-side of the stream is closed. // This happens when Close() or CancelWrite() is called, or when the peer // cancels the read-side of their stream. // The cancellation cause is set to the error that caused the stream to // close, or `context.Canceled` in case the stream is closed without error. Context() context.Context // SetWriteDeadline sets the deadline for future Write calls // and any currently-blocked Write call. // Even if write times out, it may return n > 0, indicating that // some data was successfully written. // A zero value for t means Write will not time out. SetWriteDeadline(t time.Time) error } // A Connection is a QUIC connection between two peers. // Calls to the connection (and to streams) can return the following types of errors: // * ApplicationError: for errors triggered by the application running on top of QUIC // * TransportError: for errors triggered by the QUIC transport (in many cases a misbehaving peer) // * IdleTimeoutError: when the peer goes away unexpectedly (this is a net.Error timeout error) // * HandshakeTimeoutError: when the cryptographic handshake takes too long (this is a net.Error timeout error) // * StatelessResetError: when we receive a stateless reset // * VersionNegotiationError: returned by the client, when there's no version overlap between the peers type Connection interface { // AcceptStream returns the next stream opened by the peer, blocking until one is available. // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. AcceptStream(context.Context) (Stream, error) // AcceptUniStream returns the next unidirectional stream opened by the peer, blocking until one is available. // If the connection was closed due to a timeout, the error satisfies // the net.Error interface, and Timeout() will be true. AcceptUniStream(context.Context) (ReceiveStream, error) // OpenStream opens a new bidirectional QUIC stream. // There is no signaling to the peer about new streams: // The peer can only accept the stream after data has been sent on the stream, // or the stream has been reset or closed. // When reaching the peer's stream limit, it is not possible to open a new stream until the // peer raises the stream limit. In that case, a StreamLimitReachedError is returned. OpenStream() (Stream, error) // OpenStreamSync opens a new bidirectional QUIC stream. // It blocks until a new stream can be opened. // There is no signaling to the peer about new streams: // The peer can only accept the stream after data has been sent on the stream, // or the stream has been reset or closed. OpenStreamSync(context.Context) (Stream, error) // OpenUniStream opens a new outgoing unidirectional QUIC stream. // There is no signaling to the peer about new streams: // The peer can only accept the stream after data has been sent on the stream, // or the stream has been reset or closed. // When reaching the peer's stream limit, it is not possible to open a new stream until the // peer raises the stream limit. In that case, a StreamLimitReachedError is returned. OpenUniStream() (SendStream, error) // OpenUniStreamSync opens a new outgoing unidirectional QUIC stream. // It blocks until a new stream can be opened. // There is no signaling to the peer about new streams: // The peer can only accept the stream after data has been sent on the stream, // or the stream has been reset or closed. OpenUniStreamSync(context.Context) (SendStream, error) // LocalAddr returns the local address. LocalAddr() net.Addr // RemoteAddr returns the address of the peer. RemoteAddr() net.Addr // CloseWithError closes the connection with an error. // The error string will be sent to the peer. CloseWithError(ApplicationErrorCode, string) error // Context returns a context that is cancelled when the connection is closed. // The cancellation cause is set to the error that caused the connection to // close, or `context.Canceled` in case the listener is closed first. Context() context.Context // ConnectionState returns basic details about the QUIC connection. // Warning: This API should not be considered stable and might change soon. ConnectionState() ConnectionState // SendDatagram sends a message using a QUIC datagram, as specified in RFC 9221. // There is no delivery guarantee for DATAGRAM frames, they are not retransmitted if lost. // The payload of the datagram needs to fit into a single QUIC packet. // In addition, a datagram may be dropped before being sent out if the available packet size suddenly decreases. // If the payload is too large to be sent at the current time, a DatagramTooLargeError is returned. SendDatagram(payload []byte) error // ReceiveDatagram gets a message received in a datagram, as specified in RFC 9221. ReceiveDatagram(context.Context) ([]byte, error) } // An EarlyConnection is a connection that is handshaking. // Data sent during the handshake is encrypted using the forward secure keys. // When using client certificates, the client's identity is only verified // after completion of the handshake. type EarlyConnection interface { Connection // HandshakeComplete blocks until the handshake completes (or fails). // For the client, data sent before completion of the handshake is encrypted with 0-RTT keys. // For the server, data sent before completion of the handshake is encrypted with 1-RTT keys, // however the client's identity is only verified once the handshake completes. HandshakeComplete() <-chan struct{} NextConnection(context.Context) (Connection, error) } // StatelessResetKey is a key used to derive stateless reset tokens. type StatelessResetKey [32]byte // TokenGeneratorKey is a key used to encrypt session resumption tokens. type TokenGeneratorKey = handshake.TokenProtectorKey // A ConnectionID is a QUIC Connection ID, as defined in RFC 9000. // It is not able to handle QUIC Connection IDs longer than 20 bytes, // as they are allowed by RFC 8999. type ConnectionID = protocol.ConnectionID // ConnectionIDFromBytes interprets b as a Connection ID. It panics if b is // longer than 20 bytes. func ConnectionIDFromBytes(b []byte) ConnectionID { return protocol.ParseConnectionID(b) } // A ConnectionIDGenerator is an interface that allows clients to implement their own format // for the Connection IDs that servers/clients use as SrcConnectionID in QUIC packets. // // Connection IDs generated by an implementation should always produce IDs of constant size. type ConnectionIDGenerator interface { // GenerateConnectionID generates a new ConnectionID. // Generated ConnectionIDs should be unique and observers should not be able to correlate two ConnectionIDs. GenerateConnectionID() (ConnectionID, error) // ConnectionIDLen tells what is the length of the ConnectionIDs generated by the implementation of // this interface. // Effectively, this means that implementations of ConnectionIDGenerator must always return constant-size // connection IDs. Valid lengths are between 0 and 20 and calls to GenerateConnectionID. // 0-length ConnectionsIDs can be used when an endpoint (server or client) does not require multiplexing connections // in the presence of a connection migration environment. ConnectionIDLen() int } // Config contains all configuration data needed for a QUIC server or client. type Config struct { // GetConfigForClient is called for incoming connections. // If the error is not nil, the connection attempt is refused. GetConfigForClient func(info *ClientHelloInfo) (*Config, error) // The QUIC versions that can be negotiated. // If not set, it uses all versions available. Versions []Version // HandshakeIdleTimeout is the idle timeout before completion of the handshake. // If we don't receive any packet from the peer within this time, the connection attempt is aborted. // Additionally, if the handshake doesn't complete in twice this time, the connection attempt is also aborted. // If this value is zero, the timeout is set to 5 seconds. HandshakeIdleTimeout time.Duration // MaxIdleTimeout is the maximum duration that may pass without any incoming network activity. // The actual value for the idle timeout is the minimum of this value and the peer's. // This value only applies after the handshake has completed. // If the timeout is exceeded, the connection is closed. // If this value is zero, the timeout is set to 30 seconds. MaxIdleTimeout time.Duration // The TokenStore stores tokens received from the server. // Tokens are used to skip address validation on future connection attempts. // The key used to store tokens is the ServerName from the tls.Config, if set // otherwise the token is associated with the server's IP address. TokenStore TokenStore // InitialStreamReceiveWindow is the initial size of the stream-level flow control window for receiving data. // If the application is consuming data quickly enough, the flow control auto-tuning algorithm // will increase the window up to MaxStreamReceiveWindow. // If this value is zero, it will default to 512 KB. // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. InitialStreamReceiveWindow uint64 // MaxStreamReceiveWindow is the maximum stream-level flow control window for receiving data. // If this value is zero, it will default to 6 MB. // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. MaxStreamReceiveWindow uint64 // InitialConnectionReceiveWindow is the initial size of the stream-level flow control window for receiving data. // If the application is consuming data quickly enough, the flow control auto-tuning algorithm // will increase the window up to MaxConnectionReceiveWindow. // If this value is zero, it will default to 512 KB. // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. InitialConnectionReceiveWindow uint64 // MaxConnectionReceiveWindow is the connection-level flow control window for receiving data. // If this value is zero, it will default to 15 MB. // Values larger than the maximum varint (quicvarint.Max) will be clipped to that value. MaxConnectionReceiveWindow uint64 // AllowConnectionWindowIncrease is called every time the connection flow controller attempts // to increase the connection flow control window. // If set, the caller can prevent an increase of the window. Typically, it would do so to // limit the memory usage. // To avoid deadlocks, it is not valid to call other functions on the connection or on streams // in this callback. AllowConnectionWindowIncrease func(conn Connection, delta uint64) bool // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. // If not set, it will default to 100. // If set to a negative value, it doesn't allow any bidirectional streams. // Values larger than 2^60 will be clipped to that value. MaxIncomingStreams int64 // MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open. // If not set, it will default to 100. // If set to a negative value, it doesn't allow any unidirectional streams. // Values larger than 2^60 will be clipped to that value. MaxIncomingUniStreams int64 // KeepAlivePeriod defines whether this peer will periodically send a packet to keep the connection alive. // If set to 0, then no keep alive is sent. Otherwise, the keep alive is sent on that period (or at most // every half of MaxIdleTimeout, whichever is smaller). KeepAlivePeriod time.Duration // InitialPacketSize is the initial size of packets sent. // It is usually not necessary to manually set this value, // since Path MTU discovery very quickly finds the path's MTU. // If set too high, the path might not support packets that large, leading to a timeout of the QUIC handshake. // Values below 1200 are invalid. InitialPacketSize uint16 // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899). // This allows the sending of QUIC packets that fully utilize the available MTU of the path. // Path MTU discovery is only available on systems that allow setting of the Don't Fragment (DF) bit. DisablePathMTUDiscovery bool // Allow0RTT allows the application to decide if a 0-RTT connection attempt should be accepted. // Only valid for the server. Allow0RTT bool // Enable QUIC datagram support (RFC 9221). EnableDatagrams bool Tracer func(context.Context, logging.Perspective, ConnectionID) *logging.ConnectionTracer } // ClientHelloInfo contains information about an incoming connection attempt. type ClientHelloInfo struct { // RemoteAddr is the remote address on the Initial packet. // Unless AddrVerified is set, the address is not yet verified, and could be a spoofed IP address. RemoteAddr net.Addr // AddrVerified says if the remote address was verified using QUIC's Retry mechanism. // Note that the Retry mechanism costs one network roundtrip, // and is not performed unless Transport.MaxUnvalidatedHandshakes is surpassed. AddrVerified bool } // ConnectionState records basic details about a QUIC connection type ConnectionState struct { // TLS contains information about the TLS connection state, incl. the tls.ConnectionState. TLS tls.ConnectionState // SupportsDatagrams says if support for QUIC datagrams (RFC 9221) was negotiated. // This requires both nodes to support and enable the datagram extensions (via Config.EnableDatagrams). // If datagram support was negotiated, datagrams can be sent and received using the // SendDatagram and ReceiveDatagram methods on the Connection. SupportsDatagrams bool // Used0RTT says if 0-RTT resumption was used. Used0RTT bool // Version is the QUIC version of the QUIC connection. Version Version // GSO says if generic segmentation offload is used GSO bool } golang-github-lucas-clemente-quic-go-0.46.0/internal/000077500000000000000000000000001465664453100224235ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/000077500000000000000000000000001465664453100245175ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/ack_eliciting.go000066400000000000000000000010261465664453100276320ustar00rootroot00000000000000package ackhandler import "github.com/quic-go/quic-go/internal/wire" // IsFrameAckEliciting returns true if the frame is ack-eliciting. func IsFrameAckEliciting(f wire.Frame) bool { _, isAck := f.(*wire.AckFrame) _, isConnectionClose := f.(*wire.ConnectionCloseFrame) return !isAck && !isConnectionClose } // HasAckElicitingFrames returns true if at least one frame is ack-eliciting. func HasAckElicitingFrames(fs []Frame) bool { for _, f := range fs { if IsFrameAckEliciting(f.Frame) { return true } } return false } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/ack_eliciting_test.go000066400000000000000000000015141465664453100306730ustar00rootroot00000000000000package ackhandler import ( "reflect" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("ack-eliciting frames", func() { for fl, el := range map[wire.Frame]bool{ &wire.AckFrame{}: false, &wire.ConnectionCloseFrame{}: false, &wire.DataBlockedFrame{}: true, &wire.PingFrame{}: true, &wire.ResetStreamFrame{}: true, &wire.StreamFrame{}: true, &wire.MaxDataFrame{}: true, &wire.MaxStreamDataFrame{}: true, } { f := fl e := el fName := reflect.ValueOf(f).Elem().Type().Name() It("works for "+fName, func() { Expect(IsFrameAckEliciting(f)).To(Equal(e)) }) It("HasAckElicitingFrames works for "+fName, func() { Expect(HasAckElicitingFrames([]Frame{{Frame: f}})).To(Equal(e)) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/ackhandler.go000066400000000000000000000016301465664453100271420ustar00rootroot00000000000000package ackhandler import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) // NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler. // clientAddressValidated indicates whether the address was validated beforehand by an address validation token. // clientAddressValidated has no effect for a client. func NewAckHandler( initialPacketNumber protocol.PacketNumber, initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, clientAddressValidated bool, enableECN bool, pers protocol.Perspective, tracer *logging.ConnectionTracer, logger utils.Logger, ) (SentPacketHandler, ReceivedPacketHandler) { sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, clientAddressValidated, enableECN, pers, tracer, logger) return sph, newReceivedPacketHandler(sph, logger) } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/ackhandler_suite_test.go000066400000000000000000000005771465664453100314230ustar00rootroot00000000000000package ackhandler import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func TestCrypto(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "AckHandler Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/ecn.go000066400000000000000000000237771465664453100256330ustar00rootroot00000000000000package ackhandler import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) type ecnState uint8 const ( ecnStateInitial ecnState = iota ecnStateTesting ecnStateUnknown ecnStateCapable ecnStateFailed ) // must fit into an uint8, otherwise numSentTesting and numLostTesting must have a larger type const numECNTestingPackets = 10 type ecnHandler interface { SentPacket(protocol.PacketNumber, protocol.ECN) Mode() protocol.ECN HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) LostPacket(protocol.PacketNumber) } // The ecnTracker performs ECN validation of a path. // Once failed, it doesn't do any re-validation of the path. // It is designed only work for 1-RTT packets, it doesn't handle multiple packet number spaces. // In order to avoid revealing any internal state to on-path observers, // callers should make sure to start using ECN (i.e. calling Mode) for the very first 1-RTT packet sent. // The validation logic implemented here strictly follows the algorithm described in RFC 9000 section 13.4.2 and A.4. type ecnTracker struct { state ecnState numSentTesting, numLostTesting uint8 firstTestingPacket protocol.PacketNumber lastTestingPacket protocol.PacketNumber firstCapablePacket protocol.PacketNumber numSentECT0, numSentECT1 int64 numAckedECT0, numAckedECT1, numAckedECNCE int64 tracer *logging.ConnectionTracer logger utils.Logger } var _ ecnHandler = &ecnTracker{} func newECNTracker(logger utils.Logger, tracer *logging.ConnectionTracer) *ecnTracker { return &ecnTracker{ firstTestingPacket: protocol.InvalidPacketNumber, lastTestingPacket: protocol.InvalidPacketNumber, firstCapablePacket: protocol.InvalidPacketNumber, state: ecnStateInitial, logger: logger, tracer: tracer, } } func (e *ecnTracker) SentPacket(pn protocol.PacketNumber, ecn protocol.ECN) { //nolint:exhaustive // These are the only ones we need to take care of. switch ecn { case protocol.ECNNon: return case protocol.ECT0: e.numSentECT0++ case protocol.ECT1: e.numSentECT1++ case protocol.ECNUnsupported: if e.state != ecnStateFailed { panic("didn't expect ECN to be unsupported") } default: panic(fmt.Sprintf("sent packet with unexpected ECN marking: %s", ecn)) } if e.state == ecnStateCapable && e.firstCapablePacket == protocol.InvalidPacketNumber { e.firstCapablePacket = pn } if e.state != ecnStateTesting { return } e.numSentTesting++ if e.firstTestingPacket == protocol.InvalidPacketNumber { e.firstTestingPacket = pn } if e.numSentECT0+e.numSentECT1 >= numECNTestingPackets { if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) } e.state = ecnStateUnknown e.lastTestingPacket = pn } } func (e *ecnTracker) Mode() protocol.ECN { switch e.state { case ecnStateInitial: if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) } e.state = ecnStateTesting return e.Mode() case ecnStateTesting, ecnStateCapable: return protocol.ECT0 case ecnStateUnknown, ecnStateFailed: return protocol.ECNNon default: panic(fmt.Sprintf("unknown ECN state: %d", e.state)) } } func (e *ecnTracker) LostPacket(pn protocol.PacketNumber) { if e.state != ecnStateTesting && e.state != ecnStateUnknown { return } if !e.isTestingPacket(pn) { return } e.numLostTesting++ // Only proceed if we have sent all 10 testing packets. if e.state != ecnStateUnknown { return } if e.numLostTesting >= e.numSentTesting { e.logger.Debugf("Disabling ECN. All testing packets were lost.") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets) } e.state = ecnStateFailed return } // Path validation also fails if some testing packets are lost, and all other testing packets where CE-marked e.failIfMangled() } // HandleNewlyAcked handles the ECN counts on an ACK frame. // It must only be called for ACK frames that increase the largest acknowledged packet number, // see section 13.4.2.1 of RFC 9000. func (e *ecnTracker) HandleNewlyAcked(packets []*packet, ect0, ect1, ecnce int64) (congested bool) { if e.state == ecnStateFailed { return false } // ECN validation can fail if the received total count for either ECT(0) or ECT(1) exceeds // the total number of packets sent with each corresponding ECT codepoint. if ect0 > e.numSentECT0 || ect1 > e.numSentECT1 { e.logger.Debugf("Disabling ECN. Received more ECT(0) / ECT(1) acknowledgements than packets sent.") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent) } e.state = ecnStateFailed return false } // Count ECT0 and ECT1 marks that we used when sending the packets that are now being acknowledged. var ackedECT0, ackedECT1 int64 for _, p := range packets { //nolint:exhaustive // We only ever send ECT(0) and ECT(1). switch e.ecnMarking(p.PacketNumber) { case protocol.ECT0: ackedECT0++ case protocol.ECT1: ackedECT1++ } } // If an ACK frame newly acknowledges a packet that the endpoint sent with either the ECT(0) or ECT(1) // codepoint set, ECN validation fails if the corresponding ECN counts are not present in the ACK frame. // This check detects: // * paths that bleach all ECN marks, and // * peers that don't report any ECN counts if (ackedECT0 > 0 || ackedECT1 > 0) && ect0 == 0 && ect1 == 0 && ecnce == 0 { e.logger.Debugf("Disabling ECN. ECN-marked packet acknowledged, but no ECN counts on ACK frame.") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts) } e.state = ecnStateFailed return false } // Determine the increase in ECT0, ECT1 and ECNCE marks newECT0 := ect0 - e.numAckedECT0 newECT1 := ect1 - e.numAckedECT1 newECNCE := ecnce - e.numAckedECNCE // We're only processing ACKs that increase the Largest Acked. // Therefore, the ECN counters should only ever increase. // Any decrease means that the peer's counting logic is broken. if newECT0 < 0 || newECT1 < 0 || newECNCE < 0 { e.logger.Debugf("Disabling ECN. ECN counts decreased unexpectedly.") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedDecreasedECNCounts) } e.state = ecnStateFailed return false } // ECN validation also fails if the sum of the increase in ECT(0) and ECN-CE counts is less than the number // of newly acknowledged packets that were originally sent with an ECT(0) marking. // This could be the result of (partial) bleaching. if newECT0+newECNCE < ackedECT0 { e.logger.Debugf("Disabling ECN. Received less ECT(0) + ECN-CE than packets sent with ECT(0).") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) } e.state = ecnStateFailed return false } // Similarly, ECN validation fails if the sum of the increases to ECT(1) and ECN-CE counts is less than // the number of newly acknowledged packets sent with an ECT(1) marking. if newECT1+newECNCE < ackedECT1 { e.logger.Debugf("Disabling ECN. Received less ECT(1) + ECN-CE than packets sent with ECT(1).") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) } e.state = ecnStateFailed return false } // update our counters e.numAckedECT0 = ect0 e.numAckedECT1 = ect1 e.numAckedECNCE = ecnce // Detect mangling (a path remarking all ECN-marked testing packets as CE), // once all 10 testing packets have been sent out. if e.state == ecnStateUnknown { e.failIfMangled() if e.state == ecnStateFailed { return false } } if e.state == ecnStateTesting || e.state == ecnStateUnknown { var ackedTestingPacket bool for _, p := range packets { if e.isTestingPacket(p.PacketNumber) { ackedTestingPacket = true break } } // This check won't succeed if the path is mangling ECN-marks (i.e. rewrites all ECN-marked packets to CE). if ackedTestingPacket && (newECT0 > 0 || newECT1 > 0) { e.logger.Debugf("ECN capability confirmed.") if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) } e.state = ecnStateCapable } } // Don't trust CE marks before having confirmed ECN capability of the path. // Otherwise, mangling would be misinterpreted as actual congestion. return e.state == ecnStateCapable && newECNCE > 0 } // failIfMangled fails ECN validation if all testing packets are lost or CE-marked. func (e *ecnTracker) failIfMangled() { numAckedECNCE := e.numAckedECNCE + int64(e.numLostTesting) if e.numSentECT0+e.numSentECT1 > numAckedECNCE { return } if e.tracer != nil && e.tracer.ECNStateUpdated != nil { e.tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) } e.state = ecnStateFailed } func (e *ecnTracker) ecnMarking(pn protocol.PacketNumber) protocol.ECN { if pn < e.firstTestingPacket || e.firstTestingPacket == protocol.InvalidPacketNumber { return protocol.ECNNon } if pn < e.lastTestingPacket || e.lastTestingPacket == protocol.InvalidPacketNumber { return protocol.ECT0 } if pn < e.firstCapablePacket || e.firstCapablePacket == protocol.InvalidPacketNumber { return protocol.ECNNon } // We don't need to deal with the case when ECN validation fails, // since we're ignoring any ECN counts reported in ACK frames in that case. return protocol.ECT0 } func (e *ecnTracker) isTestingPacket(pn protocol.PacketNumber) bool { if e.firstTestingPacket == protocol.InvalidPacketNumber { return false } return pn >= e.firstTestingPacket && (pn <= e.lastTestingPacket || e.lastTestingPacket == protocol.InvalidPacketNumber) } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/ecn_test.go000066400000000000000000000311111465664453100266470ustar00rootroot00000000000000package ackhandler import ( mocklogging "github.com/quic-go/quic-go/internal/mocks/logging" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("ECN tracker", func() { var ecnTracker *ecnTracker var tracer *mocklogging.MockConnectionTracer getAckedPackets := func(pns ...protocol.PacketNumber) []*packet { var packets []*packet for _, p := range pns { packets = append(packets, &packet{PacketNumber: p}) } return packets } BeforeEach(func() { var tr *logging.ConnectionTracer tr, tracer = mocklogging.NewMockConnectionTracer(mockCtrl) ecnTracker = newECNTracker(utils.DefaultLogger, tr) }) It("sends exactly 10 testing packets", func() { tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) for i := 0; i < 9; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) // Do this twice to make sure only sent packets are counted Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(protocol.PacketNumber(10+i), protocol.ECT0) } Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) ecnTracker.SentPacket(20, protocol.ECT0) // In unknown state, packets shouldn't be ECN-marked. Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) }) sendAllTestingPackets := func() { tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) for i := 0; i < 10; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0) } } It("fails ECN validation if all ECN testing packets are lost", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } for i := 0; i < 9; i++ { ecnTracker.LostPacket(protocol.PacketNumber(i)) } // We don't care about the loss of non-testing packets ecnTracker.LostPacket(15) // Now lose the last testing packet. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets) ecnTracker.LostPacket(9) Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) // We still don't care about more non-testing packets being lost ecnTracker.LostPacket(16) }) It("only detects ECN mangling after sending all testing packets", func() { tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) for i := 0; i < 9; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0) ecnTracker.LostPacket(protocol.PacketNumber(i)) } // Send the last testing packet, and receive a tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(9, protocol.ECT0) // Now lose the last testing packet. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedLostAllTestingPackets) ecnTracker.LostPacket(9) }) It("passes ECN validation when a testing packet is acknowledged, while still in testing state", func() { tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) for i := 0; i < 5; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0) } tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(3), 1, 0, 0)).To(BeFalse()) // make sure we continue sending ECT(0) packets for i := 5; i < 100; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0) } }) It("passes ECN validation when a testing packet is acknowledged, while in unknown state", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // Lose some packets to make sure this doesn't influence the outcome. for i := 0; i < 5; i++ { ecnTracker.LostPacket(protocol.PacketNumber(i)) } tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) Expect(ecnTracker.HandleNewlyAcked([]*packet{{PacketNumber: 7}}, 1, 0, 0)).To(BeFalse()) }) It("fails ECN validation when the ACK contains more ECN counts than we sent packets", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // only 10 ECT(0) packets were sent, but the ACK claims to have received 12 of them tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 12, 0, 0)).To(BeFalse()) }) It("fails ECN validation when the ACK contains ECN counts for the wrong code point", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // We sent ECT(0), but this ACK acknowledges ECT(1). tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedMoreECNCountsThanSent) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12), 0, 1, 0)).To(BeFalse()) }) It("fails ECN validation when the ACK doesn't contain ECN counts", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // First only acknowledge packets sent without ECN marks. Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(12, 13, 14), 0, 0, 0)).To(BeFalse()) // Now acknowledge some packets sent with ECN marks. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 15), 0, 0, 0)).To(BeFalse()) }) It("fails ECN validation when an ACK decreases ECN counts", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 3, 0, 0)).To(BeFalse()) // Now acknowledge some more packets, but decrease the ECN counts. Obviously, this doesn't make any sense. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedDecreasedECNCounts) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 13), 2, 0, 0)).To(BeFalse()) // make sure that new ACKs are ignored Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(7, 8, 9, 14), 5, 0, 0)).To(BeFalse()) }) // This can happen if ACK are lost / reordered. It("doesn't fail validation if the ACK contains more ECN counts than it acknowledges packets", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 8, 0, 0)).To(BeFalse()) }) It("fails ECN validation when the ACK doesn't contain enough ECN counts", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // First only acknowledge some packets sent with ECN marks. tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 2, 0, 1)).To(BeTrue()) // Now acknowledge some more packets sent with ECN marks, but don't increase the counters enough. // This ACK acknowledges 3 more ECN-marked packets, but the counters only increase by 2. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedTooFewECNCounts) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 15), 3, 0, 2)).To(BeFalse()) }) It("detects ECN mangling if all testing packets are marked CE", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // ECN capability not confirmed yet, therefore CE marks are not regarded as congestion events Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3), 0, 0, 4)).To(BeFalse()) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 10, 11, 12), 0, 0, 7)).To(BeFalse()) // With the next ACK, all testing packets will now have been marked CE. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(7, 8, 9, 13), 0, 0, 10)).To(BeFalse()) }) It("only detects ECN mangling after sending all testing packets", func() { tracer.EXPECT().ECNStateUpdated(logging.ECNStateTesting, logging.ECNTriggerNoTrigger) for i := 0; i < 9; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECT0) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(protocol.PacketNumber(i)), 0, 0, int64(i+1))).To(BeFalse()) } // Send the last testing packet, and receive a tracer.EXPECT().ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) Expect(ecnTracker.Mode()).To(Equal(protocol.ECT0)) ecnTracker.SentPacket(9, protocol.ECT0) // This ACK now reports the last testing packets as CE as well. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(9), 0, 0, 10)).To(BeFalse()) }) It("detects ECN mangling, if some testing packets are marked CE, and then others are lost", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // ECN capability not confirmed yet, therefore CE marks are not regarded as congestion events Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(0, 1, 2, 3), 0, 0, 4)).To(BeFalse()) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(6, 7, 8, 9), 0, 0, 8)).To(BeFalse()) // Lose one of the two unacknowledged packets. ecnTracker.LostPacket(4) // By losing the last unacknowledged testing packets, we should detect the mangling. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) ecnTracker.LostPacket(5) }) It("detects ECN mangling, if some testing packets are lost, and then others are marked CE", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // Lose a few packets. ecnTracker.LostPacket(0) ecnTracker.LostPacket(1) ecnTracker.LostPacket(2) // ECN capability not confirmed yet, therefore CE marks are not regarded as congestion events Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(3, 4, 5, 6, 7, 8), 0, 0, 6)).To(BeFalse()) // By CE-marking the last unacknowledged testing packets, we should detect the mangling. tracer.EXPECT().ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedManglingDetected) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(9), 0, 0, 7)).To(BeFalse()) }) It("declares congestion", func() { sendAllTestingPackets() for i := 10; i < 20; i++ { Expect(ecnTracker.Mode()).To(Equal(protocol.ECNNon)) ecnTracker.SentPacket(protocol.PacketNumber(i), protocol.ECNNon) } // Receive one CE count. tracer.EXPECT().ECNStateUpdated(logging.ECNStateCapable, logging.ECNTriggerNoTrigger) Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(1, 2, 3, 12), 2, 0, 1)).To(BeTrue()) // No increase in CE. No congestion. Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(4, 5, 6, 13), 5, 0, 1)).To(BeFalse()) // Increase in CE. More congestion. Expect(ecnTracker.HandleNewlyAcked(getAckedPackets(7, 8, 9, 14), 7, 0, 2)).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/frame.go000066400000000000000000000006551465664453100261460ustar00rootroot00000000000000package ackhandler import ( "github.com/quic-go/quic-go/internal/wire" ) // FrameHandler handles the acknowledgement and the loss of a frame. type FrameHandler interface { OnAcked(wire.Frame) OnLost(wire.Frame) } type Frame struct { Frame wire.Frame // nil if the frame has already been acknowledged in another packet Handler FrameHandler } type StreamFrame struct { Frame *wire.StreamFrame Handler FrameHandler } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/interfaces.go000066400000000000000000000042461465664453100271770ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) // SentPacketHandler handles ACKs received for outgoing packets type SentPacketHandler interface { // SentPacket may modify the packet SentPacket(t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool) // ReceivedAck processes an ACK frame. // It does not store a copy of the frame. ReceivedAck(f *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* 1-RTT packet acked */, error) ReceivedBytes(protocol.ByteCount) DropPackets(protocol.EncryptionLevel) ResetForRetry(rcvTime time.Time) error SetHandshakeConfirmed() // The SendMode determines if and what kind of packets can be sent. SendMode(now time.Time) SendMode // TimeUntilSend is the time when the next packet should be sent. // It is used for pacing packets. TimeUntilSend() time.Time SetMaxDatagramSize(count protocol.ByteCount) // only to be called once the handshake is complete QueueProbePacket(protocol.EncryptionLevel) bool /* was a packet queued */ ECNMode(isShortHeaderPacket bool) protocol.ECN // isShortHeaderPacket should only be true for non-coalesced 1-RTT packets PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber GetLossDetectionTimeout() time.Time OnLossDetectionTimeout() error } type sentPacketTracker interface { GetLowestPacketNotConfirmedAcked() protocol.PacketNumber ReceivedPacket(protocol.EncryptionLevel) } // ReceivedPacketHandler handles ACKs needed to send for incoming packets type ReceivedPacketHandler interface { IsPotentiallyDuplicate(protocol.PacketNumber, protocol.EncryptionLevel) bool ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, ackEliciting bool) error DropPackets(protocol.EncryptionLevel) GetAlarmTimeout() time.Time GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/mock_ecn_handler_test.go000066400000000000000000000060651465664453100313670ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/ackhandler (interfaces: ECNHandler) // // Generated by this command: // // mockgen -build_flags=-tags=gomock -package ackhandler -destination mock_ecn_handler_test.go github.com/quic-go/quic-go/internal/ackhandler ECNHandler // // Package ackhandler is a generated GoMock package. package ackhandler import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockECNHandler is a mock of ECNHandler interface. type MockECNHandler struct { ctrl *gomock.Controller recorder *MockECNHandlerMockRecorder } // MockECNHandlerMockRecorder is the mock recorder for MockECNHandler. type MockECNHandlerMockRecorder struct { mock *MockECNHandler } // NewMockECNHandler creates a new mock instance. func NewMockECNHandler(ctrl *gomock.Controller) *MockECNHandler { mock := &MockECNHandler{ctrl: ctrl} mock.recorder = &MockECNHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockECNHandler) EXPECT() *MockECNHandlerMockRecorder { return m.recorder } // HandleNewlyAcked mocks base method. func (m *MockECNHandler) HandleNewlyAcked(arg0 []*packet, arg1, arg2, arg3 int64) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleNewlyAcked", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(bool) return ret0 } // HandleNewlyAcked indicates an expected call of HandleNewlyAcked. func (mr *MockECNHandlerMockRecorder) HandleNewlyAcked(arg0, arg1, arg2, arg3 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleNewlyAcked", reflect.TypeOf((*MockECNHandler)(nil).HandleNewlyAcked), arg0, arg1, arg2, arg3) } // LostPacket mocks base method. func (m *MockECNHandler) LostPacket(arg0 protocol.PacketNumber) { m.ctrl.T.Helper() m.ctrl.Call(m, "LostPacket", arg0) } // LostPacket indicates an expected call of LostPacket. func (mr *MockECNHandlerMockRecorder) LostPacket(arg0 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LostPacket", reflect.TypeOf((*MockECNHandler)(nil).LostPacket), arg0) } // Mode mocks base method. func (m *MockECNHandler) Mode() protocol.ECN { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Mode") ret0, _ := ret[0].(protocol.ECN) return ret0 } // Mode indicates an expected call of Mode. func (mr *MockECNHandlerMockRecorder) Mode() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Mode", reflect.TypeOf((*MockECNHandler)(nil).Mode)) } // SentPacket mocks base method. func (m *MockECNHandler) SentPacket(arg0 protocol.PacketNumber, arg1 protocol.ECN) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentPacket", arg0, arg1) } // SentPacket indicates an expected call of SentPacket. func (mr *MockECNHandlerMockRecorder) SentPacket(arg0, arg1 any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockECNHandler)(nil).SentPacket), arg0, arg1) } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/mock_sent_packet_tracker_test.go000066400000000000000000000104061465664453100331320ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/ackhandler (interfaces: SentPacketTracker) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker // // Package ackhandler is a generated GoMock package. package ackhandler import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockSentPacketTracker is a mock of SentPacketTracker interface. type MockSentPacketTracker struct { ctrl *gomock.Controller recorder *MockSentPacketTrackerMockRecorder } // MockSentPacketTrackerMockRecorder is the mock recorder for MockSentPacketTracker. type MockSentPacketTrackerMockRecorder struct { mock *MockSentPacketTracker } // NewMockSentPacketTracker creates a new mock instance. func NewMockSentPacketTracker(ctrl *gomock.Controller) *MockSentPacketTracker { mock := &MockSentPacketTracker{ctrl: ctrl} mock.recorder = &MockSentPacketTrackerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSentPacketTracker) EXPECT() *MockSentPacketTrackerMockRecorder { return m.recorder } // GetLowestPacketNotConfirmedAcked mocks base method. func (m *MockSentPacketTracker) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLowestPacketNotConfirmedAcked") ret0, _ := ret[0].(protocol.PacketNumber) return ret0 } // GetLowestPacketNotConfirmedAcked indicates an expected call of GetLowestPacketNotConfirmedAcked. func (mr *MockSentPacketTrackerMockRecorder) GetLowestPacketNotConfirmedAcked() *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLowestPacketNotConfirmedAcked", reflect.TypeOf((*MockSentPacketTracker)(nil).GetLowestPacketNotConfirmedAcked)) return &MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall{Call: call} } // MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall wrap *gomock.Call type MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall) Return(arg0 protocol.PacketNumber) *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall) Do(f func() protocol.PacketNumber) *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall) DoAndReturn(f func() protocol.PacketNumber) *MockSentPacketTrackerGetLowestPacketNotConfirmedAckedCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedPacket mocks base method. func (m *MockSentPacketTracker) ReceivedPacket(arg0 protocol.EncryptionLevel) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedPacket", arg0) } // ReceivedPacket indicates an expected call of ReceivedPacket. func (mr *MockSentPacketTrackerMockRecorder) ReceivedPacket(arg0 any) *MockSentPacketTrackerReceivedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedPacket", reflect.TypeOf((*MockSentPacketTracker)(nil).ReceivedPacket), arg0) return &MockSentPacketTrackerReceivedPacketCall{Call: call} } // MockSentPacketTrackerReceivedPacketCall wrap *gomock.Call type MockSentPacketTrackerReceivedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketTrackerReceivedPacketCall) Return() *MockSentPacketTrackerReceivedPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketTrackerReceivedPacketCall) Do(f func(protocol.EncryptionLevel)) *MockSentPacketTrackerReceivedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketTrackerReceivedPacketCall) DoAndReturn(f func(protocol.EncryptionLevel)) *MockSentPacketTrackerReceivedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/mockgen.go000066400000000000000000000010441465664453100264700ustar00rootroot00000000000000//go:build gomock || generate package ackhandler //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_sent_packet_tracker_test.go github.com/quic-go/quic-go/internal/ackhandler SentPacketTracker" type SentPacketTracker = sentPacketTracker //go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package ackhandler -destination mock_ecn_handler_test.go github.com/quic-go/quic-go/internal/ackhandler ECNHandler" type ECNHandler = ecnHandler golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/packet.go000066400000000000000000000027231465664453100263210ustar00rootroot00000000000000package ackhandler import ( "sync" "time" "github.com/quic-go/quic-go/internal/protocol" ) // A Packet is a packet type packet struct { SendTime time.Time PacketNumber protocol.PacketNumber StreamFrames []StreamFrame Frames []Frame LargestAcked protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK Length protocol.ByteCount EncryptionLevel protocol.EncryptionLevel IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller. includedInBytesInFlight bool declaredLost bool skippedPacket bool } func (p *packet) outstanding() bool { return !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket } var packetPool = sync.Pool{New: func() any { return &packet{} }} func getPacket() *packet { p := packetPool.Get().(*packet) p.PacketNumber = 0 p.StreamFrames = nil p.Frames = nil p.LargestAcked = 0 p.Length = 0 p.EncryptionLevel = protocol.EncryptionLevel(0) p.SendTime = time.Time{} p.IsPathMTUProbePacket = false p.includedInBytesInFlight = false p.declaredLost = false p.skippedPacket = false return p } // We currently only return Packets back into the pool when they're acknowledged (not when they're lost). // This simplifies the code, and gives the vast majority of the performance benefit we can gain from using the pool. func putPacket(p *packet) { p.Frames = nil p.StreamFrames = nil packetPool.Put(p) } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/packet_number_generator.go000066400000000000000000000045101465664453100317330ustar00rootroot00000000000000package ackhandler import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) type packetNumberGenerator interface { Peek() protocol.PacketNumber // Pop pops the packet number. // It reports if the packet number (before the one just popped) was skipped. // It never skips more than one packet number in a row. Pop() (skipped bool, _ protocol.PacketNumber) } type sequentialPacketNumberGenerator struct { next protocol.PacketNumber } var _ packetNumberGenerator = &sequentialPacketNumberGenerator{} func newSequentialPacketNumberGenerator(initial protocol.PacketNumber) packetNumberGenerator { return &sequentialPacketNumberGenerator{next: initial} } func (p *sequentialPacketNumberGenerator) Peek() protocol.PacketNumber { return p.next } func (p *sequentialPacketNumberGenerator) Pop() (bool, protocol.PacketNumber) { next := p.next p.next++ return false, next } // The skippingPacketNumberGenerator generates the packet number for the next packet // it randomly skips a packet number every averagePeriod packets (on average). // It is guaranteed to never skip two consecutive packet numbers. type skippingPacketNumberGenerator struct { period protocol.PacketNumber maxPeriod protocol.PacketNumber next protocol.PacketNumber nextToSkip protocol.PacketNumber rng utils.Rand } var _ packetNumberGenerator = &skippingPacketNumberGenerator{} func newSkippingPacketNumberGenerator(initial, initialPeriod, maxPeriod protocol.PacketNumber) packetNumberGenerator { g := &skippingPacketNumberGenerator{ next: initial, period: initialPeriod, maxPeriod: maxPeriod, } g.generateNewSkip() return g } func (p *skippingPacketNumberGenerator) Peek() protocol.PacketNumber { if p.next == p.nextToSkip { return p.next + 1 } return p.next } func (p *skippingPacketNumberGenerator) Pop() (bool, protocol.PacketNumber) { next := p.next if p.next == p.nextToSkip { next++ p.next += 2 p.generateNewSkip() return true, next } p.next++ // generate a new packet number for the next packet return false, next } func (p *skippingPacketNumberGenerator) generateNewSkip() { // make sure that there are never two consecutive packet numbers that are skipped p.nextToSkip = p.next + 3 + protocol.PacketNumber(p.rng.Int31n(int32(2*p.period))) p.period = min(2*p.period, p.maxPeriod) } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/packet_number_generator_test.go000066400000000000000000000062351465664453100330000ustar00rootroot00000000000000package ackhandler import ( "fmt" "math" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Sequential Packet Number Generator", func() { It("generates sequential packet numbers", func() { const initialPN protocol.PacketNumber = 123 png := newSequentialPacketNumberGenerator(initialPN) for i := initialPN; i < initialPN+1000; i++ { Expect(png.Peek()).To(Equal(i)) Expect(png.Peek()).To(Equal(i)) skipNext, pn := png.Pop() Expect(skipNext).To(BeFalse()) Expect(pn).To(Equal(i)) } }) }) var _ = Describe("Skipping Packet Number Generator", func() { const initialPN protocol.PacketNumber = 8 const initialPeriod protocol.PacketNumber = 25 const maxPeriod protocol.PacketNumber = 300 It("uses a maximum period that is sufficiently small such that using a 32-bit random number is ok", func() { Expect(2 * protocol.SkipPacketMaxPeriod).To(BeNumerically("<", math.MaxInt32)) }) It("can be initialized to return any first packet number", func() { png := newSkippingPacketNumberGenerator(12345, initialPeriod, maxPeriod) _, pn := png.Pop() Expect(pn).To(Equal(protocol.PacketNumber(12345))) }) It("allows peeking", func() { png := newSkippingPacketNumberGenerator(initialPN, initialPeriod, maxPeriod).(*skippingPacketNumberGenerator) Expect(png.Peek()).To(Equal(initialPN)) Expect(png.Peek()).To(Equal(initialPN)) skipped, pn := png.Pop() Expect(pn).To(Equal(initialPN)) next := initialPN + 1 if skipped { next++ } Expect(png.Peek()).To(Equal(next)) Expect(png.Peek()).To(Equal(next)) }) It("skips a packet number", func() { png := newSkippingPacketNumberGenerator(initialPN, initialPeriod, maxPeriod) var last protocol.PacketNumber var skipped bool for i := 0; i < int(maxPeriod); i++ { didSkip, num := png.Pop() if didSkip { skipped = true _, nextNum := png.Pop() Expect(nextNum).To(Equal(num + 1)) break } if i != 0 { Expect(num).To(Equal(last + 1)) } last = num } Expect(skipped).To(BeTrue()) }) It("generates a new packet number to skip", func() { const rep = 2500 periods := make([][]protocol.PacketNumber, rep) expectedPeriods := []protocol.PacketNumber{25, 50, 100, 200, 300, 300, 300} for i := 0; i < rep; i++ { png := newSkippingPacketNumberGenerator(initialPN, initialPeriod, maxPeriod) lastSkip := initialPN for len(periods[i]) < len(expectedPeriods) { skipNext, next := png.Pop() if skipNext { skipped := next + 1 Expect(skipped).To(BeNumerically(">", lastSkip+1)) periods[i] = append(periods[i], skipped-lastSkip-1) lastSkip = skipped } } } for j := 0; j < len(expectedPeriods); j++ { var average float64 for i := 0; i < rep; i++ { average += float64(periods[i][j]) / float64(len(periods)) } fmt.Fprintf(GinkgoWriter, "Period %d: %.2f (expected %d)\n", j, average, expectedPeriods[j]) tolerance := protocol.PacketNumber(5) if t := expectedPeriods[j] / 10; t > tolerance { tolerance = t } Expect(average).To(BeNumerically("~", expectedPeriods[j]+1 /* we never skip two packet numbers at the same time */, tolerance)) } }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/received_packet_handler.go000066400000000000000000000077501465664453100316710ustar00rootroot00000000000000package ackhandler import ( "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) type receivedPacketHandler struct { sentPackets sentPacketTracker initialPackets *receivedPacketTracker handshakePackets *receivedPacketTracker appDataPackets appDataReceivedPacketTracker lowest1RTTPacket protocol.PacketNumber } var _ ReceivedPacketHandler = &receivedPacketHandler{} func newReceivedPacketHandler(sentPackets sentPacketTracker, logger utils.Logger) ReceivedPacketHandler { return &receivedPacketHandler{ sentPackets: sentPackets, initialPackets: newReceivedPacketTracker(), handshakePackets: newReceivedPacketTracker(), appDataPackets: *newAppDataReceivedPacketTracker(logger), lowest1RTTPacket: protocol.InvalidPacketNumber, } } func (h *receivedPacketHandler) ReceivedPacket( pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, ackEliciting bool, ) error { h.sentPackets.ReceivedPacket(encLevel) switch encLevel { case protocol.EncryptionInitial: return h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) case protocol.EncryptionHandshake: // The Handshake packet number space might already have been dropped as a result // of processing the CRYPTO frame that was contained in this packet. if h.handshakePackets == nil { return nil } return h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) case protocol.Encryption0RTT: if h.lowest1RTTPacket != protocol.InvalidPacketNumber && pn > h.lowest1RTTPacket { return fmt.Errorf("received packet number %d on a 0-RTT packet after receiving %d on a 1-RTT packet", pn, h.lowest1RTTPacket) } return h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting) case protocol.Encryption1RTT: if h.lowest1RTTPacket == protocol.InvalidPacketNumber || pn < h.lowest1RTTPacket { h.lowest1RTTPacket = pn } if err := h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { return err } h.appDataPackets.IgnoreBelow(h.sentPackets.GetLowestPacketNotConfirmedAcked()) return nil default: panic(fmt.Sprintf("received packet with unknown encryption level: %s", encLevel)) } } func (h *receivedPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { //nolint:exhaustive // 1-RTT packet number space is never dropped. switch encLevel { case protocol.EncryptionInitial: h.initialPackets = nil case protocol.EncryptionHandshake: h.handshakePackets = nil case protocol.Encryption0RTT: // Nothing to do here. // If we are rejecting 0-RTT, no 0-RTT packets will have been decrypted. default: panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel)) } } func (h *receivedPacketHandler) GetAlarmTimeout() time.Time { return h.appDataPackets.GetAlarmTimeout() } func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame { //nolint:exhaustive // 0-RTT packets can't contain ACK frames. switch encLevel { case protocol.EncryptionInitial: if h.initialPackets != nil { return h.initialPackets.GetAckFrame() } return nil case protocol.EncryptionHandshake: if h.handshakePackets != nil { return h.handshakePackets.GetAckFrame() } return nil case protocol.Encryption1RTT: return h.appDataPackets.GetAckFrame(onlyIfQueued) default: // 0-RTT packets can't contain ACK frames return nil } } func (h *receivedPacketHandler) IsPotentiallyDuplicate(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) bool { switch encLevel { case protocol.EncryptionInitial: if h.initialPackets != nil { return h.initialPackets.IsPotentiallyDuplicate(pn) } case protocol.EncryptionHandshake: if h.handshakePackets != nil { return h.handshakePackets.IsPotentiallyDuplicate(pn) } case protocol.Encryption0RTT, protocol.Encryption1RTT: return h.appDataPackets.IsPotentiallyDuplicate(pn) } panic("unexpected encryption level") } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/received_packet_handler_test.go000066400000000000000000000214111465664453100327160ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Received Packet Handler", func() { var handler ReceivedPacketHandler var sentPackets *MockSentPacketTracker BeforeEach(func() { sentPackets = NewMockSentPacketTracker(mockCtrl) handler = newReceivedPacketHandler(sentPackets, utils.DefaultLogger) }) It("generates ACKs for different packet number spaces", func() { sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().AnyTimes() sendTime := time.Now().Add(-time.Second) sentPackets.EXPECT().ReceivedPacket(protocol.EncryptionInitial).Times(2) sentPackets.EXPECT().ReceivedPacket(protocol.EncryptionHandshake).Times(2) sentPackets.EXPECT().ReceivedPacket(protocol.Encryption1RTT).Times(2) Expect(handler.ReceivedPacket(2, protocol.ECT0, protocol.EncryptionInitial, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(1, protocol.ECT1, protocol.EncryptionHandshake, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(5, protocol.ECNCE, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(3, protocol.ECT0, protocol.EncryptionInitial, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(2, protocol.ECT1, protocol.EncryptionHandshake, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(4, protocol.ECNCE, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) initialAck := handler.GetAckFrame(protocol.EncryptionInitial, true) Expect(initialAck).ToNot(BeNil()) Expect(initialAck.AckRanges).To(HaveLen(1)) Expect(initialAck.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 2, Largest: 3})) Expect(initialAck.DelayTime).To(BeZero()) Expect(initialAck.ECT0).To(BeEquivalentTo(2)) Expect(initialAck.ECT1).To(BeZero()) Expect(initialAck.ECNCE).To(BeZero()) handshakeAck := handler.GetAckFrame(protocol.EncryptionHandshake, true) Expect(handshakeAck).ToNot(BeNil()) Expect(handshakeAck.AckRanges).To(HaveLen(1)) Expect(handshakeAck.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 1, Largest: 2})) Expect(handshakeAck.DelayTime).To(BeZero()) Expect(handshakeAck.ECT0).To(BeZero()) Expect(handshakeAck.ECT1).To(BeEquivalentTo(2)) Expect(handshakeAck.ECNCE).To(BeZero()) oneRTTAck := handler.GetAckFrame(protocol.Encryption1RTT, true) Expect(oneRTTAck).ToNot(BeNil()) Expect(oneRTTAck.AckRanges).To(HaveLen(1)) Expect(oneRTTAck.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 4, Largest: 5})) Expect(oneRTTAck.DelayTime).To(BeNumerically("~", time.Second, 50*time.Millisecond)) Expect(oneRTTAck.ECT0).To(BeZero()) Expect(oneRTTAck.ECT1).To(BeZero()) Expect(oneRTTAck.ECNCE).To(BeEquivalentTo(2)) }) It("uses the same packet number space for 0-RTT and 1-RTT packets", func() { sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().AnyTimes() sentPackets.EXPECT().ReceivedPacket(protocol.Encryption0RTT) sentPackets.EXPECT().ReceivedPacket(protocol.Encryption1RTT) sendTime := time.Now().Add(-time.Second) Expect(handler.ReceivedPacket(2, protocol.ECNNon, protocol.Encryption0RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(3, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) ack := handler.GetAckFrame(protocol.Encryption1RTT, true) Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(HaveLen(1)) Expect(ack.AckRanges[0]).To(Equal(wire.AckRange{Smallest: 2, Largest: 3})) }) It("rejects 0-RTT packets with higher packet numbers than 1-RTT packets", func() { sentPackets.EXPECT().ReceivedPacket(gomock.Any()).Times(3) sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().AnyTimes() sendTime := time.Now() Expect(handler.ReceivedPacket(10, protocol.ECNNon, protocol.Encryption0RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(11, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(12, protocol.ECNNon, protocol.Encryption0RTT, sendTime, true)).To(MatchError("received packet number 12 on a 0-RTT packet after receiving 11 on a 1-RTT packet")) }) It("allows reordered 0-RTT packets", func() { sentPackets.EXPECT().ReceivedPacket(gomock.Any()).Times(3) sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().AnyTimes() sendTime := time.Now() Expect(handler.ReceivedPacket(10, protocol.ECNNon, protocol.Encryption0RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(12, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(11, protocol.ECNNon, protocol.Encryption0RTT, sendTime, true)).To(Succeed()) }) It("drops Initial packets", func() { sentPackets.EXPECT().ReceivedPacket(gomock.Any()).Times(2) sendTime := time.Now().Add(-time.Second) Expect(handler.ReceivedPacket(2, protocol.ECNNon, protocol.EncryptionInitial, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(1, protocol.ECNNon, protocol.EncryptionHandshake, sendTime, true)).To(Succeed()) Expect(handler.GetAckFrame(protocol.EncryptionInitial, true)).ToNot(BeNil()) handler.DropPackets(protocol.EncryptionInitial) Expect(handler.GetAckFrame(protocol.EncryptionInitial, true)).To(BeNil()) Expect(handler.GetAckFrame(protocol.EncryptionHandshake, true)).ToNot(BeNil()) }) It("drops Handshake packets", func() { sentPackets.EXPECT().ReceivedPacket(gomock.Any()).Times(2) sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().AnyTimes() sendTime := time.Now().Add(-time.Second) Expect(handler.ReceivedPacket(1, protocol.ECNNon, protocol.EncryptionHandshake, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(2, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) Expect(handler.GetAckFrame(protocol.EncryptionHandshake, true)).ToNot(BeNil()) handler.DropPackets(protocol.EncryptionInitial) Expect(handler.GetAckFrame(protocol.EncryptionHandshake, true)).To(BeNil()) Expect(handler.GetAckFrame(protocol.Encryption1RTT, true)).ToNot(BeNil()) }) It("does nothing when dropping 0-RTT packets", func() { handler.DropPackets(protocol.Encryption0RTT) }) It("drops old ACK ranges", func() { sentPackets.EXPECT().ReceivedPacket(gomock.Any()).AnyTimes() sendTime := time.Now() sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().Times(2) Expect(handler.ReceivedPacket(1, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) Expect(handler.ReceivedPacket(2, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) ack := handler.GetAckFrame(protocol.Encryption1RTT, true) Expect(ack).ToNot(BeNil()) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(1))) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(2))) sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked() Expect(handler.ReceivedPacket(3, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().Return(protocol.PacketNumber(2)) Expect(handler.ReceivedPacket(4, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) ack = handler.GetAckFrame(protocol.Encryption1RTT, true) Expect(ack).ToNot(BeNil()) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(2))) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(4))) }) It("says if packets are duplicates", func() { sendTime := time.Now() sentPackets.EXPECT().ReceivedPacket(gomock.Any()).AnyTimes() sentPackets.EXPECT().GetLowestPacketNotConfirmedAcked().AnyTimes() // Initial Expect(handler.IsPotentiallyDuplicate(3, protocol.EncryptionInitial)).To(BeFalse()) Expect(handler.ReceivedPacket(3, protocol.ECNNon, protocol.EncryptionInitial, sendTime, true)).To(Succeed()) Expect(handler.IsPotentiallyDuplicate(3, protocol.EncryptionInitial)).To(BeTrue()) // Handshake Expect(handler.IsPotentiallyDuplicate(3, protocol.EncryptionHandshake)).To(BeFalse()) Expect(handler.ReceivedPacket(3, protocol.ECNNon, protocol.EncryptionHandshake, sendTime, true)).To(Succeed()) Expect(handler.IsPotentiallyDuplicate(3, protocol.EncryptionHandshake)).To(BeTrue()) // 0-RTT Expect(handler.IsPotentiallyDuplicate(3, protocol.Encryption0RTT)).To(BeFalse()) Expect(handler.ReceivedPacket(3, protocol.ECNNon, protocol.Encryption0RTT, sendTime, true)).To(Succeed()) Expect(handler.IsPotentiallyDuplicate(3, protocol.Encryption0RTT)).To(BeTrue()) // 1-RTT Expect(handler.IsPotentiallyDuplicate(3, protocol.Encryption1RTT)).To(BeTrue()) Expect(handler.IsPotentiallyDuplicate(4, protocol.Encryption1RTT)).To(BeFalse()) Expect(handler.ReceivedPacket(4, protocol.ECNNon, protocol.Encryption1RTT, sendTime, true)).To(Succeed()) Expect(handler.IsPotentiallyDuplicate(4, protocol.Encryption1RTT)).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/received_packet_history.go000066400000000000000000000076431465664453100317560ustar00rootroot00000000000000package ackhandler import ( "slices" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) // interval is an interval from one PacketNumber to the other type interval struct { Start protocol.PacketNumber End protocol.PacketNumber } // The receivedPacketHistory stores if a packet number has already been received. // It generates ACK ranges which can be used to assemble an ACK frame. // It does not store packet contents. type receivedPacketHistory struct { ranges []interval // maximum length: protocol.MaxNumAckRanges deletedBelow protocol.PacketNumber } func newReceivedPacketHistory() *receivedPacketHistory { return &receivedPacketHistory{} } // ReceivedPacket registers a packet with PacketNumber p and updates the ranges func (h *receivedPacketHistory) ReceivedPacket(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ { // ignore delayed packets, if we already deleted the range if p < h.deletedBelow { return false } isNew := h.addToRanges(p) // Delete old ranges, if we're tracking too many of them. // This is a DoS defense against a peer that sends us too many gaps. if len(h.ranges) > protocol.MaxNumAckRanges { h.ranges = slices.Delete(h.ranges, 0, len(h.ranges)-protocol.MaxNumAckRanges) } return isNew } func (h *receivedPacketHistory) addToRanges(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ { if len(h.ranges) == 0 { h.ranges = append(h.ranges, interval{Start: p, End: p}) return true } for i := len(h.ranges) - 1; i >= 0; i-- { // p already included in an existing range. Nothing to do here if p >= h.ranges[i].Start && p <= h.ranges[i].End { return false } if h.ranges[i].End == p-1 { // extend a range at the end h.ranges[i].End = p return true } if h.ranges[i].Start == p+1 { // extend a range at the beginning h.ranges[i].Start = p if i > 0 && h.ranges[i-1].End+1 == h.ranges[i].Start { // merge two ranges h.ranges[i-1].End = h.ranges[i].End h.ranges = slices.Delete(h.ranges, i, i+1) } return true } // create a new range after the current one if p > h.ranges[i].End { h.ranges = slices.Insert(h.ranges, i+1, interval{Start: p, End: p}) return true } } // create a new range at the beginning h.ranges = slices.Insert(h.ranges, 0, interval{Start: p, End: p}) return true } // DeleteBelow deletes all entries below (but not including) p func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) { if p < h.deletedBelow { return } h.deletedBelow = p if len(h.ranges) == 0 { return } idx := -1 for i := 0; i < len(h.ranges); i++ { if h.ranges[i].End < p { // delete a whole range idx = i } else if p > h.ranges[i].Start && p <= h.ranges[i].End { h.ranges[i].Start = p break } else { // no ranges affected. Nothing to do break } } if idx >= 0 { h.ranges = slices.Delete(h.ranges, 0, idx+1) } } // AppendAckRanges appends to a slice of all AckRanges that can be used in an AckFrame func (h *receivedPacketHistory) AppendAckRanges(ackRanges []wire.AckRange) []wire.AckRange { for i := len(h.ranges) - 1; i >= 0; i-- { ackRanges = append(ackRanges, wire.AckRange{Smallest: h.ranges[i].Start, Largest: h.ranges[i].End}) } return ackRanges } func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange { ackRange := wire.AckRange{} if len(h.ranges) > 0 { ackRange.Smallest = h.ranges[len(h.ranges)-1].Start ackRange.Largest = h.ranges[len(h.ranges)-1].End } return ackRange } func (h *receivedPacketHistory) IsPotentiallyDuplicate(p protocol.PacketNumber) bool { if p < h.deletedBelow { return true } // Iterating over the slices is faster than using a binary search (using slices.BinarySearchFunc). for i := len(h.ranges) - 1; i >= 0; i-- { if p > h.ranges[i].End { return false } if p <= h.ranges[i].End && p >= h.ranges[i].Start { return true } } return false } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/received_packet_history_test.go000066400000000000000000000324561465664453100330150ustar00rootroot00000000000000package ackhandler import ( "fmt" "math/rand" "sort" "testing" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("receivedPacketHistory", func() { var hist *receivedPacketHistory BeforeEach(func() { hist = newReceivedPacketHistory() }) Context("ranges", func() { It("adds the first packet", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{{Start: 4, End: 4}})) }) It("doesn't care about duplicate packets", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(4)).To(BeFalse()) Expect(hist.ranges).To(Equal([]interval{{Start: 4, End: 4}})) }) It("adds a few consecutive packets", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{{Start: 4, End: 6}})) }) It("doesn't care about a duplicate packet contained in an existing range", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeFalse()) Expect(hist.ranges).To(Equal([]interval{{Start: 4, End: 6}})) }) It("extends a range at the front", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(3)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{{Start: 3, End: 4}})) }) It("creates a new range when a packet is lost", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{ {Start: 4, End: 4}, {Start: 6, End: 6}, })) }) It("creates a new range in between two ranges", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(10)).To(BeTrue()) Expect(hist.ranges).To(HaveLen(2)) Expect(hist.ReceivedPacket(7)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{ {Start: 4, End: 4}, {Start: 7, End: 7}, {Start: 10, End: 10}, })) }) It("creates a new range before an existing range for a belated packet", func() { Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{ {Start: 4, End: 4}, {Start: 6, End: 6}, })) }) It("extends a previous range at the end", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(7)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{ {Start: 4, End: 5}, {Start: 7, End: 7}, })) }) It("extends a range at the front", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(7)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{ {Start: 4, End: 4}, {Start: 6, End: 7}, })) }) It("closes a range", func() { Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ranges).To(HaveLen(2)) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{{Start: 4, End: 6}})) }) It("closes a range in the middle", func() { Expect(hist.ReceivedPacket(1)).To(BeTrue()) Expect(hist.ReceivedPacket(10)).To(BeTrue()) Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ranges).To(HaveLen(4)) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ranges).To(Equal([]interval{ {Start: 1, End: 1}, {Start: 4, End: 6}, {Start: 10, End: 10}, })) }) }) Context("deleting", func() { It("does nothing when the history is empty", func() { hist.DeleteBelow(5) Expect(hist.ranges).To(BeEmpty()) }) It("deletes a range", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(10)).To(BeTrue()) hist.DeleteBelow(6) Expect(hist.ranges).To(Equal([]interval{{Start: 10, End: 10}})) }) It("deletes multiple ranges", func() { Expect(hist.ReceivedPacket(1)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(10)).To(BeTrue()) hist.DeleteBelow(8) Expect(hist.ranges).To(Equal([]interval{{Start: 10, End: 10}})) }) It("adjusts a range, if packets are delete from an existing range", func() { Expect(hist.ReceivedPacket(3)).To(BeTrue()) Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(7)).To(BeTrue()) hist.DeleteBelow(5) Expect(hist.ranges).To(Equal([]interval{{Start: 5, End: 7}})) }) It("adjusts a range, if only one packet remains in the range", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(10)).To(BeTrue()) hist.DeleteBelow(5) Expect(hist.ranges).To(Equal([]interval{ {Start: 5, End: 5}, {Start: 10, End: 10}, })) }) It("keeps a one-packet range, if deleting up to the packet directly below", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) hist.DeleteBelow(4) Expect(hist.ranges).To(Equal([]interval{{Start: 4, End: 4}})) }) It("doesn't add delayed packets below deleted ranges", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) hist.DeleteBelow(5) Expect(hist.ranges).To(Equal([]interval{{Start: 5, End: 6}})) Expect(hist.ReceivedPacket(2)).To(BeFalse()) Expect(hist.ranges).To(Equal([]interval{{Start: 5, End: 6}})) }) It("doesn't create more than MaxNumAckRanges ranges", func() { for i := protocol.PacketNumber(0); i < protocol.MaxNumAckRanges; i++ { Expect(hist.ReceivedPacket(2 * i)).To(BeTrue()) } Expect(hist.ranges).To(HaveLen(protocol.MaxNumAckRanges)) Expect(hist.ranges[0]).To(Equal(interval{Start: 0, End: 0})) hist.ReceivedPacket(2*protocol.MaxNumAckRanges + 1000) // check that the oldest ACK range was deleted Expect(hist.ranges).To(HaveLen(protocol.MaxNumAckRanges)) Expect(hist.ranges[0]).To(Equal(interval{Start: 2, End: 2})) }) }) Context("ACK range export", func() { It("returns nil if there are no ranges", func() { Expect(hist.AppendAckRanges(nil)).To(BeEmpty()) }) It("gets a single ACK range", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) ackRanges := hist.AppendAckRanges(nil) Expect(ackRanges).To(Equal([]wire.AckRange{{Smallest: 4, Largest: 5}})) }) It("appends ACK ranges", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) ackRanges := hist.AppendAckRanges([]wire.AckRange{{Smallest: 1, Largest: 2}}) Expect(ackRanges).To(Equal([]wire.AckRange{ {Smallest: 1, Largest: 2}, {Smallest: 4, Largest: 5}, })) }) It("gets multiple ACK ranges", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(1)).To(BeTrue()) Expect(hist.ReceivedPacket(11)).To(BeTrue()) Expect(hist.ReceivedPacket(10)).To(BeTrue()) Expect(hist.ReceivedPacket(2)).To(BeTrue()) ackRanges := hist.AppendAckRanges(nil) Expect(ackRanges).To(Equal([]wire.AckRange{ {Smallest: 10, Largest: 11}, {Smallest: 4, Largest: 6}, {Smallest: 1, Largest: 2}, })) }) }) Context("Getting the highest ACK range", func() { It("returns the zero value if there are no ranges", func() { Expect(hist.GetHighestAckRange()).To(BeZero()) }) It("gets a single ACK range", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.GetHighestAckRange()).To(Equal(wire.AckRange{Smallest: 4, Largest: 5})) }) It("gets the highest of multiple ACK ranges", func() { Expect(hist.ReceivedPacket(3)).To(BeTrue()) Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(7)).To(BeTrue()) Expect(hist.GetHighestAckRange()).To(Equal(wire.AckRange{Smallest: 6, Largest: 7})) }) }) Context("duplicate detection", func() { It("doesn't declare the first packet a duplicate", func() { Expect(hist.IsPotentiallyDuplicate(5)).To(BeFalse()) }) It("detects a duplicate in a range", func() { hist.ReceivedPacket(4) hist.ReceivedPacket(5) hist.ReceivedPacket(6) Expect(hist.IsPotentiallyDuplicate(3)).To(BeFalse()) Expect(hist.IsPotentiallyDuplicate(4)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(5)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(6)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(7)).To(BeFalse()) }) It("detects a duplicate in multiple ranges", func() { hist.ReceivedPacket(4) hist.ReceivedPacket(5) hist.ReceivedPacket(8) hist.ReceivedPacket(9) Expect(hist.IsPotentiallyDuplicate(3)).To(BeFalse()) Expect(hist.IsPotentiallyDuplicate(4)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(5)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(6)).To(BeFalse()) Expect(hist.IsPotentiallyDuplicate(7)).To(BeFalse()) Expect(hist.IsPotentiallyDuplicate(8)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(9)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(10)).To(BeFalse()) }) It("says a packet is a potentially duplicate if the ranges were already deleted", func() { hist.ReceivedPacket(4) hist.ReceivedPacket(5) hist.ReceivedPacket(8) hist.ReceivedPacket(9) hist.ReceivedPacket(11) hist.DeleteBelow(8) Expect(hist.IsPotentiallyDuplicate(3)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(4)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(5)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(6)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(7)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(8)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(9)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(10)).To(BeFalse()) Expect(hist.IsPotentiallyDuplicate(11)).To(BeTrue()) Expect(hist.IsPotentiallyDuplicate(12)).To(BeFalse()) }) }) Context("randomized receiving", func() { It("receiving packets in a random order, with gaps", func() { packets := make(map[protocol.PacketNumber]int) // Make sure we never end up with more than protocol.MaxNumAckRanges ACK ranges, even // when we're receiving packets in a random order. const num = 2 * protocol.MaxNumAckRanges numLostPackets := rand.Intn(protocol.MaxNumAckRanges) numRcvdPackets := num - numLostPackets for i := 0; i < num; i++ { packets[protocol.PacketNumber(i)] = 0 } lostPackets := make([]protocol.PacketNumber, 0, numLostPackets) for len(lostPackets) < numLostPackets { p := protocol.PacketNumber(rand.Intn(num)) if _, ok := packets[p]; ok { lostPackets = append(lostPackets, p) delete(packets, p) } } sort.Slice(lostPackets, func(i, j int) bool { return lostPackets[i] < lostPackets[j] }) fmt.Fprintf(GinkgoWriter, "Losing packets: %v\n", lostPackets) ordered := make([]protocol.PacketNumber, 0, numRcvdPackets) for p := range packets { ordered = append(ordered, p) } rand.Shuffle(len(ordered), func(i, j int) { ordered[i], ordered[j] = ordered[j], ordered[i] }) fmt.Fprintf(GinkgoWriter, "Receiving packets: %v\n", ordered) for i, p := range ordered { Expect(hist.ReceivedPacket(p)).To(BeTrue()) // sometimes receive a duplicate if i > 0 && rand.Int()%5 == 0 { Expect(hist.ReceivedPacket(ordered[rand.Intn(i)])).To(BeFalse()) } } var counter int ackRanges := hist.AppendAckRanges(nil) fmt.Fprintf(GinkgoWriter, "ACK ranges: %v\n", ackRanges) Expect(len(ackRanges)).To(BeNumerically("<=", numLostPackets+1)) for _, ackRange := range ackRanges { for p := ackRange.Smallest; p <= ackRange.Largest; p++ { counter++ Expect(packets).To(HaveKey(p)) } } Expect(counter).To(Equal(numRcvdPackets)) }) }) }) func BenchmarkHistoryReceiveSequentialPackets(b *testing.B) { hist := newReceivedPacketHistory() for i := 0; i < b.N; i++ { hist.ReceivedPacket(protocol.PacketNumber(i)) } } // Packets are received sequentially, with occasional gaps func BenchmarkHistoryReceiveCommonCase(b *testing.B) { hist := newReceivedPacketHistory() var pn protocol.PacketNumber for i := 0; i < b.N; i++ { hist.ReceivedPacket(pn) pn++ if i%2000 == 0 { pn += 4 } } } func BenchmarkHistoryReceiveSequentialPacketsWithGaps(b *testing.B) { hist := newReceivedPacketHistory() for i := 0; i < b.N; i++ { hist.ReceivedPacket(protocol.PacketNumber(2 * i)) } } func BenchmarkHistoryReceiveReversePacketsWithGaps(b *testing.B) { hist := newReceivedPacketHistory() for i := 0; i < b.N; i++ { hist.ReceivedPacket(protocol.PacketNumber(2 * (b.N - i))) } } func BenchmarkHistoryIsDuplicate(b *testing.B) { b.ReportAllocs() hist := newReceivedPacketHistory() var pn protocol.PacketNumber for i := 0; i < protocol.MaxNumAckRanges; i++ { for j := 0; j < 5; j++ { hist.ReceivedPacket(pn) pn++ } pn += 5 // create a gap } b.ResetTimer() for i := 0; i < b.N; i++ { hist.IsPotentiallyDuplicate(protocol.PacketNumber(i) % pn) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/received_packet_tracker.go000066400000000000000000000147461465664453100317120ustar00rootroot00000000000000package ackhandler import ( "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) // The receivedPacketTracker tracks packets for the Initial and Handshake packet number space. // Every received packet is acknowledged immediately. type receivedPacketTracker struct { ect0, ect1, ecnce uint64 packetHistory receivedPacketHistory lastAck *wire.AckFrame hasNewAck bool // true as soon as we received an ack-eliciting new packet } func newReceivedPacketTracker() *receivedPacketTracker { return &receivedPacketTracker{packetHistory: *newReceivedPacketHistory()} } func (h *receivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { if isNew := h.packetHistory.ReceivedPacket(pn); !isNew { return fmt.Errorf("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet %d", pn) } //nolint:exhaustive // Only need to count ECT(0), ECT(1) and ECN-CE. switch ecn { case protocol.ECT0: h.ect0++ case protocol.ECT1: h.ect1++ case protocol.ECNCE: h.ecnce++ } if !ackEliciting { return nil } h.hasNewAck = true return nil } func (h *receivedPacketTracker) GetAckFrame() *wire.AckFrame { if !h.hasNewAck { return nil } // This function always returns the same ACK frame struct, filled with the most recent values. ack := h.lastAck if ack == nil { ack = &wire.AckFrame{} } ack.Reset() ack.ECT0 = h.ect0 ack.ECT1 = h.ect1 ack.ECNCE = h.ecnce ack.AckRanges = h.packetHistory.AppendAckRanges(ack.AckRanges) h.lastAck = ack h.hasNewAck = false return ack } func (h *receivedPacketTracker) IsPotentiallyDuplicate(pn protocol.PacketNumber) bool { return h.packetHistory.IsPotentiallyDuplicate(pn) } // number of ack-eliciting packets received before sending an ACK const packetsBeforeAck = 2 // The appDataReceivedPacketTracker tracks packets received in the Application Data packet number space. // It waits until at least 2 packets were received before queueing an ACK, or until the max_ack_delay was reached. type appDataReceivedPacketTracker struct { receivedPacketTracker largestObservedRcvdTime time.Time largestObserved protocol.PacketNumber ignoreBelow protocol.PacketNumber maxAckDelay time.Duration ackQueued bool // true if we need send a new ACK ackElicitingPacketsReceivedSinceLastAck int ackAlarm time.Time logger utils.Logger } func newAppDataReceivedPacketTracker(logger utils.Logger) *appDataReceivedPacketTracker { h := &appDataReceivedPacketTracker{ receivedPacketTracker: *newReceivedPacketTracker(), maxAckDelay: protocol.MaxAckDelay, logger: logger, } return h } func (h *appDataReceivedPacketTracker) ReceivedPacket(pn protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, ackEliciting bool) error { if err := h.receivedPacketTracker.ReceivedPacket(pn, ecn, rcvTime, ackEliciting); err != nil { return err } if pn >= h.largestObserved { h.largestObserved = pn h.largestObservedRcvdTime = rcvTime } if !ackEliciting { return nil } h.ackElicitingPacketsReceivedSinceLastAck++ isMissing := h.isMissing(pn) if !h.ackQueued && h.shouldQueueACK(pn, ecn, isMissing) { h.ackQueued = true h.ackAlarm = time.Time{} // cancel the ack alarm } if !h.ackQueued { // No ACK queued, but we'll need to acknowledge the packet after max_ack_delay. h.ackAlarm = rcvTime.Add(h.maxAckDelay) if h.logger.Debug() { h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay) } } return nil } // IgnoreBelow sets a lower limit for acknowledging packets. // Packets with packet numbers smaller than p will not be acked. func (h *appDataReceivedPacketTracker) IgnoreBelow(pn protocol.PacketNumber) { if pn <= h.ignoreBelow { return } h.ignoreBelow = pn h.packetHistory.DeleteBelow(pn) if h.logger.Debug() { h.logger.Debugf("\tIgnoring all packets below %d.", pn) } } // isMissing says if a packet was reported missing in the last ACK. func (h *appDataReceivedPacketTracker) isMissing(p protocol.PacketNumber) bool { if h.lastAck == nil || p < h.ignoreBelow { return false } return p < h.lastAck.LargestAcked() && !h.lastAck.AcksPacket(p) } func (h *appDataReceivedPacketTracker) hasNewMissingPackets() bool { if h.lastAck == nil { return false } highestRange := h.packetHistory.GetHighestAckRange() return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1 } func (h *appDataReceivedPacketTracker) shouldQueueACK(pn protocol.PacketNumber, ecn protocol.ECN, wasMissing bool) bool { // always acknowledge the first packet if h.lastAck == nil { h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.") return true } // Send an ACK if this packet was reported missing in an ACK sent before. // Ack decimation with reordering relies on the timer to send an ACK, but if // missing packets we reported in the previous ACK, send an ACK immediately. if wasMissing { if h.logger.Debug() { h.logger.Debugf("\tQueueing ACK because packet %d was missing before.", pn) } return true } // send an ACK every 2 ack-eliciting packets if h.ackElicitingPacketsReceivedSinceLastAck >= packetsBeforeAck { if h.logger.Debug() { h.logger.Debugf("\tQueueing ACK because packet %d packets were received after the last ACK (using initial threshold: %d).", h.ackElicitingPacketsReceivedSinceLastAck, packetsBeforeAck) } return true } // queue an ACK if there are new missing packets to report if h.hasNewMissingPackets() { h.logger.Debugf("\tQueuing ACK because there's a new missing packet to report.") return true } // queue an ACK if the packet was ECN-CE marked if ecn == protocol.ECNCE { h.logger.Debugf("\tQueuing ACK because the packet was ECN-CE marked.") return true } return false } func (h *appDataReceivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame { now := time.Now() if onlyIfQueued && !h.ackQueued { if h.ackAlarm.IsZero() || h.ackAlarm.After(now) { return nil } if h.logger.Debug() && !h.ackAlarm.IsZero() { h.logger.Debugf("Sending ACK because the ACK timer expired.") } } ack := h.receivedPacketTracker.GetAckFrame() if ack == nil { return nil } ack.DelayTime = max(0, now.Sub(h.largestObservedRcvdTime)) h.ackQueued = false h.ackAlarm = time.Time{} h.ackElicitingPacketsReceivedSinceLastAck = 0 return ack } func (h *appDataReceivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/received_packet_tracker_test.go000066400000000000000000000413421465664453100327410ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Received Packet Tracker", func() { var tracker *receivedPacketTracker BeforeEach(func() { tracker = newReceivedPacketTracker() }) It("acknowledges packets", func() { t := time.Now().Add(-10 * time.Second) Expect(tracker.ReceivedPacket(protocol.PacketNumber(3), protocol.ECNNon, t, true)).To(Succeed()) ack := tracker.GetAckFrame() Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(Equal([]wire.AckRange{{Smallest: 3, Largest: 3}})) Expect(ack.DelayTime).To(BeZero()) // now receive another packet Expect(tracker.ReceivedPacket(protocol.PacketNumber(4), protocol.ECNNon, t.Add(time.Second), true)).To(Succeed()) ack = tracker.GetAckFrame() Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(Equal([]wire.AckRange{{Smallest: 3, Largest: 4}})) Expect(ack.DelayTime).To(BeZero()) }) It("also acknowledges delayed packets", func() { t := time.Now().Add(-10 * time.Second) Expect(tracker.ReceivedPacket(protocol.PacketNumber(3), protocol.ECNNon, t, true)).To(Succeed()) ack := tracker.GetAckFrame() Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(3))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(3))) Expect(ack.DelayTime).To(BeZero()) // now receive another packet Expect(tracker.ReceivedPacket(protocol.PacketNumber(1), protocol.ECNNon, t.Add(time.Second), true)).To(Succeed()) ack = tracker.GetAckFrame() Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(HaveLen(2)) Expect(ack.AckRanges).To(ContainElement(wire.AckRange{Smallest: 1, Largest: 1})) Expect(ack.AckRanges).To(ContainElement(wire.AckRange{Smallest: 3, Largest: 3})) Expect(ack.DelayTime).To(BeZero()) }) It("doesn't trigger ACKs for non-ack-eliciting packets", func() { t := time.Now().Add(-10 * time.Second) Expect(tracker.ReceivedPacket(protocol.PacketNumber(3), protocol.ECNNon, t, false)).To(Succeed()) Expect(tracker.GetAckFrame()).To(BeNil()) Expect(tracker.ReceivedPacket(protocol.PacketNumber(4), protocol.ECNNon, t.Add(5*time.Second), false)).To(Succeed()) Expect(tracker.GetAckFrame()).To(BeNil()) Expect(tracker.ReceivedPacket(protocol.PacketNumber(5), protocol.ECNNon, t.Add(10*time.Second), true)).To(Succeed()) ack := tracker.GetAckFrame() Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(Equal([]wire.AckRange{{Smallest: 3, Largest: 5}})) }) }) var _ = Describe("Application Data Received Packet Tracker", func() { var tracker *appDataReceivedPacketTracker BeforeEach(func() { tracker = newAppDataReceivedPacketTracker(utils.DefaultLogger) }) Context("accepting packets", func() { It("saves the time when each packet arrived", func() { t := time.Now() Expect(tracker.ReceivedPacket(protocol.PacketNumber(3), protocol.ECNNon, t, true)).To(Succeed()) Expect(tracker.largestObservedRcvdTime).To(Equal(t)) }) It("updates the largestObserved and the largestObservedRcvdTime", func() { now := time.Now() tracker.largestObserved = 3 tracker.largestObservedRcvdTime = now.Add(-1 * time.Second) Expect(tracker.ReceivedPacket(5, protocol.ECNNon, now, true)).To(Succeed()) Expect(tracker.largestObserved).To(Equal(protocol.PacketNumber(5))) Expect(tracker.largestObservedRcvdTime).To(Equal(now)) }) It("doesn't update the largestObserved and the largestObservedRcvdTime for a belated packet", func() { now := time.Now() timestamp := now.Add(-1 * time.Second) tracker.largestObserved = 5 tracker.largestObservedRcvdTime = timestamp Expect(tracker.ReceivedPacket(4, protocol.ECNNon, now, true)).To(Succeed()) Expect(tracker.largestObserved).To(Equal(protocol.PacketNumber(5))) Expect(tracker.largestObservedRcvdTime).To(Equal(timestamp)) }) }) Context("ACKs", func() { Context("queueing ACKs", func() { // receives and gets ACKs for packet numbers 1 to 10 (including) receiveAndAck10Packets := func() { for i := 1; i <= 10; i++ { Expect(tracker.ReceivedPacket(protocol.PacketNumber(i), protocol.ECNNon, time.Time{}, true)).To(Succeed()) } Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) Expect(tracker.ackQueued).To(BeFalse()) } It("always queues an ACK for the first packet", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ackQueued).To(BeTrue()) Expect(tracker.GetAlarmTimeout()).To(BeZero()) Expect(tracker.GetAckFrame(true).DelayTime).To(BeNumerically("~", 0, time.Second)) }) It("works with packet number 0", func() { Expect(tracker.ReceivedPacket(0, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ackQueued).To(BeTrue()) Expect(tracker.GetAlarmTimeout()).To(BeZero()) Expect(tracker.GetAckFrame(true).DelayTime).To(BeNumerically("~", 0, time.Second)) }) It("sets ECN flags", func() { Expect(tracker.ReceivedPacket(0, protocol.ECT0, time.Now(), true)).To(Succeed()) pn := protocol.PacketNumber(1) for i := 0; i < 2; i++ { Expect(tracker.ReceivedPacket(pn, protocol.ECT1, time.Now(), true)).To(Succeed()) pn++ } for i := 0; i < 3; i++ { Expect(tracker.ReceivedPacket(pn, protocol.ECNCE, time.Now(), true)).To(Succeed()) pn++ } ack := tracker.GetAckFrame(false) Expect(ack.ECT0).To(BeEquivalentTo(1)) Expect(ack.ECT1).To(BeEquivalentTo(2)) Expect(ack.ECNCE).To(BeEquivalentTo(3)) }) It("queues an ACK for every second ack-eliciting packet", func() { receiveAndAck10Packets() p := protocol.PacketNumber(11) for i := 0; i <= 20; i++ { Expect(tracker.ReceivedPacket(p, protocol.ECNNon, time.Time{}, true)).To(Succeed()) Expect(tracker.ackQueued).To(BeFalse()) p++ Expect(tracker.ReceivedPacket(p, protocol.ECNNon, time.Time{}, true)).To(Succeed()) Expect(tracker.ackQueued).To(BeTrue()) p++ // dequeue the ACK frame Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) } }) It("resets the counter when a non-queued ACK frame is generated", func() { receiveAndAck10Packets() rcvTime := time.Now() Expect(tracker.ReceivedPacket(11, protocol.ECNNon, rcvTime, true)).To(Succeed()) Expect(tracker.GetAckFrame(false)).ToNot(BeNil()) Expect(tracker.ReceivedPacket(12, protocol.ECNNon, rcvTime, true)).To(Succeed()) Expect(tracker.GetAckFrame(true)).To(BeNil()) Expect(tracker.ReceivedPacket(13, protocol.ECNNon, rcvTime, true)).To(Succeed()) Expect(tracker.GetAckFrame(false)).ToNot(BeNil()) }) It("only sets the timer when receiving a ack-eliciting packets", func() { receiveAndAck10Packets() Expect(tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), false)).To(Succeed()) Expect(tracker.ackQueued).To(BeFalse()) Expect(tracker.GetAlarmTimeout()).To(BeZero()) rcvTime := time.Now().Add(10 * time.Millisecond) Expect(tracker.ReceivedPacket(12, protocol.ECNNon, rcvTime, true)).To(Succeed()) Expect(tracker.ackQueued).To(BeFalse()) Expect(tracker.GetAlarmTimeout()).To(Equal(rcvTime.Add(protocol.MaxAckDelay))) }) It("queues an ACK if the packet was ECN-CE marked", func() { receiveAndAck10Packets() Expect(tracker.ReceivedPacket(11, protocol.ECNCE, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(HaveLen(1)) Expect(ack.AckRanges[0].Largest).To(Equal(protocol.PacketNumber(11))) Expect(ack.ECNCE).To(BeEquivalentTo(1)) }) It("queues an ACK if it was reported missing before", func() { receiveAndAck10Packets() Expect(tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ReceivedPacket(13, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) // ACK: 1-11 and 13, missing: 12 Expect(ack).ToNot(BeNil()) Expect(ack.HasMissingRanges()).To(BeTrue()) Expect(tracker.ackQueued).To(BeFalse()) Expect(tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ackQueued).To(BeTrue()) }) It("doesn't recognize in-order packets as out-of-order after raising the threshold", func() { receiveAndAck10Packets() Expect(tracker.lastAck.LargestAcked()).To(Equal(protocol.PacketNumber(10))) Expect(tracker.ackQueued).To(BeFalse()) tracker.IgnoreBelow(11) Expect(tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.GetAckFrame(true)).To(BeNil()) }) It("recognizes out-of-order packets after raising the threshold", func() { receiveAndAck10Packets() Expect(tracker.lastAck.LargestAcked()).To(Equal(protocol.PacketNumber(10))) Expect(tracker.ackQueued).To(BeFalse()) tracker.IgnoreBelow(11) Expect(tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.AckRanges).To(Equal([]wire.AckRange{{Smallest: 12, Largest: 12}})) }) It("doesn't queue an ACK if for non-ack-eliciting packets arriving out-of-order", func() { receiveAndAck10Packets() Expect(tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.GetAckFrame(true)).To(BeNil()) Expect(tracker.ReceivedPacket(13, protocol.ECNNon, time.Now(), false)).To(Succeed()) // receive a non-ack-eliciting packet out-of-order Expect(tracker.GetAckFrame(true)).To(BeNil()) }) It("doesn't queue an ACK if packets arrive out-of-order, but haven't been acknowledged yet", func() { receiveAndAck10Packets() Expect(tracker.lastAck).ToNot(BeNil()) Expect(tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), false)).To(Succeed()) Expect(tracker.GetAckFrame(true)).To(BeNil()) // 11 is received out-of-order, but this hasn't been reported in an ACK frame yet Expect(tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.GetAckFrame(true)).To(BeNil()) }) }) Context("ACK generation", func() { It("generates an ACK for an ack-eliciting packet, if no ACK is queued yet", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) // The first packet is always acknowledged. Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) }) It("doesn't generate ACK for a non-ack-eliciting packet, if no ACK is queued yet", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) // The first packet is always acknowledged. Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) Expect(tracker.ReceivedPacket(2, protocol.ECNNon, time.Now(), false)).To(Succeed()) Expect(tracker.GetAckFrame(false)).To(BeNil()) Expect(tracker.ReceivedPacket(3, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(false) Expect(ack).ToNot(BeNil()) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(1))) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(3))) }) Context("for queued ACKs", func() { BeforeEach(func() { tracker.ackQueued = true }) It("generates a simple ACK frame", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ReceivedPacket(2, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(2))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(1))) Expect(ack.HasMissingRanges()).To(BeFalse()) }) It("generates an ACK for packet number 0", func() { Expect(tracker.ReceivedPacket(0, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(0))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(0))) Expect(ack.HasMissingRanges()).To(BeFalse()) }) It("sets the delay time", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ReceivedPacket(2, protocol.ECNNon, time.Now().Add(-1337*time.Millisecond), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.DelayTime).To(BeNumerically("~", 1337*time.Millisecond, 50*time.Millisecond)) }) It("uses a 0 delay time if the delay would be negative", func() { Expect(tracker.ReceivedPacket(0, protocol.ECNNon, time.Now().Add(time.Hour), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.DelayTime).To(BeZero()) }) It("saves the last sent ACK", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(tracker.lastAck).To(Equal(ack)) Expect(tracker.ReceivedPacket(2, protocol.ECNNon, time.Now(), true)).To(Succeed()) tracker.ackQueued = true ack = tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(tracker.lastAck).To(Equal(ack)) }) It("generates an ACK frame with missing packets", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ReceivedPacket(4, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(4))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(1))) Expect(ack.AckRanges).To(Equal([]wire.AckRange{ {Smallest: 4, Largest: 4}, {Smallest: 1, Largest: 1}, })) }) It("generates an ACK for packet number 0 and other packets", func() { Expect(tracker.ReceivedPacket(0, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) Expect(tracker.ReceivedPacket(3, protocol.ECNNon, time.Now(), true)).To(Succeed()) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(3))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(0))) Expect(ack.AckRanges).To(Equal([]wire.AckRange{ {Smallest: 3, Largest: 3}, {Smallest: 0, Largest: 1}, })) }) It("errors when called with an old packet", func() { tracker.IgnoreBelow(7) Expect(tracker.IsPotentiallyDuplicate(4)).To(BeTrue()) Expect(tracker.ReceivedPacket(4, protocol.ECNNon, time.Now(), true)).To(MatchError("recevedPacketTracker BUG: ReceivedPacket called for old / duplicate packet 4")) }) It("deletes packets from the packetHistory when a lower limit is set", func() { for i := 1; i <= 12; i++ { Expect(tracker.ReceivedPacket(protocol.PacketNumber(i), protocol.ECNNon, time.Now(), true)).To(Succeed()) } tracker.IgnoreBelow(7) // check that the packets were deleted from the receivedPacketHistory by checking the values in an ACK frame ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(12))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(7))) Expect(ack.HasMissingRanges()).To(BeFalse()) }) It("resets all counters needed for the ACK queueing decision when sending an ACK", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) tracker.ackAlarm = time.Now().Add(-time.Minute) Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) Expect(tracker.GetAlarmTimeout()).To(BeZero()) Expect(tracker.ackElicitingPacketsReceivedSinceLastAck).To(BeZero()) Expect(tracker.ackQueued).To(BeFalse()) }) It("doesn't generate an ACK when none is queued and the timer is not set", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) tracker.ackQueued = false tracker.ackAlarm = time.Time{} Expect(tracker.GetAckFrame(true)).To(BeNil()) }) It("doesn't generate an ACK when none is queued and the timer has not yet expired", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) tracker.ackQueued = false tracker.ackAlarm = time.Now().Add(time.Minute) Expect(tracker.GetAckFrame(true)).To(BeNil()) }) It("generates an ACK when the timer has expired", func() { Expect(tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true)).To(Succeed()) tracker.ackQueued = false tracker.ackAlarm = time.Now().Add(-time.Minute) Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) }) }) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/send_mode.go000066400000000000000000000023311465664453100270020ustar00rootroot00000000000000package ackhandler import "fmt" // The SendMode says what kind of packets can be sent. type SendMode uint8 const ( // SendNone means that no packets should be sent SendNone SendMode = iota // SendAck means an ACK-only packet should be sent SendAck // SendPTOInitial means that an Initial probe packet should be sent SendPTOInitial // SendPTOHandshake means that a Handshake probe packet should be sent SendPTOHandshake // SendPTOAppData means that an Application data probe packet should be sent SendPTOAppData // SendPacingLimited means that the pacer doesn't allow sending of a packet right now, // but will do in a little while. // The timestamp when sending is allowed again can be obtained via the SentPacketHandler.TimeUntilSend. SendPacingLimited // SendAny means that any packet should be sent SendAny ) func (s SendMode) String() string { switch s { case SendNone: return "none" case SendAck: return "ack" case SendPTOInitial: return "pto (Initial)" case SendPTOHandshake: return "pto (Handshake)" case SendPTOAppData: return "pto (Application Data)" case SendAny: return "any" case SendPacingLimited: return "pacing limited" default: return fmt.Sprintf("invalid send mode: %d", s) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/send_mode_test.go000066400000000000000000000012051465664453100300400ustar00rootroot00000000000000package ackhandler import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Send Mode", func() { It("has a string representation", func() { Expect(SendNone.String()).To(Equal("none")) Expect(SendAny.String()).To(Equal("any")) Expect(SendPacingLimited.String()).To(Equal("pacing limited")) Expect(SendAck.String()).To(Equal("ack")) Expect(SendPTOInitial.String()).To(Equal("pto (Initial)")) Expect(SendPTOHandshake.String()).To(Equal("pto (Handshake)")) Expect(SendPTOAppData.String()).To(Equal("pto (Application Data)")) Expect(SendMode(123).String()).To(Equal("invalid send mode: 123")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/sent_packet_handler.go000066400000000000000000000737201465664453100310540ustar00rootroot00000000000000package ackhandler import ( "errors" "fmt" "time" "github.com/quic-go/quic-go/internal/congestion" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" ) const ( // Maximum reordering in time space before time based loss detection considers a packet lost. // Specified as an RTT multiplier. timeThreshold = 9.0 / 8 // Maximum reordering in packets before packet threshold loss detection considers a packet lost. packetThreshold = 3 // Before validating the client's address, the server won't send more than 3x bytes than it received. amplificationFactor = 3 // We use Retry packets to derive an RTT estimate. Make sure we don't set the RTT to a super low value yet. minRTTAfterRetry = 5 * time.Millisecond // The PTO duration uses exponential backoff, but is truncated to a maximum value, as allowed by RFC 8961, section 4.4. maxPTODuration = 60 * time.Second ) type packetNumberSpace struct { history sentPacketHistory pns packetNumberGenerator lossTime time.Time lastAckElicitingPacketTime time.Time largestAcked protocol.PacketNumber largestSent protocol.PacketNumber } func newPacketNumberSpace(initialPN protocol.PacketNumber, isAppData bool) *packetNumberSpace { var pns packetNumberGenerator if isAppData { pns = newSkippingPacketNumberGenerator(initialPN, protocol.SkipPacketInitialPeriod, protocol.SkipPacketMaxPeriod) } else { pns = newSequentialPacketNumberGenerator(initialPN) } return &packetNumberSpace{ history: *newSentPacketHistory(isAppData), pns: pns, largestSent: protocol.InvalidPacketNumber, largestAcked: protocol.InvalidPacketNumber, } } type sentPacketHandler struct { initialPackets *packetNumberSpace handshakePackets *packetNumberSpace appDataPackets *packetNumberSpace // Do we know that the peer completed address validation yet? // Always true for the server. peerCompletedAddressValidation bool bytesReceived protocol.ByteCount bytesSent protocol.ByteCount // Have we validated the peer's address yet? // Always true for the client. peerAddressValidated bool handshakeConfirmed bool // lowestNotConfirmedAcked is the lowest packet number that we sent an ACK for, but haven't received confirmation, that this ACK actually arrived // example: we send an ACK for packets 90-100 with packet number 20 // once we receive an ACK from the peer for packet 20, the lowestNotConfirmedAcked is 101 // Only applies to the application-data packet number space. lowestNotConfirmedAcked protocol.PacketNumber ackedPackets []*packet // to avoid allocations in detectAndRemoveAckedPackets bytesInFlight protocol.ByteCount congestion congestion.SendAlgorithmWithDebugInfos rttStats *utils.RTTStats // The number of times a PTO has been sent without receiving an ack. ptoCount uint32 ptoMode SendMode // The number of PTO probe packets that should be sent. // Only applies to the application-data packet number space. numProbesToSend int // The alarm timeout alarm time.Time enableECN bool ecnTracker ecnHandler perspective protocol.Perspective tracer *logging.ConnectionTracer logger utils.Logger } var ( _ SentPacketHandler = &sentPacketHandler{} _ sentPacketTracker = &sentPacketHandler{} ) // clientAddressValidated indicates whether the address was validated beforehand by an address validation token. // If the address was validated, the amplification limit doesn't apply. It has no effect for a client. func newSentPacketHandler( initialPN protocol.PacketNumber, initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, clientAddressValidated bool, enableECN bool, pers protocol.Perspective, tracer *logging.ConnectionTracer, logger utils.Logger, ) *sentPacketHandler { congestion := congestion.NewCubicSender( congestion.DefaultClock{}, rttStats, initialMaxDatagramSize, true, // use Reno tracer, ) h := &sentPacketHandler{ peerCompletedAddressValidation: pers == protocol.PerspectiveServer, peerAddressValidated: pers == protocol.PerspectiveClient || clientAddressValidated, initialPackets: newPacketNumberSpace(initialPN, false), handshakePackets: newPacketNumberSpace(0, false), appDataPackets: newPacketNumberSpace(0, true), rttStats: rttStats, congestion: congestion, perspective: pers, tracer: tracer, logger: logger, } if enableECN { h.enableECN = true h.ecnTracker = newECNTracker(logger, tracer) } return h } func (h *sentPacketHandler) removeFromBytesInFlight(p *packet) { if p.includedInBytesInFlight { if p.Length > h.bytesInFlight { panic("negative bytes_in_flight") } h.bytesInFlight -= p.Length p.includedInBytesInFlight = false } } func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { // The server won't await address validation after the handshake is confirmed. // This applies even if we didn't receive an ACK for a Handshake packet. if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionHandshake { h.peerCompletedAddressValidation = true } // remove outstanding packets from bytes_in_flight if encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake { pnSpace := h.getPacketNumberSpace(encLevel) // We might already have dropped this packet number space. if pnSpace == nil { return } pnSpace.history.Iterate(func(p *packet) (bool, error) { h.removeFromBytesInFlight(p) return true, nil }) } // drop the packet history //nolint:exhaustive // Not every packet number space can be dropped. switch encLevel { case protocol.EncryptionInitial: h.initialPackets = nil case protocol.EncryptionHandshake: h.handshakePackets = nil case protocol.Encryption0RTT: // This function is only called when 0-RTT is rejected, // and not when the client drops 0-RTT keys when the handshake completes. // When 0-RTT is rejected, all application data sent so far becomes invalid. // Delete the packets from the history and remove them from bytes_in_flight. h.appDataPackets.history.Iterate(func(p *packet) (bool, error) { if p.EncryptionLevel != protocol.Encryption0RTT && !p.skippedPacket { return false, nil } h.removeFromBytesInFlight(p) h.appDataPackets.history.Remove(p.PacketNumber) return true, nil }) default: panic(fmt.Sprintf("Cannot drop keys for encryption level %s", encLevel)) } if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { h.tracer.UpdatedPTOCount(0) } h.ptoCount = 0 h.numProbesToSend = 0 h.ptoMode = SendNone h.setLossDetectionTimer() } func (h *sentPacketHandler) ReceivedBytes(n protocol.ByteCount) { wasAmplificationLimit := h.isAmplificationLimited() h.bytesReceived += n if wasAmplificationLimit && !h.isAmplificationLimited() { h.setLossDetectionTimer() } } func (h *sentPacketHandler) ReceivedPacket(l protocol.EncryptionLevel) { if h.perspective == protocol.PerspectiveServer && l == protocol.EncryptionHandshake && !h.peerAddressValidated { h.peerAddressValidated = true h.setLossDetectionTimer() } } func (h *sentPacketHandler) packetsInFlight() int { packetsInFlight := h.appDataPackets.history.Len() if h.handshakePackets != nil { packetsInFlight += h.handshakePackets.history.Len() } if h.initialPackets != nil { packetsInFlight += h.initialPackets.history.Len() } return packetsInFlight } func (h *sentPacketHandler) SentPacket( t time.Time, pn, largestAcked protocol.PacketNumber, streamFrames []StreamFrame, frames []Frame, encLevel protocol.EncryptionLevel, ecn protocol.ECN, size protocol.ByteCount, isPathMTUProbePacket bool, ) { h.bytesSent += size pnSpace := h.getPacketNumberSpace(encLevel) if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() { for p := max(0, pnSpace.largestSent+1); p < pn; p++ { h.logger.Debugf("Skipping packet number %d", p) } } pnSpace.largestSent = pn isAckEliciting := len(streamFrames) > 0 || len(frames) > 0 if isAckEliciting { pnSpace.lastAckElicitingPacketTime = t h.bytesInFlight += size if h.numProbesToSend > 0 { h.numProbesToSend-- } } h.congestion.OnPacketSent(t, h.bytesInFlight, pn, size, isAckEliciting) if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { h.ecnTracker.SentPacket(pn, ecn) } if !isAckEliciting { pnSpace.history.SentNonAckElicitingPacket(pn) if !h.peerCompletedAddressValidation { h.setLossDetectionTimer() } return } p := getPacket() p.SendTime = t p.PacketNumber = pn p.EncryptionLevel = encLevel p.Length = size p.LargestAcked = largestAcked p.StreamFrames = streamFrames p.Frames = frames p.IsPathMTUProbePacket = isPathMTUProbePacket p.includedInBytesInFlight = true pnSpace.history.SentAckElicitingPacket(p) if h.tracer != nil && h.tracer.UpdatedMetrics != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } h.setLossDetectionTimer() } func (h *sentPacketHandler) getPacketNumberSpace(encLevel protocol.EncryptionLevel) *packetNumberSpace { switch encLevel { case protocol.EncryptionInitial: return h.initialPackets case protocol.EncryptionHandshake: return h.handshakePackets case protocol.Encryption0RTT, protocol.Encryption1RTT: return h.appDataPackets default: panic("invalid packet number space") } } func (h *sentPacketHandler) ReceivedAck(ack *wire.AckFrame, encLevel protocol.EncryptionLevel, rcvTime time.Time) (bool /* contained 1-RTT packet */, error) { pnSpace := h.getPacketNumberSpace(encLevel) largestAcked := ack.LargestAcked() if largestAcked > pnSpace.largestSent { return false, &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received ACK for an unsent packet", } } // Servers complete address validation when a protected packet is received. if h.perspective == protocol.PerspectiveClient && !h.peerCompletedAddressValidation && (encLevel == protocol.EncryptionHandshake || encLevel == protocol.Encryption1RTT) { h.peerCompletedAddressValidation = true h.logger.Debugf("Peer doesn't await address validation any longer.") // Make sure that the timer is reset, even if this ACK doesn't acknowledge any (ack-eliciting) packets. h.setLossDetectionTimer() } priorInFlight := h.bytesInFlight ackedPackets, err := h.detectAndRemoveAckedPackets(ack, encLevel) if err != nil || len(ackedPackets) == 0 { return false, err } // update the RTT, if the largest acked is newly acknowledged if len(ackedPackets) > 0 { if p := ackedPackets[len(ackedPackets)-1]; p.PacketNumber == ack.LargestAcked() { // don't use the ack delay for Initial and Handshake packets var ackDelay time.Duration if encLevel == protocol.Encryption1RTT { ackDelay = min(ack.DelayTime, h.rttStats.MaxAckDelay()) } h.rttStats.UpdateRTT(rcvTime.Sub(p.SendTime), ackDelay, rcvTime) if h.logger.Debug() { h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) } h.congestion.MaybeExitSlowStart() } } // Only inform the ECN tracker about new 1-RTT ACKs if the ACK increases the largest acked. if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil && largestAcked > pnSpace.largestAcked { congested := h.ecnTracker.HandleNewlyAcked(ackedPackets, int64(ack.ECT0), int64(ack.ECT1), int64(ack.ECNCE)) if congested { h.congestion.OnCongestionEvent(largestAcked, 0, priorInFlight) } } pnSpace.largestAcked = max(pnSpace.largestAcked, largestAcked) if err := h.detectLostPackets(rcvTime, encLevel); err != nil { return false, err } var acked1RTTPacket bool for _, p := range ackedPackets { if p.includedInBytesInFlight && !p.declaredLost { h.congestion.OnPacketAcked(p.PacketNumber, p.Length, priorInFlight, rcvTime) } if p.EncryptionLevel == protocol.Encryption1RTT { acked1RTTPacket = true } h.removeFromBytesInFlight(p) putPacket(p) } // After this point, we must not use ackedPackets any longer! // We've already returned the buffers. ackedPackets = nil //nolint:ineffassign // This is just to be on the safe side. // Reset the pto_count unless the client is unsure if the server has validated the client's address. if h.peerCompletedAddressValidation { if h.tracer != nil && h.tracer.UpdatedPTOCount != nil && h.ptoCount != 0 { h.tracer.UpdatedPTOCount(0) } h.ptoCount = 0 } h.numProbesToSend = 0 if h.tracer != nil && h.tracer.UpdatedMetrics != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } h.setLossDetectionTimer() return acked1RTTPacket, nil } func (h *sentPacketHandler) GetLowestPacketNotConfirmedAcked() protocol.PacketNumber { return h.lowestNotConfirmedAcked } // Packets are returned in ascending packet number order. func (h *sentPacketHandler) detectAndRemoveAckedPackets(ack *wire.AckFrame, encLevel protocol.EncryptionLevel) ([]*packet, error) { pnSpace := h.getPacketNumberSpace(encLevel) h.ackedPackets = h.ackedPackets[:0] ackRangeIndex := 0 lowestAcked := ack.LowestAcked() largestAcked := ack.LargestAcked() err := pnSpace.history.Iterate(func(p *packet) (bool, error) { // Ignore packets below the lowest acked if p.PacketNumber < lowestAcked { return true, nil } // Break after largest acked is reached if p.PacketNumber > largestAcked { return false, nil } if ack.HasMissingRanges() { ackRange := ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex] for p.PacketNumber > ackRange.Largest && ackRangeIndex < len(ack.AckRanges)-1 { ackRangeIndex++ ackRange = ack.AckRanges[len(ack.AckRanges)-1-ackRangeIndex] } if p.PacketNumber < ackRange.Smallest { // packet not contained in ACK range return true, nil } if p.PacketNumber > ackRange.Largest { return false, fmt.Errorf("BUG: ackhandler would have acked wrong packet %d, while evaluating range %d -> %d", p.PacketNumber, ackRange.Smallest, ackRange.Largest) } } if p.skippedPacket { return false, &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: fmt.Sprintf("received an ACK for skipped packet number: %d (%s)", p.PacketNumber, encLevel), } } h.ackedPackets = append(h.ackedPackets, p) return true, nil }) if h.logger.Debug() && len(h.ackedPackets) > 0 { pns := make([]protocol.PacketNumber, len(h.ackedPackets)) for i, p := range h.ackedPackets { pns[i] = p.PacketNumber } h.logger.Debugf("\tnewly acked packets (%d): %d", len(pns), pns) } for _, p := range h.ackedPackets { if p.LargestAcked != protocol.InvalidPacketNumber && encLevel == protocol.Encryption1RTT { h.lowestNotConfirmedAcked = max(h.lowestNotConfirmedAcked, p.LargestAcked+1) } for _, f := range p.Frames { if f.Handler != nil { f.Handler.OnAcked(f.Frame) } } for _, f := range p.StreamFrames { if f.Handler != nil { f.Handler.OnAcked(f.Frame) } } if err := pnSpace.history.Remove(p.PacketNumber); err != nil { return nil, err } if h.tracer != nil && h.tracer.AcknowledgedPacket != nil { h.tracer.AcknowledgedPacket(encLevel, p.PacketNumber) } } return h.ackedPackets, err } func (h *sentPacketHandler) getLossTimeAndSpace() (time.Time, protocol.EncryptionLevel) { var encLevel protocol.EncryptionLevel var lossTime time.Time if h.initialPackets != nil { lossTime = h.initialPackets.lossTime encLevel = protocol.EncryptionInitial } if h.handshakePackets != nil && (lossTime.IsZero() || (!h.handshakePackets.lossTime.IsZero() && h.handshakePackets.lossTime.Before(lossTime))) { lossTime = h.handshakePackets.lossTime encLevel = protocol.EncryptionHandshake } if lossTime.IsZero() || (!h.appDataPackets.lossTime.IsZero() && h.appDataPackets.lossTime.Before(lossTime)) { lossTime = h.appDataPackets.lossTime encLevel = protocol.Encryption1RTT } return lossTime, encLevel } func (h *sentPacketHandler) getScaledPTO(includeMaxAckDelay bool) time.Duration { pto := h.rttStats.PTO(includeMaxAckDelay) << h.ptoCount if pto > maxPTODuration || pto <= 0 { return maxPTODuration } return pto } // same logic as getLossTimeAndSpace, but for lastAckElicitingPacketTime instead of lossTime func (h *sentPacketHandler) getPTOTimeAndSpace() (pto time.Time, encLevel protocol.EncryptionLevel, ok bool) { // We only send application data probe packets once the handshake is confirmed, // because before that, we don't have the keys to decrypt ACKs sent in 1-RTT packets. if !h.handshakeConfirmed && !h.hasOutstandingCryptoPackets() { if h.peerCompletedAddressValidation { return } t := time.Now().Add(h.getScaledPTO(false)) if h.initialPackets != nil { return t, protocol.EncryptionInitial, true } return t, protocol.EncryptionHandshake, true } if h.initialPackets != nil { encLevel = protocol.EncryptionInitial if t := h.initialPackets.lastAckElicitingPacketTime; !t.IsZero() { pto = t.Add(h.getScaledPTO(false)) } } if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() { t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(false)) if pto.IsZero() || (!t.IsZero() && t.Before(pto)) { pto = t encLevel = protocol.EncryptionHandshake } } if h.handshakeConfirmed && !h.appDataPackets.lastAckElicitingPacketTime.IsZero() { t := h.appDataPackets.lastAckElicitingPacketTime.Add(h.getScaledPTO(true)) if pto.IsZero() || (!t.IsZero() && t.Before(pto)) { pto = t encLevel = protocol.Encryption1RTT } } return pto, encLevel, true } func (h *sentPacketHandler) hasOutstandingCryptoPackets() bool { if h.initialPackets != nil && h.initialPackets.history.HasOutstandingPackets() { return true } if h.handshakePackets != nil && h.handshakePackets.history.HasOutstandingPackets() { return true } return false } func (h *sentPacketHandler) hasOutstandingPackets() bool { return h.appDataPackets.history.HasOutstandingPackets() || h.hasOutstandingCryptoPackets() } func (h *sentPacketHandler) setLossDetectionTimer() { oldAlarm := h.alarm // only needed in case tracing is enabled lossTime, encLevel := h.getLossTimeAndSpace() if !lossTime.IsZero() { // Early retransmit timer or time loss detection. h.alarm = lossTime if h.tracer != nil && h.tracer.SetLossTimer != nil && h.alarm != oldAlarm { h.tracer.SetLossTimer(logging.TimerTypeACK, encLevel, h.alarm) } return } // Cancel the alarm if amplification limited. if h.isAmplificationLimited() { h.alarm = time.Time{} if !oldAlarm.IsZero() { h.logger.Debugf("Canceling loss detection timer. Amplification limited.") if h.tracer != nil && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } return } // Cancel the alarm if no packets are outstanding if !h.hasOutstandingPackets() && h.peerCompletedAddressValidation { h.alarm = time.Time{} if !oldAlarm.IsZero() { h.logger.Debugf("Canceling loss detection timer. No packets in flight.") if h.tracer != nil && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } return } // PTO alarm ptoTime, encLevel, ok := h.getPTOTimeAndSpace() if !ok { if !oldAlarm.IsZero() { h.alarm = time.Time{} h.logger.Debugf("Canceling loss detection timer. No PTO needed..") if h.tracer != nil && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } return } h.alarm = ptoTime if h.tracer != nil && h.tracer.SetLossTimer != nil && h.alarm != oldAlarm { h.tracer.SetLossTimer(logging.TimerTypePTO, encLevel, h.alarm) } } func (h *sentPacketHandler) detectLostPackets(now time.Time, encLevel protocol.EncryptionLevel) error { pnSpace := h.getPacketNumberSpace(encLevel) pnSpace.lossTime = time.Time{} maxRTT := float64(max(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT())) lossDelay := time.Duration(timeThreshold * maxRTT) // Minimum time of granularity before packets are deemed lost. lossDelay = max(lossDelay, protocol.TimerGranularity) // Packets sent before this time are deemed lost. lostSendTime := now.Add(-lossDelay) priorInFlight := h.bytesInFlight return pnSpace.history.Iterate(func(p *packet) (bool, error) { if p.PacketNumber > pnSpace.largestAcked { return false, nil } var packetLost bool if p.SendTime.Before(lostSendTime) { packetLost = true if !p.skippedPacket { if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber) } if h.tracer != nil && h.tracer.LostPacket != nil { h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossTimeThreshold) } } } else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold { packetLost = true if !p.skippedPacket { if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber) } if h.tracer != nil && h.tracer.LostPacket != nil { h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossReorderingThreshold) } } } else if pnSpace.lossTime.IsZero() { // Note: This conditional is only entered once per call lossTime := p.SendTime.Add(lossDelay) if h.logger.Debug() { h.logger.Debugf("\tsetting loss timer for packet %d (%s) to %s (in %s)", p.PacketNumber, encLevel, lossDelay, lossTime) } pnSpace.lossTime = lossTime } if packetLost { pnSpace.history.DeclareLost(p.PacketNumber) if !p.skippedPacket { // the bytes in flight need to be reduced no matter if the frames in this packet will be retransmitted h.removeFromBytesInFlight(p) h.queueFramesForRetransmission(p) if !p.IsPathMTUProbePacket { h.congestion.OnCongestionEvent(p.PacketNumber, p.Length, priorInFlight) } if encLevel == protocol.Encryption1RTT && h.ecnTracker != nil { h.ecnTracker.LostPacket(p.PacketNumber) } } } return true, nil }) } func (h *sentPacketHandler) OnLossDetectionTimeout() error { defer h.setLossDetectionTimer() earliestLossTime, encLevel := h.getLossTimeAndSpace() if !earliestLossTime.IsZero() { if h.logger.Debug() { h.logger.Debugf("Loss detection alarm fired in loss timer mode. Loss time: %s", earliestLossTime) } if h.tracer != nil && h.tracer.LossTimerExpired != nil { h.tracer.LossTimerExpired(logging.TimerTypeACK, encLevel) } // Early retransmit or time loss detection return h.detectLostPackets(time.Now(), encLevel) } // PTO // When all outstanding are acknowledged, the alarm is canceled in // setLossDetectionTimer. This doesn't reset the timer in the session though. // When OnAlarm is called, we therefore need to make sure that there are // actually packets outstanding. if h.bytesInFlight == 0 && !h.peerCompletedAddressValidation { h.ptoCount++ h.numProbesToSend++ if h.initialPackets != nil { h.ptoMode = SendPTOInitial } else if h.handshakePackets != nil { h.ptoMode = SendPTOHandshake } else { return errors.New("sentPacketHandler BUG: PTO fired, but bytes_in_flight is 0 and Initial and Handshake already dropped") } return nil } _, encLevel, ok := h.getPTOTimeAndSpace() if !ok { return nil } if ps := h.getPacketNumberSpace(encLevel); !ps.history.HasOutstandingPackets() && !h.peerCompletedAddressValidation { return nil } h.ptoCount++ if h.logger.Debug() { h.logger.Debugf("Loss detection alarm for %s fired in PTO mode. PTO count: %d", encLevel, h.ptoCount) } if h.tracer != nil { if h.tracer.LossTimerExpired != nil { h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel) } if h.tracer.UpdatedPTOCount != nil { h.tracer.UpdatedPTOCount(h.ptoCount) } } h.numProbesToSend += 2 //nolint:exhaustive // We never arm a PTO timer for 0-RTT packets. switch encLevel { case protocol.EncryptionInitial: h.ptoMode = SendPTOInitial case protocol.EncryptionHandshake: h.ptoMode = SendPTOHandshake case protocol.Encryption1RTT: // skip a packet number in order to elicit an immediate ACK pn := h.PopPacketNumber(protocol.Encryption1RTT) h.getPacketNumberSpace(protocol.Encryption1RTT).history.SkippedPacket(pn) h.ptoMode = SendPTOAppData default: return fmt.Errorf("PTO timer in unexpected encryption level: %s", encLevel) } return nil } func (h *sentPacketHandler) GetLossDetectionTimeout() time.Time { return h.alarm } func (h *sentPacketHandler) ECNMode(isShortHeaderPacket bool) protocol.ECN { if !h.enableECN { return protocol.ECNUnsupported } if !isShortHeaderPacket { return protocol.ECNNon } return h.ecnTracker.Mode() } func (h *sentPacketHandler) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) { pnSpace := h.getPacketNumberSpace(encLevel) pn := pnSpace.pns.Peek() // See section 17.1 of RFC 9000. return pn, protocol.GetPacketNumberLengthForHeader(pn, pnSpace.largestAcked) } func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber { pnSpace := h.getPacketNumberSpace(encLevel) skipped, pn := pnSpace.pns.Pop() if skipped { skippedPN := pn - 1 pnSpace.history.SkippedPacket(skippedPN) if h.logger.Debug() { h.logger.Debugf("Skipping packet number %d", skippedPN) } } return pn } func (h *sentPacketHandler) SendMode(now time.Time) SendMode { numTrackedPackets := h.appDataPackets.history.Len() if h.initialPackets != nil { numTrackedPackets += h.initialPackets.history.Len() } if h.handshakePackets != nil { numTrackedPackets += h.handshakePackets.history.Len() } if h.isAmplificationLimited() { h.logger.Debugf("Amplification window limited. Received %d bytes, already sent out %d bytes", h.bytesReceived, h.bytesSent) return SendNone } // Don't send any packets if we're keeping track of the maximum number of packets. // Note that since MaxOutstandingSentPackets is smaller than MaxTrackedSentPackets, // we will stop sending out new data when reaching MaxOutstandingSentPackets, // but still allow sending of retransmissions and ACKs. if numTrackedPackets >= protocol.MaxTrackedSentPackets { if h.logger.Debug() { h.logger.Debugf("Limited by the number of tracked packets: tracking %d packets, maximum %d", numTrackedPackets, protocol.MaxTrackedSentPackets) } return SendNone } if h.numProbesToSend > 0 { return h.ptoMode } // Only send ACKs if we're congestion limited. if !h.congestion.CanSend(h.bytesInFlight) { if h.logger.Debug() { h.logger.Debugf("Congestion limited: bytes in flight %d, window %d", h.bytesInFlight, h.congestion.GetCongestionWindow()) } return SendAck } if numTrackedPackets >= protocol.MaxOutstandingSentPackets { if h.logger.Debug() { h.logger.Debugf("Max outstanding limited: tracking %d packets, maximum: %d", numTrackedPackets, protocol.MaxOutstandingSentPackets) } return SendAck } if !h.congestion.HasPacingBudget(now) { return SendPacingLimited } return SendAny } func (h *sentPacketHandler) TimeUntilSend() time.Time { return h.congestion.TimeUntilSend(h.bytesInFlight) } func (h *sentPacketHandler) SetMaxDatagramSize(s protocol.ByteCount) { h.congestion.SetMaxDatagramSize(s) } func (h *sentPacketHandler) isAmplificationLimited() bool { if h.peerAddressValidated { return false } return h.bytesSent >= amplificationFactor*h.bytesReceived } func (h *sentPacketHandler) QueueProbePacket(encLevel protocol.EncryptionLevel) bool { pnSpace := h.getPacketNumberSpace(encLevel) p := pnSpace.history.FirstOutstanding() if p == nil { return false } h.queueFramesForRetransmission(p) // TODO: don't declare the packet lost here. // Keep track of acknowledged frames instead. h.removeFromBytesInFlight(p) pnSpace.history.DeclareLost(p.PacketNumber) return true } func (h *sentPacketHandler) queueFramesForRetransmission(p *packet) { if len(p.Frames) == 0 && len(p.StreamFrames) == 0 { panic("no frames") } for _, f := range p.Frames { if f.Handler != nil { f.Handler.OnLost(f.Frame) } } for _, f := range p.StreamFrames { if f.Handler != nil { f.Handler.OnLost(f.Frame) } } p.StreamFrames = nil p.Frames = nil } func (h *sentPacketHandler) ResetForRetry(now time.Time) error { h.bytesInFlight = 0 var firstPacketSendTime time.Time h.initialPackets.history.Iterate(func(p *packet) (bool, error) { if firstPacketSendTime.IsZero() { firstPacketSendTime = p.SendTime } if p.declaredLost || p.skippedPacket { return true, nil } h.queueFramesForRetransmission(p) return true, nil }) // All application data packets sent at this point are 0-RTT packets. // In the case of a Retry, we can assume that the server dropped all of them. h.appDataPackets.history.Iterate(func(p *packet) (bool, error) { if !p.declaredLost && !p.skippedPacket { h.queueFramesForRetransmission(p) } return true, nil }) // Only use the Retry to estimate the RTT if we didn't send any retransmission for the Initial. // Otherwise, we don't know which Initial the Retry was sent in response to. if h.ptoCount == 0 { // Don't set the RTT to a value lower than 5ms here. h.rttStats.UpdateRTT(max(minRTTAfterRetry, now.Sub(firstPacketSendTime)), 0, now) if h.logger.Debug() { h.logger.Debugf("\tupdated RTT: %s (σ: %s)", h.rttStats.SmoothedRTT(), h.rttStats.MeanDeviation()) } if h.tracer != nil && h.tracer.UpdatedMetrics != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } } h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Peek(), false) h.appDataPackets = newPacketNumberSpace(h.appDataPackets.pns.Peek(), true) oldAlarm := h.alarm h.alarm = time.Time{} if h.tracer != nil { if h.tracer.UpdatedPTOCount != nil { h.tracer.UpdatedPTOCount(0) } if !oldAlarm.IsZero() && h.tracer.LossTimerCanceled != nil { h.tracer.LossTimerCanceled() } } h.ptoCount = 0 return nil } func (h *sentPacketHandler) SetHandshakeConfirmed() { if h.initialPackets != nil { panic("didn't drop initial correctly") } if h.handshakePackets != nil { panic("didn't drop handshake correctly") } h.handshakeConfirmed = true // We don't send PTOs for application data packets before the handshake completes. // Make sure the timer is armed now, if necessary. h.setLossDetectionTimer() } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/sent_packet_handler_test.go000066400000000000000000002064371465664453100321160ustar00rootroot00000000000000package ackhandler import ( "fmt" "time" "github.com/quic-go/quic-go/internal/mocks" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) type customFrameHandler struct { onLost, onAcked func(wire.Frame) } func (h *customFrameHandler) OnLost(f wire.Frame) { if h.onLost != nil { h.onLost(f) } } func (h *customFrameHandler) OnAcked(f wire.Frame) { if h.onAcked != nil { h.onAcked(f) } } var _ = Describe("SentPacketHandler", func() { var ( handler *sentPacketHandler streamFrame wire.StreamFrame lostPackets []protocol.PacketNumber perspective protocol.Perspective ) BeforeEach(func() { perspective = protocol.PerspectiveServer }) JustBeforeEach(func() { lostPackets = nil rttStats := utils.NewRTTStats() handler = newSentPacketHandler(42, protocol.InitialPacketSize, rttStats, false, false, perspective, nil, utils.DefaultLogger) streamFrame = wire.StreamFrame{ StreamID: 5, Data: []byte{0x13, 0x37}, } }) getPacket := func(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) *packet { for _, p := range handler.getPacketNumberSpace(encLevel).history.packets { if p != nil && p.PacketNumber == pn { return p } } return nil } ackElicitingPacket := func(p *packet) *packet { if p.EncryptionLevel == 0 { p.EncryptionLevel = protocol.Encryption1RTT } if p.Length == 0 { p.Length = 1 } if p.SendTime.IsZero() { p.SendTime = time.Now() } if len(p.Frames) == 0 { p.Frames = []Frame{ {Frame: &wire.PingFrame{}, Handler: &customFrameHandler{ onLost: func(wire.Frame) { lostPackets = append(lostPackets, p.PacketNumber) }, }}, } } return p } nonAckElicitingPacket := func(p *packet) *packet { p = ackElicitingPacket(p) p.Frames = nil p.LargestAcked = 1 return p } initialPacket := func(p *packet) *packet { p = ackElicitingPacket(p) p.EncryptionLevel = protocol.EncryptionInitial return p } handshakePacket := func(p *packet) *packet { p = ackElicitingPacket(p) p.EncryptionLevel = protocol.EncryptionHandshake return p } handshakePacketNonAckEliciting := func(p *packet) *packet { p = nonAckElicitingPacket(p) p.EncryptionLevel = protocol.EncryptionHandshake return p } sentPacket := func(p *packet) { handler.SentPacket(p.SendTime, p.PacketNumber, p.LargestAcked, p.StreamFrames, p.Frames, p.EncryptionLevel, protocol.ECNNon, p.Length, p.IsPathMTUProbePacket) } expectInPacketHistory := func(expected []protocol.PacketNumber, encLevel protocol.EncryptionLevel) { pnSpace := handler.getPacketNumberSpace(encLevel) var length int pnSpace.history.Iterate(func(p *packet) (bool, error) { if !p.declaredLost && !p.skippedPacket { length++ } return true, nil }) ExpectWithOffset(1, length).To(Equal(len(expected))) for _, p := range expected { ExpectWithOffset(2, getPacket(p, encLevel)).ToNot(BeNil()) } } updateRTT := func(rtt time.Duration) { handler.rttStats.UpdateRTT(rtt, 0, time.Now()) ExpectWithOffset(1, handler.rttStats.SmoothedRTT()).To(Equal(rtt)) } // setHandshakeConfirmed drops both Initial and Handshake packets and then confirms the handshake setHandshakeConfirmed := func() { handler.DropPackets(protocol.EncryptionInitial) handler.DropPackets(protocol.EncryptionHandshake) handler.SetHandshakeConfirmed() } Context("registering sent packets", func() { It("accepts two consecutive packets", func() { sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, EncryptionLevel: protocol.EncryptionHandshake})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, EncryptionLevel: protocol.EncryptionHandshake})) Expect(handler.handshakePackets.largestSent).To(Equal(protocol.PacketNumber(2))) expectInPacketHistory([]protocol.PacketNumber{1, 2}, protocol.EncryptionHandshake) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2))) }) It("uses the same packet number space for 0-RTT and 1-RTT packets", func() { sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, EncryptionLevel: protocol.Encryption0RTT})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, EncryptionLevel: protocol.Encryption1RTT})) Expect(handler.appDataPackets.largestSent).To(Equal(protocol.PacketNumber(2))) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2))) }) It("accepts packet number 0", func() { sentPacket(ackElicitingPacket(&packet{PacketNumber: 0, EncryptionLevel: protocol.Encryption1RTT})) Expect(handler.appDataPackets.largestSent).To(BeZero()) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, EncryptionLevel: protocol.Encryption1RTT})) Expect(handler.appDataPackets.largestSent).To(Equal(protocol.PacketNumber(1))) expectInPacketHistory([]protocol.PacketNumber{0, 1}, protocol.Encryption1RTT) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(2))) }) It("stores the sent time", func() { sendTime := time.Now().Add(-time.Minute) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: sendTime})) Expect(handler.appDataPackets.lastAckElicitingPacketTime).To(Equal(sendTime)) }) It("stores the sent time of Initial packets", func() { sendTime := time.Now().Add(-time.Minute) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: sendTime, EncryptionLevel: protocol.EncryptionInitial})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: sendTime.Add(time.Hour), EncryptionLevel: protocol.Encryption1RTT})) Expect(handler.initialPackets.lastAckElicitingPacketTime).To(Equal(sendTime)) }) }) Context("ACK processing", func() { JustBeforeEach(func() { for i := protocol.PacketNumber(0); i < 10; i++ { sentPacket(ackElicitingPacket(&packet{PacketNumber: i})) } // Increase RTT, because the tests would be flaky otherwise updateRTT(time.Hour) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10))) }) Context("ACK processing", func() { It("accepts ACKs sent in packet 0", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 5}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(5))) }) It("says if a 1-RTT packet was acknowledged", func() { sentPacket(ackElicitingPacket(&packet{PacketNumber: 10, EncryptionLevel: protocol.Encryption0RTT})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 11, EncryptionLevel: protocol.Encryption0RTT})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 12, EncryptionLevel: protocol.Encryption1RTT})) acked1RTT, err := handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 11}}}, protocol.Encryption1RTT, time.Now(), ) Expect(err).ToNot(HaveOccurred()) Expect(acked1RTT).To(BeFalse()) acked1RTT, err = handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 11, Largest: 12}}}, protocol.Encryption1RTT, time.Now(), ) Expect(err).ToNot(HaveOccurred()) Expect(acked1RTT).To(BeTrue()) }) It("accepts multiple ACKs sent in the same packet", func() { ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 3}}} ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 4}}} _, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(3))) // this wouldn't happen in practice // for testing purposes, we pretend to send a different ACK frame in a duplicated packet, to be able to verify that it actually doesn't get processed _, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(4))) }) It("rejects ACKs that acknowledge a skipped packet number", func() { sentPacket(ackElicitingPacket(&packet{PacketNumber: 10})) handler.appDataPackets.history.SkippedPacket(11) sentPacket(ackElicitingPacket(&packet{PacketNumber: 12})) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 12}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received an ACK for skipped packet number: 11 (1-RTT)", })) }) It("rejects ACKs with a too high LargestAcked packet number", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 9999}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received ACK for an unsent packet", })) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10))) }) It("ignores repeated ACKs", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 3}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6))) _, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(3))) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6))) }) }) Context("acks the right packets", func() { expectInPacketHistoryOrLost := func(expected []protocol.PacketNumber, encLevel protocol.EncryptionLevel) { pnSpace := handler.getPacketNumberSpace(encLevel) var length int pnSpace.history.Iterate(func(p *packet) (bool, error) { if !p.declaredLost { length++ } return true, nil }) ExpectWithOffset(1, length+len(lostPackets)).To(Equal(len(expected))) expectedLoop: for _, p := range expected { if getPacket(p, encLevel) != nil { continue } for _, lostP := range lostPackets { if lostP == p { continue expectedLoop } } Fail(fmt.Sprintf("Packet %d not in packet history.", p)) } } It("adjusts the LargestAcked, and adjusts the bytes in flight", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 5}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.appDataPackets.largestAcked).To(Equal(protocol.PacketNumber(5))) expectInPacketHistoryOrLost([]protocol.PacketNumber{6, 7, 8, 9}, protocol.Encryption1RTT) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(4))) }) It("acks packet 0", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 0, Largest: 0}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(getPacket(0, protocol.Encryption1RTT)).To(BeNil()) expectInPacketHistoryOrLost([]protocol.PacketNumber{1, 2, 3, 4, 5, 6, 7, 8, 9}, protocol.Encryption1RTT) }) It("calls the OnAcked callback", func() { var acked bool ping := &wire.PingFrame{} sentPacket(ackElicitingPacket(&packet{ PacketNumber: 10, Frames: []Frame{{ Frame: ping, Handler: &customFrameHandler{ onAcked: func(f wire.Frame) { Expect(f).To(Equal(ping)) acked = true }, }, }}, })) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 10}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(acked).To(BeTrue()) }) It("handles an ACK frame with one missing packet range", func() { ack := &wire.AckFrame{ // lose 4 and 5 AckRanges: []wire.AckRange{ {Smallest: 6, Largest: 9}, {Smallest: 1, Largest: 3}, }, } _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 4, 5}, protocol.Encryption1RTT) }) It("does not ack packets below the LowestAcked", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 3, Largest: 8}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 1, 2, 9}, protocol.Encryption1RTT) }) It("handles an ACK with multiple missing packet ranges", func() { ack := &wire.AckFrame{ // packets 2, 4 and 5, and 8 were lost AckRanges: []wire.AckRange{ {Smallest: 9, Largest: 9}, {Smallest: 6, Largest: 7}, {Smallest: 3, Largest: 3}, {Smallest: 1, Largest: 1}, }, } _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 2, 4, 5, 8}, protocol.Encryption1RTT) }) It("processes an ACK frame that would be sent after a late arrival of a packet", func() { ack1 := &wire.AckFrame{ // 5 lost AckRanges: []wire.AckRange{ {Smallest: 6, Largest: 6}, {Smallest: 1, Largest: 4}, }, } _, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 5, 7, 8, 9}, protocol.Encryption1RTT) ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 6}}} // now ack 5 _, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 7, 8, 9}, protocol.Encryption1RTT) }) It("processes an ACK that contains old ACK ranges", func() { ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 6}}} _, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 7, 8, 9}, protocol.Encryption1RTT) ack2 := &wire.AckFrame{ AckRanges: []wire.AckRange{ {Smallest: 8, Largest: 8}, {Smallest: 3, Largest: 3}, {Smallest: 1, Largest: 1}, }, } _, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) expectInPacketHistoryOrLost([]protocol.PacketNumber{0, 7, 9}, protocol.Encryption1RTT) }) }) Context("calculating RTT", func() { It("computes the RTT", func() { now := time.Now() // First, fake the sent times of the first, second and last packet getPacket(1, protocol.Encryption1RTT).SendTime = now.Add(-10 * time.Minute) getPacket(2, protocol.Encryption1RTT).SendTime = now.Add(-5 * time.Minute) getPacket(6, protocol.Encryption1RTT).SendTime = now.Add(-1 * time.Minute) // Now, check that the proper times are used when calculating the deltas ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 10*time.Minute, 1*time.Second)) ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 2}}} _, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second)) ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 6}}} _, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 1*time.Minute, 1*time.Second)) }) It("ignores the DelayTime for Initial and Handshake packets", func() { sentPacket(initialPacket(&packet{PacketNumber: 1})) handler.rttStats.SetMaxAckDelay(time.Hour) // make sure the rttStats have a min RTT, so that the delay is used handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now()) getPacket(1, protocol.EncryptionInitial).SendTime = time.Now().Add(-10 * time.Minute) ack := &wire.AckFrame{ AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}, DelayTime: 5 * time.Minute, } _, err := handler.ReceivedAck(ack, protocol.EncryptionInitial, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 10*time.Minute, 1*time.Second)) }) It("uses the DelayTime in the ACK frame", func() { handler.rttStats.SetMaxAckDelay(time.Hour) // make sure the rttStats have a min RTT, so that the delay is used handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now()) getPacket(1, protocol.Encryption1RTT).SendTime = time.Now().Add(-10 * time.Minute) ack := &wire.AckFrame{ AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}, DelayTime: 5 * time.Minute, } _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 5*time.Minute, 1*time.Second)) }) It("limits the DelayTime in the ACK frame to max_ack_delay", func() { handler.rttStats.SetMaxAckDelay(time.Minute) // make sure the rttStats have a min RTT, so that the delay is used handler.rttStats.UpdateRTT(5*time.Minute, 0, time.Now()) getPacket(1, protocol.Encryption1RTT).SendTime = time.Now().Add(-10 * time.Minute) ack := &wire.AckFrame{ AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}, DelayTime: 5 * time.Minute, } _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.LatestRTT()).To(BeNumerically("~", 9*time.Minute, 1*time.Second)) }) }) Context("determining which ACKs we have received an ACK for", func() { JustBeforeEach(func() { morePackets := []*packet{ { PacketNumber: 10, LargestAcked: 100, Frames: []Frame{{Frame: &streamFrame}}, Length: 1, EncryptionLevel: protocol.Encryption1RTT, }, { PacketNumber: 11, LargestAcked: 200, Frames: []Frame{{Frame: &streamFrame}}, Length: 1, EncryptionLevel: protocol.Encryption1RTT, }, { PacketNumber: 12, Frames: []Frame{{Frame: &streamFrame}}, Length: 1, EncryptionLevel: protocol.Encryption1RTT, }, } for _, packet := range morePackets { sentPacket(packet) } }) It("determines which ACK we have received an ACK for", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 12}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201))) }) It("doesn't do anything when the acked packet didn't contain an ACK", func() { ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 10}}} ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 12, Largest: 12}}} _, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(101))) _, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(101))) }) It("doesn't decrease the value", func() { ack1 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 11, Largest: 11}}} ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 10}}} _, err := handler.ReceivedAck(ack1, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201))) _, err = handler.ReceivedAck(ack2, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.GetLowestPacketNotConfirmedAcked()).To(Equal(protocol.PacketNumber(201))) }) }) }) Context("congestion", func() { var cong *mocks.MockSendAlgorithmWithDebugInfos JustBeforeEach(func() { cong = mocks.NewMockSendAlgorithmWithDebugInfos(mockCtrl) handler.congestion = cong }) It("should call OnSent", func() { cong.EXPECT().OnPacketSent( gomock.Any(), protocol.ByteCount(42), protocol.PacketNumber(1), protocol.ByteCount(42), true, ) sentPacket(&packet{ PacketNumber: 1, Length: 42, Frames: []Frame{{Frame: &wire.PingFrame{}}}, EncryptionLevel: protocol.Encryption1RTT, }) }) It("should call MaybeExitSlowStart and OnPacketAcked", func() { rcvTime := time.Now().Add(-5 * time.Second) cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), // must be called before packets are acked cong.EXPECT().OnPacketAcked(protocol.PacketNumber(1), protocol.ByteCount(1), protocol.ByteCount(3), rcvTime), cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(3), rcvTime), ) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 3})) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, rcvTime) Expect(err).ToNot(HaveOccurred()) }) It("doesn't call OnPacketAcked when a retransmitted packet is acked", func() { cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2})) // lose packet 1 gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), cong.EXPECT().OnCongestionEvent(protocol.PacketNumber(1), protocol.ByteCount(1), protocol.ByteCount(2)), cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(2), gomock.Any()), ) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) // don't EXPECT any further calls to the congestion controller ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 2}}} _, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) It("doesn't call OnCongestionEvent when a Path MTU probe packet is lost", func() { cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(2) var mtuPacketDeclaredLost bool sentPacket(ackElicitingPacket(&packet{ PacketNumber: 1, SendTime: time.Now().Add(-time.Hour), IsPathMTUProbePacket: true, Frames: []Frame{ { Frame: &wire.PingFrame{}, Handler: &customFrameHandler{onLost: func(wire.Frame) { mtuPacketDeclaredLost = true }}, }, }, })) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2})) // lose packet 1, but don't EXPECT any calls to OnCongestionEvent() gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(2), gomock.Any()), ) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(mtuPacketDeclaredLost).To(BeTrue()) Expect(handler.bytesInFlight).To(BeZero()) }) It("calls OnPacketAcked and OnCongestionEvent with the right bytes_in_flight value", func() { cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(4) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: time.Now().Add(-30 * time.Minute)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 3, SendTime: time.Now().Add(-30 * time.Minute)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 4, SendTime: time.Now()})) // receive the first ACK gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), cong.EXPECT().OnCongestionEvent(protocol.PacketNumber(1), protocol.ByteCount(1), protocol.ByteCount(4)), cong.EXPECT().OnPacketAcked(protocol.PacketNumber(2), protocol.ByteCount(1), protocol.ByteCount(4), gomock.Any()), ) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now().Add(-30*time.Minute)) Expect(err).ToNot(HaveOccurred()) // receive the second ACK gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), cong.EXPECT().OnCongestionEvent(protocol.PacketNumber(3), protocol.ByteCount(1), protocol.ByteCount(2)), cong.EXPECT().OnPacketAcked(protocol.PacketNumber(4), protocol.ByteCount(1), protocol.ByteCount(2), gomock.Any()), ) ack = &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 4, Largest: 4}}} _, err = handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) It("passes the bytes in flight to the congestion controller", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) cong.EXPECT().OnPacketSent(gomock.Any(), protocol.ByteCount(42), gomock.Any(), protocol.ByteCount(42), true) sentPacket(&packet{ Length: 42, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) cong.EXPECT().CanSend(protocol.ByteCount(42)).Return(true) cong.EXPECT().HasPacingBudget(gomock.Any()).Return(true) handler.SendMode(time.Now()) }) It("allows sending of ACKs when congestion limited", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) cong.EXPECT().CanSend(gomock.Any()).Return(true) cong.EXPECT().HasPacingBudget(gomock.Any()).Return(true) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) cong.EXPECT().CanSend(gomock.Any()).Return(false) Expect(handler.SendMode(time.Now())).To(Equal(SendAck)) }) It("allows sending of ACKs when we're keeping track of MaxOutstandingSentPackets packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) cong.EXPECT().CanSend(gomock.Any()).Return(true).AnyTimes() cong.EXPECT().HasPacingBudget(gomock.Any()).Return(true).AnyTimes() cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() for i := protocol.PacketNumber(0); i < protocol.MaxOutstandingSentPackets; i++ { Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) sentPacket(ackElicitingPacket(&packet{PacketNumber: i})) } Expect(handler.SendMode(time.Now())).To(Equal(SendAck)) }) It("allows PTOs, even when congestion limited", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) // note that we don't EXPECT a call to GetCongestionWindow // that means retransmissions are sent without considering the congestion window handler.numProbesToSend = 1 handler.ptoMode = SendPTOHandshake Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake)) }) It("returns the pacing delay", func() { t := time.Now() cong.EXPECT().TimeUntilSend(gomock.Any()).Return(t) Expect(handler.TimeUntilSend()).To(Equal(t)) }) }) It("doesn't set an alarm if there are no outstanding packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) sentPacket(ackElicitingPacket(&packet{PacketNumber: 10})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 11})) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 11}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) It("does nothing on OnAlarm if there are no outstanding packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) Context("probe packets", func() { It("queues a probe packet", func() { sentPacket(ackElicitingPacket(&packet{PacketNumber: 10})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 11})) queued := handler.QueueProbePacket(protocol.Encryption1RTT) Expect(queued).To(BeTrue()) Expect(lostPackets).To(Equal([]protocol.PacketNumber{10})) }) It("says when it can't queue a probe packet", func() { queued := handler.QueueProbePacket(protocol.Encryption1RTT) Expect(queued).To(BeFalse()) }) It("implements exponential backoff", func() { handler.peerAddressValidated = true setHandshakeConfirmed() sendTime := time.Now().Add(-time.Hour) sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: sendTime})) timeout := handler.GetLossDetectionTimeout().Sub(sendTime) Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(timeout)) handler.ptoCount = 1 handler.setLossDetectionTimer() Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(2 * timeout)) handler.ptoCount = 2 handler.setLossDetectionTimer() Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(4 * timeout)) // truncated when the exponential gets too large handler.ptoCount = 20 handler.setLossDetectionTimer() Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(maxPTODuration)) // protected from rollover handler.ptoCount = 100 handler.setLossDetectionTimer() Expect(handler.GetLossDetectionTimeout().Sub(sendTime)).To(Equal(maxPTODuration)) }) It("reset the PTO count when receiving an ACK", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) now := time.Now() setHandshakeConfirmed() sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Minute)})) handler.appDataPackets.pns.(*skippingPacketNumberGenerator).next = 3 Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", now.Add(-time.Minute), time.Second)) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) Expect(handler.ptoCount).To(BeEquivalentTo(1)) _, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.ptoCount).To(BeZero()) }) It("resets the PTO mode and PTO count when a packet number space is dropped", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) now := time.Now() handler.rttStats.UpdateRTT(time.Second/2, 0, now) Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second / 2)) Expect(handler.rttStats.PTO(true)).To(And( BeNumerically(">", time.Second), BeNumerically("<", 2*time.Second), )) sendTimeHandshake := now.Add(-2 * time.Minute) sendTimeAppData := now.Add(-time.Minute) sentPacket(ackElicitingPacket(&packet{ PacketNumber: 1, EncryptionLevel: protocol.EncryptionHandshake, SendTime: sendTimeHandshake, })) sentPacket(ackElicitingPacket(&packet{ PacketNumber: 2, SendTime: sendTimeAppData, })) // PTO timer based on the Handshake packet Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.ptoCount).To(BeEquivalentTo(1)) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake)) Expect(handler.GetLossDetectionTimeout()).To(Equal(sendTimeHandshake.Add(handler.rttStats.PTO(false) << 1))) setHandshakeConfirmed() handler.DropPackets(protocol.EncryptionHandshake) // PTO timer based on the 1-RTT packet Expect(handler.GetLossDetectionTimeout()).To(Equal(sendTimeAppData.Add(handler.rttStats.PTO(true)))) // no backoff. PTO count = 0 Expect(handler.SendMode(time.Now())).ToNot(Equal(SendPTOHandshake)) Expect(handler.ptoCount).To(BeZero()) }) It("allows two 1-RTT PTOs", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) setHandshakeConfirmed() var lostPackets []protocol.PacketNumber sentPacket(ackElicitingPacket(&packet{ PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT), SendTime: time.Now().Add(-time.Hour), Frames: []Frame{ { Frame: &wire.PingFrame{}, Handler: &customFrameHandler{onLost: func(wire.Frame) { lostPackets = append(lostPackets, 1) }}, }, }, })) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).ToNot(Equal(SendPTOAppData)) }) It("only counts ack-eliciting packets as probe packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) setHandshakeConfirmed() sentPacket(ackElicitingPacket(&packet{ PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT), SendTime: time.Now().Add(-time.Hour), })) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) for i := 0; i < 30; i++ { sentPacket(nonAckElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) } sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).ToNot(Equal(SendPTOAppData)) }) It("gets two probe packets if PTO expires", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) setHandshakeConfirmed() sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) updateRTT(time.Hour) Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) // TLP Expect(handler.ptoCount).To(BeEquivalentTo(1)) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) // PTO Expect(handler.ptoCount).To(BeEquivalentTo(2)) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) It("gets two probe packets if PTO expires, for Handshake packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) sentPacket(initialPacket(&packet{PacketNumber: 1})) sentPacket(initialPacket(&packet{PacketNumber: 2})) updateRTT(time.Hour) Expect(handler.initialPackets.lossTime.IsZero()).To(BeTrue()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial)) sentPacket(initialPacket(&packet{PacketNumber: 3})) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial)) sentPacket(initialPacket(&packet{PacketNumber: 4})) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) It("doesn't send 1-RTT probe packets before the handshake completes", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) sentPacket(ackElicitingPacket(&packet{PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT)})) updateRTT(time.Hour) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) setHandshakeConfirmed() Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) }) It("resets the send mode when it receives an acknowledgement after queueing probe packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) setHandshakeConfirmed() pn := handler.PopPacketNumber(protocol.Encryption1RTT) sentPacket(ackElicitingPacket(&packet{PacketNumber: pn, SendTime: time.Now().Add(-time.Hour)})) updateRTT(time.Second) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOAppData)) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: pn, Largest: pn}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) It("handles ACKs for the original packet", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) sentPacket(ackElicitingPacket(&packet{ PacketNumber: handler.PopPacketNumber(protocol.Encryption1RTT), SendTime: time.Now().Add(-time.Hour), })) updateRTT(time.Second) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) }) It("doesn't set the PTO timer for Path MTU probe packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) setHandshakeConfirmed() updateRTT(time.Second) sentPacket(ackElicitingPacket(&packet{PacketNumber: 5, SendTime: time.Now(), IsPathMTUProbePacket: true})) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) }) Context("amplification limit, for the server", func() { It("limits the window to 3x the bytes received, to avoid amplification attacks", func() { now := time.Now() handler.ReceivedPacket(protocol.EncryptionInitial) // receiving an Initial packet doesn't validate the client's address handler.ReceivedBytes(200) sentPacket(&packet{ PacketNumber: 1, Length: 599, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: now, }) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) sentPacket(&packet{ PacketNumber: 2, Length: 1, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: now, }) Expect(handler.SendMode(time.Now())).To(Equal(SendNone)) }) It("cancels the loss detection timer when it is amplification limited, and resets it when becoming unblocked", func() { handler.ReceivedBytes(300) sentPacket(&packet{ PacketNumber: 1, Length: 900, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) // Amplification limited. We don't need to set a timer now. Expect(handler.GetLossDetectionTimeout()).To(BeZero()) // Unblock the server. Now we should fire up the timer. handler.ReceivedBytes(1) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) }) It("resets the loss detection timer when the client's address is validated", func() { handler.ReceivedBytes(300) sentPacket(&packet{ PacketNumber: 1, Length: 900, EncryptionLevel: protocol.EncryptionHandshake, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) // Amplification limited. We don't need to set a timer now. Expect(handler.GetLossDetectionTimeout()).To(BeZero()) handler.ReceivedPacket(protocol.EncryptionHandshake) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) }) It("cancels the loss detection alarm when all Handshake packets are acknowledged", func() { t := time.Now().Add(-time.Second) handler.ReceivedBytes(99999) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: t})) sentPacket(handshakePacket(&packet{PacketNumber: 3, SendTime: t})) sentPacket(handshakePacket(&packet{PacketNumber: 4, SendTime: t})) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 3, Largest: 4}}}, protocol.EncryptionHandshake, time.Now()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) }) Context("amplification limit, for the server, with validated address", func() { JustBeforeEach(func() { rttStats := utils.NewRTTStats() handler = newSentPacketHandler(42, protocol.InitialPacketSize, rttStats, true, false, perspective, nil, utils.DefaultLogger) }) It("do not limits the window", func() { handler.ReceivedBytes(0) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) sentPacket(&packet{ PacketNumber: 1, Length: 900, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) }) Context("amplification limit, for the client", func() { BeforeEach(func() { perspective = protocol.PerspectiveClient }) It("sends an Initial packet to unblock the server", func() { sentPacket(initialPacket(&packet{PacketNumber: 1})) _, err := handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionInitial, time.Now(), ) Expect(err).ToNot(HaveOccurred()) // No packets are outstanding at this point. // Make sure that a probe packet is sent. Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial)) // send a single packet to unblock the server sentPacket(initialPacket(&packet{PacketNumber: 2})) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) // Now receive an ACK for a Handshake packet. // This tells the client that the server completed address validation. sentPacket(handshakePacket(&packet{PacketNumber: 1})) handler.DropPackets(protocol.EncryptionInitial) // sending a Handshake packet drops the Initial packet number space _, err = handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionHandshake, time.Now(), ) Expect(err).ToNot(HaveOccurred()) // Make sure that no timer is set at this point. Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) It("sends a Handshake packet to unblock the server, if Initial keys were already dropped", func() { sentPacket(initialPacket(&packet{PacketNumber: 1})) _, err := handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionInitial, time.Now(), ) Expect(err).ToNot(HaveOccurred()) sentPacket(handshakePacketNonAckEliciting(&packet{PacketNumber: 1})) handler.DropPackets(protocol.EncryptionInitial) // sending a Handshake packet drops the Initial packet number space Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake)) // Now receive an ACK for this packet, and send another one. _, err = handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionHandshake, time.Now(), ) Expect(err).ToNot(HaveOccurred()) sentPacket(handshakePacketNonAckEliciting(&packet{PacketNumber: 2})) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) It("doesn't send a packet to unblock the server after handshake confirmation, even if no Handshake ACK was received", func() { sentPacket(handshakePacket(&packet{PacketNumber: 1})) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake)) // confirm the handshake handler.DropPackets(protocol.EncryptionHandshake) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) It("correctly sets the timer after the Initial packet number space has been dropped", func() { sentPacket(initialPacket(&packet{PacketNumber: 1, SendTime: time.Now().Add(-19 * time.Second)})) _, err := handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionInitial, time.Now(), ) Expect(err).ToNot(HaveOccurred()) sentPacket(handshakePacketNonAckEliciting(&packet{PacketNumber: 1, SendTime: time.Now()})) handler.DropPackets(protocol.EncryptionInitial) // sending a Handshake packet drops the Initial packet number space pto := handler.rttStats.PTO(false) Expect(pto).ToNot(BeZero()) // pto is approximately 19 * 3. Using a number > 19 above will // run into the maxPTODuration limit Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", time.Now().Add(pto), 10*time.Millisecond)) }) It("doesn't reset the PTO count when receiving an ACK", func() { now := time.Now() sentPacket(initialPacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)})) sentPacket(initialPacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Minute)})) Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", now.Add(-time.Minute), time.Second)) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial)) Expect(handler.ptoCount).To(BeEquivalentTo(1)) _, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionInitial, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.ptoCount).To(BeEquivalentTo(1)) }) }) Context("Packet-based loss detection", func() { It("declares packet below the packet loss threshold as lost", func() { now := time.Now() for i := protocol.PacketNumber(1); i <= 6; i++ { sentPacket(ackElicitingPacket(&packet{PacketNumber: i})) } ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 6, Largest: 6}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now) Expect(err).ToNot(HaveOccurred()) expectInPacketHistory([]protocol.PacketNumber{4, 5}, protocol.Encryption1RTT) Expect(lostPackets).To(Equal([]protocol.PacketNumber{1, 2, 3})) }) }) Context("Delay-based loss detection", func() { It("immediately detects old packets as lost when receiving an ACK", func() { now := time.Now() sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Hour)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Second)})) Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue()) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now) Expect(err).ToNot(HaveOccurred()) // no need to set an alarm, since packet 1 was already declared lost Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue()) Expect(handler.bytesInFlight).To(BeZero()) }) It("sets the early retransmit alarm", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.handshakeConfirmed = true now := time.Now() sentPacket(ackElicitingPacket(&packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second)})) sentPacket(ackElicitingPacket(&packet{PacketNumber: 3, SendTime: now})) Expect(handler.appDataPackets.lossTime.IsZero()).To(BeTrue()) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now.Add(-time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second)) // Packet 1 should be considered lost (1+1/8) RTTs after it was sent. Expect(handler.GetLossDetectionTimeout().Sub(getPacket(1, protocol.Encryption1RTT).SendTime)).To(Equal(time.Second * 9 / 8)) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) expectInPacketHistory([]protocol.PacketNumber{1, 3}, protocol.Encryption1RTT) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) expectInPacketHistory([]protocol.PacketNumber{3}, protocol.Encryption1RTT) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) It("sets the early retransmit alarm for crypto packets", func() { handler.ReceivedBytes(1000) now := time.Now() sentPacket(initialPacket(&packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second)})) sentPacket(initialPacket(&packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second)})) sentPacket(initialPacket(&packet{PacketNumber: 3, SendTime: now})) Expect(handler.initialPackets.lossTime.IsZero()).To(BeTrue()) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.EncryptionInitial, now.Add(-time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second)) // Packet 1 should be considered lost (1+1/8) RTTs after it was sent. Expect(handler.GetLossDetectionTimeout().Sub(getPacket(1, protocol.EncryptionInitial).SendTime)).To(Equal(time.Second * 9 / 8)) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) expectInPacketHistory([]protocol.PacketNumber{1, 3}, protocol.EncryptionInitial) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) expectInPacketHistory([]protocol.PacketNumber{3}, protocol.EncryptionInitial) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) It("sets the early retransmit alarm for Path MTU probe packets", func() { var mtuPacketDeclaredLost bool now := time.Now() sentPacket(ackElicitingPacket(&packet{ PacketNumber: 1, SendTime: now.Add(-3 * time.Second), IsPathMTUProbePacket: true, Frames: []Frame{ { Frame: &wire.PingFrame{}, Handler: &customFrameHandler{onLost: func(wire.Frame) { mtuPacketDeclaredLost = true }}, }, }, })) sentPacket(ackElicitingPacket(&packet{PacketNumber: 2, SendTime: now.Add(-3 * time.Second)})) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 2, Largest: 2}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, now.Add(-time.Second)) Expect(err).ToNot(HaveOccurred()) Expect(mtuPacketDeclaredLost).To(BeFalse()) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(mtuPacketDeclaredLost).To(BeTrue()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) }) }) Context("crypto packets", func() { It("rejects an ACK that acks packets with a higher encryption level", func() { sentPacket(ackElicitingPacket(&packet{ PacketNumber: 13, EncryptionLevel: protocol.Encryption1RTT, })) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 13, Largest: 13}}} _, err := handler.ReceivedAck(ack, protocol.EncryptionHandshake, time.Now()) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received ACK for an unsent packet", })) }) It("deletes Initial packets, as a server", func() { for i := protocol.PacketNumber(0); i < 6; i++ { sentPacket(ackElicitingPacket(&packet{ PacketNumber: i, EncryptionLevel: protocol.EncryptionInitial, })) } for i := protocol.PacketNumber(0); i < 10; i++ { sentPacket(ackElicitingPacket(&packet{ PacketNumber: i, EncryptionLevel: protocol.EncryptionHandshake, })) } Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(16))) handler.DropPackets(protocol.EncryptionInitial) Expect(lostPackets).To(BeEmpty()) // frames must not be queued for retransmission Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10))) Expect(handler.initialPackets).To(BeNil()) Expect(handler.handshakePackets.history.Len()).ToNot(BeZero()) }) It("deletes Handshake packets", func() { for i := protocol.PacketNumber(0); i < 6; i++ { sentPacket(ackElicitingPacket(&packet{ PacketNumber: i, EncryptionLevel: protocol.EncryptionHandshake, })) } for i := protocol.PacketNumber(0); i < 10; i++ { sentPacket(ackElicitingPacket(&packet{ PacketNumber: i, EncryptionLevel: protocol.Encryption1RTT, })) } Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(16))) handler.DropPackets(protocol.EncryptionHandshake) Expect(lostPackets).To(BeEmpty()) // frames must not be queued for retransmission Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(10))) Expect(handler.handshakePackets).To(BeNil()) }) It("doesn't retransmit 0-RTT packets when 0-RTT keys are dropped", func() { for i := protocol.PacketNumber(0); i < 6; i++ { if i == 3 { handler.appDataPackets.history.SkippedPacket(3) continue } sentPacket(ackElicitingPacket(&packet{ PacketNumber: i, EncryptionLevel: protocol.Encryption0RTT, })) } for i := protocol.PacketNumber(6); i < 12; i++ { sentPacket(ackElicitingPacket(&packet{PacketNumber: i})) } Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(11))) handler.DropPackets(protocol.Encryption0RTT) Expect(lostPackets).To(BeEmpty()) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6))) }) It("cancels the PTO when dropping a packet number space", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) now := time.Now() sentPacket(handshakePacket(&packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)})) sentPacket(handshakePacket(&packet{PacketNumber: 2, SendTime: now.Add(-time.Minute)})) Expect(handler.GetLossDetectionTimeout()).To(BeTemporally("~", now.Add(-time.Minute), time.Second)) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOHandshake)) Expect(handler.ptoCount).To(BeEquivalentTo(1)) handler.DropPackets(protocol.EncryptionHandshake) Expect(handler.ptoCount).To(BeZero()) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) }) }) Context("peeking and popping packet number", func() { It("peeks and pops the initial packet number", func() { pn, _ := handler.PeekPacketNumber(protocol.EncryptionInitial) Expect(pn).To(Equal(protocol.PacketNumber(42))) Expect(handler.PopPacketNumber(protocol.EncryptionInitial)).To(Equal(protocol.PacketNumber(42))) }) It("peeks and pops beyond the initial packet number", func() { Expect(handler.PopPacketNumber(protocol.EncryptionInitial)).To(Equal(protocol.PacketNumber(42))) Expect(handler.PopPacketNumber(protocol.EncryptionInitial)).To(BeNumerically(">", 42)) }) It("starts at 0 for handshake and application-data packet number space", func() { pn, _ := handler.PeekPacketNumber(protocol.EncryptionHandshake) Expect(pn).To(BeZero()) Expect(handler.PopPacketNumber(protocol.EncryptionHandshake)).To(BeZero()) pn, _ = handler.PeekPacketNumber(protocol.Encryption1RTT) Expect(pn).To(BeZero()) Expect(handler.PopPacketNumber(protocol.Encryption1RTT)).To(BeZero()) }) }) Context("for the client", func() { BeforeEach(func() { perspective = protocol.PerspectiveClient }) It("considers the server's address validated right away", func() { }) It("queues outstanding packets for retransmission, cancels alarms and resets PTO count when receiving a Retry", func() { sentPacket(initialPacket(&packet{PacketNumber: 42})) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.bytesInFlight).ToNot(BeZero()) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) // now receive a Retry Expect(handler.ResetForRetry(time.Now())).To(Succeed()) Expect(lostPackets).To(Equal([]protocol.PacketNumber{42})) Expect(handler.bytesInFlight).To(BeZero()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) Expect(handler.SendMode(time.Now())).To(Equal(SendAny)) Expect(handler.ptoCount).To(BeZero()) }) It("queues outstanding frames for retransmission and cancels alarms when receiving a Retry", func() { var lostInitial, lost0RTT bool sentPacket(&packet{ PacketNumber: 13, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{ { Frame: &wire.CryptoFrame{Data: []byte("foobar")}, Handler: &customFrameHandler{onLost: func(wire.Frame) { lostInitial = true }}, }, }, Length: 100, }) pn := handler.PopPacketNumber(protocol.Encryption0RTT) sentPacket(&packet{ PacketNumber: pn, EncryptionLevel: protocol.Encryption0RTT, Frames: []Frame{ { Frame: &wire.StreamFrame{Data: []byte("foobar")}, Handler: &customFrameHandler{onLost: func(wire.Frame) { lost0RTT = true }}, }, }, Length: 999, }) Expect(handler.bytesInFlight).ToNot(BeZero()) // now receive a Retry Expect(handler.ResetForRetry(time.Now())).To(Succeed()) Expect(handler.bytesInFlight).To(BeZero()) Expect(lostInitial).To(BeTrue()) Expect(lost0RTT).To(BeTrue()) // make sure we keep increasing the packet number for 0-RTT packets Expect(handler.PopPacketNumber(protocol.Encryption0RTT)).To(BeNumerically(">", pn)) }) It("uses a Retry for an RTT estimate, if it was not retransmitted", func() { now := time.Now() sentPacket(ackElicitingPacket(&packet{ PacketNumber: 42, EncryptionLevel: protocol.EncryptionInitial, SendTime: now, })) sentPacket(ackElicitingPacket(&packet{ PacketNumber: 43, EncryptionLevel: protocol.EncryptionInitial, SendTime: now.Add(500 * time.Millisecond), })) Expect(handler.ResetForRetry(now.Add(time.Second))).To(Succeed()) Expect(handler.rttStats.SmoothedRTT()).To(Equal(time.Second)) }) It("uses a Retry for an RTT estimate, but doesn't set the RTT to a value lower than 5ms", func() { now := time.Now() sentPacket(ackElicitingPacket(&packet{ PacketNumber: 42, EncryptionLevel: protocol.EncryptionInitial, SendTime: now, })) sentPacket(ackElicitingPacket(&packet{ PacketNumber: 43, EncryptionLevel: protocol.EncryptionInitial, SendTime: now.Add(2 * time.Millisecond), })) Expect(handler.ResetForRetry(now.Add(4 * time.Millisecond))).To(Succeed()) Expect(minRTTAfterRetry).To(BeNumerically(">", 4*time.Millisecond)) Expect(handler.rttStats.SmoothedRTT()).To(Equal(minRTTAfterRetry)) }) It("doesn't use a Retry for an RTT estimate, if it was not retransmitted", func() { now := time.Now() sentPacket(ackElicitingPacket(&packet{ PacketNumber: 42, EncryptionLevel: protocol.EncryptionInitial, SendTime: now, })) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode(time.Now())).To(Equal(SendPTOInitial)) sentPacket(ackElicitingPacket(&packet{ PacketNumber: 43, EncryptionLevel: protocol.EncryptionInitial, SendTime: now.Add(500 * time.Millisecond), })) Expect(handler.ResetForRetry(now.Add(time.Second))).To(Succeed()) Expect(handler.rttStats.SmoothedRTT()).To(BeZero()) }) }) Context("ECN handling", func() { var ecnHandler *MockECNHandler var cong *mocks.MockSendAlgorithmWithDebugInfos JustBeforeEach(func() { cong = mocks.NewMockSendAlgorithmWithDebugInfos(mockCtrl) cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() cong.EXPECT().OnPacketAcked(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() cong.EXPECT().MaybeExitSlowStart().AnyTimes() ecnHandler = NewMockECNHandler(mockCtrl) lostPackets = nil rttStats := utils.NewRTTStats() rttStats.UpdateRTT(time.Hour, 0, time.Now()) handler = newSentPacketHandler(42, protocol.InitialPacketSize, rttStats, false, false, perspective, nil, utils.DefaultLogger) handler.ecnTracker = ecnHandler handler.congestion = cong }) It("informs about sent packets", func() { // Check that only 1-RTT packets are reported handler.SentPacket(time.Now(), 100, -1, nil, nil, protocol.EncryptionInitial, protocol.ECT1, 1200, false) handler.SentPacket(time.Now(), 101, -1, nil, nil, protocol.EncryptionHandshake, protocol.ECT0, 1200, false) handler.SentPacket(time.Now(), 102, -1, nil, nil, protocol.Encryption0RTT, protocol.ECNCE, 1200, false) ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(103), protocol.ECT1) handler.SentPacket(time.Now(), 103, -1, nil, nil, protocol.Encryption1RTT, protocol.ECT1, 1200, false) }) It("informs about sent packets", func() { // Check that only 1-RTT packets are reported handler.SentPacket(time.Now(), 100, -1, nil, nil, protocol.EncryptionInitial, protocol.ECT1, 1200, false) handler.SentPacket(time.Now(), 101, -1, nil, nil, protocol.EncryptionHandshake, protocol.ECT0, 1200, false) handler.SentPacket(time.Now(), 102, -1, nil, nil, protocol.Encryption0RTT, protocol.ECNCE, 1200, false) ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(103), protocol.ECT1) handler.SentPacket(time.Now(), 103, -1, nil, nil, protocol.Encryption1RTT, protocol.ECT1, 1200, false) }) It("informs about lost packets", func() { for i := 10; i < 20; i++ { ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(i), protocol.ECT1) handler.SentPacket(time.Now(), protocol.PacketNumber(i), -1, []StreamFrame{{Frame: &streamFrame}}, nil, protocol.Encryption1RTT, protocol.ECT1, 1200, false) } cong.EXPECT().OnCongestionEvent(gomock.Any(), gomock.Any(), gomock.Any()).Times(3) ecnHandler.EXPECT().LostPacket(protocol.PacketNumber(10)) ecnHandler.EXPECT().LostPacket(protocol.PacketNumber(11)) ecnHandler.EXPECT().LostPacket(protocol.PacketNumber(12)) ecnHandler.EXPECT().HandleNewlyAcked(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) _, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 16, Smallest: 13}}}, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) It("processes ACKs", func() { // Check that we only care about 1-RTT packets. handler.SentPacket(time.Now(), 100, -1, []StreamFrame{{Frame: &streamFrame}}, nil, protocol.EncryptionInitial, protocol.ECT1, 1200, false) _, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 100, Smallest: 100}}}, protocol.EncryptionInitial, time.Now()) Expect(err).ToNot(HaveOccurred()) for i := 10; i < 20; i++ { ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(i), protocol.ECT1) handler.SentPacket(time.Now(), protocol.PacketNumber(i), -1, []StreamFrame{{Frame: &streamFrame}}, nil, protocol.Encryption1RTT, protocol.ECT1, 1200, false) } ecnHandler.EXPECT().HandleNewlyAcked(gomock.Any(), int64(1), int64(2), int64(3)).DoAndReturn(func(packets []*packet, _, _, _ int64) bool { Expect(packets).To(HaveLen(5)) Expect(packets[0].PacketNumber).To(Equal(protocol.PacketNumber(10))) Expect(packets[1].PacketNumber).To(Equal(protocol.PacketNumber(11))) Expect(packets[2].PacketNumber).To(Equal(protocol.PacketNumber(12))) Expect(packets[3].PacketNumber).To(Equal(protocol.PacketNumber(14))) Expect(packets[4].PacketNumber).To(Equal(protocol.PacketNumber(15))) return false }) _, err = handler.ReceivedAck(&wire.AckFrame{ AckRanges: []wire.AckRange{ {Largest: 15, Smallest: 14}, {Largest: 12, Smallest: 10}, }, ECT0: 1, ECT1: 2, ECNCE: 3, }, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) It("ignores reordered ACKs", func() { for i := 10; i < 20; i++ { ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(i), protocol.ECT1) handler.SentPacket(time.Now(), protocol.PacketNumber(i), -1, []StreamFrame{{Frame: &streamFrame}}, nil, protocol.Encryption1RTT, protocol.ECT1, 1200, false) } ecnHandler.EXPECT().HandleNewlyAcked(gomock.Any(), int64(1), int64(2), int64(3)).DoAndReturn(func(packets []*packet, _, _, _ int64) bool { Expect(packets).To(HaveLen(2)) Expect(packets[0].PacketNumber).To(Equal(protocol.PacketNumber(11))) Expect(packets[1].PacketNumber).To(Equal(protocol.PacketNumber(12))) return false }) _, err := handler.ReceivedAck(&wire.AckFrame{ AckRanges: []wire.AckRange{{Largest: 12, Smallest: 11}}, ECT0: 1, ECT1: 2, ECNCE: 3, }, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) // acknowledge packet 10 now, but don't increase the largest acked _, err = handler.ReceivedAck(&wire.AckFrame{ AckRanges: []wire.AckRange{{Largest: 12, Smallest: 10}}, ECT0: 1, ECNCE: 3, }, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) It("ignores ACKs that don't increase the largest acked", func() { for i := 10; i < 20; i++ { ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(i), protocol.ECT1) handler.SentPacket(time.Now(), protocol.PacketNumber(i), -1, []StreamFrame{{Frame: &streamFrame}}, nil, protocol.Encryption1RTT, protocol.ECT1, 1200, false) } ecnHandler.EXPECT().HandleNewlyAcked(gomock.Any(), int64(1), int64(2), int64(3)).DoAndReturn(func(packets []*packet, _, _, _ int64) bool { Expect(packets).To(HaveLen(1)) Expect(packets[0].PacketNumber).To(Equal(protocol.PacketNumber(11))) return false }) _, err := handler.ReceivedAck(&wire.AckFrame{ AckRanges: []wire.AckRange{{Largest: 11, Smallest: 11}}, ECT0: 1, ECT1: 2, ECNCE: 3, }, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) _, err = handler.ReceivedAck(&wire.AckFrame{ AckRanges: []wire.AckRange{{Largest: 11, Smallest: 10}}, ECT0: 1, ECNCE: 3, }, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) It("informs the congestion controller about CE events", func() { for i := 10; i < 20; i++ { ecnHandler.EXPECT().SentPacket(protocol.PacketNumber(i), protocol.ECT0) handler.SentPacket(time.Now(), protocol.PacketNumber(i), -1, []StreamFrame{{Frame: &streamFrame}}, nil, protocol.Encryption1RTT, protocol.ECT0, 1200, false) } ecnHandler.EXPECT().HandleNewlyAcked(gomock.Any(), int64(0), int64(0), int64(0)).Return(true) cong.EXPECT().OnCongestionEvent(protocol.PacketNumber(15), gomock.Any(), gomock.Any()) _, err := handler.ReceivedAck(&wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 15, Smallest: 10}}}, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/sent_packet_history.go000066400000000000000000000074271465664453100311410ustar00rootroot00000000000000package ackhandler import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" ) type sentPacketHistory struct { packets []*packet numOutstanding int highestPacketNumber protocol.PacketNumber } func newSentPacketHistory(isAppData bool) *sentPacketHistory { h := &sentPacketHistory{ highestPacketNumber: protocol.InvalidPacketNumber, } if isAppData { h.packets = make([]*packet, 0, 32) } else { h.packets = make([]*packet, 0, 6) } return h } func (h *sentPacketHistory) checkSequentialPacketNumberUse(pn protocol.PacketNumber) { if h.highestPacketNumber != protocol.InvalidPacketNumber { if pn != h.highestPacketNumber+1 { panic("non-sequential packet number use") } } } func (h *sentPacketHistory) SkippedPacket(pn protocol.PacketNumber) { h.checkSequentialPacketNumberUse(pn) h.highestPacketNumber = pn h.packets = append(h.packets, &packet{ PacketNumber: pn, skippedPacket: true, }) } func (h *sentPacketHistory) SentNonAckElicitingPacket(pn protocol.PacketNumber) { h.checkSequentialPacketNumberUse(pn) h.highestPacketNumber = pn if len(h.packets) > 0 { h.packets = append(h.packets, nil) } } func (h *sentPacketHistory) SentAckElicitingPacket(p *packet) { h.checkSequentialPacketNumberUse(p.PacketNumber) h.highestPacketNumber = p.PacketNumber h.packets = append(h.packets, p) if p.outstanding() { h.numOutstanding++ } } // Iterate iterates through all packets. func (h *sentPacketHistory) Iterate(cb func(*packet) (cont bool, err error)) error { for _, p := range h.packets { if p == nil { continue } cont, err := cb(p) if err != nil { return err } if !cont { return nil } } return nil } // FirstOutstanding returns the first outstanding packet. func (h *sentPacketHistory) FirstOutstanding() *packet { if !h.HasOutstandingPackets() { return nil } for _, p := range h.packets { if p != nil && p.outstanding() { return p } } return nil } func (h *sentPacketHistory) Len() int { return len(h.packets) } func (h *sentPacketHistory) Remove(pn protocol.PacketNumber) error { idx, ok := h.getIndex(pn) if !ok { return fmt.Errorf("packet %d not found in sent packet history", pn) } p := h.packets[idx] if p.outstanding() { h.numOutstanding-- if h.numOutstanding < 0 { panic("negative number of outstanding packets") } } h.packets[idx] = nil // clean up all skipped packets directly before this packet number for idx > 0 { idx-- p := h.packets[idx] if p == nil || !p.skippedPacket { break } h.packets[idx] = nil } if idx == 0 { h.cleanupStart() } if len(h.packets) > 0 && h.packets[0] == nil { panic("remove failed") } return nil } // getIndex gets the index of packet p in the packets slice. func (h *sentPacketHistory) getIndex(p protocol.PacketNumber) (int, bool) { if len(h.packets) == 0 { return 0, false } first := h.packets[0].PacketNumber if p < first { return 0, false } index := int(p - first) if index > len(h.packets)-1 { return 0, false } return index, true } func (h *sentPacketHistory) HasOutstandingPackets() bool { return h.numOutstanding > 0 } // delete all nil entries at the beginning of the packets slice func (h *sentPacketHistory) cleanupStart() { for i, p := range h.packets { if p != nil { h.packets = h.packets[i:] return } } h.packets = h.packets[:0] } func (h *sentPacketHistory) LowestPacketNumber() protocol.PacketNumber { if len(h.packets) == 0 { return protocol.InvalidPacketNumber } return h.packets[0].PacketNumber } func (h *sentPacketHistory) DeclareLost(pn protocol.PacketNumber) { idx, ok := h.getIndex(pn) if !ok { return } p := h.packets[idx] if p.outstanding() { h.numOutstanding-- if h.numOutstanding < 0 { panic("negative number of outstanding packets") } } h.packets[idx] = nil if idx == 0 { h.cleanupStart() } } golang-github-lucas-clemente-quic-go-0.46.0/internal/ackhandler/sent_packet_history_test.go000066400000000000000000000235551465664453100322000ustar00rootroot00000000000000package ackhandler import ( "errors" "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("SentPacketHistory", func() { var hist *sentPacketHistory expectInHistory := func(expected []protocol.PacketNumber) { pns := make([]protocol.PacketNumber, 0, len(expected)) for _, p := range hist.packets { if p != nil && !p.skippedPacket { pns = append(pns, p.PacketNumber) } } if len(expected) == 0 { Expect(pns).To(BeEmpty()) return } Expect(pns).To(Equal(expected)) } expectSkippedInHistory := func(expected []protocol.PacketNumber) { pns := make([]protocol.PacketNumber, 0, len(expected)) for _, p := range hist.packets { if p != nil && p.skippedPacket { pns = append(pns, p.PacketNumber) } } if len(expected) == 0 { Expect(pns).To(BeEmpty()) return } Expect(pns).To(Equal(expected)) } BeforeEach(func() { hist = newSentPacketHistory(true) }) It("saves sent packets", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 0}) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SentAckElicitingPacket(&packet{PacketNumber: 2}) expectInHistory([]protocol.PacketNumber{0, 1, 2}) expectSkippedInHistory(nil) }) It("saves non-ack-eliciting packets", func() { now := time.Now() hist.SentNonAckElicitingPacket(0) hist.SentAckElicitingPacket(&packet{PacketNumber: 1, SendTime: now}) hist.SentNonAckElicitingPacket(2) hist.SentAckElicitingPacket(&packet{PacketNumber: 3, SendTime: now}) expectInHistory([]protocol.PacketNumber{1, 3}) }) It("saves sent packets, with skipped packet number", func() { hist.SkippedPacket(0) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SkippedPacket(2) hist.SentAckElicitingPacket(&packet{PacketNumber: 3}) hist.SentAckElicitingPacket(&packet{PacketNumber: 4}) expectInHistory([]protocol.PacketNumber{1, 3, 4}) expectSkippedInHistory([]protocol.PacketNumber{0, 2}) }) It("doesn't save non-ack-eliciting packets", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SkippedPacket(2) hist.SentNonAckElicitingPacket(3) hist.SentAckElicitingPacket(&packet{PacketNumber: 4}) expectInHistory([]protocol.PacketNumber{1, 4}) }) It("gets the length", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 0}) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SentAckElicitingPacket(&packet{PacketNumber: 2}) Expect(hist.Len()).To(Equal(3)) }) Context("getting the first outstanding packet", func() { It("gets nil, if there are no packets", func() { Expect(hist.FirstOutstanding()).To(BeNil()) }) It("gets the first outstanding packet", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 2}) hist.SentAckElicitingPacket(&packet{PacketNumber: 3}) front := hist.FirstOutstanding() Expect(front).ToNot(BeNil()) Expect(front.PacketNumber).To(Equal(protocol.PacketNumber(2))) hist.Remove(2) front = hist.FirstOutstanding() Expect(front).ToNot(BeNil()) Expect(front.PacketNumber).To(Equal(protocol.PacketNumber(3))) }) It("doesn't regard path MTU packets as outstanding", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 2}) hist.SkippedPacket(3) hist.SentAckElicitingPacket(&packet{PacketNumber: 4, IsPathMTUProbePacket: true}) front := hist.FirstOutstanding() Expect(front).ToNot(BeNil()) Expect(front.PacketNumber).To(Equal(protocol.PacketNumber(2))) }) }) It("removes packets", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 0}) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SentAckElicitingPacket(&packet{PacketNumber: 2}) hist.SentAckElicitingPacket(&packet{PacketNumber: 3}) Expect(hist.Remove(2)).To(Succeed()) expectInHistory([]protocol.PacketNumber{0, 1, 3}) }) It("also removes skipped packets before the removed packet", func() { hist.SkippedPacket(0) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SkippedPacket(2) hist.SkippedPacket(3) hist.SentAckElicitingPacket(&packet{PacketNumber: 4}) expectSkippedInHistory([]protocol.PacketNumber{0, 2, 3}) Expect(hist.Remove(4)).To(Succeed()) expectSkippedInHistory([]protocol.PacketNumber{0}) expectInHistory([]protocol.PacketNumber{1}) Expect(hist.Remove(1)).To(Succeed()) expectInHistory(nil) expectSkippedInHistory(nil) }) It("panics on non-sequential packet number use", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 100}) Expect(func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 102}) }).To(Panic()) }) It("removes and adds packets", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 0}) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SkippedPacket(2) hist.SkippedPacket(3) hist.SentAckElicitingPacket(&packet{PacketNumber: 4}) hist.SkippedPacket(5) hist.SentAckElicitingPacket(&packet{PacketNumber: 6}) Expect(hist.Remove(0)).To(Succeed()) Expect(hist.Remove(1)).To(Succeed()) hist.SentAckElicitingPacket(&packet{PacketNumber: 7}) expectInHistory([]protocol.PacketNumber{4, 6, 7}) }) It("removes the last packet, then adds more", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 0}) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) Expect(hist.Remove(0)).To(Succeed()) Expect(hist.Remove(1)).To(Succeed()) expectInHistory([]protocol.PacketNumber{}) hist.SentAckElicitingPacket(&packet{PacketNumber: 2}) expectInHistory([]protocol.PacketNumber{2}) Expect(hist.Remove(2)).To(Succeed()) expectInHistory(nil) }) It("errors when trying to remove a non existing packet", func() { hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) Expect(hist.Remove(2)).To(MatchError("packet 2 not found in sent packet history")) }) Context("iterating", func() { BeforeEach(func() { hist.SkippedPacket(0) hist.SentAckElicitingPacket(&packet{PacketNumber: 1}) hist.SkippedPacket(2) hist.SkippedPacket(3) hist.SentAckElicitingPacket(&packet{PacketNumber: 4}) hist.SkippedPacket(5) hist.SkippedPacket(6) hist.SkippedPacket(7) hist.SentAckElicitingPacket(&packet{PacketNumber: 8}) }) It("iterates over all packets", func() { var iterations []protocol.PacketNumber Expect(hist.Iterate(func(p *packet) (bool, error) { if p.skippedPacket { return true, nil } iterations = append(iterations, p.PacketNumber) return true, nil })).To(Succeed()) Expect(iterations).To(Equal([]protocol.PacketNumber{1, 4, 8})) }) It("also iterates over skipped packets", func() { var packets, skippedPackets, allPackets []protocol.PacketNumber Expect(hist.Iterate(func(p *packet) (bool, error) { if p.skippedPacket { skippedPackets = append(skippedPackets, p.PacketNumber) } else { packets = append(packets, p.PacketNumber) } allPackets = append(allPackets, p.PacketNumber) return true, nil })).To(Succeed()) Expect(packets).To(Equal([]protocol.PacketNumber{1, 4, 8})) Expect(skippedPackets).To(Equal([]protocol.PacketNumber{0, 2, 3, 5, 6, 7})) Expect(allPackets).To(Equal([]protocol.PacketNumber{0, 1, 2, 3, 4, 5, 6, 7, 8})) }) It("stops iterating", func() { var iterations []protocol.PacketNumber Expect(hist.Iterate(func(p *packet) (bool, error) { if p.skippedPacket { return true, nil } iterations = append(iterations, p.PacketNumber) return p.PacketNumber != 4, nil })).To(Succeed()) Expect(iterations).To(Equal([]protocol.PacketNumber{1, 4})) }) It("returns the error", func() { testErr := errors.New("test error") var iterations []protocol.PacketNumber Expect(hist.Iterate(func(p *packet) (bool, error) { if p.skippedPacket { return true, nil } iterations = append(iterations, p.PacketNumber) if p.PacketNumber == 4 { return false, testErr } return true, nil })).To(MatchError(testErr)) Expect(iterations).To(Equal([]protocol.PacketNumber{1, 4})) }) It("doesn't iterate over deleted packets", func() { hist.Remove(4) var iterations []protocol.PacketNumber Expect(hist.Iterate(func(p *packet) (bool, error) { if p.skippedPacket { return true, nil } iterations = append(iterations, p.PacketNumber) if p.PacketNumber == 4 { Expect(hist.Remove(4)).To(Succeed()) } return true, nil })).To(Succeed()) Expect(iterations).To(Equal([]protocol.PacketNumber{1, 8})) }) It("allows deletions", func() { var iterations []protocol.PacketNumber Expect(hist.Iterate(func(p *packet) (bool, error) { if p.skippedPacket { return true, nil } iterations = append(iterations, p.PacketNumber) if p.PacketNumber == 4 { Expect(hist.Remove(4)).To(Succeed()) } return true, nil })).To(Succeed()) expectInHistory([]protocol.PacketNumber{1, 8}) Expect(iterations).To(Equal([]protocol.PacketNumber{1, 4, 8})) }) }) Context("outstanding packets", func() { It("says if it has outstanding packets", func() { Expect(hist.HasOutstandingPackets()).To(BeFalse()) hist.SentAckElicitingPacket(&packet{EncryptionLevel: protocol.Encryption1RTT, PacketNumber: 0}) Expect(hist.HasOutstandingPackets()).To(BeTrue()) }) It("accounts for deleted packets", func() { hist.SentAckElicitingPacket(&packet{ PacketNumber: 10, EncryptionLevel: protocol.Encryption1RTT, }) Expect(hist.HasOutstandingPackets()).To(BeTrue()) Expect(hist.Remove(10)).To(Succeed()) Expect(hist.HasOutstandingPackets()).To(BeFalse()) }) It("counts the number of packets", func() { hist.SentAckElicitingPacket(&packet{ PacketNumber: 10, EncryptionLevel: protocol.Encryption1RTT, }) hist.SentAckElicitingPacket(&packet{ PacketNumber: 11, EncryptionLevel: protocol.Encryption1RTT, }) Expect(hist.Remove(11)).To(Succeed()) Expect(hist.HasOutstandingPackets()).To(BeTrue()) Expect(hist.Remove(10)).To(Succeed()) Expect(hist.HasOutstandingPackets()).To(BeFalse()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/000077500000000000000000000000001465664453100245735ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/bandwidth.go000066400000000000000000000011401465664453100270620ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/quic-go/quic-go/internal/protocol" ) // Bandwidth of a connection type Bandwidth uint64 const infBandwidth Bandwidth = math.MaxUint64 const ( // BitsPerSecond is 1 bit per second BitsPerSecond Bandwidth = 1 // BytesPerSecond is 1 byte per second BytesPerSecond = 8 * BitsPerSecond ) // BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta func BandwidthFromDelta(bytes protocol.ByteCount, delta time.Duration) Bandwidth { return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/bandwidth_test.go000066400000000000000000000004161465664453100301260ustar00rootroot00000000000000package congestion import ( "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Bandwidth", func() { It("converts from time delta", func() { Expect(BandwidthFromDelta(1, time.Millisecond)).To(Equal(1000 * BytesPerSecond)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/clock.go000066400000000000000000000005161465664453100262170ustar00rootroot00000000000000package congestion import "time" // A Clock returns the current time type Clock interface { Now() time.Time } // DefaultClock implements the Clock interface using the Go stdlib clock. type DefaultClock struct{} var _ Clock = DefaultClock{} // Now gets the current time func (DefaultClock) Now() time.Time { return time.Now() } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/congestion_suite_test.go000066400000000000000000000003111465664453100315350ustar00rootroot00000000000000package congestion import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestCongestion(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Congestion Suite") } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/cubic.go000066400000000000000000000204211465664453100262060ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/quic-go/quic-go/internal/protocol" ) // This cubic implementation is based on the one found in Chromiums's QUIC // implementation, in the files net/quic/congestion_control/cubic.{hh,cc}. // Constants based on TCP defaults. // The following constants are in 2^10 fractions of a second instead of ms to // allow a 10 shift right to divide. // 1024*1024^3 (first 1024 is from 0.100^3) // where 0.100 is 100 ms which is the scaling round trip time. const ( cubeScale = 40 cubeCongestionWindowScale = 410 cubeFactor = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize // TODO: when re-enabling cubic, make sure to use the actual packet size here maxDatagramSize = protocol.ByteCount(protocol.InitialPacketSize) ) const defaultNumConnections = 1 // Default Cubic backoff factor const beta float32 = 0.7 // Additional backoff factor when loss occurs in the concave part of the Cubic // curve. This additional backoff factor is expected to give up bandwidth to // new concurrent flows and speed up convergence. const betaLastMax float32 = 0.85 // Cubic implements the cubic algorithm from TCP type Cubic struct { clock Clock // Number of connections to simulate. numConnections int // Time when this cycle started, after last loss event. epoch time.Time // Max congestion window used just before last loss event. // Note: to improve fairness to other streams an additional back off is // applied to this value if the new value is below our latest value. lastMaxCongestionWindow protocol.ByteCount // Number of acked bytes since the cycle started (epoch). ackedBytesCount protocol.ByteCount // TCP Reno equivalent congestion window in packets. estimatedTCPcongestionWindow protocol.ByteCount // Origin point of cubic function. originPointCongestionWindow protocol.ByteCount // Time to origin point of cubic function in 2^10 fractions of a second. timeToOriginPoint uint32 // Last congestion window in packets computed by cubic function. lastTargetCongestionWindow protocol.ByteCount } // NewCubic returns a new Cubic instance func NewCubic(clock Clock) *Cubic { c := &Cubic{ clock: clock, numConnections: defaultNumConnections, } c.Reset() return c } // Reset is called after a timeout to reset the cubic state func (c *Cubic) Reset() { c.epoch = time.Time{} c.lastMaxCongestionWindow = 0 c.ackedBytesCount = 0 c.estimatedTCPcongestionWindow = 0 c.originPointCongestionWindow = 0 c.timeToOriginPoint = 0 c.lastTargetCongestionWindow = 0 } func (c *Cubic) alpha() float32 { // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that // beta here is a cwnd multiplier, and is equal to 1-beta from the paper. // We derive the equivalent alpha for an N-connection emulation as: b := c.beta() return 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b) } func (c *Cubic) beta() float32 { // kNConnectionBeta is the backoff factor after loss for our N-connection // emulation, which emulates the effective backoff of an ensemble of N // TCP-Reno connections on a single loss event. The effective multiplier is // computed as: return (float32(c.numConnections) - 1 + beta) / float32(c.numConnections) } func (c *Cubic) betaLastMax() float32 { // betaLastMax is the additional backoff factor after loss for our // N-connection emulation, which emulates the additional backoff of // an ensemble of N TCP-Reno connections on a single loss event. The // effective multiplier is computed as: return (float32(c.numConnections) - 1 + betaLastMax) / float32(c.numConnections) } // OnApplicationLimited is called on ack arrival when sender is unable to use // the available congestion window. Resets Cubic state during quiescence. func (c *Cubic) OnApplicationLimited() { // When sender is not using the available congestion window, the window does // not grow. But to be RTT-independent, Cubic assumes that the sender has been // using the entire window during the time since the beginning of the current // "epoch" (the end of the last loss recovery period). Since // application-limited periods break this assumption, we reset the epoch when // in such a period. This reset effectively freezes congestion window growth // through application-limited periods and allows Cubic growth to continue // when the entire window is being used. c.epoch = time.Time{} } // CongestionWindowAfterPacketLoss computes a new congestion window to use after // a loss event. Returns the new congestion window in packets. The new // congestion window is a multiplicative decrease of our current window. func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow protocol.ByteCount) protocol.ByteCount { if currentCongestionWindow+maxDatagramSize < c.lastMaxCongestionWindow { // We never reached the old max, so assume we are competing with another // flow. Use our extra back off factor to allow the other flow to go up. c.lastMaxCongestionWindow = protocol.ByteCount(c.betaLastMax() * float32(currentCongestionWindow)) } else { c.lastMaxCongestionWindow = currentCongestionWindow } c.epoch = time.Time{} // Reset time. return protocol.ByteCount(float32(currentCongestionWindow) * c.beta()) } // CongestionWindowAfterAck computes a new congestion window to use after a received ACK. // Returns the new congestion window in packets. The new congestion window // follows a cubic function that depends on the time passed since last // packet loss. func (c *Cubic) CongestionWindowAfterAck( ackedBytes protocol.ByteCount, currentCongestionWindow protocol.ByteCount, delayMin time.Duration, eventTime time.Time, ) protocol.ByteCount { c.ackedBytesCount += ackedBytes if c.epoch.IsZero() { // First ACK after a loss event. c.epoch = eventTime // Start of epoch. c.ackedBytesCount = ackedBytes // Reset count. // Reset estimated_tcp_congestion_window_ to be in sync with cubic. c.estimatedTCPcongestionWindow = currentCongestionWindow if c.lastMaxCongestionWindow <= currentCongestionWindow { c.timeToOriginPoint = 0 c.originPointCongestionWindow = currentCongestionWindow } else { c.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow)))) c.originPointCongestionWindow = c.lastMaxCongestionWindow } } // Change the time unit from microseconds to 2^10 fractions per second. Take // the round trip time in account. This is done to allow us to use shift as a // divide operator. elapsedTime := int64(eventTime.Add(delayMin).Sub(c.epoch)/time.Microsecond) << 10 / (1000 * 1000) // Right-shifts of negative, signed numbers have implementation-dependent // behavior, so force the offset to be positive, as is done in the kernel. offset := int64(c.timeToOriginPoint) - elapsedTime if offset < 0 { offset = -offset } deltaCongestionWindow := protocol.ByteCount(cubeCongestionWindowScale*offset*offset*offset) * maxDatagramSize >> cubeScale var targetCongestionWindow protocol.ByteCount if elapsedTime > int64(c.timeToOriginPoint) { targetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow } else { targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow } // Limit the CWND increase to half the acked bytes. targetCongestionWindow = min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) // Increase the window by approximately Alpha * 1 MSS of bytes every // time we ack an estimated tcp window of bytes. For small // congestion windows (less than 25), the formula below will // increase slightly slower than linearly per estimated tcp window // of bytes. c.estimatedTCPcongestionWindow += protocol.ByteCount(float32(c.ackedBytesCount) * c.alpha() * float32(maxDatagramSize) / float32(c.estimatedTCPcongestionWindow)) c.ackedBytesCount = 0 // We have a new cubic congestion window. c.lastTargetCongestionWindow = targetCongestionWindow // Compute target congestion_window based on cubic target and estimated TCP // congestion_window, use highest (fastest). if targetCongestionWindow < c.estimatedTCPcongestionWindow { targetCongestionWindow = c.estimatedTCPcongestionWindow } return targetCongestionWindow } // SetNumConnections sets the number of emulated connections func (c *Cubic) SetNumConnections(n int) { c.numConnections = n } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/cubic_sender.go000066400000000000000000000233751465664453100275610ustar00rootroot00000000000000package congestion import ( "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) const ( // maxDatagramSize is the default maximum packet size used in the Linux TCP implementation. // Used in QUIC for congestion window computations in bytes. initialMaxDatagramSize = protocol.ByteCount(protocol.InitialPacketSize) maxBurstPackets = 3 renoBeta = 0.7 // Reno backoff factor. minCongestionWindowPackets = 2 initialCongestionWindow = 32 ) type cubicSender struct { hybridSlowStart HybridSlowStart rttStats *utils.RTTStats cubic *Cubic pacer *pacer clock Clock reno bool // Track the largest packet that has been sent. largestSentPacketNumber protocol.PacketNumber // Track the largest packet that has been acked. largestAckedPacketNumber protocol.PacketNumber // Track the largest packet number outstanding when a CWND cutback occurs. largestSentAtLastCutback protocol.PacketNumber // Whether the last loss event caused us to exit slowstart. // Used for stats collection of slowstartPacketsLost lastCutbackExitedSlowstart bool // Congestion window in bytes. congestionWindow protocol.ByteCount // Slow start congestion window in bytes, aka ssthresh. slowStartThreshold protocol.ByteCount // ACK counter for the Reno implementation. numAckedPackets uint64 initialCongestionWindow protocol.ByteCount initialMaxCongestionWindow protocol.ByteCount maxDatagramSize protocol.ByteCount lastState logging.CongestionState tracer *logging.ConnectionTracer } var ( _ SendAlgorithm = &cubicSender{} _ SendAlgorithmWithDebugInfos = &cubicSender{} ) // NewCubicSender makes a new cubic sender func NewCubicSender( clock Clock, rttStats *utils.RTTStats, initialMaxDatagramSize protocol.ByteCount, reno bool, tracer *logging.ConnectionTracer, ) *cubicSender { return newCubicSender( clock, rttStats, reno, initialMaxDatagramSize, initialCongestionWindow*initialMaxDatagramSize, protocol.MaxCongestionWindowPackets*initialMaxDatagramSize, tracer, ) } func newCubicSender( clock Clock, rttStats *utils.RTTStats, reno bool, initialMaxDatagramSize, initialCongestionWindow, initialMaxCongestionWindow protocol.ByteCount, tracer *logging.ConnectionTracer, ) *cubicSender { c := &cubicSender{ rttStats: rttStats, largestSentPacketNumber: protocol.InvalidPacketNumber, largestAckedPacketNumber: protocol.InvalidPacketNumber, largestSentAtLastCutback: protocol.InvalidPacketNumber, initialCongestionWindow: initialCongestionWindow, initialMaxCongestionWindow: initialMaxCongestionWindow, congestionWindow: initialCongestionWindow, slowStartThreshold: protocol.MaxByteCount, cubic: NewCubic(clock), clock: clock, reno: reno, tracer: tracer, maxDatagramSize: initialMaxDatagramSize, } c.pacer = newPacer(c.BandwidthEstimate) if c.tracer != nil && c.tracer.UpdatedCongestionState != nil { c.lastState = logging.CongestionStateSlowStart c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart) } return c } // TimeUntilSend returns when the next packet should be sent. func (c *cubicSender) TimeUntilSend(_ protocol.ByteCount) time.Time { return c.pacer.TimeUntilSend() } func (c *cubicSender) HasPacingBudget(now time.Time) bool { return c.pacer.Budget(now) >= c.maxDatagramSize } func (c *cubicSender) maxCongestionWindow() protocol.ByteCount { return c.maxDatagramSize * protocol.MaxCongestionWindowPackets } func (c *cubicSender) minCongestionWindow() protocol.ByteCount { return c.maxDatagramSize * minCongestionWindowPackets } func (c *cubicSender) OnPacketSent( sentTime time.Time, _ protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool, ) { c.pacer.SentPacket(sentTime, bytes) if !isRetransmittable { return } c.largestSentPacketNumber = packetNumber c.hybridSlowStart.OnPacketSent(packetNumber) } func (c *cubicSender) CanSend(bytesInFlight protocol.ByteCount) bool { return bytesInFlight < c.GetCongestionWindow() } func (c *cubicSender) InRecovery() bool { return c.largestAckedPacketNumber != protocol.InvalidPacketNumber && c.largestAckedPacketNumber <= c.largestSentAtLastCutback } func (c *cubicSender) InSlowStart() bool { return c.GetCongestionWindow() < c.slowStartThreshold } func (c *cubicSender) GetCongestionWindow() protocol.ByteCount { return c.congestionWindow } func (c *cubicSender) MaybeExitSlowStart() { if c.InSlowStart() && c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/c.maxDatagramSize) { // exit slow start c.slowStartThreshold = c.congestionWindow c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) } } func (c *cubicSender) OnPacketAcked( ackedPacketNumber protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time, ) { c.largestAckedPacketNumber = max(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { return } c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime) if c.InSlowStart() { c.hybridSlowStart.OnPacketAcked(ackedPacketNumber) } } func (c *cubicSender) OnCongestionEvent(packetNumber protocol.PacketNumber, lostBytes, priorInFlight protocol.ByteCount) { // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets // already sent should be treated as a single loss event, since it's expected. if packetNumber <= c.largestSentAtLastCutback { return } c.lastCutbackExitedSlowstart = c.InSlowStart() c.maybeTraceStateChange(logging.CongestionStateRecovery) if c.reno { c.congestionWindow = protocol.ByteCount(float64(c.congestionWindow) * renoBeta) } else { c.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow) } if minCwnd := c.minCongestionWindow(); c.congestionWindow < minCwnd { c.congestionWindow = minCwnd } c.slowStartThreshold = c.congestionWindow c.largestSentAtLastCutback = c.largestSentPacketNumber // reset packet count from congestion avoidance mode. We start // counting again when we're out of recovery. c.numAckedPackets = 0 } // Called when we receive an ack. Normal TCP tracks how many packets one ack // represents, but quic has a separate ack for each packet. func (c *cubicSender) maybeIncreaseCwnd( _ protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time, ) { // Do not increase the congestion window unless the sender is close to using // the current window. if !c.isCwndLimited(priorInFlight) { c.cubic.OnApplicationLimited() c.maybeTraceStateChange(logging.CongestionStateApplicationLimited) return } if c.congestionWindow >= c.maxCongestionWindow() { return } if c.InSlowStart() { // TCP slow start, exponential growth, increase by one for each ACK. c.congestionWindow += c.maxDatagramSize c.maybeTraceStateChange(logging.CongestionStateSlowStart) return } // Congestion avoidance c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) if c.reno { // Classic Reno congestion avoidance. c.numAckedPackets++ if c.numAckedPackets >= uint64(c.congestionWindow/c.maxDatagramSize) { c.congestionWindow += c.maxDatagramSize c.numAckedPackets = 0 } } else { c.congestionWindow = min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) } } func (c *cubicSender) isCwndLimited(bytesInFlight protocol.ByteCount) bool { congestionWindow := c.GetCongestionWindow() if bytesInFlight >= congestionWindow { return true } availableBytes := congestionWindow - bytesInFlight slowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2 return slowStartLimited || availableBytes <= maxBurstPackets*c.maxDatagramSize } // BandwidthEstimate returns the current bandwidth estimate func (c *cubicSender) BandwidthEstimate() Bandwidth { srtt := c.rttStats.SmoothedRTT() if srtt == 0 { // If we haven't measured an rtt, the bandwidth estimate is unknown. return infBandwidth } return BandwidthFromDelta(c.GetCongestionWindow(), srtt) } // OnRetransmissionTimeout is called on an retransmission timeout func (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) { c.largestSentAtLastCutback = protocol.InvalidPacketNumber if !packetsRetransmitted { return } c.hybridSlowStart.Restart() c.cubic.Reset() c.slowStartThreshold = c.congestionWindow / 2 c.congestionWindow = c.minCongestionWindow() } // OnConnectionMigration is called when the connection is migrated (?) func (c *cubicSender) OnConnectionMigration() { c.hybridSlowStart.Restart() c.largestSentPacketNumber = protocol.InvalidPacketNumber c.largestAckedPacketNumber = protocol.InvalidPacketNumber c.largestSentAtLastCutback = protocol.InvalidPacketNumber c.lastCutbackExitedSlowstart = false c.cubic.Reset() c.numAckedPackets = 0 c.congestionWindow = c.initialCongestionWindow c.slowStartThreshold = c.initialMaxCongestionWindow } func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) { if c.tracer == nil || c.tracer.UpdatedCongestionState == nil || new == c.lastState { return } c.tracer.UpdatedCongestionState(new) c.lastState = new } func (c *cubicSender) SetMaxDatagramSize(s protocol.ByteCount) { if s < c.maxDatagramSize { panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s)) } cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow() c.maxDatagramSize = s if cwndIsMinCwnd { c.congestionWindow = c.minCongestionWindow() } c.pacer.SetMaxDatagramSize(s) } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/cubic_sender_test.go000066400000000000000000000455661465664453100306260ustar00rootroot00000000000000package congestion import ( "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) const ( initialCongestionWindowPackets = 10 defaultWindowTCP = protocol.ByteCount(initialCongestionWindowPackets) * maxDatagramSize ) type mockClock time.Time func (c *mockClock) Now() time.Time { return time.Time(*c) } func (c *mockClock) Advance(d time.Duration) { *c = mockClock(time.Time(*c).Add(d)) } const MaxCongestionWindow = 200 * maxDatagramSize var _ = Describe("Cubic Sender", func() { var ( sender *cubicSender clock mockClock bytesInFlight protocol.ByteCount packetNumber protocol.PacketNumber ackedPacketNumber protocol.PacketNumber rttStats *utils.RTTStats ) BeforeEach(func() { bytesInFlight = 0 packetNumber = 1 ackedPacketNumber = 0 clock = mockClock{} rttStats = utils.NewRTTStats() sender = newCubicSender( &clock, rttStats, true, /*reno*/ protocol.InitialPacketSize, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow, nil, ) }) SendAvailableSendWindowLen := func(packetLength protocol.ByteCount) int { var packetsSent int for sender.CanSend(bytesInFlight) { sender.OnPacketSent(clock.Now(), bytesInFlight, packetNumber, packetLength, true) packetNumber++ packetsSent++ bytesInFlight += packetLength } return packetsSent } // Normal is that TCP acks every other segment. AckNPackets := func(n int) { rttStats.UpdateRTT(60*time.Millisecond, 0, clock.Now()) sender.MaybeExitSlowStart() for i := 0; i < n; i++ { ackedPacketNumber++ sender.OnPacketAcked(ackedPacketNumber, maxDatagramSize, bytesInFlight, clock.Now()) } bytesInFlight -= protocol.ByteCount(n) * maxDatagramSize clock.Advance(time.Millisecond) } LoseNPacketsLen := func(n int, packetLength protocol.ByteCount) { for i := 0; i < n; i++ { ackedPacketNumber++ sender.OnCongestionEvent(ackedPacketNumber, packetLength, bytesInFlight) } bytesInFlight -= protocol.ByteCount(n) * packetLength } // Does not increment acked_packet_number_. LosePacket := func(number protocol.PacketNumber) { sender.OnCongestionEvent(number, maxDatagramSize, bytesInFlight) bytesInFlight -= maxDatagramSize } SendAvailableSendWindow := func() int { return SendAvailableSendWindowLen(maxDatagramSize) } LoseNPackets := func(n int) { LoseNPacketsLen(n, maxDatagramSize) } It("has the right values at startup", func() { // At startup make sure we are at the default. Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) // Make sure we can send. Expect(sender.TimeUntilSend(0)).To(BeZero()) Expect(sender.CanSend(bytesInFlight)).To(BeTrue()) // And that window is un-affected. Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) // Fill the send window with data, then verify that we can't send. SendAvailableSendWindow() Expect(sender.CanSend(bytesInFlight)).To(BeFalse()) }) It("paces", func() { rttStats.UpdateRTT(10*time.Millisecond, 0, time.Now()) clock.Advance(time.Hour) // Fill the send window with data, then verify that we can't send. SendAvailableSendWindow() AckNPackets(1) delay := sender.TimeUntilSend(bytesInFlight) Expect(delay).ToNot(BeZero()) Expect(delay.Sub(clock.Now())).To(BeNumerically("<", time.Hour)) }) It("application limited slow start", func() { // Send exactly 10 packets and ensure the CWND ends at 14 packets. const numberOfAcks = 5 // At startup make sure we can send. Expect(sender.CanSend(0)).To(BeTrue()) Expect(sender.TimeUntilSend(0)).To(BeZero()) SendAvailableSendWindow() for i := 0; i < numberOfAcks; i++ { AckNPackets(2) } bytesToSend := sender.GetCongestionWindow() // It's expected 2 acks will arrive when the bytes_in_flight are greater than // half the CWND. Expect(bytesToSend).To(Equal(defaultWindowTCP + maxDatagramSize*2*2)) }) It("exponential slow start", func() { const numberOfAcks = 20 // At startup make sure we can send. Expect(sender.CanSend(0)).To(BeTrue()) Expect(sender.TimeUntilSend(0)).To(BeZero()) Expect(sender.BandwidthEstimate()).To(Equal(infBandwidth)) // Make sure we can send. Expect(sender.TimeUntilSend(0)).To(BeZero()) for i := 0; i < numberOfAcks; i++ { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) } cwnd := sender.GetCongestionWindow() Expect(cwnd).To(Equal(defaultWindowTCP + maxDatagramSize*2*numberOfAcks)) Expect(sender.BandwidthEstimate()).To(Equal(BandwidthFromDelta(cwnd, rttStats.SmoothedRTT()))) }) It("slow start packet loss", func() { const numberOfAcks = 10 for i := 0; i < numberOfAcks; i++ { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) } SendAvailableSendWindow() expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Lose a packet to exit slow start. LoseNPackets(1) packetsInRecoveryWindow := expectedSendWindow / maxDatagramSize // We should now have fallen out of slow start with a reduced window. expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Recovery phase. We need to ack every packet in the recovery window before // we exit recovery. numberOfPacketsInWindow := expectedSendWindow / maxDatagramSize AckNPackets(int(packetsInRecoveryWindow)) SendAvailableSendWindow() Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // We need to ack an entire window before we increase CWND by 1. AckNPackets(int(numberOfPacketsInWindow) - 2) SendAvailableSendWindow() Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Next ack should increase cwnd by 1. AckNPackets(1) expectedSendWindow += maxDatagramSize Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Now RTO and ensure slow start gets reset. Expect(sender.hybridSlowStart.Started()).To(BeTrue()) sender.OnRetransmissionTimeout(true) Expect(sender.hybridSlowStart.Started()).To(BeFalse()) }) It("slow start packet loss PRR", func() { // Test based on the first example in RFC6937. // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example. const numberOfAcks = 5 for i := 0; i < numberOfAcks; i++ { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) } SendAvailableSendWindow() expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) LoseNPackets(1) // We should now have fallen out of slow start with a reduced window. sendWindowBeforeLoss := expectedSendWindow expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Testing TCP proportional rate reduction. // We should send packets paced over the received acks for the remaining // outstanding packets. The number of packets before we exit recovery is the // original CWND minus the packet that has been lost and the one which // triggered the loss. remainingPacketsInRecovery := sendWindowBeforeLoss/maxDatagramSize - 2 for i := protocol.ByteCount(0); i < remainingPacketsInRecovery; i++ { AckNPackets(1) SendAvailableSendWindow() Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) } // We need to ack another window before we increase CWND by 1. numberOfPacketsInWindow := expectedSendWindow / maxDatagramSize for i := protocol.ByteCount(0); i < numberOfPacketsInWindow; i++ { AckNPackets(1) Expect(SendAvailableSendWindow()).To(Equal(1)) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) } AckNPackets(1) expectedSendWindow += maxDatagramSize Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) }) It("slow start burst packet loss PRR", func() { // Test based on the second example in RFC6937, though we also implement // forward acknowledgements, so the first two incoming acks will trigger // PRR immediately. // Ack 20 packets in 10 acks to raise the CWND to 30. const numberOfAcks = 10 for i := 0; i < numberOfAcks; i++ { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) } SendAvailableSendWindow() expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Lose one more than the congestion window reduction, so that after loss, // bytes_in_flight is lesser than the congestion window. sendWindowAfterLoss := protocol.ByteCount(renoBeta * float32(expectedSendWindow)) numPacketsToLose := (expectedSendWindow-sendWindowAfterLoss)/maxDatagramSize + 1 LoseNPackets(int(numPacketsToLose)) // Immediately after the loss, ensure at least one packet can be sent. // Losses without subsequent acks can occur with timer based loss detection. Expect(sender.CanSend(bytesInFlight)).To(BeTrue()) AckNPackets(1) // We should now have fallen out of slow start with a reduced window. expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Only 2 packets should be allowed to be sent, per PRR-SSRB Expect(SendAvailableSendWindow()).To(Equal(2)) // Ack the next packet, which triggers another loss. LoseNPackets(1) AckNPackets(1) // Send 2 packets to simulate PRR-SSRB. Expect(SendAvailableSendWindow()).To(Equal(2)) // Ack the next packet, which triggers another loss. LoseNPackets(1) AckNPackets(1) // Send 2 packets to simulate PRR-SSRB. Expect(SendAvailableSendWindow()).To(Equal(2)) // Exit recovery and return to sending at the new rate. for i := 0; i < numberOfAcks; i++ { AckNPackets(1) Expect(SendAvailableSendWindow()).To(Equal(1)) } }) It("RTO congestion window", func() { Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) Expect(sender.slowStartThreshold).To(Equal(protocol.MaxByteCount)) // Expect the window to decrease to the minimum once the RTO fires // and slow start threshold to be set to 1/2 of the CWND. sender.OnRetransmissionTimeout(true) Expect(sender.GetCongestionWindow()).To(Equal(2 * maxDatagramSize)) Expect(sender.slowStartThreshold).To(Equal(5 * maxDatagramSize)) }) It("RTO congestion window no retransmission", func() { Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) // Expect the window to remain unchanged if the RTO fires but no // packets are retransmitted. sender.OnRetransmissionTimeout(false) Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) }) It("tcp cubic reset epoch on quiescence", func() { const maxCongestionWindow = 50 const maxCongestionWindowBytes = maxCongestionWindow * maxDatagramSize sender = newCubicSender(&clock, rttStats, false, protocol.InitialPacketSize, initialCongestionWindowPackets*maxDatagramSize, maxCongestionWindowBytes, nil) numSent := SendAvailableSendWindow() // Make sure we fall out of slow start. savedCwnd := sender.GetCongestionWindow() LoseNPackets(1) Expect(savedCwnd).To(BeNumerically(">", sender.GetCongestionWindow())) // Ack the rest of the outstanding packets to get out of recovery. for i := 1; i < numSent; i++ { AckNPackets(1) } Expect(bytesInFlight).To(BeZero()) // Send a new window of data and ack all; cubic growth should occur. savedCwnd = sender.GetCongestionWindow() numSent = SendAvailableSendWindow() for i := 0; i < numSent; i++ { AckNPackets(1) } Expect(savedCwnd).To(BeNumerically("<", sender.GetCongestionWindow())) Expect(maxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow())) Expect(bytesInFlight).To(BeZero()) // Quiescent time of 100 seconds clock.Advance(100 * time.Second) // Send new window of data and ack one packet. Cubic epoch should have // been reset; ensure cwnd increase is not dramatic. savedCwnd = sender.GetCongestionWindow() SendAvailableSendWindow() AckNPackets(1) Expect(savedCwnd).To(BeNumerically("~", sender.GetCongestionWindow(), maxDatagramSize)) Expect(maxCongestionWindowBytes).To(BeNumerically(">", sender.GetCongestionWindow())) }) It("multiple losses in one window", func() { SendAvailableSendWindow() initialWindow := sender.GetCongestionWindow() LosePacket(ackedPacketNumber + 1) postLossWindow := sender.GetCongestionWindow() Expect(initialWindow).To(BeNumerically(">", postLossWindow)) LosePacket(ackedPacketNumber + 3) Expect(sender.GetCongestionWindow()).To(Equal(postLossWindow)) LosePacket(packetNumber - 1) Expect(sender.GetCongestionWindow()).To(Equal(postLossWindow)) // Lose a later packet and ensure the window decreases. LosePacket(packetNumber) Expect(postLossWindow).To(BeNumerically(">", sender.GetCongestionWindow())) }) It("1 connection congestion avoidance at end of recovery", func() { // Ack 10 packets in 5 acks to raise the CWND to 20. const numberOfAcks = 5 for i := 0; i < numberOfAcks; i++ { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) } SendAvailableSendWindow() expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) LoseNPackets(1) // We should now have fallen out of slow start with a reduced window. expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // No congestion window growth should occur in recovery phase, i.e., until the // currently outstanding 20 packets are acked. for i := 0; i < 10; i++ { // Send our full send window. SendAvailableSendWindow() Expect(sender.InRecovery()).To(BeTrue()) AckNPackets(2) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) } Expect(sender.InRecovery()).To(BeFalse()) // Out of recovery now. Congestion window should not grow during RTT. for i := protocol.ByteCount(0); i < expectedSendWindow/maxDatagramSize-2; i += 2 { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) } // Next ack should cause congestion window to grow by 1MSS. SendAvailableSendWindow() AckNPackets(2) expectedSendWindow += maxDatagramSize Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) }) It("no PRR", func() { SendAvailableSendWindow() LoseNPackets(9) AckNPackets(1) Expect(sender.GetCongestionWindow()).To(Equal(protocol.ByteCount(renoBeta * float32(defaultWindowTCP)))) windowInPackets := renoBeta * float32(defaultWindowTCP) / float32(maxDatagramSize) numSent := SendAvailableSendWindow() Expect(numSent).To(BeEquivalentTo(windowInPackets)) }) It("reset after connection migration", func() { Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) Expect(sender.slowStartThreshold).To(Equal(protocol.MaxByteCount)) // Starts with slow start. const numberOfAcks = 10 for i := 0; i < numberOfAcks; i++ { // Send our full send window. SendAvailableSendWindow() AckNPackets(2) } SendAvailableSendWindow() expectedSendWindow := defaultWindowTCP + (maxDatagramSize * 2 * numberOfAcks) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) // Loses a packet to exit slow start. LoseNPackets(1) // We should now have fallen out of slow start with a reduced window. Slow // start threshold is also updated. expectedSendWindow = protocol.ByteCount(float32(expectedSendWindow) * renoBeta) Expect(sender.GetCongestionWindow()).To(Equal(expectedSendWindow)) Expect(sender.slowStartThreshold).To(Equal(expectedSendWindow)) // Resets cwnd and slow start threshold on connection migrations. sender.OnConnectionMigration() Expect(sender.GetCongestionWindow()).To(Equal(defaultWindowTCP)) Expect(sender.slowStartThreshold).To(Equal(MaxCongestionWindow)) Expect(sender.hybridSlowStart.Started()).To(BeFalse()) }) It("slow starts up to the maximum congestion window", func() { const initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize sender = newCubicSender(&clock, rttStats, true, protocol.InitialPacketSize, initialCongestionWindowPackets*maxDatagramSize, initialMaxCongestionWindow, nil) for i := 1; i < protocol.MaxCongestionWindowPackets; i++ { sender.MaybeExitSlowStart() sender.OnPacketAcked(protocol.PacketNumber(i), 1350, sender.GetCongestionWindow(), clock.Now()) } Expect(sender.GetCongestionWindow()).To(Equal(initialMaxCongestionWindow)) }) It("doesn't allow reductions of the maximum packet size", func() { Expect(func() { sender.SetMaxDatagramSize(initialMaxDatagramSize - 1) }).To(Panic()) }) It("slow starts up to maximum congestion window, if larger packets are sent", func() { const initialMaxCongestionWindow = protocol.MaxCongestionWindowPackets * initialMaxDatagramSize sender = newCubicSender(&clock, rttStats, true, protocol.InitialPacketSize, initialCongestionWindowPackets*maxDatagramSize, initialMaxCongestionWindow, nil) const packetSize = initialMaxDatagramSize + 100 sender.SetMaxDatagramSize(packetSize) for i := 1; i < protocol.MaxCongestionWindowPackets; i++ { sender.OnPacketAcked(protocol.PacketNumber(i), packetSize, sender.GetCongestionWindow(), clock.Now()) } const maxCwnd = protocol.MaxCongestionWindowPackets * packetSize Expect(sender.GetCongestionWindow()).To(And( BeNumerically(">", maxCwnd), BeNumerically("<=", maxCwnd+packetSize), )) }) It("limit cwnd increase in congestion avoidance", func() { // Enable Cubic. sender = newCubicSender(&clock, rttStats, false, protocol.InitialPacketSize, initialCongestionWindowPackets*maxDatagramSize, MaxCongestionWindow, nil) numSent := SendAvailableSendWindow() // Make sure we fall out of slow start. savedCwnd := sender.GetCongestionWindow() LoseNPackets(1) Expect(savedCwnd).To(BeNumerically(">", sender.GetCongestionWindow())) // Ack the rest of the outstanding packets to get out of recovery. for i := 1; i < numSent; i++ { AckNPackets(1) } Expect(bytesInFlight).To(BeZero()) savedCwnd = sender.GetCongestionWindow() SendAvailableSendWindow() // Ack packets until the CWND increases. for sender.GetCongestionWindow() == savedCwnd { AckNPackets(1) SendAvailableSendWindow() } // Bytes in flight may be larger than the CWND if the CWND isn't an exact // multiple of the packet sizes being sent. Expect(bytesInFlight).To(BeNumerically(">=", sender.GetCongestionWindow())) savedCwnd = sender.GetCongestionWindow() // Advance time 2 seconds waiting for an ack. clock.Advance(2 * time.Second) // Ack two packets. The CWND should increase by only one packet. AckNPackets(2) Expect(sender.GetCongestionWindow()).To(Equal(savedCwnd + maxDatagramSize)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/cubic_test.go000066400000000000000000000246371465664453100272620ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) const ( numConnections uint32 = 2 nConnectionBeta float32 = (float32(numConnections) - 1 + beta) / float32(numConnections) nConnectionBetaLastMax float32 = (float32(numConnections) - 1 + betaLastMax) / float32(numConnections) nConnectionAlpha float32 = 3 * float32(numConnections) * float32(numConnections) * (1 - nConnectionBeta) / (1 + nConnectionBeta) maxCubicTimeInterval = 30 * time.Millisecond ) var _ = Describe("Cubic", func() { var ( clock mockClock cubic *Cubic ) BeforeEach(func() { clock = mockClock{} cubic = NewCubic(&clock) cubic.SetNumConnections(int(numConnections)) }) renoCwnd := func(currentCwnd protocol.ByteCount) protocol.ByteCount { return currentCwnd + protocol.ByteCount(float32(maxDatagramSize)*nConnectionAlpha*float32(maxDatagramSize)/float32(currentCwnd)) } cubicConvexCwnd := func(initialCwnd protocol.ByteCount, rtt, elapsedTime time.Duration) protocol.ByteCount { offset := protocol.ByteCount((elapsedTime+rtt)/time.Microsecond) << 10 / 1000000 deltaCongestionWindow := 410 * offset * offset * offset * maxDatagramSize >> 40 return initialCwnd + deltaCongestionWindow } It("works above origin (with tighter bounds)", func() { // Convex growth. const rttMin = 100 * time.Millisecond const rttMinS = float32(rttMin/time.Millisecond) / 1000.0 currentCwnd := 10 * maxDatagramSize initialCwnd := currentCwnd clock.Advance(time.Millisecond) initialTime := clock.Now() expectedFirstCwnd := renoCwnd(currentCwnd) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, initialTime) Expect(expectedFirstCwnd).To(Equal(currentCwnd)) // Normal TCP phase. // The maximum number of expected reno RTTs can be calculated by // finding the point where the cubic curve and the reno curve meet. maxRenoRtts := int(math.Sqrt(float64(nConnectionAlpha/(0.4*rttMinS*rttMinS*rttMinS))) - 2) for i := 0; i < maxRenoRtts; i++ { // Alternatively, we expect it to increase by one, every time we // receive current_cwnd/Alpha acks back. (This is another way of // saying we expect cwnd to increase by approximately Alpha once // we receive current_cwnd number ofacks back). numAcksThisEpoch := int(float32(currentCwnd/maxDatagramSize) / nConnectionAlpha) initialCwndThisEpoch := currentCwnd for n := 0; n < numAcksThisEpoch; n++ { // Call once per ACK. expectedNextCwnd := renoCwnd(currentCwnd) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) Expect(currentCwnd).To(Equal(expectedNextCwnd)) } // Our byte-wise Reno implementation is an estimate. We expect // the cwnd to increase by approximately one MSS every // cwnd/kDefaultTCPMSS/Alpha acks, but it may be off by as much as // half a packet for smaller values of current_cwnd. cwndChangeThisEpoch := currentCwnd - initialCwndThisEpoch Expect(cwndChangeThisEpoch).To(BeNumerically("~", maxDatagramSize, maxDatagramSize/2)) clock.Advance(100 * time.Millisecond) } for i := 0; i < 54; i++ { maxAcksThisEpoch := currentCwnd / maxDatagramSize interval := time.Duration(100*1000/maxAcksThisEpoch) * time.Microsecond for n := 0; n < int(maxAcksThisEpoch); n++ { clock.Advance(interval) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) expectedCwnd := cubicConvexCwnd(initialCwnd, rttMin, clock.Now().Sub(initialTime)) // If we allow per-ack updates, every update is a small cubic update. Expect(currentCwnd).To(Equal(expectedCwnd)) } } expectedCwnd := cubicConvexCwnd(initialCwnd, rttMin, clock.Now().Sub(initialTime)) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) Expect(currentCwnd).To(Equal(expectedCwnd)) }) It("works above the origin with fine grained cubing", func() { // Start the test with an artificially large cwnd to prevent Reno // from over-taking cubic. currentCwnd := 1000 * maxDatagramSize initialCwnd := currentCwnd rttMin := 100 * time.Millisecond clock.Advance(time.Millisecond) initialTime := clock.Now() currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) clock.Advance(600 * time.Millisecond) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) // We expect the algorithm to perform only non-zero, fine-grained cubic // increases on every ack in this case. for i := 0; i < 100; i++ { clock.Advance(10 * time.Millisecond) expectedCwnd := cubicConvexCwnd(initialCwnd, rttMin, clock.Now().Sub(initialTime)) nextCwnd := cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) // Make sure we are performing cubic increases. Expect(nextCwnd).To(Equal(expectedCwnd)) // Make sure that these are non-zero, less-than-packet sized increases. Expect(nextCwnd).To(BeNumerically(">", currentCwnd)) cwndDelta := nextCwnd - currentCwnd Expect(maxDatagramSize / 10).To(BeNumerically(">", cwndDelta)) currentCwnd = nextCwnd } }) It("handles per ack updates", func() { // Start the test with a large cwnd and RTT, to force the first // increase to be a cubic increase. initialCwndPackets := 150 currentCwnd := protocol.ByteCount(initialCwndPackets) * maxDatagramSize rttMin := 350 * time.Millisecond // Initialize the epoch clock.Advance(time.Millisecond) // Keep track of the growth of the reno-equivalent cwnd. rCwnd := renoCwnd(currentCwnd) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) initialCwnd := currentCwnd // Simulate the return of cwnd packets in less than // MaxCubicInterval() time. maxAcks := int(float32(initialCwndPackets) / nConnectionAlpha) interval := maxCubicTimeInterval / time.Duration(maxAcks+1) // In this scenario, the first increase is dictated by the cubic // equation, but it is less than one byte, so the cwnd doesn't // change. Normally, without per-ack increases, any cwnd plateau // will cause the cwnd to be pinned for MaxCubicTimeInterval(). If // we enable per-ack updates, the cwnd will continue to grow, // regardless of the temporary plateau. clock.Advance(interval) rCwnd = renoCwnd(rCwnd) Expect(cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now())).To(Equal(currentCwnd)) for i := 1; i < maxAcks; i++ { clock.Advance(interval) nextCwnd := cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) rCwnd = renoCwnd(rCwnd) // The window shoud increase on every ack. Expect(nextCwnd).To(BeNumerically(">", currentCwnd)) Expect(nextCwnd).To(Equal(rCwnd)) currentCwnd = nextCwnd } // After all the acks are returned from the epoch, we expect the // cwnd to have increased by nearly one packet. (Not exactly one // packet, because our byte-wise Reno algorithm is always a slight // under-estimation). Without per-ack updates, the current_cwnd // would otherwise be unchanged. minimumExpectedIncrease := maxDatagramSize * 9 / 10 Expect(currentCwnd).To(BeNumerically(">", initialCwnd+minimumExpectedIncrease)) }) It("handles loss events", func() { rttMin := 100 * time.Millisecond currentCwnd := 422 * maxDatagramSize expectedCwnd := renoCwnd(currentCwnd) // Initialize the state. clock.Advance(time.Millisecond) Expect(cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now())).To(Equal(expectedCwnd)) // On the first loss, the last max congestion window is set to the // congestion window before the loss. preLossCwnd := currentCwnd Expect(cubic.lastMaxCongestionWindow).To(BeZero()) expectedCwnd = protocol.ByteCount(float32(currentCwnd) * nConnectionBeta) Expect(cubic.CongestionWindowAfterPacketLoss(currentCwnd)).To(Equal(expectedCwnd)) Expect(cubic.lastMaxCongestionWindow).To(Equal(preLossCwnd)) currentCwnd = expectedCwnd // On the second loss, the current congestion window has not yet // reached the last max congestion window. The last max congestion // window will be reduced by an additional backoff factor to allow // for competition. preLossCwnd = currentCwnd expectedCwnd = protocol.ByteCount(float32(currentCwnd) * nConnectionBeta) Expect(cubic.CongestionWindowAfterPacketLoss(currentCwnd)).To(Equal(expectedCwnd)) currentCwnd = expectedCwnd Expect(preLossCwnd).To(BeNumerically(">", cubic.lastMaxCongestionWindow)) expectedLastMax := protocol.ByteCount(float32(preLossCwnd) * nConnectionBetaLastMax) Expect(cubic.lastMaxCongestionWindow).To(Equal(expectedLastMax)) Expect(expectedCwnd).To(BeNumerically("<", cubic.lastMaxCongestionWindow)) // Simulate an increase, and check that we are below the origin. currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) Expect(cubic.lastMaxCongestionWindow).To(BeNumerically(">", currentCwnd)) // On the final loss, simulate the condition where the congestion // window had a chance to grow nearly to the last congestion window. currentCwnd = cubic.lastMaxCongestionWindow - 1 preLossCwnd = currentCwnd expectedCwnd = protocol.ByteCount(float32(currentCwnd) * nConnectionBeta) Expect(cubic.CongestionWindowAfterPacketLoss(currentCwnd)).To(Equal(expectedCwnd)) expectedLastMax = preLossCwnd Expect(cubic.lastMaxCongestionWindow).To(Equal(expectedLastMax)) }) It("works below origin", func() { // Concave growth. rttMin := 100 * time.Millisecond currentCwnd := 422 * maxDatagramSize expectedCwnd := renoCwnd(currentCwnd) // Initialize the state. clock.Advance(time.Millisecond) Expect(cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now())).To(Equal(expectedCwnd)) expectedCwnd = protocol.ByteCount(float32(currentCwnd) * nConnectionBeta) Expect(cubic.CongestionWindowAfterPacketLoss(currentCwnd)).To(Equal(expectedCwnd)) currentCwnd = expectedCwnd // First update after loss to initialize the epoch. currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) // Cubic phase. for i := 0; i < 40; i++ { clock.Advance(100 * time.Millisecond) currentCwnd = cubic.CongestionWindowAfterAck(maxDatagramSize, currentCwnd, rttMin, clock.Now()) } expectedCwnd = 553632 * maxDatagramSize / 1460 Expect(currentCwnd).To(Equal(expectedCwnd)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/hybrid_slow_start.go000066400000000000000000000076521465664453100306760ustar00rootroot00000000000000package congestion import ( "time" "github.com/quic-go/quic-go/internal/protocol" ) // Note(pwestin): the magic clamping numbers come from the original code in // tcp_cubic.c. const hybridStartLowWindow = protocol.ByteCount(16) // Number of delay samples for detecting the increase of delay. const hybridStartMinSamples = uint32(8) // Exit slow start if the min rtt has increased by more than 1/8th. const hybridStartDelayFactorExp = 3 // 2^3 = 8 // The original paper specifies 2 and 8ms, but those have changed over time. const ( hybridStartDelayMinThresholdUs = int64(4000) hybridStartDelayMaxThresholdUs = int64(16000) ) // HybridSlowStart implements the TCP hybrid slow start algorithm type HybridSlowStart struct { endPacketNumber protocol.PacketNumber lastSentPacketNumber protocol.PacketNumber started bool currentMinRTT time.Duration rttSampleCount uint32 hystartFound bool } // StartReceiveRound is called for the start of each receive round (burst) in the slow start phase. func (s *HybridSlowStart) StartReceiveRound(lastSent protocol.PacketNumber) { s.endPacketNumber = lastSent s.currentMinRTT = 0 s.rttSampleCount = 0 s.started = true } // IsEndOfRound returns true if this ack is the last packet number of our current slow start round. func (s *HybridSlowStart) IsEndOfRound(ack protocol.PacketNumber) bool { return s.endPacketNumber < ack } // ShouldExitSlowStart should be called on every new ack frame, since a new // RTT measurement can be made then. // rtt: the RTT for this ack packet. // minRTT: is the lowest delay (RTT) we have seen during the session. // congestionWindow: the congestion window in packets. func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow protocol.ByteCount) bool { if !s.started { // Time to start the hybrid slow start. s.StartReceiveRound(s.lastSentPacketNumber) } if s.hystartFound { return true } // Second detection parameter - delay increase detection. // Compare the minimum delay (s.currentMinRTT) of the current // burst of packets relative to the minimum delay during the session. // Note: we only look at the first few(8) packets in each burst, since we // only want to compare the lowest RTT of the burst relative to previous // bursts. s.rttSampleCount++ if s.rttSampleCount <= hybridStartMinSamples { if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT { s.currentMinRTT = latestRTT } } // We only need to check this once per round. if s.rttSampleCount == hybridStartMinSamples { // Divide minRTT by 8 to get a rtt increase threshold for exiting. minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) // Ensure the rtt threshold is never less than 2ms or more than 16ms. minRTTincreaseThresholdUs = min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) minRTTincreaseThreshold := time.Duration(max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { s.hystartFound = true } } // Exit from slow start if the cwnd is greater than 16 and // increasing delay is found. return congestionWindow >= hybridStartLowWindow && s.hystartFound } // OnPacketSent is called when a packet was sent func (s *HybridSlowStart) OnPacketSent(packetNumber protocol.PacketNumber) { s.lastSentPacketNumber = packetNumber } // OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end // the round when the final packet of the burst is received and start it on // the next incoming ack. func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber protocol.PacketNumber) { if s.IsEndOfRound(ackedPacketNumber) { s.started = false } } // Started returns true if started func (s *HybridSlowStart) Started() bool { return s.started } // Restart the slow start phase func (s *HybridSlowStart) Restart() { s.started = false s.hystartFound = false } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/hybrid_slow_start_test.go000066400000000000000000000043131465664453100317240ustar00rootroot00000000000000package congestion import ( "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Hybrid slow start", func() { var slowStart HybridSlowStart BeforeEach(func() { slowStart = HybridSlowStart{} }) It("works in a simple case", func() { packetNumber := protocol.PacketNumber(1) endPacketNumber := protocol.PacketNumber(3) slowStart.StartReceiveRound(endPacketNumber) packetNumber++ Expect(slowStart.IsEndOfRound(packetNumber)).To(BeFalse()) // Test duplicates. Expect(slowStart.IsEndOfRound(packetNumber)).To(BeFalse()) packetNumber++ Expect(slowStart.IsEndOfRound(packetNumber)).To(BeFalse()) packetNumber++ Expect(slowStart.IsEndOfRound(packetNumber)).To(BeTrue()) // Test without a new registered end_packet_number; packetNumber++ Expect(slowStart.IsEndOfRound(packetNumber)).To(BeTrue()) endPacketNumber = 20 slowStart.StartReceiveRound(endPacketNumber) for packetNumber < endPacketNumber { packetNumber++ Expect(slowStart.IsEndOfRound(packetNumber)).To(BeFalse()) } packetNumber++ Expect(slowStart.IsEndOfRound(packetNumber)).To(BeTrue()) }) It("works with delay", func() { rtt := 60 * time.Millisecond // We expect to detect the increase at +1/8 of the RTT; hence at a typical // RTT of 60ms the detection will happen at 67.5 ms. const hybridStartMinSamples = 8 // Number of acks required to trigger. endPacketNumber := protocol.PacketNumber(1) endPacketNumber++ slowStart.StartReceiveRound(endPacketNumber) // Will not trigger since our lowest RTT in our burst is the same as the long // term RTT provided. for n := 0; n < hybridStartMinSamples; n++ { Expect(slowStart.ShouldExitSlowStart(rtt+time.Duration(n)*time.Millisecond, rtt, 100)).To(BeFalse()) } endPacketNumber++ slowStart.StartReceiveRound(endPacketNumber) for n := 1; n < hybridStartMinSamples; n++ { Expect(slowStart.ShouldExitSlowStart(rtt+(time.Duration(n)+10)*time.Millisecond, rtt, 100)).To(BeFalse()) } // Expect to trigger since all packets in this burst was above the long term // RTT provided. Expect(slowStart.ShouldExitSlowStart(rtt+10*time.Millisecond, rtt, 100)).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/interface.go000066400000000000000000000020331465664453100270600ustar00rootroot00000000000000package congestion import ( "time" "github.com/quic-go/quic-go/internal/protocol" ) // A SendAlgorithm performs congestion control type SendAlgorithm interface { TimeUntilSend(bytesInFlight protocol.ByteCount) time.Time HasPacingBudget(now time.Time) bool OnPacketSent(sentTime time.Time, bytesInFlight protocol.ByteCount, packetNumber protocol.PacketNumber, bytes protocol.ByteCount, isRetransmittable bool) CanSend(bytesInFlight protocol.ByteCount) bool MaybeExitSlowStart() OnPacketAcked(number protocol.PacketNumber, ackedBytes protocol.ByteCount, priorInFlight protocol.ByteCount, eventTime time.Time) OnCongestionEvent(number protocol.PacketNumber, lostBytes protocol.ByteCount, priorInFlight protocol.ByteCount) OnRetransmissionTimeout(packetsRetransmitted bool) SetMaxDatagramSize(protocol.ByteCount) } // A SendAlgorithmWithDebugInfos is a SendAlgorithm that exposes some debug infos type SendAlgorithmWithDebugInfos interface { SendAlgorithm InSlowStart() bool InRecovery() bool GetCongestionWindow() protocol.ByteCount } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/pacer.go000066400000000000000000000051771465664453100262260ustar00rootroot00000000000000package congestion import ( "time" "github.com/quic-go/quic-go/internal/protocol" ) const maxBurstSizePackets = 10 // The pacer implements a token bucket pacing algorithm. type pacer struct { budgetAtLastSent protocol.ByteCount maxDatagramSize protocol.ByteCount lastSentTime time.Time adjustedBandwidth func() uint64 // in bytes/s } func newPacer(getBandwidth func() Bandwidth) *pacer { p := &pacer{ maxDatagramSize: initialMaxDatagramSize, adjustedBandwidth: func() uint64 { // Bandwidth is in bits/s. We need the value in bytes/s. bw := uint64(getBandwidth() / BytesPerSecond) // Use a slightly higher value than the actual measured bandwidth. // RTT variations then won't result in under-utilization of the congestion window. // Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire, // provided the congestion window is fully utilized and acknowledgments arrive at regular intervals. return bw * 5 / 4 }, } p.budgetAtLastSent = p.maxBurstSize() return p } func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) { budget := p.Budget(sendTime) if size >= budget { p.budgetAtLastSent = 0 } else { p.budgetAtLastSent = budget - size } p.lastSentTime = sendTime } func (p *pacer) Budget(now time.Time) protocol.ByteCount { if p.lastSentTime.IsZero() { return p.maxBurstSize() } budget := p.budgetAtLastSent + (protocol.ByteCount(p.adjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 if budget < 0 { // protect against overflows budget = protocol.MaxByteCount } return min(p.maxBurstSize(), budget) } func (p *pacer) maxBurstSize() protocol.ByteCount { return max( protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.adjustedBandwidth())/1e9, maxBurstSizePackets*p.maxDatagramSize, ) } // TimeUntilSend returns when the next packet should be sent. // It returns the zero value of time.Time if a packet can be sent immediately. func (p *pacer) TimeUntilSend() time.Time { if p.budgetAtLastSent >= p.maxDatagramSize { return time.Time{} } diff := 1e9 * uint64(p.maxDatagramSize-p.budgetAtLastSent) bw := p.adjustedBandwidth() // We might need to round up this value. // Otherwise, we might have a budget (slightly) smaller than the datagram size when the timer expires. d := diff / bw // this is effectively a math.Ceil, but using only integer math if diff%bw > 0 { d++ } return p.lastSentTime.Add(max(protocol.MinPacingDelay, time.Duration(d)*time.Nanosecond)) } func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) { p.maxDatagramSize = s } golang-github-lucas-clemente-quic-go-0.46.0/internal/congestion/pacer_test.go000066400000000000000000000116441465664453100272610ustar00rootroot00000000000000package congestion import ( "math/rand" "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Pacer", func() { var p *pacer const packetsPerSecond = 50 var bandwidth uint64 // in bytes/s BeforeEach(func() { bandwidth = uint64(packetsPerSecond * initialMaxDatagramSize) // 50 full-size packets per second // The pacer will multiply the bandwidth with 1.25 to achieve a slightly higher pacing speed. // For the tests, cancel out this factor, so we can do the math using the exact bandwidth. p = newPacer(func() Bandwidth { return Bandwidth(bandwidth) * BytesPerSecond * 4 / 5 }) }) It("allows a burst at the beginning", func() { t := time.Now() Expect(p.TimeUntilSend()).To(BeZero()) Expect(p.Budget(t)).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize)) }) It("allows a big burst for high pacing rates", func() { t := time.Now() bandwidth = uint64(10000 * packetsPerSecond * initialMaxDatagramSize) Expect(p.TimeUntilSend()).To(BeZero()) Expect(p.Budget(t)).To(BeNumerically(">", maxBurstSizePackets*initialMaxDatagramSize)) }) It("reduces the budget when sending packets", func() { t := time.Now() budget := p.Budget(t) for budget > 0 { Expect(p.TimeUntilSend()).To(BeZero()) Expect(p.Budget(t)).To(Equal(budget)) p.SentPacket(t, initialMaxDatagramSize) budget -= initialMaxDatagramSize } Expect(p.Budget(t)).To(BeZero()) Expect(p.TimeUntilSend()).ToNot(BeZero()) }) sendBurst := func(t time.Time) { for p.Budget(t) > 0 { p.SentPacket(t, initialMaxDatagramSize) } } It("paces packets after a burst", func() { t := time.Now() sendBurst(t) // send 100 exactly paced packets for i := 0; i < 100; i++ { t2 := p.TimeUntilSend() Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond)) Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize)) p.SentPacket(t2, initialMaxDatagramSize) t = t2 } }) It("accounts for non-full-size packets", func() { t := time.Now() sendBurst(t) t2 := p.TimeUntilSend() Expect(t2.Sub(t)).To(BeNumerically("~", time.Second/packetsPerSecond, time.Nanosecond)) // send a half-full packet Expect(p.Budget(t2)).To(BeEquivalentTo(initialMaxDatagramSize)) size := initialMaxDatagramSize / 2 p.SentPacket(t2, size) Expect(p.Budget(t2)).To(Equal(initialMaxDatagramSize - size)) Expect(p.TimeUntilSend()).To(BeTemporally("~", t2.Add(time.Second/packetsPerSecond/2), time.Nanosecond)) }) It("accumulates budget, if no packets are sent", func() { t := time.Now() sendBurst(t) t2 := p.TimeUntilSend() Expect(t2).To(BeTemporally(">", t)) // wait for 5 times the duration Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * initialMaxDatagramSize)) }) It("accumulates budget, if no packets are sent, for larger packet sizes", func() { t := time.Now() sendBurst(t) const packetSize = initialMaxDatagramSize + 200 p.SetMaxDatagramSize(packetSize) t2 := p.TimeUntilSend() Expect(t2).To(BeTemporally(">", t)) // wait for 5 times the duration Expect(p.Budget(t.Add(5 * t2.Sub(t)))).To(BeEquivalentTo(5 * packetSize)) }) It("has enough budget for at least one packet when the timer expires", func() { t := time.Now() sendBurst(t) for bw := uint64(100); bw < uint64(5*initialMaxDatagramSize); bw++ { bandwidth = bw // reduce the bandwidth to 5 packet per second t2 := p.TimeUntilSend() Expect(t2).To(BeTemporally(">", t)) Expect(p.Budget(t2)).To(BeNumerically(">=", initialMaxDatagramSize)) } }) It("never allows bursts larger than the maximum burst size", func() { t := time.Now() sendBurst(t) Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * initialMaxDatagramSize)) }) It("never allows bursts larger than the maximum burst size, for larger packets", func() { t := time.Now() const packetSize = initialMaxDatagramSize + 200 p.SetMaxDatagramSize(packetSize) sendBurst(t) Expect(p.Budget(t.Add(time.Hour))).To(BeEquivalentTo(maxBurstSizePackets * packetSize)) }) It("changes the bandwidth", func() { t := time.Now() sendBurst(t) bandwidth = uint64(5 * initialMaxDatagramSize) // reduce the bandwidth to 5 packet per second Expect(p.TimeUntilSend()).To(Equal(t.Add(time.Second / 5))) }) It("doesn't pace faster than the minimum pacing duration", func() { t := time.Now() sendBurst(t) bandwidth = uint64(1e6 * initialMaxDatagramSize) Expect(p.TimeUntilSend()).To(Equal(t.Add(protocol.MinPacingDelay))) Expect(p.Budget(t.Add(protocol.MinPacingDelay))).To(Equal(protocol.ByteCount(protocol.MinPacingDelay) * initialMaxDatagramSize * 1e6 / 1e9)) }) It("protects against overflows", func() { p = newPacer(func() Bandwidth { return infBandwidth }) t := time.Now() p.SentPacket(t, initialMaxDatagramSize) for i := 0; i < 1e5; i++ { Expect(p.Budget(t.Add(time.Duration(rand.Int63())))).To(BeNumerically(">=", 0)) } }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/000077500000000000000000000000001465664453100247735ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/base_flow_controller.go000066400000000000000000000076621465664453100315410ustar00rootroot00000000000000package flowcontrol import ( "sync" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) type baseFlowController struct { // for sending data bytesSent protocol.ByteCount sendWindow protocol.ByteCount lastBlockedAt protocol.ByteCount // for receiving data //nolint:structcheck // The mutex is used both by the stream and the connection flow controller mutex sync.Mutex bytesRead protocol.ByteCount highestReceived protocol.ByteCount receiveWindow protocol.ByteCount receiveWindowSize protocol.ByteCount maxReceiveWindowSize protocol.ByteCount allowWindowIncrease func(size protocol.ByteCount) bool epochStartTime time.Time epochStartOffset protocol.ByteCount rttStats *utils.RTTStats logger utils.Logger } // IsNewlyBlocked says if it is newly blocked by flow control. // For every offset, it only returns true once. // If it is blocked, the offset is returned. func (c *baseFlowController) IsNewlyBlocked() (bool, protocol.ByteCount) { if c.sendWindowSize() != 0 || c.sendWindow == c.lastBlockedAt { return false, 0 } c.lastBlockedAt = c.sendWindow return true, c.sendWindow } func (c *baseFlowController) AddBytesSent(n protocol.ByteCount) { c.bytesSent += n } // UpdateSendWindow is called after receiving a MAX_{STREAM_}DATA frame. func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) (updated bool) { if offset > c.sendWindow { c.sendWindow = offset return true } return false } func (c *baseFlowController) sendWindowSize() protocol.ByteCount { // this only happens during connection establishment, when data is sent before we receive the peer's transport parameters if c.bytesSent > c.sendWindow { return 0 } return c.sendWindow - c.bytesSent } // needs to be called with locked mutex func (c *baseFlowController) addBytesRead(n protocol.ByteCount) { // pretend we sent a WindowUpdate when reading the first byte // this way auto-tuning of the window size already works for the first WindowUpdate if c.bytesRead == 0 { c.startNewAutoTuningEpoch(time.Now()) } c.bytesRead += n } func (c *baseFlowController) hasWindowUpdate() bool { bytesRemaining := c.receiveWindow - c.bytesRead // update the window when more than the threshold was consumed return bytesRemaining <= protocol.ByteCount(float64(c.receiveWindowSize)*(1-protocol.WindowUpdateThreshold)) } // getWindowUpdate updates the receive window, if necessary // it returns the new offset func (c *baseFlowController) getWindowUpdate() protocol.ByteCount { if !c.hasWindowUpdate() { return 0 } c.maybeAdjustWindowSize() c.receiveWindow = c.bytesRead + c.receiveWindowSize return c.receiveWindow } // maybeAdjustWindowSize increases the receiveWindowSize if we're sending updates too often. // For details about auto-tuning, see https://docs.google.com/document/d/1SExkMmGiz8VYzV3s9E35JQlJ73vhzCekKkDi85F1qCE/edit?usp=sharing. func (c *baseFlowController) maybeAdjustWindowSize() { bytesReadInEpoch := c.bytesRead - c.epochStartOffset // don't do anything if less than half the window has been consumed if bytesReadInEpoch <= c.receiveWindowSize/2 { return } rtt := c.rttStats.SmoothedRTT() if rtt == 0 { return } fraction := float64(bytesReadInEpoch) / float64(c.receiveWindowSize) now := time.Now() if now.Sub(c.epochStartTime) < time.Duration(4*fraction*float64(rtt)) { // window is consumed too fast, try to increase the window size newSize := min(2*c.receiveWindowSize, c.maxReceiveWindowSize) if newSize > c.receiveWindowSize && (c.allowWindowIncrease == nil || c.allowWindowIncrease(newSize-c.receiveWindowSize)) { c.receiveWindowSize = newSize } } c.startNewAutoTuningEpoch(now) } func (c *baseFlowController) startNewAutoTuningEpoch(now time.Time) { c.epochStartTime = now c.epochStartOffset = c.bytesRead } func (c *baseFlowController) checkFlowControlViolation() bool { return c.highestReceived > c.receiveWindow } golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/base_flow_controller_test.go000066400000000000000000000217471465664453100326000ustar00rootroot00000000000000package flowcontrol import ( "os" "strconv" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) // on the CIs, the timing is a lot less precise, so scale every duration by this factor // //nolint:unparam func scaleDuration(t time.Duration) time.Duration { scaleFactor := 1 if f, err := strconv.Atoi(os.Getenv("TIMESCALE_FACTOR")); err == nil { // parsing "" errors, so this works fine if the env is not set scaleFactor = f } Expect(scaleFactor).ToNot(BeZero()) return time.Duration(scaleFactor) * t } var _ = Describe("Base Flow controller", func() { var controller *baseFlowController BeforeEach(func() { controller = &baseFlowController{} controller.rttStats = &utils.RTTStats{} }) Context("send flow control", func() { It("adds bytes sent", func() { controller.bytesSent = 5 controller.AddBytesSent(6) Expect(controller.bytesSent).To(Equal(protocol.ByteCount(5 + 6))) }) It("gets the size of the remaining flow control window", func() { controller.bytesSent = 5 controller.sendWindow = 12 Expect(controller.sendWindowSize()).To(Equal(protocol.ByteCount(12 - 5))) }) It("updates the size of the flow control window", func() { controller.AddBytesSent(5) controller.UpdateSendWindow(15) Expect(controller.sendWindow).To(Equal(protocol.ByteCount(15))) Expect(controller.sendWindowSize()).To(Equal(protocol.ByteCount(15 - 5))) }) It("says that the window size is 0 if we sent more than we were allowed to", func() { controller.AddBytesSent(15) controller.UpdateSendWindow(10) Expect(controller.sendWindowSize()).To(BeZero()) }) It("does not decrease the flow control window", func() { Expect(controller.UpdateSendWindow(20)).To(BeTrue()) Expect(controller.sendWindowSize()).To(Equal(protocol.ByteCount(20))) Expect(controller.UpdateSendWindow(10)).To(BeFalse()) Expect(controller.sendWindowSize()).To(Equal(protocol.ByteCount(20))) }) It("says when it's blocked", func() { controller.UpdateSendWindow(100) Expect(controller.IsNewlyBlocked()).To(BeFalse()) controller.AddBytesSent(100) blocked, offset := controller.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(offset).To(Equal(protocol.ByteCount(100))) }) It("doesn't say that it's newly blocked multiple times for the same offset", func() { controller.UpdateSendWindow(100) controller.AddBytesSent(100) newlyBlocked, offset := controller.IsNewlyBlocked() Expect(newlyBlocked).To(BeTrue()) Expect(offset).To(Equal(protocol.ByteCount(100))) newlyBlocked, _ = controller.IsNewlyBlocked() Expect(newlyBlocked).To(BeFalse()) controller.UpdateSendWindow(150) controller.AddBytesSent(150) newlyBlocked, _ = controller.IsNewlyBlocked() Expect(newlyBlocked).To(BeTrue()) }) }) Context("receive flow control", func() { var ( receiveWindow protocol.ByteCount = 10000 receiveWindowSize protocol.ByteCount = 1000 ) BeforeEach(func() { controller.bytesRead = receiveWindow - receiveWindowSize controller.receiveWindow = receiveWindow controller.receiveWindowSize = receiveWindowSize }) It("adds bytes read", func() { controller.bytesRead = 5 controller.addBytesRead(6) Expect(controller.bytesRead).To(Equal(protocol.ByteCount(5 + 6))) }) It("triggers a window update when necessary", func() { bytesConsumed := float64(receiveWindowSize)*protocol.WindowUpdateThreshold + 1 // consumed 1 byte more than the threshold bytesRemaining := receiveWindowSize - protocol.ByteCount(bytesConsumed) readPosition := receiveWindow - bytesRemaining controller.bytesRead = readPosition offset := controller.getWindowUpdate() Expect(offset).To(Equal(readPosition + receiveWindowSize)) Expect(controller.receiveWindow).To(Equal(readPosition + receiveWindowSize)) }) It("doesn't trigger a window update when not necessary", func() { bytesConsumed := float64(receiveWindowSize)*protocol.WindowUpdateThreshold - 1 // consumed 1 byte less than the threshold bytesRemaining := receiveWindowSize - protocol.ByteCount(bytesConsumed) readPosition := receiveWindow - bytesRemaining controller.bytesRead = readPosition offset := controller.getWindowUpdate() Expect(offset).To(BeZero()) }) Context("receive window size auto-tuning", func() { var oldWindowSize protocol.ByteCount BeforeEach(func() { oldWindowSize = controller.receiveWindowSize controller.maxReceiveWindowSize = 5000 }) // update the congestion such that it returns a given value for the smoothed RTT setRtt := func(t time.Duration) { controller.rttStats.UpdateRTT(t, 0, time.Now()) Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked } It("doesn't increase the window size for a new stream", func() { controller.maybeAdjustWindowSize() Expect(controller.receiveWindowSize).To(Equal(oldWindowSize)) }) It("doesn't increase the window size when no RTT estimate is available", func() { setRtt(0) controller.startNewAutoTuningEpoch(time.Now()) controller.addBytesRead(400) offset := controller.getWindowUpdate() Expect(offset).ToNot(BeZero()) // make sure a window update is sent Expect(controller.receiveWindowSize).To(Equal(oldWindowSize)) }) It("increases the window size if read so fast that the window would be consumed in less than 4 RTTs", func() { bytesRead := controller.bytesRead rtt := scaleDuration(50 * time.Millisecond) setRtt(rtt) // consume more than 2/3 of the window... dataRead := receiveWindowSize*2/3 + 1 // ... in 4*2/3 of the RTT controller.epochStartOffset = controller.bytesRead controller.epochStartTime = time.Now().Add(-rtt * 4 * 2 / 3) controller.addBytesRead(dataRead) offset := controller.getWindowUpdate() Expect(offset).ToNot(BeZero()) // check that the window size was increased newWindowSize := controller.receiveWindowSize Expect(newWindowSize).To(Equal(2 * oldWindowSize)) // check that the new window size was used to increase the offset Expect(offset).To(Equal(bytesRead + dataRead + newWindowSize)) }) It("doesn't increase the window size if data is read so fast that the window would be consumed in less than 4 RTTs, but less than half the window has been read", func() { bytesRead := controller.bytesRead rtt := scaleDuration(20 * time.Millisecond) setRtt(rtt) // consume more than 2/3 of the window... dataRead := receiveWindowSize*1/3 + 1 // ... in 4*2/3 of the RTT controller.epochStartOffset = controller.bytesRead controller.epochStartTime = time.Now().Add(-rtt * 4 * 1 / 3) controller.addBytesRead(dataRead) offset := controller.getWindowUpdate() Expect(offset).ToNot(BeZero()) // check that the window size was not increased newWindowSize := controller.receiveWindowSize Expect(newWindowSize).To(Equal(oldWindowSize)) // check that the new window size was used to increase the offset Expect(offset).To(Equal(bytesRead + dataRead + newWindowSize)) }) It("doesn't increase the window size if read too slowly", func() { bytesRead := controller.bytesRead rtt := scaleDuration(20 * time.Millisecond) setRtt(rtt) // consume less than 2/3 of the window... dataRead := receiveWindowSize*2/3 - 1 // ... in 4*2/3 of the RTT controller.epochStartOffset = controller.bytesRead controller.epochStartTime = time.Now().Add(-rtt * 4 * 2 / 3) controller.addBytesRead(dataRead) offset := controller.getWindowUpdate() Expect(offset).ToNot(BeZero()) // check that the window size was not increased Expect(controller.receiveWindowSize).To(Equal(oldWindowSize)) // check that the new window size was used to increase the offset Expect(offset).To(Equal(bytesRead + dataRead + oldWindowSize)) }) It("doesn't increase the window size to a value higher than the maxReceiveWindowSize", func() { resetEpoch := func() { // make sure the next call to maybeAdjustWindowSize will increase the window controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.epochStartOffset = controller.bytesRead controller.addBytesRead(controller.receiveWindowSize/2 + 1) } setRtt(scaleDuration(20 * time.Millisecond)) resetEpoch() controller.maybeAdjustWindowSize() Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) // 2000 // because the lastWindowUpdateTime is updated by MaybeTriggerWindowUpdate(), we can just call maybeAdjustWindowSize() multiple times and get an increase of the window size every time resetEpoch() controller.maybeAdjustWindowSize() Expect(controller.receiveWindowSize).To(Equal(2 * 2 * oldWindowSize)) // 4000 resetEpoch() controller.maybeAdjustWindowSize() Expect(controller.receiveWindowSize).To(Equal(controller.maxReceiveWindowSize)) // 5000 controller.maybeAdjustWindowSize() Expect(controller.receiveWindowSize).To(Equal(controller.maxReceiveWindowSize)) // 5000 }) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/connection_flow_controller.go000066400000000000000000000066401465664453100327610ustar00rootroot00000000000000package flowcontrol import ( "errors" "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" ) type connectionFlowController struct { baseFlowController } var _ ConnectionFlowController = &connectionFlowController{} // NewConnectionFlowController gets a new flow controller for the connection // It is created before we receive the peer's transport parameters, thus it starts with a sendWindow of 0. func NewConnectionFlowController( receiveWindow protocol.ByteCount, maxReceiveWindow protocol.ByteCount, allowWindowIncrease func(size protocol.ByteCount) bool, rttStats *utils.RTTStats, logger utils.Logger, ) ConnectionFlowController { return &connectionFlowController{ baseFlowController: baseFlowController{ rttStats: rttStats, receiveWindow: receiveWindow, receiveWindowSize: receiveWindow, maxReceiveWindowSize: maxReceiveWindow, allowWindowIncrease: allowWindowIncrease, logger: logger, }, } } func (c *connectionFlowController) SendWindowSize() protocol.ByteCount { return c.baseFlowController.sendWindowSize() } // IncrementHighestReceived adds an increment to the highestReceived value func (c *connectionFlowController) IncrementHighestReceived(increment protocol.ByteCount) error { c.mutex.Lock() defer c.mutex.Unlock() c.highestReceived += increment if c.checkFlowControlViolation() { return &qerr.TransportError{ ErrorCode: qerr.FlowControlError, ErrorMessage: fmt.Sprintf("received %d bytes for the connection, allowed %d bytes", c.highestReceived, c.receiveWindow), } } return nil } func (c *connectionFlowController) AddBytesRead(n protocol.ByteCount) { c.mutex.Lock() c.baseFlowController.addBytesRead(n) c.mutex.Unlock() } func (c *connectionFlowController) GetWindowUpdate() protocol.ByteCount { c.mutex.Lock() oldWindowSize := c.receiveWindowSize offset := c.baseFlowController.getWindowUpdate() if c.logger.Debug() && oldWindowSize < c.receiveWindowSize { c.logger.Debugf("Increasing receive flow control window for the connection to %d kB", c.receiveWindowSize/(1<<10)) } c.mutex.Unlock() return offset } // EnsureMinimumWindowSize sets a minimum window size // it should make sure that the connection-level window is increased when a stream-level window grows func (c *connectionFlowController) EnsureMinimumWindowSize(inc protocol.ByteCount) { c.mutex.Lock() if inc > c.receiveWindowSize { c.logger.Debugf("Increasing receive flow control window for the connection to %d kB, in response to stream flow control window increase", c.receiveWindowSize/(1<<10)) newSize := min(inc, c.maxReceiveWindowSize) if delta := newSize - c.receiveWindowSize; delta > 0 && c.allowWindowIncrease(delta) { c.receiveWindowSize = newSize } c.startNewAutoTuningEpoch(time.Now()) } c.mutex.Unlock() } // Reset rests the flow controller. This happens when 0-RTT is rejected. // All stream data is invalidated, it's if we had never opened a stream and never sent any data. // At that point, we only have sent stream data, but we didn't have the keys to open 1-RTT keys yet. func (c *connectionFlowController) Reset() error { c.mutex.Lock() defer c.mutex.Unlock() if c.bytesRead > 0 || c.highestReceived > 0 || !c.epochStartTime.IsZero() { return errors.New("flow controller reset after reading data") } c.bytesSent = 0 c.lastBlockedAt = 0 return nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/connection_flow_controller_test.go000066400000000000000000000136421465664453100340200ustar00rootroot00000000000000package flowcontrol import ( "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Connection Flow controller", func() { var controller *connectionFlowController // update the congestion such that it returns a given value for the smoothed RTT setRtt := func(t time.Duration) { controller.rttStats.UpdateRTT(t, 0, time.Now()) Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked } BeforeEach(func() { controller = &connectionFlowController{} controller.rttStats = &utils.RTTStats{} controller.logger = utils.DefaultLogger controller.allowWindowIncrease = func(protocol.ByteCount) bool { return true } }) Context("Constructor", func() { rttStats := &utils.RTTStats{} It("sets the send and receive windows", func() { receiveWindow := protocol.ByteCount(2000) maxReceiveWindow := protocol.ByteCount(3000) fc := NewConnectionFlowController( receiveWindow, maxReceiveWindow, func(protocol.ByteCount) bool { return true }, rttStats, utils.DefaultLogger).(*connectionFlowController) Expect(fc.receiveWindow).To(Equal(receiveWindow)) Expect(fc.maxReceiveWindowSize).To(Equal(maxReceiveWindow)) }) }) Context("receive flow control", func() { It("increases the highestReceived by a given window size", func() { controller.highestReceived = 1337 controller.IncrementHighestReceived(123) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1337 + 123))) }) Context("getting window updates", func() { BeforeEach(func() { controller.receiveWindow = 100 controller.receiveWindowSize = 60 controller.maxReceiveWindowSize = 1000 controller.bytesRead = 100 - 60 }) It("queues window updates", func() { controller.AddBytesRead(1) Expect(controller.GetWindowUpdate()).To(BeZero()) controller.AddBytesRead(29) Expect(controller.GetWindowUpdate()).ToNot(BeZero()) controller.AddBytesRead(1) Expect(controller.GetWindowUpdate()).To(BeZero()) }) It("gets a window update", func() { windowSize := controller.receiveWindowSize oldOffset := controller.bytesRead dataRead := windowSize/2 - 1 // make sure not to trigger auto-tuning controller.AddBytesRead(dataRead) offset := controller.GetWindowUpdate() Expect(offset).To(Equal(oldOffset + dataRead + 60)) }) It("auto-tunes the window", func() { var allowed protocol.ByteCount controller.allowWindowIncrease = func(size protocol.ByteCount) bool { allowed = size return true } oldOffset := controller.bytesRead oldWindowSize := controller.receiveWindowSize rtt := scaleDuration(20 * time.Millisecond) setRtt(rtt) controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.epochStartOffset = oldOffset dataRead := oldWindowSize/2 + 1 controller.AddBytesRead(dataRead) offset := controller.GetWindowUpdate() newWindowSize := controller.receiveWindowSize Expect(newWindowSize).To(Equal(2 * oldWindowSize)) Expect(offset).To(Equal(oldOffset + dataRead + newWindowSize)) Expect(allowed).To(Equal(oldWindowSize)) }) It("doesn't auto-tune the window if it's not allowed", func() { controller.allowWindowIncrease = func(protocol.ByteCount) bool { return false } oldOffset := controller.bytesRead oldWindowSize := controller.receiveWindowSize rtt := scaleDuration(20 * time.Millisecond) setRtt(rtt) controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.epochStartOffset = oldOffset dataRead := oldWindowSize/2 + 1 controller.AddBytesRead(dataRead) offset := controller.GetWindowUpdate() newWindowSize := controller.receiveWindowSize Expect(newWindowSize).To(Equal(oldWindowSize)) Expect(offset).To(Equal(oldOffset + dataRead + newWindowSize)) }) }) }) Context("setting the minimum window size", func() { var ( oldWindowSize protocol.ByteCount receiveWindow protocol.ByteCount = 10000 receiveWindowSize protocol.ByteCount = 1000 ) BeforeEach(func() { controller.receiveWindow = receiveWindow controller.receiveWindowSize = receiveWindowSize oldWindowSize = controller.receiveWindowSize controller.maxReceiveWindowSize = 3000 }) It("sets the minimum window window size", func() { controller.EnsureMinimumWindowSize(1800) Expect(controller.receiveWindowSize).To(Equal(protocol.ByteCount(1800))) }) It("doesn't reduce the window window size", func() { controller.EnsureMinimumWindowSize(1) Expect(controller.receiveWindowSize).To(Equal(oldWindowSize)) }) It("doesn't increase the window size beyond the maxReceiveWindowSize", func() { max := controller.maxReceiveWindowSize controller.EnsureMinimumWindowSize(2 * max) Expect(controller.receiveWindowSize).To(Equal(max)) }) It("starts a new epoch after the window size was increased", func() { controller.EnsureMinimumWindowSize(1912) Expect(controller.epochStartTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond)) }) }) Context("resetting", func() { It("resets", func() { const initialWindow protocol.ByteCount = 1337 controller.UpdateSendWindow(initialWindow) controller.AddBytesSent(1000) Expect(controller.SendWindowSize()).To(Equal(initialWindow - 1000)) Expect(controller.Reset()).To(Succeed()) Expect(controller.SendWindowSize()).To(Equal(initialWindow)) }) It("says if is blocked after resetting", func() { const initialWindow protocol.ByteCount = 1337 controller.UpdateSendWindow(initialWindow) controller.AddBytesSent(initialWindow) blocked, _ := controller.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(controller.Reset()).To(Succeed()) controller.AddBytesSent(initialWindow) blocked, blockedAt := controller.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(blockedAt).To(Equal(initialWindow)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/flowcontrol_suite_test.go000066400000000000000000000006061465664453100321440ustar00rootroot00000000000000package flowcontrol import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func TestFlowControl(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "FlowControl Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/interface.go000066400000000000000000000027701465664453100272700ustar00rootroot00000000000000package flowcontrol import "github.com/quic-go/quic-go/internal/protocol" type flowController interface { // for sending SendWindowSize() protocol.ByteCount UpdateSendWindow(protocol.ByteCount) (updated bool) AddBytesSent(protocol.ByteCount) // for receiving GetWindowUpdate() protocol.ByteCount // returns 0 if no update is necessary } // A StreamFlowController is a flow controller for a QUIC stream. type StreamFlowController interface { flowController AddBytesRead(protocol.ByteCount) (shouldQueueWindowUpdate bool) // UpdateHighestReceived is called when a new highest offset is received // final has to be to true if this is the final offset of the stream, // as contained in a STREAM frame with FIN bit, and the RESET_STREAM frame UpdateHighestReceived(offset protocol.ByteCount, final bool) error // Abandon is called when reading from the stream is aborted early, // and there won't be any further calls to AddBytesRead. Abandon() IsNewlyBlocked() bool } // The ConnectionFlowController is the flow controller for the connection. type ConnectionFlowController interface { flowController AddBytesRead(protocol.ByteCount) Reset() error IsNewlyBlocked() (bool, protocol.ByteCount) } type connectionFlowControllerI interface { ConnectionFlowController // The following two methods are not supposed to be called from outside this packet, but are needed internally // for sending EnsureMinimumWindowSize(protocol.ByteCount) // for receiving IncrementHighestReceived(protocol.ByteCount) error } golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/stream_flow_controller.go000066400000000000000000000112301465664453100321040ustar00rootroot00000000000000package flowcontrol import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" ) type streamFlowController struct { baseFlowController streamID protocol.StreamID connection connectionFlowControllerI receivedFinalOffset bool } var _ StreamFlowController = &streamFlowController{} // NewStreamFlowController gets a new flow controller for a stream func NewStreamFlowController( streamID protocol.StreamID, cfc ConnectionFlowController, receiveWindow protocol.ByteCount, maxReceiveWindow protocol.ByteCount, initialSendWindow protocol.ByteCount, rttStats *utils.RTTStats, logger utils.Logger, ) StreamFlowController { return &streamFlowController{ streamID: streamID, connection: cfc.(connectionFlowControllerI), baseFlowController: baseFlowController{ rttStats: rttStats, receiveWindow: receiveWindow, receiveWindowSize: receiveWindow, maxReceiveWindowSize: maxReceiveWindow, sendWindow: initialSendWindow, logger: logger, }, } } // UpdateHighestReceived updates the highestReceived value, if the offset is higher. func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error { // If the final offset for this stream is already known, check for consistency. if c.receivedFinalOffset { // If we receive another final offset, check that it's the same. if final && offset != c.highestReceived { return &qerr.TransportError{ ErrorCode: qerr.FinalSizeError, ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset), } } // Check that the offset is below the final offset. if offset > c.highestReceived { return &qerr.TransportError{ ErrorCode: qerr.FinalSizeError, ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived), } } } if final { c.receivedFinalOffset = true } if offset == c.highestReceived { return nil } // A higher offset was received before. // This can happen due to reordering. if offset <= c.highestReceived { if final { return &qerr.TransportError{ ErrorCode: qerr.FinalSizeError, ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived), } } return nil } increment := offset - c.highestReceived c.highestReceived = offset if c.checkFlowControlViolation() { return &qerr.TransportError{ ErrorCode: qerr.FlowControlError, ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow), } } return c.connection.IncrementHighestReceived(increment) } func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) (shouldQueueWindowUpdate bool) { c.mutex.Lock() c.baseFlowController.addBytesRead(n) shouldQueueWindowUpdate = c.shouldQueueWindowUpdate() c.mutex.Unlock() c.connection.AddBytesRead(n) return } func (c *streamFlowController) Abandon() { c.mutex.Lock() unread := c.highestReceived - c.bytesRead c.bytesRead = c.highestReceived c.mutex.Unlock() if unread > 0 { c.connection.AddBytesRead(unread) } } func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) { c.baseFlowController.AddBytesSent(n) c.connection.AddBytesSent(n) } func (c *streamFlowController) SendWindowSize() protocol.ByteCount { return min(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize()) } func (c *streamFlowController) IsNewlyBlocked() bool { blocked, _ := c.baseFlowController.IsNewlyBlocked() return blocked } func (c *streamFlowController) shouldQueueWindowUpdate() bool { return !c.receivedFinalOffset && c.hasWindowUpdate() } func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount { // If we already received the final offset for this stream, the peer won't need any additional flow control credit. if c.receivedFinalOffset { return 0 } // Don't use defer for unlocking the mutex here, GetWindowUpdate() is called frequently and defer shows up in the profiler c.mutex.Lock() oldWindowSize := c.receiveWindowSize offset := c.baseFlowController.getWindowUpdate() if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10)) c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier)) } c.mutex.Unlock() return offset } golang-github-lucas-clemente-quic-go-0.46.0/internal/flowcontrol/stream_flow_controller_test.go000066400000000000000000000252241465664453100331530ustar00rootroot00000000000000package flowcontrol import ( "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Stream Flow controller", func() { var controller *streamFlowController BeforeEach(func() { rttStats := &utils.RTTStats{} controller = &streamFlowController{ streamID: 10, connection: NewConnectionFlowController( 1000, 1000, func(protocol.ByteCount) bool { return true }, rttStats, utils.DefaultLogger, ).(*connectionFlowController), } controller.maxReceiveWindowSize = 10000 controller.rttStats = rttStats controller.logger = utils.DefaultLogger }) Context("Constructor", func() { rttStats := &utils.RTTStats{} const receiveWindow protocol.ByteCount = 2000 const maxReceiveWindow protocol.ByteCount = 3000 const sendWindow protocol.ByteCount = 4000 It("sets the send and receive windows", func() { cc := NewConnectionFlowController(0, 0, func(protocol.ByteCount) bool { return true }, nil, utils.DefaultLogger) fc := NewStreamFlowController(5, cc, receiveWindow, maxReceiveWindow, sendWindow, rttStats, utils.DefaultLogger).(*streamFlowController) Expect(fc.streamID).To(Equal(protocol.StreamID(5))) Expect(fc.receiveWindow).To(Equal(receiveWindow)) Expect(fc.maxReceiveWindowSize).To(Equal(maxReceiveWindow)) Expect(fc.sendWindow).To(Equal(sendWindow)) }) It("queues window updates", func() { cc := NewConnectionFlowController(receiveWindow, maxReceiveWindow, func(protocol.ByteCount) bool { return true }, nil, utils.DefaultLogger) fc := NewStreamFlowController(5, cc, receiveWindow, maxReceiveWindow, sendWindow, rttStats, utils.DefaultLogger).(*streamFlowController) Expect(fc.AddBytesRead(receiveWindow)).To(BeTrue()) }) }) Context("receiving data", func() { Context("registering received offsets", func() { var receiveWindow protocol.ByteCount = 10000 var receiveWindowSize protocol.ByteCount = 600 BeforeEach(func() { controller.receiveWindow = receiveWindow controller.receiveWindowSize = receiveWindowSize }) It("updates the highestReceived", func() { controller.highestReceived = 1337 Expect(controller.UpdateHighestReceived(1338, false)).To(Succeed()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1338))) }) It("informs the connection flow controller about received data", func() { controller.highestReceived = 10 controller.connection.(*connectionFlowController).highestReceived = 100 Expect(controller.UpdateHighestReceived(20, false)).To(Succeed()) Expect(controller.connection.(*connectionFlowController).highestReceived).To(Equal(protocol.ByteCount(100 + 10))) }) It("does not decrease the highestReceived", func() { controller.highestReceived = 1337 Expect(controller.UpdateHighestReceived(1000, false)).To(Succeed()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1337))) }) It("does nothing when setting the same byte offset", func() { controller.highestReceived = 1337 Expect(controller.UpdateHighestReceived(1337, false)).To(Succeed()) }) It("does not give a flow control violation when using the window completely", func() { controller.connection.(*connectionFlowController).receiveWindow = receiveWindow Expect(controller.UpdateHighestReceived(receiveWindow, false)).To(Succeed()) }) It("detects a flow control violation", func() { Expect(controller.UpdateHighestReceived(receiveWindow+1, false)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FlowControlError, ErrorMessage: "received 10001 bytes on stream 10, allowed 10000 bytes", })) }) It("accepts a final offset higher than the highest received", func() { Expect(controller.UpdateHighestReceived(100, false)).To(Succeed()) Expect(controller.UpdateHighestReceived(101, true)).To(Succeed()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(101))) }) It("errors when receiving a final offset smaller than the highest offset received so far", func() { controller.UpdateHighestReceived(100, false) Expect(controller.UpdateHighestReceived(50, true)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FinalSizeError, ErrorMessage: "received final offset 50 for stream 10, but already received offset 100 before", })) }) It("accepts delayed data after receiving a final offset", func() { Expect(controller.UpdateHighestReceived(300, true)).To(Succeed()) Expect(controller.UpdateHighestReceived(250, false)).To(Succeed()) }) It("errors when receiving a higher offset after receiving a final offset", func() { Expect(controller.UpdateHighestReceived(200, true)).To(Succeed()) Expect(controller.UpdateHighestReceived(250, false)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FinalSizeError, ErrorMessage: "received offset 250 for stream 10, but final offset was already received at 200", })) }) It("accepts duplicate final offsets", func() { Expect(controller.UpdateHighestReceived(200, true)).To(Succeed()) Expect(controller.UpdateHighestReceived(200, true)).To(Succeed()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(200))) }) It("errors when receiving inconsistent final offsets", func() { Expect(controller.UpdateHighestReceived(200, true)).To(Succeed()) Expect(controller.UpdateHighestReceived(201, true)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FinalSizeError, ErrorMessage: "received inconsistent final offset for stream 10 (old: 200, new: 201 bytes)", })) }) It("tells the connection flow controller when a stream is abandoned", func() { controller.AddBytesRead(5) Expect(controller.UpdateHighestReceived(100, true)).To(Succeed()) controller.Abandon() Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(100))) }) It("tolerates repeated calls to Abandon", func() { controller.AddBytesRead(5) Expect(controller.UpdateHighestReceived(100, true)).To(Succeed()) controller.Abandon() controller.Abandon() controller.Abandon() Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(100))) }) }) It("saves when data is read", func() { controller.AddBytesRead(200) Expect(controller.bytesRead).To(Equal(protocol.ByteCount(200))) Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(200))) }) Context("generating window updates", func() { var oldWindowSize protocol.ByteCount // update the congestion such that it returns a given value for the smoothed RTT setRtt := func(t time.Duration) { controller.rttStats.UpdateRTT(t, 0, time.Now()) Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked } BeforeEach(func() { controller.receiveWindow = 100 controller.receiveWindowSize = 60 controller.bytesRead = 100 - 60 controller.connection.(*connectionFlowController).receiveWindow = 100 controller.connection.(*connectionFlowController).receiveWindowSize = 120 oldWindowSize = controller.receiveWindowSize }) It("queues window updates", func() { Expect(controller.AddBytesRead(1)).To(BeFalse()) Expect(controller.AddBytesRead(29)).To(BeTrue()) Expect(controller.GetWindowUpdate()).ToNot(BeZero()) Expect(controller.AddBytesRead(1)).To(BeFalse()) }) It("tells the connection flow controller when the window was auto-tuned", func() { var allowed protocol.ByteCount controller.connection.(*connectionFlowController).allowWindowIncrease = func(size protocol.ByteCount) bool { allowed = size return true } oldOffset := controller.bytesRead setRtt(scaleDuration(20 * time.Millisecond)) controller.epochStartOffset = oldOffset controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.AddBytesRead(55) offset := controller.GetWindowUpdate() Expect(offset).To(Equal(oldOffset + 55 + 2*oldWindowSize)) Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) Expect(allowed).To(Equal(oldWindowSize)) Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(protocol.ByteCount(float64(controller.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))) }) It("doesn't increase the connection flow control window if it's not allowed", func() { oldOffset := controller.bytesRead oldConnectionSize := controller.connection.(*connectionFlowController).receiveWindowSize controller.connection.(*connectionFlowController).allowWindowIncrease = func(protocol.ByteCount) bool { return false } setRtt(scaleDuration(20 * time.Millisecond)) controller.epochStartOffset = oldOffset controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.AddBytesRead(55) offset := controller.GetWindowUpdate() Expect(offset).To(Equal(oldOffset + 55 + 2*oldWindowSize)) Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(oldConnectionSize)) }) It("sends a connection-level window update when a large stream is abandoned", func() { Expect(controller.UpdateHighestReceived(90, true)).To(Succeed()) Expect(controller.connection.GetWindowUpdate()).To(BeZero()) controller.Abandon() Expect(controller.connection.GetWindowUpdate()).ToNot(BeZero()) }) It("doesn't increase the window after a final offset was already received", func() { Expect(controller.UpdateHighestReceived(90, true)).To(Succeed()) Expect(controller.AddBytesRead(30)).To(BeFalse()) Expect(controller.GetWindowUpdate()).To(BeZero()) }) }) }) Context("sending data", func() { It("gets the size of the send window", func() { controller.connection.UpdateSendWindow(1000) controller.UpdateSendWindow(15) controller.AddBytesSent(5) Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(10))) }) It("makes sure that it doesn't overflow the connection-level window", func() { controller.connection.UpdateSendWindow(12) controller.UpdateSendWindow(20) controller.AddBytesSent(10) Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(2))) }) It("doesn't say that it's blocked, if only the connection is blocked", func() { controller.connection.UpdateSendWindow(50) controller.UpdateSendWindow(100) controller.AddBytesSent(50) blocked, _ := controller.connection.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(controller.IsNewlyBlocked()).To(BeFalse()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/000077500000000000000000000000001465664453100243515ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/aead.go000066400000000000000000000051511465664453100255740ustar00rootroot00000000000000package handshake import ( "encoding/binary" "github.com/quic-go/quic-go/internal/protocol" ) func createAEAD(suite *cipherSuite, trafficSecret []byte, v protocol.Version) *xorNonceAEAD { keyLabel := hkdfLabelKeyV1 ivLabel := hkdfLabelIVV1 if v == protocol.Version2 { keyLabel = hkdfLabelKeyV2 ivLabel = hkdfLabelIVV2 } key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, keyLabel, suite.KeyLen) iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, ivLabel, suite.IVLen()) return suite.AEAD(key, iv) } type longHeaderSealer struct { aead *xorNonceAEAD headerProtector headerProtector nonceBuf [8]byte } var _ LongHeaderSealer = &longHeaderSealer{} func newLongHeaderSealer(aead *xorNonceAEAD, headerProtector headerProtector) LongHeaderSealer { if aead.NonceSize() != 8 { panic("unexpected nonce size") } return &longHeaderSealer{ aead: aead, headerProtector: headerProtector, } } func (s *longHeaderSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { binary.BigEndian.PutUint64(s.nonceBuf[:], uint64(pn)) return s.aead.Seal(dst, s.nonceBuf[:], src, ad) } func (s *longHeaderSealer) EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { s.headerProtector.EncryptHeader(sample, firstByte, pnBytes) } func (s *longHeaderSealer) Overhead() int { return s.aead.Overhead() } type longHeaderOpener struct { aead *xorNonceAEAD headerProtector headerProtector highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected) // use a single array to avoid allocations nonceBuf [8]byte } var _ LongHeaderOpener = &longHeaderOpener{} func newLongHeaderOpener(aead *xorNonceAEAD, headerProtector headerProtector) LongHeaderOpener { if aead.NonceSize() != 8 { panic("unexpected nonce size") } return &longHeaderOpener{ aead: aead, headerProtector: headerProtector, } } func (o *longHeaderOpener) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber { return protocol.DecodePacketNumber(wirePNLen, o.highestRcvdPN, wirePN) } func (o *longHeaderOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) { binary.BigEndian.PutUint64(o.nonceBuf[:], uint64(pn)) dec, err := o.aead.Open(dst, o.nonceBuf[:], src, ad) if err == nil { o.highestRcvdPN = max(o.highestRcvdPN, pn) } else { err = ErrDecryptionFailed } return dec, err } func (o *longHeaderOpener) DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) { o.headerProtector.DecryptHeader(sample, firstByte, pnBytes) } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/aead_test.go000066400000000000000000000124161465664453100266350ustar00rootroot00000000000000package handshake import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/tls" "fmt" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Long Header AEAD", func() { for _, ver := range []protocol.Version{protocol.Version1, protocol.Version2} { v := ver Context(fmt.Sprintf("using version %s", v), func() { for i := range cipherSuites { cs := cipherSuites[i] Context(fmt.Sprintf("using %s", tls.CipherSuiteName(cs.ID)), func() { getSealerAndOpener := func() (LongHeaderSealer, LongHeaderOpener) { key := make([]byte, 16) hpKey := make([]byte, 16) rand.Read(key) rand.Read(hpKey) block, err := aes.NewCipher(key) Expect(err).ToNot(HaveOccurred()) aead, err := cipher.NewGCM(block) Expect(err).ToNot(HaveOccurred()) return newLongHeaderSealer(&xorNonceAEAD{aead: aead}, newHeaderProtector(cs, hpKey, true, v)), newLongHeaderOpener(&xorNonceAEAD{aead: aead}, newHeaderProtector(cs, hpKey, true, v)) } Context("message encryption", func() { msg := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") ad := []byte("Donec in velit neque.") It("encrypts and decrypts a message", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) opened, err := opener.Open(nil, encrypted, 0x1337, ad) Expect(err).ToNot(HaveOccurred()) Expect(opened).To(Equal(msg)) }) It("fails to open a message if the associated data is not the same", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted, 0x1337, []byte("wrong ad")) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("fails to open a message if the packet number is not the same", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted, 0x42, ad) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("decodes the packet number", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted, 0x1337, ad) Expect(err).ToNot(HaveOccurred()) Expect(opener.DecodePacketNumber(0x38, protocol.PacketNumberLen1)).To(BeEquivalentTo(0x1338)) }) It("ignores packets it can't decrypt for packet number derivation", func() { sealer, opener := getSealerAndOpener() encrypted := sealer.Seal(nil, msg, 0x1337, ad) _, err := opener.Open(nil, encrypted[:len(encrypted)-1], 0x1337, ad) Expect(err).To(HaveOccurred()) Expect(opener.DecodePacketNumber(0x38, protocol.PacketNumberLen1)).To(BeEquivalentTo(0x38)) }) }) Context("header encryption", func() { It("encrypts and encrypts the header", func() { sealer, opener := getSealerAndOpener() var lastFourBitsDifferent int for i := 0; i < 100; i++ { sample := make([]byte, 16) rand.Read(sample) header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} sealer.EncryptHeader(sample, &header[0], header[9:13]) if header[0]&0xf != 0xb5&0xf { lastFourBitsDifferent++ } Expect(header[0] & 0xf0).To(Equal(byte(0xb5 & 0xf0))) Expect(header[1:9]).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(header[9:13]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) opener.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).To(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) } Expect(lastFourBitsDifferent).To(BeNumerically(">", 75)) }) It("encrypts and encrypts the header, for a 0xfff..fff sample", func() { sealer, opener := getSealerAndOpener() var lastFourBitsDifferent int for i := 0; i < 100; i++ { sample := bytes.Repeat([]byte{0xff}, 16) header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} sealer.EncryptHeader(sample, &header[0], header[9:13]) if header[0]&0xf != 0xb5&0xf { lastFourBitsDifferent++ } Expect(header[0] & 0xf0).To(Equal(byte(0xb5 & 0xf0))) Expect(header[1:9]).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(header[9:13]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) opener.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).To(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) } }) It("fails to decrypt the header when using a different sample", func() { sealer, opener := getSealerAndOpener() header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} sample := make([]byte, 16) rand.Read(sample) sealer.EncryptHeader(sample, &header[0], header[9:13]) rand.Read(sample) // use a different sample opener.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).ToNot(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) }) }) }) } }) } }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/cipher_suite.go000066400000000000000000000052021465664453100273620ustar00rootroot00000000000000package handshake import ( "crypto" "crypto/aes" "crypto/cipher" "crypto/tls" "fmt" "golang.org/x/crypto/chacha20poly1305" ) // These cipher suite implementations are copied from the standard library crypto/tls package. const aeadNonceLength = 12 type cipherSuite struct { ID uint16 Hash crypto.Hash KeyLen int AEAD func(key, nonceMask []byte) *xorNonceAEAD } func (s cipherSuite) IVLen() int { return aeadNonceLength } func getCipherSuite(id uint16) *cipherSuite { switch id { case tls.TLS_AES_128_GCM_SHA256: return &cipherSuite{ID: tls.TLS_AES_128_GCM_SHA256, Hash: crypto.SHA256, KeyLen: 16, AEAD: aeadAESGCMTLS13} case tls.TLS_CHACHA20_POLY1305_SHA256: return &cipherSuite{ID: tls.TLS_CHACHA20_POLY1305_SHA256, Hash: crypto.SHA256, KeyLen: 32, AEAD: aeadChaCha20Poly1305} case tls.TLS_AES_256_GCM_SHA384: return &cipherSuite{ID: tls.TLS_AES_256_GCM_SHA384, Hash: crypto.SHA384, KeyLen: 32, AEAD: aeadAESGCMTLS13} default: panic(fmt.Sprintf("unknown cypher suite: %d", id)) } } func aeadAESGCMTLS13(key, nonceMask []byte) *xorNonceAEAD { if len(nonceMask) != aeadNonceLength { panic("tls: internal error: wrong nonce length") } aes, err := aes.NewCipher(key) if err != nil { panic(err) } aead, err := cipher.NewGCM(aes) if err != nil { panic(err) } ret := &xorNonceAEAD{aead: aead} copy(ret.nonceMask[:], nonceMask) return ret } func aeadChaCha20Poly1305(key, nonceMask []byte) *xorNonceAEAD { if len(nonceMask) != aeadNonceLength { panic("tls: internal error: wrong nonce length") } aead, err := chacha20poly1305.New(key) if err != nil { panic(err) } ret := &xorNonceAEAD{aead: aead} copy(ret.nonceMask[:], nonceMask) return ret } // xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce // before each call. type xorNonceAEAD struct { nonceMask [aeadNonceLength]byte aead cipher.AEAD } func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() } func (f *xorNonceAEAD) explicitNonceLen() int { return 0 } func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte { for i, b := range nonce { f.nonceMask[4+i] ^= b } result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData) for i, b := range nonce { f.nonceMask[4+i] ^= b } return result } func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) { for i, b := range nonce { f.nonceMask[4+i] ^= b } result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData) for i, b := range nonce { f.nonceMask[4+i] ^= b } return result, err } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/crypto_setup.go000066400000000000000000000447541465664453100274560ustar00rootroot00000000000000package handshake import ( "context" "crypto/tls" "errors" "fmt" "net" "strings" "sync/atomic" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go/quicvarint" ) type quicVersionContextKey struct{} var QUICVersionContextKey = &quicVersionContextKey{} const clientSessionStateRevision = 4 type cryptoSetup struct { tlsConf *tls.Config conn *tls.QUICConn events []Event version protocol.Version ourParams *wire.TransportParameters peerParams *wire.TransportParameters zeroRTTParameters *wire.TransportParameters allow0RTT bool rttStats *utils.RTTStats tracer *logging.ConnectionTracer logger utils.Logger perspective protocol.Perspective handshakeCompleteTime time.Time zeroRTTOpener LongHeaderOpener // only set for the server zeroRTTSealer LongHeaderSealer // only set for the client initialOpener LongHeaderOpener initialSealer LongHeaderSealer handshakeOpener LongHeaderOpener handshakeSealer LongHeaderSealer used0RTT atomic.Bool aead *updatableAEAD has1RTTSealer bool has1RTTOpener bool } var _ CryptoSetup = &cryptoSetup{} // NewCryptoSetupClient creates a new crypto setup for the client func NewCryptoSetupClient( connID protocol.ConnectionID, tp *wire.TransportParameters, tlsConf *tls.Config, enable0RTT bool, rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.Version, ) CryptoSetup { cs := newCryptoSetup( connID, tp, rttStats, tracer, logger, protocol.PerspectiveClient, version, ) tlsConf = tlsConf.Clone() tlsConf.MinVersion = tls.VersionTLS13 quicConf := &tls.QUICConfig{TLSConfig: tlsConf} qtls.SetupConfigForClient(quicConf, cs.marshalDataForSessionState, cs.handleDataFromSessionState) cs.tlsConf = tlsConf cs.allow0RTT = enable0RTT cs.conn = tls.QUICClient(quicConf) cs.conn.SetTransportParameters(cs.ourParams.Marshal(protocol.PerspectiveClient)) return cs } // NewCryptoSetupServer creates a new crypto setup for the server func NewCryptoSetupServer( connID protocol.ConnectionID, localAddr, remoteAddr net.Addr, tp *wire.TransportParameters, tlsConf *tls.Config, allow0RTT bool, rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.Version, ) CryptoSetup { cs := newCryptoSetup( connID, tp, rttStats, tracer, logger, protocol.PerspectiveServer, version, ) cs.allow0RTT = allow0RTT tlsConf = qtls.SetupConfigForServer(tlsConf, localAddr, remoteAddr, cs.getDataForSessionTicket, cs.handleSessionTicket) cs.tlsConf = tlsConf cs.conn = tls.QUICServer(&tls.QUICConfig{TLSConfig: tlsConf}) return cs } func newCryptoSetup( connID protocol.ConnectionID, tp *wire.TransportParameters, rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, perspective protocol.Perspective, version protocol.Version, ) *cryptoSetup { initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version) if tracer != nil && tracer.UpdatedKeyFromTLS != nil { tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) } return &cryptoSetup{ initialSealer: initialSealer, initialOpener: initialOpener, aead: newUpdatableAEAD(rttStats, tracer, logger, version), events: make([]Event, 0, 16), ourParams: tp, rttStats: rttStats, tracer: tracer, logger: logger, perspective: perspective, version: version, } } func (h *cryptoSetup) ChangeConnectionID(id protocol.ConnectionID) { initialSealer, initialOpener := NewInitialAEAD(id, h.perspective, h.version) h.initialSealer = initialSealer h.initialOpener = initialOpener if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) h.tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) } } func (h *cryptoSetup) SetLargest1RTTAcked(pn protocol.PacketNumber) error { return h.aead.SetLargestAcked(pn) } func (h *cryptoSetup) StartHandshake(ctx context.Context) error { err := h.conn.Start(context.WithValue(ctx, QUICVersionContextKey, h.version)) if err != nil { return wrapError(err) } for { ev := h.conn.NextEvent() done, err := h.handleEvent(ev) if err != nil { return wrapError(err) } if done { break } } if h.perspective == protocol.PerspectiveClient { if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { h.logger.Debugf("Doing 0-RTT.") h.events = append(h.events, Event{Kind: EventRestoredTransportParameters, TransportParameters: h.zeroRTTParameters}) } else { h.logger.Debugf("Not doing 0-RTT. Has sealer: %t, has params: %t", h.zeroRTTSealer != nil, h.zeroRTTParameters != nil) } } return nil } // Close closes the crypto setup. // It aborts the handshake, if it is still running. func (h *cryptoSetup) Close() error { return h.conn.Close() } // HandleMessage handles a TLS handshake message. // It is called by the crypto streams when a new message is available. func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) error { if err := h.handleMessage(data, encLevel); err != nil { return wrapError(err) } return nil } func (h *cryptoSetup) handleMessage(data []byte, encLevel protocol.EncryptionLevel) error { if err := h.conn.HandleData(qtls.ToTLSEncryptionLevel(encLevel), data); err != nil { return err } for { ev := h.conn.NextEvent() done, err := h.handleEvent(ev) if err != nil { return err } if done { return nil } } } func (h *cryptoSetup) handleEvent(ev tls.QUICEvent) (done bool, err error) { switch ev.Kind { case tls.QUICNoEvent: return true, nil case tls.QUICSetReadSecret: h.setReadKey(ev.Level, ev.Suite, ev.Data) return false, nil case tls.QUICSetWriteSecret: h.setWriteKey(ev.Level, ev.Suite, ev.Data) return false, nil case tls.QUICTransportParameters: return false, h.handleTransportParameters(ev.Data) case tls.QUICTransportParametersRequired: h.conn.SetTransportParameters(h.ourParams.Marshal(h.perspective)) return false, nil case tls.QUICRejectedEarlyData: h.rejected0RTT() return false, nil case tls.QUICWriteData: h.writeRecord(ev.Level, ev.Data) return false, nil case tls.QUICHandshakeDone: h.handshakeComplete() return false, nil default: // Unknown events should be ignored. // crypto/tls will ensure that this is safe to do. // See the discussion following https://github.com/golang/go/issues/68124#issuecomment-2187042510 for details. return false, nil } } func (h *cryptoSetup) NextEvent() Event { if len(h.events) == 0 { return Event{Kind: EventNoEvent} } ev := h.events[0] h.events = h.events[1:] return ev } func (h *cryptoSetup) handleTransportParameters(data []byte) error { var tp wire.TransportParameters if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil { return err } h.peerParams = &tp h.events = append(h.events, Event{Kind: EventReceivedTransportParameters, TransportParameters: h.peerParams}) return nil } // must be called after receiving the transport parameters func (h *cryptoSetup) marshalDataForSessionState(earlyData bool) []byte { b := make([]byte, 0, 256) b = quicvarint.Append(b, clientSessionStateRevision) b = quicvarint.Append(b, uint64(h.rttStats.SmoothedRTT().Microseconds())) if earlyData { // only save the transport parameters for 0-RTT enabled session tickets return h.peerParams.MarshalForSessionTicket(b) } return b } func (h *cryptoSetup) handleDataFromSessionState(data []byte, earlyData bool) (allowEarlyData bool) { rtt, tp, err := decodeDataFromSessionState(data, earlyData) if err != nil { h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error()) return } h.rttStats.SetInitialRTT(rtt) // The session ticket might have been saved from a connection that allowed 0-RTT, // and therefore contain transport parameters. // Only use them if 0-RTT is actually used on the new connection. if tp != nil && h.allow0RTT { h.zeroRTTParameters = tp return true } return false } func decodeDataFromSessionState(b []byte, earlyData bool) (time.Duration, *wire.TransportParameters, error) { ver, l, err := quicvarint.Parse(b) if err != nil { return 0, nil, err } b = b[l:] if ver != clientSessionStateRevision { return 0, nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision) } rttEncoded, l, err := quicvarint.Parse(b) if err != nil { return 0, nil, err } b = b[l:] rtt := time.Duration(rttEncoded) * time.Microsecond if !earlyData { return rtt, nil, nil } var tp wire.TransportParameters if err := tp.UnmarshalFromSessionTicket(b); err != nil { return 0, nil, err } return rtt, &tp, nil } func (h *cryptoSetup) getDataForSessionTicket() []byte { ticket := &sessionTicket{ RTT: h.rttStats.SmoothedRTT(), } if h.allow0RTT { ticket.Parameters = h.ourParams } return ticket.Marshal() } // GetSessionTicket generates a new session ticket. // Due to limitations in crypto/tls, it's only possible to generate a single session ticket per connection. // It is only valid for the server. func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { if err := h.conn.SendSessionTicket(tls.QUICSessionTicketOptions{EarlyData: h.allow0RTT}); err != nil { // Session tickets might be disabled by tls.Config.SessionTicketsDisabled. // We can't check h.tlsConfig here, since the actual config might have been obtained from // the GetConfigForClient callback. // See https://github.com/golang/go/issues/62032. // Once that issue is resolved, this error assertion can be removed. if strings.Contains(err.Error(), "session ticket keys unavailable") { return nil, nil } return nil, err } ev := h.conn.NextEvent() if ev.Kind != tls.QUICWriteData || ev.Level != tls.QUICEncryptionLevelApplication { panic("crypto/tls bug: where's my session ticket?") } ticket := ev.Data if ev := h.conn.NextEvent(); ev.Kind != tls.QUICNoEvent { panic("crypto/tls bug: why more than one ticket?") } return ticket, nil } // handleSessionTicket is called for the server when receiving the client's session ticket. // It reads parameters from the session ticket and checks whether to accept 0-RTT if the session ticket enabled 0-RTT. // Note that the fact that the session ticket allows 0-RTT doesn't mean that the actual TLS handshake enables 0-RTT: // A client may use a 0-RTT enabled session to resume a TLS session without using 0-RTT. func (h *cryptoSetup) handleSessionTicket(sessionTicketData []byte, using0RTT bool) bool { var t sessionTicket if err := t.Unmarshal(sessionTicketData, using0RTT); err != nil { h.logger.Debugf("Unmarshalling session ticket failed: %s", err.Error()) return false } h.rttStats.SetInitialRTT(t.RTT) if !using0RTT { return false } valid := h.ourParams.ValidFor0RTT(t.Parameters) if !valid { h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.") return false } if !h.allow0RTT { h.logger.Debugf("0-RTT not allowed. Rejecting 0-RTT.") return false } h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT) return true } // rejected0RTT is called for the client when the server rejects 0-RTT. func (h *cryptoSetup) rejected0RTT() { h.logger.Debugf("0-RTT was rejected. Dropping 0-RTT keys.") had0RTTKeys := h.zeroRTTSealer != nil h.zeroRTTSealer = nil if had0RTTKeys { h.events = append(h.events, Event{Kind: EventDiscard0RTTKeys}) } } func (h *cryptoSetup) setReadKey(el tls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { suite := getCipherSuite(suiteID) //nolint:exhaustive // The TLS stack doesn't export Initial keys. switch el { case tls.QUICEncryptionLevelEarly: if h.perspective == protocol.PerspectiveClient { panic("Received 0-RTT read key for the client") } h.zeroRTTOpener = newLongHeaderOpener( createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), ) h.used0RTT.Store(true) if h.logger.Debug() { h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) } case tls.QUICEncryptionLevelHandshake: h.handshakeOpener = newLongHeaderOpener( createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), ) if h.logger.Debug() { h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID)) } case tls.QUICEncryptionLevelApplication: h.aead.SetReadKey(suite, trafficSecret) h.has1RTTOpener = true if h.logger.Debug() { h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) } default: panic("unexpected read encryption level") } h.events = append(h.events, Event{Kind: EventReceivedReadKeys}) if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective.Opposite()) } } func (h *cryptoSetup) setWriteKey(el tls.QUICEncryptionLevel, suiteID uint16, trafficSecret []byte) { suite := getCipherSuite(suiteID) //nolint:exhaustive // The TLS stack doesn't export Initial keys. switch el { case tls.QUICEncryptionLevelEarly: if h.perspective == protocol.PerspectiveServer { panic("Received 0-RTT write key for the server") } h.zeroRTTSealer = newLongHeaderSealer( createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), ) if h.logger.Debug() { h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective) } // don't set used0RTT here. 0-RTT might still get rejected. return case tls.QUICEncryptionLevelHandshake: h.handshakeSealer = newLongHeaderSealer( createAEAD(suite, trafficSecret, h.version), newHeaderProtector(suite, trafficSecret, true, h.version), ) if h.logger.Debug() { h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } case tls.QUICEncryptionLevelApplication: h.aead.SetWriteKey(suite, trafficSecret) h.has1RTTSealer = true if h.logger.Debug() { h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) } if h.zeroRTTSealer != nil { // Once we receive handshake keys, we know that 0-RTT was not rejected. h.used0RTT.Store(true) h.zeroRTTSealer = nil h.logger.Debugf("Dropping 0-RTT keys.") if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) } } default: panic("unexpected write encryption level") } if h.tracer != nil && h.tracer.UpdatedKeyFromTLS != nil { h.tracer.UpdatedKeyFromTLS(qtls.FromTLSEncryptionLevel(el), h.perspective) } } // writeRecord is called when TLS writes data func (h *cryptoSetup) writeRecord(encLevel tls.QUICEncryptionLevel, p []byte) { //nolint:exhaustive // handshake records can only be written for Initial and Handshake. switch encLevel { case tls.QUICEncryptionLevelInitial: h.events = append(h.events, Event{Kind: EventWriteInitialData, Data: p}) case tls.QUICEncryptionLevelHandshake: h.events = append(h.events, Event{Kind: EventWriteHandshakeData, Data: p}) case tls.QUICEncryptionLevelApplication: panic("unexpected write") default: panic(fmt.Sprintf("unexpected write encryption level: %s", encLevel)) } } func (h *cryptoSetup) DiscardInitialKeys() { dropped := h.initialOpener != nil h.initialOpener = nil h.initialSealer = nil if dropped { h.logger.Debugf("Dropping Initial keys.") } } func (h *cryptoSetup) handshakeComplete() { h.handshakeCompleteTime = time.Now() h.events = append(h.events, Event{Kind: EventHandshakeComplete}) } func (h *cryptoSetup) SetHandshakeConfirmed() { h.aead.SetHandshakeConfirmed() // drop Handshake keys var dropped bool if h.handshakeOpener != nil { h.handshakeOpener = nil h.handshakeSealer = nil dropped = true } if dropped { h.logger.Debugf("Dropping Handshake keys.") } } func (h *cryptoSetup) GetInitialSealer() (LongHeaderSealer, error) { if h.initialSealer == nil { return nil, ErrKeysDropped } return h.initialSealer, nil } func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) { if h.zeroRTTSealer == nil { return nil, ErrKeysDropped } return h.zeroRTTSealer, nil } func (h *cryptoSetup) GetHandshakeSealer() (LongHeaderSealer, error) { if h.handshakeSealer == nil { if h.initialSealer == nil { return nil, ErrKeysDropped } return nil, ErrKeysNotYetAvailable } return h.handshakeSealer, nil } func (h *cryptoSetup) Get1RTTSealer() (ShortHeaderSealer, error) { if !h.has1RTTSealer { return nil, ErrKeysNotYetAvailable } return h.aead, nil } func (h *cryptoSetup) GetInitialOpener() (LongHeaderOpener, error) { if h.initialOpener == nil { return nil, ErrKeysDropped } return h.initialOpener, nil } func (h *cryptoSetup) Get0RTTOpener() (LongHeaderOpener, error) { if h.zeroRTTOpener == nil { if h.initialOpener != nil { return nil, ErrKeysNotYetAvailable } // if the initial opener is also not available, the keys were already dropped return nil, ErrKeysDropped } return h.zeroRTTOpener, nil } func (h *cryptoSetup) GetHandshakeOpener() (LongHeaderOpener, error) { if h.handshakeOpener == nil { if h.initialOpener != nil { return nil, ErrKeysNotYetAvailable } // if the initial opener is also not available, the keys were already dropped return nil, ErrKeysDropped } return h.handshakeOpener, nil } func (h *cryptoSetup) Get1RTTOpener() (ShortHeaderOpener, error) { if h.zeroRTTOpener != nil && time.Since(h.handshakeCompleteTime) > 3*h.rttStats.PTO(true) { h.zeroRTTOpener = nil h.logger.Debugf("Dropping 0-RTT keys.") if h.tracer != nil && h.tracer.DroppedEncryptionLevel != nil { h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) } } if !h.has1RTTOpener { return nil, ErrKeysNotYetAvailable } return h.aead, nil } func (h *cryptoSetup) ConnectionState() ConnectionState { return ConnectionState{ ConnectionState: h.conn.ConnectionState(), Used0RTT: h.used0RTT.Load(), } } func wrapError(err error) error { if alertErr := tls.AlertError(0); errors.As(err, &alertErr) { return qerr.NewLocalCryptoError(uint8(alertErr), err) } return &qerr.TransportError{ErrorCode: qerr.InternalError, ErrorMessage: err.Error()} } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/crypto_setup_test.go000066400000000000000000000463041465664453100305060ustar00rootroot00000000000000package handshake import ( "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "errors" "math/big" "net" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/testdata" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) const ( typeClientHello = 1 typeNewSessionTicket = 4 ) type mockClientSessionCache struct { cache tls.ClientSessionCache puts chan *tls.ClientSessionState } var _ tls.ClientSessionCache = &mockClientSessionCache{} func newMockClientSessionCache() *mockClientSessionCache { return &mockClientSessionCache{ puts: make(chan *tls.ClientSessionState, 1), cache: tls.NewLRUClientSessionCache(1), } } func (m *mockClientSessionCache) Get(sessionKey string) (session *tls.ClientSessionState, ok bool) { return m.cache.Get(sessionKey) } func (m *mockClientSessionCache) Put(sessionKey string, cs *tls.ClientSessionState) { m.puts <- cs m.cache.Put(sessionKey, cs) } var _ = Describe("Crypto Setup TLS", func() { generateCert := func() tls.Certificate { priv, err := rsa.GenerateKey(rand.Reader, 2048) Expect(err).ToNot(HaveOccurred()) tmpl := &x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{}, SignatureAlgorithm: x509.SHA256WithRSA, NotBefore: time.Now(), NotAfter: time.Now().Add(time.Hour), // valid for an hour BasicConstraintsValid: true, } certDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, priv.Public(), priv) Expect(err).ToNot(HaveOccurred()) return tls.Certificate{ PrivateKey: priv, Certificate: [][]byte{certDER}, } } var clientConf, serverConf *tls.Config BeforeEach(func() { serverConf = testdata.GetTLSConfig() serverConf.NextProtos = []string{"crypto-setup"} clientConf = &tls.Config{ ServerName: "localhost", RootCAs: testdata.GetRootCA(), NextProtos: []string{"crypto-setup"}, } }) It("handles qtls errors occurring before during ClientHello generation", func() { tlsConf := testdata.GetTLSConfig() tlsConf.InsecureSkipVerify = true tlsConf.NextProtos = []string{""} cl := NewCryptoSetupClient( protocol.ConnectionID{}, &wire.TransportParameters{}, tlsConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("client"), protocol.Version1, ) var terr *qerr.TransportError err := cl.StartHandshake(context.Background()) Expect(errors.As(err, &terr)).To(BeTrue()) Expect(terr.ErrorCode).To(BeEquivalentTo(0x100 + 0x50)) Expect(err.Error()).To(ContainSubstring("tls: invalid NextProtos value")) }) It("errors when a message is received at the wrong encryption level", func() { var token protocol.StatelessResetToken server := NewCryptoSetupServer( protocol.ConnectionID{}, &net.UDPAddr{IP: net.IPv6loopback, Port: 1234}, &net.UDPAddr{IP: net.IPv6loopback, Port: 4321}, &wire.TransportParameters{StatelessResetToken: &token}, testdata.GetTLSConfig(), false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.Version1, ) Expect(server.StartHandshake(context.Background())).To(Succeed()) fakeCH := append([]byte{typeClientHello, 0, 0, 6}, []byte("foobar")...) // wrong encryption level err := server.HandleMessage(fakeCH, protocol.EncryptionHandshake) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("tls: handshake data received at wrong level")) }) Context("doing the handshake", func() { newRTTStatsWithRTT := func(rtt time.Duration) *utils.RTTStats { rttStats := &utils.RTTStats{} rttStats.UpdateRTT(rtt, 0, time.Now()) ExpectWithOffset(1, rttStats.SmoothedRTT()).To(Equal(rtt)) return rttStats } // The clientEvents and serverEvents contain all events that were not processed by the function, // i.e. not EventWriteInitialData, EventWriteHandshakeData, EventHandshakeComplete. handshake := func(client, server CryptoSetup) (clientEvents []Event, clientErr error, serverEvents []Event, serverErr error) { Expect(client.StartHandshake(context.Background())).To(Succeed()) Expect(server.StartHandshake(context.Background())).To(Succeed()) var clientHandshakeComplete, serverHandshakeComplete bool for { clientLoop: for { ev := client.NextEvent() switch ev.Kind { case EventNoEvent: break clientLoop case EventWriteInitialData: if err := server.HandleMessage(ev.Data, protocol.EncryptionInitial); err != nil { serverErr = err return } case EventWriteHandshakeData: if err := server.HandleMessage(ev.Data, protocol.EncryptionHandshake); err != nil { serverErr = err return } case EventHandshakeComplete: clientHandshakeComplete = true default: clientEvents = append(clientEvents, ev) } } serverLoop: for { ev := server.NextEvent() switch ev.Kind { case EventNoEvent: break serverLoop case EventWriteInitialData: if err := client.HandleMessage(ev.Data, protocol.EncryptionInitial); err != nil { clientErr = err return } case EventWriteHandshakeData: if err := client.HandleMessage(ev.Data, protocol.EncryptionHandshake); err != nil { clientErr = err return } case EventHandshakeComplete: serverHandshakeComplete = true ticket, err := server.GetSessionTicket() Expect(err).ToNot(HaveOccurred()) if ticket != nil { Expect(client.HandleMessage(ticket, protocol.Encryption1RTT)).To(Succeed()) } default: serverEvents = append(serverEvents, ev) } } if clientHandshakeComplete && serverHandshakeComplete { break } } return } handshakeWithTLSConf := func( clientConf, serverConf *tls.Config, clientRTTStats, serverRTTStats *utils.RTTStats, clientTransportParameters, serverTransportParameters *wire.TransportParameters, enable0RTT bool, ) (CryptoSetup /* client */, []Event /* more client events */, error, /* client error */ CryptoSetup /* server */, []Event /* more server events */, error, /* server error */ ) { client := NewCryptoSetupClient( protocol.ConnectionID{}, clientTransportParameters, clientConf, enable0RTT, clientRTTStats, nil, utils.DefaultLogger.WithPrefix("client"), protocol.Version1, ) if serverTransportParameters.StatelessResetToken == nil { var token protocol.StatelessResetToken serverTransportParameters.StatelessResetToken = &token } server := NewCryptoSetupServer( protocol.ConnectionID{}, &net.UDPAddr{IP: net.IPv6loopback, Port: 1234}, &net.UDPAddr{IP: net.IPv6loopback, Port: 4321}, serverTransportParameters, serverConf, enable0RTT, serverRTTStats, nil, utils.DefaultLogger.WithPrefix("server"), protocol.Version1, ) cEvents, cErr, sEvents, sErr := handshake(client, server) return client, cEvents, cErr, server, sEvents, sErr } It("handshakes", func() { _, _, clientErr, _, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) }) It("performs a HelloRetryRequst", func() { serverConf.CurvePreferences = []tls.CurveID{tls.CurveP384} _, _, clientErr, _, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) }) It("handshakes with client auth", func() { clientConf.Certificates = []tls.Certificate{generateCert()} serverConf.ClientAuth = tls.RequireAnyClientCert _, _, clientErr, _, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) }) It("receives transport parameters", func() { cTransportParameters := &wire.TransportParameters{ActiveConnectionIDLimit: 2, MaxIdleTimeout: 42 * time.Second} client := NewCryptoSetupClient( protocol.ConnectionID{}, cTransportParameters, clientConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("client"), protocol.Version1, ) var token protocol.StatelessResetToken sTransportParameters := &wire.TransportParameters{ MaxIdleTimeout: 1337 * time.Second, StatelessResetToken: &token, ActiveConnectionIDLimit: 2, } server := NewCryptoSetupServer( protocol.ConnectionID{}, &net.UDPAddr{IP: net.IPv6loopback, Port: 1234}, &net.UDPAddr{IP: net.IPv6loopback, Port: 4321}, sTransportParameters, serverConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.Version1, ) clientEvents, cErr, serverEvents, sErr := handshake(client, server) Expect(cErr).ToNot(HaveOccurred()) Expect(sErr).ToNot(HaveOccurred()) var clientReceivedTransportParameters *wire.TransportParameters for _, ev := range clientEvents { if ev.Kind == EventReceivedTransportParameters { clientReceivedTransportParameters = ev.TransportParameters } } Expect(clientReceivedTransportParameters).ToNot(BeNil()) Expect(clientReceivedTransportParameters.MaxIdleTimeout).To(Equal(1337 * time.Second)) var serverReceivedTransportParameters *wire.TransportParameters for _, ev := range serverEvents { if ev.Kind == EventReceivedTransportParameters { serverReceivedTransportParameters = ev.TransportParameters } } Expect(serverReceivedTransportParameters).ToNot(BeNil()) Expect(serverReceivedTransportParameters.MaxIdleTimeout).To(Equal(42 * time.Second)) }) Context("with session tickets", func() { It("errors when the NewSessionTicket is sent at the wrong encryption level", func() { client, _, clientErr, _, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) // inject an invalid session ticket b := append([]byte{uint8(typeNewSessionTicket), 0, 0, 6}, []byte("foobar")...) err := client.HandleMessage(b, protocol.EncryptionHandshake) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("tls: handshake data received at wrong level")) }) It("errors when handling the NewSessionTicket fails", func() { client, _, clientErr, _, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) // inject an invalid session ticket b := append([]byte{uint8(typeNewSessionTicket), 0, 0, 6}, []byte("foobar")...) err := client.HandleMessage(b, protocol.Encryption1RTT) Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode.IsCryptoError()).To(BeTrue()) }) It("uses session resumption", func() { csc := newMockClientSessionCache() clientConf.ClientSessionCache = csc const serverRTT = 25 * time.Millisecond // RTT as measured by the server. Should be restored. const clientRTT = 30 * time.Millisecond // RTT as measured by the client. Should be restored. serverOrigRTTStats := newRTTStatsWithRTT(serverRTT) clientOrigRTTStats := newRTTStatsWithRTT(clientRTT) client, _, clientErr, server, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientOrigRTTStats, serverOrigRTTStats, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(csc.puts).Should(Receive()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) clientRTTStats := &utils.RTTStats{} serverRTTStats := &utils.RTTStats{} client, _, clientErr, server, _, serverErr = handshakeWithTLSConf( clientConf, serverConf, clientRTTStats, serverRTTStats, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(csc.puts).Should(Receive()) Expect(server.ConnectionState().DidResume).To(BeTrue()) Expect(client.ConnectionState().DidResume).To(BeTrue()) Expect(clientRTTStats.SmoothedRTT()).To(Equal(clientRTT)) Expect(serverRTTStats.SmoothedRTT()).To(Equal(serverRTT)) }) It("doesn't use session resumption if the server disabled it", func() { csc := newMockClientSessionCache() clientConf.ClientSessionCache = csc client, _, clientErr, server, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(csc.puts).Should(Receive()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) serverConf.SessionTicketsDisabled = true client, _, clientErr, server, _, serverErr = handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Consistently(csc.puts, 25*time.Millisecond).ShouldNot(Receive()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) }) It("uses 0-RTT", func() { csc := newMockClientSessionCache() clientConf.ClientSessionCache = csc const serverRTT = 25 * time.Millisecond // RTT as measured by the server. Should be restored. const clientRTT = 30 * time.Millisecond // RTT as measured by the client. Should be restored. serverOrigRTTStats := newRTTStatsWithRTT(serverRTT) clientOrigRTTStats := newRTTStatsWithRTT(clientRTT) const initialMaxData protocol.ByteCount = 1337 client, _, clientErr, server, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientOrigRTTStats, serverOrigRTTStats, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(csc.puts).Should(Receive()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) clientRTTStats := &utils.RTTStats{} serverRTTStats := &utils.RTTStats{} client, clientEvents, clientErr, server, serverEvents, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientRTTStats, serverRTTStats, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Expect(clientRTTStats.SmoothedRTT()).To(Equal(clientRTT)) Expect(serverRTTStats.SmoothedRTT()).To(Equal(serverRTT)) var tp *wire.TransportParameters var clientReceived0RTTKeys bool for _, ev := range clientEvents { switch ev.Kind { case EventRestoredTransportParameters: tp = ev.TransportParameters case EventReceivedReadKeys: clientReceived0RTTKeys = true } } Expect(clientReceived0RTTKeys).To(BeTrue()) Expect(tp).ToNot(BeNil()) Expect(tp.InitialMaxData).To(Equal(initialMaxData)) var serverReceived0RTTKeys bool for _, ev := range serverEvents { switch ev.Kind { case EventReceivedReadKeys: serverReceived0RTTKeys = true } } Expect(serverReceived0RTTKeys).To(BeTrue()) Expect(server.ConnectionState().DidResume).To(BeTrue()) Expect(client.ConnectionState().DidResume).To(BeTrue()) Expect(server.ConnectionState().Used0RTT).To(BeTrue()) Expect(client.ConnectionState().Used0RTT).To(BeTrue()) }) It("rejects 0-RTT, when the transport parameters changed", func() { csc := newMockClientSessionCache() clientConf.ClientSessionCache = csc const clientRTT = 30 * time.Millisecond // RTT as measured by the client. Should be restored. clientOrigRTTStats := newRTTStatsWithRTT(clientRTT) const initialMaxData protocol.ByteCount = 1337 client, _, clientErr, server, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientOrigRTTStats, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(csc.puts).Should(Receive()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) clientRTTStats := &utils.RTTStats{} client, clientEvents, clientErr, server, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientRTTStats, &utils.RTTStats{}, &wire.TransportParameters{ActiveConnectionIDLimit: 2}, &wire.TransportParameters{ActiveConnectionIDLimit: 2, InitialMaxData: initialMaxData - 1}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Expect(clientRTTStats.SmoothedRTT()).To(Equal(clientRTT)) var tp *wire.TransportParameters var clientReceived0RTTKeys bool for _, ev := range clientEvents { switch ev.Kind { case EventRestoredTransportParameters: tp = ev.TransportParameters case EventReceivedReadKeys: clientReceived0RTTKeys = true } } Expect(clientReceived0RTTKeys).To(BeTrue()) Expect(tp).ToNot(BeNil()) Expect(tp.InitialMaxData).To(Equal(initialMaxData)) Expect(server.ConnectionState().DidResume).To(BeTrue()) Expect(client.ConnectionState().DidResume).To(BeTrue()) Expect(server.ConnectionState().Used0RTT).To(BeFalse()) Expect(client.ConnectionState().Used0RTT).To(BeFalse()) }) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/handshake_suite_test.go000066400000000000000000000015371465664453100311040ustar00rootroot00000000000000package handshake import ( "crypto/tls" "encoding/hex" "strings" "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func TestHandshake(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Handshake Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) func splitHexString(s string) (slice []byte) { for _, ss := range strings.Split(s, " ") { if ss[0:2] == "0x" { ss = ss[2:] } d, err := hex.DecodeString(ss) ExpectWithOffset(1, err).ToNot(HaveOccurred()) slice = append(slice, d...) } return } var cipherSuites = []*cipherSuite{ getCipherSuite(tls.TLS_AES_128_GCM_SHA256), getCipherSuite(tls.TLS_AES_256_GCM_SHA384), getCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256), } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/header_protector.go000066400000000000000000000070771465664453100302440ustar00rootroot00000000000000package handshake import ( "crypto/aes" "crypto/cipher" "crypto/tls" "encoding/binary" "fmt" "golang.org/x/crypto/chacha20" "github.com/quic-go/quic-go/internal/protocol" ) type headerProtector interface { EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) } func hkdfHeaderProtectionLabel(v protocol.Version) string { if v == protocol.Version2 { return "quicv2 hp" } return "quic hp" } func newHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, v protocol.Version) headerProtector { hkdfLabel := hkdfHeaderProtectionLabel(v) switch suite.ID { case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: return newAESHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) case tls.TLS_CHACHA20_POLY1305_SHA256: return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader, hkdfLabel) default: panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID)) } } type aesHeaderProtector struct { mask [16]byte // AES always has a 16 byte block size block cipher.Block isLongHeader bool } var _ headerProtector = &aesHeaderProtector{} func newAESHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) block, err := aes.NewCipher(hpKey) if err != nil { panic(fmt.Sprintf("error creating new AES cipher: %s", err)) } return &aesHeaderProtector{ block: block, isLongHeader: isLongHeader, } } func (p *aesHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { p.apply(sample, firstByte, hdrBytes) } func (p *aesHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { p.apply(sample, firstByte, hdrBytes) } func (p *aesHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) { if len(sample) != len(p.mask) { panic("invalid sample size") } p.block.Encrypt(p.mask[:], sample) if p.isLongHeader { *firstByte ^= p.mask[0] & 0xf } else { *firstByte ^= p.mask[0] & 0x1f } for i := range hdrBytes { hdrBytes[i] ^= p.mask[i+1] } } type chachaHeaderProtector struct { mask [5]byte key [32]byte isLongHeader bool } var _ headerProtector = &chachaHeaderProtector{} func newChaChaHeaderProtector(suite *cipherSuite, trafficSecret []byte, isLongHeader bool, hkdfLabel string) headerProtector { hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, hkdfLabel, suite.KeyLen) p := &chachaHeaderProtector{ isLongHeader: isLongHeader, } copy(p.key[:], hpKey) return p } func (p *chachaHeaderProtector) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { p.apply(sample, firstByte, hdrBytes) } func (p *chachaHeaderProtector) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { p.apply(sample, firstByte, hdrBytes) } func (p *chachaHeaderProtector) apply(sample []byte, firstByte *byte, hdrBytes []byte) { if len(sample) != 16 { panic("invalid sample size") } for i := 0; i < 5; i++ { p.mask[i] = 0 } cipher, err := chacha20.NewUnauthenticatedCipher(p.key[:], sample[4:]) if err != nil { panic(err) } cipher.SetCounter(binary.LittleEndian.Uint32(sample[:4])) cipher.XORKeyStream(p.mask[:], p.mask[:]) p.applyMask(firstByte, hdrBytes) } func (p *chachaHeaderProtector) applyMask(firstByte *byte, hdrBytes []byte) { if p.isLongHeader { *firstByte ^= p.mask[0] & 0xf } else { *firstByte ^= p.mask[0] & 0x1f } for i := range hdrBytes { hdrBytes[i] ^= p.mask[i+1] } } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/hkdf.go000066400000000000000000000016001465664453100256110ustar00rootroot00000000000000package handshake import ( "crypto" "encoding/binary" "golang.org/x/crypto/hkdf" ) // hkdfExpandLabel HKDF expands a label as defined in RFC 8446, section 7.1. // Since this implementation avoids using a cryptobyte.Builder, it is about 15% faster than the // hkdfExpandLabel in the standard library. func hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte { b := make([]byte, 3, 3+6+len(label)+1+len(context)) binary.BigEndian.PutUint16(b, uint16(length)) b[2] = uint8(6 + len(label)) b = append(b, []byte("tls13 ")...) b = append(b, []byte(label)...) b = b[:3+6+len(label)+1] b[3+6+len(label)] = uint8(len(context)) b = append(b, context...) out := make([]byte, length) n, err := hkdf.Expand(hash.New, secret, b).Read(out) if err != nil || n != length { panic("quic: HKDF-Expand-Label invocation failed unexpectedly") } return out } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/hkdf_test.go000066400000000000000000000053241465664453100266570ustar00rootroot00000000000000package handshake import ( "crypto" "crypto/cipher" "crypto/tls" "testing" "unsafe" "golang.org/x/exp/rand" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type cipherSuiteTLS13 struct { ID uint16 KeyLen int AEAD func(key, fixedNonce []byte) cipher.AEAD Hash crypto.Hash } //go:linkname cipherSuitesTLS13 crypto/tls.cipherSuitesTLS13 var cipherSuitesTLS13 []unsafe.Pointer func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 { for _, v := range cipherSuitesTLS13 { cs := (*cipherSuiteTLS13)(v) if cs.ID == id { return cs } } return nil } //go:linkname expandLabel crypto/tls.(*cipherSuiteTLS13).expandLabel func expandLabel(cs *cipherSuiteTLS13, secret []byte, label string, context []byte, length int) []byte var _ = Describe("HKDF", func() { DescribeTable("gets the same results as crypto/tls", func(cipherSuite uint16, secret, context []byte, label string, length int) { cs := cipherSuiteTLS13ByID(cipherSuite) expected := expandLabel(cs, secret, label, context, length) expanded := hkdfExpandLabel(cs.Hash, secret, context, label, length) Expect(expanded).To(Equal(expected)) }, Entry("TLS_AES_128_GCM_SHA256", tls.TLS_AES_128_GCM_SHA256, []byte("secret"), []byte("context"), "label", 42), Entry("TLS_AES_256_GCM_SHA384", tls.TLS_AES_256_GCM_SHA384, []byte("secret"), []byte("context"), "label", 100), Entry("TLS_CHACHA20_POLY1305_SHA256", tls.TLS_CHACHA20_POLY1305_SHA256, []byte("secret"), []byte("context"), "label", 77), ) }) func BenchmarkHKDFExpandLabelStandardLibrary(b *testing.B) { b.Run("TLS_AES_128_GCM_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_128_GCM_SHA256, true) }) b.Run("TLS_AES_256_GCM_SHA384", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_256_GCM_SHA384, true) }) b.Run("TLS_CHACHA20_POLY1305_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_CHACHA20_POLY1305_SHA256, true) }) } func BenchmarkHKDFExpandLabelOptimized(b *testing.B) { b.Run("TLS_AES_128_GCM_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_128_GCM_SHA256, false) }) b.Run("TLS_AES_256_GCM_SHA384", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_AES_256_GCM_SHA384, false) }) b.Run("TLS_CHACHA20_POLY1305_SHA256", func(b *testing.B) { benchmarkHKDFExpandLabel(b, tls.TLS_CHACHA20_POLY1305_SHA256, false) }) } func benchmarkHKDFExpandLabel(b *testing.B, cipherSuite uint16, useStdLib bool) { b.ReportAllocs() cs := cipherSuiteTLS13ByID(cipherSuite) secret := make([]byte, 32) rand.Read(secret) b.ResetTimer() for i := 0; i < b.N; i++ { if useStdLib { expandLabel(cs, secret, "label", []byte("context"), 42) } else { hkdfExpandLabel(cs.Hash, secret, []byte("context"), "label", 42) } } } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/initial_aead.go000066400000000000000000000045441465664453100273120ustar00rootroot00000000000000package handshake import ( "crypto" "crypto/tls" "golang.org/x/crypto/hkdf" "github.com/quic-go/quic-go/internal/protocol" ) var ( quicSaltV1 = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} quicSaltV2 = []byte{0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9} ) const ( hkdfLabelKeyV1 = "quic key" hkdfLabelKeyV2 = "quicv2 key" hkdfLabelIVV1 = "quic iv" hkdfLabelIVV2 = "quicv2 iv" ) func getSalt(v protocol.Version) []byte { if v == protocol.Version2 { return quicSaltV2 } return quicSaltV1 } var initialSuite = getCipherSuite(tls.TLS_AES_128_GCM_SHA256) // NewInitialAEAD creates a new AEAD for Initial encryption / decryption. func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v protocol.Version) (LongHeaderSealer, LongHeaderOpener) { clientSecret, serverSecret := computeSecrets(connID, v) var mySecret, otherSecret []byte if pers == protocol.PerspectiveClient { mySecret = clientSecret otherSecret = serverSecret } else { mySecret = serverSecret otherSecret = clientSecret } myKey, myIV := computeInitialKeyAndIV(mySecret, v) otherKey, otherIV := computeInitialKeyAndIV(otherSecret, v) encrypter := initialSuite.AEAD(myKey, myIV) decrypter := initialSuite.AEAD(otherKey, otherIV) return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true, v)), newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true, hkdfHeaderProtectionLabel(v))) } func computeSecrets(connID protocol.ConnectionID, v protocol.Version) (clientSecret, serverSecret []byte) { initialSecret := hkdf.Extract(crypto.SHA256.New, connID.Bytes(), getSalt(v)) clientSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "client in", crypto.SHA256.Size()) serverSecret = hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, "server in", crypto.SHA256.Size()) return } func computeInitialKeyAndIV(secret []byte, v protocol.Version) (key, iv []byte) { keyLabel := hkdfLabelKeyV1 ivLabel := hkdfLabelIVV1 if v == protocol.Version2 { keyLabel = hkdfLabelKeyV2 ivLabel = hkdfLabelIVV2 } key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, keyLabel, 16) iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, ivLabel, 12) return } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/initial_aead_test.go000066400000000000000000000404441465664453100303500ustar00rootroot00000000000000package handshake import ( "bytes" "fmt" "testing" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Initial AEAD using AES-GCM", func() { It("converts the string representation used in the draft into byte slices", func() { Expect(splitHexString("0xdeadbeef")).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) Expect(splitHexString("deadbeef")).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) Expect(splitHexString("dead beef")).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) }) connID := protocol.ParseConnectionID(splitHexString("0x8394c8f03e515708")) DescribeTable("computes the client key and IV", func(v protocol.Version, expectedClientSecret, expectedKey, expectedIV []byte) { clientSecret, _ := computeSecrets(connID, v) Expect(clientSecret).To(Equal(expectedClientSecret)) key, iv := computeInitialKeyAndIV(clientSecret, v) Expect(key).To(Equal(expectedKey)) Expect(iv).To(Equal(expectedIV)) }, Entry("QUIC v1", protocol.Version1, splitHexString("c00cf151ca5be075ed0ebfb5c80323c4 2d6b7db67881289af4008f1f6c357aea"), splitHexString("1f369613dd76d5467730efcbe3b1a22d"), splitHexString("fa044b2f42a3fd3b46fb255c"), ), Entry("QUIC v2", protocol.Version2, splitHexString("14ec9d6eb9fd7af83bf5a668bc17a7e2 83766aade7ecd0891f70f9ff7f4bf47b"), splitHexString("8b1a0bc121284290a29e0971b5cd045d"), splitHexString("91f73e2351d8fa91660e909f"), ), ) DescribeTable("computes the server key and IV", func(v protocol.Version, expectedServerSecret, expectedKey, expectedIV []byte) { _, serverSecret := computeSecrets(connID, v) Expect(serverSecret).To(Equal(expectedServerSecret)) key, iv := computeInitialKeyAndIV(serverSecret, v) Expect(key).To(Equal(expectedKey)) Expect(iv).To(Equal(expectedIV)) }, Entry("QUIC v1", protocol.Version1, splitHexString("3c199828fd139efd216c155ad844cc81 fb82fa8d7446fa7d78be803acdda951b"), splitHexString("cf3a5331653c364c88f0f379b6067e37"), splitHexString("0ac1493ca1905853b0bba03e"), ), Entry("QUIC v2", protocol.Version2, splitHexString("0263db1782731bf4588e7e4d93b74639 07cb8cd8200b5da55a8bd488eafc37c1"), splitHexString("82db637861d55e1d011f19ea71d5d2a7"), splitHexString("dd13c276499c0249d3310652"), ), ) DescribeTable("encrypts the client's Initial", func(v protocol.Version, header, data, expectedSample []byte, expectedHdrFirstByte byte, expectedHdr, expectedPacket []byte) { sealer, _ := NewInitialAEAD(connID, protocol.PerspectiveClient, v) data = append(data, make([]byte, 1162-len(data))...) // add PADDING sealed := sealer.Seal(nil, data, 2, header) sample := sealed[0:16] Expect(sample).To(Equal(expectedSample)) sealer.EncryptHeader(sample, &header[0], header[len(header)-4:]) Expect(header[0]).To(Equal(expectedHdrFirstByte)) Expect(header[len(header)-4:]).To(Equal(expectedHdr)) packet := append(header, sealed...) Expect(packet).To(Equal(expectedPacket)) }, Entry("QUIC v1", protocol.Version1, splitHexString("c300000001088394c8f03e5157080000449e00000002"), splitHexString("060040f1010000ed0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e868 04fe3a47f06a2b69484c000004130113 02010000c000000010000e00000b6578 616d706c652e636f6dff01000100000a 00080006001d00170018001000070005 04616c706e0005000501000000000033 00260024001d00209370b2c9caa47fba baf4559fedba753de171fa71f50f1ce1 5d43e994ec74d748002b000302030400 0d0010000e0403050306030203080408 050806002d00020101001c0002400100 3900320408ffffffffffffffff050480 00ffff07048000ffff08011001048000 75300901100f088394c8f03e51570806 048000ffff"), splitHexString("d1b1c98dd7689fb8ec11d242b123dc9b"), byte(0xc0), splitHexString("7b9aec34"), splitHexString("c000000001088394c8f03e5157080000 449e7b9aec34d1b1c98dd7689fb8ec11 d242b123dc9bd8bab936b47d92ec356c 0bab7df5976d27cd449f63300099f399 1c260ec4c60d17b31f8429157bb35a12 82a643a8d2262cad67500cadb8e7378c 8eb7539ec4d4905fed1bee1fc8aafba1 7c750e2c7ace01e6005f80fcb7df6212 30c83711b39343fa028cea7f7fb5ff89 eac2308249a02252155e2347b63d58c5 457afd84d05dfffdb20392844ae81215 4682e9cf012f9021a6f0be17ddd0c208 4dce25ff9b06cde535d0f920a2db1bf3 62c23e596d11a4f5a6cf3948838a3aec 4e15daf8500a6ef69ec4e3feb6b1d98e 610ac8b7ec3faf6ad760b7bad1db4ba3 485e8a94dc250ae3fdb41ed15fb6a8e5 eba0fc3dd60bc8e30c5c4287e53805db 059ae0648db2f64264ed5e39be2e20d8 2df566da8dd5998ccabdae053060ae6c 7b4378e846d29f37ed7b4ea9ec5d82e7 961b7f25a9323851f681d582363aa5f8 9937f5a67258bf63ad6f1a0b1d96dbd4 faddfcefc5266ba6611722395c906556 be52afe3f565636ad1b17d508b73d874 3eeb524be22b3dcbc2c7468d54119c74 68449a13d8e3b95811a198f3491de3e7 fe942b330407abf82a4ed7c1b311663a c69890f4157015853d91e923037c227a 33cdd5ec281ca3f79c44546b9d90ca00 f064c99e3dd97911d39fe9c5d0b23a22 9a234cb36186c4819e8b9c5927726632 291d6a418211cc2962e20fe47feb3edf 330f2c603a9d48c0fcb5699dbfe58964 25c5bac4aee82e57a85aaf4e2513e4f0 5796b07ba2ee47d80506f8d2c25e50fd 14de71e6c418559302f939b0e1abd576 f279c4b2e0feb85c1f28ff18f58891ff ef132eef2fa09346aee33c28eb130ff2 8f5b766953334113211996d20011a198 e3fc433f9f2541010ae17c1bf202580f 6047472fb36857fe843b19f5984009dd c324044e847a4f4a0ab34f719595de37 252d6235365e9b84392b061085349d73 203a4a13e96f5432ec0fd4a1ee65accd d5e3904df54c1da510b0ff20dcc0c77f cb2c0e0eb605cb0504db87632cf3d8b4 dae6e705769d1de354270123cb11450e fc60ac47683d7b8d0f811365565fd98c 4c8eb936bcab8d069fc33bd801b03ade a2e1fbc5aa463d08ca19896d2bf59a07 1b851e6c239052172f296bfb5e724047 90a2181014f3b94a4e97d117b4381303 68cc39dbb2d198065ae3986547926cd2 162f40a29f0c3c8745c0f50fba3852e5 66d44575c29d39a03f0cda721984b6f4 40591f355e12d439ff150aab7613499d bd49adabc8676eef023b15b65bfc5ca0 6948109f23f350db82123535eb8a7433 bdabcb909271a6ecbcb58b936a88cd4e 8f2e6ff5800175f113253d8fa9ca8885 c2f552e657dc603f252e1a8e308f76f0 be79e2fb8f5d5fbbe2e30ecadd220723 c8c0aea8078cdfcb3868263ff8f09400 54da48781893a7e49ad5aff4af300cd8 04a6b6279ab3ff3afb64491c85194aab 760d58a606654f9f4400e8b38591356f bf6425aca26dc85244259ff2b19c41b9 f96f3ca9ec1dde434da7d2d392b905dd f3d1f9af93d1af5950bd493f5aa731b4 056df31bd267b6b90a079831aaf579be 0a39013137aac6d404f518cfd4684064 7e78bfe706ca4cf5e9c5453e9f7cfd2b 8b4c8d169a44e55c88d4a9a7f9474241 e221af44860018ab0856972e194cd934"), ), Entry("QUIC v2", protocol.Version2, splitHexString("d36b3343cf088394c8f03e5157080000449e00000002"), splitHexString("060040f1010000ed0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e868 04fe3a47f06a2b69484c000004130113 02010000c000000010000e00000b6578 616d706c652e636f6dff01000100000a 00080006001d00170018001000070005 04616c706e0005000501000000000033 00260024001d00209370b2c9caa47fba baf4559fedba753de171fa71f50f1ce1 5d43e994ec74d748002b000302030400 0d0010000e0403050306030203080408 050806002d00020101001c0002400100 3900320408ffffffffffffffff050480 00ffff07048000ffff08011001048000 75300901100f088394c8f03e51570806 048000ffff"), splitHexString("ffe67b6abcdb4298b485dd04de806071"), byte(0xd7), splitHexString("a0c95e82"), splitHexString("d76b3343cf088394c8f03e5157080000 449ea0c95e82ffe67b6abcdb4298b485 dd04de806071bf03dceebfa162e75d6c 96058bdbfb127cdfcbf903388e99ad04 9f9a3dd4425ae4d0992cfff18ecf0fdb 5a842d09747052f17ac2053d21f57c5d 250f2c4f0e0202b70785b7946e992e58 a59ac52dea6774d4f03b55545243cf1a 12834e3f249a78d395e0d18f4d766004 f1a2674802a747eaa901c3f10cda5500 cb9122faa9f1df66c392079a1b40f0de 1c6054196a11cbea40afb6ef5253cd68 18f6625efce3b6def6ba7e4b37a40f77 32e093daa7d52190935b8da58976ff33 12ae50b187c1433c0f028edcc4c2838b 6a9bfc226ca4b4530e7a4ccee1bfa2a3 d396ae5a3fb512384b2fdd851f784a65 e03f2c4fbe11a53c7777c023462239dd 6f7521a3f6c7d5dd3ec9b3f233773d4b 46d23cc375eb198c63301c21801f6520 bcfb7966fc49b393f0061d974a2706df 8c4a9449f11d7f3d2dcbb90c6b877045 636e7c0c0fe4eb0f697545460c806910 d2c355f1d253bc9d2452aaa549e27a1f ac7cf4ed77f322e8fa894b6a83810a34 b361901751a6f5eb65a0326e07de7c12 16ccce2d0193f958bb3850a833f7ae43 2b65bc5a53975c155aa4bcb4f7b2c4e5 4df16efaf6ddea94e2c50b4cd1dfe060 17e0e9d02900cffe1935e0491d77ffb4 fdf85290fdd893d577b1131a610ef6a5 c32b2ee0293617a37cbb08b847741c3b 8017c25ca9052ca1079d8b78aebd4787 6d330a30f6a8c6d61dd1ab5589329de7 14d19d61370f8149748c72f132f0fc99 f34d766c6938597040d8f9e2bb522ff9 9c63a344d6a2ae8aa8e51b7b90a4a806 105fcbca31506c446151adfeceb51b91 abfe43960977c87471cf9ad4074d30e1 0d6a7f03c63bd5d4317f68ff325ba3bd 80bf4dc8b52a0ba031758022eb025cdd 770b44d6d6cf0670f4e990b22347a7db 848265e3e5eb72dfe8299ad7481a4083 22cac55786e52f633b2fb6b614eaed18 d703dd84045a274ae8bfa73379661388 d6991fe39b0d93debb41700b41f90a15 c4d526250235ddcd6776fc77bc97e7a4 17ebcb31600d01e57f32162a8560cacc 7e27a096d37a1a86952ec71bd89a3e9a 30a2a26162984d7740f81193e8238e61 f6b5b984d4d3dfa033c1bb7e4f0037fe bf406d91c0dccf32acf423cfa1e70710 10d3f270121b493ce85054ef58bada42 310138fe081adb04e2bd901f2f13458b 3d6758158197107c14ebb193230cd115 7380aa79cae1374a7c1e5bbcb80ee23e 06ebfde206bfb0fcbc0edc4ebec30966 1bdd908d532eb0c6adc38b7ca7331dce 8dfce39ab71e7c32d318d136b6100671 a1ae6a6600e3899f31f0eed19e3417d1 34b90c9058f8632c798d4490da498730 7cba922d61c39805d072b589bd52fdf1 e86215c2d54e6670e07383a27bbffb5a ddf47d66aa85a0c6f9f32e59d85a44dd 5d3b22dc2be80919b490437ae4f36a0a e55edf1d0b5cb4e9a3ecabee93dfc6e3 8d209d0fa6536d27a5d6fbb17641cde2 7525d61093f1b28072d111b2b4ae5f89 d5974ee12e5cf7d5da4d6a31123041f3 3e61407e76cffcdcfd7e19ba58cf4b53 6f4c4938ae79324dc402894b44faf8af bab35282ab659d13c93f70412e85cb19 9a37ddec600545473cfb5a05e08d0b20 9973b2172b4d21fb69745a262ccde96b a18b2faa745b6fe189cf772a9f84cbfc"), ), ) DescribeTable("encrypts the server's Initial", func(v protocol.Version, header, data, expectedSample, expectedHdr, expectedPacket []byte) { sealer, _ := NewInitialAEAD(connID, protocol.PerspectiveServer, v) sealed := sealer.Seal(nil, data, 1, header) sample := sealed[2 : 2+16] Expect(sample).To(Equal(expectedSample)) sealer.EncryptHeader(sample, &header[0], header[len(header)-2:]) Expect(header).To(Equal(expectedHdr)) packet := append(header, sealed...) Expect(packet).To(Equal(expectedPacket)) }, Entry("QUIC v1", protocol.Version1, splitHexString("c1000000010008f067a5502a4262b50040750001"), splitHexString("02000000000600405a020000560303ee fce7f7b37ba1d1632e96677825ddf739 88cfc79825df566dc5430b9a045a1200 130100002e00330024001d00209d3c94 0d89690b84d08a60993c144eca684d10 81287c834d5311bcf32bb9da1a002b00 020304"), splitHexString("2cd0991cd25b0aac406a5816b6394100"), splitHexString("cf000000010008f067a5502a4262b5004075c0d9"), splitHexString("cf000000010008f067a5502a4262b500 4075c0d95a482cd0991cd25b0aac406a 5816b6394100f37a1c69797554780bb3 8cc5a99f5ede4cf73c3ec2493a1839b3 dbcba3f6ea46c5b7684df3548e7ddeb9 c3bf9c73cc3f3bded74b562bfb19fb84 022f8ef4cdd93795d77d06edbb7aaf2f 58891850abbdca3d20398c276456cbc4 2158407dd074ee"), ), Entry("QUIC v2", protocol.Version2, splitHexString("d16b3343cf0008f067a5502a4262b50040750001"), splitHexString("02000000000600405a020000560303ee fce7f7b37ba1d1632e96677825ddf739 88cfc79825df566dc5430b9a045a1200 130100002e00330024001d00209d3c94 0d89690b84d08a60993c144eca684d10 81287c834d5311bcf32bb9da1a002b00 020304"), splitHexString("6f05d8a4398c47089698baeea26b91eb"), splitHexString("dc6b3343cf0008f067a5502a4262b5004075d92f"), splitHexString("dc6b3343cf0008f067a5502a4262b500 4075d92faaf16f05d8a4398c47089698 baeea26b91eb761d9b89237bbf872630 17915358230035f7fd3945d88965cf17 f9af6e16886c61bfc703106fbaf3cb4c fa52382dd16a393e42757507698075b2 c984c707f0a0812d8cd5a6881eaf21ce da98f4bd23f6fe1a3e2c43edd9ce7ca8 4bed8521e2e140"), ), ) for _, ver := range []protocol.Version{protocol.Version1, protocol.Version2} { v := ver Context(fmt.Sprintf("using version %s", v), func() { It("seals and opens", func() { connectionID := protocol.ParseConnectionID([]byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}) clientSealer, clientOpener := NewInitialAEAD(connectionID, protocol.PerspectiveClient, v) serverSealer, serverOpener := NewInitialAEAD(connectionID, protocol.PerspectiveServer, v) clientMessage := clientSealer.Seal(nil, []byte("foobar"), 42, []byte("aad")) m, err := serverOpener.Open(nil, clientMessage, 42, []byte("aad")) Expect(err).ToNot(HaveOccurred()) Expect(m).To(Equal([]byte("foobar"))) serverMessage := serverSealer.Seal(nil, []byte("raboof"), 99, []byte("daa")) m, err = clientOpener.Open(nil, serverMessage, 99, []byte("daa")) Expect(err).ToNot(HaveOccurred()) Expect(m).To(Equal([]byte("raboof"))) }) It("doesn't work if initialized with different connection IDs", func() { c1 := protocol.ParseConnectionID([]byte{0, 0, 0, 0, 0, 0, 0, 1}) c2 := protocol.ParseConnectionID([]byte{0, 0, 0, 0, 0, 0, 0, 2}) clientSealer, _ := NewInitialAEAD(c1, protocol.PerspectiveClient, v) _, serverOpener := NewInitialAEAD(c2, protocol.PerspectiveServer, v) clientMessage := clientSealer.Seal(nil, []byte("foobar"), 42, []byte("aad")) _, err := serverOpener.Open(nil, clientMessage, 42, []byte("aad")) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("encrypts und decrypts the header", func() { connID := protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}) clientSealer, clientOpener := NewInitialAEAD(connID, protocol.PerspectiveClient, v) serverSealer, serverOpener := NewInitialAEAD(connID, protocol.PerspectiveServer, v) // the first byte and the last 4 bytes should be encrypted header := []byte{0x5e, 0, 1, 2, 3, 4, 0xde, 0xad, 0xbe, 0xef} sample := make([]byte, 16) rand.Read(sample) clientSealer.EncryptHeader(sample, &header[0], header[6:10]) // only the last 4 bits of the first byte are encrypted. Check that the first 4 bits are unmodified Expect(header[0] & 0xf0).To(Equal(byte(0x5e & 0xf0))) Expect(header[1:6]).To(Equal([]byte{0, 1, 2, 3, 4})) Expect(header[6:10]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) serverOpener.DecryptHeader(sample, &header[0], header[6:10]) Expect(header[0]).To(Equal(byte(0x5e))) Expect(header[1:6]).To(Equal([]byte{0, 1, 2, 3, 4})) Expect(header[6:10]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) serverSealer.EncryptHeader(sample, &header[0], header[6:10]) // only the last 4 bits of the first byte are encrypted. Check that the first 4 bits are unmodified Expect(header[0] & 0xf0).To(Equal(byte(0x5e & 0xf0))) Expect(header[1:6]).To(Equal([]byte{0, 1, 2, 3, 4})) Expect(header[6:10]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) clientOpener.DecryptHeader(sample, &header[0], header[6:10]) Expect(header[0]).To(Equal(byte(0x5e))) Expect(header[1:6]).To(Equal([]byte{0, 1, 2, 3, 4})) Expect(header[6:10]).To(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) }) }) } }) func BenchmarkInitialAEADCreate(b *testing.B) { b.ReportAllocs() connID := protocol.ParseConnectionID([]byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef}) for i := 0; i < b.N; i++ { NewInitialAEAD(connID, protocol.PerspectiveServer, protocol.Version1) } } func BenchmarkInitialAEAD(b *testing.B) { connectionID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd}) clientSealer, _ := NewInitialAEAD(connectionID, protocol.PerspectiveClient, protocol.Version1) _, serverOpener := NewInitialAEAD(connectionID, protocol.PerspectiveServer, protocol.Version1) r := rand.New(rand.NewSource(1)) packetData := make([]byte, 1200) r.Read(packetData) hdr := make([]byte, 50) r.Read(hdr) msg := clientSealer.Seal(nil, packetData, 42, hdr) m, err := serverOpener.Open(nil, msg, 42, hdr) if err != nil { b.Fatalf("opening failed: %s", err) } if !bytes.Equal(m, packetData) { b.Fatal("decrypted data doesn't match") } b.Run("opening 100 bytes", func(b *testing.B) { benchmarkOpen(b, serverOpener, clientSealer.Seal(nil, packetData[:100], 42, hdr), hdr) }) b.Run("opening 1200 bytes", func(b *testing.B) { benchmarkOpen(b, serverOpener, msg, hdr) }) b.Run("sealing 100 bytes", func(b *testing.B) { benchmarkSeal(b, clientSealer, packetData[:100], hdr) }) b.Run("sealing 1200 bytes", func(b *testing.B) { benchmarkSeal(b, clientSealer, packetData, hdr) }) } func benchmarkOpen(b *testing.B, aead LongHeaderOpener, msg, hdr []byte) { b.ReportAllocs() dst := make([]byte, 0, 1500) for i := 0; i < b.N; i++ { dst = dst[:0] if _, err := aead.Open(dst, msg, 42, hdr); err != nil { b.Fatalf("opening failed: %s", err) } } } func benchmarkSeal(b *testing.B, aead LongHeaderSealer, msg, hdr []byte) { b.ReportAllocs() dst := make([]byte, 0, 1500) for i := 0; i < b.N; i++ { dst = dst[:0] aead.Seal(dst, msg, protocol.PacketNumber(i), hdr) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/interface.go000066400000000000000000000077651465664453100266570ustar00rootroot00000000000000package handshake import ( "context" "crypto/tls" "errors" "io" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" ) var ( // ErrKeysNotYetAvailable is returned when an opener or a sealer is requested for an encryption level, // but the corresponding opener has not yet been initialized // This can happen when packets arrive out of order. ErrKeysNotYetAvailable = errors.New("CryptoSetup: keys at this encryption level not yet available") // ErrKeysDropped is returned when an opener or a sealer is requested for an encryption level, // but the corresponding keys have already been dropped. ErrKeysDropped = errors.New("CryptoSetup: keys were already dropped") // ErrDecryptionFailed is returned when the AEAD fails to open the packet. ErrDecryptionFailed = errors.New("decryption failed") ) type headerDecryptor interface { DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) } // LongHeaderOpener opens a long header packet type LongHeaderOpener interface { headerDecryptor DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber Open(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) ([]byte, error) } // ShortHeaderOpener opens a short header packet type ShortHeaderOpener interface { headerDecryptor DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, associatedData []byte) ([]byte, error) } // LongHeaderSealer seals a long header packet type LongHeaderSealer interface { Seal(dst, src []byte, packetNumber protocol.PacketNumber, associatedData []byte) []byte EncryptHeader(sample []byte, firstByte *byte, pnBytes []byte) Overhead() int } // ShortHeaderSealer seals a short header packet type ShortHeaderSealer interface { LongHeaderSealer KeyPhase() protocol.KeyPhaseBit } type ConnectionState struct { tls.ConnectionState Used0RTT bool } // EventKind is the kind of handshake event. type EventKind uint8 const ( // EventNoEvent signals that there are no new handshake events EventNoEvent EventKind = iota + 1 // EventWriteInitialData contains new CRYPTO data to send at the Initial encryption level EventWriteInitialData // EventWriteHandshakeData contains new CRYPTO data to send at the Handshake encryption level EventWriteHandshakeData // EventReceivedReadKeys signals that new decryption keys are available. // It doesn't say which encryption level those keys are for. EventReceivedReadKeys // EventDiscard0RTTKeys signals that the Handshake keys were discarded. EventDiscard0RTTKeys // EventReceivedTransportParameters contains the transport parameters sent by the peer. EventReceivedTransportParameters // EventRestoredTransportParameters contains the transport parameters restored from the session ticket. // It is only used for the client. EventRestoredTransportParameters // EventHandshakeComplete signals that the TLS handshake was completed. EventHandshakeComplete ) // Event is a handshake event. type Event struct { Kind EventKind Data []byte TransportParameters *wire.TransportParameters } // CryptoSetup handles the handshake and protecting / unprotecting packets type CryptoSetup interface { StartHandshake(context.Context) error io.Closer ChangeConnectionID(protocol.ConnectionID) GetSessionTicket() ([]byte, error) HandleMessage([]byte, protocol.EncryptionLevel) error NextEvent() Event SetLargest1RTTAcked(protocol.PacketNumber) error DiscardInitialKeys() SetHandshakeConfirmed() ConnectionState() ConnectionState GetInitialOpener() (LongHeaderOpener, error) GetHandshakeOpener() (LongHeaderOpener, error) Get0RTTOpener() (LongHeaderOpener, error) Get1RTTOpener() (ShortHeaderOpener, error) GetInitialSealer() (LongHeaderSealer, error) GetHandshakeSealer() (LongHeaderSealer, error) Get0RTTSealer() (LongHeaderSealer, error) Get1RTTSealer() (ShortHeaderSealer, error) } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/retry.go000066400000000000000000000032571465664453100260540ustar00rootroot00000000000000package handshake import ( "bytes" "crypto/aes" "crypto/cipher" "fmt" "sync" "github.com/quic-go/quic-go/internal/protocol" ) var ( retryAEADv1 cipher.AEAD // used for QUIC v1 (RFC 9000) retryAEADv2 cipher.AEAD // used for QUIC v2 (RFC 9369) ) func init() { retryAEADv1 = initAEAD([16]byte{0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e}) retryAEADv2 = initAEAD([16]byte{0x8f, 0xb4, 0xb0, 0x1b, 0x56, 0xac, 0x48, 0xe2, 0x60, 0xfb, 0xcb, 0xce, 0xad, 0x7c, 0xcc, 0x92}) } func initAEAD(key [16]byte) cipher.AEAD { aes, err := aes.NewCipher(key[:]) if err != nil { panic(err) } aead, err := cipher.NewGCM(aes) if err != nil { panic(err) } return aead } var ( retryBuf bytes.Buffer retryMutex sync.Mutex retryNonceV1 = [12]byte{0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb} retryNonceV2 = [12]byte{0xd8, 0x69, 0x69, 0xbc, 0x2d, 0x7c, 0x6d, 0x99, 0x90, 0xef, 0xb0, 0x4a} ) // GetRetryIntegrityTag calculates the integrity tag on a Retry packet func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, version protocol.Version) *[16]byte { retryMutex.Lock() defer retryMutex.Unlock() retryBuf.WriteByte(uint8(origDestConnID.Len())) retryBuf.Write(origDestConnID.Bytes()) retryBuf.Write(retry) defer retryBuf.Reset() var tag [16]byte var sealed []byte if version == protocol.Version2 { sealed = retryAEADv2.Seal(tag[:0], retryNonceV2[:], nil, retryBuf.Bytes()) } else { sealed = retryAEADv1.Seal(tag[:0], retryNonceV1[:], nil, retryBuf.Bytes()) } if len(sealed) != 16 { panic(fmt.Sprintf("unexpected Retry integrity tag length: %d", len(sealed))) } return &tag } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/retry_test.go000066400000000000000000000031111465664453100271000ustar00rootroot00000000000000package handshake import ( "encoding/binary" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Retry Integrity Check", func() { It("calculates retry integrity tags", func() { connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) fooTag := GetRetryIntegrityTag([]byte("foo"), connID, protocol.Version1) barTag := GetRetryIntegrityTag([]byte("bar"), connID, protocol.Version1) Expect(fooTag).ToNot(BeNil()) Expect(barTag).ToNot(BeNil()) Expect(*fooTag).ToNot(Equal(*barTag)) }) It("includes the original connection ID in the tag calculation", func() { connID1 := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) connID2 := protocol.ParseConnectionID([]byte{4, 3, 2, 1}) t1 := GetRetryIntegrityTag([]byte("foobar"), connID1, protocol.Version1) t2 := GetRetryIntegrityTag([]byte("foobar"), connID2, protocol.Version1) Expect(*t1).ToNot(Equal(*t2)) }) DescribeTable("using the test vectors", func(version protocol.Version, data []byte) { v := binary.BigEndian.Uint32(data[1:5]) Expect(protocol.Version(v)).To(Equal(version)) connID := protocol.ParseConnectionID(splitHexString("0x8394c8f03e515708")) Expect(GetRetryIntegrityTag(data[:len(data)-16], connID, version)[:]).To(Equal(data[len(data)-16:])) }, Entry("v1", protocol.Version1, splitHexString("ff000000010008f067a5502a4262b574 6f6b656e04a265ba2eff4d829058fb3f 0f2496ba"), ), Entry("v2", protocol.Version2, splitHexString("cf6b3343cf0008f067a5502a4262b574 6f6b656ec8646ce8bfe33952d9555436 65dcc7b6"), ), ) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/session_ticket.go000066400000000000000000000025231465664453100277300ustar00rootroot00000000000000package handshake import ( "errors" "fmt" "time" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/quicvarint" ) const sessionTicketRevision = 4 type sessionTicket struct { Parameters *wire.TransportParameters RTT time.Duration // to be encoded in mus } func (t *sessionTicket) Marshal() []byte { b := make([]byte, 0, 256) b = quicvarint.Append(b, sessionTicketRevision) b = quicvarint.Append(b, uint64(t.RTT.Microseconds())) if t.Parameters == nil { return b } return t.Parameters.MarshalForSessionTicket(b) } func (t *sessionTicket) Unmarshal(b []byte, using0RTT bool) error { rev, l, err := quicvarint.Parse(b) if err != nil { return errors.New("failed to read session ticket revision") } b = b[l:] if rev != sessionTicketRevision { return fmt.Errorf("unknown session ticket revision: %d", rev) } rtt, l, err := quicvarint.Parse(b) if err != nil { return errors.New("failed to read RTT") } b = b[l:] if using0RTT { var tp wire.TransportParameters if err := tp.UnmarshalFromSessionTicket(b); err != nil { return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) } t.Parameters = &tp } else if len(b) > 0 { return fmt.Errorf("the session ticket has more bytes than expected") } t.RTT = time.Duration(rtt) * time.Microsecond return nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/session_ticket_test.go000066400000000000000000000067201465664453100307720ustar00rootroot00000000000000package handshake import ( "time" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Session Ticket", func() { It("marshals and unmarshals a 0-RTT session ticket", func() { ticket := &sessionTicket{ Parameters: &wire.TransportParameters{ InitialMaxStreamDataBidiLocal: 1, InitialMaxStreamDataBidiRemote: 2, ActiveConnectionIDLimit: 10, MaxDatagramFrameSize: 20, }, RTT: 1337 * time.Microsecond, } var t sessionTicket Expect(t.Unmarshal(ticket.Marshal(), true)).To(Succeed()) Expect(t.Parameters.InitialMaxStreamDataBidiLocal).To(BeEquivalentTo(1)) Expect(t.Parameters.InitialMaxStreamDataBidiRemote).To(BeEquivalentTo(2)) Expect(t.Parameters.ActiveConnectionIDLimit).To(BeEquivalentTo(10)) Expect(t.Parameters.MaxDatagramFrameSize).To(BeEquivalentTo(20)) Expect(t.RTT).To(Equal(1337 * time.Microsecond)) // fails to unmarshal the ticket as a non-0-RTT ticket Expect(t.Unmarshal(ticket.Marshal(), false)).To(MatchError("the session ticket has more bytes than expected")) }) It("marshals and unmarshals a non-0-RTT session ticket", func() { ticket := &sessionTicket{ RTT: 1337 * time.Microsecond, } var t sessionTicket Expect(t.Unmarshal(ticket.Marshal(), false)).To(Succeed()) Expect(t.Parameters).To(BeNil()) Expect(t.RTT).To(Equal(1337 * time.Microsecond)) // fails to unmarshal the ticket as a 0-RTT ticket Expect(t.Unmarshal(ticket.Marshal(), true)).To(MatchError(ContainSubstring("unmarshaling transport parameters from session ticket failed"))) }) It("refuses to unmarshal if the ticket is too short for the revision", func() { Expect((&sessionTicket{}).Unmarshal([]byte{}, true)).To(MatchError("failed to read session ticket revision")) Expect((&sessionTicket{}).Unmarshal([]byte{}, false)).To(MatchError("failed to read session ticket revision")) }) It("refuses to unmarshal if the revision doesn't match", func() { b := quicvarint.Append(nil, 1337) Expect((&sessionTicket{}).Unmarshal(b, true)).To(MatchError("unknown session ticket revision: 1337")) Expect((&sessionTicket{}).Unmarshal(b, false)).To(MatchError("unknown session ticket revision: 1337")) }) It("refuses to unmarshal if the RTT cannot be read", func() { b := quicvarint.Append(nil, sessionTicketRevision) Expect((&sessionTicket{}).Unmarshal(b, true)).To(MatchError("failed to read RTT")) Expect((&sessionTicket{}).Unmarshal(b, false)).To(MatchError("failed to read RTT")) }) It("refuses to unmarshal a 0-RTT session ticket if unmarshaling the transport parameters fails", func() { b := quicvarint.Append(nil, sessionTicketRevision) b = append(b, []byte("foobar")...) err := (&sessionTicket{}).Unmarshal(b, true) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("unmarshaling transport parameters from session ticket failed")) }) It("refuses to unmarshal if the non-0-RTT session ticket has more bytes than expected", func() { ticket := &sessionTicket{ Parameters: &wire.TransportParameters{ InitialMaxStreamDataBidiLocal: 1, InitialMaxStreamDataBidiRemote: 2, ActiveConnectionIDLimit: 10, MaxDatagramFrameSize: 20, }, RTT: 1234 * time.Microsecond, } err := (&sessionTicket{}).Unmarshal(ticket.Marshal(), false) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("the session ticket has more bytes than expected")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/token_generator.go000066400000000000000000000064651465664453100301010ustar00rootroot00000000000000package handshake import ( "bytes" "encoding/asn1" "fmt" "net" "time" "github.com/quic-go/quic-go/internal/protocol" ) const ( tokenPrefixIP byte = iota tokenPrefixString ) // A Token is derived from the client address and can be used to verify the ownership of this address. type Token struct { IsRetryToken bool SentTime time.Time encodedRemoteAddr []byte // only set for retry tokens OriginalDestConnectionID protocol.ConnectionID RetrySrcConnectionID protocol.ConnectionID } // ValidateRemoteAddr validates the address, but does not check expiration func (t *Token) ValidateRemoteAddr(addr net.Addr) bool { return bytes.Equal(encodeRemoteAddr(addr), t.encodedRemoteAddr) } // token is the struct that is used for ASN1 serialization and deserialization type token struct { IsRetryToken bool RemoteAddr []byte Timestamp int64 OriginalDestConnectionID []byte RetrySrcConnectionID []byte } // A TokenGenerator generates tokens type TokenGenerator struct { tokenProtector tokenProtector } // NewTokenGenerator initializes a new TokenGenerator func NewTokenGenerator(key TokenProtectorKey) *TokenGenerator { return &TokenGenerator{tokenProtector: *newTokenProtector(key)} } // NewRetryToken generates a new token for a Retry for a given source address func (g *TokenGenerator) NewRetryToken( raddr net.Addr, origDestConnID protocol.ConnectionID, retrySrcConnID protocol.ConnectionID, ) ([]byte, error) { data, err := asn1.Marshal(token{ IsRetryToken: true, RemoteAddr: encodeRemoteAddr(raddr), OriginalDestConnectionID: origDestConnID.Bytes(), RetrySrcConnectionID: retrySrcConnID.Bytes(), Timestamp: time.Now().UnixNano(), }) if err != nil { return nil, err } return g.tokenProtector.NewToken(data) } // NewToken generates a new token to be sent in a NEW_TOKEN frame func (g *TokenGenerator) NewToken(raddr net.Addr) ([]byte, error) { data, err := asn1.Marshal(token{ RemoteAddr: encodeRemoteAddr(raddr), Timestamp: time.Now().UnixNano(), }) if err != nil { return nil, err } return g.tokenProtector.NewToken(data) } // DecodeToken decodes a token func (g *TokenGenerator) DecodeToken(encrypted []byte) (*Token, error) { // if the client didn't send any token, DecodeToken will be called with a nil-slice if len(encrypted) == 0 { return nil, nil } data, err := g.tokenProtector.DecodeToken(encrypted) if err != nil { return nil, err } t := &token{} rest, err := asn1.Unmarshal(data, t) if err != nil { return nil, err } if len(rest) != 0 { return nil, fmt.Errorf("rest when unpacking token: %d", len(rest)) } token := &Token{ IsRetryToken: t.IsRetryToken, SentTime: time.Unix(0, t.Timestamp), encodedRemoteAddr: t.RemoteAddr, } if t.IsRetryToken { token.OriginalDestConnectionID = protocol.ParseConnectionID(t.OriginalDestConnectionID) token.RetrySrcConnectionID = protocol.ParseConnectionID(t.RetrySrcConnectionID) } return token, nil } // encodeRemoteAddr encodes a remote address such that it can be saved in the token func encodeRemoteAddr(remoteAddr net.Addr) []byte { if udpAddr, ok := remoteAddr.(*net.UDPAddr); ok { return append([]byte{tokenPrefixIP}, udpAddr.IP...) } return append([]byte{tokenPrefixString}, []byte(remoteAddr.String())...) } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/token_generator_test.go000066400000000000000000000105711465664453100311310ustar00rootroot00000000000000package handshake import ( "crypto/rand" "encoding/asn1" "net" "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Token Generator", func() { var tokenGen *TokenGenerator BeforeEach(func() { var key TokenProtectorKey rand.Read(key[:]) tokenGen = NewTokenGenerator(key) }) It("generates a token", func() { ip := net.IPv4(127, 0, 0, 1) token, err := tokenGen.NewRetryToken(&net.UDPAddr{IP: ip, Port: 1337}, protocol.ConnectionID{}, protocol.ConnectionID{}) Expect(err).ToNot(HaveOccurred()) Expect(token).ToNot(BeEmpty()) }) It("works with nil tokens", func() { token, err := tokenGen.DecodeToken(nil) Expect(err).ToNot(HaveOccurred()) Expect(token).To(BeNil()) }) It("accepts a valid token", func() { addr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} tokenEnc, err := tokenGen.NewRetryToken(addr, protocol.ConnectionID{}, protocol.ConnectionID{}) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.ValidateRemoteAddr(addr)).To(BeTrue()) Expect(token.ValidateRemoteAddr(&net.UDPAddr{IP: net.IPv4(192, 168, 0, 2), Port: 1337})).To(BeFalse()) Expect(token.SentTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond)) Expect(token.OriginalDestConnectionID.Len()).To(BeZero()) Expect(token.RetrySrcConnectionID.Len()).To(BeZero()) }) It("saves the connection ID", func() { connID1 := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) connID2 := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) tokenEnc, err := tokenGen.NewRetryToken(&net.UDPAddr{}, connID1, connID2) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.OriginalDestConnectionID).To(Equal(connID1)) Expect(token.RetrySrcConnectionID).To(Equal(connID2)) }) It("rejects invalid tokens", func() { _, err := tokenGen.DecodeToken([]byte("invalid token")) Expect(err).To(HaveOccurred()) }) It("rejects tokens that cannot be decoded", func() { token, err := tokenGen.tokenProtector.NewToken([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) _, err = tokenGen.DecodeToken(token) Expect(err).To(HaveOccurred()) }) It("rejects tokens that can be decoded, but have additional payload", func() { t, err := asn1.Marshal(token{RemoteAddr: []byte("foobar")}) Expect(err).ToNot(HaveOccurred()) t = append(t, []byte("rest")...) enc, err := tokenGen.tokenProtector.NewToken(t) Expect(err).ToNot(HaveOccurred()) _, err = tokenGen.DecodeToken(enc) Expect(err).To(MatchError("rest when unpacking token: 4")) }) // we don't generate tokens that have no data, but we should be able to handle them if we receive one for whatever reason It("doesn't panic if a tokens has no data", func() { t, err := asn1.Marshal(token{RemoteAddr: []byte("")}) Expect(err).ToNot(HaveOccurred()) enc, err := tokenGen.tokenProtector.NewToken(t) Expect(err).ToNot(HaveOccurred()) _, err = tokenGen.DecodeToken(enc) Expect(err).ToNot(HaveOccurred()) }) It("works with an IPv6 addresses ", func() { addresses := []string{ "2001:db8::68", "2001:0000:4136:e378:8000:63bf:3fff:fdd2", "2001::1", "ff01:0:0:0:0:0:0:2", } for _, addr := range addresses { ip := net.ParseIP(addr) Expect(ip).ToNot(BeNil()) raddr := &net.UDPAddr{IP: ip, Port: 1337} tokenEnc, err := tokenGen.NewRetryToken(raddr, protocol.ConnectionID{}, protocol.ConnectionID{}) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.ValidateRemoteAddr(raddr)).To(BeTrue()) Expect(token.SentTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond)) } }) It("uses the string representation an address that is not a UDP address", func() { raddr := &net.TCPAddr{IP: net.IPv4(192, 168, 13, 37), Port: 1337} tokenEnc, err := tokenGen.NewRetryToken(raddr, protocol.ConnectionID{}, protocol.ConnectionID{}) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.ValidateRemoteAddr(raddr)).To(BeTrue()) Expect(token.ValidateRemoteAddr(&net.TCPAddr{IP: net.IPv4(192, 168, 13, 37), Port: 1338})).To(BeFalse()) Expect(token.SentTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/token_protector.go000066400000000000000000000035341465664453100301260ustar00rootroot00000000000000package handshake import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "fmt" "io" "golang.org/x/crypto/hkdf" ) // TokenProtectorKey is the key used to encrypt both Retry and session resumption tokens. type TokenProtectorKey [32]byte const tokenNonceSize = 32 // tokenProtector is used to create and verify a token type tokenProtector struct { key TokenProtectorKey } // newTokenProtector creates a source for source address tokens func newTokenProtector(key TokenProtectorKey) *tokenProtector { return &tokenProtector{key: key} } // NewToken encodes data into a new token. func (s *tokenProtector) NewToken(data []byte) ([]byte, error) { var nonce [tokenNonceSize]byte if _, err := rand.Read(nonce[:]); err != nil { return nil, err } aead, aeadNonce, err := s.createAEAD(nonce[:]) if err != nil { return nil, err } return append(nonce[:], aead.Seal(nil, aeadNonce, data, nil)...), nil } // DecodeToken decodes a token. func (s *tokenProtector) DecodeToken(p []byte) ([]byte, error) { if len(p) < tokenNonceSize { return nil, fmt.Errorf("token too short: %d", len(p)) } nonce := p[:tokenNonceSize] aead, aeadNonce, err := s.createAEAD(nonce) if err != nil { return nil, err } return aead.Open(nil, aeadNonce, p[tokenNonceSize:], nil) } func (s *tokenProtector) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) { h := hkdf.New(sha256.New, s.key[:], nonce, []byte("quic-go token source")) key := make([]byte, 32) // use a 32 byte key, in order to select AES-256 if _, err := io.ReadFull(h, key); err != nil { return nil, nil, err } aeadNonce := make([]byte, 12) if _, err := io.ReadFull(h, aeadNonce); err != nil { return nil, nil, err } c, err := aes.NewCipher(key) if err != nil { return nil, nil, err } aead, err := cipher.NewGCM(c) if err != nil { return nil, nil, err } return aead, aeadNonce, nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/token_protector_test.go000066400000000000000000000034221465664453100311610ustar00rootroot00000000000000package handshake import ( "crypto/rand" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Token Protector", func() { var tp *tokenProtector BeforeEach(func() { var key TokenProtectorKey rand.Read(key[:]) var err error tp = newTokenProtector(key) Expect(err).ToNot(HaveOccurred()) }) It("encodes and decodes tokens", func() { token, err := tp.NewToken([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(token).ToNot(ContainSubstring("foobar")) decoded, err := tp.DecodeToken(token) Expect(err).ToNot(HaveOccurred()) Expect(decoded).To(Equal([]byte("foobar"))) }) It("uses the different keys", func() { var key1, key2 TokenProtectorKey rand.Read(key1[:]) rand.Read(key2[:]) tp1 := newTokenProtector(key1) tp2 := newTokenProtector(key2) t1, err := tp1.NewToken([]byte("foo")) Expect(err).ToNot(HaveOccurred()) t2, err := tp2.NewToken([]byte("foo")) Expect(err).ToNot(HaveOccurred()) _, err = tp1.DecodeToken(t1) Expect(err).ToNot(HaveOccurred()) _, err = tp1.DecodeToken(t2) Expect(err).To(HaveOccurred()) // now create another token protector, reusing key1 tp3 := newTokenProtector(key1) _, err = tp3.DecodeToken(t1) Expect(err).ToNot(HaveOccurred()) _, err = tp3.DecodeToken(t2) Expect(err).To(HaveOccurred()) }) It("doesn't decode invalid tokens", func() { token, err := tp.NewToken([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) _, err = tp.DecodeToken(token[1:]) // the token is invalid without the first byte Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("message authentication failed")) }) It("errors when decoding too short tokens", func() { _, err := tp.DecodeToken([]byte("foobar")) Expect(err).To(MatchError("token too short: 6")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/updatable_aead.go000066400000000000000000000271411465664453100276200ustar00rootroot00000000000000package handshake import ( "crypto" "crypto/cipher" "crypto/tls" "encoding/binary" "fmt" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) // KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update. // It's a package-level variable to allow modifying it for testing purposes. var KeyUpdateInterval uint64 = protocol.KeyUpdateInterval // FirstKeyUpdateInterval is the maximum number of packets we send or receive before initiating the first key update. // It's a package-level variable to allow modifying it for testing purposes. var FirstKeyUpdateInterval uint64 = 100 type updatableAEAD struct { suite *cipherSuite keyPhase protocol.KeyPhase largestAcked protocol.PacketNumber firstPacketNumber protocol.PacketNumber handshakeConfirmed bool invalidPacketLimit uint64 invalidPacketCount uint64 // Time when the keys should be dropped. Keys are dropped on the next call to Open(). prevRcvAEADExpiry time.Time prevRcvAEAD cipher.AEAD firstRcvdWithCurrentKey protocol.PacketNumber firstSentWithCurrentKey protocol.PacketNumber highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected) numRcvdWithCurrentKey uint64 numSentWithCurrentKey uint64 rcvAEAD cipher.AEAD sendAEAD cipher.AEAD // caches cipher.AEAD.Overhead(). This speeds up calls to Overhead(). aeadOverhead int nextRcvAEAD cipher.AEAD nextSendAEAD cipher.AEAD nextRcvTrafficSecret []byte nextSendTrafficSecret []byte headerDecrypter headerProtector headerEncrypter headerProtector rttStats *utils.RTTStats tracer *logging.ConnectionTracer logger utils.Logger version protocol.Version // use a single slice to avoid allocations nonceBuf []byte } var ( _ ShortHeaderOpener = &updatableAEAD{} _ ShortHeaderSealer = &updatableAEAD{} ) func newUpdatableAEAD(rttStats *utils.RTTStats, tracer *logging.ConnectionTracer, logger utils.Logger, version protocol.Version) *updatableAEAD { return &updatableAEAD{ firstPacketNumber: protocol.InvalidPacketNumber, largestAcked: protocol.InvalidPacketNumber, firstRcvdWithCurrentKey: protocol.InvalidPacketNumber, firstSentWithCurrentKey: protocol.InvalidPacketNumber, rttStats: rttStats, tracer: tracer, logger: logger, version: version, } } func (a *updatableAEAD) rollKeys() { if a.prevRcvAEAD != nil { a.logger.Debugf("Dropping key phase %d ahead of scheduled time. Drop time was: %s", a.keyPhase-1, a.prevRcvAEADExpiry) if a.tracer != nil && a.tracer.DroppedKey != nil { a.tracer.DroppedKey(a.keyPhase - 1) } a.prevRcvAEADExpiry = time.Time{} } a.keyPhase++ a.firstRcvdWithCurrentKey = protocol.InvalidPacketNumber a.firstSentWithCurrentKey = protocol.InvalidPacketNumber a.numRcvdWithCurrentKey = 0 a.numSentWithCurrentKey = 0 a.prevRcvAEAD = a.rcvAEAD a.rcvAEAD = a.nextRcvAEAD a.sendAEAD = a.nextSendAEAD a.nextRcvTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextRcvTrafficSecret) a.nextSendTrafficSecret = a.getNextTrafficSecret(a.suite.Hash, a.nextSendTrafficSecret) a.nextRcvAEAD = createAEAD(a.suite, a.nextRcvTrafficSecret, a.version) a.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret, a.version) } func (a *updatableAEAD) startKeyDropTimer(now time.Time) { d := 3 * a.rttStats.PTO(true) a.logger.Debugf("Starting key drop timer to drop key phase %d (in %s)", a.keyPhase-1, d) a.prevRcvAEADExpiry = now.Add(d) } func (a *updatableAEAD) getNextTrafficSecret(hash crypto.Hash, ts []byte) []byte { return hkdfExpandLabel(hash, ts, []byte{}, "quic ku", hash.Size()) } // SetReadKey sets the read key. // For the client, this function is called before SetWriteKey. // For the server, this function is called after SetWriteKey. func (a *updatableAEAD) SetReadKey(suite *cipherSuite, trafficSecret []byte) { a.rcvAEAD = createAEAD(suite, trafficSecret, a.version) a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false, a.version) if a.suite == nil { a.setAEADParameters(a.rcvAEAD, suite) } a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret, a.version) } // SetWriteKey sets the write key. // For the client, this function is called after SetReadKey. // For the server, this function is called before SetReadKey. func (a *updatableAEAD) SetWriteKey(suite *cipherSuite, trafficSecret []byte) { a.sendAEAD = createAEAD(suite, trafficSecret, a.version) a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false, a.version) if a.suite == nil { a.setAEADParameters(a.sendAEAD, suite) } a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret, a.version) } func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *cipherSuite) { a.nonceBuf = make([]byte, aead.NonceSize()) a.aeadOverhead = aead.Overhead() a.suite = suite switch suite.ID { case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: a.invalidPacketLimit = protocol.InvalidPacketLimitAES case tls.TLS_CHACHA20_POLY1305_SHA256: a.invalidPacketLimit = protocol.InvalidPacketLimitChaCha default: panic(fmt.Sprintf("unknown cipher suite %d", suite.ID)) } } func (a *updatableAEAD) DecodePacketNumber(wirePN protocol.PacketNumber, wirePNLen protocol.PacketNumberLen) protocol.PacketNumber { return protocol.DecodePacketNumber(wirePNLen, a.highestRcvdPN, wirePN) } func (a *updatableAEAD) Open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { dec, err := a.open(dst, src, rcvTime, pn, kp, ad) if err == ErrDecryptionFailed { a.invalidPacketCount++ if a.invalidPacketCount >= a.invalidPacketLimit { return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached} } } if err == nil { a.highestRcvdPN = max(a.highestRcvdPN, pn) } return dec, err } func (a *updatableAEAD) open(dst, src []byte, rcvTime time.Time, pn protocol.PacketNumber, kp protocol.KeyPhaseBit, ad []byte) ([]byte, error) { if a.prevRcvAEAD != nil && !a.prevRcvAEADExpiry.IsZero() && rcvTime.After(a.prevRcvAEADExpiry) { a.prevRcvAEAD = nil a.logger.Debugf("Dropping key phase %d", a.keyPhase-1) a.prevRcvAEADExpiry = time.Time{} if a.tracer != nil && a.tracer.DroppedKey != nil { a.tracer.DroppedKey(a.keyPhase - 1) } } binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn)) if kp != a.keyPhase.Bit() { if a.keyPhase > 0 && a.firstRcvdWithCurrentKey == protocol.InvalidPacketNumber || pn < a.firstRcvdWithCurrentKey { if a.prevRcvAEAD == nil { return nil, ErrKeysDropped } // we updated the key, but the peer hasn't updated yet dec, err := a.prevRcvAEAD.Open(dst, a.nonceBuf, src, ad) if err != nil { err = ErrDecryptionFailed } return dec, err } // try opening the packet with the next key phase dec, err := a.nextRcvAEAD.Open(dst, a.nonceBuf, src, ad) if err != nil { return nil, ErrDecryptionFailed } // Opening succeeded. Check if the peer was allowed to update. if a.keyPhase > 0 && a.firstSentWithCurrentKey == protocol.InvalidPacketNumber { return nil, &qerr.TransportError{ ErrorCode: qerr.KeyUpdateError, ErrorMessage: "keys updated too quickly", } } a.rollKeys() a.logger.Debugf("Peer updated keys to %d", a.keyPhase) // The peer initiated this key update. It's safe to drop the keys for the previous generation now. // Start a timer to drop the previous key generation. a.startKeyDropTimer(rcvTime) if a.tracer != nil && a.tracer.UpdatedKey != nil { a.tracer.UpdatedKey(a.keyPhase, true) } a.firstRcvdWithCurrentKey = pn return dec, err } // The AEAD we're using here will be the qtls.aeadAESGCM13. // It uses the nonce provided here and XOR it with the IV. dec, err := a.rcvAEAD.Open(dst, a.nonceBuf, src, ad) if err != nil { return dec, ErrDecryptionFailed } a.numRcvdWithCurrentKey++ if a.firstRcvdWithCurrentKey == protocol.InvalidPacketNumber { // We initiated the key updated, and now we received the first packet protected with the new key phase. // Therefore, we are certain that the peer rolled its keys as well. Start a timer to drop the old keys. if a.keyPhase > 0 { a.logger.Debugf("Peer confirmed key update to phase %d", a.keyPhase) a.startKeyDropTimer(rcvTime) } a.firstRcvdWithCurrentKey = pn } return dec, err } func (a *updatableAEAD) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { if a.firstSentWithCurrentKey == protocol.InvalidPacketNumber { a.firstSentWithCurrentKey = pn } if a.firstPacketNumber == protocol.InvalidPacketNumber { a.firstPacketNumber = pn } a.numSentWithCurrentKey++ binary.BigEndian.PutUint64(a.nonceBuf[len(a.nonceBuf)-8:], uint64(pn)) // The AEAD we're using here will be the qtls.aeadAESGCM13. // It uses the nonce provided here and XOR it with the IV. return a.sendAEAD.Seal(dst, a.nonceBuf, src, ad) } func (a *updatableAEAD) SetLargestAcked(pn protocol.PacketNumber) error { if a.firstSentWithCurrentKey != protocol.InvalidPacketNumber && pn >= a.firstSentWithCurrentKey && a.numRcvdWithCurrentKey == 0 { return &qerr.TransportError{ ErrorCode: qerr.KeyUpdateError, ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", a.keyPhase), } } a.largestAcked = pn return nil } func (a *updatableAEAD) SetHandshakeConfirmed() { a.handshakeConfirmed = true } func (a *updatableAEAD) updateAllowed() bool { if !a.handshakeConfirmed { return false } // the first key update is allowed as soon as the handshake is confirmed return a.keyPhase == 0 || // subsequent key updates as soon as a packet sent with that key phase has been acknowledged (a.firstSentWithCurrentKey != protocol.InvalidPacketNumber && a.largestAcked != protocol.InvalidPacketNumber && a.largestAcked >= a.firstSentWithCurrentKey) } func (a *updatableAEAD) shouldInitiateKeyUpdate() bool { if !a.updateAllowed() { return false } // Initiate the first key update shortly after the handshake, in order to exercise the key update mechanism. if a.keyPhase == 0 { if a.numRcvdWithCurrentKey >= FirstKeyUpdateInterval || a.numSentWithCurrentKey >= FirstKeyUpdateInterval { return true } } if a.numRcvdWithCurrentKey >= KeyUpdateInterval { a.logger.Debugf("Received %d packets with current key phase. Initiating key update to the next key phase: %d", a.numRcvdWithCurrentKey, a.keyPhase+1) return true } if a.numSentWithCurrentKey >= KeyUpdateInterval { a.logger.Debugf("Sent %d packets with current key phase. Initiating key update to the next key phase: %d", a.numSentWithCurrentKey, a.keyPhase+1) return true } return false } func (a *updatableAEAD) KeyPhase() protocol.KeyPhaseBit { if a.shouldInitiateKeyUpdate() { a.rollKeys() a.logger.Debugf("Initiating key update to key phase %d", a.keyPhase) if a.tracer != nil && a.tracer.UpdatedKey != nil { a.tracer.UpdatedKey(a.keyPhase, false) } } return a.keyPhase.Bit() } func (a *updatableAEAD) Overhead() int { return a.aeadOverhead } func (a *updatableAEAD) EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { a.headerEncrypter.EncryptHeader(sample, firstByte, hdrBytes) } func (a *updatableAEAD) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) { a.headerDecrypter.DecryptHeader(sample, firstByte, hdrBytes) } func (a *updatableAEAD) FirstPacketNumber() protocol.PacketNumber { return a.firstPacketNumber } golang-github-lucas-clemente-quic-go-0.46.0/internal/handshake/updatable_aead_test.go000066400000000000000000000652651465664453100306700ustar00rootroot00000000000000package handshake import ( "crypto/rand" "crypto/tls" "fmt" "testing" "time" mocklogging "github.com/quic-go/quic-go/internal/mocks/logging" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Updatable AEAD", func() { DescribeTable("ChaCha test vector", func(v protocol.Version, expectedPayload, expectedPacket []byte) { secret := splitHexString("9ac312a7f877468ebe69422748ad00a1 5443f18203a07d6060f688f30f21632b") aead := newUpdatableAEAD(&utils.RTTStats{}, nil, nil, v) chacha := cipherSuites[2] Expect(chacha.ID).To(Equal(tls.TLS_CHACHA20_POLY1305_SHA256)) aead.SetWriteKey(chacha, secret) const pnOffset = 1 header := splitHexString("4200bff4") payloadOffset := len(header) plaintext := splitHexString("01") payload := aead.Seal(nil, plaintext, 654360564, header) Expect(payload).To(Equal(expectedPayload)) packet := append(header, payload...) aead.EncryptHeader(packet[pnOffset+4:pnOffset+4+16], &packet[0], packet[pnOffset:payloadOffset]) Expect(packet).To(Equal(expectedPacket)) }, Entry("QUIC v1", protocol.Version1, splitHexString("655e5cd55c41f69080575d7999c25a5bfb"), splitHexString("4cfe4189655e5cd55c41f69080575d7999c25a5bfb"), ), Entry("QUIC v2", protocol.Version2, splitHexString("0ae7b6b932bc27d786f4bc2bb20f2162ba"), splitHexString("5558b1c60ae7b6b932bc27d786f4bc2bb20f2162ba"), ), ) for _, ver := range []protocol.Version{protocol.Version1, protocol.Version2} { v := ver Context(fmt.Sprintf("using version %s", v), func() { for i := range cipherSuites { cs := cipherSuites[i] Context(fmt.Sprintf("using %s", tls.CipherSuiteName(cs.ID)), func() { var ( client, server *updatableAEAD serverTracer *mocklogging.MockConnectionTracer rttStats *utils.RTTStats ) BeforeEach(func() { var tr *logging.ConnectionTracer tr, serverTracer = mocklogging.NewMockConnectionTracer(mockCtrl) trafficSecret1 := make([]byte, 16) trafficSecret2 := make([]byte, 16) rand.Read(trafficSecret1) rand.Read(trafficSecret2) rttStats = utils.NewRTTStats() client = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, v) server = newUpdatableAEAD(rttStats, tr, utils.DefaultLogger, v) client.SetReadKey(cs, trafficSecret2) client.SetWriteKey(cs, trafficSecret1) server.SetReadKey(cs, trafficSecret1) server.SetWriteKey(cs, trafficSecret2) }) Context("header protection", func() { It("encrypts and decrypts the header", func() { var lastFiveBitsDifferent int for i := 0; i < 100; i++ { sample := make([]byte, 16) rand.Read(sample) header := []byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef} client.EncryptHeader(sample, &header[0], header[9:13]) if header[0]&0x1f != 0xb5&0x1f { lastFiveBitsDifferent++ } Expect(header[0] & 0xe0).To(Equal(byte(0xb5 & 0xe0))) Expect(header[1:9]).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(header[9:13]).ToNot(Equal([]byte{0xde, 0xad, 0xbe, 0xef})) server.DecryptHeader(sample, &header[0], header[9:13]) Expect(header).To(Equal([]byte{0xb5, 1, 2, 3, 4, 5, 6, 7, 8, 0xde, 0xad, 0xbe, 0xef})) } Expect(lastFiveBitsDifferent).To(BeNumerically(">", 75)) }) }) Context("message encryption", func() { var msg, ad []byte BeforeEach(func() { msg = []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") ad = []byte("Donec in velit neque.") }) It("encrypts and decrypts a message", func() { encrypted := server.Seal(nil, msg, 0x1337, ad) opened, err := client.Open(nil, encrypted, time.Now(), 0x1337, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) Expect(opened).To(Equal(msg)) }) It("saves the first packet number", func() { client.Seal(nil, msg, 0x1337, ad) Expect(client.FirstPacketNumber()).To(Equal(protocol.PacketNumber(0x1337))) client.Seal(nil, msg, 0x1338, ad) Expect(client.FirstPacketNumber()).To(Equal(protocol.PacketNumber(0x1337))) }) It("fails to open a message if the associated data is not the same", func() { encrypted := client.Seal(nil, msg, 0x1337, ad) _, err := server.Open(nil, encrypted, time.Now(), 0x1337, protocol.KeyPhaseZero, []byte("wrong ad")) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("fails to open a message if the packet number is not the same", func() { encrypted := server.Seal(nil, msg, 0x1337, ad) _, err := client.Open(nil, encrypted, time.Now(), 0x42, protocol.KeyPhaseZero, ad) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("decodes the packet number", func() { encrypted := server.Seal(nil, msg, 0x1337, ad) _, err := client.Open(nil, encrypted, time.Now(), 0x1337, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) Expect(client.DecodePacketNumber(0x38, protocol.PacketNumberLen1)).To(BeEquivalentTo(0x1338)) }) It("ignores packets it can't decrypt for packet number derivation", func() { encrypted := server.Seal(nil, msg, 0x1337, ad) _, err := client.Open(nil, encrypted[:len(encrypted)-1], time.Now(), 0x1337, protocol.KeyPhaseZero, ad) Expect(err).To(HaveOccurred()) Expect(client.DecodePacketNumber(0x38, protocol.PacketNumberLen1)).To(BeEquivalentTo(0x38)) }) It("returns an AEAD_LIMIT_REACHED error when reaching the AEAD limit", func() { client.invalidPacketLimit = 10 for i := 0; i < 9; i++ { _, err := client.Open(nil, []byte("foobar"), time.Now(), protocol.PacketNumber(i), protocol.KeyPhaseZero, []byte("ad")) Expect(err).To(MatchError(ErrDecryptionFailed)) } _, err := client.Open(nil, []byte("foobar"), time.Now(), 10, protocol.KeyPhaseZero, []byte("ad")) Expect(err).To(HaveOccurred()) Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.AEADLimitReached)) }) Context("key updates", func() { Context("receiving key updates", func() { It("updates keys", func() { now := time.Now() Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) encrypted0 := server.Seal(nil, msg, 0x1337, ad) server.rollKeys() Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) encrypted1 := server.Seal(nil, msg, 0x1337, ad) Expect(encrypted0).ToNot(Equal(encrypted1)) // expect opening to fail. The client didn't roll keys yet _, err := client.Open(nil, encrypted1, now, 0x1337, protocol.KeyPhaseZero, ad) Expect(err).To(MatchError(ErrDecryptionFailed)) client.rollKeys() decrypted, err := client.Open(nil, encrypted1, now, 0x1337, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) Expect(decrypted).To(Equal(msg)) }) It("updates the keys when receiving a packet with the next key phase", func() { now := time.Now() // receive the first packet at key phase zero encrypted0 := client.Seal(nil, msg, 0x42, ad) decrypted, err := server.Open(nil, encrypted0, now, 0x42, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) Expect(decrypted).To(Equal(msg)) // send one packet at key phase zero Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) _ = server.Seal(nil, msg, 0x1, ad) // now received a message at key phase one client.rollKeys() encrypted1 := client.Seal(nil, msg, 0x43, ad) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), true) decrypted, err = server.Open(nil, encrypted1, now, 0x43, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) Expect(decrypted).To(Equal(msg)) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) }) It("opens a reordered packet with the old keys after an update", func() { now := time.Now() encrypted01 := client.Seal(nil, msg, 0x42, ad) encrypted02 := client.Seal(nil, msg, 0x43, ad) // receive the first packet with key phase 0 _, err := server.Open(nil, encrypted01, now, 0x42, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) // send one packet at key phase zero _ = server.Seal(nil, msg, 0x1, ad) // now receive a packet with key phase 1 client.rollKeys() encrypted1 := client.Seal(nil, msg, 0x44, ad) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), true) _, err = server.Open(nil, encrypted1, now, 0x44, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // now receive a reordered packet with key phase 0 decrypted, err := server.Open(nil, encrypted02, now, 0x43, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) Expect(decrypted).To(Equal(msg)) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) }) It("drops keys 3 PTOs after a key update", func() { now := time.Now() rttStats.UpdateRTT(10*time.Millisecond, 0, now) pto := rttStats.PTO(true) encrypted01 := client.Seal(nil, msg, 0x42, ad) encrypted02 := client.Seal(nil, msg, 0x43, ad) // receive the first packet with key phase 0 _, err := server.Open(nil, encrypted01, now, 0x42, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) // send one packet at key phase zero _ = server.Seal(nil, msg, 0x1, ad) // now receive a packet with key phase 1 client.rollKeys() encrypted1 := client.Seal(nil, msg, 0x44, ad) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), true) serverTracer.EXPECT().DroppedKey(protocol.KeyPhase(0)) _, err = server.Open(nil, encrypted1, now, 0x44, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // now receive a reordered packet with key phase 0 _, err = server.Open(nil, encrypted02, now.Add(3*pto).Add(time.Nanosecond), 0x43, protocol.KeyPhaseZero, ad) Expect(err).To(MatchError(ErrKeysDropped)) }) It("allows the first key update immediately", func() { // receive a packet at key phase one, before having sent or received any packets at key phase 0 client.rollKeys() encrypted1 := client.Seal(nil, msg, 0x1337, ad) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), true) _, err := server.Open(nil, encrypted1, time.Now(), 0x1337, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) }) It("only errors when the peer starts with key phase 1 if decrypting the packet succeeds", func() { client.rollKeys() encrypted := client.Seal(nil, msg, 0x1337, ad) encrypted = encrypted[:len(encrypted)-1] _, err := server.Open(nil, encrypted, time.Now(), 0x1337, protocol.KeyPhaseOne, ad) Expect(err).To(MatchError(ErrDecryptionFailed)) }) It("errors when the peer updates keys too frequently", func() { server.rollKeys() client.rollKeys() // receive the first packet at key phase one encrypted0 := client.Seal(nil, msg, 0x42, ad) _, err := server.Open(nil, encrypted0, time.Now(), 0x42, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) // now receive a packet at key phase two, before having sent any packets client.rollKeys() encrypted1 := client.Seal(nil, msg, 0x42, ad) _, err = server.Open(nil, encrypted1, time.Now(), 0x42, protocol.KeyPhaseZero, ad) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.KeyUpdateError, ErrorMessage: "keys updated too quickly", })) }) }) Context("initiating key updates", func() { const firstKeyUpdateInterval = 5 const keyUpdateInterval = 20 var origKeyUpdateInterval, origFirstKeyUpdateInterval uint64 BeforeEach(func() { origKeyUpdateInterval = KeyUpdateInterval origFirstKeyUpdateInterval = FirstKeyUpdateInterval KeyUpdateInterval = keyUpdateInterval FirstKeyUpdateInterval = firstKeyUpdateInterval server.SetHandshakeConfirmed() }) AfterEach(func() { KeyUpdateInterval = origKeyUpdateInterval FirstKeyUpdateInterval = origFirstKeyUpdateInterval }) It("initiates a key update after sealing the maximum number of packets, for the first update", func() { for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) } // the first update is allowed without receiving an acknowledgement serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) }) It("initiates a key update after sealing the maximum number of packets, for subsequent updates", func() { server.rollKeys() client.rollKeys() for i := 0; i < keyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) server.Seal(nil, msg, pn, ad) } // no update allowed before receiving an acknowledgement for the current key phase Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // receive an ACK for a packet sent in key phase 0 b := client.Seal(nil, []byte("foobar"), 1, []byte("ad")) _, err := server.Open(nil, b, time.Now(), 1, protocol.KeyPhaseOne, []byte("ad")) Expect(err).ToNot(HaveOccurred()) ExpectWithOffset(1, server.SetLargestAcked(0)).To(Succeed()) serverTracer.EXPECT().DroppedKey(protocol.KeyPhase(0)) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(2), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) }) It("errors if the peer acknowledges a packet sent in the next key phase using the old key phase", func() { // First make sure that we update our keys. for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) } serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // Now that our keys are updated, send a packet using the new keys. const nextPN = firstKeyUpdateInterval + 1 server.Seal(nil, msg, nextPN, ad) // We haven't decrypted any packet in the new key phase yet. // This means that the ACK must have been sent in the old key phase. Expect(server.SetLargestAcked(nextPN)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.KeyUpdateError, ErrorMessage: "received ACK for key phase 1, but peer didn't update keys", })) }) It("doesn't error before actually sending a packet in the new key phase", func() { // First make sure that we update our keys. for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) } b := client.Seal(nil, []byte("foobar"), 1, []byte("ad")) _, err := server.Open(nil, b, time.Now(), 1, protocol.KeyPhaseZero, []byte("ad")) Expect(err).ToNot(HaveOccurred()) ExpectWithOffset(1, server.SetLargestAcked(0)).To(Succeed()) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) // Now that our keys are updated, send a packet using the new keys. Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // We haven't decrypted any packet in the new key phase yet. // This means that the ACK must have been sent in the old key phase. Expect(server.SetLargestAcked(1)).ToNot(HaveOccurred()) }) It("initiates a key update after opening the maximum number of packets, for the first update", func() { for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) encrypted := client.Seal(nil, msg, pn, ad) _, err := server.Open(nil, encrypted, time.Now(), pn, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) } // the first update is allowed without receiving an acknowledgement serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) }) It("initiates a key update after opening the maximum number of packets, for subsequent updates", func() { server.rollKeys() client.rollKeys() for i := 0; i < keyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) encrypted := client.Seal(nil, msg, pn, ad) _, err := server.Open(nil, encrypted, time.Now(), pn, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) } // no update allowed before receiving an acknowledgement for the current key phase Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) server.Seal(nil, msg, 1, ad) Expect(server.SetLargestAcked(1)).To(Succeed()) serverTracer.EXPECT().DroppedKey(protocol.KeyPhase(0)) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(2), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) }) It("drops keys 3 PTOs after a key update", func() { now := time.Now() for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) } b := client.Seal(nil, []byte("foobar"), 1, []byte("ad")) _, err := server.Open(nil, b, now, 1, protocol.KeyPhaseZero, []byte("ad")) Expect(err).ToNot(HaveOccurred()) Expect(server.SetLargestAcked(0)).To(Succeed()) // Now we've initiated the first key update. // Decrypt a message sent from the client more than 3 PTO later to make sure the key is still there threePTO := 3 * rttStats.PTO(false) dataKeyPhaseZero := client.Seal(nil, msg, 1, ad) _, err = server.Open(nil, dataKeyPhaseZero, now.Add(threePTO).Add(time.Second), 1, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) // Now receive a packet with key phase 1. // This should start the timer to drop the keys after 3 PTOs. client.rollKeys() dataKeyPhaseOne := client.Seal(nil, msg, 10, ad) t := now.Add(threePTO).Add(time.Second) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), true) _, err = server.Open(nil, dataKeyPhaseOne, t, 10, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) // Make sure the keys are still here. _, err = server.Open(nil, dataKeyPhaseZero, t.Add(threePTO*9/10), 1, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) serverTracer.EXPECT().DroppedKey(protocol.KeyPhase(0)) _, err = server.Open(nil, dataKeyPhaseZero, t.Add(threePTO).Add(time.Nanosecond), 1, protocol.KeyPhaseZero, ad) Expect(err).To(MatchError(ErrKeysDropped)) }) It("doesn't drop the first key generation too early", func() { now := time.Now() data1 := client.Seal(nil, msg, 1, ad) _, err := server.Open(nil, data1, now, 1, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) Expect(server.SetLargestAcked(pn)).To(Succeed()) } serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // The server never received a packet at key phase 1. // Make sure the key phase 0 is still there at a much later point. data2 := client.Seal(nil, msg, 1, ad) _, err = server.Open(nil, data2, now.Add(10*rttStats.PTO(true)), 1, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) }) It("drops keys early when the peer forces initiates a key update within the 3 PTO period", func() { for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) } b := client.Seal(nil, []byte("foobar"), 1, []byte("ad")) _, err := server.Open(nil, b, time.Now(), 1, protocol.KeyPhaseZero, []byte("ad")) Expect(err).ToNot(HaveOccurred()) ExpectWithOffset(1, server.SetLargestAcked(0)).To(Succeed()) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) const nextPN = keyUpdateInterval + 1 // Send and receive an acknowledgement for a packet in key phase 1. // We are now running a timer to drop the keys with 3 PTO. server.Seal(nil, msg, nextPN, ad) client.rollKeys() dataKeyPhaseOne := client.Seal(nil, msg, 2, ad) now := time.Now() _, err = server.Open(nil, dataKeyPhaseOne, now, 2, protocol.KeyPhaseOne, ad) Expect(err).ToNot(HaveOccurred()) Expect(server.SetLargestAcked(nextPN)) // Now the client sends us a packet in key phase 2, forcing us to update keys before the 3 PTO period is over. // This mean that we need to drop the keys for key phase 0 immediately. client.rollKeys() dataKeyPhaseTwo := client.Seal(nil, msg, 3, ad) gomock.InOrder( serverTracer.EXPECT().DroppedKey(protocol.KeyPhase(0)), serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(2), true), ) _, err = server.Open(nil, dataKeyPhaseTwo, now, 3, protocol.KeyPhaseZero, ad) Expect(err).ToNot(HaveOccurred()) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) }) It("drops keys early when we initiate another key update within the 3 PTO period", func() { server.SetHandshakeConfirmed() // send so many packets that we initiate the first key update for i := 0; i < firstKeyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) server.Seal(nil, msg, pn, ad) } b := client.Seal(nil, []byte("foobar"), 1, []byte("ad")) _, err := server.Open(nil, b, time.Now(), 1, protocol.KeyPhaseZero, []byte("ad")) Expect(err).ToNot(HaveOccurred()) ExpectWithOffset(1, server.SetLargestAcked(0)).To(Succeed()) serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(1), false) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) // send so many packets that we initiate the next key update for i := keyUpdateInterval; i < 2*keyUpdateInterval; i++ { pn := protocol.PacketNumber(i) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseOne)) server.Seal(nil, msg, pn, ad) } client.rollKeys() b = client.Seal(nil, []byte("foobar"), 2, []byte("ad")) now := time.Now() _, err = server.Open(nil, b, now, 2, protocol.KeyPhaseOne, []byte("ad")) Expect(err).ToNot(HaveOccurred()) ExpectWithOffset(1, server.SetLargestAcked(keyUpdateInterval)).To(Succeed()) gomock.InOrder( serverTracer.EXPECT().DroppedKey(protocol.KeyPhase(0)), serverTracer.EXPECT().UpdatedKey(protocol.KeyPhase(2), false), ) Expect(server.KeyPhase()).To(Equal(protocol.KeyPhaseZero)) // We haven't received an ACK for a packet sent in key phase 2 yet. // Make sure we canceled the timer to drop the previous key phase. b = client.Seal(nil, []byte("foobar"), 3, []byte("ad")) _, err = server.Open(nil, b, now.Add(10*rttStats.PTO(true)), 3, protocol.KeyPhaseOne, []byte("ad")) Expect(err).ToNot(HaveOccurred()) }) }) }) }) }) } }) } }) func getClientAndServer() (client, server *updatableAEAD) { trafficSecret1 := make([]byte, 16) trafficSecret2 := make([]byte, 16) rand.Read(trafficSecret1) rand.Read(trafficSecret2) cs := cipherSuites[0] rttStats := utils.NewRTTStats() client = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, protocol.Version1) server = newUpdatableAEAD(rttStats, nil, utils.DefaultLogger, protocol.Version1) client.SetReadKey(cs, trafficSecret2) client.SetWriteKey(cs, trafficSecret1) server.SetReadKey(cs, trafficSecret1) server.SetWriteKey(cs, trafficSecret2) return } func BenchmarkPacketEncryption(b *testing.B) { client, _ := getClientAndServer() const l = 1200 src := make([]byte, l) rand.Read(src) ad := make([]byte, 32) rand.Read(ad) for i := 0; i < b.N; i++ { src = client.Seal(src[:0], src[:l], protocol.PacketNumber(i), ad) } } func BenchmarkPacketDecryption(b *testing.B) { client, server := getClientAndServer() const l = 1200 src := make([]byte, l) dst := make([]byte, l) rand.Read(src) ad := make([]byte, 32) rand.Read(ad) src = client.Seal(src[:0], src[:l], 1337, ad) for i := 0; i < b.N; i++ { if _, err := server.Open(dst[:0], src, time.Time{}, 1337, protocol.KeyPhaseZero, ad); err != nil { b.Fatalf("opening failed: %v", err) } } } func BenchmarkRollKeys(b *testing.B) { client, _ := getClientAndServer() for i := 0; i < b.N; i++ { client.rollKeys() } if int(client.keyPhase) != b.N { b.Fatal("didn't roll keys often enough") } } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/000077500000000000000000000000001465664453100235375ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/ackhandler/000077500000000000000000000000001465664453100256335ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/ackhandler/received_packet_handler.go000066400000000000000000000215651465664453100330050ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/ackhandler (interfaces: ReceivedPacketHandler) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mockackhandler -destination ackhandler/received_packet_handler.go github.com/quic-go/quic-go/internal/ackhandler ReceivedPacketHandler // // Package mockackhandler is a generated GoMock package. package mockackhandler import ( reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockReceivedPacketHandler is a mock of ReceivedPacketHandler interface. type MockReceivedPacketHandler struct { ctrl *gomock.Controller recorder *MockReceivedPacketHandlerMockRecorder } // MockReceivedPacketHandlerMockRecorder is the mock recorder for MockReceivedPacketHandler. type MockReceivedPacketHandlerMockRecorder struct { mock *MockReceivedPacketHandler } // NewMockReceivedPacketHandler creates a new mock instance. func NewMockReceivedPacketHandler(ctrl *gomock.Controller) *MockReceivedPacketHandler { mock := &MockReceivedPacketHandler{ctrl: ctrl} mock.recorder = &MockReceivedPacketHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReceivedPacketHandler) EXPECT() *MockReceivedPacketHandlerMockRecorder { return m.recorder } // DropPackets mocks base method. func (m *MockReceivedPacketHandler) DropPackets(arg0 protocol.EncryptionLevel) { m.ctrl.T.Helper() m.ctrl.Call(m, "DropPackets", arg0) } // DropPackets indicates an expected call of DropPackets. func (mr *MockReceivedPacketHandlerMockRecorder) DropPackets(arg0 any) *MockReceivedPacketHandlerDropPacketsCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropPackets", reflect.TypeOf((*MockReceivedPacketHandler)(nil).DropPackets), arg0) return &MockReceivedPacketHandlerDropPacketsCall{Call: call} } // MockReceivedPacketHandlerDropPacketsCall wrap *gomock.Call type MockReceivedPacketHandlerDropPacketsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceivedPacketHandlerDropPacketsCall) Return() *MockReceivedPacketHandlerDropPacketsCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockReceivedPacketHandlerDropPacketsCall) Do(f func(protocol.EncryptionLevel)) *MockReceivedPacketHandlerDropPacketsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceivedPacketHandlerDropPacketsCall) DoAndReturn(f func(protocol.EncryptionLevel)) *MockReceivedPacketHandlerDropPacketsCall { c.Call = c.Call.DoAndReturn(f) return c } // GetAckFrame mocks base method. func (m *MockReceivedPacketHandler) GetAckFrame(arg0 protocol.EncryptionLevel, arg1 bool) *wire.AckFrame { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckFrame", arg0, arg1) ret0, _ := ret[0].(*wire.AckFrame) return ret0 } // GetAckFrame indicates an expected call of GetAckFrame. func (mr *MockReceivedPacketHandlerMockRecorder) GetAckFrame(arg0, arg1 any) *MockReceivedPacketHandlerGetAckFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckFrame", reflect.TypeOf((*MockReceivedPacketHandler)(nil).GetAckFrame), arg0, arg1) return &MockReceivedPacketHandlerGetAckFrameCall{Call: call} } // MockReceivedPacketHandlerGetAckFrameCall wrap *gomock.Call type MockReceivedPacketHandlerGetAckFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceivedPacketHandlerGetAckFrameCall) Return(arg0 *wire.AckFrame) *MockReceivedPacketHandlerGetAckFrameCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceivedPacketHandlerGetAckFrameCall) Do(f func(protocol.EncryptionLevel, bool) *wire.AckFrame) *MockReceivedPacketHandlerGetAckFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceivedPacketHandlerGetAckFrameCall) DoAndReturn(f func(protocol.EncryptionLevel, bool) *wire.AckFrame) *MockReceivedPacketHandlerGetAckFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // GetAlarmTimeout mocks base method. func (m *MockReceivedPacketHandler) GetAlarmTimeout() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAlarmTimeout") ret0, _ := ret[0].(time.Time) return ret0 } // GetAlarmTimeout indicates an expected call of GetAlarmTimeout. func (mr *MockReceivedPacketHandlerMockRecorder) GetAlarmTimeout() *MockReceivedPacketHandlerGetAlarmTimeoutCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAlarmTimeout", reflect.TypeOf((*MockReceivedPacketHandler)(nil).GetAlarmTimeout)) return &MockReceivedPacketHandlerGetAlarmTimeoutCall{Call: call} } // MockReceivedPacketHandlerGetAlarmTimeoutCall wrap *gomock.Call type MockReceivedPacketHandlerGetAlarmTimeoutCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceivedPacketHandlerGetAlarmTimeoutCall) Return(arg0 time.Time) *MockReceivedPacketHandlerGetAlarmTimeoutCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceivedPacketHandlerGetAlarmTimeoutCall) Do(f func() time.Time) *MockReceivedPacketHandlerGetAlarmTimeoutCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceivedPacketHandlerGetAlarmTimeoutCall) DoAndReturn(f func() time.Time) *MockReceivedPacketHandlerGetAlarmTimeoutCall { c.Call = c.Call.DoAndReturn(f) return c } // IsPotentiallyDuplicate mocks base method. func (m *MockReceivedPacketHandler) IsPotentiallyDuplicate(arg0 protocol.PacketNumber, arg1 protocol.EncryptionLevel) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsPotentiallyDuplicate", arg0, arg1) ret0, _ := ret[0].(bool) return ret0 } // IsPotentiallyDuplicate indicates an expected call of IsPotentiallyDuplicate. func (mr *MockReceivedPacketHandlerMockRecorder) IsPotentiallyDuplicate(arg0, arg1 any) *MockReceivedPacketHandlerIsPotentiallyDuplicateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPotentiallyDuplicate", reflect.TypeOf((*MockReceivedPacketHandler)(nil).IsPotentiallyDuplicate), arg0, arg1) return &MockReceivedPacketHandlerIsPotentiallyDuplicateCall{Call: call} } // MockReceivedPacketHandlerIsPotentiallyDuplicateCall wrap *gomock.Call type MockReceivedPacketHandlerIsPotentiallyDuplicateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceivedPacketHandlerIsPotentiallyDuplicateCall) Return(arg0 bool) *MockReceivedPacketHandlerIsPotentiallyDuplicateCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceivedPacketHandlerIsPotentiallyDuplicateCall) Do(f func(protocol.PacketNumber, protocol.EncryptionLevel) bool) *MockReceivedPacketHandlerIsPotentiallyDuplicateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceivedPacketHandlerIsPotentiallyDuplicateCall) DoAndReturn(f func(protocol.PacketNumber, protocol.EncryptionLevel) bool) *MockReceivedPacketHandlerIsPotentiallyDuplicateCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedPacket mocks base method. func (m *MockReceivedPacketHandler) ReceivedPacket(arg0 protocol.PacketNumber, arg1 protocol.ECN, arg2 protocol.EncryptionLevel, arg3 time.Time, arg4 bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReceivedPacket", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(error) return ret0 } // ReceivedPacket indicates an expected call of ReceivedPacket. func (mr *MockReceivedPacketHandlerMockRecorder) ReceivedPacket(arg0, arg1, arg2, arg3, arg4 any) *MockReceivedPacketHandlerReceivedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedPacket", reflect.TypeOf((*MockReceivedPacketHandler)(nil).ReceivedPacket), arg0, arg1, arg2, arg3, arg4) return &MockReceivedPacketHandlerReceivedPacketCall{Call: call} } // MockReceivedPacketHandlerReceivedPacketCall wrap *gomock.Call type MockReceivedPacketHandlerReceivedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceivedPacketHandlerReceivedPacketCall) Return(arg0 error) *MockReceivedPacketHandlerReceivedPacketCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceivedPacketHandlerReceivedPacketCall) Do(f func(protocol.PacketNumber, protocol.ECN, protocol.EncryptionLevel, time.Time, bool) error) *MockReceivedPacketHandlerReceivedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceivedPacketHandlerReceivedPacketCall) DoAndReturn(f func(protocol.PacketNumber, protocol.ECN, protocol.EncryptionLevel, time.Time, bool) error) *MockReceivedPacketHandlerReceivedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/ackhandler/sent_packet_handler.go000066400000000000000000000546501465664453100321710ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/ackhandler (interfaces: SentPacketHandler) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mockackhandler -destination ackhandler/sent_packet_handler.go github.com/quic-go/quic-go/internal/ackhandler SentPacketHandler // // Package mockackhandler is a generated GoMock package. package mockackhandler import ( reflect "reflect" time "time" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" protocol "github.com/quic-go/quic-go/internal/protocol" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockSentPacketHandler is a mock of SentPacketHandler interface. type MockSentPacketHandler struct { ctrl *gomock.Controller recorder *MockSentPacketHandlerMockRecorder } // MockSentPacketHandlerMockRecorder is the mock recorder for MockSentPacketHandler. type MockSentPacketHandlerMockRecorder struct { mock *MockSentPacketHandler } // NewMockSentPacketHandler creates a new mock instance. func NewMockSentPacketHandler(ctrl *gomock.Controller) *MockSentPacketHandler { mock := &MockSentPacketHandler{ctrl: ctrl} mock.recorder = &MockSentPacketHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSentPacketHandler) EXPECT() *MockSentPacketHandlerMockRecorder { return m.recorder } // DropPackets mocks base method. func (m *MockSentPacketHandler) DropPackets(arg0 protocol.EncryptionLevel) { m.ctrl.T.Helper() m.ctrl.Call(m, "DropPackets", arg0) } // DropPackets indicates an expected call of DropPackets. func (mr *MockSentPacketHandlerMockRecorder) DropPackets(arg0 any) *MockSentPacketHandlerDropPacketsCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropPackets", reflect.TypeOf((*MockSentPacketHandler)(nil).DropPackets), arg0) return &MockSentPacketHandlerDropPacketsCall{Call: call} } // MockSentPacketHandlerDropPacketsCall wrap *gomock.Call type MockSentPacketHandlerDropPacketsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerDropPacketsCall) Return() *MockSentPacketHandlerDropPacketsCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerDropPacketsCall) Do(f func(protocol.EncryptionLevel)) *MockSentPacketHandlerDropPacketsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerDropPacketsCall) DoAndReturn(f func(protocol.EncryptionLevel)) *MockSentPacketHandlerDropPacketsCall { c.Call = c.Call.DoAndReturn(f) return c } // ECNMode mocks base method. func (m *MockSentPacketHandler) ECNMode(arg0 bool) protocol.ECN { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ECNMode", arg0) ret0, _ := ret[0].(protocol.ECN) return ret0 } // ECNMode indicates an expected call of ECNMode. func (mr *MockSentPacketHandlerMockRecorder) ECNMode(arg0 any) *MockSentPacketHandlerECNModeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ECNMode", reflect.TypeOf((*MockSentPacketHandler)(nil).ECNMode), arg0) return &MockSentPacketHandlerECNModeCall{Call: call} } // MockSentPacketHandlerECNModeCall wrap *gomock.Call type MockSentPacketHandlerECNModeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerECNModeCall) Return(arg0 protocol.ECN) *MockSentPacketHandlerECNModeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerECNModeCall) Do(f func(bool) protocol.ECN) *MockSentPacketHandlerECNModeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerECNModeCall) DoAndReturn(f func(bool) protocol.ECN) *MockSentPacketHandlerECNModeCall { c.Call = c.Call.DoAndReturn(f) return c } // GetLossDetectionTimeout mocks base method. func (m *MockSentPacketHandler) GetLossDetectionTimeout() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLossDetectionTimeout") ret0, _ := ret[0].(time.Time) return ret0 } // GetLossDetectionTimeout indicates an expected call of GetLossDetectionTimeout. func (mr *MockSentPacketHandlerMockRecorder) GetLossDetectionTimeout() *MockSentPacketHandlerGetLossDetectionTimeoutCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLossDetectionTimeout", reflect.TypeOf((*MockSentPacketHandler)(nil).GetLossDetectionTimeout)) return &MockSentPacketHandlerGetLossDetectionTimeoutCall{Call: call} } // MockSentPacketHandlerGetLossDetectionTimeoutCall wrap *gomock.Call type MockSentPacketHandlerGetLossDetectionTimeoutCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerGetLossDetectionTimeoutCall) Return(arg0 time.Time) *MockSentPacketHandlerGetLossDetectionTimeoutCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerGetLossDetectionTimeoutCall) Do(f func() time.Time) *MockSentPacketHandlerGetLossDetectionTimeoutCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerGetLossDetectionTimeoutCall) DoAndReturn(f func() time.Time) *MockSentPacketHandlerGetLossDetectionTimeoutCall { c.Call = c.Call.DoAndReturn(f) return c } // OnLossDetectionTimeout mocks base method. func (m *MockSentPacketHandler) OnLossDetectionTimeout() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OnLossDetectionTimeout") ret0, _ := ret[0].(error) return ret0 } // OnLossDetectionTimeout indicates an expected call of OnLossDetectionTimeout. func (mr *MockSentPacketHandlerMockRecorder) OnLossDetectionTimeout() *MockSentPacketHandlerOnLossDetectionTimeoutCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnLossDetectionTimeout", reflect.TypeOf((*MockSentPacketHandler)(nil).OnLossDetectionTimeout)) return &MockSentPacketHandlerOnLossDetectionTimeoutCall{Call: call} } // MockSentPacketHandlerOnLossDetectionTimeoutCall wrap *gomock.Call type MockSentPacketHandlerOnLossDetectionTimeoutCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerOnLossDetectionTimeoutCall) Return(arg0 error) *MockSentPacketHandlerOnLossDetectionTimeoutCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerOnLossDetectionTimeoutCall) Do(f func() error) *MockSentPacketHandlerOnLossDetectionTimeoutCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerOnLossDetectionTimeoutCall) DoAndReturn(f func() error) *MockSentPacketHandlerOnLossDetectionTimeoutCall { c.Call = c.Call.DoAndReturn(f) return c } // PeekPacketNumber mocks base method. func (m *MockSentPacketHandler) PeekPacketNumber(arg0 protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PeekPacketNumber", arg0) ret0, _ := ret[0].(protocol.PacketNumber) ret1, _ := ret[1].(protocol.PacketNumberLen) return ret0, ret1 } // PeekPacketNumber indicates an expected call of PeekPacketNumber. func (mr *MockSentPacketHandlerMockRecorder) PeekPacketNumber(arg0 any) *MockSentPacketHandlerPeekPacketNumberCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeekPacketNumber", reflect.TypeOf((*MockSentPacketHandler)(nil).PeekPacketNumber), arg0) return &MockSentPacketHandlerPeekPacketNumberCall{Call: call} } // MockSentPacketHandlerPeekPacketNumberCall wrap *gomock.Call type MockSentPacketHandlerPeekPacketNumberCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerPeekPacketNumberCall) Return(arg0 protocol.PacketNumber, arg1 protocol.PacketNumberLen) *MockSentPacketHandlerPeekPacketNumberCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerPeekPacketNumberCall) Do(f func(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen)) *MockSentPacketHandlerPeekPacketNumberCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerPeekPacketNumberCall) DoAndReturn(f func(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen)) *MockSentPacketHandlerPeekPacketNumberCall { c.Call = c.Call.DoAndReturn(f) return c } // PopPacketNumber mocks base method. func (m *MockSentPacketHandler) PopPacketNumber(arg0 protocol.EncryptionLevel) protocol.PacketNumber { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PopPacketNumber", arg0) ret0, _ := ret[0].(protocol.PacketNumber) return ret0 } // PopPacketNumber indicates an expected call of PopPacketNumber. func (mr *MockSentPacketHandlerMockRecorder) PopPacketNumber(arg0 any) *MockSentPacketHandlerPopPacketNumberCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopPacketNumber", reflect.TypeOf((*MockSentPacketHandler)(nil).PopPacketNumber), arg0) return &MockSentPacketHandlerPopPacketNumberCall{Call: call} } // MockSentPacketHandlerPopPacketNumberCall wrap *gomock.Call type MockSentPacketHandlerPopPacketNumberCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerPopPacketNumberCall) Return(arg0 protocol.PacketNumber) *MockSentPacketHandlerPopPacketNumberCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerPopPacketNumberCall) Do(f func(protocol.EncryptionLevel) protocol.PacketNumber) *MockSentPacketHandlerPopPacketNumberCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerPopPacketNumberCall) DoAndReturn(f func(protocol.EncryptionLevel) protocol.PacketNumber) *MockSentPacketHandlerPopPacketNumberCall { c.Call = c.Call.DoAndReturn(f) return c } // QueueProbePacket mocks base method. func (m *MockSentPacketHandler) QueueProbePacket(arg0 protocol.EncryptionLevel) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "QueueProbePacket", arg0) ret0, _ := ret[0].(bool) return ret0 } // QueueProbePacket indicates an expected call of QueueProbePacket. func (mr *MockSentPacketHandlerMockRecorder) QueueProbePacket(arg0 any) *MockSentPacketHandlerQueueProbePacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueProbePacket", reflect.TypeOf((*MockSentPacketHandler)(nil).QueueProbePacket), arg0) return &MockSentPacketHandlerQueueProbePacketCall{Call: call} } // MockSentPacketHandlerQueueProbePacketCall wrap *gomock.Call type MockSentPacketHandlerQueueProbePacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerQueueProbePacketCall) Return(arg0 bool) *MockSentPacketHandlerQueueProbePacketCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerQueueProbePacketCall) Do(f func(protocol.EncryptionLevel) bool) *MockSentPacketHandlerQueueProbePacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerQueueProbePacketCall) DoAndReturn(f func(protocol.EncryptionLevel) bool) *MockSentPacketHandlerQueueProbePacketCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedAck mocks base method. func (m *MockSentPacketHandler) ReceivedAck(arg0 *wire.AckFrame, arg1 protocol.EncryptionLevel, arg2 time.Time) (bool, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReceivedAck", arg0, arg1, arg2) ret0, _ := ret[0].(bool) ret1, _ := ret[1].(error) return ret0, ret1 } // ReceivedAck indicates an expected call of ReceivedAck. func (mr *MockSentPacketHandlerMockRecorder) ReceivedAck(arg0, arg1, arg2 any) *MockSentPacketHandlerReceivedAckCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedAck", reflect.TypeOf((*MockSentPacketHandler)(nil).ReceivedAck), arg0, arg1, arg2) return &MockSentPacketHandlerReceivedAckCall{Call: call} } // MockSentPacketHandlerReceivedAckCall wrap *gomock.Call type MockSentPacketHandlerReceivedAckCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerReceivedAckCall) Return(arg0 bool, arg1 error) *MockSentPacketHandlerReceivedAckCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerReceivedAckCall) Do(f func(*wire.AckFrame, protocol.EncryptionLevel, time.Time) (bool, error)) *MockSentPacketHandlerReceivedAckCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerReceivedAckCall) DoAndReturn(f func(*wire.AckFrame, protocol.EncryptionLevel, time.Time) (bool, error)) *MockSentPacketHandlerReceivedAckCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedBytes mocks base method. func (m *MockSentPacketHandler) ReceivedBytes(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedBytes", arg0) } // ReceivedBytes indicates an expected call of ReceivedBytes. func (mr *MockSentPacketHandlerMockRecorder) ReceivedBytes(arg0 any) *MockSentPacketHandlerReceivedBytesCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedBytes", reflect.TypeOf((*MockSentPacketHandler)(nil).ReceivedBytes), arg0) return &MockSentPacketHandlerReceivedBytesCall{Call: call} } // MockSentPacketHandlerReceivedBytesCall wrap *gomock.Call type MockSentPacketHandlerReceivedBytesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerReceivedBytesCall) Return() *MockSentPacketHandlerReceivedBytesCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerReceivedBytesCall) Do(f func(protocol.ByteCount)) *MockSentPacketHandlerReceivedBytesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerReceivedBytesCall) DoAndReturn(f func(protocol.ByteCount)) *MockSentPacketHandlerReceivedBytesCall { c.Call = c.Call.DoAndReturn(f) return c } // ResetForRetry mocks base method. func (m *MockSentPacketHandler) ResetForRetry(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetForRetry", arg0) ret0, _ := ret[0].(error) return ret0 } // ResetForRetry indicates an expected call of ResetForRetry. func (mr *MockSentPacketHandlerMockRecorder) ResetForRetry(arg0 any) *MockSentPacketHandlerResetForRetryCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetForRetry", reflect.TypeOf((*MockSentPacketHandler)(nil).ResetForRetry), arg0) return &MockSentPacketHandlerResetForRetryCall{Call: call} } // MockSentPacketHandlerResetForRetryCall wrap *gomock.Call type MockSentPacketHandlerResetForRetryCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerResetForRetryCall) Return(arg0 error) *MockSentPacketHandlerResetForRetryCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerResetForRetryCall) Do(f func(time.Time) error) *MockSentPacketHandlerResetForRetryCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerResetForRetryCall) DoAndReturn(f func(time.Time) error) *MockSentPacketHandlerResetForRetryCall { c.Call = c.Call.DoAndReturn(f) return c } // SendMode mocks base method. func (m *MockSentPacketHandler) SendMode(arg0 time.Time) ackhandler.SendMode { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendMode", arg0) ret0, _ := ret[0].(ackhandler.SendMode) return ret0 } // SendMode indicates an expected call of SendMode. func (mr *MockSentPacketHandlerMockRecorder) SendMode(arg0 any) *MockSentPacketHandlerSendModeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMode", reflect.TypeOf((*MockSentPacketHandler)(nil).SendMode), arg0) return &MockSentPacketHandlerSendModeCall{Call: call} } // MockSentPacketHandlerSendModeCall wrap *gomock.Call type MockSentPacketHandlerSendModeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerSendModeCall) Return(arg0 ackhandler.SendMode) *MockSentPacketHandlerSendModeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerSendModeCall) Do(f func(time.Time) ackhandler.SendMode) *MockSentPacketHandlerSendModeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerSendModeCall) DoAndReturn(f func(time.Time) ackhandler.SendMode) *MockSentPacketHandlerSendModeCall { c.Call = c.Call.DoAndReturn(f) return c } // SentPacket mocks base method. func (m *MockSentPacketHandler) SentPacket(arg0 time.Time, arg1, arg2 protocol.PacketNumber, arg3 []ackhandler.StreamFrame, arg4 []ackhandler.Frame, arg5 protocol.EncryptionLevel, arg6 protocol.ECN, arg7 protocol.ByteCount, arg8 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentPacket", arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) } // SentPacket indicates an expected call of SentPacket. func (mr *MockSentPacketHandlerMockRecorder) SentPacket(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 any) *MockSentPacketHandlerSentPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockSentPacketHandler)(nil).SentPacket), arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) return &MockSentPacketHandlerSentPacketCall{Call: call} } // MockSentPacketHandlerSentPacketCall wrap *gomock.Call type MockSentPacketHandlerSentPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerSentPacketCall) Return() *MockSentPacketHandlerSentPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerSentPacketCall) Do(f func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool)) *MockSentPacketHandlerSentPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerSentPacketCall) DoAndReturn(f func(time.Time, protocol.PacketNumber, protocol.PacketNumber, []ackhandler.StreamFrame, []ackhandler.Frame, protocol.EncryptionLevel, protocol.ECN, protocol.ByteCount, bool)) *MockSentPacketHandlerSentPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SetHandshakeConfirmed mocks base method. func (m *MockSentPacketHandler) SetHandshakeConfirmed() { m.ctrl.T.Helper() m.ctrl.Call(m, "SetHandshakeConfirmed") } // SetHandshakeConfirmed indicates an expected call of SetHandshakeConfirmed. func (mr *MockSentPacketHandlerMockRecorder) SetHandshakeConfirmed() *MockSentPacketHandlerSetHandshakeConfirmedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHandshakeConfirmed", reflect.TypeOf((*MockSentPacketHandler)(nil).SetHandshakeConfirmed)) return &MockSentPacketHandlerSetHandshakeConfirmedCall{Call: call} } // MockSentPacketHandlerSetHandshakeConfirmedCall wrap *gomock.Call type MockSentPacketHandlerSetHandshakeConfirmedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerSetHandshakeConfirmedCall) Return() *MockSentPacketHandlerSetHandshakeConfirmedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerSetHandshakeConfirmedCall) Do(f func()) *MockSentPacketHandlerSetHandshakeConfirmedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerSetHandshakeConfirmedCall) DoAndReturn(f func()) *MockSentPacketHandlerSetHandshakeConfirmedCall { c.Call = c.Call.DoAndReturn(f) return c } // SetMaxDatagramSize mocks base method. func (m *MockSentPacketHandler) SetMaxDatagramSize(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetMaxDatagramSize", arg0) } // SetMaxDatagramSize indicates an expected call of SetMaxDatagramSize. func (mr *MockSentPacketHandlerMockRecorder) SetMaxDatagramSize(arg0 any) *MockSentPacketHandlerSetMaxDatagramSizeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxDatagramSize", reflect.TypeOf((*MockSentPacketHandler)(nil).SetMaxDatagramSize), arg0) return &MockSentPacketHandlerSetMaxDatagramSizeCall{Call: call} } // MockSentPacketHandlerSetMaxDatagramSizeCall wrap *gomock.Call type MockSentPacketHandlerSetMaxDatagramSizeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerSetMaxDatagramSizeCall) Return() *MockSentPacketHandlerSetMaxDatagramSizeCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerSetMaxDatagramSizeCall) Do(f func(protocol.ByteCount)) *MockSentPacketHandlerSetMaxDatagramSizeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerSetMaxDatagramSizeCall) DoAndReturn(f func(protocol.ByteCount)) *MockSentPacketHandlerSetMaxDatagramSizeCall { c.Call = c.Call.DoAndReturn(f) return c } // TimeUntilSend mocks base method. func (m *MockSentPacketHandler) TimeUntilSend() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TimeUntilSend") ret0, _ := ret[0].(time.Time) return ret0 } // TimeUntilSend indicates an expected call of TimeUntilSend. func (mr *MockSentPacketHandlerMockRecorder) TimeUntilSend() *MockSentPacketHandlerTimeUntilSendCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeUntilSend", reflect.TypeOf((*MockSentPacketHandler)(nil).TimeUntilSend)) return &MockSentPacketHandlerTimeUntilSendCall{Call: call} } // MockSentPacketHandlerTimeUntilSendCall wrap *gomock.Call type MockSentPacketHandlerTimeUntilSendCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSentPacketHandlerTimeUntilSendCall) Return(arg0 time.Time) *MockSentPacketHandlerTimeUntilSendCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSentPacketHandlerTimeUntilSendCall) Do(f func() time.Time) *MockSentPacketHandlerTimeUntilSendCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSentPacketHandlerTimeUntilSendCall) DoAndReturn(f func() time.Time) *MockSentPacketHandlerTimeUntilSendCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/congestion.go000066400000000000000000000463171465664453100262510ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/congestion (interfaces: SendAlgorithmWithDebugInfos) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination congestion.go github.com/quic-go/quic-go/internal/congestion SendAlgorithmWithDebugInfos // // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockSendAlgorithmWithDebugInfos is a mock of SendAlgorithmWithDebugInfos interface. type MockSendAlgorithmWithDebugInfos struct { ctrl *gomock.Controller recorder *MockSendAlgorithmWithDebugInfosMockRecorder } // MockSendAlgorithmWithDebugInfosMockRecorder is the mock recorder for MockSendAlgorithmWithDebugInfos. type MockSendAlgorithmWithDebugInfosMockRecorder struct { mock *MockSendAlgorithmWithDebugInfos } // NewMockSendAlgorithmWithDebugInfos creates a new mock instance. func NewMockSendAlgorithmWithDebugInfos(ctrl *gomock.Controller) *MockSendAlgorithmWithDebugInfos { mock := &MockSendAlgorithmWithDebugInfos{ctrl: ctrl} mock.recorder = &MockSendAlgorithmWithDebugInfosMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSendAlgorithmWithDebugInfos) EXPECT() *MockSendAlgorithmWithDebugInfosMockRecorder { return m.recorder } // CanSend mocks base method. func (m *MockSendAlgorithmWithDebugInfos) CanSend(arg0 protocol.ByteCount) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CanSend", arg0) ret0, _ := ret[0].(bool) return ret0 } // CanSend indicates an expected call of CanSend. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) CanSend(arg0 any) *MockSendAlgorithmWithDebugInfosCanSendCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanSend", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).CanSend), arg0) return &MockSendAlgorithmWithDebugInfosCanSendCall{Call: call} } // MockSendAlgorithmWithDebugInfosCanSendCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosCanSendCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosCanSendCall) Return(arg0 bool) *MockSendAlgorithmWithDebugInfosCanSendCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosCanSendCall) Do(f func(protocol.ByteCount) bool) *MockSendAlgorithmWithDebugInfosCanSendCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosCanSendCall) DoAndReturn(f func(protocol.ByteCount) bool) *MockSendAlgorithmWithDebugInfosCanSendCall { c.Call = c.Call.DoAndReturn(f) return c } // GetCongestionWindow mocks base method. func (m *MockSendAlgorithmWithDebugInfos) GetCongestionWindow() protocol.ByteCount { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCongestionWindow") ret0, _ := ret[0].(protocol.ByteCount) return ret0 } // GetCongestionWindow indicates an expected call of GetCongestionWindow. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) GetCongestionWindow() *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCongestionWindow", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).GetCongestionWindow)) return &MockSendAlgorithmWithDebugInfosGetCongestionWindowCall{Call: call} } // MockSendAlgorithmWithDebugInfosGetCongestionWindowCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosGetCongestionWindowCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall) Return(arg0 protocol.ByteCount) *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall) Do(f func() protocol.ByteCount) *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall) DoAndReturn(f func() protocol.ByteCount) *MockSendAlgorithmWithDebugInfosGetCongestionWindowCall { c.Call = c.Call.DoAndReturn(f) return c } // HasPacingBudget mocks base method. func (m *MockSendAlgorithmWithDebugInfos) HasPacingBudget(arg0 time.Time) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasPacingBudget", arg0) ret0, _ := ret[0].(bool) return ret0 } // HasPacingBudget indicates an expected call of HasPacingBudget. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) HasPacingBudget(arg0 any) *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPacingBudget", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).HasPacingBudget), arg0) return &MockSendAlgorithmWithDebugInfosHasPacingBudgetCall{Call: call} } // MockSendAlgorithmWithDebugInfosHasPacingBudgetCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosHasPacingBudgetCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall) Return(arg0 bool) *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall) Do(f func(time.Time) bool) *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall) DoAndReturn(f func(time.Time) bool) *MockSendAlgorithmWithDebugInfosHasPacingBudgetCall { c.Call = c.Call.DoAndReturn(f) return c } // InRecovery mocks base method. func (m *MockSendAlgorithmWithDebugInfos) InRecovery() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InRecovery") ret0, _ := ret[0].(bool) return ret0 } // InRecovery indicates an expected call of InRecovery. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) InRecovery() *MockSendAlgorithmWithDebugInfosInRecoveryCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InRecovery", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).InRecovery)) return &MockSendAlgorithmWithDebugInfosInRecoveryCall{Call: call} } // MockSendAlgorithmWithDebugInfosInRecoveryCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosInRecoveryCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosInRecoveryCall) Return(arg0 bool) *MockSendAlgorithmWithDebugInfosInRecoveryCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosInRecoveryCall) Do(f func() bool) *MockSendAlgorithmWithDebugInfosInRecoveryCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosInRecoveryCall) DoAndReturn(f func() bool) *MockSendAlgorithmWithDebugInfosInRecoveryCall { c.Call = c.Call.DoAndReturn(f) return c } // InSlowStart mocks base method. func (m *MockSendAlgorithmWithDebugInfos) InSlowStart() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "InSlowStart") ret0, _ := ret[0].(bool) return ret0 } // InSlowStart indicates an expected call of InSlowStart. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) InSlowStart() *MockSendAlgorithmWithDebugInfosInSlowStartCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InSlowStart", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).InSlowStart)) return &MockSendAlgorithmWithDebugInfosInSlowStartCall{Call: call} } // MockSendAlgorithmWithDebugInfosInSlowStartCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosInSlowStartCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosInSlowStartCall) Return(arg0 bool) *MockSendAlgorithmWithDebugInfosInSlowStartCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosInSlowStartCall) Do(f func() bool) *MockSendAlgorithmWithDebugInfosInSlowStartCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosInSlowStartCall) DoAndReturn(f func() bool) *MockSendAlgorithmWithDebugInfosInSlowStartCall { c.Call = c.Call.DoAndReturn(f) return c } // MaybeExitSlowStart mocks base method. func (m *MockSendAlgorithmWithDebugInfos) MaybeExitSlowStart() { m.ctrl.T.Helper() m.ctrl.Call(m, "MaybeExitSlowStart") } // MaybeExitSlowStart indicates an expected call of MaybeExitSlowStart. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) MaybeExitSlowStart() *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybeExitSlowStart", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).MaybeExitSlowStart)) return &MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall{Call: call} } // MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall) Return() *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall) Do(f func()) *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall) DoAndReturn(f func()) *MockSendAlgorithmWithDebugInfosMaybeExitSlowStartCall { c.Call = c.Call.DoAndReturn(f) return c } // OnCongestionEvent mocks base method. func (m *MockSendAlgorithmWithDebugInfos) OnCongestionEvent(arg0 protocol.PacketNumber, arg1, arg2 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnCongestionEvent", arg0, arg1, arg2) } // OnCongestionEvent indicates an expected call of OnCongestionEvent. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) OnCongestionEvent(arg0, arg1, arg2 any) *MockSendAlgorithmWithDebugInfosOnCongestionEventCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnCongestionEvent", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnCongestionEvent), arg0, arg1, arg2) return &MockSendAlgorithmWithDebugInfosOnCongestionEventCall{Call: call} } // MockSendAlgorithmWithDebugInfosOnCongestionEventCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosOnCongestionEventCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosOnCongestionEventCall) Return() *MockSendAlgorithmWithDebugInfosOnCongestionEventCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosOnCongestionEventCall) Do(f func(protocol.PacketNumber, protocol.ByteCount, protocol.ByteCount)) *MockSendAlgorithmWithDebugInfosOnCongestionEventCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosOnCongestionEventCall) DoAndReturn(f func(protocol.PacketNumber, protocol.ByteCount, protocol.ByteCount)) *MockSendAlgorithmWithDebugInfosOnCongestionEventCall { c.Call = c.Call.DoAndReturn(f) return c } // OnPacketAcked mocks base method. func (m *MockSendAlgorithmWithDebugInfos) OnPacketAcked(arg0 protocol.PacketNumber, arg1, arg2 protocol.ByteCount, arg3 time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnPacketAcked", arg0, arg1, arg2, arg3) } // OnPacketAcked indicates an expected call of OnPacketAcked. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) OnPacketAcked(arg0, arg1, arg2, arg3 any) *MockSendAlgorithmWithDebugInfosOnPacketAckedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPacketAcked", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnPacketAcked), arg0, arg1, arg2, arg3) return &MockSendAlgorithmWithDebugInfosOnPacketAckedCall{Call: call} } // MockSendAlgorithmWithDebugInfosOnPacketAckedCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosOnPacketAckedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosOnPacketAckedCall) Return() *MockSendAlgorithmWithDebugInfosOnPacketAckedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosOnPacketAckedCall) Do(f func(protocol.PacketNumber, protocol.ByteCount, protocol.ByteCount, time.Time)) *MockSendAlgorithmWithDebugInfosOnPacketAckedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosOnPacketAckedCall) DoAndReturn(f func(protocol.PacketNumber, protocol.ByteCount, protocol.ByteCount, time.Time)) *MockSendAlgorithmWithDebugInfosOnPacketAckedCall { c.Call = c.Call.DoAndReturn(f) return c } // OnPacketSent mocks base method. func (m *MockSendAlgorithmWithDebugInfos) OnPacketSent(arg0 time.Time, arg1 protocol.ByteCount, arg2 protocol.PacketNumber, arg3 protocol.ByteCount, arg4 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnPacketSent", arg0, arg1, arg2, arg3, arg4) } // OnPacketSent indicates an expected call of OnPacketSent. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) OnPacketSent(arg0, arg1, arg2, arg3, arg4 any) *MockSendAlgorithmWithDebugInfosOnPacketSentCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPacketSent", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnPacketSent), arg0, arg1, arg2, arg3, arg4) return &MockSendAlgorithmWithDebugInfosOnPacketSentCall{Call: call} } // MockSendAlgorithmWithDebugInfosOnPacketSentCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosOnPacketSentCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosOnPacketSentCall) Return() *MockSendAlgorithmWithDebugInfosOnPacketSentCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosOnPacketSentCall) Do(f func(time.Time, protocol.ByteCount, protocol.PacketNumber, protocol.ByteCount, bool)) *MockSendAlgorithmWithDebugInfosOnPacketSentCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosOnPacketSentCall) DoAndReturn(f func(time.Time, protocol.ByteCount, protocol.PacketNumber, protocol.ByteCount, bool)) *MockSendAlgorithmWithDebugInfosOnPacketSentCall { c.Call = c.Call.DoAndReturn(f) return c } // OnRetransmissionTimeout mocks base method. func (m *MockSendAlgorithmWithDebugInfos) OnRetransmissionTimeout(arg0 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnRetransmissionTimeout", arg0) } // OnRetransmissionTimeout indicates an expected call of OnRetransmissionTimeout. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) OnRetransmissionTimeout(arg0 any) *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnRetransmissionTimeout", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnRetransmissionTimeout), arg0) return &MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall{Call: call} } // MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall) Return() *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall) Do(f func(bool)) *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall) DoAndReturn(f func(bool)) *MockSendAlgorithmWithDebugInfosOnRetransmissionTimeoutCall { c.Call = c.Call.DoAndReturn(f) return c } // SetMaxDatagramSize mocks base method. func (m *MockSendAlgorithmWithDebugInfos) SetMaxDatagramSize(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetMaxDatagramSize", arg0) } // SetMaxDatagramSize indicates an expected call of SetMaxDatagramSize. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) SetMaxDatagramSize(arg0 any) *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxDatagramSize", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).SetMaxDatagramSize), arg0) return &MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall{Call: call} } // MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall) Return() *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall) Do(f func(protocol.ByteCount)) *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall) DoAndReturn(f func(protocol.ByteCount)) *MockSendAlgorithmWithDebugInfosSetMaxDatagramSizeCall { c.Call = c.Call.DoAndReturn(f) return c } // TimeUntilSend mocks base method. func (m *MockSendAlgorithmWithDebugInfos) TimeUntilSend(arg0 protocol.ByteCount) time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TimeUntilSend", arg0) ret0, _ := ret[0].(time.Time) return ret0 } // TimeUntilSend indicates an expected call of TimeUntilSend. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) TimeUntilSend(arg0 any) *MockSendAlgorithmWithDebugInfosTimeUntilSendCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeUntilSend", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).TimeUntilSend), arg0) return &MockSendAlgorithmWithDebugInfosTimeUntilSendCall{Call: call} } // MockSendAlgorithmWithDebugInfosTimeUntilSendCall wrap *gomock.Call type MockSendAlgorithmWithDebugInfosTimeUntilSendCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendAlgorithmWithDebugInfosTimeUntilSendCall) Return(arg0 time.Time) *MockSendAlgorithmWithDebugInfosTimeUntilSendCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendAlgorithmWithDebugInfosTimeUntilSendCall) Do(f func(protocol.ByteCount) time.Time) *MockSendAlgorithmWithDebugInfosTimeUntilSendCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendAlgorithmWithDebugInfosTimeUntilSendCall) DoAndReturn(f func(protocol.ByteCount) time.Time) *MockSendAlgorithmWithDebugInfosTimeUntilSendCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/connection_flow_controller.go000066400000000000000000000261621465664453100315260ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/flowcontrol (interfaces: ConnectionFlowController) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination connection_flow_controller.go github.com/quic-go/quic-go/internal/flowcontrol ConnectionFlowController // // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockConnectionFlowController is a mock of ConnectionFlowController interface. type MockConnectionFlowController struct { ctrl *gomock.Controller recorder *MockConnectionFlowControllerMockRecorder } // MockConnectionFlowControllerMockRecorder is the mock recorder for MockConnectionFlowController. type MockConnectionFlowControllerMockRecorder struct { mock *MockConnectionFlowController } // NewMockConnectionFlowController creates a new mock instance. func NewMockConnectionFlowController(ctrl *gomock.Controller) *MockConnectionFlowController { mock := &MockConnectionFlowController{ctrl: ctrl} mock.recorder = &MockConnectionFlowControllerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConnectionFlowController) EXPECT() *MockConnectionFlowControllerMockRecorder { return m.recorder } // AddBytesRead mocks base method. func (m *MockConnectionFlowController) AddBytesRead(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddBytesRead", arg0) } // AddBytesRead indicates an expected call of AddBytesRead. func (mr *MockConnectionFlowControllerMockRecorder) AddBytesRead(arg0 any) *MockConnectionFlowControllerAddBytesReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesRead", reflect.TypeOf((*MockConnectionFlowController)(nil).AddBytesRead), arg0) return &MockConnectionFlowControllerAddBytesReadCall{Call: call} } // MockConnectionFlowControllerAddBytesReadCall wrap *gomock.Call type MockConnectionFlowControllerAddBytesReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerAddBytesReadCall) Return() *MockConnectionFlowControllerAddBytesReadCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerAddBytesReadCall) Do(f func(protocol.ByteCount)) *MockConnectionFlowControllerAddBytesReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerAddBytesReadCall) DoAndReturn(f func(protocol.ByteCount)) *MockConnectionFlowControllerAddBytesReadCall { c.Call = c.Call.DoAndReturn(f) return c } // AddBytesSent mocks base method. func (m *MockConnectionFlowController) AddBytesSent(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddBytesSent", arg0) } // AddBytesSent indicates an expected call of AddBytesSent. func (mr *MockConnectionFlowControllerMockRecorder) AddBytesSent(arg0 any) *MockConnectionFlowControllerAddBytesSentCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesSent", reflect.TypeOf((*MockConnectionFlowController)(nil).AddBytesSent), arg0) return &MockConnectionFlowControllerAddBytesSentCall{Call: call} } // MockConnectionFlowControllerAddBytesSentCall wrap *gomock.Call type MockConnectionFlowControllerAddBytesSentCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerAddBytesSentCall) Return() *MockConnectionFlowControllerAddBytesSentCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerAddBytesSentCall) Do(f func(protocol.ByteCount)) *MockConnectionFlowControllerAddBytesSentCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerAddBytesSentCall) DoAndReturn(f func(protocol.ByteCount)) *MockConnectionFlowControllerAddBytesSentCall { c.Call = c.Call.DoAndReturn(f) return c } // GetWindowUpdate mocks base method. func (m *MockConnectionFlowController) GetWindowUpdate() protocol.ByteCount { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWindowUpdate") ret0, _ := ret[0].(protocol.ByteCount) return ret0 } // GetWindowUpdate indicates an expected call of GetWindowUpdate. func (mr *MockConnectionFlowControllerMockRecorder) GetWindowUpdate() *MockConnectionFlowControllerGetWindowUpdateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWindowUpdate", reflect.TypeOf((*MockConnectionFlowController)(nil).GetWindowUpdate)) return &MockConnectionFlowControllerGetWindowUpdateCall{Call: call} } // MockConnectionFlowControllerGetWindowUpdateCall wrap *gomock.Call type MockConnectionFlowControllerGetWindowUpdateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerGetWindowUpdateCall) Return(arg0 protocol.ByteCount) *MockConnectionFlowControllerGetWindowUpdateCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerGetWindowUpdateCall) Do(f func() protocol.ByteCount) *MockConnectionFlowControllerGetWindowUpdateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerGetWindowUpdateCall) DoAndReturn(f func() protocol.ByteCount) *MockConnectionFlowControllerGetWindowUpdateCall { c.Call = c.Call.DoAndReturn(f) return c } // IsNewlyBlocked mocks base method. func (m *MockConnectionFlowController) IsNewlyBlocked() (bool, protocol.ByteCount) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNewlyBlocked") ret0, _ := ret[0].(bool) ret1, _ := ret[1].(protocol.ByteCount) return ret0, ret1 } // IsNewlyBlocked indicates an expected call of IsNewlyBlocked. func (mr *MockConnectionFlowControllerMockRecorder) IsNewlyBlocked() *MockConnectionFlowControllerIsNewlyBlockedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNewlyBlocked", reflect.TypeOf((*MockConnectionFlowController)(nil).IsNewlyBlocked)) return &MockConnectionFlowControllerIsNewlyBlockedCall{Call: call} } // MockConnectionFlowControllerIsNewlyBlockedCall wrap *gomock.Call type MockConnectionFlowControllerIsNewlyBlockedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerIsNewlyBlockedCall) Return(arg0 bool, arg1 protocol.ByteCount) *MockConnectionFlowControllerIsNewlyBlockedCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerIsNewlyBlockedCall) Do(f func() (bool, protocol.ByteCount)) *MockConnectionFlowControllerIsNewlyBlockedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerIsNewlyBlockedCall) DoAndReturn(f func() (bool, protocol.ByteCount)) *MockConnectionFlowControllerIsNewlyBlockedCall { c.Call = c.Call.DoAndReturn(f) return c } // Reset mocks base method. func (m *MockConnectionFlowController) Reset() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Reset") ret0, _ := ret[0].(error) return ret0 } // Reset indicates an expected call of Reset. func (mr *MockConnectionFlowControllerMockRecorder) Reset() *MockConnectionFlowControllerResetCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockConnectionFlowController)(nil).Reset)) return &MockConnectionFlowControllerResetCall{Call: call} } // MockConnectionFlowControllerResetCall wrap *gomock.Call type MockConnectionFlowControllerResetCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerResetCall) Return(arg0 error) *MockConnectionFlowControllerResetCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerResetCall) Do(f func() error) *MockConnectionFlowControllerResetCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerResetCall) DoAndReturn(f func() error) *MockConnectionFlowControllerResetCall { c.Call = c.Call.DoAndReturn(f) return c } // SendWindowSize mocks base method. func (m *MockConnectionFlowController) SendWindowSize() protocol.ByteCount { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendWindowSize") ret0, _ := ret[0].(protocol.ByteCount) return ret0 } // SendWindowSize indicates an expected call of SendWindowSize. func (mr *MockConnectionFlowControllerMockRecorder) SendWindowSize() *MockConnectionFlowControllerSendWindowSizeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWindowSize", reflect.TypeOf((*MockConnectionFlowController)(nil).SendWindowSize)) return &MockConnectionFlowControllerSendWindowSizeCall{Call: call} } // MockConnectionFlowControllerSendWindowSizeCall wrap *gomock.Call type MockConnectionFlowControllerSendWindowSizeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerSendWindowSizeCall) Return(arg0 protocol.ByteCount) *MockConnectionFlowControllerSendWindowSizeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerSendWindowSizeCall) Do(f func() protocol.ByteCount) *MockConnectionFlowControllerSendWindowSizeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerSendWindowSizeCall) DoAndReturn(f func() protocol.ByteCount) *MockConnectionFlowControllerSendWindowSizeCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdateSendWindow mocks base method. func (m *MockConnectionFlowController) UpdateSendWindow(arg0 protocol.ByteCount) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateSendWindow", arg0) ret0, _ := ret[0].(bool) return ret0 } // UpdateSendWindow indicates an expected call of UpdateSendWindow. func (mr *MockConnectionFlowControllerMockRecorder) UpdateSendWindow(arg0 any) *MockConnectionFlowControllerUpdateSendWindowCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSendWindow", reflect.TypeOf((*MockConnectionFlowController)(nil).UpdateSendWindow), arg0) return &MockConnectionFlowControllerUpdateSendWindowCall{Call: call} } // MockConnectionFlowControllerUpdateSendWindowCall wrap *gomock.Call type MockConnectionFlowControllerUpdateSendWindowCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionFlowControllerUpdateSendWindowCall) Return(arg0 bool) *MockConnectionFlowControllerUpdateSendWindowCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionFlowControllerUpdateSendWindowCall) Do(f func(protocol.ByteCount) bool) *MockConnectionFlowControllerUpdateSendWindowCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionFlowControllerUpdateSendWindowCall) DoAndReturn(f func(protocol.ByteCount) bool) *MockConnectionFlowControllerUpdateSendWindowCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/crypto_setup.go000066400000000000000000000617221465664453100266360ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/handshake (interfaces: CryptoSetup) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination crypto_setup_tmp.go github.com/quic-go/quic-go/internal/handshake CryptoSetup // // Package mocks is a generated GoMock package. package mocks import ( context "context" reflect "reflect" handshake "github.com/quic-go/quic-go/internal/handshake" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockCryptoSetup is a mock of CryptoSetup interface. type MockCryptoSetup struct { ctrl *gomock.Controller recorder *MockCryptoSetupMockRecorder } // MockCryptoSetupMockRecorder is the mock recorder for MockCryptoSetup. type MockCryptoSetupMockRecorder struct { mock *MockCryptoSetup } // NewMockCryptoSetup creates a new mock instance. func NewMockCryptoSetup(ctrl *gomock.Controller) *MockCryptoSetup { mock := &MockCryptoSetup{ctrl: ctrl} mock.recorder = &MockCryptoSetupMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCryptoSetup) EXPECT() *MockCryptoSetupMockRecorder { return m.recorder } // ChangeConnectionID mocks base method. func (m *MockCryptoSetup) ChangeConnectionID(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "ChangeConnectionID", arg0) } // ChangeConnectionID indicates an expected call of ChangeConnectionID. func (mr *MockCryptoSetupMockRecorder) ChangeConnectionID(arg0 any) *MockCryptoSetupChangeConnectionIDCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChangeConnectionID", reflect.TypeOf((*MockCryptoSetup)(nil).ChangeConnectionID), arg0) return &MockCryptoSetupChangeConnectionIDCall{Call: call} } // MockCryptoSetupChangeConnectionIDCall wrap *gomock.Call type MockCryptoSetupChangeConnectionIDCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupChangeConnectionIDCall) Return() *MockCryptoSetupChangeConnectionIDCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupChangeConnectionIDCall) Do(f func(protocol.ConnectionID)) *MockCryptoSetupChangeConnectionIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupChangeConnectionIDCall) DoAndReturn(f func(protocol.ConnectionID)) *MockCryptoSetupChangeConnectionIDCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockCryptoSetup) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockCryptoSetupMockRecorder) Close() *MockCryptoSetupCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCryptoSetup)(nil).Close)) return &MockCryptoSetupCloseCall{Call: call} } // MockCryptoSetupCloseCall wrap *gomock.Call type MockCryptoSetupCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupCloseCall) Return(arg0 error) *MockCryptoSetupCloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupCloseCall) Do(f func() error) *MockCryptoSetupCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupCloseCall) DoAndReturn(f func() error) *MockCryptoSetupCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // ConnectionState mocks base method. func (m *MockCryptoSetup) ConnectionState() handshake.ConnectionState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConnectionState") ret0, _ := ret[0].(handshake.ConnectionState) return ret0 } // ConnectionState indicates an expected call of ConnectionState. func (mr *MockCryptoSetupMockRecorder) ConnectionState() *MockCryptoSetupConnectionStateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockCryptoSetup)(nil).ConnectionState)) return &MockCryptoSetupConnectionStateCall{Call: call} } // MockCryptoSetupConnectionStateCall wrap *gomock.Call type MockCryptoSetupConnectionStateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupConnectionStateCall) Return(arg0 handshake.ConnectionState) *MockCryptoSetupConnectionStateCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupConnectionStateCall) Do(f func() handshake.ConnectionState) *MockCryptoSetupConnectionStateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupConnectionStateCall) DoAndReturn(f func() handshake.ConnectionState) *MockCryptoSetupConnectionStateCall { c.Call = c.Call.DoAndReturn(f) return c } // DiscardInitialKeys mocks base method. func (m *MockCryptoSetup) DiscardInitialKeys() { m.ctrl.T.Helper() m.ctrl.Call(m, "DiscardInitialKeys") } // DiscardInitialKeys indicates an expected call of DiscardInitialKeys. func (mr *MockCryptoSetupMockRecorder) DiscardInitialKeys() *MockCryptoSetupDiscardInitialKeysCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DiscardInitialKeys", reflect.TypeOf((*MockCryptoSetup)(nil).DiscardInitialKeys)) return &MockCryptoSetupDiscardInitialKeysCall{Call: call} } // MockCryptoSetupDiscardInitialKeysCall wrap *gomock.Call type MockCryptoSetupDiscardInitialKeysCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupDiscardInitialKeysCall) Return() *MockCryptoSetupDiscardInitialKeysCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupDiscardInitialKeysCall) Do(f func()) *MockCryptoSetupDiscardInitialKeysCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupDiscardInitialKeysCall) DoAndReturn(f func()) *MockCryptoSetupDiscardInitialKeysCall { c.Call = c.Call.DoAndReturn(f) return c } // Get0RTTOpener mocks base method. func (m *MockCryptoSetup) Get0RTTOpener() (handshake.LongHeaderOpener, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get0RTTOpener") ret0, _ := ret[0].(handshake.LongHeaderOpener) ret1, _ := ret[1].(error) return ret0, ret1 } // Get0RTTOpener indicates an expected call of Get0RTTOpener. func (mr *MockCryptoSetupMockRecorder) Get0RTTOpener() *MockCryptoSetupGet0RTTOpenerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get0RTTOpener", reflect.TypeOf((*MockCryptoSetup)(nil).Get0RTTOpener)) return &MockCryptoSetupGet0RTTOpenerCall{Call: call} } // MockCryptoSetupGet0RTTOpenerCall wrap *gomock.Call type MockCryptoSetupGet0RTTOpenerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGet0RTTOpenerCall) Return(arg0 handshake.LongHeaderOpener, arg1 error) *MockCryptoSetupGet0RTTOpenerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGet0RTTOpenerCall) Do(f func() (handshake.LongHeaderOpener, error)) *MockCryptoSetupGet0RTTOpenerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGet0RTTOpenerCall) DoAndReturn(f func() (handshake.LongHeaderOpener, error)) *MockCryptoSetupGet0RTTOpenerCall { c.Call = c.Call.DoAndReturn(f) return c } // Get0RTTSealer mocks base method. func (m *MockCryptoSetup) Get0RTTSealer() (handshake.LongHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get0RTTSealer") ret0, _ := ret[0].(handshake.LongHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // Get0RTTSealer indicates an expected call of Get0RTTSealer. func (mr *MockCryptoSetupMockRecorder) Get0RTTSealer() *MockCryptoSetupGet0RTTSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get0RTTSealer", reflect.TypeOf((*MockCryptoSetup)(nil).Get0RTTSealer)) return &MockCryptoSetupGet0RTTSealerCall{Call: call} } // MockCryptoSetupGet0RTTSealerCall wrap *gomock.Call type MockCryptoSetupGet0RTTSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGet0RTTSealerCall) Return(arg0 handshake.LongHeaderSealer, arg1 error) *MockCryptoSetupGet0RTTSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGet0RTTSealerCall) Do(f func() (handshake.LongHeaderSealer, error)) *MockCryptoSetupGet0RTTSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGet0RTTSealerCall) DoAndReturn(f func() (handshake.LongHeaderSealer, error)) *MockCryptoSetupGet0RTTSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // Get1RTTOpener mocks base method. func (m *MockCryptoSetup) Get1RTTOpener() (handshake.ShortHeaderOpener, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get1RTTOpener") ret0, _ := ret[0].(handshake.ShortHeaderOpener) ret1, _ := ret[1].(error) return ret0, ret1 } // Get1RTTOpener indicates an expected call of Get1RTTOpener. func (mr *MockCryptoSetupMockRecorder) Get1RTTOpener() *MockCryptoSetupGet1RTTOpenerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get1RTTOpener", reflect.TypeOf((*MockCryptoSetup)(nil).Get1RTTOpener)) return &MockCryptoSetupGet1RTTOpenerCall{Call: call} } // MockCryptoSetupGet1RTTOpenerCall wrap *gomock.Call type MockCryptoSetupGet1RTTOpenerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGet1RTTOpenerCall) Return(arg0 handshake.ShortHeaderOpener, arg1 error) *MockCryptoSetupGet1RTTOpenerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGet1RTTOpenerCall) Do(f func() (handshake.ShortHeaderOpener, error)) *MockCryptoSetupGet1RTTOpenerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGet1RTTOpenerCall) DoAndReturn(f func() (handshake.ShortHeaderOpener, error)) *MockCryptoSetupGet1RTTOpenerCall { c.Call = c.Call.DoAndReturn(f) return c } // Get1RTTSealer mocks base method. func (m *MockCryptoSetup) Get1RTTSealer() (handshake.ShortHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get1RTTSealer") ret0, _ := ret[0].(handshake.ShortHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // Get1RTTSealer indicates an expected call of Get1RTTSealer. func (mr *MockCryptoSetupMockRecorder) Get1RTTSealer() *MockCryptoSetupGet1RTTSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get1RTTSealer", reflect.TypeOf((*MockCryptoSetup)(nil).Get1RTTSealer)) return &MockCryptoSetupGet1RTTSealerCall{Call: call} } // MockCryptoSetupGet1RTTSealerCall wrap *gomock.Call type MockCryptoSetupGet1RTTSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGet1RTTSealerCall) Return(arg0 handshake.ShortHeaderSealer, arg1 error) *MockCryptoSetupGet1RTTSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGet1RTTSealerCall) Do(f func() (handshake.ShortHeaderSealer, error)) *MockCryptoSetupGet1RTTSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGet1RTTSealerCall) DoAndReturn(f func() (handshake.ShortHeaderSealer, error)) *MockCryptoSetupGet1RTTSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetHandshakeOpener mocks base method. func (m *MockCryptoSetup) GetHandshakeOpener() (handshake.LongHeaderOpener, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHandshakeOpener") ret0, _ := ret[0].(handshake.LongHeaderOpener) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHandshakeOpener indicates an expected call of GetHandshakeOpener. func (mr *MockCryptoSetupMockRecorder) GetHandshakeOpener() *MockCryptoSetupGetHandshakeOpenerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandshakeOpener", reflect.TypeOf((*MockCryptoSetup)(nil).GetHandshakeOpener)) return &MockCryptoSetupGetHandshakeOpenerCall{Call: call} } // MockCryptoSetupGetHandshakeOpenerCall wrap *gomock.Call type MockCryptoSetupGetHandshakeOpenerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGetHandshakeOpenerCall) Return(arg0 handshake.LongHeaderOpener, arg1 error) *MockCryptoSetupGetHandshakeOpenerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGetHandshakeOpenerCall) Do(f func() (handshake.LongHeaderOpener, error)) *MockCryptoSetupGetHandshakeOpenerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGetHandshakeOpenerCall) DoAndReturn(f func() (handshake.LongHeaderOpener, error)) *MockCryptoSetupGetHandshakeOpenerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetHandshakeSealer mocks base method. func (m *MockCryptoSetup) GetHandshakeSealer() (handshake.LongHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHandshakeSealer") ret0, _ := ret[0].(handshake.LongHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHandshakeSealer indicates an expected call of GetHandshakeSealer. func (mr *MockCryptoSetupMockRecorder) GetHandshakeSealer() *MockCryptoSetupGetHandshakeSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandshakeSealer", reflect.TypeOf((*MockCryptoSetup)(nil).GetHandshakeSealer)) return &MockCryptoSetupGetHandshakeSealerCall{Call: call} } // MockCryptoSetupGetHandshakeSealerCall wrap *gomock.Call type MockCryptoSetupGetHandshakeSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGetHandshakeSealerCall) Return(arg0 handshake.LongHeaderSealer, arg1 error) *MockCryptoSetupGetHandshakeSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGetHandshakeSealerCall) Do(f func() (handshake.LongHeaderSealer, error)) *MockCryptoSetupGetHandshakeSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGetHandshakeSealerCall) DoAndReturn(f func() (handshake.LongHeaderSealer, error)) *MockCryptoSetupGetHandshakeSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetInitialOpener mocks base method. func (m *MockCryptoSetup) GetInitialOpener() (handshake.LongHeaderOpener, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInitialOpener") ret0, _ := ret[0].(handshake.LongHeaderOpener) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInitialOpener indicates an expected call of GetInitialOpener. func (mr *MockCryptoSetupMockRecorder) GetInitialOpener() *MockCryptoSetupGetInitialOpenerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitialOpener", reflect.TypeOf((*MockCryptoSetup)(nil).GetInitialOpener)) return &MockCryptoSetupGetInitialOpenerCall{Call: call} } // MockCryptoSetupGetInitialOpenerCall wrap *gomock.Call type MockCryptoSetupGetInitialOpenerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGetInitialOpenerCall) Return(arg0 handshake.LongHeaderOpener, arg1 error) *MockCryptoSetupGetInitialOpenerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGetInitialOpenerCall) Do(f func() (handshake.LongHeaderOpener, error)) *MockCryptoSetupGetInitialOpenerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGetInitialOpenerCall) DoAndReturn(f func() (handshake.LongHeaderOpener, error)) *MockCryptoSetupGetInitialOpenerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetInitialSealer mocks base method. func (m *MockCryptoSetup) GetInitialSealer() (handshake.LongHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInitialSealer") ret0, _ := ret[0].(handshake.LongHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInitialSealer indicates an expected call of GetInitialSealer. func (mr *MockCryptoSetupMockRecorder) GetInitialSealer() *MockCryptoSetupGetInitialSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitialSealer", reflect.TypeOf((*MockCryptoSetup)(nil).GetInitialSealer)) return &MockCryptoSetupGetInitialSealerCall{Call: call} } // MockCryptoSetupGetInitialSealerCall wrap *gomock.Call type MockCryptoSetupGetInitialSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGetInitialSealerCall) Return(arg0 handshake.LongHeaderSealer, arg1 error) *MockCryptoSetupGetInitialSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGetInitialSealerCall) Do(f func() (handshake.LongHeaderSealer, error)) *MockCryptoSetupGetInitialSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGetInitialSealerCall) DoAndReturn(f func() (handshake.LongHeaderSealer, error)) *MockCryptoSetupGetInitialSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetSessionTicket mocks base method. func (m *MockCryptoSetup) GetSessionTicket() ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetSessionTicket") ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // GetSessionTicket indicates an expected call of GetSessionTicket. func (mr *MockCryptoSetupMockRecorder) GetSessionTicket() *MockCryptoSetupGetSessionTicketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSessionTicket", reflect.TypeOf((*MockCryptoSetup)(nil).GetSessionTicket)) return &MockCryptoSetupGetSessionTicketCall{Call: call} } // MockCryptoSetupGetSessionTicketCall wrap *gomock.Call type MockCryptoSetupGetSessionTicketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupGetSessionTicketCall) Return(arg0 []byte, arg1 error) *MockCryptoSetupGetSessionTicketCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupGetSessionTicketCall) Do(f func() ([]byte, error)) *MockCryptoSetupGetSessionTicketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupGetSessionTicketCall) DoAndReturn(f func() ([]byte, error)) *MockCryptoSetupGetSessionTicketCall { c.Call = c.Call.DoAndReturn(f) return c } // HandleMessage mocks base method. func (m *MockCryptoSetup) HandleMessage(arg0 []byte, arg1 protocol.EncryptionLevel) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleMessage", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // HandleMessage indicates an expected call of HandleMessage. func (mr *MockCryptoSetupMockRecorder) HandleMessage(arg0, arg1 any) *MockCryptoSetupHandleMessageCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockCryptoSetup)(nil).HandleMessage), arg0, arg1) return &MockCryptoSetupHandleMessageCall{Call: call} } // MockCryptoSetupHandleMessageCall wrap *gomock.Call type MockCryptoSetupHandleMessageCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupHandleMessageCall) Return(arg0 error) *MockCryptoSetupHandleMessageCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupHandleMessageCall) Do(f func([]byte, protocol.EncryptionLevel) error) *MockCryptoSetupHandleMessageCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupHandleMessageCall) DoAndReturn(f func([]byte, protocol.EncryptionLevel) error) *MockCryptoSetupHandleMessageCall { c.Call = c.Call.DoAndReturn(f) return c } // NextEvent mocks base method. func (m *MockCryptoSetup) NextEvent() handshake.Event { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NextEvent") ret0, _ := ret[0].(handshake.Event) return ret0 } // NextEvent indicates an expected call of NextEvent. func (mr *MockCryptoSetupMockRecorder) NextEvent() *MockCryptoSetupNextEventCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextEvent", reflect.TypeOf((*MockCryptoSetup)(nil).NextEvent)) return &MockCryptoSetupNextEventCall{Call: call} } // MockCryptoSetupNextEventCall wrap *gomock.Call type MockCryptoSetupNextEventCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupNextEventCall) Return(arg0 handshake.Event) *MockCryptoSetupNextEventCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupNextEventCall) Do(f func() handshake.Event) *MockCryptoSetupNextEventCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupNextEventCall) DoAndReturn(f func() handshake.Event) *MockCryptoSetupNextEventCall { c.Call = c.Call.DoAndReturn(f) return c } // SetHandshakeConfirmed mocks base method. func (m *MockCryptoSetup) SetHandshakeConfirmed() { m.ctrl.T.Helper() m.ctrl.Call(m, "SetHandshakeConfirmed") } // SetHandshakeConfirmed indicates an expected call of SetHandshakeConfirmed. func (mr *MockCryptoSetupMockRecorder) SetHandshakeConfirmed() *MockCryptoSetupSetHandshakeConfirmedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHandshakeConfirmed", reflect.TypeOf((*MockCryptoSetup)(nil).SetHandshakeConfirmed)) return &MockCryptoSetupSetHandshakeConfirmedCall{Call: call} } // MockCryptoSetupSetHandshakeConfirmedCall wrap *gomock.Call type MockCryptoSetupSetHandshakeConfirmedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupSetHandshakeConfirmedCall) Return() *MockCryptoSetupSetHandshakeConfirmedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupSetHandshakeConfirmedCall) Do(f func()) *MockCryptoSetupSetHandshakeConfirmedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupSetHandshakeConfirmedCall) DoAndReturn(f func()) *MockCryptoSetupSetHandshakeConfirmedCall { c.Call = c.Call.DoAndReturn(f) return c } // SetLargest1RTTAcked mocks base method. func (m *MockCryptoSetup) SetLargest1RTTAcked(arg0 protocol.PacketNumber) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetLargest1RTTAcked", arg0) ret0, _ := ret[0].(error) return ret0 } // SetLargest1RTTAcked indicates an expected call of SetLargest1RTTAcked. func (mr *MockCryptoSetupMockRecorder) SetLargest1RTTAcked(arg0 any) *MockCryptoSetupSetLargest1RTTAckedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLargest1RTTAcked", reflect.TypeOf((*MockCryptoSetup)(nil).SetLargest1RTTAcked), arg0) return &MockCryptoSetupSetLargest1RTTAckedCall{Call: call} } // MockCryptoSetupSetLargest1RTTAckedCall wrap *gomock.Call type MockCryptoSetupSetLargest1RTTAckedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupSetLargest1RTTAckedCall) Return(arg0 error) *MockCryptoSetupSetLargest1RTTAckedCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupSetLargest1RTTAckedCall) Do(f func(protocol.PacketNumber) error) *MockCryptoSetupSetLargest1RTTAckedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupSetLargest1RTTAckedCall) DoAndReturn(f func(protocol.PacketNumber) error) *MockCryptoSetupSetLargest1RTTAckedCall { c.Call = c.Call.DoAndReturn(f) return c } // StartHandshake mocks base method. func (m *MockCryptoSetup) StartHandshake(arg0 context.Context) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StartHandshake", arg0) ret0, _ := ret[0].(error) return ret0 } // StartHandshake indicates an expected call of StartHandshake. func (mr *MockCryptoSetupMockRecorder) StartHandshake(arg0 any) *MockCryptoSetupStartHandshakeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartHandshake", reflect.TypeOf((*MockCryptoSetup)(nil).StartHandshake), arg0) return &MockCryptoSetupStartHandshakeCall{Call: call} } // MockCryptoSetupStartHandshakeCall wrap *gomock.Call type MockCryptoSetupStartHandshakeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockCryptoSetupStartHandshakeCall) Return(arg0 error) *MockCryptoSetupStartHandshakeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockCryptoSetupStartHandshakeCall) Do(f func(context.Context) error) *MockCryptoSetupStartHandshakeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockCryptoSetupStartHandshakeCall) DoAndReturn(f func(context.Context) error) *MockCryptoSetupStartHandshakeCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/000077500000000000000000000000001465664453100251655ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/connection_tracer.go000066400000000000000000000103331465664453100312130ustar00rootroot00000000000000//go:build !gomock && !generate package mocklogging import ( "net" "time" "github.com/quic-go/quic-go/internal/mocks/logging/internal" "github.com/quic-go/quic-go/logging" "go.uber.org/mock/gomock" ) type MockConnectionTracer = internal.MockConnectionTracer func NewMockConnectionTracer(ctrl *gomock.Controller) (*logging.ConnectionTracer, *MockConnectionTracer) { t := internal.NewMockConnectionTracer(ctrl) return &logging.ConnectionTracer{ StartedConnection: func(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { t.StartedConnection(local, remote, srcConnID, destConnID) }, NegotiatedVersion: func(chosen logging.Version, clientVersions, serverVersions []logging.Version) { t.NegotiatedVersion(chosen, clientVersions, serverVersions) }, ClosedConnection: func(e error) { t.ClosedConnection(e) }, SentTransportParameters: func(tp *logging.TransportParameters) { t.SentTransportParameters(tp) }, ReceivedTransportParameters: func(tp *logging.TransportParameters) { t.ReceivedTransportParameters(tp) }, RestoredTransportParameters: func(tp *logging.TransportParameters) { t.RestoredTransportParameters(tp) }, SentLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) }, SentShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) }, ReceivedVersionNegotiationPacket: func(dest, src logging.ArbitraryLenConnectionID, versions []logging.Version) { t.ReceivedVersionNegotiationPacket(dest, src, versions) }, ReceivedRetry: func(hdr *logging.Header) { t.ReceivedRetry(hdr) }, ReceivedLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) }, ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) }, BufferedPacket: func(typ logging.PacketType, size logging.ByteCount) { t.BufferedPacket(typ, size) }, DroppedPacket: func(typ logging.PacketType, pn logging.PacketNumber, size logging.ByteCount, reason logging.PacketDropReason) { t.DroppedPacket(typ, pn, size, reason) }, UpdatedMetrics: func(rttStats *logging.RTTStats, cwnd, bytesInFlight logging.ByteCount, packetsInFlight int) { t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) }, AcknowledgedPacket: func(encLevel logging.EncryptionLevel, pn logging.PacketNumber) { t.AcknowledgedPacket(encLevel, pn) }, LostPacket: func(encLevel logging.EncryptionLevel, pn logging.PacketNumber, reason logging.PacketLossReason) { t.LostPacket(encLevel, pn, reason) }, UpdatedMTU: func(mtu logging.ByteCount, done bool) { t.UpdatedMTU(mtu, done) }, UpdatedCongestionState: func(state logging.CongestionState) { t.UpdatedCongestionState(state) }, UpdatedPTOCount: func(value uint32) { t.UpdatedPTOCount(value) }, UpdatedKeyFromTLS: func(encLevel logging.EncryptionLevel, perspective logging.Perspective) { t.UpdatedKeyFromTLS(encLevel, perspective) }, UpdatedKey: func(generation logging.KeyPhase, remote bool) { t.UpdatedKey(generation, remote) }, DroppedEncryptionLevel: func(encLevel logging.EncryptionLevel) { t.DroppedEncryptionLevel(encLevel) }, DroppedKey: func(generation logging.KeyPhase) { t.DroppedKey(generation) }, SetLossTimer: func(typ logging.TimerType, encLevel logging.EncryptionLevel, exp time.Time) { t.SetLossTimer(typ, encLevel, exp) }, LossTimerExpired: func(typ logging.TimerType, encLevel logging.EncryptionLevel) { t.LossTimerExpired(typ, encLevel) }, LossTimerCanceled: func() { t.LossTimerCanceled() }, ECNStateUpdated: func(state logging.ECNState, trigger logging.ECNStateTrigger) { t.ECNStateUpdated(state, trigger) }, ChoseALPN: func(protocol string) { t.ChoseALPN(protocol) }, Close: func() { t.Close() }, Debug: func(name, msg string) { t.Debug(name, msg) }, }, t } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/internal/000077500000000000000000000000001465664453100270015ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/internal/connection_tracer.go000066400000000000000000001314701465664453100330350ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/mocks/logging (interfaces: ConnectionTracer) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package internal -destination internal/connection_tracer.go github.com/quic-go/quic-go/internal/mocks/logging ConnectionTracer // // Package internal is a generated GoMock package. package internal import ( net "net" reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" utils "github.com/quic-go/quic-go/internal/utils" wire "github.com/quic-go/quic-go/internal/wire" logging "github.com/quic-go/quic-go/logging" gomock "go.uber.org/mock/gomock" ) // MockConnectionTracer is a mock of ConnectionTracer interface. type MockConnectionTracer struct { ctrl *gomock.Controller recorder *MockConnectionTracerMockRecorder } // MockConnectionTracerMockRecorder is the mock recorder for MockConnectionTracer. type MockConnectionTracerMockRecorder struct { mock *MockConnectionTracer } // NewMockConnectionTracer creates a new mock instance. func NewMockConnectionTracer(ctrl *gomock.Controller) *MockConnectionTracer { mock := &MockConnectionTracer{ctrl: ctrl} mock.recorder = &MockConnectionTracerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConnectionTracer) EXPECT() *MockConnectionTracerMockRecorder { return m.recorder } // AcknowledgedPacket mocks base method. func (m *MockConnectionTracer) AcknowledgedPacket(arg0 protocol.EncryptionLevel, arg1 protocol.PacketNumber) { m.ctrl.T.Helper() m.ctrl.Call(m, "AcknowledgedPacket", arg0, arg1) } // AcknowledgedPacket indicates an expected call of AcknowledgedPacket. func (mr *MockConnectionTracerMockRecorder) AcknowledgedPacket(arg0, arg1 any) *MockConnectionTracerAcknowledgedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcknowledgedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).AcknowledgedPacket), arg0, arg1) return &MockConnectionTracerAcknowledgedPacketCall{Call: call} } // MockConnectionTracerAcknowledgedPacketCall wrap *gomock.Call type MockConnectionTracerAcknowledgedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerAcknowledgedPacketCall) Return() *MockConnectionTracerAcknowledgedPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerAcknowledgedPacketCall) Do(f func(protocol.EncryptionLevel, protocol.PacketNumber)) *MockConnectionTracerAcknowledgedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerAcknowledgedPacketCall) DoAndReturn(f func(protocol.EncryptionLevel, protocol.PacketNumber)) *MockConnectionTracerAcknowledgedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // BufferedPacket mocks base method. func (m *MockConnectionTracer) BufferedPacket(arg0 logging.PacketType, arg1 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "BufferedPacket", arg0, arg1) } // BufferedPacket indicates an expected call of BufferedPacket. func (mr *MockConnectionTracerMockRecorder) BufferedPacket(arg0, arg1 any) *MockConnectionTracerBufferedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BufferedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).BufferedPacket), arg0, arg1) return &MockConnectionTracerBufferedPacketCall{Call: call} } // MockConnectionTracerBufferedPacketCall wrap *gomock.Call type MockConnectionTracerBufferedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerBufferedPacketCall) Return() *MockConnectionTracerBufferedPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerBufferedPacketCall) Do(f func(logging.PacketType, protocol.ByteCount)) *MockConnectionTracerBufferedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerBufferedPacketCall) DoAndReturn(f func(logging.PacketType, protocol.ByteCount)) *MockConnectionTracerBufferedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // ChoseALPN mocks base method. func (m *MockConnectionTracer) ChoseALPN(arg0 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "ChoseALPN", arg0) } // ChoseALPN indicates an expected call of ChoseALPN. func (mr *MockConnectionTracerMockRecorder) ChoseALPN(arg0 any) *MockConnectionTracerChoseALPNCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChoseALPN", reflect.TypeOf((*MockConnectionTracer)(nil).ChoseALPN), arg0) return &MockConnectionTracerChoseALPNCall{Call: call} } // MockConnectionTracerChoseALPNCall wrap *gomock.Call type MockConnectionTracerChoseALPNCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerChoseALPNCall) Return() *MockConnectionTracerChoseALPNCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerChoseALPNCall) Do(f func(string)) *MockConnectionTracerChoseALPNCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerChoseALPNCall) DoAndReturn(f func(string)) *MockConnectionTracerChoseALPNCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockConnectionTracer) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockConnectionTracerMockRecorder) Close() *MockConnectionTracerCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConnectionTracer)(nil).Close)) return &MockConnectionTracerCloseCall{Call: call} } // MockConnectionTracerCloseCall wrap *gomock.Call type MockConnectionTracerCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerCloseCall) Return() *MockConnectionTracerCloseCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerCloseCall) Do(f func()) *MockConnectionTracerCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerCloseCall) DoAndReturn(f func()) *MockConnectionTracerCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // ClosedConnection mocks base method. func (m *MockConnectionTracer) ClosedConnection(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "ClosedConnection", arg0) } // ClosedConnection indicates an expected call of ClosedConnection. func (mr *MockConnectionTracerMockRecorder) ClosedConnection(arg0 any) *MockConnectionTracerClosedConnectionCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClosedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).ClosedConnection), arg0) return &MockConnectionTracerClosedConnectionCall{Call: call} } // MockConnectionTracerClosedConnectionCall wrap *gomock.Call type MockConnectionTracerClosedConnectionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerClosedConnectionCall) Return() *MockConnectionTracerClosedConnectionCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerClosedConnectionCall) Do(f func(error)) *MockConnectionTracerClosedConnectionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerClosedConnectionCall) DoAndReturn(f func(error)) *MockConnectionTracerClosedConnectionCall { c.Call = c.Call.DoAndReturn(f) return c } // Debug mocks base method. func (m *MockConnectionTracer) Debug(arg0, arg1 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "Debug", arg0, arg1) } // Debug indicates an expected call of Debug. func (mr *MockConnectionTracerMockRecorder) Debug(arg0, arg1 any) *MockConnectionTracerDebugCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockConnectionTracer)(nil).Debug), arg0, arg1) return &MockConnectionTracerDebugCall{Call: call} } // MockConnectionTracerDebugCall wrap *gomock.Call type MockConnectionTracerDebugCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerDebugCall) Return() *MockConnectionTracerDebugCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerDebugCall) Do(f func(string, string)) *MockConnectionTracerDebugCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerDebugCall) DoAndReturn(f func(string, string)) *MockConnectionTracerDebugCall { c.Call = c.Call.DoAndReturn(f) return c } // DroppedEncryptionLevel mocks base method. func (m *MockConnectionTracer) DroppedEncryptionLevel(arg0 protocol.EncryptionLevel) { m.ctrl.T.Helper() m.ctrl.Call(m, "DroppedEncryptionLevel", arg0) } // DroppedEncryptionLevel indicates an expected call of DroppedEncryptionLevel. func (mr *MockConnectionTracerMockRecorder) DroppedEncryptionLevel(arg0 any) *MockConnectionTracerDroppedEncryptionLevelCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedEncryptionLevel", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedEncryptionLevel), arg0) return &MockConnectionTracerDroppedEncryptionLevelCall{Call: call} } // MockConnectionTracerDroppedEncryptionLevelCall wrap *gomock.Call type MockConnectionTracerDroppedEncryptionLevelCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerDroppedEncryptionLevelCall) Return() *MockConnectionTracerDroppedEncryptionLevelCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerDroppedEncryptionLevelCall) Do(f func(protocol.EncryptionLevel)) *MockConnectionTracerDroppedEncryptionLevelCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerDroppedEncryptionLevelCall) DoAndReturn(f func(protocol.EncryptionLevel)) *MockConnectionTracerDroppedEncryptionLevelCall { c.Call = c.Call.DoAndReturn(f) return c } // DroppedKey mocks base method. func (m *MockConnectionTracer) DroppedKey(arg0 protocol.KeyPhase) { m.ctrl.T.Helper() m.ctrl.Call(m, "DroppedKey", arg0) } // DroppedKey indicates an expected call of DroppedKey. func (mr *MockConnectionTracerMockRecorder) DroppedKey(arg0 any) *MockConnectionTracerDroppedKeyCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedKey", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedKey), arg0) return &MockConnectionTracerDroppedKeyCall{Call: call} } // MockConnectionTracerDroppedKeyCall wrap *gomock.Call type MockConnectionTracerDroppedKeyCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerDroppedKeyCall) Return() *MockConnectionTracerDroppedKeyCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerDroppedKeyCall) Do(f func(protocol.KeyPhase)) *MockConnectionTracerDroppedKeyCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerDroppedKeyCall) DoAndReturn(f func(protocol.KeyPhase)) *MockConnectionTracerDroppedKeyCall { c.Call = c.Call.DoAndReturn(f) return c } // DroppedPacket mocks base method. func (m *MockConnectionTracer) DroppedPacket(arg0 logging.PacketType, arg1 protocol.PacketNumber, arg2 protocol.ByteCount, arg3 logging.PacketDropReason) { m.ctrl.T.Helper() m.ctrl.Call(m, "DroppedPacket", arg0, arg1, arg2, arg3) } // DroppedPacket indicates an expected call of DroppedPacket. func (mr *MockConnectionTracerMockRecorder) DroppedPacket(arg0, arg1, arg2, arg3 any) *MockConnectionTracerDroppedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedPacket), arg0, arg1, arg2, arg3) return &MockConnectionTracerDroppedPacketCall{Call: call} } // MockConnectionTracerDroppedPacketCall wrap *gomock.Call type MockConnectionTracerDroppedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerDroppedPacketCall) Return() *MockConnectionTracerDroppedPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerDroppedPacketCall) Do(f func(logging.PacketType, protocol.PacketNumber, protocol.ByteCount, logging.PacketDropReason)) *MockConnectionTracerDroppedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerDroppedPacketCall) DoAndReturn(f func(logging.PacketType, protocol.PacketNumber, protocol.ByteCount, logging.PacketDropReason)) *MockConnectionTracerDroppedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // ECNStateUpdated mocks base method. func (m *MockConnectionTracer) ECNStateUpdated(arg0 logging.ECNState, arg1 logging.ECNStateTrigger) { m.ctrl.T.Helper() m.ctrl.Call(m, "ECNStateUpdated", arg0, arg1) } // ECNStateUpdated indicates an expected call of ECNStateUpdated. func (mr *MockConnectionTracerMockRecorder) ECNStateUpdated(arg0, arg1 any) *MockConnectionTracerECNStateUpdatedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ECNStateUpdated", reflect.TypeOf((*MockConnectionTracer)(nil).ECNStateUpdated), arg0, arg1) return &MockConnectionTracerECNStateUpdatedCall{Call: call} } // MockConnectionTracerECNStateUpdatedCall wrap *gomock.Call type MockConnectionTracerECNStateUpdatedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerECNStateUpdatedCall) Return() *MockConnectionTracerECNStateUpdatedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerECNStateUpdatedCall) Do(f func(logging.ECNState, logging.ECNStateTrigger)) *MockConnectionTracerECNStateUpdatedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerECNStateUpdatedCall) DoAndReturn(f func(logging.ECNState, logging.ECNStateTrigger)) *MockConnectionTracerECNStateUpdatedCall { c.Call = c.Call.DoAndReturn(f) return c } // LossTimerCanceled mocks base method. func (m *MockConnectionTracer) LossTimerCanceled() { m.ctrl.T.Helper() m.ctrl.Call(m, "LossTimerCanceled") } // LossTimerCanceled indicates an expected call of LossTimerCanceled. func (mr *MockConnectionTracerMockRecorder) LossTimerCanceled() *MockConnectionTracerLossTimerCanceledCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LossTimerCanceled", reflect.TypeOf((*MockConnectionTracer)(nil).LossTimerCanceled)) return &MockConnectionTracerLossTimerCanceledCall{Call: call} } // MockConnectionTracerLossTimerCanceledCall wrap *gomock.Call type MockConnectionTracerLossTimerCanceledCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerLossTimerCanceledCall) Return() *MockConnectionTracerLossTimerCanceledCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerLossTimerCanceledCall) Do(f func()) *MockConnectionTracerLossTimerCanceledCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerLossTimerCanceledCall) DoAndReturn(f func()) *MockConnectionTracerLossTimerCanceledCall { c.Call = c.Call.DoAndReturn(f) return c } // LossTimerExpired mocks base method. func (m *MockConnectionTracer) LossTimerExpired(arg0 logging.TimerType, arg1 protocol.EncryptionLevel) { m.ctrl.T.Helper() m.ctrl.Call(m, "LossTimerExpired", arg0, arg1) } // LossTimerExpired indicates an expected call of LossTimerExpired. func (mr *MockConnectionTracerMockRecorder) LossTimerExpired(arg0, arg1 any) *MockConnectionTracerLossTimerExpiredCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LossTimerExpired", reflect.TypeOf((*MockConnectionTracer)(nil).LossTimerExpired), arg0, arg1) return &MockConnectionTracerLossTimerExpiredCall{Call: call} } // MockConnectionTracerLossTimerExpiredCall wrap *gomock.Call type MockConnectionTracerLossTimerExpiredCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerLossTimerExpiredCall) Return() *MockConnectionTracerLossTimerExpiredCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerLossTimerExpiredCall) Do(f func(logging.TimerType, protocol.EncryptionLevel)) *MockConnectionTracerLossTimerExpiredCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerLossTimerExpiredCall) DoAndReturn(f func(logging.TimerType, protocol.EncryptionLevel)) *MockConnectionTracerLossTimerExpiredCall { c.Call = c.Call.DoAndReturn(f) return c } // LostPacket mocks base method. func (m *MockConnectionTracer) LostPacket(arg0 protocol.EncryptionLevel, arg1 protocol.PacketNumber, arg2 logging.PacketLossReason) { m.ctrl.T.Helper() m.ctrl.Call(m, "LostPacket", arg0, arg1, arg2) } // LostPacket indicates an expected call of LostPacket. func (mr *MockConnectionTracerMockRecorder) LostPacket(arg0, arg1, arg2 any) *MockConnectionTracerLostPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LostPacket", reflect.TypeOf((*MockConnectionTracer)(nil).LostPacket), arg0, arg1, arg2) return &MockConnectionTracerLostPacketCall{Call: call} } // MockConnectionTracerLostPacketCall wrap *gomock.Call type MockConnectionTracerLostPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerLostPacketCall) Return() *MockConnectionTracerLostPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerLostPacketCall) Do(f func(protocol.EncryptionLevel, protocol.PacketNumber, logging.PacketLossReason)) *MockConnectionTracerLostPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerLostPacketCall) DoAndReturn(f func(protocol.EncryptionLevel, protocol.PacketNumber, logging.PacketLossReason)) *MockConnectionTracerLostPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // NegotiatedVersion mocks base method. func (m *MockConnectionTracer) NegotiatedVersion(arg0 protocol.Version, arg1, arg2 []protocol.Version) { m.ctrl.T.Helper() m.ctrl.Call(m, "NegotiatedVersion", arg0, arg1, arg2) } // NegotiatedVersion indicates an expected call of NegotiatedVersion. func (mr *MockConnectionTracerMockRecorder) NegotiatedVersion(arg0, arg1, arg2 any) *MockConnectionTracerNegotiatedVersionCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NegotiatedVersion", reflect.TypeOf((*MockConnectionTracer)(nil).NegotiatedVersion), arg0, arg1, arg2) return &MockConnectionTracerNegotiatedVersionCall{Call: call} } // MockConnectionTracerNegotiatedVersionCall wrap *gomock.Call type MockConnectionTracerNegotiatedVersionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerNegotiatedVersionCall) Return() *MockConnectionTracerNegotiatedVersionCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerNegotiatedVersionCall) Do(f func(protocol.Version, []protocol.Version, []protocol.Version)) *MockConnectionTracerNegotiatedVersionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerNegotiatedVersionCall) DoAndReturn(f func(protocol.Version, []protocol.Version, []protocol.Version)) *MockConnectionTracerNegotiatedVersionCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedLongHeaderPacket mocks base method. func (m *MockConnectionTracer) ReceivedLongHeaderPacket(arg0 *wire.ExtendedHeader, arg1 protocol.ByteCount, arg2 protocol.ECN, arg3 []logging.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedLongHeaderPacket", arg0, arg1, arg2, arg3) } // ReceivedLongHeaderPacket indicates an expected call of ReceivedLongHeaderPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedLongHeaderPacket(arg0, arg1, arg2, arg3 any) *MockConnectionTracerReceivedLongHeaderPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedLongHeaderPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedLongHeaderPacket), arg0, arg1, arg2, arg3) return &MockConnectionTracerReceivedLongHeaderPacketCall{Call: call} } // MockConnectionTracerReceivedLongHeaderPacketCall wrap *gomock.Call type MockConnectionTracerReceivedLongHeaderPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerReceivedLongHeaderPacketCall) Return() *MockConnectionTracerReceivedLongHeaderPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerReceivedLongHeaderPacketCall) Do(f func(*wire.ExtendedHeader, protocol.ByteCount, protocol.ECN, []logging.Frame)) *MockConnectionTracerReceivedLongHeaderPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerReceivedLongHeaderPacketCall) DoAndReturn(f func(*wire.ExtendedHeader, protocol.ByteCount, protocol.ECN, []logging.Frame)) *MockConnectionTracerReceivedLongHeaderPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedRetry mocks base method. func (m *MockConnectionTracer) ReceivedRetry(arg0 *wire.Header) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedRetry", arg0) } // ReceivedRetry indicates an expected call of ReceivedRetry. func (mr *MockConnectionTracerMockRecorder) ReceivedRetry(arg0 any) *MockConnectionTracerReceivedRetryCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedRetry", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedRetry), arg0) return &MockConnectionTracerReceivedRetryCall{Call: call} } // MockConnectionTracerReceivedRetryCall wrap *gomock.Call type MockConnectionTracerReceivedRetryCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerReceivedRetryCall) Return() *MockConnectionTracerReceivedRetryCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerReceivedRetryCall) Do(f func(*wire.Header)) *MockConnectionTracerReceivedRetryCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerReceivedRetryCall) DoAndReturn(f func(*wire.Header)) *MockConnectionTracerReceivedRetryCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedShortHeaderPacket mocks base method. func (m *MockConnectionTracer) ReceivedShortHeaderPacket(arg0 *logging.ShortHeader, arg1 protocol.ByteCount, arg2 protocol.ECN, arg3 []logging.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedShortHeaderPacket", arg0, arg1, arg2, arg3) } // ReceivedShortHeaderPacket indicates an expected call of ReceivedShortHeaderPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedShortHeaderPacket(arg0, arg1, arg2, arg3 any) *MockConnectionTracerReceivedShortHeaderPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedShortHeaderPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedShortHeaderPacket), arg0, arg1, arg2, arg3) return &MockConnectionTracerReceivedShortHeaderPacketCall{Call: call} } // MockConnectionTracerReceivedShortHeaderPacketCall wrap *gomock.Call type MockConnectionTracerReceivedShortHeaderPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerReceivedShortHeaderPacketCall) Return() *MockConnectionTracerReceivedShortHeaderPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerReceivedShortHeaderPacketCall) Do(f func(*logging.ShortHeader, protocol.ByteCount, protocol.ECN, []logging.Frame)) *MockConnectionTracerReceivedShortHeaderPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerReceivedShortHeaderPacketCall) DoAndReturn(f func(*logging.ShortHeader, protocol.ByteCount, protocol.ECN, []logging.Frame)) *MockConnectionTracerReceivedShortHeaderPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedTransportParameters mocks base method. func (m *MockConnectionTracer) ReceivedTransportParameters(arg0 *wire.TransportParameters) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedTransportParameters", arg0) } // ReceivedTransportParameters indicates an expected call of ReceivedTransportParameters. func (mr *MockConnectionTracerMockRecorder) ReceivedTransportParameters(arg0 any) *MockConnectionTracerReceivedTransportParametersCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedTransportParameters), arg0) return &MockConnectionTracerReceivedTransportParametersCall{Call: call} } // MockConnectionTracerReceivedTransportParametersCall wrap *gomock.Call type MockConnectionTracerReceivedTransportParametersCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerReceivedTransportParametersCall) Return() *MockConnectionTracerReceivedTransportParametersCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerReceivedTransportParametersCall) Do(f func(*wire.TransportParameters)) *MockConnectionTracerReceivedTransportParametersCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerReceivedTransportParametersCall) DoAndReturn(f func(*wire.TransportParameters)) *MockConnectionTracerReceivedTransportParametersCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceivedVersionNegotiationPacket mocks base method. func (m *MockConnectionTracer) ReceivedVersionNegotiationPacket(arg0, arg1 protocol.ArbitraryLenConnectionID, arg2 []protocol.Version) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedVersionNegotiationPacket", arg0, arg1, arg2) } // ReceivedVersionNegotiationPacket indicates an expected call of ReceivedVersionNegotiationPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedVersionNegotiationPacket(arg0, arg1, arg2 any) *MockConnectionTracerReceivedVersionNegotiationPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedVersionNegotiationPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedVersionNegotiationPacket), arg0, arg1, arg2) return &MockConnectionTracerReceivedVersionNegotiationPacketCall{Call: call} } // MockConnectionTracerReceivedVersionNegotiationPacketCall wrap *gomock.Call type MockConnectionTracerReceivedVersionNegotiationPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerReceivedVersionNegotiationPacketCall) Return() *MockConnectionTracerReceivedVersionNegotiationPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerReceivedVersionNegotiationPacketCall) Do(f func(protocol.ArbitraryLenConnectionID, protocol.ArbitraryLenConnectionID, []protocol.Version)) *MockConnectionTracerReceivedVersionNegotiationPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerReceivedVersionNegotiationPacketCall) DoAndReturn(f func(protocol.ArbitraryLenConnectionID, protocol.ArbitraryLenConnectionID, []protocol.Version)) *MockConnectionTracerReceivedVersionNegotiationPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // RestoredTransportParameters mocks base method. func (m *MockConnectionTracer) RestoredTransportParameters(arg0 *wire.TransportParameters) { m.ctrl.T.Helper() m.ctrl.Call(m, "RestoredTransportParameters", arg0) } // RestoredTransportParameters indicates an expected call of RestoredTransportParameters. func (mr *MockConnectionTracerMockRecorder) RestoredTransportParameters(arg0 any) *MockConnectionTracerRestoredTransportParametersCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoredTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).RestoredTransportParameters), arg0) return &MockConnectionTracerRestoredTransportParametersCall{Call: call} } // MockConnectionTracerRestoredTransportParametersCall wrap *gomock.Call type MockConnectionTracerRestoredTransportParametersCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerRestoredTransportParametersCall) Return() *MockConnectionTracerRestoredTransportParametersCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerRestoredTransportParametersCall) Do(f func(*wire.TransportParameters)) *MockConnectionTracerRestoredTransportParametersCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerRestoredTransportParametersCall) DoAndReturn(f func(*wire.TransportParameters)) *MockConnectionTracerRestoredTransportParametersCall { c.Call = c.Call.DoAndReturn(f) return c } // SentLongHeaderPacket mocks base method. func (m *MockConnectionTracer) SentLongHeaderPacket(arg0 *wire.ExtendedHeader, arg1 protocol.ByteCount, arg2 protocol.ECN, arg3 *wire.AckFrame, arg4 []logging.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentLongHeaderPacket", arg0, arg1, arg2, arg3, arg4) } // SentLongHeaderPacket indicates an expected call of SentLongHeaderPacket. func (mr *MockConnectionTracerMockRecorder) SentLongHeaderPacket(arg0, arg1, arg2, arg3, arg4 any) *MockConnectionTracerSentLongHeaderPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentLongHeaderPacket", reflect.TypeOf((*MockConnectionTracer)(nil).SentLongHeaderPacket), arg0, arg1, arg2, arg3, arg4) return &MockConnectionTracerSentLongHeaderPacketCall{Call: call} } // MockConnectionTracerSentLongHeaderPacketCall wrap *gomock.Call type MockConnectionTracerSentLongHeaderPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerSentLongHeaderPacketCall) Return() *MockConnectionTracerSentLongHeaderPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerSentLongHeaderPacketCall) Do(f func(*wire.ExtendedHeader, protocol.ByteCount, protocol.ECN, *wire.AckFrame, []logging.Frame)) *MockConnectionTracerSentLongHeaderPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerSentLongHeaderPacketCall) DoAndReturn(f func(*wire.ExtendedHeader, protocol.ByteCount, protocol.ECN, *wire.AckFrame, []logging.Frame)) *MockConnectionTracerSentLongHeaderPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SentShortHeaderPacket mocks base method. func (m *MockConnectionTracer) SentShortHeaderPacket(arg0 *logging.ShortHeader, arg1 protocol.ByteCount, arg2 protocol.ECN, arg3 *wire.AckFrame, arg4 []logging.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentShortHeaderPacket", arg0, arg1, arg2, arg3, arg4) } // SentShortHeaderPacket indicates an expected call of SentShortHeaderPacket. func (mr *MockConnectionTracerMockRecorder) SentShortHeaderPacket(arg0, arg1, arg2, arg3, arg4 any) *MockConnectionTracerSentShortHeaderPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentShortHeaderPacket", reflect.TypeOf((*MockConnectionTracer)(nil).SentShortHeaderPacket), arg0, arg1, arg2, arg3, arg4) return &MockConnectionTracerSentShortHeaderPacketCall{Call: call} } // MockConnectionTracerSentShortHeaderPacketCall wrap *gomock.Call type MockConnectionTracerSentShortHeaderPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerSentShortHeaderPacketCall) Return() *MockConnectionTracerSentShortHeaderPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerSentShortHeaderPacketCall) Do(f func(*logging.ShortHeader, protocol.ByteCount, protocol.ECN, *wire.AckFrame, []logging.Frame)) *MockConnectionTracerSentShortHeaderPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerSentShortHeaderPacketCall) DoAndReturn(f func(*logging.ShortHeader, protocol.ByteCount, protocol.ECN, *wire.AckFrame, []logging.Frame)) *MockConnectionTracerSentShortHeaderPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SentTransportParameters mocks base method. func (m *MockConnectionTracer) SentTransportParameters(arg0 *wire.TransportParameters) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentTransportParameters", arg0) } // SentTransportParameters indicates an expected call of SentTransportParameters. func (mr *MockConnectionTracerMockRecorder) SentTransportParameters(arg0 any) *MockConnectionTracerSentTransportParametersCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).SentTransportParameters), arg0) return &MockConnectionTracerSentTransportParametersCall{Call: call} } // MockConnectionTracerSentTransportParametersCall wrap *gomock.Call type MockConnectionTracerSentTransportParametersCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerSentTransportParametersCall) Return() *MockConnectionTracerSentTransportParametersCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerSentTransportParametersCall) Do(f func(*wire.TransportParameters)) *MockConnectionTracerSentTransportParametersCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerSentTransportParametersCall) DoAndReturn(f func(*wire.TransportParameters)) *MockConnectionTracerSentTransportParametersCall { c.Call = c.Call.DoAndReturn(f) return c } // SetLossTimer mocks base method. func (m *MockConnectionTracer) SetLossTimer(arg0 logging.TimerType, arg1 protocol.EncryptionLevel, arg2 time.Time) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetLossTimer", arg0, arg1, arg2) } // SetLossTimer indicates an expected call of SetLossTimer. func (mr *MockConnectionTracerMockRecorder) SetLossTimer(arg0, arg1, arg2 any) *MockConnectionTracerSetLossTimerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLossTimer", reflect.TypeOf((*MockConnectionTracer)(nil).SetLossTimer), arg0, arg1, arg2) return &MockConnectionTracerSetLossTimerCall{Call: call} } // MockConnectionTracerSetLossTimerCall wrap *gomock.Call type MockConnectionTracerSetLossTimerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerSetLossTimerCall) Return() *MockConnectionTracerSetLossTimerCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerSetLossTimerCall) Do(f func(logging.TimerType, protocol.EncryptionLevel, time.Time)) *MockConnectionTracerSetLossTimerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerSetLossTimerCall) DoAndReturn(f func(logging.TimerType, protocol.EncryptionLevel, time.Time)) *MockConnectionTracerSetLossTimerCall { c.Call = c.Call.DoAndReturn(f) return c } // StartedConnection mocks base method. func (m *MockConnectionTracer) StartedConnection(arg0, arg1 net.Addr, arg2, arg3 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "StartedConnection", arg0, arg1, arg2, arg3) } // StartedConnection indicates an expected call of StartedConnection. func (mr *MockConnectionTracerMockRecorder) StartedConnection(arg0, arg1, arg2, arg3 any) *MockConnectionTracerStartedConnectionCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).StartedConnection), arg0, arg1, arg2, arg3) return &MockConnectionTracerStartedConnectionCall{Call: call} } // MockConnectionTracerStartedConnectionCall wrap *gomock.Call type MockConnectionTracerStartedConnectionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerStartedConnectionCall) Return() *MockConnectionTracerStartedConnectionCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerStartedConnectionCall) Do(f func(net.Addr, net.Addr, protocol.ConnectionID, protocol.ConnectionID)) *MockConnectionTracerStartedConnectionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerStartedConnectionCall) DoAndReturn(f func(net.Addr, net.Addr, protocol.ConnectionID, protocol.ConnectionID)) *MockConnectionTracerStartedConnectionCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdatedCongestionState mocks base method. func (m *MockConnectionTracer) UpdatedCongestionState(arg0 logging.CongestionState) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedCongestionState", arg0) } // UpdatedCongestionState indicates an expected call of UpdatedCongestionState. func (mr *MockConnectionTracerMockRecorder) UpdatedCongestionState(arg0 any) *MockConnectionTracerUpdatedCongestionStateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedCongestionState", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedCongestionState), arg0) return &MockConnectionTracerUpdatedCongestionStateCall{Call: call} } // MockConnectionTracerUpdatedCongestionStateCall wrap *gomock.Call type MockConnectionTracerUpdatedCongestionStateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerUpdatedCongestionStateCall) Return() *MockConnectionTracerUpdatedCongestionStateCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerUpdatedCongestionStateCall) Do(f func(logging.CongestionState)) *MockConnectionTracerUpdatedCongestionStateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerUpdatedCongestionStateCall) DoAndReturn(f func(logging.CongestionState)) *MockConnectionTracerUpdatedCongestionStateCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdatedKey mocks base method. func (m *MockConnectionTracer) UpdatedKey(arg0 protocol.KeyPhase, arg1 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedKey", arg0, arg1) } // UpdatedKey indicates an expected call of UpdatedKey. func (mr *MockConnectionTracerMockRecorder) UpdatedKey(arg0, arg1 any) *MockConnectionTracerUpdatedKeyCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedKey", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedKey), arg0, arg1) return &MockConnectionTracerUpdatedKeyCall{Call: call} } // MockConnectionTracerUpdatedKeyCall wrap *gomock.Call type MockConnectionTracerUpdatedKeyCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerUpdatedKeyCall) Return() *MockConnectionTracerUpdatedKeyCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerUpdatedKeyCall) Do(f func(protocol.KeyPhase, bool)) *MockConnectionTracerUpdatedKeyCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerUpdatedKeyCall) DoAndReturn(f func(protocol.KeyPhase, bool)) *MockConnectionTracerUpdatedKeyCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdatedKeyFromTLS mocks base method. func (m *MockConnectionTracer) UpdatedKeyFromTLS(arg0 protocol.EncryptionLevel, arg1 protocol.Perspective) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedKeyFromTLS", arg0, arg1) } // UpdatedKeyFromTLS indicates an expected call of UpdatedKeyFromTLS. func (mr *MockConnectionTracerMockRecorder) UpdatedKeyFromTLS(arg0, arg1 any) *MockConnectionTracerUpdatedKeyFromTLSCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedKeyFromTLS", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedKeyFromTLS), arg0, arg1) return &MockConnectionTracerUpdatedKeyFromTLSCall{Call: call} } // MockConnectionTracerUpdatedKeyFromTLSCall wrap *gomock.Call type MockConnectionTracerUpdatedKeyFromTLSCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerUpdatedKeyFromTLSCall) Return() *MockConnectionTracerUpdatedKeyFromTLSCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerUpdatedKeyFromTLSCall) Do(f func(protocol.EncryptionLevel, protocol.Perspective)) *MockConnectionTracerUpdatedKeyFromTLSCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerUpdatedKeyFromTLSCall) DoAndReturn(f func(protocol.EncryptionLevel, protocol.Perspective)) *MockConnectionTracerUpdatedKeyFromTLSCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdatedMTU mocks base method. func (m *MockConnectionTracer) UpdatedMTU(arg0 protocol.ByteCount, arg1 bool) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedMTU", arg0, arg1) } // UpdatedMTU indicates an expected call of UpdatedMTU. func (mr *MockConnectionTracerMockRecorder) UpdatedMTU(arg0, arg1 any) *MockConnectionTracerUpdatedMTUCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedMTU", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedMTU), arg0, arg1) return &MockConnectionTracerUpdatedMTUCall{Call: call} } // MockConnectionTracerUpdatedMTUCall wrap *gomock.Call type MockConnectionTracerUpdatedMTUCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerUpdatedMTUCall) Return() *MockConnectionTracerUpdatedMTUCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerUpdatedMTUCall) Do(f func(protocol.ByteCount, bool)) *MockConnectionTracerUpdatedMTUCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerUpdatedMTUCall) DoAndReturn(f func(protocol.ByteCount, bool)) *MockConnectionTracerUpdatedMTUCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdatedMetrics mocks base method. func (m *MockConnectionTracer) UpdatedMetrics(arg0 *utils.RTTStats, arg1, arg2 protocol.ByteCount, arg3 int) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedMetrics", arg0, arg1, arg2, arg3) } // UpdatedMetrics indicates an expected call of UpdatedMetrics. func (mr *MockConnectionTracerMockRecorder) UpdatedMetrics(arg0, arg1, arg2, arg3 any) *MockConnectionTracerUpdatedMetricsCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedMetrics", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedMetrics), arg0, arg1, arg2, arg3) return &MockConnectionTracerUpdatedMetricsCall{Call: call} } // MockConnectionTracerUpdatedMetricsCall wrap *gomock.Call type MockConnectionTracerUpdatedMetricsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerUpdatedMetricsCall) Return() *MockConnectionTracerUpdatedMetricsCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerUpdatedMetricsCall) Do(f func(*utils.RTTStats, protocol.ByteCount, protocol.ByteCount, int)) *MockConnectionTracerUpdatedMetricsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerUpdatedMetricsCall) DoAndReturn(f func(*utils.RTTStats, protocol.ByteCount, protocol.ByteCount, int)) *MockConnectionTracerUpdatedMetricsCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdatedPTOCount mocks base method. func (m *MockConnectionTracer) UpdatedPTOCount(arg0 uint32) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedPTOCount", arg0) } // UpdatedPTOCount indicates an expected call of UpdatedPTOCount. func (mr *MockConnectionTracerMockRecorder) UpdatedPTOCount(arg0 any) *MockConnectionTracerUpdatedPTOCountCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedPTOCount", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedPTOCount), arg0) return &MockConnectionTracerUpdatedPTOCountCall{Call: call} } // MockConnectionTracerUpdatedPTOCountCall wrap *gomock.Call type MockConnectionTracerUpdatedPTOCountCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnectionTracerUpdatedPTOCountCall) Return() *MockConnectionTracerUpdatedPTOCountCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnectionTracerUpdatedPTOCountCall) Do(f func(uint32)) *MockConnectionTracerUpdatedPTOCountCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnectionTracerUpdatedPTOCountCall) DoAndReturn(f func(uint32)) *MockConnectionTracerUpdatedPTOCountCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/internal/tracer.go000066400000000000000000000165351465664453100306220ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/mocks/logging (interfaces: Tracer) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package internal -destination internal/tracer.go github.com/quic-go/quic-go/internal/mocks/logging Tracer // // Package internal is a generated GoMock package. package internal import ( net "net" reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" wire "github.com/quic-go/quic-go/internal/wire" logging "github.com/quic-go/quic-go/logging" gomock "go.uber.org/mock/gomock" ) // MockTracer is a mock of Tracer interface. type MockTracer struct { ctrl *gomock.Controller recorder *MockTracerMockRecorder } // MockTracerMockRecorder is the mock recorder for MockTracer. type MockTracerMockRecorder struct { mock *MockTracer } // NewMockTracer creates a new mock instance. func NewMockTracer(ctrl *gomock.Controller) *MockTracer { mock := &MockTracer{ctrl: ctrl} mock.recorder = &MockTracerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTracer) EXPECT() *MockTracerMockRecorder { return m.recorder } // Close mocks base method. func (m *MockTracer) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockTracerMockRecorder) Close() *MockTracerCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockTracer)(nil).Close)) return &MockTracerCloseCall{Call: call} } // MockTracerCloseCall wrap *gomock.Call type MockTracerCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockTracerCloseCall) Return() *MockTracerCloseCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockTracerCloseCall) Do(f func()) *MockTracerCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockTracerCloseCall) DoAndReturn(f func()) *MockTracerCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // Debug mocks base method. func (m *MockTracer) Debug(arg0, arg1 string) { m.ctrl.T.Helper() m.ctrl.Call(m, "Debug", arg0, arg1) } // Debug indicates an expected call of Debug. func (mr *MockTracerMockRecorder) Debug(arg0, arg1 any) *MockTracerDebugCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockTracer)(nil).Debug), arg0, arg1) return &MockTracerDebugCall{Call: call} } // MockTracerDebugCall wrap *gomock.Call type MockTracerDebugCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockTracerDebugCall) Return() *MockTracerDebugCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockTracerDebugCall) Do(f func(string, string)) *MockTracerDebugCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockTracerDebugCall) DoAndReturn(f func(string, string)) *MockTracerDebugCall { c.Call = c.Call.DoAndReturn(f) return c } // DroppedPacket mocks base method. func (m *MockTracer) DroppedPacket(arg0 net.Addr, arg1 logging.PacketType, arg2 protocol.ByteCount, arg3 logging.PacketDropReason) { m.ctrl.T.Helper() m.ctrl.Call(m, "DroppedPacket", arg0, arg1, arg2, arg3) } // DroppedPacket indicates an expected call of DroppedPacket. func (mr *MockTracerMockRecorder) DroppedPacket(arg0, arg1, arg2, arg3 any) *MockTracerDroppedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedPacket", reflect.TypeOf((*MockTracer)(nil).DroppedPacket), arg0, arg1, arg2, arg3) return &MockTracerDroppedPacketCall{Call: call} } // MockTracerDroppedPacketCall wrap *gomock.Call type MockTracerDroppedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockTracerDroppedPacketCall) Return() *MockTracerDroppedPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockTracerDroppedPacketCall) Do(f func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason)) *MockTracerDroppedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockTracerDroppedPacketCall) DoAndReturn(f func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason)) *MockTracerDroppedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SentPacket mocks base method. func (m *MockTracer) SentPacket(arg0 net.Addr, arg1 *wire.Header, arg2 protocol.ByteCount, arg3 []logging.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentPacket", arg0, arg1, arg2, arg3) } // SentPacket indicates an expected call of SentPacket. func (mr *MockTracerMockRecorder) SentPacket(arg0, arg1, arg2, arg3 any) *MockTracerSentPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockTracer)(nil).SentPacket), arg0, arg1, arg2, arg3) return &MockTracerSentPacketCall{Call: call} } // MockTracerSentPacketCall wrap *gomock.Call type MockTracerSentPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockTracerSentPacketCall) Return() *MockTracerSentPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockTracerSentPacketCall) Do(f func(net.Addr, *wire.Header, protocol.ByteCount, []logging.Frame)) *MockTracerSentPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockTracerSentPacketCall) DoAndReturn(f func(net.Addr, *wire.Header, protocol.ByteCount, []logging.Frame)) *MockTracerSentPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SentVersionNegotiationPacket mocks base method. func (m *MockTracer) SentVersionNegotiationPacket(arg0 net.Addr, arg1, arg2 protocol.ArbitraryLenConnectionID, arg3 []protocol.Version) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentVersionNegotiationPacket", arg0, arg1, arg2, arg3) } // SentVersionNegotiationPacket indicates an expected call of SentVersionNegotiationPacket. func (mr *MockTracerMockRecorder) SentVersionNegotiationPacket(arg0, arg1, arg2, arg3 any) *MockTracerSentVersionNegotiationPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentVersionNegotiationPacket", reflect.TypeOf((*MockTracer)(nil).SentVersionNegotiationPacket), arg0, arg1, arg2, arg3) return &MockTracerSentVersionNegotiationPacketCall{Call: call} } // MockTracerSentVersionNegotiationPacketCall wrap *gomock.Call type MockTracerSentVersionNegotiationPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockTracerSentVersionNegotiationPacketCall) Return() *MockTracerSentVersionNegotiationPacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockTracerSentVersionNegotiationPacketCall) Do(f func(net.Addr, protocol.ArbitraryLenConnectionID, protocol.ArbitraryLenConnectionID, []protocol.Version)) *MockTracerSentVersionNegotiationPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockTracerSentVersionNegotiationPacketCall) DoAndReturn(f func(net.Addr, protocol.ArbitraryLenConnectionID, protocol.ArbitraryLenConnectionID, []protocol.Version)) *MockTracerSentVersionNegotiationPacketCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/mockgen.go000066400000000000000000000056061465664453100271460ustar00rootroot00000000000000//go:build gomock || generate package mocklogging import ( "net" "time" "github.com/quic-go/quic-go/logging" ) //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package internal -destination internal/tracer.go github.com/quic-go/quic-go/internal/mocks/logging Tracer" type Tracer interface { SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) SentVersionNegotiationPacket(_ net.Addr, dest, src logging.ArbitraryLenConnectionID, _ []logging.Version) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) Debug(name, msg string) Close() } //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package internal -destination internal/connection_tracer.go github.com/quic-go/quic-go/internal/mocks/logging ConnectionTracer" type ConnectionTracer interface { StartedConnection(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) NegotiatedVersion(chosen logging.Version, clientVersions, serverVersions []logging.Version) ClosedConnection(error) SentTransportParameters(*logging.TransportParameters) ReceivedTransportParameters(*logging.TransportParameters) RestoredTransportParameters(parameters *logging.TransportParameters) // for 0-RTT SentLongHeaderPacket(*logging.ExtendedHeader, logging.ByteCount, logging.ECN, *logging.AckFrame, []logging.Frame) SentShortHeaderPacket(*logging.ShortHeader, logging.ByteCount, logging.ECN, *logging.AckFrame, []logging.Frame) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, _ []logging.Version) ReceivedRetry(*logging.Header) ReceivedLongHeaderPacket(*logging.ExtendedHeader, logging.ByteCount, logging.ECN, []logging.Frame) ReceivedShortHeaderPacket(*logging.ShortHeader, logging.ByteCount, logging.ECN, []logging.Frame) BufferedPacket(logging.PacketType, logging.ByteCount) DroppedPacket(logging.PacketType, logging.PacketNumber, logging.ByteCount, logging.PacketDropReason) UpdatedMTU(mtu logging.ByteCount, done bool) UpdatedMetrics(rttStats *logging.RTTStats, cwnd, bytesInFlight logging.ByteCount, packetsInFlight int) AcknowledgedPacket(logging.EncryptionLevel, logging.PacketNumber) LostPacket(logging.EncryptionLevel, logging.PacketNumber, logging.PacketLossReason) UpdatedCongestionState(logging.CongestionState) UpdatedPTOCount(value uint32) UpdatedKeyFromTLS(logging.EncryptionLevel, logging.Perspective) UpdatedKey(generation logging.KeyPhase, remote bool) DroppedEncryptionLevel(logging.EncryptionLevel) DroppedKey(generation logging.KeyPhase) SetLossTimer(logging.TimerType, logging.EncryptionLevel, time.Time) LossTimerExpired(logging.TimerType, logging.EncryptionLevel) LossTimerCanceled() ECNStateUpdated(state logging.ECNState, trigger logging.ECNStateTrigger) ChoseALPN(protocol string) // Close is called when the connection is closed. Close() Debug(name, msg string) } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/logging/tracer.go000066400000000000000000000017571465664453100270060ustar00rootroot00000000000000//go:build !gomock && !generate package mocklogging import ( "net" "github.com/quic-go/quic-go/internal/mocks/logging/internal" "github.com/quic-go/quic-go/logging" "go.uber.org/mock/gomock" ) type MockTracer = internal.MockTracer func NewMockTracer(ctrl *gomock.Controller) (*logging.Tracer, *MockTracer) { t := internal.NewMockTracer(ctrl) return &logging.Tracer{ SentPacket: func(remote net.Addr, hdr *logging.Header, size logging.ByteCount, frames []logging.Frame) { t.SentPacket(remote, hdr, size, frames) }, SentVersionNegotiationPacket: func(remote net.Addr, dest, src logging.ArbitraryLenConnectionID, versions []logging.Version) { t.SentVersionNegotiationPacket(remote, dest, src, versions) }, DroppedPacket: func(remote net.Addr, typ logging.PacketType, size logging.ByteCount, reason logging.PacketDropReason) { t.DroppedPacket(remote, typ, size, reason) }, Debug: func(name, msg string) { t.Debug(name, msg) }, Close: func() { t.Close() }, }, t } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/long_header_opener.go000066400000000000000000000130371465664453100277110ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/handshake (interfaces: LongHeaderOpener) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination long_header_opener.go github.com/quic-go/quic-go/internal/handshake LongHeaderOpener // // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockLongHeaderOpener is a mock of LongHeaderOpener interface. type MockLongHeaderOpener struct { ctrl *gomock.Controller recorder *MockLongHeaderOpenerMockRecorder } // MockLongHeaderOpenerMockRecorder is the mock recorder for MockLongHeaderOpener. type MockLongHeaderOpenerMockRecorder struct { mock *MockLongHeaderOpener } // NewMockLongHeaderOpener creates a new mock instance. func NewMockLongHeaderOpener(ctrl *gomock.Controller) *MockLongHeaderOpener { mock := &MockLongHeaderOpener{ctrl: ctrl} mock.recorder = &MockLongHeaderOpenerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockLongHeaderOpener) EXPECT() *MockLongHeaderOpenerMockRecorder { return m.recorder } // DecodePacketNumber mocks base method. func (m *MockLongHeaderOpener) DecodePacketNumber(arg0 protocol.PacketNumber, arg1 protocol.PacketNumberLen) protocol.PacketNumber { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DecodePacketNumber", arg0, arg1) ret0, _ := ret[0].(protocol.PacketNumber) return ret0 } // DecodePacketNumber indicates an expected call of DecodePacketNumber. func (mr *MockLongHeaderOpenerMockRecorder) DecodePacketNumber(arg0, arg1 any) *MockLongHeaderOpenerDecodePacketNumberCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodePacketNumber", reflect.TypeOf((*MockLongHeaderOpener)(nil).DecodePacketNumber), arg0, arg1) return &MockLongHeaderOpenerDecodePacketNumberCall{Call: call} } // MockLongHeaderOpenerDecodePacketNumberCall wrap *gomock.Call type MockLongHeaderOpenerDecodePacketNumberCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockLongHeaderOpenerDecodePacketNumberCall) Return(arg0 protocol.PacketNumber) *MockLongHeaderOpenerDecodePacketNumberCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockLongHeaderOpenerDecodePacketNumberCall) Do(f func(protocol.PacketNumber, protocol.PacketNumberLen) protocol.PacketNumber) *MockLongHeaderOpenerDecodePacketNumberCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockLongHeaderOpenerDecodePacketNumberCall) DoAndReturn(f func(protocol.PacketNumber, protocol.PacketNumberLen) protocol.PacketNumber) *MockLongHeaderOpenerDecodePacketNumberCall { c.Call = c.Call.DoAndReturn(f) return c } // DecryptHeader mocks base method. func (m *MockLongHeaderOpener) DecryptHeader(arg0 []byte, arg1 *byte, arg2 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "DecryptHeader", arg0, arg1, arg2) } // DecryptHeader indicates an expected call of DecryptHeader. func (mr *MockLongHeaderOpenerMockRecorder) DecryptHeader(arg0, arg1, arg2 any) *MockLongHeaderOpenerDecryptHeaderCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptHeader", reflect.TypeOf((*MockLongHeaderOpener)(nil).DecryptHeader), arg0, arg1, arg2) return &MockLongHeaderOpenerDecryptHeaderCall{Call: call} } // MockLongHeaderOpenerDecryptHeaderCall wrap *gomock.Call type MockLongHeaderOpenerDecryptHeaderCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockLongHeaderOpenerDecryptHeaderCall) Return() *MockLongHeaderOpenerDecryptHeaderCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockLongHeaderOpenerDecryptHeaderCall) Do(f func([]byte, *byte, []byte)) *MockLongHeaderOpenerDecryptHeaderCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockLongHeaderOpenerDecryptHeaderCall) DoAndReturn(f func([]byte, *byte, []byte)) *MockLongHeaderOpenerDecryptHeaderCall { c.Call = c.Call.DoAndReturn(f) return c } // Open mocks base method. func (m *MockLongHeaderOpener) Open(arg0, arg1 []byte, arg2 protocol.PacketNumber, arg3 []byte) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Open", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // Open indicates an expected call of Open. func (mr *MockLongHeaderOpenerMockRecorder) Open(arg0, arg1, arg2, arg3 any) *MockLongHeaderOpenerOpenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockLongHeaderOpener)(nil).Open), arg0, arg1, arg2, arg3) return &MockLongHeaderOpenerOpenCall{Call: call} } // MockLongHeaderOpenerOpenCall wrap *gomock.Call type MockLongHeaderOpenerOpenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockLongHeaderOpenerOpenCall) Return(arg0 []byte, arg1 error) *MockLongHeaderOpenerOpenCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockLongHeaderOpenerOpenCall) Do(f func([]byte, []byte, protocol.PacketNumber, []byte) ([]byte, error)) *MockLongHeaderOpenerOpenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockLongHeaderOpenerOpenCall) DoAndReturn(f func([]byte, []byte, protocol.PacketNumber, []byte) ([]byte, error)) *MockLongHeaderOpenerOpenCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/mockgen.go000066400000000000000000000053511465664453100255150ustar00rootroot00000000000000//go:build gomock || generate package mocks //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mockquic -destination quic/stream.go github.com/quic-go/quic-go Stream" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mockquic -destination quic/early_conn_tmp.go github.com/quic-go/quic-go EarlyConnection && sed 's/qtls.ConnectionState/quic.ConnectionState/g' quic/early_conn_tmp.go > quic/early_conn.go && rm quic/early_conn_tmp.go && go run golang.org/x/tools/cmd/goimports -w quic/early_conn.go" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination short_header_sealer.go github.com/quic-go/quic-go/internal/handshake ShortHeaderSealer" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination short_header_opener.go github.com/quic-go/quic-go/internal/handshake ShortHeaderOpener" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination long_header_opener.go github.com/quic-go/quic-go/internal/handshake LongHeaderOpener" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination crypto_setup_tmp.go github.com/quic-go/quic-go/internal/handshake CryptoSetup && sed -E 's~github.com/quic-go/qtls[[:alnum:]_-]*~github.com/quic-go/quic-go/internal/qtls~g; s~qtls.ConnectionStateWith0RTT~qtls.ConnectionState~g' crypto_setup_tmp.go > crypto_setup.go && rm crypto_setup_tmp.go && go run golang.org/x/tools/cmd/goimports -w crypto_setup.go" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination stream_flow_controller.go github.com/quic-go/quic-go/internal/flowcontrol StreamFlowController" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination congestion.go github.com/quic-go/quic-go/internal/congestion SendAlgorithmWithDebugInfos" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mocks -destination connection_flow_controller.go github.com/quic-go/quic-go/internal/flowcontrol ConnectionFlowController" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mockackhandler -destination ackhandler/sent_packet_handler.go github.com/quic-go/quic-go/internal/ackhandler SentPacketHandler" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package mockackhandler -destination ackhandler/received_packet_handler.go github.com/quic-go/quic-go/internal/ackhandler ReceivedPacketHandler" golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/quic/000077500000000000000000000000001465664453100245005ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/quic/early_conn.go000066400000000000000000000527321465664453100271710ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: EarlyConnection) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mockquic -destination quic/early_conn_tmp.go github.com/quic-go/quic-go EarlyConnection // // Package mockquic is a generated GoMock package. package mockquic import ( context "context" net "net" reflect "reflect" quic "github.com/quic-go/quic-go" qerr "github.com/quic-go/quic-go/internal/qerr" gomock "go.uber.org/mock/gomock" ) // MockEarlyConnection is a mock of EarlyConnection interface. type MockEarlyConnection struct { ctrl *gomock.Controller recorder *MockEarlyConnectionMockRecorder } // MockEarlyConnectionMockRecorder is the mock recorder for MockEarlyConnection. type MockEarlyConnectionMockRecorder struct { mock *MockEarlyConnection } // NewMockEarlyConnection creates a new mock instance. func NewMockEarlyConnection(ctrl *gomock.Controller) *MockEarlyConnection { mock := &MockEarlyConnection{ctrl: ctrl} mock.recorder = &MockEarlyConnectionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEarlyConnection) EXPECT() *MockEarlyConnectionMockRecorder { return m.recorder } // AcceptStream mocks base method. func (m *MockEarlyConnection) AcceptStream(arg0 context.Context) (quic.Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AcceptStream", arg0) ret0, _ := ret[0].(quic.Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // AcceptStream indicates an expected call of AcceptStream. func (mr *MockEarlyConnectionMockRecorder) AcceptStream(arg0 any) *MockEarlyConnectionAcceptStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptStream", reflect.TypeOf((*MockEarlyConnection)(nil).AcceptStream), arg0) return &MockEarlyConnectionAcceptStreamCall{Call: call} } // MockEarlyConnectionAcceptStreamCall wrap *gomock.Call type MockEarlyConnectionAcceptStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionAcceptStreamCall) Return(arg0 quic.Stream, arg1 error) *MockEarlyConnectionAcceptStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionAcceptStreamCall) Do(f func(context.Context) (quic.Stream, error)) *MockEarlyConnectionAcceptStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionAcceptStreamCall) DoAndReturn(f func(context.Context) (quic.Stream, error)) *MockEarlyConnectionAcceptStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // AcceptUniStream mocks base method. func (m *MockEarlyConnection) AcceptUniStream(arg0 context.Context) (quic.ReceiveStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AcceptUniStream", arg0) ret0, _ := ret[0].(quic.ReceiveStream) ret1, _ := ret[1].(error) return ret0, ret1 } // AcceptUniStream indicates an expected call of AcceptUniStream. func (mr *MockEarlyConnectionMockRecorder) AcceptUniStream(arg0 any) *MockEarlyConnectionAcceptUniStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUniStream", reflect.TypeOf((*MockEarlyConnection)(nil).AcceptUniStream), arg0) return &MockEarlyConnectionAcceptUniStreamCall{Call: call} } // MockEarlyConnectionAcceptUniStreamCall wrap *gomock.Call type MockEarlyConnectionAcceptUniStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionAcceptUniStreamCall) Return(arg0 quic.ReceiveStream, arg1 error) *MockEarlyConnectionAcceptUniStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionAcceptUniStreamCall) Do(f func(context.Context) (quic.ReceiveStream, error)) *MockEarlyConnectionAcceptUniStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionAcceptUniStreamCall) DoAndReturn(f func(context.Context) (quic.ReceiveStream, error)) *MockEarlyConnectionAcceptUniStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // CloseWithError mocks base method. func (m *MockEarlyConnection) CloseWithError(arg0 qerr.ApplicationErrorCode, arg1 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CloseWithError", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // CloseWithError indicates an expected call of CloseWithError. func (mr *MockEarlyConnectionMockRecorder) CloseWithError(arg0, arg1 any) *MockEarlyConnectionCloseWithErrorCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseWithError", reflect.TypeOf((*MockEarlyConnection)(nil).CloseWithError), arg0, arg1) return &MockEarlyConnectionCloseWithErrorCall{Call: call} } // MockEarlyConnectionCloseWithErrorCall wrap *gomock.Call type MockEarlyConnectionCloseWithErrorCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionCloseWithErrorCall) Return(arg0 error) *MockEarlyConnectionCloseWithErrorCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionCloseWithErrorCall) Do(f func(qerr.ApplicationErrorCode, string) error) *MockEarlyConnectionCloseWithErrorCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionCloseWithErrorCall) DoAndReturn(f func(qerr.ApplicationErrorCode, string) error) *MockEarlyConnectionCloseWithErrorCall { c.Call = c.Call.DoAndReturn(f) return c } // ConnectionState mocks base method. func (m *MockEarlyConnection) ConnectionState() quic.ConnectionState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConnectionState") ret0, _ := ret[0].(quic.ConnectionState) return ret0 } // ConnectionState indicates an expected call of ConnectionState. func (mr *MockEarlyConnectionMockRecorder) ConnectionState() *MockEarlyConnectionConnectionStateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockEarlyConnection)(nil).ConnectionState)) return &MockEarlyConnectionConnectionStateCall{Call: call} } // MockEarlyConnectionConnectionStateCall wrap *gomock.Call type MockEarlyConnectionConnectionStateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionConnectionStateCall) Return(arg0 quic.ConnectionState) *MockEarlyConnectionConnectionStateCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionConnectionStateCall) Do(f func() quic.ConnectionState) *MockEarlyConnectionConnectionStateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionConnectionStateCall) DoAndReturn(f func() quic.ConnectionState) *MockEarlyConnectionConnectionStateCall { c.Call = c.Call.DoAndReturn(f) return c } // Context mocks base method. func (m *MockEarlyConnection) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockEarlyConnectionMockRecorder) Context() *MockEarlyConnectionContextCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockEarlyConnection)(nil).Context)) return &MockEarlyConnectionContextCall{Call: call} } // MockEarlyConnectionContextCall wrap *gomock.Call type MockEarlyConnectionContextCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionContextCall) Return(arg0 context.Context) *MockEarlyConnectionContextCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionContextCall) Do(f func() context.Context) *MockEarlyConnectionContextCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionContextCall) DoAndReturn(f func() context.Context) *MockEarlyConnectionContextCall { c.Call = c.Call.DoAndReturn(f) return c } // HandshakeComplete mocks base method. func (m *MockEarlyConnection) HandshakeComplete() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandshakeComplete") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // HandshakeComplete indicates an expected call of HandshakeComplete. func (mr *MockEarlyConnectionMockRecorder) HandshakeComplete() *MockEarlyConnectionHandshakeCompleteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandshakeComplete", reflect.TypeOf((*MockEarlyConnection)(nil).HandshakeComplete)) return &MockEarlyConnectionHandshakeCompleteCall{Call: call} } // MockEarlyConnectionHandshakeCompleteCall wrap *gomock.Call type MockEarlyConnectionHandshakeCompleteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionHandshakeCompleteCall) Return(arg0 <-chan struct{}) *MockEarlyConnectionHandshakeCompleteCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionHandshakeCompleteCall) Do(f func() <-chan struct{}) *MockEarlyConnectionHandshakeCompleteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionHandshakeCompleteCall) DoAndReturn(f func() <-chan struct{}) *MockEarlyConnectionHandshakeCompleteCall { c.Call = c.Call.DoAndReturn(f) return c } // LocalAddr mocks base method. func (m *MockEarlyConnection) LocalAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LocalAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // LocalAddr indicates an expected call of LocalAddr. func (mr *MockEarlyConnectionMockRecorder) LocalAddr() *MockEarlyConnectionLocalAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockEarlyConnection)(nil).LocalAddr)) return &MockEarlyConnectionLocalAddrCall{Call: call} } // MockEarlyConnectionLocalAddrCall wrap *gomock.Call type MockEarlyConnectionLocalAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionLocalAddrCall) Return(arg0 net.Addr) *MockEarlyConnectionLocalAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionLocalAddrCall) Do(f func() net.Addr) *MockEarlyConnectionLocalAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionLocalAddrCall) DoAndReturn(f func() net.Addr) *MockEarlyConnectionLocalAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // NextConnection mocks base method. func (m *MockEarlyConnection) NextConnection(arg0 context.Context) (quic.Connection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NextConnection", arg0) ret0, _ := ret[0].(quic.Connection) ret1, _ := ret[1].(error) return ret0, ret1 } // NextConnection indicates an expected call of NextConnection. func (mr *MockEarlyConnectionMockRecorder) NextConnection(arg0 any) *MockEarlyConnectionNextConnectionCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextConnection", reflect.TypeOf((*MockEarlyConnection)(nil).NextConnection), arg0) return &MockEarlyConnectionNextConnectionCall{Call: call} } // MockEarlyConnectionNextConnectionCall wrap *gomock.Call type MockEarlyConnectionNextConnectionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionNextConnectionCall) Return(arg0 quic.Connection, arg1 error) *MockEarlyConnectionNextConnectionCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionNextConnectionCall) Do(f func(context.Context) (quic.Connection, error)) *MockEarlyConnectionNextConnectionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionNextConnectionCall) DoAndReturn(f func(context.Context) (quic.Connection, error)) *MockEarlyConnectionNextConnectionCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenStream mocks base method. func (m *MockEarlyConnection) OpenStream() (quic.Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenStream") ret0, _ := ret[0].(quic.Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenStream indicates an expected call of OpenStream. func (mr *MockEarlyConnectionMockRecorder) OpenStream() *MockEarlyConnectionOpenStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockEarlyConnection)(nil).OpenStream)) return &MockEarlyConnectionOpenStreamCall{Call: call} } // MockEarlyConnectionOpenStreamCall wrap *gomock.Call type MockEarlyConnectionOpenStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionOpenStreamCall) Return(arg0 quic.Stream, arg1 error) *MockEarlyConnectionOpenStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionOpenStreamCall) Do(f func() (quic.Stream, error)) *MockEarlyConnectionOpenStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionOpenStreamCall) DoAndReturn(f func() (quic.Stream, error)) *MockEarlyConnectionOpenStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenStreamSync mocks base method. func (m *MockEarlyConnection) OpenStreamSync(arg0 context.Context) (quic.Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenStreamSync", arg0) ret0, _ := ret[0].(quic.Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenStreamSync indicates an expected call of OpenStreamSync. func (mr *MockEarlyConnectionMockRecorder) OpenStreamSync(arg0 any) *MockEarlyConnectionOpenStreamSyncCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStreamSync", reflect.TypeOf((*MockEarlyConnection)(nil).OpenStreamSync), arg0) return &MockEarlyConnectionOpenStreamSyncCall{Call: call} } // MockEarlyConnectionOpenStreamSyncCall wrap *gomock.Call type MockEarlyConnectionOpenStreamSyncCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionOpenStreamSyncCall) Return(arg0 quic.Stream, arg1 error) *MockEarlyConnectionOpenStreamSyncCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionOpenStreamSyncCall) Do(f func(context.Context) (quic.Stream, error)) *MockEarlyConnectionOpenStreamSyncCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionOpenStreamSyncCall) DoAndReturn(f func(context.Context) (quic.Stream, error)) *MockEarlyConnectionOpenStreamSyncCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenUniStream mocks base method. func (m *MockEarlyConnection) OpenUniStream() (quic.SendStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenUniStream") ret0, _ := ret[0].(quic.SendStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenUniStream indicates an expected call of OpenUniStream. func (mr *MockEarlyConnectionMockRecorder) OpenUniStream() *MockEarlyConnectionOpenUniStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStream", reflect.TypeOf((*MockEarlyConnection)(nil).OpenUniStream)) return &MockEarlyConnectionOpenUniStreamCall{Call: call} } // MockEarlyConnectionOpenUniStreamCall wrap *gomock.Call type MockEarlyConnectionOpenUniStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionOpenUniStreamCall) Return(arg0 quic.SendStream, arg1 error) *MockEarlyConnectionOpenUniStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionOpenUniStreamCall) Do(f func() (quic.SendStream, error)) *MockEarlyConnectionOpenUniStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionOpenUniStreamCall) DoAndReturn(f func() (quic.SendStream, error)) *MockEarlyConnectionOpenUniStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenUniStreamSync mocks base method. func (m *MockEarlyConnection) OpenUniStreamSync(arg0 context.Context) (quic.SendStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenUniStreamSync", arg0) ret0, _ := ret[0].(quic.SendStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenUniStreamSync indicates an expected call of OpenUniStreamSync. func (mr *MockEarlyConnectionMockRecorder) OpenUniStreamSync(arg0 any) *MockEarlyConnectionOpenUniStreamSyncCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStreamSync", reflect.TypeOf((*MockEarlyConnection)(nil).OpenUniStreamSync), arg0) return &MockEarlyConnectionOpenUniStreamSyncCall{Call: call} } // MockEarlyConnectionOpenUniStreamSyncCall wrap *gomock.Call type MockEarlyConnectionOpenUniStreamSyncCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionOpenUniStreamSyncCall) Return(arg0 quic.SendStream, arg1 error) *MockEarlyConnectionOpenUniStreamSyncCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionOpenUniStreamSyncCall) Do(f func(context.Context) (quic.SendStream, error)) *MockEarlyConnectionOpenUniStreamSyncCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionOpenUniStreamSyncCall) DoAndReturn(f func(context.Context) (quic.SendStream, error)) *MockEarlyConnectionOpenUniStreamSyncCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceiveDatagram mocks base method. func (m *MockEarlyConnection) ReceiveDatagram(arg0 context.Context) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReceiveDatagram", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // ReceiveDatagram indicates an expected call of ReceiveDatagram. func (mr *MockEarlyConnectionMockRecorder) ReceiveDatagram(arg0 any) *MockEarlyConnectionReceiveDatagramCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveDatagram", reflect.TypeOf((*MockEarlyConnection)(nil).ReceiveDatagram), arg0) return &MockEarlyConnectionReceiveDatagramCall{Call: call} } // MockEarlyConnectionReceiveDatagramCall wrap *gomock.Call type MockEarlyConnectionReceiveDatagramCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionReceiveDatagramCall) Return(arg0 []byte, arg1 error) *MockEarlyConnectionReceiveDatagramCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionReceiveDatagramCall) Do(f func(context.Context) ([]byte, error)) *MockEarlyConnectionReceiveDatagramCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionReceiveDatagramCall) DoAndReturn(f func(context.Context) ([]byte, error)) *MockEarlyConnectionReceiveDatagramCall { c.Call = c.Call.DoAndReturn(f) return c } // RemoteAddr mocks base method. func (m *MockEarlyConnection) RemoteAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoteAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // RemoteAddr indicates an expected call of RemoteAddr. func (mr *MockEarlyConnectionMockRecorder) RemoteAddr() *MockEarlyConnectionRemoteAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockEarlyConnection)(nil).RemoteAddr)) return &MockEarlyConnectionRemoteAddrCall{Call: call} } // MockEarlyConnectionRemoteAddrCall wrap *gomock.Call type MockEarlyConnectionRemoteAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionRemoteAddrCall) Return(arg0 net.Addr) *MockEarlyConnectionRemoteAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionRemoteAddrCall) Do(f func() net.Addr) *MockEarlyConnectionRemoteAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionRemoteAddrCall) DoAndReturn(f func() net.Addr) *MockEarlyConnectionRemoteAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // SendDatagram mocks base method. func (m *MockEarlyConnection) SendDatagram(arg0 []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendDatagram", arg0) ret0, _ := ret[0].(error) return ret0 } // SendDatagram indicates an expected call of SendDatagram. func (mr *MockEarlyConnectionMockRecorder) SendDatagram(arg0 any) *MockEarlyConnectionSendDatagramCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendDatagram", reflect.TypeOf((*MockEarlyConnection)(nil).SendDatagram), arg0) return &MockEarlyConnectionSendDatagramCall{Call: call} } // MockEarlyConnectionSendDatagramCall wrap *gomock.Call type MockEarlyConnectionSendDatagramCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockEarlyConnectionSendDatagramCall) Return(arg0 error) *MockEarlyConnectionSendDatagramCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockEarlyConnectionSendDatagramCall) Do(f func([]byte) error) *MockEarlyConnectionSendDatagramCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockEarlyConnectionSendDatagramCall) DoAndReturn(f func([]byte) error) *MockEarlyConnectionSendDatagramCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/quic/stream.go000066400000000000000000000302241465664453100263230ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: Stream) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mockquic -destination quic/stream.go github.com/quic-go/quic-go Stream // // Package mockquic is a generated GoMock package. package mockquic import ( context "context" reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" qerr "github.com/quic-go/quic-go/internal/qerr" gomock "go.uber.org/mock/gomock" ) // MockStream is a mock of Stream interface. type MockStream struct { ctrl *gomock.Controller recorder *MockStreamMockRecorder } // MockStreamMockRecorder is the mock recorder for MockStream. type MockStreamMockRecorder struct { mock *MockStream } // NewMockStream creates a new mock instance. func NewMockStream(ctrl *gomock.Controller) *MockStream { mock := &MockStream{ctrl: ctrl} mock.recorder = &MockStreamMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStream) EXPECT() *MockStreamMockRecorder { return m.recorder } // CancelRead mocks base method. func (m *MockStream) CancelRead(arg0 qerr.StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelRead", arg0) } // CancelRead indicates an expected call of CancelRead. func (mr *MockStreamMockRecorder) CancelRead(arg0 any) *MockStreamCancelReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRead", reflect.TypeOf((*MockStream)(nil).CancelRead), arg0) return &MockStreamCancelReadCall{Call: call} } // MockStreamCancelReadCall wrap *gomock.Call type MockStreamCancelReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamCancelReadCall) Return() *MockStreamCancelReadCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamCancelReadCall) Do(f func(qerr.StreamErrorCode)) *MockStreamCancelReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamCancelReadCall) DoAndReturn(f func(qerr.StreamErrorCode)) *MockStreamCancelReadCall { c.Call = c.Call.DoAndReturn(f) return c } // CancelWrite mocks base method. func (m *MockStream) CancelWrite(arg0 qerr.StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelWrite", arg0) } // CancelWrite indicates an expected call of CancelWrite. func (mr *MockStreamMockRecorder) CancelWrite(arg0 any) *MockStreamCancelWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelWrite", reflect.TypeOf((*MockStream)(nil).CancelWrite), arg0) return &MockStreamCancelWriteCall{Call: call} } // MockStreamCancelWriteCall wrap *gomock.Call type MockStreamCancelWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamCancelWriteCall) Return() *MockStreamCancelWriteCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamCancelWriteCall) Do(f func(qerr.StreamErrorCode)) *MockStreamCancelWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamCancelWriteCall) DoAndReturn(f func(qerr.StreamErrorCode)) *MockStreamCancelWriteCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockStream) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockStreamMockRecorder) Close() *MockStreamCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStream)(nil).Close)) return &MockStreamCloseCall{Call: call} } // MockStreamCloseCall wrap *gomock.Call type MockStreamCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamCloseCall) Return(arg0 error) *MockStreamCloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamCloseCall) Do(f func() error) *MockStreamCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamCloseCall) DoAndReturn(f func() error) *MockStreamCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // Context mocks base method. func (m *MockStream) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockStreamMockRecorder) Context() *MockStreamContextCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockStream)(nil).Context)) return &MockStreamContextCall{Call: call} } // MockStreamContextCall wrap *gomock.Call type MockStreamContextCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamContextCall) Return(arg0 context.Context) *MockStreamContextCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamContextCall) Do(f func() context.Context) *MockStreamContextCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamContextCall) DoAndReturn(f func() context.Context) *MockStreamContextCall { c.Call = c.Call.DoAndReturn(f) return c } // Read mocks base method. func (m *MockStream) Read(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Read indicates an expected call of Read. func (mr *MockStreamMockRecorder) Read(arg0 any) *MockStreamReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockStream)(nil).Read), arg0) return &MockStreamReadCall{Call: call} } // MockStreamReadCall wrap *gomock.Call type MockStreamReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamReadCall) Return(arg0 int, arg1 error) *MockStreamReadCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamReadCall) Do(f func([]byte) (int, error)) *MockStreamReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamReadCall) DoAndReturn(f func([]byte) (int, error)) *MockStreamReadCall { c.Call = c.Call.DoAndReturn(f) return c } // SetDeadline mocks base method. func (m *MockStream) SetDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetDeadline indicates an expected call of SetDeadline. func (mr *MockStreamMockRecorder) SetDeadline(arg0 any) *MockStreamSetDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockStream)(nil).SetDeadline), arg0) return &MockStreamSetDeadlineCall{Call: call} } // MockStreamSetDeadlineCall wrap *gomock.Call type MockStreamSetDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamSetDeadlineCall) Return(arg0 error) *MockStreamSetDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamSetDeadlineCall) Do(f func(time.Time) error) *MockStreamSetDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamSetDeadlineCall) DoAndReturn(f func(time.Time) error) *MockStreamSetDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // SetReadDeadline mocks base method. func (m *MockStream) SetReadDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockStreamMockRecorder) SetReadDeadline(arg0 any) *MockStreamSetReadDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockStream)(nil).SetReadDeadline), arg0) return &MockStreamSetReadDeadlineCall{Call: call} } // MockStreamSetReadDeadlineCall wrap *gomock.Call type MockStreamSetReadDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamSetReadDeadlineCall) Return(arg0 error) *MockStreamSetReadDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamSetReadDeadlineCall) Do(f func(time.Time) error) *MockStreamSetReadDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamSetReadDeadlineCall) DoAndReturn(f func(time.Time) error) *MockStreamSetReadDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // SetWriteDeadline mocks base method. func (m *MockStream) SetWriteDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetWriteDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetWriteDeadline indicates an expected call of SetWriteDeadline. func (mr *MockStreamMockRecorder) SetWriteDeadline(arg0 any) *MockStreamSetWriteDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockStream)(nil).SetWriteDeadline), arg0) return &MockStreamSetWriteDeadlineCall{Call: call} } // MockStreamSetWriteDeadlineCall wrap *gomock.Call type MockStreamSetWriteDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamSetWriteDeadlineCall) Return(arg0 error) *MockStreamSetWriteDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamSetWriteDeadlineCall) Do(f func(time.Time) error) *MockStreamSetWriteDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamSetWriteDeadlineCall) DoAndReturn(f func(time.Time) error) *MockStreamSetWriteDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // StreamID mocks base method. func (m *MockStream) StreamID() protocol.StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(protocol.StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockStreamMockRecorder) StreamID() *MockStreamStreamIDCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockStream)(nil).StreamID)) return &MockStreamStreamIDCall{Call: call} } // MockStreamStreamIDCall wrap *gomock.Call type MockStreamStreamIDCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamStreamIDCall) Return(arg0 protocol.StreamID) *MockStreamStreamIDCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamStreamIDCall) Do(f func() protocol.StreamID) *MockStreamStreamIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamStreamIDCall) DoAndReturn(f func() protocol.StreamID) *MockStreamStreamIDCall { c.Call = c.Call.DoAndReturn(f) return c } // Write mocks base method. func (m *MockStream) Write(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *MockStreamMockRecorder) Write(arg0 any) *MockStreamWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockStream)(nil).Write), arg0) return &MockStreamWriteCall{Call: call} } // MockStreamWriteCall wrap *gomock.Call type MockStreamWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamWriteCall) Return(arg0 int, arg1 error) *MockStreamWriteCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamWriteCall) Do(f func([]byte) (int, error)) *MockStreamWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamWriteCall) DoAndReturn(f func([]byte) (int, error)) *MockStreamWriteCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/short_header_opener.go000066400000000000000000000133661465664453100301160ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/handshake (interfaces: ShortHeaderOpener) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination short_header_opener.go github.com/quic-go/quic-go/internal/handshake ShortHeaderOpener // // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockShortHeaderOpener is a mock of ShortHeaderOpener interface. type MockShortHeaderOpener struct { ctrl *gomock.Controller recorder *MockShortHeaderOpenerMockRecorder } // MockShortHeaderOpenerMockRecorder is the mock recorder for MockShortHeaderOpener. type MockShortHeaderOpenerMockRecorder struct { mock *MockShortHeaderOpener } // NewMockShortHeaderOpener creates a new mock instance. func NewMockShortHeaderOpener(ctrl *gomock.Controller) *MockShortHeaderOpener { mock := &MockShortHeaderOpener{ctrl: ctrl} mock.recorder = &MockShortHeaderOpenerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShortHeaderOpener) EXPECT() *MockShortHeaderOpenerMockRecorder { return m.recorder } // DecodePacketNumber mocks base method. func (m *MockShortHeaderOpener) DecodePacketNumber(arg0 protocol.PacketNumber, arg1 protocol.PacketNumberLen) protocol.PacketNumber { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DecodePacketNumber", arg0, arg1) ret0, _ := ret[0].(protocol.PacketNumber) return ret0 } // DecodePacketNumber indicates an expected call of DecodePacketNumber. func (mr *MockShortHeaderOpenerMockRecorder) DecodePacketNumber(arg0, arg1 any) *MockShortHeaderOpenerDecodePacketNumberCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodePacketNumber", reflect.TypeOf((*MockShortHeaderOpener)(nil).DecodePacketNumber), arg0, arg1) return &MockShortHeaderOpenerDecodePacketNumberCall{Call: call} } // MockShortHeaderOpenerDecodePacketNumberCall wrap *gomock.Call type MockShortHeaderOpenerDecodePacketNumberCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderOpenerDecodePacketNumberCall) Return(arg0 protocol.PacketNumber) *MockShortHeaderOpenerDecodePacketNumberCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderOpenerDecodePacketNumberCall) Do(f func(protocol.PacketNumber, protocol.PacketNumberLen) protocol.PacketNumber) *MockShortHeaderOpenerDecodePacketNumberCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderOpenerDecodePacketNumberCall) DoAndReturn(f func(protocol.PacketNumber, protocol.PacketNumberLen) protocol.PacketNumber) *MockShortHeaderOpenerDecodePacketNumberCall { c.Call = c.Call.DoAndReturn(f) return c } // DecryptHeader mocks base method. func (m *MockShortHeaderOpener) DecryptHeader(arg0 []byte, arg1 *byte, arg2 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "DecryptHeader", arg0, arg1, arg2) } // DecryptHeader indicates an expected call of DecryptHeader. func (mr *MockShortHeaderOpenerMockRecorder) DecryptHeader(arg0, arg1, arg2 any) *MockShortHeaderOpenerDecryptHeaderCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptHeader", reflect.TypeOf((*MockShortHeaderOpener)(nil).DecryptHeader), arg0, arg1, arg2) return &MockShortHeaderOpenerDecryptHeaderCall{Call: call} } // MockShortHeaderOpenerDecryptHeaderCall wrap *gomock.Call type MockShortHeaderOpenerDecryptHeaderCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderOpenerDecryptHeaderCall) Return() *MockShortHeaderOpenerDecryptHeaderCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderOpenerDecryptHeaderCall) Do(f func([]byte, *byte, []byte)) *MockShortHeaderOpenerDecryptHeaderCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderOpenerDecryptHeaderCall) DoAndReturn(f func([]byte, *byte, []byte)) *MockShortHeaderOpenerDecryptHeaderCall { c.Call = c.Call.DoAndReturn(f) return c } // Open mocks base method. func (m *MockShortHeaderOpener) Open(arg0, arg1 []byte, arg2 time.Time, arg3 protocol.PacketNumber, arg4 protocol.KeyPhaseBit, arg5 []byte) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Open", arg0, arg1, arg2, arg3, arg4, arg5) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // Open indicates an expected call of Open. func (mr *MockShortHeaderOpenerMockRecorder) Open(arg0, arg1, arg2, arg3, arg4, arg5 any) *MockShortHeaderOpenerOpenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockShortHeaderOpener)(nil).Open), arg0, arg1, arg2, arg3, arg4, arg5) return &MockShortHeaderOpenerOpenCall{Call: call} } // MockShortHeaderOpenerOpenCall wrap *gomock.Call type MockShortHeaderOpenerOpenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderOpenerOpenCall) Return(arg0 []byte, arg1 error) *MockShortHeaderOpenerOpenCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderOpenerOpenCall) Do(f func([]byte, []byte, time.Time, protocol.PacketNumber, protocol.KeyPhaseBit, []byte) ([]byte, error)) *MockShortHeaderOpenerOpenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderOpenerOpenCall) DoAndReturn(f func([]byte, []byte, time.Time, protocol.PacketNumber, protocol.KeyPhaseBit, []byte) ([]byte, error)) *MockShortHeaderOpenerOpenCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/short_header_sealer.go000066400000000000000000000144721465664453100301000ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/handshake (interfaces: ShortHeaderSealer) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination short_header_sealer.go github.com/quic-go/quic-go/internal/handshake ShortHeaderSealer // // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockShortHeaderSealer is a mock of ShortHeaderSealer interface. type MockShortHeaderSealer struct { ctrl *gomock.Controller recorder *MockShortHeaderSealerMockRecorder } // MockShortHeaderSealerMockRecorder is the mock recorder for MockShortHeaderSealer. type MockShortHeaderSealerMockRecorder struct { mock *MockShortHeaderSealer } // NewMockShortHeaderSealer creates a new mock instance. func NewMockShortHeaderSealer(ctrl *gomock.Controller) *MockShortHeaderSealer { mock := &MockShortHeaderSealer{ctrl: ctrl} mock.recorder = &MockShortHeaderSealerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockShortHeaderSealer) EXPECT() *MockShortHeaderSealerMockRecorder { return m.recorder } // EncryptHeader mocks base method. func (m *MockShortHeaderSealer) EncryptHeader(arg0 []byte, arg1 *byte, arg2 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "EncryptHeader", arg0, arg1, arg2) } // EncryptHeader indicates an expected call of EncryptHeader. func (mr *MockShortHeaderSealerMockRecorder) EncryptHeader(arg0, arg1, arg2 any) *MockShortHeaderSealerEncryptHeaderCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptHeader", reflect.TypeOf((*MockShortHeaderSealer)(nil).EncryptHeader), arg0, arg1, arg2) return &MockShortHeaderSealerEncryptHeaderCall{Call: call} } // MockShortHeaderSealerEncryptHeaderCall wrap *gomock.Call type MockShortHeaderSealerEncryptHeaderCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderSealerEncryptHeaderCall) Return() *MockShortHeaderSealerEncryptHeaderCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderSealerEncryptHeaderCall) Do(f func([]byte, *byte, []byte)) *MockShortHeaderSealerEncryptHeaderCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderSealerEncryptHeaderCall) DoAndReturn(f func([]byte, *byte, []byte)) *MockShortHeaderSealerEncryptHeaderCall { c.Call = c.Call.DoAndReturn(f) return c } // KeyPhase mocks base method. func (m *MockShortHeaderSealer) KeyPhase() protocol.KeyPhaseBit { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "KeyPhase") ret0, _ := ret[0].(protocol.KeyPhaseBit) return ret0 } // KeyPhase indicates an expected call of KeyPhase. func (mr *MockShortHeaderSealerMockRecorder) KeyPhase() *MockShortHeaderSealerKeyPhaseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyPhase", reflect.TypeOf((*MockShortHeaderSealer)(nil).KeyPhase)) return &MockShortHeaderSealerKeyPhaseCall{Call: call} } // MockShortHeaderSealerKeyPhaseCall wrap *gomock.Call type MockShortHeaderSealerKeyPhaseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderSealerKeyPhaseCall) Return(arg0 protocol.KeyPhaseBit) *MockShortHeaderSealerKeyPhaseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderSealerKeyPhaseCall) Do(f func() protocol.KeyPhaseBit) *MockShortHeaderSealerKeyPhaseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderSealerKeyPhaseCall) DoAndReturn(f func() protocol.KeyPhaseBit) *MockShortHeaderSealerKeyPhaseCall { c.Call = c.Call.DoAndReturn(f) return c } // Overhead mocks base method. func (m *MockShortHeaderSealer) Overhead() int { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Overhead") ret0, _ := ret[0].(int) return ret0 } // Overhead indicates an expected call of Overhead. func (mr *MockShortHeaderSealerMockRecorder) Overhead() *MockShortHeaderSealerOverheadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Overhead", reflect.TypeOf((*MockShortHeaderSealer)(nil).Overhead)) return &MockShortHeaderSealerOverheadCall{Call: call} } // MockShortHeaderSealerOverheadCall wrap *gomock.Call type MockShortHeaderSealerOverheadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderSealerOverheadCall) Return(arg0 int) *MockShortHeaderSealerOverheadCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderSealerOverheadCall) Do(f func() int) *MockShortHeaderSealerOverheadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderSealerOverheadCall) DoAndReturn(f func() int) *MockShortHeaderSealerOverheadCall { c.Call = c.Call.DoAndReturn(f) return c } // Seal mocks base method. func (m *MockShortHeaderSealer) Seal(arg0, arg1 []byte, arg2 protocol.PacketNumber, arg3 []byte) []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Seal", arg0, arg1, arg2, arg3) ret0, _ := ret[0].([]byte) return ret0 } // Seal indicates an expected call of Seal. func (mr *MockShortHeaderSealerMockRecorder) Seal(arg0, arg1, arg2, arg3 any) *MockShortHeaderSealerSealCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seal", reflect.TypeOf((*MockShortHeaderSealer)(nil).Seal), arg0, arg1, arg2, arg3) return &MockShortHeaderSealerSealCall{Call: call} } // MockShortHeaderSealerSealCall wrap *gomock.Call type MockShortHeaderSealerSealCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockShortHeaderSealerSealCall) Return(arg0 []byte) *MockShortHeaderSealerSealCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockShortHeaderSealerSealCall) Do(f func([]byte, []byte, protocol.PacketNumber, []byte) []byte) *MockShortHeaderSealerSealCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockShortHeaderSealerSealCall) DoAndReturn(f func([]byte, []byte, protocol.PacketNumber, []byte) []byte) *MockShortHeaderSealerSealCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/mocks/stream_flow_controller.go000066400000000000000000000302071465664453100306550ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go/internal/flowcontrol (interfaces: StreamFlowController) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package mocks -destination stream_flow_controller.go github.com/quic-go/quic-go/internal/flowcontrol StreamFlowController // // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockStreamFlowController is a mock of StreamFlowController interface. type MockStreamFlowController struct { ctrl *gomock.Controller recorder *MockStreamFlowControllerMockRecorder } // MockStreamFlowControllerMockRecorder is the mock recorder for MockStreamFlowController. type MockStreamFlowControllerMockRecorder struct { mock *MockStreamFlowController } // NewMockStreamFlowController creates a new mock instance. func NewMockStreamFlowController(ctrl *gomock.Controller) *MockStreamFlowController { mock := &MockStreamFlowController{ctrl: ctrl} mock.recorder = &MockStreamFlowControllerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStreamFlowController) EXPECT() *MockStreamFlowControllerMockRecorder { return m.recorder } // Abandon mocks base method. func (m *MockStreamFlowController) Abandon() { m.ctrl.T.Helper() m.ctrl.Call(m, "Abandon") } // Abandon indicates an expected call of Abandon. func (mr *MockStreamFlowControllerMockRecorder) Abandon() *MockStreamFlowControllerAbandonCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Abandon", reflect.TypeOf((*MockStreamFlowController)(nil).Abandon)) return &MockStreamFlowControllerAbandonCall{Call: call} } // MockStreamFlowControllerAbandonCall wrap *gomock.Call type MockStreamFlowControllerAbandonCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerAbandonCall) Return() *MockStreamFlowControllerAbandonCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerAbandonCall) Do(f func()) *MockStreamFlowControllerAbandonCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerAbandonCall) DoAndReturn(f func()) *MockStreamFlowControllerAbandonCall { c.Call = c.Call.DoAndReturn(f) return c } // AddBytesRead mocks base method. func (m *MockStreamFlowController) AddBytesRead(arg0 protocol.ByteCount) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddBytesRead", arg0) ret0, _ := ret[0].(bool) return ret0 } // AddBytesRead indicates an expected call of AddBytesRead. func (mr *MockStreamFlowControllerMockRecorder) AddBytesRead(arg0 any) *MockStreamFlowControllerAddBytesReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesRead", reflect.TypeOf((*MockStreamFlowController)(nil).AddBytesRead), arg0) return &MockStreamFlowControllerAddBytesReadCall{Call: call} } // MockStreamFlowControllerAddBytesReadCall wrap *gomock.Call type MockStreamFlowControllerAddBytesReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerAddBytesReadCall) Return(arg0 bool) *MockStreamFlowControllerAddBytesReadCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerAddBytesReadCall) Do(f func(protocol.ByteCount) bool) *MockStreamFlowControllerAddBytesReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerAddBytesReadCall) DoAndReturn(f func(protocol.ByteCount) bool) *MockStreamFlowControllerAddBytesReadCall { c.Call = c.Call.DoAndReturn(f) return c } // AddBytesSent mocks base method. func (m *MockStreamFlowController) AddBytesSent(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddBytesSent", arg0) } // AddBytesSent indicates an expected call of AddBytesSent. func (mr *MockStreamFlowControllerMockRecorder) AddBytesSent(arg0 any) *MockStreamFlowControllerAddBytesSentCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesSent", reflect.TypeOf((*MockStreamFlowController)(nil).AddBytesSent), arg0) return &MockStreamFlowControllerAddBytesSentCall{Call: call} } // MockStreamFlowControllerAddBytesSentCall wrap *gomock.Call type MockStreamFlowControllerAddBytesSentCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerAddBytesSentCall) Return() *MockStreamFlowControllerAddBytesSentCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerAddBytesSentCall) Do(f func(protocol.ByteCount)) *MockStreamFlowControllerAddBytesSentCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerAddBytesSentCall) DoAndReturn(f func(protocol.ByteCount)) *MockStreamFlowControllerAddBytesSentCall { c.Call = c.Call.DoAndReturn(f) return c } // GetWindowUpdate mocks base method. func (m *MockStreamFlowController) GetWindowUpdate() protocol.ByteCount { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWindowUpdate") ret0, _ := ret[0].(protocol.ByteCount) return ret0 } // GetWindowUpdate indicates an expected call of GetWindowUpdate. func (mr *MockStreamFlowControllerMockRecorder) GetWindowUpdate() *MockStreamFlowControllerGetWindowUpdateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWindowUpdate", reflect.TypeOf((*MockStreamFlowController)(nil).GetWindowUpdate)) return &MockStreamFlowControllerGetWindowUpdateCall{Call: call} } // MockStreamFlowControllerGetWindowUpdateCall wrap *gomock.Call type MockStreamFlowControllerGetWindowUpdateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerGetWindowUpdateCall) Return(arg0 protocol.ByteCount) *MockStreamFlowControllerGetWindowUpdateCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerGetWindowUpdateCall) Do(f func() protocol.ByteCount) *MockStreamFlowControllerGetWindowUpdateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerGetWindowUpdateCall) DoAndReturn(f func() protocol.ByteCount) *MockStreamFlowControllerGetWindowUpdateCall { c.Call = c.Call.DoAndReturn(f) return c } // IsNewlyBlocked mocks base method. func (m *MockStreamFlowController) IsNewlyBlocked() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsNewlyBlocked") ret0, _ := ret[0].(bool) return ret0 } // IsNewlyBlocked indicates an expected call of IsNewlyBlocked. func (mr *MockStreamFlowControllerMockRecorder) IsNewlyBlocked() *MockStreamFlowControllerIsNewlyBlockedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNewlyBlocked", reflect.TypeOf((*MockStreamFlowController)(nil).IsNewlyBlocked)) return &MockStreamFlowControllerIsNewlyBlockedCall{Call: call} } // MockStreamFlowControllerIsNewlyBlockedCall wrap *gomock.Call type MockStreamFlowControllerIsNewlyBlockedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerIsNewlyBlockedCall) Return(arg0 bool) *MockStreamFlowControllerIsNewlyBlockedCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerIsNewlyBlockedCall) Do(f func() bool) *MockStreamFlowControllerIsNewlyBlockedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerIsNewlyBlockedCall) DoAndReturn(f func() bool) *MockStreamFlowControllerIsNewlyBlockedCall { c.Call = c.Call.DoAndReturn(f) return c } // SendWindowSize mocks base method. func (m *MockStreamFlowController) SendWindowSize() protocol.ByteCount { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendWindowSize") ret0, _ := ret[0].(protocol.ByteCount) return ret0 } // SendWindowSize indicates an expected call of SendWindowSize. func (mr *MockStreamFlowControllerMockRecorder) SendWindowSize() *MockStreamFlowControllerSendWindowSizeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWindowSize", reflect.TypeOf((*MockStreamFlowController)(nil).SendWindowSize)) return &MockStreamFlowControllerSendWindowSizeCall{Call: call} } // MockStreamFlowControllerSendWindowSizeCall wrap *gomock.Call type MockStreamFlowControllerSendWindowSizeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerSendWindowSizeCall) Return(arg0 protocol.ByteCount) *MockStreamFlowControllerSendWindowSizeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerSendWindowSizeCall) Do(f func() protocol.ByteCount) *MockStreamFlowControllerSendWindowSizeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerSendWindowSizeCall) DoAndReturn(f func() protocol.ByteCount) *MockStreamFlowControllerSendWindowSizeCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdateHighestReceived mocks base method. func (m *MockStreamFlowController) UpdateHighestReceived(arg0 protocol.ByteCount, arg1 bool) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateHighestReceived", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // UpdateHighestReceived indicates an expected call of UpdateHighestReceived. func (mr *MockStreamFlowControllerMockRecorder) UpdateHighestReceived(arg0, arg1 any) *MockStreamFlowControllerUpdateHighestReceivedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHighestReceived", reflect.TypeOf((*MockStreamFlowController)(nil).UpdateHighestReceived), arg0, arg1) return &MockStreamFlowControllerUpdateHighestReceivedCall{Call: call} } // MockStreamFlowControllerUpdateHighestReceivedCall wrap *gomock.Call type MockStreamFlowControllerUpdateHighestReceivedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerUpdateHighestReceivedCall) Return(arg0 error) *MockStreamFlowControllerUpdateHighestReceivedCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerUpdateHighestReceivedCall) Do(f func(protocol.ByteCount, bool) error) *MockStreamFlowControllerUpdateHighestReceivedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerUpdateHighestReceivedCall) DoAndReturn(f func(protocol.ByteCount, bool) error) *MockStreamFlowControllerUpdateHighestReceivedCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdateSendWindow mocks base method. func (m *MockStreamFlowController) UpdateSendWindow(arg0 protocol.ByteCount) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UpdateSendWindow", arg0) ret0, _ := ret[0].(bool) return ret0 } // UpdateSendWindow indicates an expected call of UpdateSendWindow. func (mr *MockStreamFlowControllerMockRecorder) UpdateSendWindow(arg0 any) *MockStreamFlowControllerUpdateSendWindowCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSendWindow", reflect.TypeOf((*MockStreamFlowController)(nil).UpdateSendWindow), arg0) return &MockStreamFlowControllerUpdateSendWindowCall{Call: call} } // MockStreamFlowControllerUpdateSendWindowCall wrap *gomock.Call type MockStreamFlowControllerUpdateSendWindowCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamFlowControllerUpdateSendWindowCall) Return(arg0 bool) *MockStreamFlowControllerUpdateSendWindowCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamFlowControllerUpdateSendWindowCall) Do(f func(protocol.ByteCount) bool) *MockStreamFlowControllerUpdateSendWindowCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamFlowControllerUpdateSendWindowCall) DoAndReturn(f func(protocol.ByteCount) bool) *MockStreamFlowControllerUpdateSendWindowCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/000077500000000000000000000000001465664453100242645ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/connection_id.go000066400000000000000000000053361465664453100274350ustar00rootroot00000000000000package protocol import ( "crypto/rand" "errors" "fmt" "io" ) var ErrInvalidConnectionIDLen = errors.New("invalid Connection ID length") // An ArbitraryLenConnectionID is a QUIC Connection ID able to represent Connection IDs according to RFC 8999. // Future QUIC versions might allow connection ID lengths up to 255 bytes, while QUIC v1 // restricts the length to 20 bytes. type ArbitraryLenConnectionID []byte func (c ArbitraryLenConnectionID) Len() int { return len(c) } func (c ArbitraryLenConnectionID) Bytes() []byte { return c } func (c ArbitraryLenConnectionID) String() string { if c.Len() == 0 { return "(empty)" } return fmt.Sprintf("%x", c.Bytes()) } const maxConnectionIDLen = 20 // A ConnectionID in QUIC type ConnectionID struct { b [20]byte l uint8 } // GenerateConnectionID generates a connection ID using cryptographic random func GenerateConnectionID(l int) (ConnectionID, error) { var c ConnectionID c.l = uint8(l) _, err := rand.Read(c.b[:l]) return c, err } // ParseConnectionID interprets b as a Connection ID. // It panics if b is longer than 20 bytes. func ParseConnectionID(b []byte) ConnectionID { if len(b) > maxConnectionIDLen { panic("invalid conn id length") } var c ConnectionID c.l = uint8(len(b)) copy(c.b[:c.l], b) return c } // GenerateConnectionIDForInitial generates a connection ID for the Initial packet. // It uses a length randomly chosen between 8 and 20 bytes. func GenerateConnectionIDForInitial() (ConnectionID, error) { r := make([]byte, 1) if _, err := rand.Read(r); err != nil { return ConnectionID{}, err } l := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1) return GenerateConnectionID(l) } // ReadConnectionID reads a connection ID of length len from the given io.Reader. // It returns io.EOF if there are not enough bytes to read. func ReadConnectionID(r io.Reader, l int) (ConnectionID, error) { var c ConnectionID if l == 0 { return c, nil } if l > maxConnectionIDLen { return c, ErrInvalidConnectionIDLen } c.l = uint8(l) _, err := io.ReadFull(r, c.b[:l]) if err == io.ErrUnexpectedEOF { return c, io.EOF } return c, err } // Len returns the length of the connection ID in bytes func (c ConnectionID) Len() int { return int(c.l) } // Bytes returns the byte representation func (c ConnectionID) Bytes() []byte { return c.b[:c.l] } func (c ConnectionID) String() string { if c.Len() == 0 { return "(empty)" } return fmt.Sprintf("%x", c.Bytes()) } type DefaultConnectionIDGenerator struct { ConnLen int } func (d *DefaultConnectionIDGenerator) GenerateConnectionID() (ConnectionID, error) { return GenerateConnectionID(d.ConnLen) } func (d *DefaultConnectionIDGenerator) ConnectionIDLen() int { return d.ConnLen } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/connection_id_test.go000066400000000000000000000070161465664453100304710ustar00rootroot00000000000000package protocol import ( "bytes" "crypto/rand" "io" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID generation", func() { It("generates random connection IDs", func() { c1, err := GenerateConnectionID(8) Expect(err).ToNot(HaveOccurred()) Expect(c1).ToNot(BeZero()) c2, err := GenerateConnectionID(8) Expect(err).ToNot(HaveOccurred()) Expect(c1).ToNot(Equal(c2)) }) It("generates connection IDs with the requested length", func() { c, err := GenerateConnectionID(5) Expect(err).ToNot(HaveOccurred()) Expect(c.Len()).To(Equal(5)) }) It("generates random length destination connection IDs", func() { var has8ByteConnID, has20ByteConnID bool for i := 0; i < 1000; i++ { c, err := GenerateConnectionIDForInitial() Expect(err).ToNot(HaveOccurred()) Expect(c.Len()).To(BeNumerically(">=", 8)) Expect(c.Len()).To(BeNumerically("<=", 20)) if c.Len() == 8 { has8ByteConnID = true } if c.Len() == 20 { has20ByteConnID = true } } Expect(has8ByteConnID).To(BeTrue()) Expect(has20ByteConnID).To(BeTrue()) }) It("reads the connection ID", func() { buf := bytes.NewBuffer([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9}) c, err := ReadConnectionID(buf, 9) Expect(err).ToNot(HaveOccurred()) Expect(c.Bytes()).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9})) }) It("returns io.EOF if there's not enough data to read", func() { buf := bytes.NewBuffer([]byte{1, 2, 3, 4}) _, err := ReadConnectionID(buf, 5) Expect(err).To(MatchError(io.EOF)) }) It("returns a 0 length connection ID", func() { buf := bytes.NewBuffer([]byte{1, 2, 3, 4}) c, err := ReadConnectionID(buf, 0) Expect(err).ToNot(HaveOccurred()) Expect(c.Len()).To(BeZero()) }) It("errors when trying to read a too long connection ID", func() { buf := bytes.NewBuffer(make([]byte, 21)) _, err := ReadConnectionID(buf, 21) Expect(err).To(MatchError(ErrInvalidConnectionIDLen)) }) It("returns the length", func() { c := ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7}) Expect(c.Len()).To(Equal(7)) }) It("has 0 length for the default value", func() { var c ConnectionID Expect(c.Len()).To(BeZero()) }) It("returns the bytes", func() { c := ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7}) Expect(c.Bytes()).To(Equal([]byte{1, 2, 3, 4, 5, 6, 7})) }) It("returns a nil byte slice for the default value", func() { var c ConnectionID Expect(c.Bytes()).To(HaveLen(0)) }) It("has a string representation", func() { c := ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0x42}) Expect(c.String()).To(Equal("deadbeef42")) }) It("has a long string representation", func() { c := ParseConnectionID([]byte{0x13, 0x37, 0, 0, 0xde, 0xca, 0xfb, 0xad}) Expect(c.String()).To(Equal("13370000decafbad")) }) It("has a string representation for the default value", func() { var c ConnectionID Expect(c.String()).To(Equal("(empty)")) }) Context("arbitrary length connection IDs", func() { It("returns the bytes", func() { b := make([]byte, 30) rand.Read(b) c := ArbitraryLenConnectionID(b) Expect(c.Bytes()).To(Equal(b)) }) It("returns the length", func() { c := ArbitraryLenConnectionID(make([]byte, 156)) Expect(c.Len()).To(Equal(156)) }) It("has a string representation", func() { c := ArbitraryLenConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0x42}) Expect(c.String()).To(Equal("deadbeef42")) }) It("has a string representation for the default value", func() { var c ArbitraryLenConnectionID Expect(c.String()).To(Equal("(empty)")) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/encryption_level.go000066400000000000000000000012611465664453100301740ustar00rootroot00000000000000package protocol // EncryptionLevel is the encryption level // Default value is Unencrypted type EncryptionLevel uint8 const ( // EncryptionInitial is the Initial encryption level EncryptionInitial EncryptionLevel = 1 + iota // EncryptionHandshake is the Handshake encryption level EncryptionHandshake // Encryption0RTT is the 0-RTT encryption level Encryption0RTT // Encryption1RTT is the 1-RTT encryption level Encryption1RTT ) func (e EncryptionLevel) String() string { switch e { case EncryptionInitial: return "Initial" case EncryptionHandshake: return "Handshake" case Encryption0RTT: return "0-RTT" case Encryption1RTT: return "1-RTT" } return "unknown" } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/encryption_level_test.go000066400000000000000000000011251465664453100312320ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Encryption Level", func() { It("doesn't use 0 as a value", func() { // 0 is used in some tests Expect(EncryptionInitial * EncryptionHandshake * Encryption0RTT * Encryption1RTT).ToNot(BeZero()) }) It("has the correct string representation", func() { Expect(EncryptionInitial.String()).To(Equal("Initial")) Expect(EncryptionHandshake.String()).To(Equal("Handshake")) Expect(Encryption0RTT.String()).To(Equal("0-RTT")) Expect(Encryption1RTT.String()).To(Equal("1-RTT")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/key_phase.go000066400000000000000000000011621465664453100265630ustar00rootroot00000000000000package protocol // KeyPhase is the key phase type KeyPhase uint64 // Bit determines the key phase bit func (p KeyPhase) Bit() KeyPhaseBit { if p%2 == 0 { return KeyPhaseZero } return KeyPhaseOne } // KeyPhaseBit is the key phase bit type KeyPhaseBit uint8 const ( // KeyPhaseUndefined is an undefined key phase KeyPhaseUndefined KeyPhaseBit = iota // KeyPhaseZero is key phase 0 KeyPhaseZero // KeyPhaseOne is key phase 1 KeyPhaseOne ) func (p KeyPhaseBit) String() string { //nolint:exhaustive switch p { case KeyPhaseZero: return "0" case KeyPhaseOne: return "1" default: return "undefined" } } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/key_phase_test.go000066400000000000000000000014061465664453100276230ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Key Phases", func() { It("has undefined as its default value", func() { var k KeyPhaseBit Expect(k).To(Equal(KeyPhaseUndefined)) }) It("has the correct string representation", func() { Expect(KeyPhaseZero.String()).To(Equal("0")) Expect(KeyPhaseOne.String()).To(Equal("1")) }) It("converts the key phase to the key phase bit", func() { Expect(KeyPhase(0).Bit()).To(Equal(KeyPhaseZero)) Expect(KeyPhase(2).Bit()).To(Equal(KeyPhaseZero)) Expect(KeyPhase(4).Bit()).To(Equal(KeyPhaseZero)) Expect(KeyPhase(1).Bit()).To(Equal(KeyPhaseOne)) Expect(KeyPhase(3).Bit()).To(Equal(KeyPhaseOne)) Expect(KeyPhase(5).Bit()).To(Equal(KeyPhaseOne)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/packet_number.go000066400000000000000000000044321465664453100274350ustar00rootroot00000000000000package protocol // A PacketNumber in QUIC type PacketNumber int64 // InvalidPacketNumber is a packet number that is never sent. // In QUIC, 0 is a valid packet number. const InvalidPacketNumber PacketNumber = -1 // PacketNumberLen is the length of the packet number in bytes type PacketNumberLen uint8 const ( // PacketNumberLen1 is a packet number length of 1 byte PacketNumberLen1 PacketNumberLen = 1 // PacketNumberLen2 is a packet number length of 2 bytes PacketNumberLen2 PacketNumberLen = 2 // PacketNumberLen3 is a packet number length of 3 bytes PacketNumberLen3 PacketNumberLen = 3 // PacketNumberLen4 is a packet number length of 4 bytes PacketNumberLen4 PacketNumberLen = 4 ) // DecodePacketNumber calculates the packet number based on the received packet number, its length and the last seen packet number func DecodePacketNumber( packetNumberLength PacketNumberLen, lastPacketNumber PacketNumber, wirePacketNumber PacketNumber, ) PacketNumber { var epochDelta PacketNumber switch packetNumberLength { case PacketNumberLen1: epochDelta = PacketNumber(1) << 8 case PacketNumberLen2: epochDelta = PacketNumber(1) << 16 case PacketNumberLen3: epochDelta = PacketNumber(1) << 24 case PacketNumberLen4: epochDelta = PacketNumber(1) << 32 } epoch := lastPacketNumber & ^(epochDelta - 1) var prevEpochBegin PacketNumber if epoch > epochDelta { prevEpochBegin = epoch - epochDelta } nextEpochBegin := epoch + epochDelta return closestTo( lastPacketNumber+1, epoch+wirePacketNumber, closestTo(lastPacketNumber+1, prevEpochBegin+wirePacketNumber, nextEpochBegin+wirePacketNumber), ) } func closestTo(target, a, b PacketNumber) PacketNumber { if delta(target, a) < delta(target, b) { return a } return b } func delta(a, b PacketNumber) PacketNumber { if a < b { return b - a } return a - b } // GetPacketNumberLengthForHeader gets the length of the packet number for the public header // it never chooses a PacketNumberLen of 1 byte, since this is too short under certain circumstances func GetPacketNumberLengthForHeader(packetNumber, leastUnacked PacketNumber) PacketNumberLen { diff := uint64(packetNumber - leastUnacked) if diff < (1 << (16 - 1)) { return PacketNumberLen2 } if diff < (1 << (24 - 1)) { return PacketNumberLen3 } return PacketNumberLen4 } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/packet_number_test.go000066400000000000000000000154451465664453100305020ustar00rootroot00000000000000package protocol import ( "fmt" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) // Tests taken and extended from chrome var _ = Describe("packet number calculation", func() { It("InvalidPacketNumber is smaller than all valid packet numbers", func() { Expect(InvalidPacketNumber).To(BeNumerically("<", 0)) }) It("works with the example from the draft", func() { Expect(DecodePacketNumber(PacketNumberLen2, 0xa82f30ea, 0x9b32)).To(Equal(PacketNumber(0xa82f9b32))) }) It("works with the examples from the draft", func() { Expect(GetPacketNumberLengthForHeader(0xac5c02, 0xabe8b3)).To(Equal(PacketNumberLen2)) Expect(GetPacketNumberLengthForHeader(0xace8fe, 0xabe8b3)).To(Equal(PacketNumberLen3)) }) getEpoch := func(len PacketNumberLen) uint64 { if len > 4 { Fail("invalid packet number len") } return uint64(1) << (len * 8) } check := func(length PacketNumberLen, expected, last uint64) { epoch := getEpoch(length) epochMask := epoch - 1 wirePacketNumber := expected & epochMask ExpectWithOffset(1, DecodePacketNumber(length, PacketNumber(last), PacketNumber(wirePacketNumber))).To(Equal(PacketNumber(expected))) } for _, l := range []PacketNumberLen{PacketNumberLen1, PacketNumberLen2, PacketNumberLen3, PacketNumberLen4} { length := l Context(fmt.Sprintf("with %d bytes", length), func() { epoch := getEpoch(length) epochMask := epoch - 1 It("works near epoch start", func() { // A few quick manual sanity check check(length, 1, 0) check(length, epoch+1, epochMask) check(length, epoch, epochMask) // Cases where the last number was close to the start of the range. for last := uint64(0); last < 10; last++ { // Small numbers should not wrap (even if they're out of order). for j := uint64(0); j < 10; j++ { check(length, j, last) } // Large numbers should not wrap either (because we're near 0 already). for j := uint64(0); j < 10; j++ { check(length, epoch-1-j, last) } } }) It("works near epoch end", func() { // Cases where the last number was close to the end of the range for i := uint64(0); i < 10; i++ { last := epoch - i // Small numbers should wrap. for j := uint64(0); j < 10; j++ { check(length, epoch+j, last) } // Large numbers should not (even if they're out of order). for j := uint64(0); j < 10; j++ { check(length, epoch-1-j, last) } } }) // Next check where we're in a non-zero epoch to verify we handle // reverse wrapping, too. It("works near previous epoch", func() { prevEpoch := 1 * epoch curEpoch := 2 * epoch // Cases where the last number was close to the start of the range for i := uint64(0); i < 10; i++ { last := curEpoch + i // Small number should not wrap (even if they're out of order). for j := uint64(0); j < 10; j++ { check(length, curEpoch+j, last) } // But large numbers should reverse wrap. for j := uint64(0); j < 10; j++ { num := epoch - 1 - j check(length, prevEpoch+num, last) } } }) It("works near next epoch", func() { curEpoch := 2 * epoch nextEpoch := 3 * epoch // Cases where the last number was close to the end of the range for i := uint64(0); i < 10; i++ { last := nextEpoch - 1 - i // Small numbers should wrap. for j := uint64(0); j < 10; j++ { check(length, nextEpoch+j, last) } // but large numbers should not (even if they're out of order). for j := uint64(0); j < 10; j++ { num := epoch - 1 - j check(length, curEpoch+num, last) } } }) Context("shortening a packet number for the header", func() { Context("shortening", func() { It("sends out low packet numbers as 2 byte", func() { length := GetPacketNumberLengthForHeader(4, 2) Expect(length).To(Equal(PacketNumberLen2)) }) It("sends out high packet numbers as 2 byte, if all ACKs are received", func() { length := GetPacketNumberLengthForHeader(0xdeadbeef, 0xdeadbeef-1) Expect(length).To(Equal(PacketNumberLen2)) }) It("sends out higher packet numbers as 3 bytes, if a lot of ACKs are missing", func() { length := GetPacketNumberLengthForHeader(40000, 2) Expect(length).To(Equal(PacketNumberLen3)) }) It("sends out higher packet numbers as 4 bytes, if a lot of ACKs are missing", func() { length := GetPacketNumberLengthForHeader(40000000, 2) Expect(length).To(Equal(PacketNumberLen4)) }) }) Context("self-consistency", func() { It("works for small packet numbers", func() { for i := uint64(1); i < 10000; i++ { packetNumber := PacketNumber(i) leastUnacked := PacketNumber(1) length := GetPacketNumberLengthForHeader(packetNumber, leastUnacked) wirePacketNumber := (uint64(packetNumber) << (64 - length*8)) >> (64 - length*8) decodedPacketNumber := DecodePacketNumber(length, leastUnacked, PacketNumber(wirePacketNumber)) Expect(decodedPacketNumber).To(Equal(packetNumber)) } }) It("works for small packet numbers and increasing ACKed packets", func() { for i := uint64(1); i < 10000; i++ { packetNumber := PacketNumber(i) leastUnacked := PacketNumber(i / 2) length := GetPacketNumberLengthForHeader(packetNumber, leastUnacked) epochMask := getEpoch(length) - 1 wirePacketNumber := uint64(packetNumber) & epochMask decodedPacketNumber := DecodePacketNumber(length, leastUnacked, PacketNumber(wirePacketNumber)) Expect(decodedPacketNumber).To(Equal(packetNumber)) } }) It("also works for larger packet numbers", func() { var increment uint64 for i := uint64(1); i < getEpoch(PacketNumberLen4); i += increment { packetNumber := PacketNumber(i) leastUnacked := PacketNumber(1) length := GetPacketNumberLengthForHeader(packetNumber, leastUnacked) epochMask := getEpoch(length) - 1 wirePacketNumber := uint64(packetNumber) & epochMask decodedPacketNumber := DecodePacketNumber(length, leastUnacked, PacketNumber(wirePacketNumber)) Expect(decodedPacketNumber).To(Equal(packetNumber)) increment = getEpoch(length) / 8 } }) It("works for packet numbers larger than 2^48", func() { for i := (uint64(1) << 48); i < ((uint64(1) << 63) - 1); i += (uint64(1) << 48) { packetNumber := PacketNumber(i) leastUnacked := PacketNumber(i - 1000) length := GetPacketNumberLengthForHeader(packetNumber, leastUnacked) wirePacketNumber := (uint64(packetNumber) << (64 - length*8)) >> (64 - length*8) decodedPacketNumber := DecodePacketNumber(length, leastUnacked, PacketNumber(wirePacketNumber)) Expect(decodedPacketNumber).To(Equal(packetNumber)) } }) }) }) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/params.go000066400000000000000000000215571465664453100261100ustar00rootroot00000000000000package protocol import "time" // DesiredReceiveBufferSize is the kernel UDP receive buffer size that we'd like to use. const DesiredReceiveBufferSize = (1 << 20) * 7 // 7 MB // DesiredSendBufferSize is the kernel UDP send buffer size that we'd like to use. const DesiredSendBufferSize = (1 << 20) * 7 // 7 MB // InitialPacketSize is the initial (before Path MTU discovery) maximum packet size used. const InitialPacketSize = 1280 // MaxCongestionWindowPackets is the maximum congestion window in packet. const MaxCongestionWindowPackets = 10000 // MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the connection. const MaxUndecryptablePackets = 32 // ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window // This is the value that Chromium is using const ConnectionFlowControlMultiplier = 1.5 // DefaultInitialMaxStreamData is the default initial stream-level flow control window for receiving data const DefaultInitialMaxStreamData = (1 << 10) * 512 // 512 kb // DefaultInitialMaxData is the connection-level flow control window for receiving data const DefaultInitialMaxData = ConnectionFlowControlMultiplier * DefaultInitialMaxStreamData // DefaultMaxReceiveStreamFlowControlWindow is the default maximum stream-level flow control window for receiving data const DefaultMaxReceiveStreamFlowControlWindow = 6 * (1 << 20) // 6 MB // DefaultMaxReceiveConnectionFlowControlWindow is the default connection-level flow control window for receiving data const DefaultMaxReceiveConnectionFlowControlWindow = 15 * (1 << 20) // 15 MB // WindowUpdateThreshold is the fraction of the receive window that has to be consumed before an higher offset is advertised to the client const WindowUpdateThreshold = 0.25 // DefaultMaxIncomingStreams is the maximum number of streams that a peer may open const DefaultMaxIncomingStreams = 100 // DefaultMaxIncomingUniStreams is the maximum number of unidirectional streams that a peer may open const DefaultMaxIncomingUniStreams = 100 // MaxServerUnprocessedPackets is the max number of packets stored in the server that are not yet processed. const MaxServerUnprocessedPackets = 1024 // MaxConnUnprocessedPackets is the max number of packets stored in each connection that are not yet processed. const MaxConnUnprocessedPackets = 256 // SkipPacketInitialPeriod is the initial period length used for packet number skipping to prevent an Optimistic ACK attack. // Every time a packet number is skipped, the period is doubled, up to SkipPacketMaxPeriod. const SkipPacketInitialPeriod PacketNumber = 256 // SkipPacketMaxPeriod is the maximum period length used for packet number skipping. const SkipPacketMaxPeriod PacketNumber = 128 * 1024 // MaxAcceptQueueSize is the maximum number of connections that the server queues for accepting. // If the queue is full, new connection attempts will be rejected. const MaxAcceptQueueSize = 32 // TokenValidity is the duration that a (non-retry) token is considered valid const TokenValidity = 24 * time.Hour // MaxOutstandingSentPackets is maximum number of packets saved for retransmission. // When reached, it imposes a soft limit on sending new packets: // Sending ACKs and retransmission is still allowed, but now new regular packets can be sent. const MaxOutstandingSentPackets = 2 * MaxCongestionWindowPackets // MaxTrackedSentPackets is maximum number of sent packets saved for retransmission. // When reached, no more packets will be sent. // This value *must* be larger than MaxOutstandingSentPackets. const MaxTrackedSentPackets = MaxOutstandingSentPackets * 5 / 4 // MaxNonAckElicitingAcks is the maximum number of packets containing an ACK, // but no ack-eliciting frames, that we send in a row const MaxNonAckElicitingAcks = 19 // MaxStreamFrameSorterGaps is the maximum number of gaps between received StreamFrames // prevents DoS attacks against the streamFrameSorter const MaxStreamFrameSorterGaps = 1000 // MinStreamFrameBufferSize is the minimum data length of a received STREAM frame // that we use the buffer for. This protects against a DoS where an attacker would send us // very small STREAM frames to consume a lot of memory. const MinStreamFrameBufferSize = 128 // MinCoalescedPacketSize is the minimum size of a coalesced packet that we pack. // If a packet has less than this number of bytes, we won't coalesce any more packets onto it. const MinCoalescedPacketSize = 128 // MaxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams. // This limits the size of the ClientHello and Certificates that can be received. const MaxCryptoStreamOffset = 16 * (1 << 10) // MinRemoteIdleTimeout is the minimum value that we accept for the remote idle timeout const MinRemoteIdleTimeout = 5 * time.Second // DefaultIdleTimeout is the default idle timeout const DefaultIdleTimeout = 30 * time.Second // DefaultHandshakeIdleTimeout is the default idle timeout used before handshake completion. const DefaultHandshakeIdleTimeout = 5 * time.Second // MaxKeepAliveInterval is the maximum time until we send a packet to keep a connection alive. // It should be shorter than the time that NATs clear their mapping. const MaxKeepAliveInterval = 20 * time.Second // RetiredConnectionIDDeleteTimeout is the time we keep closed connections around in order to retransmit the CONNECTION_CLOSE. // after this time all information about the old connection will be deleted const RetiredConnectionIDDeleteTimeout = 5 * time.Second // MinStreamFrameSize is the minimum size that has to be left in a packet, so that we add another STREAM frame. // This avoids splitting up STREAM frames into small pieces, which has 2 advantages: // 1. it reduces the framing overhead // 2. it reduces the head-of-line blocking, when a packet is lost const MinStreamFrameSize ByteCount = 128 // MaxPostHandshakeCryptoFrameSize is the maximum size of CRYPTO frames // we send after the handshake completes. const MaxPostHandshakeCryptoFrameSize = 1000 // MaxAckFrameSize is the maximum size for an ACK frame that we write // Due to the varint encoding, ACK frames can grow (almost) indefinitely large. // The MaxAckFrameSize should be large enough to encode many ACK range, // but must ensure that a maximum size ACK frame fits into one packet. const MaxAckFrameSize ByteCount = 1000 // MaxNumAckRanges is the maximum number of ACK ranges that we send in an ACK frame. // It also serves as a limit for the packet history. // If at any point we keep track of more ranges, old ranges are discarded. const MaxNumAckRanges = 32 // MinPacingDelay is the minimum duration that is used for packet pacing // If the packet packing frequency is higher, multiple packets might be sent at once. // Example: For a packet pacing delay of 200μs, we would send 5 packets at once, wait for 1ms, and so forth. const MinPacingDelay = time.Millisecond // DefaultConnectionIDLength is the connection ID length that is used for multiplexed connections // if no other value is configured. const DefaultConnectionIDLength = 4 // MaxActiveConnectionIDs is the number of connection IDs that we're storing. const MaxActiveConnectionIDs = 4 // MaxIssuedConnectionIDs is the maximum number of connection IDs that we're issuing at the same time. const MaxIssuedConnectionIDs = 6 // PacketsPerConnectionID is the number of packets we send using one connection ID. // If the peer provices us with enough new connection IDs, we switch to a new connection ID. const PacketsPerConnectionID = 10000 // AckDelayExponent is the ack delay exponent used when sending ACKs. const AckDelayExponent = 3 // Estimated timer granularity. // The loss detection timer will not be set to a value smaller than granularity. const TimerGranularity = time.Millisecond // MaxAckDelay is the maximum time by which we delay sending ACKs. const MaxAckDelay = 25 * time.Millisecond // MaxAckDelayInclGranularity is the max_ack_delay including the timer granularity. // This is the value that should be advertised to the peer. const MaxAckDelayInclGranularity = MaxAckDelay + TimerGranularity // KeyUpdateInterval is the maximum number of packets we send or receive before initiating a key update. const KeyUpdateInterval = 100 * 1000 // Max0RTTQueueingDuration is the maximum time that we store 0-RTT packets in order to wait for the corresponding Initial to be received. const Max0RTTQueueingDuration = 100 * time.Millisecond // Max0RTTQueues is the maximum number of connections that we buffer 0-RTT packets for. const Max0RTTQueues = 32 // Max0RTTQueueLen is the maximum number of 0-RTT packets that we buffer for each connection. // When a new connection is created, all buffered packets are passed to the connection immediately. // To avoid blocking, this value has to be smaller than MaxConnUnprocessedPackets. // To avoid packets being dropped as undecryptable by the connection, this value has to be smaller than MaxUndecryptablePackets. const Max0RTTQueueLen = 31 golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/params_test.go000066400000000000000000000005541465664453100271410ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Parameters", func() { It("can queue more packets in the session than in the 0-RTT queue", func() { Expect(MaxConnUnprocessedPackets).To(BeNumerically(">", Max0RTTQueueLen)) Expect(MaxUndecryptablePackets).To(BeNumerically(">", Max0RTTQueueLen)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/perspective.go000066400000000000000000000007651465664453100271540ustar00rootroot00000000000000package protocol // Perspective determines if we're acting as a server or a client type Perspective int // the perspectives const ( PerspectiveServer Perspective = 1 PerspectiveClient Perspective = 2 ) // Opposite returns the perspective of the peer func (p Perspective) Opposite() Perspective { return 3 - p } func (p Perspective) String() string { switch p { case PerspectiveServer: return "server" case PerspectiveClient: return "client" default: return "invalid perspective" } } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/perspective_test.go000066400000000000000000000010341465664453100302010ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Perspective", func() { It("has a string representation", func() { Expect(PerspectiveClient.String()).To(Equal("client")) Expect(PerspectiveServer.String()).To(Equal("server")) Expect(Perspective(0).String()).To(Equal("invalid perspective")) }) It("returns the opposite", func() { Expect(PerspectiveClient.Opposite()).To(Equal(PerspectiveServer)) Expect(PerspectiveServer.Opposite()).To(Equal(PerspectiveClient)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/protocol.go000066400000000000000000000077301465664453100264630ustar00rootroot00000000000000package protocol import ( "fmt" "time" ) // The PacketType is the Long Header Type type PacketType uint8 const ( // PacketTypeInitial is the packet type of an Initial packet PacketTypeInitial PacketType = 1 + iota // PacketTypeRetry is the packet type of a Retry packet PacketTypeRetry // PacketTypeHandshake is the packet type of a Handshake packet PacketTypeHandshake // PacketType0RTT is the packet type of a 0-RTT packet PacketType0RTT ) func (t PacketType) String() string { switch t { case PacketTypeInitial: return "Initial" case PacketTypeRetry: return "Retry" case PacketTypeHandshake: return "Handshake" case PacketType0RTT: return "0-RTT Protected" default: return fmt.Sprintf("unknown packet type: %d", t) } } type ECN uint8 const ( ECNUnsupported ECN = iota ECNNon // 00 ECT1 // 01 ECT0 // 10 ECNCE // 11 ) func ParseECNHeaderBits(bits byte) ECN { switch bits { case 0: return ECNNon case 0b00000010: return ECT0 case 0b00000001: return ECT1 case 0b00000011: return ECNCE default: panic("invalid ECN bits") } } func (e ECN) ToHeaderBits() byte { //nolint:exhaustive // There are only 4 values. switch e { case ECNNon: return 0 case ECT0: return 0b00000010 case ECT1: return 0b00000001 case ECNCE: return 0b00000011 default: panic("ECN unsupported") } } func (e ECN) String() string { switch e { case ECNUnsupported: return "ECN unsupported" case ECNNon: return "Not-ECT" case ECT1: return "ECT(1)" case ECT0: return "ECT(0)" case ECNCE: return "CE" default: return fmt.Sprintf("invalid ECN value: %d", e) } } // A ByteCount in QUIC type ByteCount int64 // MaxByteCount is the maximum value of a ByteCount const MaxByteCount = ByteCount(1<<62 - 1) // InvalidByteCount is an invalid byte count const InvalidByteCount ByteCount = -1 // A StatelessResetToken is a stateless reset token. type StatelessResetToken [16]byte // MaxPacketBufferSize maximum packet size of any QUIC packet, based on // ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header, // UDP adds an additional 8 bytes. This is a total overhead of 48 bytes. // Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452. const MaxPacketBufferSize = 1452 // MaxLargePacketBufferSize is used when using GSO const MaxLargePacketBufferSize = 20 * 1024 // MinInitialPacketSize is the minimum size an Initial packet is required to have. const MinInitialPacketSize = 1200 // MinUnknownVersionPacketSize is the minimum size a packet with an unknown version // needs to have in order to trigger a Version Negotiation packet. const MinUnknownVersionPacketSize = MinInitialPacketSize // MinStatelessResetSize is the minimum size of a stateless reset packet that we send const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */ // MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet. const MinConnectionIDLenInitial = 8 // DefaultAckDelayExponent is the default ack delay exponent const DefaultAckDelayExponent = 3 // DefaultActiveConnectionIDLimit is the default active connection ID limit const DefaultActiveConnectionIDLimit = 2 // MaxAckDelayExponent is the maximum ack delay exponent const MaxAckDelayExponent = 20 // DefaultMaxAckDelay is the default max_ack_delay const DefaultMaxAckDelay = 25 * time.Millisecond // MaxMaxAckDelay is the maximum max_ack_delay const MaxMaxAckDelay = (1<<14 - 1) * time.Millisecond // MaxConnIDLen is the maximum length of the connection ID const MaxConnIDLen = 20 // InvalidPacketLimitAES is the maximum number of packets that we can fail to decrypt when using // AEAD_AES_128_GCM or AEAD_AES_265_GCM. const InvalidPacketLimitAES = 1 << 52 // InvalidPacketLimitChaCha is the maximum number of packets that we can fail to decrypt when using AEAD_CHACHA20_POLY1305. const InvalidPacketLimitChaCha = 1 << 36 golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/protocol_suite_test.go000066400000000000000000000003031465664453100307200ustar00rootroot00000000000000package protocol import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestProtocol(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Protocol Suite") } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/protocol_test.go000066400000000000000000000027431465664453100275210ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Protocol", func() { Context("Long Header Packet Types", func() { It("has the correct string representation", func() { Expect(PacketTypeInitial.String()).To(Equal("Initial")) Expect(PacketTypeRetry.String()).To(Equal("Retry")) Expect(PacketTypeHandshake.String()).To(Equal("Handshake")) Expect(PacketType0RTT.String()).To(Equal("0-RTT Protected")) Expect(PacketType(10).String()).To(Equal("unknown packet type: 10")) }) }) It("converts ECN bits from the IP header wire to the correct types", func() { Expect(ParseECNHeaderBits(0)).To(Equal(ECNNon)) Expect(ParseECNHeaderBits(0b00000010)).To(Equal(ECT0)) Expect(ParseECNHeaderBits(0b00000001)).To(Equal(ECT1)) Expect(ParseECNHeaderBits(0b00000011)).To(Equal(ECNCE)) Expect(func() { ParseECNHeaderBits(0b1010101) }).To(Panic()) }) It("converts to IP header bits", func() { for _, v := range [...]ECN{ECNNon, ECT0, ECT1, ECNCE} { Expect(ParseECNHeaderBits(v.ToHeaderBits())).To(Equal(v)) } Expect(func() { ECN(42).ToHeaderBits() }).To(Panic()) }) It("has a string representation for ECN", func() { Expect(ECNUnsupported.String()).To(Equal("ECN unsupported")) Expect(ECNNon.String()).To(Equal("Not-ECT")) Expect(ECT0.String()).To(Equal("ECT(0)")) Expect(ECT1.String()).To(Equal("ECT(1)")) Expect(ECNCE.String()).To(Equal("CE")) Expect(ECN(42).String()).To(Equal("invalid ECN value: 42")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/stream.go000066400000000000000000000034101465664453100261040ustar00rootroot00000000000000package protocol // StreamType encodes if this is a unidirectional or bidirectional stream type StreamType uint8 const ( // StreamTypeUni is a unidirectional stream StreamTypeUni StreamType = iota // StreamTypeBidi is a bidirectional stream StreamTypeBidi ) // InvalidPacketNumber is a stream ID that is invalid. // The first valid stream ID in QUIC is 0. const InvalidStreamID StreamID = -1 // StreamNum is the stream number type StreamNum int64 const ( // InvalidStreamNum is an invalid stream number. InvalidStreamNum = -1 // MaxStreamCount is the maximum stream count value that can be sent in MAX_STREAMS frames // and as the stream count in the transport parameters MaxStreamCount StreamNum = 1 << 60 ) // StreamID calculates the stream ID. func (s StreamNum) StreamID(stype StreamType, pers Perspective) StreamID { if s == 0 { return InvalidStreamID } var first StreamID switch stype { case StreamTypeBidi: switch pers { case PerspectiveClient: first = 0 case PerspectiveServer: first = 1 } case StreamTypeUni: switch pers { case PerspectiveClient: first = 2 case PerspectiveServer: first = 3 } } return first + 4*StreamID(s-1) } // A StreamID in QUIC type StreamID int64 // InitiatedBy says if the stream was initiated by the client or by the server func (s StreamID) InitiatedBy() Perspective { if s%2 == 0 { return PerspectiveClient } return PerspectiveServer } // Type says if this is a unidirectional or bidirectional stream func (s StreamID) Type() StreamType { if s%4 >= 2 { return StreamTypeUni } return StreamTypeBidi } // StreamNum returns how many streams in total are below this // Example: for stream 9 it returns 3 (i.e. streams 1, 5 and 9) func (s StreamID) StreamNum() StreamNum { return StreamNum(s/4) + 1 } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/stream_test.go000066400000000000000000000057571465664453100271630ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Stream ID", func() { It("InvalidStreamID is smaller than all valid stream IDs", func() { Expect(InvalidStreamID).To(BeNumerically("<", 0)) }) It("says who initiated a stream", func() { Expect(StreamID(4).InitiatedBy()).To(Equal(PerspectiveClient)) Expect(StreamID(5).InitiatedBy()).To(Equal(PerspectiveServer)) Expect(StreamID(6).InitiatedBy()).To(Equal(PerspectiveClient)) Expect(StreamID(7).InitiatedBy()).To(Equal(PerspectiveServer)) }) It("tells the directionality", func() { Expect(StreamID(4).Type()).To(Equal(StreamTypeBidi)) Expect(StreamID(5).Type()).To(Equal(StreamTypeBidi)) Expect(StreamID(6).Type()).To(Equal(StreamTypeUni)) Expect(StreamID(7).Type()).To(Equal(StreamTypeUni)) }) It("tells the stream number", func() { Expect(StreamID(0).StreamNum()).To(BeEquivalentTo(1)) Expect(StreamID(1).StreamNum()).To(BeEquivalentTo(1)) Expect(StreamID(2).StreamNum()).To(BeEquivalentTo(1)) Expect(StreamID(3).StreamNum()).To(BeEquivalentTo(1)) Expect(StreamID(8).StreamNum()).To(BeEquivalentTo(3)) Expect(StreamID(9).StreamNum()).To(BeEquivalentTo(3)) Expect(StreamID(10).StreamNum()).To(BeEquivalentTo(3)) Expect(StreamID(11).StreamNum()).To(BeEquivalentTo(3)) }) Context("converting stream nums to stream IDs", func() { It("handles 0", func() { Expect(StreamNum(0).StreamID(StreamTypeBidi, PerspectiveClient)).To(Equal(InvalidStreamID)) Expect(StreamNum(0).StreamID(StreamTypeBidi, PerspectiveServer)).To(Equal(InvalidStreamID)) Expect(StreamNum(0).StreamID(StreamTypeUni, PerspectiveClient)).To(Equal(InvalidStreamID)) Expect(StreamNum(0).StreamID(StreamTypeUni, PerspectiveServer)).To(Equal(InvalidStreamID)) }) It("handles the first", func() { Expect(StreamNum(1).StreamID(StreamTypeBidi, PerspectiveClient)).To(Equal(StreamID(0))) Expect(StreamNum(1).StreamID(StreamTypeBidi, PerspectiveServer)).To(Equal(StreamID(1))) Expect(StreamNum(1).StreamID(StreamTypeUni, PerspectiveClient)).To(Equal(StreamID(2))) Expect(StreamNum(1).StreamID(StreamTypeUni, PerspectiveServer)).To(Equal(StreamID(3))) }) It("handles others", func() { Expect(StreamNum(100).StreamID(StreamTypeBidi, PerspectiveClient)).To(Equal(StreamID(396))) Expect(StreamNum(100).StreamID(StreamTypeBidi, PerspectiveServer)).To(Equal(StreamID(397))) Expect(StreamNum(100).StreamID(StreamTypeUni, PerspectiveClient)).To(Equal(StreamID(398))) Expect(StreamNum(100).StreamID(StreamTypeUni, PerspectiveServer)).To(Equal(StreamID(399))) }) It("has the right value for MaxStreamCount", func() { const maxStreamID = StreamID(1<<62 - 1) for _, dir := range []StreamType{StreamTypeUni, StreamTypeBidi} { for _, pers := range []Perspective{PerspectiveClient, PerspectiveServer} { Expect(MaxStreamCount.StreamID(dir, pers)).To(BeNumerically("<=", maxStreamID)) Expect((MaxStreamCount + 1).StreamID(dir, pers)).To(BeNumerically(">", maxStreamID)) } } }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/version.go000066400000000000000000000061541465664453100263060ustar00rootroot00000000000000package protocol import ( "encoding/binary" "fmt" "math" "sync" "time" "golang.org/x/exp/rand" ) // Version is a version number as int type Version uint32 // gQUIC version range as defined in the wiki: https://github.com/quicwg/base-drafts/wiki/QUIC-Versions const ( gquicVersion0 = 0x51303030 maxGquicVersion = 0x51303439 ) // The version numbers, making grepping easier const ( VersionUnknown Version = math.MaxUint32 versionDraft29 Version = 0xff00001d // draft-29 used to be a widely deployed version Version1 Version = 0x1 Version2 Version = 0x6b3343cf ) // SupportedVersions lists the versions that the server supports // must be in sorted descending order var SupportedVersions = []Version{Version1, Version2} // IsValidVersion says if the version is known to quic-go func IsValidVersion(v Version) bool { return v == Version1 || IsSupportedVersion(SupportedVersions, v) } func (vn Version) String() string { //nolint:exhaustive switch vn { case VersionUnknown: return "unknown" case versionDraft29: return "draft-29" case Version1: return "v1" case Version2: return "v2" default: if vn.isGQUIC() { return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion()) } return fmt.Sprintf("%#x", uint32(vn)) } } func (vn Version) isGQUIC() bool { return vn > gquicVersion0 && vn <= maxGquicVersion } func (vn Version) toGQUICVersion() int { return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10) } // IsSupportedVersion returns true if the server supports this version func IsSupportedVersion(supported []Version, v Version) bool { for _, t := range supported { if t == v { return true } } return false } // ChooseSupportedVersion finds the best version in the overlap of ours and theirs // ours is a slice of versions that we support, sorted by our preference (descending) // theirs is a slice of versions offered by the peer. The order does not matter. // The bool returned indicates if a matching version was found. func ChooseSupportedVersion(ours, theirs []Version) (Version, bool) { for _, ourVer := range ours { for _, theirVer := range theirs { if ourVer == theirVer { return ourVer, true } } } return 0, false } var ( versionNegotiationMx sync.Mutex versionNegotiationRand = rand.New(rand.NewSource(uint64(time.Now().UnixNano()))) ) // generateReservedVersion generates a reserved version (v & 0x0f0f0f0f == 0x0a0a0a0a) func generateReservedVersion() Version { var b [4]byte _, _ = versionNegotiationRand.Read(b[:]) // ignore the error here. Failure to read random data doesn't break anything return Version((binary.BigEndian.Uint32(b[:]) | 0x0a0a0a0a) & 0xfafafafa) } // GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position. // It doesn't modify the supported slice. func GetGreasedVersions(supported []Version) []Version { versionNegotiationMx.Lock() defer versionNegotiationMx.Unlock() randPos := rand.Intn(len(supported) + 1) greased := make([]Version, len(supported)+1) copy(greased, supported[:randPos]) greased[randPos] = generateReservedVersion() copy(greased[randPos+1:], supported[randPos:]) return greased } golang-github-lucas-clemente-quic-go-0.46.0/internal/protocol/version_test.go000066400000000000000000000073041465664453100273430ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Version", func() { isReservedVersion := func(v Version) bool { return v&0x0f0f0f0f == 0x0a0a0a0a } It("says if a version is valid", func() { Expect(IsValidVersion(VersionUnknown)).To(BeFalse()) Expect(IsValidVersion(versionDraft29)).To(BeFalse()) Expect(IsValidVersion(Version1)).To(BeTrue()) Expect(IsValidVersion(Version2)).To(BeTrue()) Expect(IsValidVersion(1234)).To(BeFalse()) }) It("has the right string representation", func() { Expect(VersionUnknown.String()).To(Equal("unknown")) Expect(versionDraft29.String()).To(Equal("draft-29")) Expect(Version1.String()).To(Equal("v1")) Expect(Version2.String()).To(Equal("v2")) // check with unsupported version numbers from the wiki Expect(Version(0x51303039).String()).To(Equal("gQUIC 9")) Expect(Version(0x51303133).String()).To(Equal("gQUIC 13")) Expect(Version(0x51303235).String()).To(Equal("gQUIC 25")) Expect(Version(0x51303438).String()).To(Equal("gQUIC 48")) Expect(Version(0x01234567).String()).To(Equal("0x1234567")) }) It("recognizes supported versions", func() { Expect(IsSupportedVersion(SupportedVersions, 0)).To(BeFalse()) Expect(IsSupportedVersion(SupportedVersions, SupportedVersions[0])).To(BeTrue()) Expect(IsSupportedVersion(SupportedVersions, SupportedVersions[len(SupportedVersions)-1])).To(BeTrue()) }) Context("highest supported version", func() { It("finds the supported version", func() { supportedVersions := []Version{1, 2, 3} other := []Version{6, 5, 4, 3} ver, ok := ChooseSupportedVersion(supportedVersions, other) Expect(ok).To(BeTrue()) Expect(ver).To(Equal(Version(3))) }) It("picks the preferred version", func() { supportedVersions := []Version{2, 1, 3} other := []Version{3, 6, 1, 8, 2, 10} ver, ok := ChooseSupportedVersion(supportedVersions, other) Expect(ok).To(BeTrue()) Expect(ver).To(Equal(Version(2))) }) It("says when no matching version was found", func() { _, ok := ChooseSupportedVersion([]Version{1}, []Version{2}) Expect(ok).To(BeFalse()) }) It("handles empty inputs", func() { _, ok := ChooseSupportedVersion([]Version{102, 101}, []Version{}) Expect(ok).To(BeFalse()) _, ok = ChooseSupportedVersion([]Version{}, []Version{1, 2}) Expect(ok).To(BeFalse()) _, ok = ChooseSupportedVersion([]Version{}, []Version{}) Expect(ok).To(BeFalse()) }) }) Context("reserved versions", func() { It("adds a greased version if passed an empty slice", func() { greased := GetGreasedVersions([]Version{}) Expect(greased).To(HaveLen(1)) Expect(isReservedVersion(greased[0])).To(BeTrue()) }) It("creates greased lists of version numbers", func() { supported := []Version{10, 18, 29} for _, v := range supported { Expect(isReservedVersion(v)).To(BeFalse()) } var greasedVersionFirst, greasedVersionLast, greasedVersionMiddle int // check that // 1. the greased version sometimes appears first // 2. the greased version sometimes appears in the middle // 3. the greased version sometimes appears last // 4. the supported versions are kept in order for i := 0; i < 100; i++ { greased := GetGreasedVersions(supported) Expect(greased).To(HaveLen(4)) var j int for i, v := range greased { if isReservedVersion(v) { if i == 0 { greasedVersionFirst++ } if i == len(greased)-1 { greasedVersionLast++ } greasedVersionMiddle++ continue } Expect(supported[j]).To(Equal(v)) j++ } } Expect(greasedVersionFirst).ToNot(BeZero()) Expect(greasedVersionLast).ToNot(BeZero()) Expect(greasedVersionMiddle).ToNot(BeZero()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/qerr/000077500000000000000000000000001465664453100233745ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/qerr/error_codes.go000066400000000000000000000047171465664453100262420ustar00rootroot00000000000000package qerr import ( "crypto/tls" "fmt" ) // TransportErrorCode is a QUIC transport error. type TransportErrorCode uint64 // The error codes defined by QUIC const ( NoError TransportErrorCode = 0x0 InternalError TransportErrorCode = 0x1 ConnectionRefused TransportErrorCode = 0x2 FlowControlError TransportErrorCode = 0x3 StreamLimitError TransportErrorCode = 0x4 StreamStateError TransportErrorCode = 0x5 FinalSizeError TransportErrorCode = 0x6 FrameEncodingError TransportErrorCode = 0x7 TransportParameterError TransportErrorCode = 0x8 ConnectionIDLimitError TransportErrorCode = 0x9 ProtocolViolation TransportErrorCode = 0xa InvalidToken TransportErrorCode = 0xb ApplicationErrorErrorCode TransportErrorCode = 0xc CryptoBufferExceeded TransportErrorCode = 0xd KeyUpdateError TransportErrorCode = 0xe AEADLimitReached TransportErrorCode = 0xf NoViablePathError TransportErrorCode = 0x10 ) func (e TransportErrorCode) IsCryptoError() bool { return e >= 0x100 && e < 0x200 } // Message is a description of the error. // It only returns a non-empty string for crypto errors. func (e TransportErrorCode) Message() string { if !e.IsCryptoError() { return "" } return tls.AlertError(e - 0x100).Error() } func (e TransportErrorCode) String() string { switch e { case NoError: return "NO_ERROR" case InternalError: return "INTERNAL_ERROR" case ConnectionRefused: return "CONNECTION_REFUSED" case FlowControlError: return "FLOW_CONTROL_ERROR" case StreamLimitError: return "STREAM_LIMIT_ERROR" case StreamStateError: return "STREAM_STATE_ERROR" case FinalSizeError: return "FINAL_SIZE_ERROR" case FrameEncodingError: return "FRAME_ENCODING_ERROR" case TransportParameterError: return "TRANSPORT_PARAMETER_ERROR" case ConnectionIDLimitError: return "CONNECTION_ID_LIMIT_ERROR" case ProtocolViolation: return "PROTOCOL_VIOLATION" case InvalidToken: return "INVALID_TOKEN" case ApplicationErrorErrorCode: return "APPLICATION_ERROR" case CryptoBufferExceeded: return "CRYPTO_BUFFER_EXCEEDED" case KeyUpdateError: return "KEY_UPDATE_ERROR" case AEADLimitReached: return "AEAD_LIMIT_REACHED" case NoViablePathError: return "NO_VIABLE_PATH" default: if e.IsCryptoError() { return fmt.Sprintf("CRYPTO_ERROR %#x", uint16(e)) } return fmt.Sprintf("unknown error code: %#x", uint16(e)) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/qerr/errorcodes_test.go000066400000000000000000000031341465664453100271320ustar00rootroot00000000000000package qerr import ( "go/ast" "go/parser" "go/token" "path" "runtime" "strconv" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("error codes", func() { // If this test breaks, you should run `go generate ./...` It("has a string representation for every error code", func() { // We parse the error code file, extract all constants, and verify that // each of them has a string version. Go FTW! _, thisfile, _, ok := runtime.Caller(0) if !ok { panic("Failed to get current frame") } filename := path.Join(path.Dir(thisfile), "error_codes.go") fileAst, err := parser.ParseFile(token.NewFileSet(), filename, nil, 0) Expect(err).NotTo(HaveOccurred()) constSpecs := fileAst.Decls[2].(*ast.GenDecl).Specs Expect(len(constSpecs)).To(BeNumerically(">", 4)) // at time of writing for _, c := range constSpecs { valString := c.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value val, err := strconv.ParseInt(valString, 0, 64) Expect(err).NotTo(HaveOccurred()) Expect(TransportErrorCode(val).String()).ToNot(Equal("unknown error code")) } }) It("has a string representation for unknown error codes", func() { Expect(TransportErrorCode(0x1337).String()).To(Equal("unknown error code: 0x1337")) }) It("says if an error is a crypto error", func() { for i := 0; i < 0x100; i++ { Expect(TransportErrorCode(i).IsCryptoError()).To(BeFalse()) } for i := 0x100; i < 0x200; i++ { Expect(TransportErrorCode(i).IsCryptoError()).To(BeTrue()) } for i := 0x200; i < 0x300; i++ { Expect(TransportErrorCode(i).IsCryptoError()).To(BeFalse()) } }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/qerr/errors.go000066400000000000000000000072431465664453100252450ustar00rootroot00000000000000package qerr import ( "fmt" "net" "github.com/quic-go/quic-go/internal/protocol" ) var ( ErrHandshakeTimeout = &HandshakeTimeoutError{} ErrIdleTimeout = &IdleTimeoutError{} ) type TransportError struct { Remote bool FrameType uint64 ErrorCode TransportErrorCode ErrorMessage string error error // only set for local errors, sometimes } var _ error = &TransportError{} // NewLocalCryptoError create a new TransportError instance for a crypto error func NewLocalCryptoError(tlsAlert uint8, err error) *TransportError { return &TransportError{ ErrorCode: 0x100 + TransportErrorCode(tlsAlert), error: err, } } func (e *TransportError) Error() string { str := fmt.Sprintf("%s (%s)", e.ErrorCode.String(), getRole(e.Remote)) if e.FrameType != 0 { str += fmt.Sprintf(" (frame type: %#x)", e.FrameType) } msg := e.ErrorMessage if len(msg) == 0 && e.error != nil { msg = e.error.Error() } if len(msg) == 0 { msg = e.ErrorCode.Message() } if len(msg) == 0 { return str } return str + ": " + msg } func (e *TransportError) Is(target error) bool { return target == net.ErrClosed } func (e *TransportError) Unwrap() error { return e.error } // An ApplicationErrorCode is an application-defined error code. type ApplicationErrorCode uint64 func (e *ApplicationError) Is(target error) bool { return target == net.ErrClosed } // A StreamErrorCode is an error code used to cancel streams. type StreamErrorCode uint64 type ApplicationError struct { Remote bool ErrorCode ApplicationErrorCode ErrorMessage string } var _ error = &ApplicationError{} func (e *ApplicationError) Error() string { if len(e.ErrorMessage) == 0 { return fmt.Sprintf("Application error %#x (%s)", e.ErrorCode, getRole(e.Remote)) } return fmt.Sprintf("Application error %#x (%s): %s", e.ErrorCode, getRole(e.Remote), e.ErrorMessage) } type IdleTimeoutError struct{} var _ error = &IdleTimeoutError{} func (e *IdleTimeoutError) Timeout() bool { return true } func (e *IdleTimeoutError) Temporary() bool { return false } func (e *IdleTimeoutError) Error() string { return "timeout: no recent network activity" } func (e *IdleTimeoutError) Is(target error) bool { return target == net.ErrClosed } type HandshakeTimeoutError struct{} var _ error = &HandshakeTimeoutError{} func (e *HandshakeTimeoutError) Timeout() bool { return true } func (e *HandshakeTimeoutError) Temporary() bool { return false } func (e *HandshakeTimeoutError) Error() string { return "timeout: handshake did not complete in time" } func (e *HandshakeTimeoutError) Is(target error) bool { return target == net.ErrClosed } // A VersionNegotiationError occurs when the client and the server can't agree on a QUIC version. type VersionNegotiationError struct { Ours []protocol.Version Theirs []protocol.Version } func (e *VersionNegotiationError) Error() string { return fmt.Sprintf("no compatible QUIC version found (we support %s, server offered %s)", e.Ours, e.Theirs) } func (e *VersionNegotiationError) Is(target error) bool { return target == net.ErrClosed } // A StatelessResetError occurs when we receive a stateless reset. type StatelessResetError struct { Token protocol.StatelessResetToken } var _ net.Error = &StatelessResetError{} func (e *StatelessResetError) Error() string { return fmt.Sprintf("received a stateless reset with token %x", e.Token) } func (e *StatelessResetError) Is(target error) bool { return target == net.ErrClosed } func (e *StatelessResetError) Timeout() bool { return false } func (e *StatelessResetError) Temporary() bool { return true } func getRole(remote bool) string { if remote { return "remote" } return "local" } golang-github-lucas-clemente-quic-go-0.46.0/internal/qerr/errors_suite_test.go000066400000000000000000000002771465664453100275150ustar00rootroot00000000000000package qerr import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestErrorcodes(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Errors Suite") } golang-github-lucas-clemente-quic-go-0.46.0/internal/qerr/errors_test.go000066400000000000000000000107321465664453100263010ustar00rootroot00000000000000package qerr import ( "errors" "fmt" "net" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type myError int var _ error = myError(0) func (e myError) Error() string { return fmt.Sprintf("my error %d", e) } var _ = Describe("QUIC Errors", func() { Context("Transport Errors", func() { It("has a string representation", func() { Expect((&TransportError{ ErrorCode: FlowControlError, ErrorMessage: "foobar", }).Error()).To(Equal("FLOW_CONTROL_ERROR (local): foobar")) }) It("has a string representation for empty error phrases", func() { Expect((&TransportError{ErrorCode: FlowControlError}).Error()).To(Equal("FLOW_CONTROL_ERROR (local)")) }) It("includes the frame type, for errors without a message", func() { Expect((&TransportError{ Remote: true, ErrorCode: FlowControlError, FrameType: 0x1337, }).Error()).To(Equal("FLOW_CONTROL_ERROR (remote) (frame type: 0x1337)")) }) It("includes the frame type, for errors with a message", func() { Expect((&TransportError{ ErrorCode: FlowControlError, FrameType: 0x1337, ErrorMessage: "foobar", }).Error()).To(Equal("FLOW_CONTROL_ERROR (local) (frame type: 0x1337): foobar")) }) Context("crypto errors", func() { It("has a string representation for errors with a message", func() { myErr := myError(1337) err := NewLocalCryptoError(0x42, myErr) Expect(err.Error()).To(Equal("CRYPTO_ERROR 0x142 (local): my error 1337")) }) It("unwraps errors", func() { var myErr myError err := NewLocalCryptoError(0x42, myError(1337)) Expect(errors.As(err, &myErr)).To(BeTrue()) Expect(myErr).To(BeEquivalentTo(1337)) }) It("has a string representation for errors without a message", func() { err := NewLocalCryptoError(0x2a, nil) Expect(err.Error()).To(Equal("CRYPTO_ERROR 0x12a (local): tls: bad certificate")) }) }) }) Context("Application Errors", func() { It("has a string representation for errors with a message", func() { Expect((&ApplicationError{ ErrorCode: 0x42, ErrorMessage: "foobar", }).Error()).To(Equal("Application error 0x42 (local): foobar")) }) It("has a string representation for errors without a message", func() { Expect((&ApplicationError{ ErrorCode: 0x42, Remote: true, }).Error()).To(Equal("Application error 0x42 (remote)")) }) }) Context("timeout errors", func() { It("handshake timeouts", func() { //nolint:gosimple // we need to assign to an interface here var err error err = &HandshakeTimeoutError{} nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(err.Error()).To(Equal("timeout: handshake did not complete in time")) }) It("idle timeouts", func() { //nolint:gosimple // we need to assign to an interface here var err error err = &IdleTimeoutError{} nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) Expect(err.Error()).To(Equal("timeout: no recent network activity")) }) }) Context("Version Negotiation errors", func() { It("has a string representation", func() { Expect((&VersionNegotiationError{ Ours: []protocol.Version{2, 3}, Theirs: []protocol.Version{4, 5, 6}, }).Error()).To(Equal("no compatible QUIC version found (we support [0x2 0x3], server offered [0x4 0x5 0x6])")) }) }) Context("Stateless Reset errors", func() { token := protocol.StatelessResetToken{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf} It("has a string representation", func() { Expect((&StatelessResetError{Token: token}).Error()).To(Equal("received a stateless reset with token 000102030405060708090a0b0c0d0e0f")) }) It("is a net.Error", func() { //nolint:gosimple // we need to assign to an interface here var err error err = &StatelessResetError{} nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeFalse()) }) }) It("says that errors are net.ErrClosed errors", func() { Expect(errors.Is(&TransportError{}, net.ErrClosed)).To(BeTrue()) Expect(errors.Is(&ApplicationError{}, net.ErrClosed)).To(BeTrue()) Expect(errors.Is(&IdleTimeoutError{}, net.ErrClosed)).To(BeTrue()) Expect(errors.Is(&HandshakeTimeoutError{}, net.ErrClosed)).To(BeTrue()) Expect(errors.Is(&StatelessResetError{}, net.ErrClosed)).To(BeTrue()) Expect(errors.Is(&VersionNegotiationError{}, net.ErrClosed)).To(BeTrue()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/000077500000000000000000000000001465664453100234065ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/cipher_suite.go000066400000000000000000000034051465664453100264220ustar00rootroot00000000000000package qtls import ( "crypto/tls" "fmt" "unsafe" ) //go:linkname cipherSuitesTLS13 crypto/tls.cipherSuitesTLS13 var cipherSuitesTLS13 []unsafe.Pointer //go:linkname defaultCipherSuitesTLS13 crypto/tls.defaultCipherSuitesTLS13 var defaultCipherSuitesTLS13 []uint16 //go:linkname defaultCipherSuitesTLS13NoAES crypto/tls.defaultCipherSuitesTLS13NoAES var defaultCipherSuitesTLS13NoAES []uint16 var cipherSuitesModified bool // SetCipherSuite modifies the cipherSuiteTLS13 slice of cipher suites inside qtls // such that it only contains the cipher suite with the chosen id. // The reset function returned resets them back to the original value. func SetCipherSuite(id uint16) (reset func()) { if cipherSuitesModified { panic("cipher suites modified multiple times without resetting") } cipherSuitesModified = true origCipherSuitesTLS13 := append([]unsafe.Pointer{}, cipherSuitesTLS13...) origDefaultCipherSuitesTLS13 := append([]uint16{}, defaultCipherSuitesTLS13...) origDefaultCipherSuitesTLS13NoAES := append([]uint16{}, defaultCipherSuitesTLS13NoAES...) // The order is given by the order of the slice elements in cipherSuitesTLS13 in qtls. switch id { case tls.TLS_AES_128_GCM_SHA256: cipherSuitesTLS13 = cipherSuitesTLS13[:1] case tls.TLS_CHACHA20_POLY1305_SHA256: cipherSuitesTLS13 = cipherSuitesTLS13[1:2] case tls.TLS_AES_256_GCM_SHA384: cipherSuitesTLS13 = cipherSuitesTLS13[2:] default: panic(fmt.Sprintf("unexpected cipher suite: %d", id)) } defaultCipherSuitesTLS13 = []uint16{id} defaultCipherSuitesTLS13NoAES = []uint16{id} return func() { cipherSuitesTLS13 = origCipherSuitesTLS13 defaultCipherSuitesTLS13 = origDefaultCipherSuitesTLS13 defaultCipherSuitesTLS13NoAES = origDefaultCipherSuitesTLS13NoAES cipherSuitesModified = false } } golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/cipher_suite_test.go000066400000000000000000000024671465664453100274700ustar00rootroot00000000000000package qtls import ( "crypto/tls" "fmt" "net" "github.com/quic-go/quic-go/internal/testdata" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Setting the Cipher Suite", func() { for _, cs := range []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_AES_256_GCM_SHA384} { cs := cs It(fmt.Sprintf("selects %s", tls.CipherSuiteName(cs)), func() { reset := SetCipherSuite(cs) defer reset() ln, err := tls.Listen("tcp4", "localhost:0", testdata.GetTLSConfig()) Expect(err).ToNot(HaveOccurred()) defer ln.Close() done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) conn, err := ln.Accept() Expect(err).ToNot(HaveOccurred()) _, err = conn.Read(make([]byte, 10)) Expect(err).ToNot(HaveOccurred()) Expect(conn.(*tls.Conn).ConnectionState().CipherSuite).To(Equal(cs)) }() conn, err := tls.Dial( "tcp4", fmt.Sprintf("localhost:%d", ln.Addr().(*net.TCPAddr).Port), &tls.Config{RootCAs: testdata.GetRootCA()}, ) Expect(err).ToNot(HaveOccurred()) _, err = conn.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().CipherSuite).To(Equal(cs)) Expect(conn.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) } }) golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/client_session_cache.go000066400000000000000000000034271465664453100301070ustar00rootroot00000000000000package qtls import ( "crypto/tls" "sync" ) type clientSessionCache struct { mx sync.Mutex getData func(earlyData bool) []byte setData func(data []byte, earlyData bool) (allowEarlyData bool) wrapped tls.ClientSessionCache } var _ tls.ClientSessionCache = &clientSessionCache{} func (c *clientSessionCache) Put(key string, cs *tls.ClientSessionState) { c.mx.Lock() defer c.mx.Unlock() if cs == nil { c.wrapped.Put(key, nil) return } ticket, state, err := cs.ResumptionState() if err != nil || state == nil { c.wrapped.Put(key, cs) return } state.Extra = append(state.Extra, addExtraPrefix(c.getData(state.EarlyData))) newCS, err := tls.NewResumptionState(ticket, state) if err != nil { // It's not clear why this would error. Just save the original state. c.wrapped.Put(key, cs) return } c.wrapped.Put(key, newCS) } func (c *clientSessionCache) Get(key string) (*tls.ClientSessionState, bool) { c.mx.Lock() defer c.mx.Unlock() cs, ok := c.wrapped.Get(key) if !ok || cs == nil { return cs, ok } ticket, state, err := cs.ResumptionState() if err != nil { // It's not clear why this would error. // Remove the ticket from the session cache, so we don't run into this error over and over again c.wrapped.Put(key, nil) return nil, false } // restore QUIC transport parameters and RTT stored in state.Extra if extra := findExtraData(state.Extra); extra != nil { earlyData := c.setData(extra, state.EarlyData) if state.EarlyData { state.EarlyData = earlyData } } session, err := tls.NewResumptionState(ticket, state) if err != nil { // It's not clear why this would error. // Remove the ticket from the session cache, so we don't run into this error over and over again c.wrapped.Put(key, nil) return nil, false } return session, true } golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/client_session_cache_test.go000066400000000000000000000044221465664453100311420ustar00rootroot00000000000000package qtls import ( "crypto/tls" "fmt" "net" "github.com/quic-go/quic-go/internal/testdata" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Client Session Cache", func() { It("adds data to and restores data from a session ticket", func() { ln, err := tls.Listen("tcp4", "localhost:0", testdata.GetTLSConfig()) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) for { conn, err := ln.Accept() if err != nil { return } _, err = conn.Read(make([]byte, 10)) Expect(err).ToNot(HaveOccurred()) _, err = conn.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) } }() restored := make(chan []byte, 1) clientConf := &tls.Config{ RootCAs: testdata.GetRootCA(), ClientSessionCache: &clientSessionCache{ wrapped: tls.NewLRUClientSessionCache(10), getData: func(bool) []byte { return []byte("session") }, setData: func(data []byte, earlyData bool) bool { Expect(earlyData).To(BeFalse()) // running on top of TCP, we can only test non-0-RTT here restored <- data return true }, }, } conn, err := tls.Dial( "tcp4", fmt.Sprintf("localhost:%d", ln.Addr().(*net.TCPAddr).Port), clientConf, ) Expect(err).ToNot(HaveOccurred()) _, err = conn.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().DidResume).To(BeFalse()) Expect(restored).To(HaveLen(0)) _, err = conn.Read(make([]byte, 10)) Expect(err).ToNot(HaveOccurred()) Expect(conn.Close()).To(Succeed()) // make sure the cache can deal with nonsensical inputs clientConf.ClientSessionCache.Put("foo", nil) clientConf.ClientSessionCache.Put("bar", &tls.ClientSessionState{}) conn, err = tls.Dial( "tcp4", fmt.Sprintf("localhost:%d", ln.Addr().(*net.TCPAddr).Port), clientConf, ) Expect(err).ToNot(HaveOccurred()) _, err = conn.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(conn.ConnectionState().DidResume).To(BeTrue()) var restoredData []byte Expect(restored).To(Receive(&restoredData)) Expect(restoredData).To(Equal([]byte("session"))) Expect(conn.Close()).To(Succeed()) Expect(ln.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/conn.go000066400000000000000000000012201465664453100246650ustar00rootroot00000000000000package qtls import ( "net" "time" ) type conn struct { localAddr, remoteAddr net.Addr } var _ net.Conn = &conn{} func (c *conn) Read([]byte) (int, error) { return 0, nil } func (c *conn) Write([]byte) (int, error) { return 0, nil } func (c *conn) Close() error { return nil } func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr } func (c *conn) LocalAddr() net.Addr { return c.localAddr } func (c *conn) SetReadDeadline(time.Time) error { return nil } func (c *conn) SetWriteDeadline(time.Time) error { return nil } func (c *conn) SetDeadline(time.Time) error { return nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/qtls.go000066400000000000000000000106441465664453100247250ustar00rootroot00000000000000package qtls import ( "bytes" "crypto/tls" "fmt" "net" "github.com/quic-go/quic-go/internal/protocol" ) func SetupConfigForServer( conf *tls.Config, localAddr, remoteAddr net.Addr, getData func() []byte, handleSessionTicket func([]byte, bool) bool, ) *tls.Config { // Workaround for https://github.com/golang/go/issues/60506. // This initializes the session tickets _before_ cloning the config. _, _ = conf.DecryptTicket(nil, tls.ConnectionState{}) conf = conf.Clone() conf.MinVersion = tls.VersionTLS13 // add callbacks to save transport parameters into the session ticket origWrapSession := conf.WrapSession conf.WrapSession = func(cs tls.ConnectionState, state *tls.SessionState) ([]byte, error) { // Add QUIC session ticket state.Extra = append(state.Extra, addExtraPrefix(getData())) if origWrapSession != nil { return origWrapSession(cs, state) } b, err := conf.EncryptTicket(cs, state) return b, err } origUnwrapSession := conf.UnwrapSession // UnwrapSession might be called multiple times, as the client can use multiple session tickets. // However, using 0-RTT is only possible with the first session ticket. // crypto/tls guarantees that this callback is called in the same order as the session ticket in the ClientHello. var unwrapCount int conf.UnwrapSession = func(identity []byte, connState tls.ConnectionState) (*tls.SessionState, error) { unwrapCount++ var state *tls.SessionState var err error if origUnwrapSession != nil { state, err = origUnwrapSession(identity, connState) } else { state, err = conf.DecryptTicket(identity, connState) } if err != nil || state == nil { return nil, err } extra := findExtraData(state.Extra) if extra != nil { state.EarlyData = handleSessionTicket(extra, state.EarlyData && unwrapCount == 1) } else { state.EarlyData = false } return state, nil } // The tls.Config contains two callbacks that pass in a tls.ClientHelloInfo. // Since crypto/tls doesn't do it, we need to make sure to set the Conn field with a fake net.Conn // that allows the caller to get the local and the remote address. if conf.GetConfigForClient != nil { gcfc := conf.GetConfigForClient conf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr} c, err := gcfc(info) if c != nil { // We're returning a tls.Config here, so we need to apply this recursively. c = SetupConfigForServer(c, localAddr, remoteAddr, getData, handleSessionTicket) } return c, err } } if conf.GetCertificate != nil { gc := conf.GetCertificate conf.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { info.Conn = &conn{localAddr: localAddr, remoteAddr: remoteAddr} return gc(info) } } return conf } func SetupConfigForClient( qconf *tls.QUICConfig, getData func(earlyData bool) []byte, setData func(data []byte, earlyData bool) (allowEarlyData bool), ) { conf := qconf.TLSConfig if conf.ClientSessionCache != nil { origCache := conf.ClientSessionCache conf.ClientSessionCache = &clientSessionCache{ wrapped: origCache, getData: getData, setData: setData, } } } func ToTLSEncryptionLevel(e protocol.EncryptionLevel) tls.QUICEncryptionLevel { switch e { case protocol.EncryptionInitial: return tls.QUICEncryptionLevelInitial case protocol.EncryptionHandshake: return tls.QUICEncryptionLevelHandshake case protocol.Encryption1RTT: return tls.QUICEncryptionLevelApplication case protocol.Encryption0RTT: return tls.QUICEncryptionLevelEarly default: panic(fmt.Sprintf("unexpected encryption level: %s", e)) } } func FromTLSEncryptionLevel(e tls.QUICEncryptionLevel) protocol.EncryptionLevel { switch e { case tls.QUICEncryptionLevelInitial: return protocol.EncryptionInitial case tls.QUICEncryptionLevelHandshake: return protocol.EncryptionHandshake case tls.QUICEncryptionLevelApplication: return protocol.Encryption1RTT case tls.QUICEncryptionLevelEarly: return protocol.Encryption0RTT default: panic(fmt.Sprintf("unexpect encryption level: %s", e)) } } const extraPrefix = "quic-go1" func addExtraPrefix(b []byte) []byte { return append([]byte(extraPrefix), b...) } func findExtraData(extras [][]byte) []byte { prefix := []byte(extraPrefix) for _, extra := range extras { if len(extra) < len(prefix) || !bytes.Equal(prefix, extra[:len(prefix)]) { continue } return extra[len(prefix):] } return nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/qtls_suite_test.go000066400000000000000000000005611465664453100271720ustar00rootroot00000000000000package qtls import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func TestQTLS(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "qtls Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) golang-github-lucas-clemente-quic-go-0.46.0/internal/qtls/qtls_test.go000066400000000000000000000116131465664453100257610ustar00rootroot00000000000000package qtls import ( "crypto/tls" "net" "reflect" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("interface go crypto/tls", func() { It("converts to tls.EncryptionLevel", func() { Expect(ToTLSEncryptionLevel(protocol.EncryptionInitial)).To(Equal(tls.QUICEncryptionLevelInitial)) Expect(ToTLSEncryptionLevel(protocol.EncryptionHandshake)).To(Equal(tls.QUICEncryptionLevelHandshake)) Expect(ToTLSEncryptionLevel(protocol.Encryption1RTT)).To(Equal(tls.QUICEncryptionLevelApplication)) Expect(ToTLSEncryptionLevel(protocol.Encryption0RTT)).To(Equal(tls.QUICEncryptionLevelEarly)) }) It("converts from tls.EncryptionLevel", func() { Expect(FromTLSEncryptionLevel(tls.QUICEncryptionLevelInitial)).To(Equal(protocol.EncryptionInitial)) Expect(FromTLSEncryptionLevel(tls.QUICEncryptionLevelHandshake)).To(Equal(protocol.EncryptionHandshake)) Expect(FromTLSEncryptionLevel(tls.QUICEncryptionLevelApplication)).To(Equal(protocol.Encryption1RTT)) Expect(FromTLSEncryptionLevel(tls.QUICEncryptionLevelEarly)).To(Equal(protocol.Encryption0RTT)) }) Context("setting up a tls.Config for the client", func() { It("sets up a session cache if there's one present on the config", func() { csc := tls.NewLRUClientSessionCache(1) conf := &tls.QUICConfig{TLSConfig: &tls.Config{ClientSessionCache: csc}} SetupConfigForClient(conf, nil, nil) Expect(conf.TLSConfig.ClientSessionCache).ToNot(BeNil()) Expect(conf.TLSConfig.ClientSessionCache).ToNot(Equal(csc)) }) It("doesn't set up a session cache if there's none present on the config", func() { conf := &tls.QUICConfig{TLSConfig: &tls.Config{}} SetupConfigForClient(conf, nil, nil) Expect(conf.TLSConfig.ClientSessionCache).To(BeNil()) }) }) Context("setting up a tls.Config for the server", func() { var ( local = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 42} remote = &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} ) It("sets the minimum TLS version to TLS 1.3", func() { orig := &tls.Config{MinVersion: tls.VersionTLS12} conf := SetupConfigForServer(orig, local, remote, nil, nil) Expect(conf.MinVersion).To(BeEquivalentTo(tls.VersionTLS13)) // check that the original config wasn't modified Expect(orig.MinVersion).To(BeEquivalentTo(tls.VersionTLS12)) }) It("wraps GetCertificate", func() { var localAddr, remoteAddr net.Addr tlsConf := &tls.Config{ GetCertificate: func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { localAddr = info.Conn.LocalAddr() remoteAddr = info.Conn.RemoteAddr() return &tls.Certificate{}, nil }, } conf := SetupConfigForServer(tlsConf, local, remote, nil, nil) _, err := conf.GetCertificate(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(localAddr).To(Equal(local)) Expect(remoteAddr).To(Equal(remote)) }) It("wraps GetConfigForClient", func() { var localAddr, remoteAddr net.Addr tlsConf := SetupConfigForServer( &tls.Config{ GetConfigForClient: func(info *tls.ClientHelloInfo) (*tls.Config, error) { localAddr = info.Conn.LocalAddr() remoteAddr = info.Conn.RemoteAddr() return &tls.Config{}, nil }, }, local, remote, nil, nil, ) conf, err := tlsConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(localAddr).To(Equal(local)) Expect(remoteAddr).To(Equal(remote)) Expect(conf).ToNot(BeNil()) Expect(conf.MinVersion).To(BeEquivalentTo(tls.VersionTLS13)) }) It("wraps GetConfigForClient, recursively", func() { var localAddr, remoteAddr net.Addr tlsConf := &tls.Config{} var innerConf *tls.Config getCert := func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { //nolint:unparam localAddr = info.Conn.LocalAddr() remoteAddr = info.Conn.RemoteAddr() return &tls.Certificate{}, nil } tlsConf.GetConfigForClient = func(info *tls.ClientHelloInfo) (*tls.Config, error) { innerConf = tlsConf.Clone() // set the MaxVersion, so we can check that quic-go doesn't overwrite the user's config innerConf.MaxVersion = tls.VersionTLS12 innerConf.GetCertificate = getCert return innerConf, nil } tlsConf = SetupConfigForServer(tlsConf, local, remote, nil, nil) conf, err := tlsConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(conf).ToNot(BeNil()) Expect(conf.MinVersion).To(BeEquivalentTo(tls.VersionTLS13)) _, err = conf.GetCertificate(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(localAddr).To(Equal(local)) Expect(remoteAddr).To(Equal(remote)) // make sure that the tls.Config returned by GetConfigForClient isn't modified Expect(reflect.ValueOf(innerConf.GetCertificate).Pointer() == reflect.ValueOf(getCert).Pointer()).To(BeTrue()) Expect(innerConf.MaxVersion).To(BeEquivalentTo(tls.VersionTLS12)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/000077500000000000000000000000001465664453100242345ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/ca.pem000066400000000000000000000020051465664453100253170ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICzDCCAbQCCQDA+rLymNnfJzANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQKDB1x dWljLWdvIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDA4MTgwOTIxMzVaFw0z MDA4MTYwOTIxMzVaMCgxJjAkBgNVBAoMHXF1aWMtZ28gQ2VydGlmaWNhdGUgQXV0 aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1OcsYrVaSDfh iDppl6oteVspOY3yFb96T9Y/biaGPJAkBO9VGKcqwOUPmUeiWpedRAUB9LE7Srs6 qBX4mnl90Icjp8jbIs5cPgIWLkIu8Qm549RghFzB3bn+EmCQSe4cxvyDMN3ndClp 3YMXpZgXWgJGiPOylVi/OwHDdWDBorw4hvry+6yDtpQo2TuI2A/xtxXPT7BgsEJD WGffdgZOYXChcFA0c1XVLIYlu2w2JhxS8c2TUF6uSDlmcoONNKVoiNCuu1Z9MorS Qmg7a2G7dSPu123KcTcSQFcmJrt+1G81gOBtHB69kacD8xDmgksj09h/ODPL/gIU 1ZcU2ci1/QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQB0Tb1JbLXp/BvWovSAhO/j wG7UEaUA1rCtkDB+fV2HS9bxCbV5eErdg8AMHKgB51ygUrq95vm/baZmUILr84XK uTEoxxrw5S9Z7SrhtbOpKCumoSeTsCPjDvCcwFExHv4XHFk+CPqZwbMHueVIMT0+ nGWss/KecCPdJLdnUgMRz0tIuXzkoRuOiUiZfUeyBNVNbDFSrLigYshTeAPGaYjX CypoHxkeS93nWfOMUu8FTYLYkvGMU5i076zDoFGKJiEtbjSiNW+Hei7u2aSEuCzp qyTKzYPWYffAq3MM2MKJgZdL04e9GEGeuce/qhM1o3q77aI/XJImwEDdut2LDec1 -----END CERTIFICATE----- golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/cert.go000066400000000000000000000023041465664453100255170ustar00rootroot00000000000000package testdata import ( "crypto/tls" "crypto/x509" "os" "path" "runtime" ) var certPath string func init() { _, filename, _, ok := runtime.Caller(0) if !ok { panic("Failed to get current frame") } certPath = path.Dir(filename) } // GetCertificatePaths returns the paths to certificate and key func GetCertificatePaths() (string, string) { return path.Join(certPath, "cert.pem"), path.Join(certPath, "priv.key") } // GetTLSConfig returns a tls config for quic.clemente.io func GetTLSConfig() *tls.Config { cert, err := tls.LoadX509KeyPair(GetCertificatePaths()) if err != nil { panic(err) } return &tls.Config{ MinVersion: tls.VersionTLS13, Certificates: []tls.Certificate{cert}, } } // AddRootCA adds the root CA certificate to a cert pool func AddRootCA(certPool *x509.CertPool) { caCertPath := path.Join(certPath, "ca.pem") caCertRaw, err := os.ReadFile(caCertPath) if err != nil { panic(err) } if ok := certPool.AppendCertsFromPEM(caCertRaw); !ok { panic("Could not add root ceritificate to pool.") } } // GetRootCA returns an x509.CertPool containing (only) the CA certificate func GetRootCA() *x509.CertPool { pool := x509.NewCertPool() AddRootCA(pool) return pool } golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/cert.pem000066400000000000000000000020221465664453100256700ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIC1TCCAb2gAwIBAgIJAK2fcqC0BVA7MA0GCSqGSIb3DQEBCwUAMCgxJjAkBgNV BAoMHXF1aWMtZ28gQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTIwMDgxODA5MjEz NVoXDTMwMDgxNjA5MjEzNVowEjEQMA4GA1UECgwHcXVpYy1nbzCCASIwDQYJKoZI hvcNAQEBBQADggEPADCCAQoCggEBAN/YwrigSXdJCL/bdBGhb0UpqtU8H+krV870 +w1yCSykLImH8x3qHZEXt9sr/vgjcJoV6Z15RZmnbEqnAx84sIClIBoIgnk0VPxu WF+/U/dElbftCfYcfJAddhRckdmGB+yb3Wogb32UJ+q3my++h6NjHsYb+OwpJPnQ meXjOE7Kkf+bXfFywHF3R8kzVdh5JUFYeKbxYmYgxRps1YTsbCrZCrSy1CbQ9FJw Wg5C8t+7yvVFmOeWPECypBCz2xS2mu+kycMNIjIWMl0SL7oVM5cBkRKPeVIG/KcM i5+/4lRSLoPh0Txh2TKBWfpzLbIOdPU8/O7cAukIGWx0XsfHUQMCAwEAAaMYMBYw FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4IBAQAyxxvebdMz shp5pt1SxMOSXbo8sTa1cpaf2rTmb4nxjXs6KPBEn53hSBz9bhe5wXE4f94SHadf 636rLh3d75KgrLUwO9Yq0HfCxMo1jUV/Ug++XwcHCI9vk58Tk/H4hqEM6C8RrdTj fYeuegQ0/oNLJ4uTw2P2A8TJbL6FC2dcICEAvUGZUcVyZ8m8tHXNRYYh6MZ7ubCh hinvL+AA5fY6EVlc5G/P4DN6fYxGn1cFNbiL4uZP4+W3dOmP+NV0YV9ihTyMzz0R vSoOZ9FeVkyw8EhMb3LoyXYKazvJy2VQST1ltzAGit9RiM1Gv4vuna74WsFzrn1U A/TbaR0ih/qG -----END CERTIFICATE----- golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/cert_test.go000066400000000000000000000013441465664453100265610ustar00rootroot00000000000000package testdata import ( "crypto/tls" "io" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("certificates", func() { It("returns certificates", func() { ln, err := tls.Listen("tcp", "localhost:4433", GetTLSConfig()) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() conn, err := ln.Accept() Expect(err).ToNot(HaveOccurred()) defer conn.Close() _, err = conn.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) }() conn, err := tls.Dial("tcp", "localhost:4433", &tls.Config{RootCAs: GetRootCA()}) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(conn) Expect(err).ToNot(HaveOccurred()) Expect(string(data)).To(Equal("foobar")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/generate_key.sh000077500000000000000000000012441465664453100272360ustar00rootroot00000000000000#!/bin/bash set -e echo "Generating CA key and certificate:" openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 \ -keyout ca.key -out ca.pem \ -subj "/O=quic-go Certificate Authority/" echo "Generating CSR" openssl req -out cert.csr -new -newkey rsa:2048 -nodes -keyout priv.key \ -subj "/O=quic-go/" echo "Sign certificate:" openssl x509 -req -sha256 -days 3650 -in cert.csr -out cert.pem \ -CA ca.pem -CAkey ca.key -CAcreateserial \ -extfile <(printf "subjectAltName=DNS:localhost") # debug output the certificate openssl x509 -noout -text -in cert.pem # we don't need the CA key, the serial number and the CSR any more rm ca.key cert.csr ca.srl golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/priv.key000066400000000000000000000032501465664453100257260ustar00rootroot00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDf2MK4oEl3SQi/ 23QRoW9FKarVPB/pK1fO9PsNcgkspCyJh/Md6h2RF7fbK/74I3CaFemdeUWZp2xK pwMfOLCApSAaCIJ5NFT8blhfv1P3RJW37Qn2HHyQHXYUXJHZhgfsm91qIG99lCfq t5svvoejYx7GG/jsKST50Jnl4zhOypH/m13xcsBxd0fJM1XYeSVBWHim8WJmIMUa bNWE7Gwq2Qq0stQm0PRScFoOQvLfu8r1RZjnljxAsqQQs9sUtprvpMnDDSIyFjJd Ei+6FTOXAZESj3lSBvynDIufv+JUUi6D4dE8YdkygVn6cy2yDnT1PPzu3ALpCBls dF7Hx1EDAgMBAAECggEBAMm+mLDBdbUWk9YmuZNyRdC13wvT5obF05vo26OglXgw dxt09b6OVBuCnuff3SpS9pdJDIYq2HnFlSorH/sxopIvQKF17fHDIp1n7ipNTCXd IHrmHkY8Il/YzaVIUQMVc2rih0mw9greTqOS20DKnYC6QvAWIeDmrDaitTGl+ge3 hm7e2lsgZi13R6fTNwQs9geEQSGzP2k7bFceHQFDChOYiQraR5+VZZ8S8AMGjk47 AUa5EsKeUe6O9t2xuDSFxzYz5eadOAiErKGDos5KXXr3VQgFcC8uPEFFjcJ/yl+8 tOe4iLeVwGSDJhTAThdR2deJOjaDcarWM7ixmxA3DAECgYEA/WVwmY4gWKwv49IJ Jnh1Gu93P772GqliMNpukdjTI+joQxfl4jRSt2hk4b1KRwyT9aaKfvdz0HFlXo/r 9NVSAYT3/3vbcw61bfvPhhtz44qRAAKua6b5cUM6XqxVt1hqdP8lrf/blvA5ln+u O51S8+wpxZMuqKz/29zdWSG6tAMCgYEA4iWXMXX9dZajI6abVkWwuosvOakXdLk4 tUy7zd+JPF7hmUzzj2gtg4hXoiQPAOi+GY3TX+1Nza3s1LD7iWaXSKeOWvvligw9 Q/wVTNW2P1+tdhScJf9QudzW69xOm5HNBgx9uWV2cHfjC12vg5aTH0k5axvaq15H 9WBXlH5q3wECgYBYoYGYBDFmMpvxmMagkSOMz1OrlVSpkLOKmOxx0SBRACc1SIec 7mY8RqR6nOX9IfYixyTMMittLiyhvb9vfKnZZDQGRcFFZlCpbplws+t+HDqJgWaW uumm5zfkY2z7204pLBF24fZhvha2gGRl76pTLTiTJd79Gr3HnmJByd1vFwKBgHL7 vfYuEeM55lT4Hz8sTAFtR2O/7+cvTgAQteSlZbfGXlp939DonUulhTkxsFc7/3wq unCpzcdoSWSTYDGqcf1FBIKKVVltg7EPeR0KBJIQabgCHqrLOBZojPZ7m5RJ+765 lysuxZvFuTFMPzNe2gssRf+JuBMt6tR+WclsxZYBAoGAEEFs1ppDil1xlP5rdH7T d3TSw/u4eU/X8Ei1zi25hdRUiV76fP9fBELYFmSrPBhugYv91vtSv/LmD4zLfLv/ yzwAD9j1lGbgM8Of8klCkk+XSJ88ryUwnMTJ5loQJW8t4L+zLv5Le7Ca9SAT0kJ1 jT0GzDymgLMGp8RPdBkpk+w= -----END PRIVATE KEY----- golang-github-lucas-clemente-quic-go-0.46.0/internal/testdata/testdata_suite_test.go000066400000000000000000000003031465664453100306400ustar00rootroot00000000000000package testdata import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestTestdata(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Testdata Suite") } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/000077500000000000000000000000001465664453100235635ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/buffered_write_closer.go000066400000000000000000000007411465664453100304570ustar00rootroot00000000000000package utils import ( "bufio" "io" ) type bufferedWriteCloser struct { *bufio.Writer io.Closer } // NewBufferedWriteCloser creates an io.WriteCloser from a bufio.Writer and an io.Closer func NewBufferedWriteCloser(writer *bufio.Writer, closer io.Closer) io.WriteCloser { return &bufferedWriteCloser{ Writer: writer, Closer: closer, } } func (h bufferedWriteCloser) Close() error { if err := h.Writer.Flush(); err != nil { return err } return h.Closer.Close() } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/buffered_write_closer_test.go000066400000000000000000000010061465664453100315110ustar00rootroot00000000000000package utils import ( "bufio" "bytes" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type nopCloser struct{} func (nopCloser) Close() error { return nil } var _ = Describe("buffered io.WriteCloser", func() { It("flushes before closing", func() { buf := &bytes.Buffer{} w := bufio.NewWriter(buf) wc := NewBufferedWriteCloser(w, &nopCloser{}) wc.Write([]byte("foobar")) Expect(buf.Len()).To(BeZero()) Expect(wc.Close()).To(Succeed()) Expect(buf.String()).To(Equal("foobar")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/linkedlist/000077500000000000000000000000001465664453100257255ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/linkedlist/README.md000066400000000000000000000004471465664453100272110ustar00rootroot00000000000000# Usage This is the Go standard library implementation of a linked list (https://golang.org/src/container/list/list.go), with the following modifications: * it uses Go generics * it allows passing in a `sync.Pool` (via the `NewWithPool` constructor) to reduce allocations of `Element` structs golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/linkedlist/linkedlist.go000066400000000000000000000157071465664453100304300ustar00rootroot00000000000000// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package list implements a doubly linked list. // // To iterate over a list (where l is a *List[T]): // // for e := l.Front(); e != nil; e = e.Next() { // // do something with e.Value // } package list import "sync" func NewPool[T any]() *sync.Pool { return &sync.Pool{New: func() any { return &Element[T]{} }} } // Element is an element of a linked list. type Element[T any] struct { // Next and previous pointers in the doubly-linked list of elements. // To simplify the implementation, internally a list l is implemented // as a ring, such that &l.root is both the next element of the last // list element (l.Back()) and the previous element of the first list // element (l.Front()). next, prev *Element[T] // The list to which this element belongs. list *List[T] // The value stored with this element. Value T } // Next returns the next list element or nil. func (e *Element[T]) Next() *Element[T] { if p := e.next; e.list != nil && p != &e.list.root { return p } return nil } // Prev returns the previous list element or nil. func (e *Element[T]) Prev() *Element[T] { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } func (e *Element[T]) List() *List[T] { return e.list } // List represents a doubly linked list. // The zero value for List is an empty list ready to use. type List[T any] struct { root Element[T] // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element pool *sync.Pool } // Init initializes or clears list l. func (l *List[T]) Init() *List[T] { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // New returns an initialized list. func New[T any]() *List[T] { return new(List[T]).Init() } // NewWithPool returns an initialized list, using a sync.Pool for list elements. func NewWithPool[T any](pool *sync.Pool) *List[T] { l := &List[T]{pool: pool} return l.Init() } // Len returns the number of elements of list l. // The complexity is O(1). func (l *List[T]) Len() int { return l.len } // Front returns the first element of list l or nil if the list is empty. func (l *List[T]) Front() *Element[T] { if l.len == 0 { return nil } return l.root.next } // Back returns the last element of list l or nil if the list is empty. func (l *List[T]) Back() *Element[T] { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List value. func (l *List[T]) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *List[T]) insert(e, at *Element[T]) *Element[T] { e.prev = at e.next = at.next e.prev.next = e e.next.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { var e *Element[T] if l.pool != nil { e = l.pool.Get().(*Element[T]) } else { e = &Element[T]{} } e.Value = v return l.insert(e, at) } // remove removes e from its list, decrements l.len func (l *List[T]) remove(e *Element[T]) { e.prev.next = e.next e.next.prev = e.prev e.next = nil // avoid memory leaks e.prev = nil // avoid memory leaks e.list = nil if l.pool != nil { l.pool.Put(e) } l.len-- } // move moves e to next to at. func (l *List[T]) move(e, at *Element[T]) { if e == at { return } e.prev.next = e.next e.next.prev = e.prev e.prev = at e.next = at.next e.prev.next = e e.next.prev = e } // Remove removes e from l if e is an element of list l. // It returns the element value e.Value. // The element must not be nil. func (l *List[T]) Remove(e *Element[T]) T { v := e.Value if e.list == l { // if e.list == l, l must have been initialized when e was inserted // in l or l == nil (e is a zero Element) and l.remove will crash l.remove(e) } return v } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *List[T]) PushFront(v T) *Element[T] { l.lazyInit() return l.insertValue(v, &l.root) } // PushBack inserts a new element e with value v at the back of list l and returns e. func (l *List[T]) PushBack(v T) *Element[T] { l.lazyInit() return l.insertValue(v, l.root.prev) } // InsertBefore inserts a new element e with value v immediately before mark and returns e. // If mark is not an element of l, the list is not modified. // The mark must not be nil. func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { if mark.list != l { return nil } // see comment in List.Remove about initialization of l return l.insertValue(v, mark.prev) } // InsertAfter inserts a new element e with value v immediately after mark and returns e. // If mark is not an element of l, the list is not modified. // The mark must not be nil. func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { if mark.list != l { return nil } // see comment in List.Remove about initialization of l return l.insertValue(v, mark) } // MoveToFront moves element e to the front of list l. // If e is not an element of l, the list is not modified. // The element must not be nil. func (l *List[T]) MoveToFront(e *Element[T]) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.move(e, &l.root) } // MoveToBack moves element e to the back of list l. // If e is not an element of l, the list is not modified. // The element must not be nil. func (l *List[T]) MoveToBack(e *Element[T]) { if e.list != l || l.root.prev == e { return } // see comment in List.Remove about initialization of l l.move(e, l.root.prev) } // MoveBefore moves element e to its new position before mark. // If e or mark is not an element of l, or e == mark, the list is not modified. // The element and mark must not be nil. func (l *List[T]) MoveBefore(e, mark *Element[T]) { if e.list != l || e == mark || mark.list != l { return } l.move(e, mark.prev) } // MoveAfter moves element e to its new position after mark. // If e or mark is not an element of l, or e == mark, the list is not modified. // The element and mark must not be nil. func (l *List[T]) MoveAfter(e, mark *Element[T]) { if e.list != l || e == mark || mark.list != l { return } l.move(e, mark) } // PushBackList inserts a copy of another list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *List[T]) PushBackList(other *List[T]) { l.lazyInit() for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { l.insertValue(e.Value, l.root.prev) } } // PushFrontList inserts a copy of another list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *List[T]) PushFrontList(other *List[T]) { l.lazyInit() for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/log.go000066400000000000000000000054231465664453100246770ustar00rootroot00000000000000package utils import ( "fmt" "log" "os" "strings" "time" ) // LogLevel of quic-go type LogLevel uint8 const ( // LogLevelNothing disables LogLevelNothing LogLevel = iota // LogLevelError enables err logs LogLevelError // LogLevelInfo enables info logs (e.g. packets) LogLevelInfo // LogLevelDebug enables debug logs (e.g. packet contents) LogLevelDebug ) const logEnv = "QUIC_GO_LOG_LEVEL" // A Logger logs. type Logger interface { SetLogLevel(LogLevel) SetLogTimeFormat(format string) WithPrefix(prefix string) Logger Debug() bool Errorf(format string, args ...interface{}) Infof(format string, args ...interface{}) Debugf(format string, args ...interface{}) } // DefaultLogger is used by quic-go for logging. var DefaultLogger Logger type defaultLogger struct { prefix string logLevel LogLevel timeFormat string } var _ Logger = &defaultLogger{} // SetLogLevel sets the log level func (l *defaultLogger) SetLogLevel(level LogLevel) { l.logLevel = level } // SetLogTimeFormat sets the format of the timestamp // an empty string disables the logging of timestamps func (l *defaultLogger) SetLogTimeFormat(format string) { log.SetFlags(0) // disable timestamp logging done by the log package l.timeFormat = format } // Debugf logs something func (l *defaultLogger) Debugf(format string, args ...interface{}) { if l.logLevel == LogLevelDebug { l.logMessage(format, args...) } } // Infof logs something func (l *defaultLogger) Infof(format string, args ...interface{}) { if l.logLevel >= LogLevelInfo { l.logMessage(format, args...) } } // Errorf logs something func (l *defaultLogger) Errorf(format string, args ...interface{}) { if l.logLevel >= LogLevelError { l.logMessage(format, args...) } } func (l *defaultLogger) logMessage(format string, args ...interface{}) { var pre string if len(l.timeFormat) > 0 { pre = time.Now().Format(l.timeFormat) + " " } if len(l.prefix) > 0 { pre += l.prefix + " " } log.Printf(pre+format, args...) } func (l *defaultLogger) WithPrefix(prefix string) Logger { if len(l.prefix) > 0 { prefix = l.prefix + " " + prefix } return &defaultLogger{ logLevel: l.logLevel, timeFormat: l.timeFormat, prefix: prefix, } } // Debug returns true if the log level is LogLevelDebug func (l *defaultLogger) Debug() bool { return l.logLevel == LogLevelDebug } func init() { DefaultLogger = &defaultLogger{} DefaultLogger.SetLogLevel(readLoggingEnv()) } func readLoggingEnv() LogLevel { switch strings.ToLower(os.Getenv(logEnv)) { case "": return LogLevelNothing case "debug": return LogLevelDebug case "info": return LogLevelInfo case "error": return LogLevelError default: fmt.Fprintln(os.Stderr, "invalid quic-go log level, see https://github.com/quic-go/quic-go/wiki/Logging") return LogLevelNothing } } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/log_test.go000066400000000000000000000076421465664453100257430ustar00rootroot00000000000000package utils import ( "bytes" "log" "os" "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Log", func() { var b *bytes.Buffer BeforeEach(func() { b = &bytes.Buffer{} log.SetOutput(b) }) AfterEach(func() { log.SetOutput(os.Stdout) DefaultLogger.SetLogLevel(LogLevelNothing) }) It("the log level has the correct numeric value", func() { Expect(LogLevelNothing).To(BeEquivalentTo(0)) Expect(LogLevelError).To(BeEquivalentTo(1)) Expect(LogLevelInfo).To(BeEquivalentTo(2)) Expect(LogLevelDebug).To(BeEquivalentTo(3)) }) It("log level nothing", func() { DefaultLogger.SetLogLevel(LogLevelNothing) DefaultLogger.Debugf("debug") DefaultLogger.Infof("info") DefaultLogger.Errorf("err") Expect(b.String()).To(BeEmpty()) }) It("log level err", func() { DefaultLogger.SetLogLevel(LogLevelError) DefaultLogger.Debugf("debug") DefaultLogger.Infof("info") DefaultLogger.Errorf("err") Expect(b.String()).To(ContainSubstring("err\n")) Expect(b.String()).ToNot(ContainSubstring("info")) Expect(b.String()).ToNot(ContainSubstring("debug")) }) It("log level info", func() { DefaultLogger.SetLogLevel(LogLevelInfo) DefaultLogger.Debugf("debug") DefaultLogger.Infof("info") DefaultLogger.Errorf("err") Expect(b.String()).To(ContainSubstring("err\n")) Expect(b.String()).To(ContainSubstring("info\n")) Expect(b.String()).ToNot(ContainSubstring("debug")) }) It("log level debug", func() { DefaultLogger.SetLogLevel(LogLevelDebug) DefaultLogger.Debugf("debug") DefaultLogger.Infof("info") DefaultLogger.Errorf("err") Expect(b.String()).To(ContainSubstring("err\n")) Expect(b.String()).To(ContainSubstring("info\n")) Expect(b.String()).To(ContainSubstring("debug\n")) }) It("doesn't add a timestamp if the time format is empty", func() { DefaultLogger.SetLogLevel(LogLevelDebug) DefaultLogger.SetLogTimeFormat("") DefaultLogger.Debugf("debug") Expect(b.String()).To(Equal("debug\n")) }) It("adds a timestamp", func() { format := "Jan 2, 2006" DefaultLogger.SetLogTimeFormat(format) DefaultLogger.SetLogLevel(LogLevelInfo) DefaultLogger.Infof("info") t, err := time.Parse(format, b.String()[:b.Len()-6]) Expect(err).ToNot(HaveOccurred()) Expect(t).To(BeTemporally("~", time.Now(), 25*time.Hour)) }) It("says whether debug is enabled", func() { Expect(DefaultLogger.Debug()).To(BeFalse()) DefaultLogger.SetLogLevel(LogLevelDebug) Expect(DefaultLogger.Debug()).To(BeTrue()) }) It("adds a prefix", func() { DefaultLogger.SetLogLevel(LogLevelDebug) prefixLogger := DefaultLogger.WithPrefix("prefix") prefixLogger.Debugf("debug") Expect(b.String()).To(ContainSubstring("prefix")) Expect(b.String()).To(ContainSubstring("debug")) }) It("adds multiple prefixes", func() { DefaultLogger.SetLogLevel(LogLevelDebug) prefixLogger := DefaultLogger.WithPrefix("prefix1") prefixPrefixLogger := prefixLogger.WithPrefix("prefix2") prefixPrefixLogger.Debugf("debug") Expect(b.String()).To(ContainSubstring("prefix")) Expect(b.String()).To(ContainSubstring("debug")) }) Context("reading from env", func() { BeforeEach(func() { Expect(DefaultLogger.(*defaultLogger).logLevel).To(Equal(LogLevelNothing)) }) It("reads DEBUG", func() { os.Setenv(logEnv, "DEBUG") Expect(readLoggingEnv()).To(Equal(LogLevelDebug)) }) It("reads debug", func() { os.Setenv(logEnv, "debug") Expect(readLoggingEnv()).To(Equal(LogLevelDebug)) }) It("reads INFO", func() { os.Setenv(logEnv, "INFO") readLoggingEnv() Expect(readLoggingEnv()).To(Equal(LogLevelInfo)) }) It("reads ERROR", func() { os.Setenv(logEnv, "ERROR") Expect(readLoggingEnv()).To(Equal(LogLevelError)) }) It("does not error reading invalid log levels from env", func() { os.Setenv(logEnv, "") Expect(readLoggingEnv()).To(Equal(LogLevelNothing)) os.Setenv(logEnv, "asdf") Expect(readLoggingEnv()).To(Equal(LogLevelNothing)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/rand.go000066400000000000000000000011571465664453100250420ustar00rootroot00000000000000package utils import ( "crypto/rand" "encoding/binary" ) // Rand is a wrapper around crypto/rand that adds some convenience functions known from math/rand. type Rand struct { buf [4]byte } func (r *Rand) Int31() int32 { rand.Read(r.buf[:]) return int32(binary.BigEndian.Uint32(r.buf[:]) & ^uint32(1<<31)) } // copied from the standard library math/rand implementation of Int63n func (r *Rand) Int31n(n int32) int32 { if n&(n-1) == 0 { // n is power of two, can mask return r.Int31() & (n - 1) } max := int32((1 << 31) - 1 - (1<<31)%uint32(n)) v := r.Int31() for v > max { v = r.Int31() } return v % n } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/rand_test.go000066400000000000000000000010421465664453100260720ustar00rootroot00000000000000package utils import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Rand", func() { It("generates random numbers", func() { const ( num = 1000 max = 12345678 ) var values [num]int32 var r Rand for i := 0; i < num; i++ { v := r.Int31n(max) Expect(v).To(And( BeNumerically(">=", 0), BeNumerically("<", max), )) values[i] = v } var sum uint64 for _, n := range values { sum += uint64(n) } Expect(float64(sum) / num).To(BeNumerically("~", max/2, max/25)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/ringbuffer/000077500000000000000000000000001465664453100257145ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/ringbuffer/ringbuffer.go000066400000000000000000000045461465664453100304050ustar00rootroot00000000000000package ringbuffer // A RingBuffer is a ring buffer. // It acts as a heap that doesn't cause any allocations. type RingBuffer[T any] struct { ring []T headPos, tailPos int full bool } // Init preallocates a buffer with a certain size. func (r *RingBuffer[T]) Init(size int) { r.ring = make([]T, size) } // Len returns the number of elements in the ring buffer. func (r *RingBuffer[T]) Len() int { if r.full { return len(r.ring) } if r.tailPos >= r.headPos { return r.tailPos - r.headPos } return r.tailPos - r.headPos + len(r.ring) } // Empty says if the ring buffer is empty. func (r *RingBuffer[T]) Empty() bool { return !r.full && r.headPos == r.tailPos } // PushBack adds a new element. // If the ring buffer is full, its capacity is increased first. func (r *RingBuffer[T]) PushBack(t T) { if r.full || len(r.ring) == 0 { r.grow() } r.ring[r.tailPos] = t r.tailPos++ if r.tailPos == len(r.ring) { r.tailPos = 0 } if r.tailPos == r.headPos { r.full = true } } // PopFront returns the next element. // It must not be called when the buffer is empty, that means that // callers might need to check if there are elements in the buffer first. func (r *RingBuffer[T]) PopFront() T { if r.Empty() { panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: pop from an empty queue") } r.full = false t := r.ring[r.headPos] r.ring[r.headPos] = *new(T) r.headPos++ if r.headPos == len(r.ring) { r.headPos = 0 } return t } // PeekFront returns the next element. // It must not be called when the buffer is empty, that means that // callers might need to check if there are elements in the buffer first. func (r *RingBuffer[T]) PeekFront() T { if r.Empty() { panic("github.com/quic-go/quic-go/internal/utils/ringbuffer: peek from an empty queue") } return r.ring[r.headPos] } // Grow the maximum size of the queue. // This method assume the queue is full. func (r *RingBuffer[T]) grow() { oldRing := r.ring newSize := len(oldRing) * 2 if newSize == 0 { newSize = 1 } r.ring = make([]T, newSize) headLen := copy(r.ring, oldRing[r.headPos:]) copy(r.ring[headLen:], oldRing[:r.headPos]) r.headPos, r.tailPos, r.full = 0, len(oldRing), false } // Clear removes all elements. func (r *RingBuffer[T]) Clear() { var zeroValue T for i := range r.ring { r.ring[i] = zeroValue } r.headPos, r.tailPos, r.full = 0, 0, false } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/ringbuffer/ringbuffer_bench_test.go000066400000000000000000000002671465664453100325770ustar00rootroot00000000000000package ringbuffer import "testing" func BenchmarkRingBuffer(b *testing.B) { r := RingBuffer[int]{} b.ResetTimer() for i := 0; i < b.N; i++ { r.PushBack(i) r.PopFront() } } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/ringbuffer/ringbuffer_suite_test.go000066400000000000000000000003071465664453100326440ustar00rootroot00000000000000package ringbuffer import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestTestdata(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "ringbuffer suite") } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/ringbuffer/ringbuffer_test.go000066400000000000000000000023131465664453100314320ustar00rootroot00000000000000package ringbuffer import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("RingBuffer", func() { It("push, peek and pop", func() { r := RingBuffer[int]{} Expect(len(r.ring)).To(Equal(0)) Expect(func() { r.PopFront() }).To(Panic()) r.PushBack(1) r.PushBack(2) r.PushBack(3) Expect(r.PeekFront()).To(Equal(1)) Expect(r.PeekFront()).To(Equal(1)) Expect(r.PopFront()).To(Equal(1)) Expect(r.PeekFront()).To(Equal(2)) Expect(r.PopFront()).To(Equal(2)) r.PushBack(4) r.PushBack(5) Expect(r.Len()).To(Equal(3)) r.PushBack(6) Expect(r.Len()).To(Equal(4)) Expect(r.PopFront()).To(Equal(3)) Expect(r.PopFront()).To(Equal(4)) Expect(r.PopFront()).To(Equal(5)) Expect(r.PopFront()).To(Equal(6)) }) It("panics when Peek or Pop are called on an empty buffer", func() { r := RingBuffer[string]{} Expect(r.Empty()).To(BeTrue()) Expect(r.Len()).To(BeZero()) Expect(func() { r.PeekFront() }).To(Panic()) Expect(func() { r.PopFront() }).To(Panic()) }) It("clearing", func() { r := RingBuffer[int]{} r.Init(2) r.PushBack(1) r.PushBack(2) Expect(r.full).To(BeTrue()) r.Clear() Expect(r.full).To(BeFalse()) Expect(r.Len()).To(Equal(0)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/rtt_stats.go000066400000000000000000000102051465664453100261370ustar00rootroot00000000000000package utils import ( "time" "github.com/quic-go/quic-go/internal/protocol" ) const ( rttAlpha = 0.125 oneMinusAlpha = 1 - rttAlpha rttBeta = 0.25 oneMinusBeta = 1 - rttBeta // The default RTT used before an RTT sample is taken. defaultInitialRTT = 100 * time.Millisecond ) // RTTStats provides round-trip statistics type RTTStats struct { hasMeasurement bool minRTT time.Duration latestRTT time.Duration smoothedRTT time.Duration meanDeviation time.Duration maxAckDelay time.Duration } // NewRTTStats makes a properly initialized RTTStats object func NewRTTStats() *RTTStats { return &RTTStats{} } // MinRTT Returns the minRTT for the entire connection. // May return Zero if no valid updates have occurred. func (r *RTTStats) MinRTT() time.Duration { return r.minRTT } // LatestRTT returns the most recent rtt measurement. // May return Zero if no valid updates have occurred. func (r *RTTStats) LatestRTT() time.Duration { return r.latestRTT } // SmoothedRTT returns the smoothed RTT for the connection. // May return Zero if no valid updates have occurred. func (r *RTTStats) SmoothedRTT() time.Duration { return r.smoothedRTT } // MeanDeviation gets the mean deviation func (r *RTTStats) MeanDeviation() time.Duration { return r.meanDeviation } // MaxAckDelay gets the max_ack_delay advertised by the peer func (r *RTTStats) MaxAckDelay() time.Duration { return r.maxAckDelay } // PTO gets the probe timeout duration. func (r *RTTStats) PTO(includeMaxAckDelay bool) time.Duration { if r.SmoothedRTT() == 0 { return 2 * defaultInitialRTT } pto := r.SmoothedRTT() + max(4*r.MeanDeviation(), protocol.TimerGranularity) if includeMaxAckDelay { pto += r.MaxAckDelay() } return pto } // UpdateRTT updates the RTT based on a new sample. func (r *RTTStats) UpdateRTT(sendDelta, ackDelay time.Duration, now time.Time) { if sendDelta <= 0 { return } // Update r.minRTT first. r.minRTT does not use an rttSample corrected for // ackDelay but the raw observed sendDelta, since poor clock granularity at // the client may cause a high ackDelay to result in underestimation of the // r.minRTT. if r.minRTT == 0 || r.minRTT > sendDelta { r.minRTT = sendDelta } // Correct for ackDelay if information received from the peer results in a // an RTT sample at least as large as minRTT. Otherwise, only use the // sendDelta. sample := sendDelta if sample-r.minRTT >= ackDelay { sample -= ackDelay } r.latestRTT = sample // First time call. if !r.hasMeasurement { r.hasMeasurement = true r.smoothedRTT = sample r.meanDeviation = sample / 2 } else { r.meanDeviation = time.Duration(oneMinusBeta*float32(r.meanDeviation/time.Microsecond)+rttBeta*float32((r.smoothedRTT-sample).Abs()/time.Microsecond)) * time.Microsecond r.smoothedRTT = time.Duration((float32(r.smoothedRTT/time.Microsecond)*oneMinusAlpha)+(float32(sample/time.Microsecond)*rttAlpha)) * time.Microsecond } } // SetMaxAckDelay sets the max_ack_delay func (r *RTTStats) SetMaxAckDelay(mad time.Duration) { r.maxAckDelay = mad } // SetInitialRTT sets the initial RTT. // It is used during the 0-RTT handshake when restoring the RTT stats from the session state. func (r *RTTStats) SetInitialRTT(t time.Duration) { // On the server side, by the time we get to process the session ticket, // we might already have obtained an RTT measurement. // This can happen if we received the ClientHello in multiple pieces, and one of those pieces was lost. // Discard the restored value. A fresh measurement is always better. if r.hasMeasurement { return } r.smoothedRTT = t r.latestRTT = t } // OnConnectionMigration is called when connection migrates and rtt measurement needs to be reset. func (r *RTTStats) OnConnectionMigration() { r.latestRTT = 0 r.minRTT = 0 r.smoothedRTT = 0 r.meanDeviation = 0 } // ExpireSmoothedMetrics causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt // is larger. The mean deviation is increased to the most recent deviation if // it's larger. func (r *RTTStats) ExpireSmoothedMetrics() { r.meanDeviation = max(r.meanDeviation, (r.smoothedRTT - r.latestRTT).Abs()) r.smoothedRTT = max(r.smoothedRTT, r.latestRTT) } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/rtt_stats_test.go000066400000000000000000000146241465664453100272070ustar00rootroot00000000000000package utils import ( "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("RTT stats", func() { var rttStats *RTTStats BeforeEach(func() { rttStats = NewRTTStats() }) It("DefaultsBeforeUpdate", func() { Expect(rttStats.MinRTT()).To(Equal(time.Duration(0))) Expect(rttStats.SmoothedRTT()).To(Equal(time.Duration(0))) }) It("SmoothedRTT", func() { // Verify that ack_delay is ignored in the first measurement. rttStats.UpdateRTT((300 * time.Millisecond), (100 * time.Millisecond), time.Time{}) Expect(rttStats.LatestRTT()).To(Equal((300 * time.Millisecond))) Expect(rttStats.SmoothedRTT()).To(Equal((300 * time.Millisecond))) // Verify that Smoothed RTT includes max ack delay if it's reasonable. rttStats.UpdateRTT((350 * time.Millisecond), (50 * time.Millisecond), time.Time{}) Expect(rttStats.LatestRTT()).To(Equal((300 * time.Millisecond))) Expect(rttStats.SmoothedRTT()).To(Equal((300 * time.Millisecond))) // Verify that large erroneous ack_delay does not change Smoothed RTT. rttStats.UpdateRTT((200 * time.Millisecond), (300 * time.Millisecond), time.Time{}) Expect(rttStats.LatestRTT()).To(Equal((200 * time.Millisecond))) Expect(rttStats.SmoothedRTT()).To(Equal((287500 * time.Microsecond))) }) It("MinRTT", func() { rttStats.UpdateRTT((200 * time.Millisecond), 0, time.Time{}) Expect(rttStats.MinRTT()).To(Equal((200 * time.Millisecond))) rttStats.UpdateRTT((10 * time.Millisecond), 0, time.Time{}.Add((10 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((10 * time.Millisecond))) rttStats.UpdateRTT((50 * time.Millisecond), 0, time.Time{}.Add((20 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((10 * time.Millisecond))) rttStats.UpdateRTT((50 * time.Millisecond), 0, time.Time{}.Add((30 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((10 * time.Millisecond))) rttStats.UpdateRTT((50 * time.Millisecond), 0, time.Time{}.Add((40 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((10 * time.Millisecond))) // Verify that ack_delay does not go into recording of MinRTT_. rttStats.UpdateRTT((7 * time.Millisecond), (2 * time.Millisecond), time.Time{}.Add((50 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((7 * time.Millisecond))) }) It("MaxAckDelay", func() { rttStats.SetMaxAckDelay(42 * time.Minute) Expect(rttStats.MaxAckDelay()).To(Equal(42 * time.Minute)) }) It("computes the PTO", func() { maxAckDelay := 42 * time.Minute rttStats.SetMaxAckDelay(maxAckDelay) rtt := time.Second rttStats.UpdateRTT(rtt, 0, time.Time{}) Expect(rttStats.SmoothedRTT()).To(Equal(rtt)) Expect(rttStats.MeanDeviation()).To(Equal(rtt / 2)) Expect(rttStats.PTO(false)).To(Equal(rtt + 4*(rtt/2))) Expect(rttStats.PTO(true)).To(Equal(rtt + 4*(rtt/2) + maxAckDelay)) }) It("uses the granularity for computing the PTO for short RTTs", func() { rtt := time.Microsecond rttStats.UpdateRTT(rtt, 0, time.Time{}) Expect(rttStats.PTO(true)).To(Equal(rtt + protocol.TimerGranularity)) }) It("ExpireSmoothedMetrics", func() { initialRtt := (10 * time.Millisecond) rttStats.UpdateRTT(initialRtt, 0, time.Time{}) Expect(rttStats.MinRTT()).To(Equal(initialRtt)) Expect(rttStats.SmoothedRTT()).To(Equal(initialRtt)) Expect(rttStats.MeanDeviation()).To(Equal(initialRtt / 2)) // Update once with a 20ms RTT. doubledRtt := initialRtt * (2) rttStats.UpdateRTT(doubledRtt, 0, time.Time{}) Expect(rttStats.SmoothedRTT()).To(Equal(time.Duration(float32(initialRtt) * 1.125))) // Expire the smoothed metrics, increasing smoothed rtt and mean deviation. rttStats.ExpireSmoothedMetrics() Expect(rttStats.SmoothedRTT()).To(Equal(doubledRtt)) Expect(rttStats.MeanDeviation()).To(Equal(time.Duration(float32(initialRtt) * 0.875))) // Now go back down to 5ms and expire the smoothed metrics, and ensure the // mean deviation increases to 15ms. halfRtt := initialRtt / 2 rttStats.UpdateRTT(halfRtt, 0, time.Time{}) Expect(doubledRtt).To(BeNumerically(">", rttStats.SmoothedRTT())) Expect(initialRtt).To(BeNumerically("<", rttStats.MeanDeviation())) }) It("UpdateRTTWithBadSendDeltas", func() { initialRtt := 10 * time.Millisecond rttStats.UpdateRTT(initialRtt, 0, time.Time{}) Expect(rttStats.MinRTT()).To(Equal(initialRtt)) Expect(rttStats.SmoothedRTT()).To(Equal(initialRtt)) badSendDeltas := []time.Duration{ 0, -1000 * time.Microsecond, } for _, badSendDelta := range badSendDeltas { rttStats.UpdateRTT(badSendDelta, 0, time.Time{}) Expect(rttStats.MinRTT()).To(Equal(initialRtt)) Expect(rttStats.SmoothedRTT()).To(Equal(initialRtt)) } }) It("ResetAfterConnectionMigrations", func() { rttStats.UpdateRTT(200*time.Millisecond, 0, time.Time{}) Expect(rttStats.LatestRTT()).To(Equal((200 * time.Millisecond))) Expect(rttStats.SmoothedRTT()).To(Equal((200 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((200 * time.Millisecond))) rttStats.UpdateRTT((300 * time.Millisecond), (100 * time.Millisecond), time.Time{}) Expect(rttStats.LatestRTT()).To(Equal((200 * time.Millisecond))) Expect(rttStats.SmoothedRTT()).To(Equal((200 * time.Millisecond))) Expect(rttStats.MinRTT()).To(Equal((200 * time.Millisecond))) // Reset rtt stats on connection migrations. rttStats.OnConnectionMigration() Expect(rttStats.LatestRTT()).To(Equal(time.Duration(0))) Expect(rttStats.SmoothedRTT()).To(Equal(time.Duration(0))) Expect(rttStats.MinRTT()).To(Equal(time.Duration(0))) }) It("restores the RTT", func() { rttStats.SetInitialRTT(10 * time.Second) Expect(rttStats.LatestRTT()).To(Equal(10 * time.Second)) Expect(rttStats.SmoothedRTT()).To(Equal(10 * time.Second)) Expect(rttStats.MeanDeviation()).To(BeZero()) // update the RTT and make sure that the initial value is immediately forgotten rttStats.UpdateRTT(200*time.Millisecond, 0, time.Time{}) Expect(rttStats.LatestRTT()).To(Equal(200 * time.Millisecond)) Expect(rttStats.SmoothedRTT()).To(Equal(200 * time.Millisecond)) Expect(rttStats.MeanDeviation()).To(Equal(100 * time.Millisecond)) }) It("doesn't restore the RTT if we already have a measurement", func() { const rtt = 10 * time.Millisecond rttStats.UpdateRTT(rtt, 0, time.Now()) Expect(rttStats.LatestRTT()).To(Equal(rtt)) Expect(rttStats.SmoothedRTT()).To(Equal(rtt)) rttStats.SetInitialRTT(time.Minute) Expect(rttStats.LatestRTT()).To(Equal(rtt)) Expect(rttStats.SmoothedRTT()).To(Equal(rtt)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/timer.go000066400000000000000000000022061465664453100252320ustar00rootroot00000000000000package utils import ( "math" "time" ) // A Timer wrapper that behaves correctly when resetting type Timer struct { t *time.Timer read bool deadline time.Time } // NewTimer creates a new timer that is not set func NewTimer() *Timer { return &Timer{t: time.NewTimer(time.Duration(math.MaxInt64))} } // Chan returns the channel of the wrapped timer func (t *Timer) Chan() <-chan time.Time { return t.t.C } // Reset the timer, no matter whether the value was read or not func (t *Timer) Reset(deadline time.Time) { if deadline.Equal(t.deadline) && !t.read { // No need to reset the timer return } // We need to drain the timer if the value from its channel was not read yet. // See https://groups.google.com/forum/#!topic/golang-dev/c9UUfASVPoU if !t.t.Stop() && !t.read { <-t.t.C } if !deadline.IsZero() { t.t.Reset(time.Until(deadline)) } t.read = false t.deadline = deadline } // SetRead should be called after the value from the chan was read func (t *Timer) SetRead() { t.read = true } func (t *Timer) Deadline() time.Time { return t.deadline } // Stop stops the timer func (t *Timer) Stop() { t.t.Stop() } golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/timer_test.go000066400000000000000000000043301465664453100262710ustar00rootroot00000000000000package utils import ( "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Timer", func() { const d = 10 * time.Millisecond It("doesn't fire a newly created timer", func() { t := NewTimer() Consistently(t.Chan()).ShouldNot(Receive()) }) It("works", func() { t := NewTimer() t.Reset(time.Now().Add(d)) Eventually(t.Chan()).Should(Receive()) }) It("returns the deadline", func() { t := NewTimer() deadline := time.Now().Add(d) t.Reset(deadline) Expect(t.Deadline()).To(Equal(deadline)) Eventually(t.Chan()).Should(Receive()) }) It("works multiple times with reading", func() { t := NewTimer() for i := 0; i < 10; i++ { t.Reset(time.Now().Add(d)) Eventually(t.Chan()).Should(Receive()) t.SetRead() } }) It("works multiple times without reading", func() { t := NewTimer() for i := 0; i < 10; i++ { t.Reset(time.Now().Add(d)) time.Sleep(d * 2) } Eventually(t.Chan()).Should(Receive()) }) It("works when resetting without expiration", func() { t := NewTimer() for i := 0; i < 10; i++ { t.Reset(time.Now().Add(time.Hour)) } t.Reset(time.Now().Add(d)) Eventually(t.Chan()).Should(Receive()) }) It("immediately fires the timer, if the deadlines has already passed", func() { t := NewTimer() t.Reset(time.Now().Add(-time.Second)) Eventually(t.Chan()).Should(Receive()) }) It("doesn't set a timer if the deadline is the zero value", func() { t := NewTimer() t.Reset(time.Time{}) Consistently(t.Chan()).ShouldNot(Receive()) }) It("fires the timer twice, if reset to the same deadline", func() { deadline := time.Now().Add(-time.Millisecond) t := NewTimer() t.Reset(deadline) Eventually(t.Chan()).Should(Receive()) t.SetRead() t.Reset(deadline) Eventually(t.Chan()).Should(Receive()) }) It("only fires the timer once, if it is reset to the same deadline, but not read in between", func() { deadline := time.Now().Add(-time.Millisecond) t := NewTimer() t.Reset(deadline) Eventually(t.Chan()).Should(Receive()) Consistently(t.Chan()).ShouldNot(Receive()) }) It("stops", func() { t := NewTimer() t.Reset(time.Now().Add(50 * time.Millisecond)) t.Stop() Consistently(t.Chan()).ShouldNot(Receive()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/utils/utils_suite_test.go000066400000000000000000000002731465664453100275240ustar00rootroot00000000000000package utils import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestCrypto(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Utils Suite") } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/000077500000000000000000000000001465664453100233715ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/ack_frame.go000066400000000000000000000161361465664453100256370ustar00rootroot00000000000000package wire import ( "errors" "math" "sort" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) var errInvalidAckRanges = errors.New("AckFrame: ACK frame contains invalid ACK ranges") // An AckFrame is an ACK frame type AckFrame struct { AckRanges []AckRange // has to be ordered. The highest ACK range goes first, the lowest ACK range goes last DelayTime time.Duration ECT0, ECT1, ECNCE uint64 } // parseAckFrame reads an ACK frame func parseAckFrame(frame *AckFrame, b []byte, typ uint64, ackDelayExponent uint8, _ protocol.Version) (int, error) { startLen := len(b) ecn := typ == ackECNFrameType la, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] largestAcked := protocol.PacketNumber(la) delay, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] delayTime := time.Duration(delay*1< largestAcked { return 0, errors.New("invalid first ACK range") } smallest := largestAcked - ackBlock frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largestAcked}) // read all the other ACK ranges for i := uint64(0); i < numBlocks; i++ { g, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] gap := protocol.PacketNumber(g) if smallest < gap+2 { return 0, errInvalidAckRanges } largest := smallest - gap - 2 ab, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] ackBlock := protocol.PacketNumber(ab) if ackBlock > largest { return 0, errInvalidAckRanges } smallest = largest - ackBlock frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largest}) } if !frame.validateAckRanges() { return 0, errInvalidAckRanges } if ecn { ect0, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] frame.ECT0 = ect0 ect1, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] frame.ECT1 = ect1 ecnce, l, err := quicvarint.Parse(b) if err != nil { return 0, replaceUnexpectedEOF(err) } b = b[l:] frame.ECNCE = ecnce } return startLen - len(b), nil } // Append appends an ACK frame. func (f *AckFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 if hasECN { b = append(b, ackECNFrameType) } else { b = append(b, ackFrameType) } b = quicvarint.Append(b, uint64(f.LargestAcked())) b = quicvarint.Append(b, encodeAckDelay(f.DelayTime)) numRanges := f.numEncodableAckRanges() b = quicvarint.Append(b, uint64(numRanges-1)) // write the first range _, firstRange := f.encodeAckRange(0) b = quicvarint.Append(b, firstRange) // write all the other range for i := 1; i < numRanges; i++ { gap, len := f.encodeAckRange(i) b = quicvarint.Append(b, gap) b = quicvarint.Append(b, len) } if hasECN { b = quicvarint.Append(b, f.ECT0) b = quicvarint.Append(b, f.ECT1) b = quicvarint.Append(b, f.ECNCE) } return b, nil } // Length of a written frame func (f *AckFrame) Length(_ protocol.Version) protocol.ByteCount { largestAcked := f.AckRanges[0].Largest numRanges := f.numEncodableAckRanges() length := 1 + quicvarint.Len(uint64(largestAcked)) + quicvarint.Len(encodeAckDelay(f.DelayTime)) length += quicvarint.Len(uint64(numRanges - 1)) lowestInFirstRange := f.AckRanges[0].Smallest length += quicvarint.Len(uint64(largestAcked - lowestInFirstRange)) for i := 1; i < numRanges; i++ { gap, len := f.encodeAckRange(i) length += quicvarint.Len(gap) length += quicvarint.Len(len) } if f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 { length += quicvarint.Len(f.ECT0) length += quicvarint.Len(f.ECT1) length += quicvarint.Len(f.ECNCE) } return protocol.ByteCount(length) } // gets the number of ACK ranges that can be encoded // such that the resulting frame is smaller than the maximum ACK frame size func (f *AckFrame) numEncodableAckRanges() int { length := 1 + quicvarint.Len(uint64(f.LargestAcked())) + quicvarint.Len(encodeAckDelay(f.DelayTime)) length += 2 // assume that the number of ranges will consume 2 bytes for i := 1; i < len(f.AckRanges); i++ { gap, len := f.encodeAckRange(i) rangeLen := quicvarint.Len(gap) + quicvarint.Len(len) if protocol.ByteCount(length+rangeLen) > protocol.MaxAckFrameSize { // Writing range i would exceed the MaxAckFrameSize. // So encode one range less than that. return i - 1 } length += rangeLen } return len(f.AckRanges) } func (f *AckFrame) encodeAckRange(i int) (uint64 /* gap */, uint64 /* length */) { if i == 0 { return 0, uint64(f.AckRanges[0].Largest - f.AckRanges[0].Smallest) } return uint64(f.AckRanges[i-1].Smallest - f.AckRanges[i].Largest - 2), uint64(f.AckRanges[i].Largest - f.AckRanges[i].Smallest) } // HasMissingRanges returns if this frame reports any missing packets func (f *AckFrame) HasMissingRanges() bool { return len(f.AckRanges) > 1 } func (f *AckFrame) validateAckRanges() bool { if len(f.AckRanges) == 0 { return false } // check the validity of every single ACK range for _, ackRange := range f.AckRanges { if ackRange.Smallest > ackRange.Largest { return false } } // check the consistency for ACK with multiple NACK ranges for i, ackRange := range f.AckRanges { if i == 0 { continue } lastAckRange := f.AckRanges[i-1] if lastAckRange.Smallest <= ackRange.Smallest { return false } if lastAckRange.Smallest <= ackRange.Largest+1 { return false } } return true } // LargestAcked is the largest acked packet number func (f *AckFrame) LargestAcked() protocol.PacketNumber { return f.AckRanges[0].Largest } // LowestAcked is the lowest acked packet number func (f *AckFrame) LowestAcked() protocol.PacketNumber { return f.AckRanges[len(f.AckRanges)-1].Smallest } // AcksPacket determines if this ACK frame acks a certain packet number func (f *AckFrame) AcksPacket(p protocol.PacketNumber) bool { if p < f.LowestAcked() || p > f.LargestAcked() { return false } i := sort.Search(len(f.AckRanges), func(i int) bool { return p >= f.AckRanges[i].Smallest }) // i will always be < len(f.AckRanges), since we checked above that p is not bigger than the largest acked return p <= f.AckRanges[i].Largest } func (f *AckFrame) Reset() { f.DelayTime = 0 f.ECT0 = 0 f.ECT1 = 0 f.ECNCE = 0 for _, r := range f.AckRanges { r.Largest = 0 r.Smallest = 0 } f.AckRanges = f.AckRanges[:0] } func encodeAckDelay(delay time.Duration) uint64 { return uint64(delay.Nanoseconds() / (1000 * (1 << protocol.AckDelayExponent))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/ack_frame_test.go000066400000000000000000000432221465664453100266720ustar00rootroot00000000000000package wire import ( "io" "math" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("ACK Frame", func() { Context("parsing", func() { It("parses an ACK frame without any ranges", func() { data := encodeVarInt(100) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(0)...) // num blocks data = append(data, encodeVarInt(10)...) // first ack block var frame AckFrame n, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(100))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(90))) Expect(frame.HasMissingRanges()).To(BeFalse()) }) It("parses an ACK frame that only acks a single packet", func() { data := encodeVarInt(55) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(0)...) // num blocks data = append(data, encodeVarInt(0)...) // first ack block var frame AckFrame n, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(55))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(55))) Expect(frame.HasMissingRanges()).To(BeFalse()) }) It("accepts an ACK frame that acks all packets from 0 to largest", func() { data := encodeVarInt(20) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(0)...) // num blocks data = append(data, encodeVarInt(20)...) // first ack block var frame AckFrame n, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(20))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(0))) Expect(frame.HasMissingRanges()).To(BeFalse()) }) It("rejects an ACK frame that has a first ACK block which is larger than LargestAcked", func() { data := encodeVarInt(20) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(0)...) // num blocks data = append(data, encodeVarInt(21)...) // first ack block var frame AckFrame _, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).To(MatchError("invalid first ACK range")) }) It("parses an ACK frame that has a single block", func() { data := encodeVarInt(1000) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(1)...) // num blocks data = append(data, encodeVarInt(100)...) // first ack block data = append(data, encodeVarInt(98)...) // gap data = append(data, encodeVarInt(50)...) // ack block var frame AckFrame n, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(1000))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(750))) Expect(frame.HasMissingRanges()).To(BeTrue()) Expect(frame.AckRanges).To(Equal([]AckRange{ {Largest: 1000, Smallest: 900}, {Largest: 800, Smallest: 750}, })) }) It("parses an ACK frame that has a multiple blocks", func() { data := encodeVarInt(100) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(2)...) // num blocks data = append(data, encodeVarInt(0)...) // first ack block data = append(data, encodeVarInt(0)...) // gap data = append(data, encodeVarInt(0)...) // ack block data = append(data, encodeVarInt(1)...) // gap data = append(data, encodeVarInt(1)...) // ack block var frame AckFrame n, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(100))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(94))) Expect(frame.HasMissingRanges()).To(BeTrue()) Expect(frame.AckRanges).To(Equal([]AckRange{ {Largest: 100, Smallest: 100}, {Largest: 98, Smallest: 98}, {Largest: 95, Smallest: 94}, })) }) It("uses the ack delay exponent", func() { const delayTime = 1 << 10 * time.Millisecond f := &AckFrame{ AckRanges: []AckRange{{Smallest: 1, Largest: 1}}, DelayTime: delayTime, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) for i := uint8(0); i < 8; i++ { typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) var frame AckFrame n, err := parseAckFrame(&frame, b[l:], typ, protocol.AckDelayExponent+i, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(b[l:]))) Expect(frame.DelayTime).To(Equal(delayTime * (1 << i))) } }) It("gracefully handles overflows of the delay time", func() { data := encodeVarInt(100) // largest acked data = append(data, encodeVarInt(math.MaxUint64/5)...) // delay data = append(data, encodeVarInt(0)...) // num blocks data = append(data, encodeVarInt(0)...) // first ack block var frame AckFrame _, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.DelayTime).To(BeNumerically(">", 0)) // The maximum encodable duration is ~292 years. Expect(frame.DelayTime.Hours()).To(BeNumerically("~", 292*365*24, 365*24)) }) It("errors on EOF", func() { data := encodeVarInt(1000) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(1)...) // num blocks data = append(data, encodeVarInt(100)...) // first ack block data = append(data, encodeVarInt(98)...) // gap data = append(data, encodeVarInt(50)...) // ack block var frame AckFrame _, err := parseAckFrame(&frame, data, ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) for i := range data { var frame AckFrame _, err := parseAckFrame(&frame, data[:i], ackFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) Context("ACK_ECN", func() { It("parses", func() { data := encodeVarInt(100) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(0)...) // num blocks data = append(data, encodeVarInt(10)...) // first ack block data = append(data, encodeVarInt(0x42)...) // ECT(0) data = append(data, encodeVarInt(0x12345)...) // ECT(1) data = append(data, encodeVarInt(0x12345678)...) // ECN-CE var frame AckFrame n, err := parseAckFrame(&frame, data, ackECNFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(100))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(90))) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(frame.ECT0).To(BeEquivalentTo(0x42)) Expect(frame.ECT1).To(BeEquivalentTo(0x12345)) Expect(frame.ECNCE).To(BeEquivalentTo(0x12345678)) }) It("errors on EOF", func() { data := encodeVarInt(1000) // largest acked data = append(data, encodeVarInt(0)...) // delay data = append(data, encodeVarInt(1)...) // num blocks data = append(data, encodeVarInt(100)...) // first ack block data = append(data, encodeVarInt(98)...) // gap data = append(data, encodeVarInt(50)...) // ack block data = append(data, encodeVarInt(0x42)...) // ECT(0) data = append(data, encodeVarInt(0x12345)...) // ECT(1) data = append(data, encodeVarInt(0x12345678)...) // ECN-CE var frame AckFrame n, err := parseAckFrame(&frame, data, ackECNFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(data))) for i := range data { var frame AckFrame _, err := parseAckFrame(&frame, data[:i], ackECNFrameType, protocol.AckDelayExponent, protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) }) Context("when writing", func() { It("writes a simple frame", func() { f := &AckFrame{ AckRanges: []AckRange{{Smallest: 100, Largest: 1337}}, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{ackFrameType} expected = append(expected, encodeVarInt(1337)...) // largest acked expected = append(expected, 0) // delay expected = append(expected, encodeVarInt(0)...) // num ranges expected = append(expected, encodeVarInt(1337-100)...) Expect(b).To(Equal(expected)) }) It("writes an ACK-ECN frame", func() { f := &AckFrame{ AckRanges: []AckRange{{Smallest: 10, Largest: 2000}}, ECT0: 13, ECT1: 37, ECNCE: 12345, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) expected := []byte{ackECNFrameType} expected = append(expected, encodeVarInt(2000)...) // largest acked expected = append(expected, 0) // delay expected = append(expected, encodeVarInt(0)...) // num ranges expected = append(expected, encodeVarInt(2000-10)...) expected = append(expected, encodeVarInt(13)...) expected = append(expected, encodeVarInt(37)...) expected = append(expected, encodeVarInt(12345)...) Expect(b).To(Equal(expected)) }) It("writes a frame that acks a single packet", func() { f := &AckFrame{ AckRanges: []AckRange{{Smallest: 0x2eadbeef, Largest: 0x2eadbeef}}, DelayTime: 18 * time.Millisecond, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] var frame AckFrame n, err := parseAckFrame(&frame, b, typ, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(b))) Expect(&frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(frame.DelayTime).To(Equal(f.DelayTime)) }) It("writes a frame that acks many packets", func() { f := &AckFrame{ AckRanges: []AckRange{{Smallest: 0x1337, Largest: 0x2eadbeef}}, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] var frame AckFrame n, err := parseAckFrame(&frame, b, typ, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(b))) Expect(&frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeFalse()) }) It("writes a frame with a a single gap", func() { f := &AckFrame{ AckRanges: []AckRange{ {Smallest: 400, Largest: 1000}, {Smallest: 100, Largest: 200}, }, } Expect(f.validateAckRanges()).To(BeTrue()) b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] var frame AckFrame n, err := parseAckFrame(&frame, b, typ, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(b))) Expect(&frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeTrue()) }) It("writes a frame with multiple ranges", func() { f := &AckFrame{ AckRanges: []AckRange{ {Smallest: 10, Largest: 10}, {Smallest: 8, Largest: 8}, {Smallest: 5, Largest: 6}, {Smallest: 1, Largest: 3}, }, } Expect(f.validateAckRanges()).To(BeTrue()) b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] var frame AckFrame n, err := parseAckFrame(&frame, b, typ, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(b))) Expect(&frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeTrue()) }) It("limits the maximum size of the ACK frame", func() { const numRanges = 1000 ackRanges := make([]AckRange, numRanges) for i := protocol.PacketNumber(1); i <= numRanges; i++ { ackRanges[numRanges-i] = AckRange{Smallest: 2 * i, Largest: 2 * i} } f := &AckFrame{AckRanges: ackRanges} Expect(f.validateAckRanges()).To(BeTrue()) b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) // make sure the ACK frame is *a little bit* smaller than the MaxAckFrameSize Expect(len(b)).To(BeNumerically(">", protocol.MaxAckFrameSize-5)) Expect(len(b)).To(BeNumerically("<=", protocol.MaxAckFrameSize)) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] var frame AckFrame n, err := parseAckFrame(&frame, b, typ, protocol.AckDelayExponent, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(len(b))) Expect(frame.HasMissingRanges()).To(BeTrue()) Expect(len(frame.AckRanges)).To(BeNumerically("<", numRanges)) // make sure we dropped some ranges }) }) Context("ACK range validator", func() { It("rejects ACKs without ranges", func() { Expect((&AckFrame{}).validateAckRanges()).To(BeFalse()) }) It("accepts an ACK without NACK Ranges", func() { ack := AckFrame{ AckRanges: []AckRange{{Smallest: 1, Largest: 7}}, } Expect(ack.validateAckRanges()).To(BeTrue()) }) It("rejects ACK ranges with Smallest greater than Largest", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 8, Largest: 10}, {Smallest: 4, Largest: 3}, }, } Expect(ack.validateAckRanges()).To(BeFalse()) }) It("rejects ACK ranges in the wrong order", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 2, Largest: 2}, {Smallest: 6, Largest: 7}, }, } Expect(ack.validateAckRanges()).To(BeFalse()) }) It("rejects with overlapping ACK ranges", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 5, Largest: 7}, {Smallest: 2, Largest: 5}, }, } Expect(ack.validateAckRanges()).To(BeFalse()) }) It("rejects ACK ranges that are part of a larger ACK range", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 4, Largest: 7}, {Smallest: 5, Largest: 6}, }, } Expect(ack.validateAckRanges()).To(BeFalse()) }) It("rejects with directly adjacent ACK ranges", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 5, Largest: 7}, {Smallest: 2, Largest: 4}, }, } Expect(ack.validateAckRanges()).To(BeFalse()) }) It("accepts an ACK with one lost packet", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 5, Largest: 10}, {Smallest: 1, Largest: 3}, }, } Expect(ack.validateAckRanges()).To(BeTrue()) }) It("accepts an ACK with multiple lost packets", func() { ack := AckFrame{ AckRanges: []AckRange{ {Smallest: 15, Largest: 20}, {Smallest: 10, Largest: 12}, {Smallest: 1, Largest: 3}, }, } Expect(ack.validateAckRanges()).To(BeTrue()) }) }) Context("check if ACK frame acks a certain packet", func() { It("works with an ACK without any ranges", func() { f := AckFrame{ AckRanges: []AckRange{{Smallest: 5, Largest: 10}}, } Expect(f.AcksPacket(1)).To(BeFalse()) Expect(f.AcksPacket(4)).To(BeFalse()) Expect(f.AcksPacket(5)).To(BeTrue()) Expect(f.AcksPacket(8)).To(BeTrue()) Expect(f.AcksPacket(10)).To(BeTrue()) Expect(f.AcksPacket(11)).To(BeFalse()) Expect(f.AcksPacket(20)).To(BeFalse()) }) It("works with an ACK with multiple ACK ranges", func() { f := AckFrame{ AckRanges: []AckRange{ {Smallest: 15, Largest: 20}, {Smallest: 5, Largest: 8}, }, } Expect(f.AcksPacket(4)).To(BeFalse()) Expect(f.AcksPacket(5)).To(BeTrue()) Expect(f.AcksPacket(6)).To(BeTrue()) Expect(f.AcksPacket(7)).To(BeTrue()) Expect(f.AcksPacket(8)).To(BeTrue()) Expect(f.AcksPacket(9)).To(BeFalse()) Expect(f.AcksPacket(14)).To(BeFalse()) Expect(f.AcksPacket(15)).To(BeTrue()) Expect(f.AcksPacket(18)).To(BeTrue()) Expect(f.AcksPacket(19)).To(BeTrue()) Expect(f.AcksPacket(20)).To(BeTrue()) Expect(f.AcksPacket(21)).To(BeFalse()) }) }) It("resets", func() { f := &AckFrame{ DelayTime: time.Second, AckRanges: []AckRange{{Smallest: 1, Largest: 3}}, ECT0: 1, ECT1: 2, ECNCE: 3, } f.Reset() Expect(f.AckRanges).To(BeEmpty()) Expect(f.AckRanges).To(HaveCap(1)) Expect(f.DelayTime).To(BeZero()) Expect(f.ECT0).To(BeZero()) Expect(f.ECT1).To(BeZero()) Expect(f.ECNCE).To(BeZero()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/ack_range.go000066400000000000000000000005211465664453100256300ustar00rootroot00000000000000package wire import "github.com/quic-go/quic-go/internal/protocol" // AckRange is an ACK range type AckRange struct { Smallest protocol.PacketNumber Largest protocol.PacketNumber } // Len returns the number of packets contained in this ACK range func (r AckRange) Len() protocol.PacketNumber { return r.Largest - r.Smallest + 1 } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/ack_range_test.go000066400000000000000000000004721465664453100266740ustar00rootroot00000000000000package wire import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("ACK range", func() { It("returns the length", func() { Expect(AckRange{Smallest: 10, Largest: 10}.Len()).To(BeEquivalentTo(1)) Expect(AckRange{Smallest: 10, Largest: 13}.Len()).To(BeEquivalentTo(4)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/connection_close_frame.go000066400000000000000000000040511465664453100304160ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A ConnectionCloseFrame is a CONNECTION_CLOSE frame type ConnectionCloseFrame struct { IsApplicationError bool ErrorCode uint64 FrameType uint64 ReasonPhrase string } func parseConnectionCloseFrame(b []byte, typ uint64, _ protocol.Version) (*ConnectionCloseFrame, int, error) { startLen := len(b) f := &ConnectionCloseFrame{IsApplicationError: typ == applicationCloseFrameType} ec, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] f.ErrorCode = ec // read the Frame Type, if this is not an application error if !f.IsApplicationError { ft, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] f.FrameType = ft } var reasonPhraseLen uint64 reasonPhraseLen, l, err = quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] if int(reasonPhraseLen) > len(b) { return nil, 0, io.EOF } reasonPhrase := make([]byte, reasonPhraseLen) copy(reasonPhrase, b) f.ReasonPhrase = string(reasonPhrase) return f, startLen - len(b) + int(reasonPhraseLen), nil } // Length of a written frame func (f *ConnectionCloseFrame) Length(protocol.Version) protocol.ByteCount { length := 1 + protocol.ByteCount(quicvarint.Len(f.ErrorCode)+quicvarint.Len(uint64(len(f.ReasonPhrase)))) + protocol.ByteCount(len(f.ReasonPhrase)) if !f.IsApplicationError { length += protocol.ByteCount(quicvarint.Len(f.FrameType)) // for the frame type } return length } func (f *ConnectionCloseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { if f.IsApplicationError { b = append(b, applicationCloseFrameType) } else { b = append(b, connectionCloseFrameType) } b = quicvarint.Append(b, f.ErrorCode) if !f.IsApplicationError { b = quicvarint.Append(b, f.FrameType) } b = quicvarint.Append(b, uint64(len(f.ReasonPhrase))) b = append(b, []byte(f.ReasonPhrase)...) return b, nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/connection_close_frame_test.go000066400000000000000000000123611465664453100314600ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("CONNECTION_CLOSE Frame", func() { Context("when parsing", func() { It("accepts sample frame containing a QUIC error code", func() { reason := "No recent network activity." data := encodeVarInt(0x19) data = append(data, encodeVarInt(0x1337)...) // frame type data = append(data, encodeVarInt(uint64(len(reason)))...) // reason phrase length data = append(data, []byte(reason)...) frame, l, err := parseConnectionCloseFrame(data, connectionCloseFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.IsApplicationError).To(BeFalse()) Expect(frame.ErrorCode).To(BeEquivalentTo(0x19)) Expect(frame.FrameType).To(BeEquivalentTo(0x1337)) Expect(frame.ReasonPhrase).To(Equal(reason)) Expect(l).To(Equal(len(data))) }) It("accepts sample frame containing an application error code", func() { reason := "The application messed things up." data := encodeVarInt(0xcafe) data = append(data, encodeVarInt(uint64(len(reason)))...) // reason phrase length data = append(data, reason...) frame, l, err := parseConnectionCloseFrame(data, applicationCloseFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.IsApplicationError).To(BeTrue()) Expect(frame.ErrorCode).To(BeEquivalentTo(0xcafe)) Expect(frame.ReasonPhrase).To(Equal(reason)) Expect(l).To(Equal(len(data))) }) It("rejects long reason phrases", func() { data := encodeVarInt(0xcafe) data = append(data, encodeVarInt(0x42)...) // frame type data = append(data, encodeVarInt(0xffff)...) // reason phrase length _, _, err := parseConnectionCloseFrame(data, connectionCloseFrameType, protocol.Version1) Expect(err).To(MatchError(io.EOF)) }) It("errors on EOFs", func() { reason := "No recent network activity." data := encodeVarInt(0x19) data = append(data, encodeVarInt(0x1337)...) // frame type data = append(data, encodeVarInt(uint64(len(reason)))...) // reason phrase length data = append(data, []byte(reason)...) _, l, err := parseConnectionCloseFrame(data, connectionCloseFrameType, protocol.Version1) Expect(l).To(Equal(len(data))) Expect(err).NotTo(HaveOccurred()) for i := range data { _, _, err = parseConnectionCloseFrame(data[:i], connectionCloseFrameType, protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) It("parses a frame without a reason phrase", func() { data := encodeVarInt(0xcafe) data = append(data, encodeVarInt(0x42)...) // frame type data = append(data, encodeVarInt(0)...) frame, l, err := parseConnectionCloseFrame(data, connectionCloseFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.ReasonPhrase).To(BeEmpty()) Expect(l).To(Equal(len(data))) }) }) Context("when writing", func() { It("writes a frame without a reason phrase", func() { frame := &ConnectionCloseFrame{ ErrorCode: 0xbeef, FrameType: 0x12345, } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{connectionCloseFrameType} expected = append(expected, encodeVarInt(0xbeef)...) expected = append(expected, encodeVarInt(0x12345)...) // frame type expected = append(expected, encodeVarInt(0)...) // reason phrase length Expect(b).To(Equal(expected)) }) It("writes a frame with a reason phrase", func() { frame := &ConnectionCloseFrame{ ErrorCode: 0xdead, ReasonPhrase: "foobar", } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{connectionCloseFrameType} expected = append(expected, encodeVarInt(0xdead)...) expected = append(expected, encodeVarInt(0)...) // frame type expected = append(expected, encodeVarInt(6)...) // reason phrase length expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("writes a frame with an application error code", func() { frame := &ConnectionCloseFrame{ IsApplicationError: true, ErrorCode: 0xdead, ReasonPhrase: "foobar", } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{applicationCloseFrameType} expected = append(expected, encodeVarInt(0xdead)...) expected = append(expected, encodeVarInt(6)...) // reason phrase length expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("has proper min length, for a frame containing a QUIC error code", func() { f := &ConnectionCloseFrame{ ErrorCode: 0xcafe, FrameType: 0xdeadbeef, ReasonPhrase: "foobar", } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) }) It("has proper min length, for a frame containing an application error code", func() { f := &ConnectionCloseFrame{ IsApplicationError: true, ErrorCode: 0xcafe, ReasonPhrase: "foobar", } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(f.Length(protocol.Version1)))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/crypto_frame.go000066400000000000000000000051601465664453100264140ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A CryptoFrame is a CRYPTO frame type CryptoFrame struct { Offset protocol.ByteCount Data []byte } func parseCryptoFrame(b []byte, _ protocol.Version) (*CryptoFrame, int, error) { startLen := len(b) frame := &CryptoFrame{} offset, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] frame.Offset = protocol.ByteCount(offset) dataLen, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] if dataLen > uint64(len(b)) { return nil, 0, io.EOF } if dataLen != 0 { frame.Data = make([]byte, dataLen) copy(frame.Data, b) } return frame, startLen - len(b) + int(dataLen), nil } func (f *CryptoFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, cryptoFrameType) b = quicvarint.Append(b, uint64(f.Offset)) b = quicvarint.Append(b, uint64(len(f.Data))) b = append(b, f.Data...) return b, nil } // Length of a written frame func (f *CryptoFrame) Length(_ protocol.Version) protocol.ByteCount { return protocol.ByteCount(1 + quicvarint.Len(uint64(f.Offset)) + quicvarint.Len(uint64(len(f.Data))) + len(f.Data)) } // MaxDataLen returns the maximum data length func (f *CryptoFrame) MaxDataLen(maxSize protocol.ByteCount) protocol.ByteCount { // pretend that the data size will be 1 bytes // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards headerLen := protocol.ByteCount(1 + quicvarint.Len(uint64(f.Offset)) + 1) if headerLen > maxSize { return 0 } maxDataLen := maxSize - headerLen if quicvarint.Len(uint64(maxDataLen)) != 1 { maxDataLen-- } return maxDataLen } // MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes. // It returns if the frame was actually split. // The frame might not be split if: // * the size is large enough to fit the whole frame // * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil. func (f *CryptoFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.Version) (*CryptoFrame, bool /* was splitting required */) { if f.Length(version) <= maxSize { return nil, false } n := f.MaxDataLen(maxSize) if n == 0 { return nil, true } newLen := protocol.ByteCount(len(f.Data)) - n new := &CryptoFrame{} new.Offset = f.Offset new.Data = make([]byte, newLen) // swap the data slices new.Data, f.Data = f.Data, new.Data copy(f.Data, new.Data[n:]) new.Data = new.Data[:n] f.Offset += n return new, true } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/crypto_frame_test.go000066400000000000000000000107001465664453100274470ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("CRYPTO frame", func() { Context("when parsing", func() { It("parses", func() { data := encodeVarInt(0xdecafbad) // offset data = append(data, encodeVarInt(6)...) // length data = append(data, []byte("foobar")...) frame, l, err := parseCryptoFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.Offset).To(Equal(protocol.ByteCount(0xdecafbad))) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdecafbad) // offset data = append(data, encodeVarInt(6)...) // data length data = append(data, []byte("foobar")...) _, l, err := parseCryptoFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseCryptoFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a frame", func() { f := &CryptoFrame{ Offset: 0x123456, Data: []byte("foobar"), } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{cryptoFrameType} expected = append(expected, encodeVarInt(0x123456)...) // offset expected = append(expected, encodeVarInt(6)...) // length expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) }) Context("max data length", func() { const maxSize = 3000 It("always returns a data length such that the resulting frame has the right size", func() { data := make([]byte, maxSize) f := &CryptoFrame{ Offset: 0xdeadbeef, } var frameOneByteTooSmallCounter int for i := 1; i < maxSize; i++ { f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i)) if maxDataLen == 0 { // 0 means that no valid CRYTPO frame can be written // check that writing a minimal size CRYPTO frame (i.e. with 1 byte data) is actually larger than the desired size f.Data = []byte{0} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(len(b)).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) // There's *one* pathological case, where a data length of x can be encoded into 1 byte // but a data lengths of x+1 needs 2 bytes // In that case, it's impossible to create a STREAM frame of the desired size if len(b) == i-1 { frameOneByteTooSmallCounter++ continue } Expect(len(b)).To(Equal(i)) } Expect(frameOneByteTooSmallCounter).To(Equal(1)) }) }) Context("length", func() { It("has the right length for a frame without offset and data length", func() { f := &CryptoFrame{ Offset: 0x1337, Data: []byte("foobar"), } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0x1337) + quicvarint.Len(6) + 6)) }) }) Context("splitting", func() { It("splits a frame", func() { f := &CryptoFrame{ Offset: 0x1337, Data: []byte("foobar"), } hdrLen := f.Length(protocol.Version1) - 6 new, needsSplit := f.MaybeSplitOffFrame(hdrLen+3, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(new.Data).To(Equal([]byte("foo"))) Expect(new.Offset).To(Equal(protocol.ByteCount(0x1337))) Expect(f.Data).To(Equal([]byte("bar"))) Expect(f.Offset).To(Equal(protocol.ByteCount(0x1337 + 3))) }) It("doesn't split if there's enough space in the frame", func() { f := &CryptoFrame{ Offset: 0x1337, Data: []byte("foobar"), } f, needsSplit := f.MaybeSplitOffFrame(f.Length(protocol.Version1), protocol.Version1) Expect(needsSplit).To(BeFalse()) Expect(f).To(BeNil()) }) It("doesn't split if the size is too small", func() { f := &CryptoFrame{ Offset: 0x1337, Data: []byte("foobar"), } length := f.Length(protocol.Version1) - 6 for i := protocol.ByteCount(0); i <= length; i++ { f, needsSplit := f.MaybeSplitOffFrame(i, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(f).To(BeNil()) } f, needsSplit := f.MaybeSplitOffFrame(length+1, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(f).ToNot(BeNil()) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/data_blocked_frame.go000066400000000000000000000015411465664453100274670ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A DataBlockedFrame is a DATA_BLOCKED frame type DataBlockedFrame struct { MaximumData protocol.ByteCount } func parseDataBlockedFrame(b []byte, _ protocol.Version) (*DataBlockedFrame, int, error) { offset, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } return &DataBlockedFrame{MaximumData: protocol.ByteCount(offset)}, l, nil } func (f *DataBlockedFrame) Append(b []byte, version protocol.Version) ([]byte, error) { b = append(b, dataBlockedFrameType) return quicvarint.Append(b, uint64(f.MaximumData)), nil } // Length of a written frame func (f *DataBlockedFrame) Length(version protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.MaximumData))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/data_blocked_frame_test.go000066400000000000000000000026611465664453100305320ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("DATA_BLOCKED frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { data := encodeVarInt(0x12345678) frame, l, err := parseDataBlockedFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.MaximumData).To(Equal(protocol.ByteCount(0x12345678))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0x12345678) _, l, err := parseDataBlockedFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseDataBlockedFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := DataBlockedFrame{MaximumData: 0xdeadbeef} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{dataBlockedFrameType} expected = append(expected, encodeVarInt(0xdeadbeef)...) Expect(b).To(Equal(expected)) }) It("has the correct min length", func() { frame := DataBlockedFrame{MaximumData: 0x12345} Expect(frame.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0x12345))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/datagram_frame.go000066400000000000000000000043151465664453100266550ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // MaxDatagramSize is the maximum size of a DATAGRAM frame (RFC 9221). // By setting it to a large value, we allow all datagrams that fit into a QUIC packet. // The value is chosen such that it can still be encoded as a 2 byte varint. // This is a var and not a const so it can be set in tests. var MaxDatagramSize protocol.ByteCount = 16383 // A DatagramFrame is a DATAGRAM frame type DatagramFrame struct { DataLenPresent bool Data []byte } func parseDatagramFrame(b []byte, typ uint64, _ protocol.Version) (*DatagramFrame, int, error) { startLen := len(b) f := &DatagramFrame{} f.DataLenPresent = typ&0x1 > 0 var length uint64 if f.DataLenPresent { var err error var l int length, l, err = quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] if length > uint64(len(b)) { return nil, 0, io.EOF } } else { length = uint64(len(b)) } f.Data = make([]byte, length) copy(f.Data, b) return f, startLen - len(b) + int(length), nil } func (f *DatagramFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { typ := uint8(0x30) if f.DataLenPresent { typ ^= 0b1 } b = append(b, typ) if f.DataLenPresent { b = quicvarint.Append(b, uint64(len(f.Data))) } b = append(b, f.Data...) return b, nil } // MaxDataLen returns the maximum data length func (f *DatagramFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.Version) protocol.ByteCount { headerLen := protocol.ByteCount(1) if f.DataLenPresent { // pretend that the data size will be 1 bytes // if it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterwards headerLen++ } if headerLen > maxSize { return 0 } maxDataLen := maxSize - headerLen if f.DataLenPresent && quicvarint.Len(uint64(maxDataLen)) != 1 { maxDataLen-- } return maxDataLen } // Length of a written frame func (f *DatagramFrame) Length(_ protocol.Version) protocol.ByteCount { length := 1 + protocol.ByteCount(len(f.Data)) if f.DataLenPresent { length += protocol.ByteCount(quicvarint.Len(uint64(len(f.Data)))) } return length } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/datagram_frame_test.go000066400000000000000000000117571465664453100277240ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("STREAM frame", func() { Context("when parsing", func() { It("parses a frame containing a length", func() { data := encodeVarInt(0x6) // length data = append(data, []byte("foobar")...) frame, l, err := parseDatagramFrame(data, 0x30^0x1, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(frame.DataLenPresent).To(BeTrue()) Expect(l).To(Equal(len(data))) }) It("parses a frame without length", func() { data := []byte("Lorem ipsum dolor sit amet") frame, l, err := parseDatagramFrame(data, 0x30, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.Data).To(Equal([]byte("Lorem ipsum dolor sit amet"))) Expect(frame.DataLenPresent).To(BeFalse()) Expect(l).To(Equal(len(data))) }) It("errors when the length is longer than the rest of the frame", func() { data := encodeVarInt(0x6) // length data = append(data, []byte("fooba")...) _, _, err := parseDatagramFrame(data, 0x30^0x1, protocol.Version1) Expect(err).To(MatchError(io.EOF)) }) It("errors on EOFs", func() { const typ = 0x30 ^ 0x1 data := encodeVarInt(6) // length data = append(data, []byte("foobar")...) _, l, err := parseDatagramFrame(data, typ, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err = parseDatagramFrame(data[0:i], typ, protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a frame with length", func() { f := &DatagramFrame{ DataLenPresent: true, Data: []byte("foobar"), } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x30 ^ 0x1} expected = append(expected, encodeVarInt(0x6)...) expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("writes a frame without length", func() { f := &DatagramFrame{Data: []byte("Lorem ipsum")} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x30} expected = append(expected, []byte("Lorem ipsum")...) Expect(b).To(Equal(expected)) }) }) Context("length", func() { It("has the right length for a frame with length", func() { f := &DatagramFrame{ DataLenPresent: true, Data: []byte("foobar"), } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(6) + 6)) }) It("has the right length for a frame without length", func() { f := &DatagramFrame{Data: []byte("foobar")} Expect(f.Length(protocol.Version1)).To(Equal(protocol.ByteCount(1 + 6))) }) }) Context("max data length", func() { const maxSize = 3000 It("returns a data length such that the resulting frame has the right size, if data length is not present", func() { data := make([]byte, maxSize) f := &DatagramFrame{} b := &bytes.Buffer{} for i := 1; i < 3000; i++ { b.Reset() f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), protocol.Version1) if maxDataLen == 0 { // 0 means that no valid STREAM frame can be written // check that writing a minimal size STREAM frame (i.e. with 1 byte data) is actually larger than the desired size f.Data = []byte{0} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(len(b)).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(i)) } }) It("always returns a data length such that the resulting frame has the right size, if data length is present", func() { data := make([]byte, maxSize) f := &DatagramFrame{DataLenPresent: true} var frameOneByteTooSmallCounter int for i := 1; i < 3000; i++ { f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), protocol.Version1) if maxDataLen == 0 { // 0 means that no valid STREAM frame can be written // check that writing a minimal size STREAM frame (i.e. with 1 byte data) is actually larger than the desired size f.Data = []byte{0} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(len(b)).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) // There's *one* pathological case, where a data length of x can be encoded into 1 byte // but a data lengths of x+1 needs 2 bytes // In that case, it's impossible to create a STREAM frame of the desired size if len(b) == i-1 { frameOneByteTooSmallCounter++ continue } Expect(b).To(HaveLen(i)) } Expect(frameOneByteTooSmallCounter).To(Equal(1)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/extended_header.go000066400000000000000000000124361465664453100270360ustar00rootroot00000000000000package wire import ( "encoding/binary" "errors" "fmt" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/quicvarint" ) // ErrInvalidReservedBits is returned when the reserved bits are incorrect. // When this error is returned, parsing continues, and an ExtendedHeader is returned. // This is necessary because we need to decrypt the packet in that case, // in order to avoid a timing side-channel. var ErrInvalidReservedBits = errors.New("invalid reserved bits") // ExtendedHeader is the header of a QUIC packet. type ExtendedHeader struct { Header typeByte byte KeyPhase protocol.KeyPhaseBit PacketNumberLen protocol.PacketNumberLen PacketNumber protocol.PacketNumber parsedLen protocol.ByteCount } func (h *ExtendedHeader) parse(data []byte) (bool /* reserved bits valid */, error) { // read the (now unencrypted) first byte h.typeByte = data[0] h.PacketNumberLen = protocol.PacketNumberLen(h.typeByte&0x3) + 1 if protocol.ByteCount(len(data)) < h.Header.ParsedLen()+protocol.ByteCount(h.PacketNumberLen) { return false, io.EOF } pn, err := readPacketNumber(data[h.Header.ParsedLen():], h.PacketNumberLen) if err != nil { return true, nil } h.PacketNumber = pn reservedBitsValid := h.typeByte&0xc == 0 h.parsedLen = h.Header.ParsedLen() + protocol.ByteCount(h.PacketNumberLen) return reservedBitsValid, err } // Append appends the Header. func (h *ExtendedHeader) Append(b []byte, v protocol.Version) ([]byte, error) { if h.DestConnectionID.Len() > protocol.MaxConnIDLen { return nil, fmt.Errorf("invalid connection ID length: %d bytes", h.DestConnectionID.Len()) } if h.SrcConnectionID.Len() > protocol.MaxConnIDLen { return nil, fmt.Errorf("invalid connection ID length: %d bytes", h.SrcConnectionID.Len()) } var packetType uint8 if v == protocol.Version2 { //nolint:exhaustive switch h.Type { case protocol.PacketTypeInitial: packetType = 0b01 case protocol.PacketType0RTT: packetType = 0b10 case protocol.PacketTypeHandshake: packetType = 0b11 case protocol.PacketTypeRetry: packetType = 0b00 } } else { //nolint:exhaustive switch h.Type { case protocol.PacketTypeInitial: packetType = 0b00 case protocol.PacketType0RTT: packetType = 0b01 case protocol.PacketTypeHandshake: packetType = 0b10 case protocol.PacketTypeRetry: packetType = 0b11 } } firstByte := 0xc0 | packetType<<4 if h.Type != protocol.PacketTypeRetry { // Retry packets don't have a packet number firstByte |= uint8(h.PacketNumberLen - 1) } b = append(b, firstByte) b = append(b, make([]byte, 4)...) binary.BigEndian.PutUint32(b[len(b)-4:], uint32(h.Version)) b = append(b, uint8(h.DestConnectionID.Len())) b = append(b, h.DestConnectionID.Bytes()...) b = append(b, uint8(h.SrcConnectionID.Len())) b = append(b, h.SrcConnectionID.Bytes()...) //nolint:exhaustive switch h.Type { case protocol.PacketTypeRetry: b = append(b, h.Token...) return b, nil case protocol.PacketTypeInitial: b = quicvarint.Append(b, uint64(len(h.Token))) b = append(b, h.Token...) } b = quicvarint.AppendWithLen(b, uint64(h.Length), 2) return appendPacketNumber(b, h.PacketNumber, h.PacketNumberLen) } // ParsedLen returns the number of bytes that were consumed when parsing the header func (h *ExtendedHeader) ParsedLen() protocol.ByteCount { return h.parsedLen } // GetLength determines the length of the Header. func (h *ExtendedHeader) GetLength(_ protocol.Version) protocol.ByteCount { length := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn ID len */ + protocol.ByteCount(h.DestConnectionID.Len()) + 1 /* src conn ID len */ + protocol.ByteCount(h.SrcConnectionID.Len()) + protocol.ByteCount(h.PacketNumberLen) + 2 /* length */ if h.Type == protocol.PacketTypeInitial { length += protocol.ByteCount(quicvarint.Len(uint64(len(h.Token))) + len(h.Token)) } return length } // Log logs the Header func (h *ExtendedHeader) Log(logger utils.Logger) { var token string if h.Type == protocol.PacketTypeInitial || h.Type == protocol.PacketTypeRetry { if len(h.Token) == 0 { token = "Token: (empty), " } else { token = fmt.Sprintf("Token: %#x, ", h.Token) } if h.Type == protocol.PacketTypeRetry { logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sVersion: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.Version) return } } logger.Debugf("\tLong Header{Type: %s, DestConnectionID: %s, SrcConnectionID: %s, %sPacketNumber: %d, PacketNumberLen: %d, Length: %d, Version: %s}", h.Type, h.DestConnectionID, h.SrcConnectionID, token, h.PacketNumber, h.PacketNumberLen, h.Length, h.Version) } func appendPacketNumber(b []byte, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen) ([]byte, error) { switch pnLen { case protocol.PacketNumberLen1: b = append(b, uint8(pn)) case protocol.PacketNumberLen2: buf := make([]byte, 2) binary.BigEndian.PutUint16(buf, uint16(pn)) b = append(b, buf...) case protocol.PacketNumberLen3: buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, uint32(pn)) b = append(b, buf[1:]...) case protocol.PacketNumberLen4: buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, uint32(pn)) b = append(b, buf...) default: return nil, fmt.Errorf("invalid packet number length: %d", pnLen) } return b, nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/extended_header_test.go000066400000000000000000000335261465664453100301000ustar00rootroot00000000000000package wire import ( "bytes" "log" "os" "testing" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Header", func() { Context("Writing", func() { Context("Long Header, version 1", func() { srcConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}) It("writes", func() { b, err := (&ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad, 0x0, 0x0, 0x13, 0x37}), Version: 0x1020304, Length: 1234, }, PacketNumber: 0xdecaf, PacketNumberLen: protocol.PacketNumberLen3, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{ 0xc0 | 0x2<<4 | 0x2, 0x1, 0x2, 0x3, 0x4, // version number 0x6, // dest connection ID length 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, // dest connection ID 0x8, // src connection ID length 0xde, 0xca, 0xfb, 0xad, 0x0, 0x0, 0x13, 0x37, // source connection ID } expected = append(expected, encodeVarInt(1234)...) // length expected = append(expected, []byte{0xd, 0xec, 0xaf}...) // packet number Expect(b).To(Equal(expected)) }) It("writes a header with a 20 byte connection ID", func() { b, err := (&ExtendedHeader{ Header: Header{ SrcConnectionID: srcConnID, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}), // connection IDs must be at most 20 bytes long Version: 0x1020304, Type: 0x5, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(ContainSubstring(string([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}))) }) It("writes an Initial containing a token", func() { token := []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") b, err := (&ExtendedHeader{ Header: Header{ Version: 0x1020304, Type: protocol.PacketTypeInitial, Token: token, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b[0]>>4&0b11 == 0) expectedSubstring := append(encodeVarInt(uint64(len(token))), token...) Expect(b).To(ContainSubstring(string(expectedSubstring))) }) It("uses a 2-byte encoding for the length on Initial packets", func() { b, err := (&ExtendedHeader{ Header: Header{ Version: 0x1020304, Type: protocol.PacketTypeInitial, Length: 37, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) lengthEncoded := quicvarint.AppendWithLen(nil, 37, 2) Expect(b[len(b)-6 : len(b)-4]).To(Equal(lengthEncoded)) }) It("writes a Retry packet", func() { token := []byte("Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.") b, err := (&ExtendedHeader{Header: Header{ Version: protocol.Version1, Type: protocol.PacketTypeRetry, Token: token, }}).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0xc0 | 0b11<<4} expected = appendVersion(expected, protocol.Version1) expected = append(expected, 0x0) // dest connection ID length expected = append(expected, 0x0) // src connection ID length expected = append(expected, token...) Expect(b).To(Equal(expected)) }) }) Context("long header, version 2", func() { It("writes an Initial", func() { b, err := (&ExtendedHeader{ Header: Header{ Version: protocol.Version2, Type: protocol.PacketTypeInitial, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Append(nil, protocol.Version2) Expect(err).ToNot(HaveOccurred()) Expect(b[0]>>4&0b11 == 0b01) }) It("writes a Retry packet", func() { token := []byte("Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.") b, err := (&ExtendedHeader{Header: Header{ Version: protocol.Version2, Type: protocol.PacketTypeRetry, Token: token, }}).Append(nil, protocol.Version2) Expect(err).ToNot(HaveOccurred()) expected := []byte{0xc0 | 0b00<<4} expected = appendVersion(expected, protocol.Version2) expected = append(expected, 0x0) // dest connection ID length expected = append(expected, 0x0) // src connection ID length expected = append(expected, token...) Expect(b).To(Equal(expected)) }) It("writes a Handshake Packet", func() { b, err := (&ExtendedHeader{ Header: Header{ Version: protocol.Version2, Type: protocol.PacketTypeHandshake, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Append(nil, protocol.Version2) Expect(err).ToNot(HaveOccurred()) Expect(b[0]>>4&0b11 == 0b11) }) It("writes a 0-RTT Packet", func() { b, err := (&ExtendedHeader{ Header: Header{ Version: protocol.Version2, Type: protocol.PacketType0RTT, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Append(nil, protocol.Version2) Expect(err).ToNot(HaveOccurred()) Expect(b[0]>>4&0b11 == 0b10) }) }) }) Context("getting the length", func() { It("has the right length for the Long Header, for a short length", func() { h := &ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), Length: 1, }, PacketNumberLen: protocol.PacketNumberLen1, } expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn ID len */ + 8 /* dest conn id */ + 1 /* src conn ID len */ + 8 /* src conn id */ + 2 /* length */ + 1 /* packet number */ Expect(h.GetLength(protocol.Version1)).To(BeEquivalentTo(expectedLen)) b, err := h.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(expectedLen)) }) It("has the right length for the Long Header, for a long length", func() { h := &ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), Length: 1500, }, PacketNumberLen: protocol.PacketNumberLen2, } expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn id len */ + 8 /* dest conn id */ + 1 /* src conn ID len */ + 8 /* src conn id */ + 2 /* long len */ + 2 /* packet number */ Expect(h.GetLength(protocol.Version1)).To(BeEquivalentTo(expectedLen)) b, err := h.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(expectedLen)) }) It("has the right length for an Initial that has a short length", func() { h := &ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), Length: 15, }, PacketNumberLen: protocol.PacketNumberLen2, } expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn id len */ + 8 /* dest conn id */ + 1 /* src conn ID len */ + 4 /* src conn id */ + 1 /* token length */ + 2 /* length len */ + 2 /* packet number */ Expect(h.GetLength(protocol.Version1)).To(BeEquivalentTo(expectedLen)) b, err := h.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(expectedLen)) }) It("has the right length for an Initial not containing a Token", func() { h := &ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), Length: 1500, }, PacketNumberLen: protocol.PacketNumberLen2, } expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn id len */ + 8 /* dest conn id */ + 1 /* src conn ID len */ + 4 /* src conn id */ + 1 /* token length */ + 2 /* length len */ + 2 /* packet number */ Expect(h.GetLength(protocol.Version1)).To(BeEquivalentTo(expectedLen)) b, err := h.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(expectedLen)) }) It("has the right length for an Initial containing a Token", func() { h := &ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), Type: protocol.PacketTypeInitial, Length: 1500, Token: []byte("foo"), }, PacketNumberLen: protocol.PacketNumberLen2, } expectedLen := 1 /* type byte */ + 4 /* version */ + 1 /* dest conn id len */ + 8 /* dest conn id */ + 1 /* src conn id len */ + 4 /* src conn id */ + 1 /* token length */ + 3 /* token */ + 2 /* long len */ + 2 /* packet number */ Expect(h.GetLength(protocol.Version1)).To(BeEquivalentTo(expectedLen)) b, err := h.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(expectedLen)) }) }) Context("Logging", func() { var ( buf *bytes.Buffer logger utils.Logger ) BeforeEach(func() { buf = &bytes.Buffer{} logger = utils.DefaultLogger logger.SetLogLevel(utils.LogLevelDebug) log.SetOutput(buf) }) AfterEach(func() { log.SetOutput(os.Stdout) }) It("logs Long Headers", func() { (&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad, 0x013, 0x37, 0x13, 0x37}), Type: protocol.PacketTypeHandshake, Length: 54321, Version: 0xfeed, }, PacketNumber: 1337, PacketNumberLen: protocol.PacketNumberLen2, }).Log(logger) Expect(buf.String()).To(ContainSubstring("Long Header{Type: Handshake, DestConnectionID: deadbeefcafe1337, SrcConnectionID: decafbad13371337, PacketNumber: 1337, PacketNumberLen: 2, Length: 54321, Version: 0xfeed}")) }) It("logs Initial Packets with a Token", func() { (&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ParseConnectionID([]byte{0xca, 0xfe, 0x13, 0x37}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), Type: protocol.PacketTypeInitial, Token: []byte{0xde, 0xad, 0xbe, 0xef}, Length: 100, Version: 0xfeed, }, PacketNumber: 42, PacketNumberLen: protocol.PacketNumberLen2, }).Log(logger) Expect(buf.String()).To(ContainSubstring("Long Header{Type: Initial, DestConnectionID: cafe1337, SrcConnectionID: decafbad, Token: 0xdeadbeef, PacketNumber: 42, PacketNumberLen: 2, Length: 100, Version: 0xfeed}")) }) It("logs Initial packets without a Token", func() { (&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ParseConnectionID([]byte{0xca, 0xfe, 0x13, 0x37}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), Type: protocol.PacketTypeInitial, Length: 100, Version: 0xfeed, }, PacketNumber: 42, PacketNumberLen: protocol.PacketNumberLen2, }).Log(logger) Expect(buf.String()).To(ContainSubstring("Long Header{Type: Initial, DestConnectionID: cafe1337, SrcConnectionID: decafbad, Token: (empty), PacketNumber: 42, PacketNumberLen: 2, Length: 100, Version: 0xfeed}")) }) It("logs Retry packets with a Token", func() { (&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ParseConnectionID([]byte{0xca, 0xfe, 0x13, 0x37}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), Type: protocol.PacketTypeRetry, Token: []byte{0x12, 0x34, 0x56}, Version: 0xfeed, }, }).Log(logger) Expect(buf.String()).To(ContainSubstring("Long Header{Type: Retry, DestConnectionID: cafe1337, SrcConnectionID: decafbad, Token: 0x123456, Version: 0xfeed}")) }) }) }) func BenchmarkParseExtendedHeader(b *testing.B) { data, err := (&ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}), SrcConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad, 0x0, 0x0, 0x13, 0x37}), Version: protocol.Version1, Length: 1234, }, PacketNumber: 0xdecaf, PacketNumberLen: protocol.PacketNumberLen3, }).Append(nil, protocol.Version1) if err != nil { b.Fatal(err) } data = append(data, make([]byte, 1231)...) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { hdr, _, _, err := ParsePacket(data) if err != nil { b.Fatal(err) } if _, err := hdr.ParseExtended(data); err != nil { b.Fatal(err) } } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/frame_parser.go000066400000000000000000000132011465664453100263630ustar00rootroot00000000000000package wire import ( "errors" "fmt" "io" "reflect" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" ) const ( pingFrameType = 0x1 ackFrameType = 0x2 ackECNFrameType = 0x3 resetStreamFrameType = 0x4 stopSendingFrameType = 0x5 cryptoFrameType = 0x6 newTokenFrameType = 0x7 maxDataFrameType = 0x10 maxStreamDataFrameType = 0x11 bidiMaxStreamsFrameType = 0x12 uniMaxStreamsFrameType = 0x13 dataBlockedFrameType = 0x14 streamDataBlockedFrameType = 0x15 bidiStreamBlockedFrameType = 0x16 uniStreamBlockedFrameType = 0x17 newConnectionIDFrameType = 0x18 retireConnectionIDFrameType = 0x19 pathChallengeFrameType = 0x1a pathResponseFrameType = 0x1b connectionCloseFrameType = 0x1c applicationCloseFrameType = 0x1d handshakeDoneFrameType = 0x1e ) // The FrameParser parses QUIC frames, one by one. type FrameParser struct { ackDelayExponent uint8 supportsDatagrams bool // To avoid allocating when parsing, keep a single ACK frame struct. // It is used over and over again. ackFrame *AckFrame } // NewFrameParser creates a new frame parser. func NewFrameParser(supportsDatagrams bool) *FrameParser { return &FrameParser{ supportsDatagrams: supportsDatagrams, ackFrame: &AckFrame{}, } } // ParseNext parses the next frame. // It skips PADDING frames. func (p *FrameParser) ParseNext(data []byte, encLevel protocol.EncryptionLevel, v protocol.Version) (int, Frame, error) { frame, l, err := p.parseNext(data, encLevel, v) return l, frame, err } func (p *FrameParser) parseNext(b []byte, encLevel protocol.EncryptionLevel, v protocol.Version) (Frame, int, error) { var parsed int for len(b) != 0 { typ, l, err := quicvarint.Parse(b) parsed += l if err != nil { return nil, parsed, &qerr.TransportError{ ErrorCode: qerr.FrameEncodingError, ErrorMessage: err.Error(), } } b = b[l:] if typ == 0x0 { // skip PADDING frames continue } f, l, err := p.parseFrame(b, typ, encLevel, v) parsed += l if err != nil { return nil, parsed, &qerr.TransportError{ FrameType: typ, ErrorCode: qerr.FrameEncodingError, ErrorMessage: err.Error(), } } return f, parsed, nil } return nil, parsed, nil } func (p *FrameParser) parseFrame(b []byte, typ uint64, encLevel protocol.EncryptionLevel, v protocol.Version) (Frame, int, error) { var frame Frame var err error var l int if typ&0xf8 == 0x8 { frame, l, err = parseStreamFrame(b, typ, v) } else { switch typ { case pingFrameType: frame = &PingFrame{} case ackFrameType, ackECNFrameType: ackDelayExponent := p.ackDelayExponent if encLevel != protocol.Encryption1RTT { ackDelayExponent = protocol.DefaultAckDelayExponent } p.ackFrame.Reset() l, err = parseAckFrame(p.ackFrame, b, typ, ackDelayExponent, v) frame = p.ackFrame case resetStreamFrameType: frame, l, err = parseResetStreamFrame(b, v) case stopSendingFrameType: frame, l, err = parseStopSendingFrame(b, v) case cryptoFrameType: frame, l, err = parseCryptoFrame(b, v) case newTokenFrameType: frame, l, err = parseNewTokenFrame(b, v) case maxDataFrameType: frame, l, err = parseMaxDataFrame(b, v) case maxStreamDataFrameType: frame, l, err = parseMaxStreamDataFrame(b, v) case bidiMaxStreamsFrameType, uniMaxStreamsFrameType: frame, l, err = parseMaxStreamsFrame(b, typ, v) case dataBlockedFrameType: frame, l, err = parseDataBlockedFrame(b, v) case streamDataBlockedFrameType: frame, l, err = parseStreamDataBlockedFrame(b, v) case bidiStreamBlockedFrameType, uniStreamBlockedFrameType: frame, l, err = parseStreamsBlockedFrame(b, typ, v) case newConnectionIDFrameType: frame, l, err = parseNewConnectionIDFrame(b, v) case retireConnectionIDFrameType: frame, l, err = parseRetireConnectionIDFrame(b, v) case pathChallengeFrameType: frame, l, err = parsePathChallengeFrame(b, v) case pathResponseFrameType: frame, l, err = parsePathResponseFrame(b, v) case connectionCloseFrameType, applicationCloseFrameType: frame, l, err = parseConnectionCloseFrame(b, typ, v) case handshakeDoneFrameType: frame = &HandshakeDoneFrame{} case 0x30, 0x31: if p.supportsDatagrams { frame, l, err = parseDatagramFrame(b, typ, v) break } fallthrough default: err = errors.New("unknown frame type") } } if err != nil { return nil, 0, err } if !p.isAllowedAtEncLevel(frame, encLevel) { return nil, l, fmt.Errorf("%s not allowed at encryption level %s", reflect.TypeOf(frame).Elem().Name(), encLevel) } return frame, l, nil } func (p *FrameParser) isAllowedAtEncLevel(f Frame, encLevel protocol.EncryptionLevel) bool { switch encLevel { case protocol.EncryptionInitial, protocol.EncryptionHandshake: switch f.(type) { case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *PingFrame: return true default: return false } case protocol.Encryption0RTT: switch f.(type) { case *CryptoFrame, *AckFrame, *ConnectionCloseFrame, *NewTokenFrame, *PathResponseFrame, *RetireConnectionIDFrame: return false default: return true } case protocol.Encryption1RTT: return true default: panic("unknown encryption level") } } // SetAckDelayExponent sets the acknowledgment delay exponent (sent in the transport parameters). // This value is used to scale the ACK Delay field in the ACK frame. func (p *FrameParser) SetAckDelayExponent(exp uint8) { p.ackDelayExponent = exp } func replaceUnexpectedEOF(e error) error { if e == io.ErrUnexpectedEOF { return io.EOF } return e } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/frame_parser_test.go000066400000000000000000000445771465664453100274460ustar00rootroot00000000000000package wire import ( "bytes" "testing" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Frame parsing", func() { var parser FrameParser BeforeEach(func() { parser = *NewFrameParser(true) }) It("returns nil if there's nothing more to read", func() { l, f, err := parser.ParseNext(nil, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(l).To(BeZero()) Expect(f).To(BeNil()) }) It("skips PADDING frames", func() { b := []byte{0, 0} // 2 PADDING frames b, err := (&PingFrame{}).Append(b, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, f, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f).To(Equal(&PingFrame{})) Expect(l).To(Equal(2 + 1)) }) It("handles PADDING at the end", func() { l, f, err := parser.ParseNext([]byte{0, 0, 0}, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeNil()) Expect(l).To(Equal(3)) }) It("parses a single frame", func() { var b []byte for i := 0; i < 10; i++ { var err error b, err = (&PingFrame{}).Append(b, protocol.Version1) Expect(err).ToNot(HaveOccurred()) } l, f, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeAssignableToTypeOf(&PingFrame{})) Expect(l).To(Equal(1)) }) It("unpacks ACK frames", func() { f := &AckFrame{AckRanges: []AckRange{{Smallest: 1, Largest: 0x13}}} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(BeAssignableToTypeOf(f)) Expect(frame.(*AckFrame).LargestAcked()).To(Equal(protocol.PacketNumber(0x13))) Expect(l).To(Equal(len(b))) }) It("uses the custom ack delay exponent for 1RTT packets", func() { parser.SetAckDelayExponent(protocol.AckDelayExponent + 2) f := &AckFrame{ AckRanges: []AckRange{{Smallest: 1, Largest: 1}}, DelayTime: time.Second, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) _, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) // The ACK frame is always written using the protocol.AckDelayExponent. // That's why we expect a different value when parsing. Expect(frame.(*AckFrame).DelayTime).To(Equal(4 * time.Second)) }) It("uses the default ack delay exponent for non-1RTT packets", func() { parser.SetAckDelayExponent(protocol.AckDelayExponent + 2) f := &AckFrame{ AckRanges: []AckRange{{Smallest: 1, Largest: 1}}, DelayTime: time.Second, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) _, frame, err := parser.ParseNext(b, protocol.EncryptionHandshake, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.(*AckFrame).DelayTime).To(Equal(time.Second)) }) It("unpacks RESET_STREAM frames", func() { f := &ResetStreamFrame{ StreamID: 0xdeadbeef, FinalSize: 0xdecafbad1234, ErrorCode: 0x1337, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks STOP_SENDING frames", func() { f := &StopSendingFrame{StreamID: 0x42} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks CRYPTO frames", func() { f := &CryptoFrame{ Offset: 0x1337, Data: []byte("lorem ipsum"), } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks NEW_TOKEN frames", func() { f := &NewTokenFrame{Token: []byte("foobar")} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks STREAM frames", func() { f := &StreamFrame{ StreamID: 0x42, Offset: 0x1337, Fin: true, Data: []byte("foobar"), } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks MAX_DATA frames", func() { f := &MaxDataFrame{ MaximumData: 0xcafe, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks MAX_STREAM_DATA frames", func() { f := &MaxStreamDataFrame{ StreamID: 0xdeadbeef, MaximumStreamData: 0xdecafbad, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks MAX_STREAMS frames", func() { f := &MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: 0x1337, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks DATA_BLOCKED frames", func() { f := &DataBlockedFrame{MaximumData: 0x1234} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks STREAM_DATA_BLOCKED frames", func() { f := &StreamDataBlockedFrame{ StreamID: 0xdeadbeef, MaximumStreamData: 0xdead, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks STREAMS_BLOCKED frames", func() { f := &StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: 0x1234567, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks NEW_CONNECTION_ID frames", func() { f := &NewConnectionIDFrame{ SequenceNumber: 0x1337, ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks RETIRE_CONNECTION_ID frames", func() { f := &RetireConnectionIDFrame{SequenceNumber: 0x1337} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks PATH_CHALLENGE frames", func() { f := &PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(BeAssignableToTypeOf(f)) Expect(frame.(*PathChallengeFrame).Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(l).To(Equal(len(b))) }) It("unpacks PATH_RESPONSE frames", func() { f := &PathResponseFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(BeAssignableToTypeOf(f)) Expect(frame.(*PathResponseFrame).Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(l).To(Equal(len(b))) }) It("unpacks CONNECTION_CLOSE frames", func() { f := &ConnectionCloseFrame{ IsApplicationError: true, ReasonPhrase: "foobar", } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks HANDSHAKE_DONE frames", func() { f := &HandshakeDoneFrame{} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("unpacks DATAGRAM frames", func() { f := &DatagramFrame{Data: []byte("foobar")} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) l, frame, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("errors when DATAGRAM frames are not supported", func() { parser = *NewFrameParser(false) f := &DatagramFrame{Data: []byte("foobar")} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) _, _, err = parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FrameEncodingError, FrameType: 0x30, ErrorMessage: "unknown frame type", })) }) It("errors on invalid type", func() { _, _, err := parser.ParseNext(encodeVarInt(0x42), protocol.Encryption1RTT, protocol.Version1) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FrameEncodingError, FrameType: 0x42, ErrorMessage: "unknown frame type", })) }) It("errors on invalid frames", func() { f := &MaxStreamDataFrame{ StreamID: 0x1337, MaximumStreamData: 0xdeadbeef, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) _, _, err = parser.ParseNext(b[:len(b)-2], protocol.Encryption1RTT, protocol.Version1) Expect(err).To(HaveOccurred()) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.FrameEncodingError)) }) Context("encryption level check", func() { frames := []Frame{ &PingFrame{}, &AckFrame{AckRanges: []AckRange{{Smallest: 1, Largest: 42}}}, &ResetStreamFrame{}, &StopSendingFrame{}, &CryptoFrame{}, &NewTokenFrame{Token: []byte("lorem ipsum")}, &StreamFrame{Data: []byte("foobar")}, &MaxDataFrame{}, &MaxStreamDataFrame{}, &MaxStreamsFrame{}, &DataBlockedFrame{}, &StreamDataBlockedFrame{}, &StreamsBlockedFrame{}, &NewConnectionIDFrame{ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef})}, &RetireConnectionIDFrame{}, &PathChallengeFrame{}, &PathResponseFrame{}, &ConnectionCloseFrame{}, &HandshakeDoneFrame{}, &DatagramFrame{}, } var framesSerialized [][]byte BeforeEach(func() { framesSerialized = nil for _, frame := range frames { b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) framesSerialized = append(framesSerialized, b) } }) It("rejects all frames but ACK, CRYPTO, PING and CONNECTION_CLOSE in Initial packets", func() { for i, b := range framesSerialized { _, _, err := parser.ParseNext(b, protocol.EncryptionInitial, protocol.Version1) switch frames[i].(type) { case *AckFrame, *ConnectionCloseFrame, *CryptoFrame, *PingFrame: Expect(err).ToNot(HaveOccurred()) default: Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.FrameEncodingError)) Expect(err.(*qerr.TransportError).ErrorMessage).To(ContainSubstring("not allowed at encryption level Initial")) } } }) It("rejects all frames but ACK, CRYPTO, PING and CONNECTION_CLOSE in Handshake packets", func() { for i, b := range framesSerialized { _, _, err := parser.ParseNext(b, protocol.EncryptionHandshake, protocol.Version1) switch frames[i].(type) { case *AckFrame, *ConnectionCloseFrame, *CryptoFrame, *PingFrame: Expect(err).ToNot(HaveOccurred()) default: Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.FrameEncodingError)) Expect(err.(*qerr.TransportError).ErrorMessage).To(ContainSubstring("not allowed at encryption level Handshake")) } } }) It("rejects all frames but ACK, CRYPTO, CONNECTION_CLOSE, NEW_TOKEN, PATH_RESPONSE and RETIRE_CONNECTION_ID in 0-RTT packets", func() { for i, b := range framesSerialized { _, _, err := parser.ParseNext(b, protocol.Encryption0RTT, protocol.Version1) switch frames[i].(type) { case *AckFrame, *ConnectionCloseFrame, *CryptoFrame, *NewTokenFrame, *PathResponseFrame, *RetireConnectionIDFrame: Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(Equal(qerr.FrameEncodingError)) Expect(err.(*qerr.TransportError).ErrorMessage).To(ContainSubstring("not allowed at encryption level 0-RTT")) default: Expect(err).ToNot(HaveOccurred()) } } }) It("accepts all frame types in 1-RTT packets", func() { for _, b := range framesSerialized { _, _, err := parser.ParseNext(b, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) } }) }) }) // STREAM and ACK are the most relevant frames for high-throughput transfers. func BenchmarkParseStreamAndACK(b *testing.B) { ack := &AckFrame{ AckRanges: []AckRange{ {Smallest: 5000, Largest: 5200}, {Smallest: 1, Largest: 4200}, }, DelayTime: 42 * time.Millisecond, ECT0: 5000, ECT1: 0, ECNCE: 10, } sf := &StreamFrame{ StreamID: 1337, Offset: 1e7, Data: make([]byte, 200), DataLenPresent: true, } rand.Read(sf.Data) data, err := ack.Append([]byte{}, protocol.Version1) if err != nil { b.Fatal(err) } data, err = sf.Append(data, protocol.Version1) if err != nil { b.Fatal(err) } parser := NewFrameParser(false) parser.SetAckDelayExponent(3) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { l, f, err := parser.ParseNext(data, protocol.Encryption1RTT, protocol.Version1) if err != nil { b.Fatal(err) } ackParsed := f.(*AckFrame) if ackParsed.DelayTime != ack.DelayTime || ackParsed.ECNCE != ack.ECNCE { b.Fatalf("incorrect ACK frame: %v vs %v", ack, ackParsed) } l2, f, err := parser.ParseNext(data[l:], protocol.Encryption1RTT, protocol.Version1) if err != nil { b.Fatal(err) } if len(data[l:]) != l2 { b.Fatal("didn't parse the entire packet") } sfParsed := f.(*StreamFrame) if sfParsed.StreamID != sf.StreamID || !bytes.Equal(sfParsed.Data, sf.Data) { b.Fatalf("incorrect STREAM frame: %v vs %v", sf, sfParsed) } } } func BenchmarkParseOtherFrames(b *testing.B) { maxDataFrame := &MaxDataFrame{MaximumData: 123456} maxStreamsFrame := &MaxStreamsFrame{MaxStreamNum: 10} maxStreamDataFrame := &MaxStreamDataFrame{StreamID: 1337, MaximumStreamData: 1e6} cryptoFrame := &CryptoFrame{Offset: 1000, Data: make([]byte, 128)} resetStreamFrame := &ResetStreamFrame{StreamID: 87654, ErrorCode: 1234, FinalSize: 1e8} rand.Read(cryptoFrame.Data) frames := []Frame{ maxDataFrame, maxStreamsFrame, maxStreamDataFrame, cryptoFrame, &PingFrame{}, resetStreamFrame, } var buf []byte for i, frame := range frames { var err error buf, err = frame.Append(buf, protocol.Version1) if err != nil { b.Fatal(err) } if i == len(frames)/2 { // add 3 PADDING frames buf = append(buf, 0) buf = append(buf, 0) buf = append(buf, 0) } } parser := NewFrameParser(false) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { data := buf for j := 0; j < len(frames); j++ { l, f, err := parser.ParseNext(data, protocol.Encryption1RTT, protocol.Version1) if err != nil { b.Fatal(err) } data = data[l:] switch j { case 0: if f.(*MaxDataFrame).MaximumData != maxDataFrame.MaximumData { b.Fatalf("MAX_DATA frame does not match: %v vs %v", f, maxDataFrame) } case 1: if f.(*MaxStreamsFrame).MaxStreamNum != maxStreamsFrame.MaxStreamNum { b.Fatalf("MAX_STREAMS frame does not match: %v vs %v", f, maxStreamsFrame) } case 2: if f.(*MaxStreamDataFrame).StreamID != maxStreamDataFrame.StreamID || f.(*MaxStreamDataFrame).MaximumStreamData != maxStreamDataFrame.MaximumStreamData { b.Fatalf("MAX_STREAM_DATA frame does not match: %v vs %v", f, maxStreamDataFrame) } case 3: if f.(*CryptoFrame).Offset != cryptoFrame.Offset || !bytes.Equal(f.(*CryptoFrame).Data, cryptoFrame.Data) { b.Fatalf("CRYPTO frame does not match: %v vs %v", f, cryptoFrame) } case 4: _ = f.(*PingFrame) case 5: rst := f.(*ResetStreamFrame) if rst.StreamID != resetStreamFrame.StreamID || rst.ErrorCode != resetStreamFrame.ErrorCode || rst.FinalSize != resetStreamFrame.FinalSize { b.Fatalf("RESET_STREAM frame does not match: %v vs %v", rst, resetStreamFrame) } } } } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/handshake_done_frame.go000066400000000000000000000006321465664453100300260ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" ) // A HandshakeDoneFrame is a HANDSHAKE_DONE frame type HandshakeDoneFrame struct{} func (f *HandshakeDoneFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { return append(b, handshakeDoneFrameType), nil } // Length of a written frame func (f *HandshakeDoneFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/handshake_done_frame_test.go000066400000000000000000000011331465664453100310620ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("HANDSHAKE_DONE frame", func() { Context("when writing", func() { It("writes a sample frame", func() { frame := HandshakeDoneFrame{} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte{handshakeDoneFrameType})) }) It("has the correct min length", func() { frame := HandshakeDoneFrame{} Expect(frame.Length(protocol.Version1)).To(Equal(protocol.ByteCount(1))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/header.go000066400000000000000000000214431465664453100251540ustar00rootroot00000000000000package wire import ( "encoding/binary" "errors" "fmt" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // ParseConnectionID parses the destination connection ID of a packet. func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) { if len(data) == 0 { return protocol.ConnectionID{}, io.EOF } if !IsLongHeaderPacket(data[0]) { if len(data) < shortHeaderConnIDLen+1 { return protocol.ConnectionID{}, io.EOF } return protocol.ParseConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil } if len(data) < 6 { return protocol.ConnectionID{}, io.EOF } destConnIDLen := int(data[5]) if destConnIDLen > protocol.MaxConnIDLen { return protocol.ConnectionID{}, protocol.ErrInvalidConnectionIDLen } if len(data) < 6+destConnIDLen { return protocol.ConnectionID{}, io.EOF } return protocol.ParseConnectionID(data[6 : 6+destConnIDLen]), nil } // ParseArbitraryLenConnectionIDs parses the most general form of a Long Header packet, // using only the version-independent packet format as described in Section 5.1 of RFC 8999: // https://datatracker.ietf.org/doc/html/rfc8999#section-5.1. // This function should only be called on Long Header packets for which we don't support the version. func ParseArbitraryLenConnectionIDs(data []byte) (bytesParsed int, dest, src protocol.ArbitraryLenConnectionID, _ error) { startLen := len(data) if len(data) < 6 { return 0, nil, nil, io.EOF } data = data[5:] // skip first byte and version field destConnIDLen := data[0] data = data[1:] destConnID := make(protocol.ArbitraryLenConnectionID, destConnIDLen) if len(data) < int(destConnIDLen)+1 { return 0, nil, nil, io.EOF } copy(destConnID, data) data = data[destConnIDLen:] srcConnIDLen := data[0] data = data[1:] if len(data) < int(srcConnIDLen) { return 0, nil, nil, io.EOF } srcConnID := make(protocol.ArbitraryLenConnectionID, srcConnIDLen) copy(srcConnID, data) return startLen - len(data) + int(srcConnIDLen), destConnID, srcConnID, nil } func IsPotentialQUICPacket(firstByte byte) bool { return firstByte&0x40 > 0 } // IsLongHeaderPacket says if this is a Long Header packet func IsLongHeaderPacket(firstByte byte) bool { return firstByte&0x80 > 0 } // ParseVersion parses the QUIC version. // It should only be called for Long Header packets (Short Header packets don't contain a version number). func ParseVersion(data []byte) (protocol.Version, error) { if len(data) < 5 { return 0, io.EOF } return protocol.Version(binary.BigEndian.Uint32(data[1:5])), nil } // IsVersionNegotiationPacket says if this is a version negotiation packet func IsVersionNegotiationPacket(b []byte) bool { if len(b) < 5 { return false } return IsLongHeaderPacket(b[0]) && b[1] == 0 && b[2] == 0 && b[3] == 0 && b[4] == 0 } // Is0RTTPacket says if this is a 0-RTT packet. // A packet sent with a version we don't understand can never be a 0-RTT packet. func Is0RTTPacket(b []byte) bool { if len(b) < 5 { return false } if !IsLongHeaderPacket(b[0]) { return false } version := protocol.Version(binary.BigEndian.Uint32(b[1:5])) //nolint:exhaustive // We only need to test QUIC versions that we support. switch version { case protocol.Version1: return b[0]>>4&0b11 == 0b01 case protocol.Version2: return b[0]>>4&0b11 == 0b10 default: return false } } var ErrUnsupportedVersion = errors.New("unsupported version") // The Header is the version independent part of the header type Header struct { typeByte byte Type protocol.PacketType Version protocol.Version SrcConnectionID protocol.ConnectionID DestConnectionID protocol.ConnectionID Length protocol.ByteCount Token []byte parsedLen protocol.ByteCount // how many bytes were read while parsing this header } // ParsePacket parses a long header packet. // The packet is cut according to the length field. // If we understand the version, the packet is parsed up unto the packet number. // Otherwise, only the invariant part of the header is parsed. func ParsePacket(data []byte) (*Header, []byte, []byte, error) { if len(data) == 0 || !IsLongHeaderPacket(data[0]) { return nil, nil, nil, errors.New("not a long header packet") } hdr, err := parseHeader(data) if err != nil { if errors.Is(err, ErrUnsupportedVersion) { return hdr, nil, nil, err } return nil, nil, nil, err } if protocol.ByteCount(len(data)) < hdr.ParsedLen()+hdr.Length { return nil, nil, nil, fmt.Errorf("packet length (%d bytes) is smaller than the expected length (%d bytes)", len(data)-int(hdr.ParsedLen()), hdr.Length) } packetLen := int(hdr.ParsedLen() + hdr.Length) return hdr, data[:packetLen], data[packetLen:], nil } // ParseHeader parses the header: // * if we understand the version: up to the packet number // * if not, only the invariant part of the header func parseHeader(b []byte) (*Header, error) { if len(b) == 0 { return nil, io.EOF } typeByte := b[0] h := &Header{typeByte: typeByte} l, err := h.parseLongHeader(b[1:]) h.parsedLen = protocol.ByteCount(l) + 1 return h, err } func (h *Header) parseLongHeader(b []byte) (int, error) { startLen := len(b) if len(b) < 5 { return 0, io.EOF } h.Version = protocol.Version(binary.BigEndian.Uint32(b[:4])) if h.Version != 0 && h.typeByte&0x40 == 0 { return startLen - len(b), errors.New("not a QUIC packet") } destConnIDLen := int(b[4]) if destConnIDLen > protocol.MaxConnIDLen { return startLen - len(b), protocol.ErrInvalidConnectionIDLen } b = b[5:] if len(b) < destConnIDLen+1 { return startLen - len(b), io.EOF } h.DestConnectionID = protocol.ParseConnectionID(b[:destConnIDLen]) srcConnIDLen := int(b[destConnIDLen]) if srcConnIDLen > protocol.MaxConnIDLen { return startLen - len(b), protocol.ErrInvalidConnectionIDLen } b = b[destConnIDLen+1:] if len(b) < srcConnIDLen { return startLen - len(b), io.EOF } h.SrcConnectionID = protocol.ParseConnectionID(b[:srcConnIDLen]) b = b[srcConnIDLen:] if h.Version == 0 { // version negotiation packet return startLen - len(b), nil } // If we don't understand the version, we have no idea how to interpret the rest of the bytes if !protocol.IsSupportedVersion(protocol.SupportedVersions, h.Version) { return startLen - len(b), ErrUnsupportedVersion } if h.Version == protocol.Version2 { switch h.typeByte >> 4 & 0b11 { case 0b00: h.Type = protocol.PacketTypeRetry case 0b01: h.Type = protocol.PacketTypeInitial case 0b10: h.Type = protocol.PacketType0RTT case 0b11: h.Type = protocol.PacketTypeHandshake } } else { switch h.typeByte >> 4 & 0b11 { case 0b00: h.Type = protocol.PacketTypeInitial case 0b01: h.Type = protocol.PacketType0RTT case 0b10: h.Type = protocol.PacketTypeHandshake case 0b11: h.Type = protocol.PacketTypeRetry } } if h.Type == protocol.PacketTypeRetry { tokenLen := len(b) - 16 if tokenLen <= 0 { return startLen - len(b), io.EOF } h.Token = make([]byte, tokenLen) copy(h.Token, b[:tokenLen]) return startLen - len(b) + tokenLen + 16, nil } if h.Type == protocol.PacketTypeInitial { tokenLen, n, err := quicvarint.Parse(b) if err != nil { return startLen - len(b), err } b = b[n:] if tokenLen > uint64(len(b)) { return startLen - len(b), io.EOF } h.Token = make([]byte, tokenLen) copy(h.Token, b[:tokenLen]) b = b[tokenLen:] } pl, n, err := quicvarint.Parse(b) if err != nil { return 0, err } h.Length = protocol.ByteCount(pl) return startLen - len(b) + n, nil } // ParsedLen returns the number of bytes that were consumed when parsing the header func (h *Header) ParsedLen() protocol.ByteCount { return h.parsedLen } // ParseExtended parses the version dependent part of the header. // The Reader has to be set such that it points to the first byte of the header. func (h *Header) ParseExtended(data []byte) (*ExtendedHeader, error) { extHdr := h.toExtendedHeader() reservedBitsValid, err := extHdr.parse(data) if err != nil { return nil, err } if !reservedBitsValid { return extHdr, ErrInvalidReservedBits } return extHdr, nil } func (h *Header) toExtendedHeader() *ExtendedHeader { return &ExtendedHeader{Header: *h} } // PacketType is the type of the packet, for logging purposes func (h *Header) PacketType() string { return h.Type.String() } func readPacketNumber(data []byte, pnLen protocol.PacketNumberLen) (protocol.PacketNumber, error) { var pn protocol.PacketNumber switch pnLen { case protocol.PacketNumberLen1: pn = protocol.PacketNumber(data[0]) case protocol.PacketNumberLen2: pn = protocol.PacketNumber(binary.BigEndian.Uint16(data[:2])) case protocol.PacketNumberLen3: pn = protocol.PacketNumber(uint32(data[2]) + uint32(data[1])<<8 + uint32(data[0])<<16) case protocol.PacketNumberLen4: pn = protocol.PacketNumber(binary.BigEndian.Uint32(data[:4])) default: return 0, fmt.Errorf("invalid packet number length: %d", pnLen) } return pn, nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/header_test.go000066400000000000000000000600441465664453100262130ustar00rootroot00000000000000package wire import ( "bytes" "crypto/rand" "encoding/binary" "io" mrand "math/rand" "testing" "time" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Header Parsing", func() { Context("Parsing the Connection ID", func() { It("parses the connection ID of a long header packet", func() { b, err := (&ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6}), Version: protocol.Version1, }, PacketNumberLen: 2, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) connID, err := ParseConnectionID(b, 8) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}))) }) It("errors on EOF, for long header packets", func() { b, err := (&ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad, 0x13, 0x37}), SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 8, 9}), Version: protocol.Version1, }, PacketNumberLen: 2, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) data := b[:len(b)-2] // cut the packet number _, err = ParseConnectionID(data, 8) Expect(err).ToNot(HaveOccurred()) for i := 0; i < 1 /* first byte */ +4 /* version */ +1 /* conn ID lengths */ +6; /* dest conn ID */ i++ { b := make([]byte, i) copy(b, data[:i]) _, err := ParseConnectionID(b, 8) Expect(err).To(MatchError(io.EOF)) } }) It("errors when encountering a too long connection ID", func() { b := []byte{0x80, 0, 0, 0, 0} binary.BigEndian.PutUint32(b[1:], uint32(protocol.Version1)) b = append(b, 21) // dest conn id len b = append(b, make([]byte, 21)...) _, err := ParseConnectionID(b, 4) Expect(err).To(MatchError(protocol.ErrInvalidConnectionIDLen)) }) }) Context("identifying 0-RTT packets", func() { It("recognizes 0-RTT packets, for QUIC v1", func() { zeroRTTHeader := make([]byte, 5) zeroRTTHeader[0] = 0x80 | 0b01<<4 binary.BigEndian.PutUint32(zeroRTTHeader[1:], uint32(protocol.Version1)) Expect(Is0RTTPacket(zeroRTTHeader)).To(BeTrue()) Expect(Is0RTTPacket(zeroRTTHeader[:4])).To(BeFalse()) // too short Expect(Is0RTTPacket([]byte{zeroRTTHeader[0], 1, 2, 3, 4})).To(BeFalse()) // unknown version Expect(Is0RTTPacket([]byte{zeroRTTHeader[0] | 0x80, 1, 2, 3, 4})).To(BeFalse()) // short header Expect(Is0RTTPacket(append(zeroRTTHeader, []byte("foobar")...))).To(BeTrue()) }) It("recognizes 0-RTT packets, for QUIC v2", func() { zeroRTTHeader := make([]byte, 5) zeroRTTHeader[0] = 0x80 | 0b10<<4 binary.BigEndian.PutUint32(zeroRTTHeader[1:], uint32(protocol.Version2)) Expect(Is0RTTPacket(zeroRTTHeader)).To(BeTrue()) Expect(Is0RTTPacket(zeroRTTHeader[:4])).To(BeFalse()) // too short Expect(Is0RTTPacket([]byte{zeroRTTHeader[0], 1, 2, 3, 4})).To(BeFalse()) // unknown version Expect(Is0RTTPacket([]byte{zeroRTTHeader[0] | 0x80, 1, 2, 3, 4})).To(BeFalse()) // short header Expect(Is0RTTPacket(append(zeroRTTHeader, []byte("foobar")...))).To(BeTrue()) }) }) Context("parsing the version", func() { It("parses the version", func() { b := []byte{0x80, 0xde, 0xad, 0xbe, 0xef} v, err := ParseVersion(b) Expect(err).ToNot(HaveOccurred()) Expect(v).To(Equal(protocol.Version(0xdeadbeef))) }) It("errors with EOF", func() { b := []byte{0x80, 0xde, 0xad, 0xbe, 0xef} _, err := ParseVersion(b) Expect(err).ToNot(HaveOccurred()) for i := range b { _, err := ParseVersion(b[:i]) Expect(err).To(MatchError(io.EOF)) } }) }) Context("parsing arbitrary length connection IDs", func() { generateConnID := func(l int) protocol.ArbitraryLenConnectionID { c := make(protocol.ArbitraryLenConnectionID, l) rand.Read(c) return c } generatePacket := func(src, dest protocol.ArbitraryLenConnectionID) []byte { b := []byte{0x80, 1, 2, 3, 4} b = append(b, uint8(dest.Len())) b = append(b, dest.Bytes()...) b = append(b, uint8(src.Len())) b = append(b, src.Bytes()...) return b } It("parses arbitrary length connection IDs", func() { src := generateConnID(mrand.Intn(255) + 1) dest := generateConnID(mrand.Intn(255) + 1) b := generatePacket(src, dest) l := len(b) b = append(b, []byte("foobar")...) // add some payload parsed, d, s, err := ParseArbitraryLenConnectionIDs(b) Expect(parsed).To(Equal(l)) Expect(err).ToNot(HaveOccurred()) Expect(s).To(Equal(src)) Expect(d).To(Equal(dest)) }) It("errors on EOF", func() { b := generatePacket(generateConnID(mrand.Intn(255)+1), generateConnID(mrand.Intn(255)+1)) _, _, _, err := ParseArbitraryLenConnectionIDs(b) Expect(err).ToNot(HaveOccurred()) for i := range b { _, _, _, err := ParseArbitraryLenConnectionIDs(b[:i]) Expect(err).To(MatchError(io.EOF)) } }) }) Context("Identifying Version Negotiation Packets", func() { It("identifies version negotiation packets", func() { Expect(IsVersionNegotiationPacket([]byte{0x80 | 0x56, 0, 0, 0, 0})).To(BeTrue()) Expect(IsVersionNegotiationPacket([]byte{0x56, 0, 0, 0, 0})).To(BeFalse()) Expect(IsVersionNegotiationPacket([]byte{0x80, 1, 0, 0, 0})).To(BeFalse()) Expect(IsVersionNegotiationPacket([]byte{0x80, 0, 1, 0, 0})).To(BeFalse()) Expect(IsVersionNegotiationPacket([]byte{0x80, 0, 0, 1, 0})).To(BeFalse()) Expect(IsVersionNegotiationPacket([]byte{0x80, 0, 0, 0, 1})).To(BeFalse()) }) It("returns false on EOF", func() { vnp := []byte{0x80, 0, 0, 0, 0} for i := range vnp { Expect(IsVersionNegotiationPacket(vnp[:i])).To(BeFalse()) } }) }) Context("Long Headers", func() { It("parses a Long Header", func() { destConnID := protocol.ParseConnectionID([]byte{9, 8, 7, 6, 5, 4, 3, 2, 1}) srcConnID := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) data := []byte{0xc0 ^ 0x3} data = appendVersion(data, protocol.Version1) data = append(data, 0x9) // dest conn id length data = append(data, destConnID.Bytes()...) data = append(data, 0x4) // src conn id length data = append(data, srcConnID.Bytes()...) data = append(data, encodeVarInt(6)...) // token length data = append(data, []byte("foobar")...) // token data = append(data, encodeVarInt(10)...) // length hdrLen := len(data) data = append(data, []byte{0, 0, 0xbe, 0xef}...) // packet number data = append(data, []byte("foobar")...) Expect(IsVersionNegotiationPacket(data)).To(BeFalse()) hdr, pdata, rest, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) Expect(pdata).To(Equal(data)) Expect(hdr.DestConnectionID).To(Equal(destConnID)) Expect(hdr.SrcConnectionID).To(Equal(srcConnID)) Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdr.Token).To(Equal([]byte("foobar"))) Expect(hdr.Length).To(Equal(protocol.ByteCount(10))) Expect(hdr.Version).To(Equal(protocol.Version1)) Expect(rest).To(BeEmpty()) extHdr, err := hdr.ParseExtended(data) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen4)) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0xbeef))) Expect(hdr.ParsedLen()).To(BeEquivalentTo(hdrLen)) Expect(extHdr.ParsedLen()).To(Equal(hdr.ParsedLen() + 4)) }) It("errors if 0x40 is not set", func() { data := []byte{ 0x80 | 0x2<<4, 0x11, // connection ID lengths 0xde, 0xca, 0xfb, 0xad, // dest conn ID 0xde, 0xad, 0xbe, 0xef, // src conn ID } _, _, _, err := ParsePacket(data) Expect(err).To(MatchError("not a QUIC packet")) }) It("stops parsing when encountering an unsupported version", func() { data := []byte{ 0xc0, 0xde, 0xad, 0xbe, 0xef, 0x8, // dest conn ID len 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, // dest conn ID 0x8, // src conn ID len 0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1, // src conn ID 'f', 'o', 'o', 'b', 'a', 'r', // unspecified bytes } hdr, _, rest, err := ParsePacket(data) Expect(err).To(MatchError(ErrUnsupportedVersion)) Expect(hdr.Version).To(Equal(protocol.Version(0xdeadbeef))) Expect(hdr.DestConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}))) Expect(hdr.SrcConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0x8, 0x7, 0x6, 0x5, 0x4, 0x3, 0x2, 0x1}))) Expect(rest).To(BeEmpty()) }) It("parses a Long Header without a destination connection ID", func() { data := []byte{0xc0 ^ 0x1<<4} data = appendVersion(data, protocol.Version1) data = append(data, 0) // dest conn ID len data = append(data, 4) // src conn ID len data = append(data, []byte{0xde, 0xad, 0xbe, 0xef}...) // source connection ID data = append(data, encodeVarInt(0)...) // length data = append(data, []byte{0xde, 0xca, 0xfb, 0xad}...) hdr, _, _, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketType0RTT)) Expect(hdr.SrcConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}))) Expect(hdr.DestConnectionID).To(BeZero()) }) It("parses a Long Header without a source connection ID", func() { data := []byte{0xc0 ^ 0x2<<4} data = appendVersion(data, protocol.Version1) data = append(data, 10) // dest conn ID len data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // dest connection ID data = append(data, 0) // src conn ID len data = append(data, encodeVarInt(0)...) // length data = append(data, []byte{0xde, 0xca, 0xfb, 0xad}...) hdr, _, _, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) Expect(hdr.SrcConnectionID).To(BeZero()) Expect(hdr.DestConnectionID).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))) }) It("parses a Long Header without a too long destination connection ID", func() { data := []byte{0xc0 ^ 0x2<<4} data = appendVersion(data, protocol.Version1) data = append(data, 21) // dest conn ID len data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}...) // dest connection ID data = append(data, 0x0) // src conn ID len data = append(data, encodeVarInt(0)...) // length data = append(data, []byte{0xde, 0xca, 0xfb, 0xad}...) _, _, _, err := ParsePacket(data) Expect(err).To(MatchError(protocol.ErrInvalidConnectionIDLen)) }) It("parses a Long Header with a 2 byte packet number", func() { data := []byte{0xc0 ^ 0x1} data = appendVersion(data, protocol.Version1) // version number data = append(data, []byte{0x0, 0x0}...) // connection ID lengths data = append(data, encodeVarInt(0)...) // token length data = append(data, encodeVarInt(0)...) // length data = append(data, []byte{0x1, 0x23}...) hdr, _, _, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) extHdr, err := hdr.ParseExtended(data) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x123))) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen2)) Expect(extHdr.ParsedLen()).To(BeEquivalentTo(len(data))) }) It("parses a Retry packet, for QUIC v1", func() { data := []byte{0xc0 | 0b11<<4 | (10 - 3) /* connection ID length */} data = appendVersion(data, protocol.Version1) data = append(data, []byte{6}...) // dest conn ID len data = append(data, []byte{6, 5, 4, 3, 2, 1}...) // dest conn ID data = append(data, []byte{10}...) // src conn ID len data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // source connection ID data = append(data, []byte{'f', 'o', 'o', 'b', 'a', 'r'}...) // token data = append(data, []byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}...) hdr, pdata, rest, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeRetry)) Expect(hdr.Version).To(Equal(protocol.Version1)) Expect(hdr.DestConnectionID).To(Equal(protocol.ParseConnectionID([]byte{6, 5, 4, 3, 2, 1}))) Expect(hdr.SrcConnectionID).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))) Expect(hdr.Token).To(Equal([]byte("foobar"))) Expect(pdata).To(Equal(data)) Expect(rest).To(BeEmpty()) }) It("parses a Retry packet, for QUIC v2", func() { data := []byte{0xc0 | 0b00<<4 | (10 - 3) /* connection ID length */} data = appendVersion(data, protocol.Version2) data = append(data, []byte{6}...) // dest conn ID len data = append(data, []byte{6, 5, 4, 3, 2, 1}...) // dest conn ID data = append(data, []byte{10}...) // src conn ID len data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // source connection ID data = append(data, []byte{'f', 'o', 'o', 'b', 'a', 'r'}...) // token data = append(data, []byte{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}...) hdr, pdata, rest, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeRetry)) Expect(hdr.Version).To(Equal(protocol.Version2)) Expect(hdr.DestConnectionID).To(Equal(protocol.ParseConnectionID([]byte{6, 5, 4, 3, 2, 1}))) Expect(hdr.SrcConnectionID).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))) Expect(hdr.Token).To(Equal([]byte("foobar"))) Expect(pdata).To(Equal(data)) Expect(rest).To(BeEmpty()) }) It("errors if the Retry packet is too short for the integrity tag", func() { data := []byte{0xc0 | 0x3<<4 | (10 - 3) /* connection ID length */} data = appendVersion(data, protocol.Version1) data = append(data, []byte{0, 0}...) // conn ID lens data = append(data, []byte{'f', 'o', 'o', 'b', 'a', 'r'}...) // token data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // this results in a token length of 0 _, _, _, err := ParsePacket(data) Expect(err).To(MatchError(io.EOF)) }) It("errors if the token length is too large", func() { data := []byte{0xc0 ^ 0x1} data = appendVersion(data, protocol.Version1) data = append(data, 0x0) // connection ID lengths data = append(data, encodeVarInt(4)...) // token length: 4 bytes (1 byte too long) data = append(data, encodeVarInt(0x42)...) // length, 1 byte data = append(data, []byte{0x12, 0x34}...) // packet number _, _, _, err := ParsePacket(data) Expect(err).To(MatchError(io.EOF)) }) It("errors if the 5th or 6th bit are set", func() { data := []byte{0xc0 | 0x2<<4 | 0x8 /* set the 5th bit */ | 0x1 /* 2 byte packet number */} data = appendVersion(data, protocol.Version1) data = append(data, []byte{0x0, 0x0}...) // connection ID lengths data = append(data, encodeVarInt(2)...) // length data = append(data, []byte{0x12, 0x34}...) // packet number hdr, _, _, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeHandshake)) extHdr, err := hdr.ParseExtended(data) Expect(err).To(MatchError(ErrInvalidReservedBits)) Expect(extHdr).ToNot(BeNil()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x1234))) }) It("errors on EOF, when parsing the header", func() { data := []byte{0xc0 ^ 0x2<<4} data = appendVersion(data, protocol.Version1) data = append(data, 0x8) // dest conn ID len data = append(data, []byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}...) // dest conn ID data = append(data, 0x8) // src conn ID len data = append(data, []byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}...) // src conn ID for i := 1; i < len(data); i++ { _, _, _, err := ParsePacket(data[:i]) Expect(err).To(Equal(io.EOF)) } }) It("errors on EOF, when parsing the extended header", func() { data := []byte{0xc0 | 0x2<<4 | 0x3} data = appendVersion(data, protocol.Version1) data = append(data, []byte{0x0, 0x0}...) // connection ID lengths data = append(data, encodeVarInt(0)...) // length hdrLen := len(data) data = append(data, []byte{0xde, 0xad, 0xbe, 0xef}...) // packet number for i := hdrLen; i < len(data); i++ { b := data[:i] hdr, _, _, err := ParsePacket(b) Expect(err).ToNot(HaveOccurred()) _, err = hdr.ParseExtended(b) Expect(err).To(Equal(io.EOF)) } }) It("errors on EOF, for a Retry packet", func() { data := []byte{0xc0 ^ 0x3<<4} data = appendVersion(data, protocol.Version1) data = append(data, []byte{0x0, 0x0}...) // connection ID lengths data = append(data, 0xa) // Orig Destination Connection ID length data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // source connection ID hdrLen := len(data) for i := hdrLen; i < len(data); i++ { data = data[:i] hdr, _, _, err := ParsePacket(data) Expect(err).ToNot(HaveOccurred()) _, err = hdr.ParseExtended(data) Expect(err).To(Equal(io.EOF)) } }) Context("coalesced packets", func() { It("cuts packets", func() { hdr := Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), Length: 2 + 6, Version: protocol.Version1, } b, err := (&ExtendedHeader{ Header: hdr, PacketNumber: 0x1337, PacketNumberLen: 2, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) hdrRaw := append([]byte{}, b...) b = append(b, []byte("foobar")...) // payload of the first packet b = append(b, []byte("raboof")...) // second packet parsedHdr, data, rest, err := ParsePacket(b) Expect(err).ToNot(HaveOccurred()) Expect(parsedHdr.Type).To(Equal(hdr.Type)) Expect(parsedHdr.DestConnectionID).To(Equal(hdr.DestConnectionID)) Expect(data).To(Equal(append(hdrRaw, []byte("foobar")...))) Expect(rest).To(Equal([]byte("raboof"))) }) It("errors on packets that are smaller than the length in the packet header, for too small packet number", func() { b, err := (&ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), Length: 3, Version: protocol.Version1, }, PacketNumber: 0x1337, PacketNumberLen: 2, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) _, _, _, err = ParsePacket(b) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("packet length (2 bytes) is smaller than the expected length (3 bytes)")) }) It("errors on packets that are smaller than the length in the packet header, for too small payload", func() { b, err := (&ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), Length: 1000, Version: protocol.Version1, }, PacketNumber: 0x1337, PacketNumberLen: 2, }).Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) b = append(b, make([]byte, 500-2 /* for packet number length */)...) _, _, _, err = ParsePacket(b) Expect(err).To(MatchError("packet length (500 bytes) is smaller than the expected length (1000 bytes)")) }) }) }) It("distinguishes long and short header packets", func() { Expect(IsLongHeaderPacket(0x40)).To(BeFalse()) Expect(IsLongHeaderPacket(0x80 ^ 0x40 ^ 0x12)).To(BeTrue()) }) It("tells its packet type for logging", func() { Expect((&Header{Type: protocol.PacketTypeInitial}).PacketType()).To(Equal("Initial")) Expect((&Header{Type: protocol.PacketTypeHandshake}).PacketType()).To(Equal("Handshake")) }) }) func BenchmarkIs0RTTPacket(b *testing.B) { random := mrand.New(mrand.NewSource(time.Now().UnixNano())) packets := make([][]byte, 1024) for i := 0; i < len(packets); i++ { packets[i] = make([]byte, random.Intn(256)) random.Read(packets[i]) } b.ResetTimer() for i := 0; i < b.N; i++ { Is0RTTPacket(packets[i%len(packets)]) } } func BenchmarkParseInitial(b *testing.B) { b.Run("without token", func(b *testing.B) { benchmarkInitialPacketParsing(b, nil) }) b.Run("with token", func(b *testing.B) { token := make([]byte, 32) rand.Read(token) benchmarkInitialPacketParsing(b, token) }) } func benchmarkInitialPacketParsing(b *testing.B, token []byte) { hdr := Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), SrcConnectionID: protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}), Length: 1000, Token: token, Version: protocol.Version1, } data, err := (&ExtendedHeader{ Header: hdr, PacketNumber: 0x1337, PacketNumberLen: 4, }).Append(nil, protocol.Version1) if err != nil { b.Fatal(err) } data = append(data, make([]byte, 1000)...) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { h, _, _, err := ParsePacket(data) if err != nil { b.Fatal(err) } if h.Type != hdr.Type || h.DestConnectionID != hdr.DestConnectionID || h.SrcConnectionID != hdr.SrcConnectionID || !bytes.Equal(h.Token, hdr.Token) { b.Fatalf("headers don't match: %v vs %v", h, hdr) } } } func BenchmarkParseRetry(b *testing.B) { token := make([]byte, 64) rand.Read(token) hdr := &ExtendedHeader{ Header: Header{ Type: protocol.PacketTypeRetry, SrcConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}), DestConnectionID: protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}), Token: token, Version: protocol.Version1, }, } data, err := hdr.Append(nil, hdr.Version) if err != nil { b.Fatal(err) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { h, _, _, err := ParsePacket(data) if err != nil { b.Fatal(err) } if h.Type != hdr.Type || h.DestConnectionID != hdr.DestConnectionID || h.SrcConnectionID != hdr.SrcConnectionID || !bytes.Equal(h.Token, hdr.Token[:len(hdr.Token)-16]) { b.Fatalf("headers don't match: %#v vs %#v", h, hdr) } } } func BenchmarkArbitraryHeaderParsing(b *testing.B) { b.Run("dest 8/ src 10", func(b *testing.B) { benchmarkArbitraryHeaderParsing(b, 8, 10) }) b.Run("dest 20 / src 20", func(b *testing.B) { benchmarkArbitraryHeaderParsing(b, 20, 20) }) b.Run("dest 100 / src 150", func(b *testing.B) { benchmarkArbitraryHeaderParsing(b, 100, 150) }) } func benchmarkArbitraryHeaderParsing(b *testing.B, destLen, srcLen int) { destConnID := make([]byte, destLen) rand.Read(destConnID) srcConnID := make([]byte, srcLen) rand.Read(srcConnID) buf := []byte{0x80, 1, 2, 3, 4} buf = append(buf, uint8(destLen)) buf = append(buf, destConnID...) buf = append(buf, uint8(srcLen)) buf = append(buf, srcConnID...) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { parsed, d, s, err := ParseArbitraryLenConnectionIDs(buf) if err != nil { b.Fatal(err) } if parsed != len(buf) { b.Fatal("expected to parse entire slice") } if !bytes.Equal(destConnID, d.Bytes()) { b.Fatalf("destination connection IDs don't match: %v vs %v", destConnID, d.Bytes()) } if !bytes.Equal(srcConnID, s.Bytes()) { b.Fatalf("source connection IDs don't match: %v vs %v", srcConnID, s.Bytes()) } } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/interface.go000066400000000000000000000003471465664453100256640ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" ) // A Frame in QUIC type Frame interface { Append(b []byte, version protocol.Version) ([]byte, error) Length(version protocol.Version) protocol.ByteCount } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/log.go000066400000000000000000000064151465664453100245070ustar00rootroot00000000000000package wire import ( "fmt" "strings" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) // LogFrame logs a frame, either sent or received func LogFrame(logger utils.Logger, frame Frame, sent bool) { if !logger.Debug() { return } dir := "<-" if sent { dir = "->" } switch f := frame.(type) { case *CryptoFrame: dataLen := protocol.ByteCount(len(f.Data)) logger.Debugf("\t%s &wire.CryptoFrame{Offset: %d, Data length: %d, Offset + Data length: %d}", dir, f.Offset, dataLen, f.Offset+dataLen) case *StreamFrame: logger.Debugf("\t%s &wire.StreamFrame{StreamID: %d, Fin: %t, Offset: %d, Data length: %d, Offset + Data length: %d}", dir, f.StreamID, f.Fin, f.Offset, f.DataLen(), f.Offset+f.DataLen()) case *ResetStreamFrame: logger.Debugf("\t%s &wire.ResetStreamFrame{StreamID: %d, ErrorCode: %#x, FinalSize: %d}", dir, f.StreamID, f.ErrorCode, f.FinalSize) case *AckFrame: hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 var ecn string if hasECN { ecn = fmt.Sprintf(", ECT0: %d, ECT1: %d, CE: %d", f.ECT0, f.ECT1, f.ECNCE) } if len(f.AckRanges) > 1 { ackRanges := make([]string, len(f.AckRanges)) for i, r := range f.AckRanges { ackRanges[i] = fmt.Sprintf("{Largest: %d, Smallest: %d}", r.Largest, r.Smallest) } logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %d, LowestAcked: %d, AckRanges: {%s}, DelayTime: %s%s}", dir, f.LargestAcked(), f.LowestAcked(), strings.Join(ackRanges, ", "), f.DelayTime.String(), ecn) } else { logger.Debugf("\t%s &wire.AckFrame{LargestAcked: %d, LowestAcked: %d, DelayTime: %s%s}", dir, f.LargestAcked(), f.LowestAcked(), f.DelayTime.String(), ecn) } case *MaxDataFrame: logger.Debugf("\t%s &wire.MaxDataFrame{MaximumData: %d}", dir, f.MaximumData) case *MaxStreamDataFrame: logger.Debugf("\t%s &wire.MaxStreamDataFrame{StreamID: %d, MaximumStreamData: %d}", dir, f.StreamID, f.MaximumStreamData) case *DataBlockedFrame: logger.Debugf("\t%s &wire.DataBlockedFrame{MaximumData: %d}", dir, f.MaximumData) case *StreamDataBlockedFrame: logger.Debugf("\t%s &wire.StreamDataBlockedFrame{StreamID: %d, MaximumStreamData: %d}", dir, f.StreamID, f.MaximumStreamData) case *MaxStreamsFrame: switch f.Type { case protocol.StreamTypeUni: logger.Debugf("\t%s &wire.MaxStreamsFrame{Type: uni, MaxStreamNum: %d}", dir, f.MaxStreamNum) case protocol.StreamTypeBidi: logger.Debugf("\t%s &wire.MaxStreamsFrame{Type: bidi, MaxStreamNum: %d}", dir, f.MaxStreamNum) } case *StreamsBlockedFrame: switch f.Type { case protocol.StreamTypeUni: logger.Debugf("\t%s &wire.StreamsBlockedFrame{Type: uni, MaxStreams: %d}", dir, f.StreamLimit) case protocol.StreamTypeBidi: logger.Debugf("\t%s &wire.StreamsBlockedFrame{Type: bidi, MaxStreams: %d}", dir, f.StreamLimit) } case *NewConnectionIDFrame: logger.Debugf("\t%s &wire.NewConnectionIDFrame{SequenceNumber: %d, RetirePriorTo: %d, ConnectionID: %s, StatelessResetToken: %#x}", dir, f.SequenceNumber, f.RetirePriorTo, f.ConnectionID, f.StatelessResetToken) case *RetireConnectionIDFrame: logger.Debugf("\t%s &wire.RetireConnectionIDFrame{SequenceNumber: %d}", dir, f.SequenceNumber) case *NewTokenFrame: logger.Debugf("\t%s &wire.NewTokenFrame{Token: %#x}", dir, f.Token) default: logger.Debugf("\t%s %#v", dir, frame) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/log_test.go000066400000000000000000000126121465664453100255420ustar00rootroot00000000000000package wire import ( "bytes" "log" "os" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Frame logging", func() { var ( buf *bytes.Buffer logger utils.Logger ) BeforeEach(func() { buf = &bytes.Buffer{} logger = utils.DefaultLogger logger.SetLogLevel(utils.LogLevelDebug) log.SetOutput(buf) }) AfterEach(func() { log.SetOutput(os.Stdout) }) It("doesn't log when debug is disabled", func() { logger.SetLogLevel(utils.LogLevelInfo) LogFrame(logger, &ResetStreamFrame{}, true) Expect(buf.Len()).To(BeZero()) }) It("logs sent frames", func() { LogFrame(logger, &ResetStreamFrame{}, true) Expect(buf.String()).To(ContainSubstring("\t-> &wire.ResetStreamFrame{StreamID: 0, ErrorCode: 0x0, FinalSize: 0}\n")) }) It("logs received frames", func() { LogFrame(logger, &ResetStreamFrame{}, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.ResetStreamFrame{StreamID: 0, ErrorCode: 0x0, FinalSize: 0}\n")) }) It("logs CRYPTO frames", func() { frame := &CryptoFrame{ Offset: 42, Data: make([]byte, 123), } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.CryptoFrame{Offset: 42, Data length: 123, Offset + Data length: 165}\n")) }) It("logs STREAM frames", func() { frame := &StreamFrame{ StreamID: 42, Offset: 1337, Data: bytes.Repeat([]byte{'f'}, 100), } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.StreamFrame{StreamID: 42, Fin: false, Offset: 1337, Data length: 100, Offset + Data length: 1437}\n")) }) It("logs ACK frames without missing packets", func() { frame := &AckFrame{ AckRanges: []AckRange{{Smallest: 42, Largest: 1337}}, DelayTime: 1 * time.Millisecond, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.AckFrame{LargestAcked: 1337, LowestAcked: 42, DelayTime: 1ms}\n")) }) It("logs ACK frames with ECN", func() { frame := &AckFrame{ AckRanges: []AckRange{{Smallest: 42, Largest: 1337}}, DelayTime: 1 * time.Millisecond, ECT0: 5, ECT1: 66, ECNCE: 777, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.AckFrame{LargestAcked: 1337, LowestAcked: 42, DelayTime: 1ms, ECT0: 5, ECT1: 66, CE: 777}\n")) }) It("logs ACK frames with missing packets", func() { frame := &AckFrame{ AckRanges: []AckRange{ {Smallest: 5, Largest: 8}, {Smallest: 2, Largest: 3}, }, DelayTime: 12 * time.Millisecond, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.AckFrame{LargestAcked: 8, LowestAcked: 2, AckRanges: {{Largest: 8, Smallest: 5}, {Largest: 3, Smallest: 2}}, DelayTime: 12ms}\n")) }) It("logs MAX_STREAMS frames", func() { frame := &MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: 42, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.MaxStreamsFrame{Type: bidi, MaxStreamNum: 42}\n")) }) It("logs MAX_DATA frames", func() { frame := &MaxDataFrame{ MaximumData: 42, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.MaxDataFrame{MaximumData: 42}\n")) }) It("logs MAX_STREAM_DATA frames", func() { frame := &MaxStreamDataFrame{ StreamID: 10, MaximumStreamData: 42, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.MaxStreamDataFrame{StreamID: 10, MaximumStreamData: 42}\n")) }) It("logs DATA_BLOCKED frames", func() { frame := &DataBlockedFrame{ MaximumData: 1000, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.DataBlockedFrame{MaximumData: 1000}\n")) }) It("logs STREAM_DATA_BLOCKED frames", func() { frame := &StreamDataBlockedFrame{ StreamID: 42, MaximumStreamData: 1000, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.StreamDataBlockedFrame{StreamID: 42, MaximumStreamData: 1000}\n")) }) It("logs STREAMS_BLOCKED frames", func() { frame := &StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: 42, } LogFrame(logger, frame, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.StreamsBlockedFrame{Type: bidi, MaxStreams: 42}\n")) }) It("logs NEW_CONNECTION_ID frames", func() { LogFrame(logger, &NewConnectionIDFrame{ SequenceNumber: 42, RetirePriorTo: 24, ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), StatelessResetToken: protocol.StatelessResetToken{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10}, }, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.NewConnectionIDFrame{SequenceNumber: 42, RetirePriorTo: 24, ConnectionID: deadbeef, StatelessResetToken: 0x0102030405060708090a0b0c0d0e0f10}")) }) It("logs RETIRE_CONNECTION_ID frames", func() { LogFrame(logger, &RetireConnectionIDFrame{SequenceNumber: 42}, false) Expect(buf.String()).To(ContainSubstring("\t<- &wire.RetireConnectionIDFrame{SequenceNumber: 42}")) }) It("logs NEW_TOKEN frames", func() { LogFrame(logger, &NewTokenFrame{ Token: []byte{0xde, 0xad, 0xbe, 0xef}, }, true) Expect(buf.String()).To(ContainSubstring("\t-> &wire.NewTokenFrame{Token: 0xdeadbeef")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/max_data_frame.go000066400000000000000000000016461465664453100266570ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A MaxDataFrame carries flow control information for the connection type MaxDataFrame struct { MaximumData protocol.ByteCount } // parseMaxDataFrame parses a MAX_DATA frame func parseMaxDataFrame(b []byte, _ protocol.Version) (*MaxDataFrame, int, error) { frame := &MaxDataFrame{} byteOffset, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } frame.MaximumData = protocol.ByteCount(byteOffset) return frame, l, nil } func (f *MaxDataFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, maxDataFrameType) b = quicvarint.Append(b, uint64(f.MaximumData)) return b, nil } // Length of a written frame func (f *MaxDataFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.MaximumData))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/max_data_frame_test.go000066400000000000000000000027161465664453100277150ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("MAX_DATA frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { data := encodeVarInt(0xdecafbad123456) // byte offset frame, l, err := parseMaxDataFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.MaximumData).To(Equal(protocol.ByteCount(0xdecafbad123456))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdecafbad1234567) // byte offset _, l, err := parseMaxDataFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseMaxDataFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("writing", func() { It("has proper length", func() { f := &MaxDataFrame{ MaximumData: 0xdeadbeef, } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0xdeadbeef))) }) It("writes a MAX_DATA frame", func() { f := &MaxDataFrame{ MaximumData: 0xdeadbeefcafe, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{maxDataFrameType} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) Expect(b).To(Equal(expected)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/max_stream_data_frame.go000066400000000000000000000023141465664453100302230ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A MaxStreamDataFrame is a MAX_STREAM_DATA frame type MaxStreamDataFrame struct { StreamID protocol.StreamID MaximumStreamData protocol.ByteCount } func parseMaxStreamDataFrame(b []byte, _ protocol.Version) (*MaxStreamDataFrame, int, error) { startLen := len(b) sid, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] offset, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] return &MaxStreamDataFrame{ StreamID: protocol.StreamID(sid), MaximumStreamData: protocol.ByteCount(offset), }, startLen - len(b), nil } func (f *MaxStreamDataFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, maxStreamDataFrameType) b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.MaximumStreamData)) return b, nil } // Length of a written frame func (f *MaxStreamDataFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamID))+quicvarint.Len(uint64(f.MaximumStreamData))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/max_stream_data_frame_test.go000066400000000000000000000036041465664453100312650ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("MAX_STREAM_DATA frame", func() { Context("parsing", func() { It("accepts sample frame", func() { data := encodeVarInt(0xdeadbeef) // Stream ID data = append(data, encodeVarInt(0x12345678)...) // Offset frame, l, err := parseMaxStreamDataFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(frame.MaximumStreamData).To(Equal(protocol.ByteCount(0x12345678))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdeadbeef) // Stream ID data = append(data, encodeVarInt(0x12345678)...) // Offset _, l, err := parseMaxStreamDataFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseMaxStreamDataFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("writing", func() { It("has proper length", func() { f := &MaxStreamDataFrame{ StreamID: 0x1337, MaximumStreamData: 0xdeadbeef, } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData)))) }) It("writes a sample frame", func() { f := &MaxStreamDataFrame{ StreamID: 0xdecafbad, MaximumStreamData: 0xdeadbeefcafe42, } expected := []byte{maxStreamDataFrameType} expected = append(expected, encodeVarInt(0xdecafbad)...) expected = append(expected, encodeVarInt(0xdeadbeefcafe42)...) b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal(expected)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/max_streams_frame.go000066400000000000000000000024621465664453100274210ustar00rootroot00000000000000package wire import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A MaxStreamsFrame is a MAX_STREAMS frame type MaxStreamsFrame struct { Type protocol.StreamType MaxStreamNum protocol.StreamNum } func parseMaxStreamsFrame(b []byte, typ uint64, _ protocol.Version) (*MaxStreamsFrame, int, error) { f := &MaxStreamsFrame{} switch typ { case bidiMaxStreamsFrameType: f.Type = protocol.StreamTypeBidi case uniMaxStreamsFrameType: f.Type = protocol.StreamTypeUni } streamID, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } f.MaxStreamNum = protocol.StreamNum(streamID) if f.MaxStreamNum > protocol.MaxStreamCount { return nil, 0, fmt.Errorf("%d exceeds the maximum stream count", f.MaxStreamNum) } return f, l, nil } func (f *MaxStreamsFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { switch f.Type { case protocol.StreamTypeBidi: b = append(b, bidiMaxStreamsFrameType) case protocol.StreamTypeUni: b = append(b, uniMaxStreamsFrameType) } b = quicvarint.Append(b, uint64(f.MaxStreamNum)) return b, nil } // Length of a written frame func (f *MaxStreamsFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.MaxStreamNum))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/max_streams_frame_test.go000066400000000000000000000066521465664453100304650ustar00rootroot00000000000000package wire import ( "fmt" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("MAX_STREAMS frame", func() { Context("parsing", func() { It("accepts a frame for a bidirectional stream", func() { data := encodeVarInt(0xdecaf) f, l, err := parseMaxStreamsFrame(data, bidiMaxStreamsFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeBidi)) Expect(f.MaxStreamNum).To(BeEquivalentTo(0xdecaf)) Expect(l).To(Equal(len(data))) }) It("accepts a frame for a bidirectional stream", func() { data := encodeVarInt(0xdecaf) f, l, err := parseMaxStreamsFrame(data, uniMaxStreamsFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeUni)) Expect(f.MaxStreamNum).To(BeEquivalentTo(0xdecaf)) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { const typ = 0x1d data := encodeVarInt(0xdeadbeefcafe13) _, l, err := parseMaxStreamsFrame(data, typ, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseMaxStreamsFrame(data[:i], typ, protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) for _, t := range []protocol.StreamType{protocol.StreamTypeUni, protocol.StreamTypeBidi} { streamType := t It("accepts a frame containing the maximum stream count", func() { f := &MaxStreamsFrame{ Type: streamType, MaxStreamNum: protocol.MaxStreamCount, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] frame, _, err := parseMaxStreamsFrame(b, typ, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("errors when receiving a too large stream count", func() { f := &MaxStreamsFrame{ Type: streamType, MaxStreamNum: protocol.MaxStreamCount + 1, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] _, _, err = parseMaxStreamsFrame(b, typ, protocol.Version1) Expect(err).To(MatchError(fmt.Sprintf("%d exceeds the maximum stream count", protocol.MaxStreamCount+1))) }) } }) Context("writing", func() { It("for a bidirectional stream", func() { f := &MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: 0xdeadbeef, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{bidiMaxStreamsFrameType} expected = append(expected, encodeVarInt(0xdeadbeef)...) Expect(b).To(Equal(expected)) }) It("for a unidirectional stream", func() { f := &MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: 0xdecafbad, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{uniMaxStreamsFrameType} expected = append(expected, encodeVarInt(0xdecafbad)...) Expect(b).To(Equal(expected)) }) It("has the correct length", func() { frame := MaxStreamsFrame{MaxStreamNum: 0x1337} Expect(frame.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0x1337))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/new_connection_id_frame.go000066400000000000000000000043601465664453100305610ustar00rootroot00000000000000package wire import ( "errors" "fmt" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A NewConnectionIDFrame is a NEW_CONNECTION_ID frame type NewConnectionIDFrame struct { SequenceNumber uint64 RetirePriorTo uint64 ConnectionID protocol.ConnectionID StatelessResetToken protocol.StatelessResetToken } func parseNewConnectionIDFrame(b []byte, _ protocol.Version) (*NewConnectionIDFrame, int, error) { startLen := len(b) seq, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] ret, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] if ret > seq { //nolint:stylecheck return nil, 0, fmt.Errorf("Retire Prior To value (%d) larger than Sequence Number (%d)", ret, seq) } if len(b) == 0 { return nil, 0, io.EOF } connIDLen := int(b[0]) b = b[1:] if connIDLen == 0 { return nil, 0, errors.New("invalid zero-length connection ID") } if connIDLen > protocol.MaxConnIDLen { return nil, 0, protocol.ErrInvalidConnectionIDLen } if len(b) < connIDLen { return nil, 0, io.EOF } frame := &NewConnectionIDFrame{ SequenceNumber: seq, RetirePriorTo: ret, ConnectionID: protocol.ParseConnectionID(b[:connIDLen]), } b = b[connIDLen:] if len(b) < len(frame.StatelessResetToken) { return nil, 0, io.EOF } copy(frame.StatelessResetToken[:], b) return frame, startLen - len(b) + len(frame.StatelessResetToken), nil } func (f *NewConnectionIDFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, newConnectionIDFrameType) b = quicvarint.Append(b, f.SequenceNumber) b = quicvarint.Append(b, f.RetirePriorTo) connIDLen := f.ConnectionID.Len() if connIDLen > protocol.MaxConnIDLen { return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen) } b = append(b, uint8(connIDLen)) b = append(b, f.ConnectionID.Bytes()...) b = append(b, f.StatelessResetToken[:]...) return b, nil } // Length of a written frame func (f *NewConnectionIDFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(f.SequenceNumber)+quicvarint.Len(f.RetirePriorTo)+1 /* connection ID length */ +f.ConnectionID.Len()) + 16 } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/new_connection_id_frame_test.go000066400000000000000000000116771465664453100316310ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("NEW_CONNECTION_ID frame", func() { Context("when parsing", func() { It("accepts a sample frame", func() { data := encodeVarInt(0xdeadbeef) // sequence number data = append(data, encodeVarInt(0xcafe)...) // retire prior to data = append(data, 10) // connection ID length data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // connection ID data = append(data, []byte("deadbeefdecafbad")...) // stateless reset token frame, l, err := parseNewConnectionIDFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.SequenceNumber).To(Equal(uint64(0xdeadbeef))) Expect(frame.RetirePriorTo).To(Equal(uint64(0xcafe))) Expect(frame.ConnectionID).To(Equal(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))) Expect(string(frame.StatelessResetToken[:])).To(Equal("deadbeefdecafbad")) Expect(l).To(Equal(len(data))) }) It("errors when the Retire Prior To value is larger than the Sequence Number", func() { data := encodeVarInt(1000) // sequence number data = append(data, encodeVarInt(1001)...) // retire prior to data = append(data, 3) data = append(data, []byte{1, 2, 3}...) data = append(data, []byte("deadbeefdecafbad")...) // stateless reset token _, _, err := parseNewConnectionIDFrame(data, protocol.Version1) Expect(err).To(MatchError("Retire Prior To value (1001) larger than Sequence Number (1000)")) }) It("errors when the connection ID has a zero-length connection ID", func() { data := encodeVarInt(42) // sequence number data = append(data, encodeVarInt(12)...) // retire prior to data = append(data, 0) // connection ID length _, _, err := parseNewConnectionIDFrame(data, protocol.Version1) Expect(err).To(MatchError("invalid zero-length connection ID")) }) It("errors when the connection ID has an invalid length (too long)", func() { data := encodeVarInt(0xdeadbeef) // sequence number data = append(data, encodeVarInt(0xcafe)...) // retire prior to data = append(data, 21) // connection ID length data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}...) // connection ID data = append(data, []byte("deadbeefdecafbad")...) // stateless reset token _, _, err := parseNewConnectionIDFrame(data, protocol.Version1) Expect(err).To(MatchError(protocol.ErrInvalidConnectionIDLen)) }) It("errors on EOFs", func() { data := encodeVarInt(0xdeadbeef) // sequence number data = append(data, encodeVarInt(0xcafe1234)...) // retire prior to data = append(data, 10) // connection ID length data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // connection ID data = append(data, []byte("deadbeefdecafbad")...) // stateless reset token _, l, err := parseNewConnectionIDFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseNewConnectionIDFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { token := protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} frame := &NewConnectionIDFrame{ SequenceNumber: 0x1337, RetirePriorTo: 0x42, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6}), StatelessResetToken: token, } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{newConnectionIDFrameType} expected = append(expected, encodeVarInt(0x1337)...) expected = append(expected, encodeVarInt(0x42)...) expected = append(expected, 6) expected = append(expected, []byte{1, 2, 3, 4, 5, 6}...) expected = append(expected, token[:]...) Expect(b).To(Equal(expected)) }) It("has the correct length", func() { token := protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} frame := &NewConnectionIDFrame{ SequenceNumber: 0xdecafbad, RetirePriorTo: 0xdeadbeefcafe, ConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), StatelessResetToken: token, } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(frame.Length(protocol.Version1)))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/new_token_frame.go000066400000000000000000000020451465664453100270640ustar00rootroot00000000000000package wire import ( "errors" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A NewTokenFrame is a NEW_TOKEN frame type NewTokenFrame struct { Token []byte } func parseNewTokenFrame(b []byte, _ protocol.Version) (*NewTokenFrame, int, error) { tokenLen, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] if tokenLen == 0 { return nil, 0, errors.New("token must not be empty") } if uint64(len(b)) < tokenLen { return nil, 0, io.EOF } token := make([]byte, int(tokenLen)) copy(token, b) return &NewTokenFrame{Token: token}, l + int(tokenLen), nil } func (f *NewTokenFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, newTokenFrameType) b = quicvarint.Append(b, uint64(len(f.Token))) b = append(b, f.Token...) return b, nil } // Length of a written frame func (f *NewTokenFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(len(f.Token)))+len(f.Token)) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/new_token_frame_test.go000066400000000000000000000041621465664453100301250ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("NEW_TOKEN frame", func() { Context("parsing", func() { It("accepts a sample frame", func() { token := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." data := encodeVarInt(uint64(len(token))) data = append(data, token...) f, l, err := parseNewTokenFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(string(f.Token)).To(Equal(token)) Expect(l).To(Equal(len(data))) }) It("rejects empty tokens", func() { data := encodeVarInt(0) _, _, err := parseNewTokenFrame(data, protocol.Version1) Expect(err).To(MatchError("token must not be empty")) }) It("errors on EOFs", func() { token := "Lorem ipsum dolor sit amet, consectetur adipiscing elit" data := encodeVarInt(uint64(len(token))) data = append(data, token...) _, l, err := parseNewTokenFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseNewTokenFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("writing", func() { It("writes a sample frame", func() { token := "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." f := &NewTokenFrame{Token: []byte(token)} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{newTokenFrameType} expected = append(expected, encodeVarInt(uint64(len(token)))...) expected = append(expected, token...) Expect(b).To(Equal(expected)) }) It("has the correct min length", func() { frame := &NewTokenFrame{Token: []byte("foobar")} Expect(frame.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(6) + 6)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/path_challenge_frame.go000066400000000000000000000012511465664453100300270ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" ) // A PathChallengeFrame is a PATH_CHALLENGE frame type PathChallengeFrame struct { Data [8]byte } func parsePathChallengeFrame(b []byte, _ protocol.Version) (*PathChallengeFrame, int, error) { f := &PathChallengeFrame{} if len(b) < 8 { return nil, 0, io.EOF } copy(f.Data[:], b) return f, 8, nil } func (f *PathChallengeFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, pathChallengeFrameType) b = append(b, f.Data[:]...) return b, nil } // Length of a written frame func (f *PathChallengeFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 + 8 } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/path_challenge_frame_test.go000066400000000000000000000025461465664453100310760ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("PATH_CHALLENGE frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { b := []byte{1, 2, 3, 4, 5, 6, 7, 8} f, l, err := parsePathChallengeFrame(b, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(l).To(Equal(len(b))) }) It("errors on EOFs", func() { data := []byte{1, 2, 3, 4, 5, 6, 7, 8} _, l, err := parsePathChallengeFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parsePathChallengeFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := PathChallengeFrame{Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte{pathChallengeFrameType, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37})) }) It("has the correct length", func() { frame := PathChallengeFrame{} Expect(frame.Length(protocol.Version1)).To(Equal(protocol.ByteCount(9))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/path_response_frame.go000066400000000000000000000012401465664453100277410ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" ) // A PathResponseFrame is a PATH_RESPONSE frame type PathResponseFrame struct { Data [8]byte } func parsePathResponseFrame(b []byte, _ protocol.Version) (*PathResponseFrame, int, error) { f := &PathResponseFrame{} if len(b) < 8 { return nil, 0, io.EOF } copy(f.Data[:], b) return f, 8, nil } func (f *PathResponseFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, pathResponseFrameType) b = append(b, f.Data[:]...) return b, nil } // Length of a written frame func (f *PathResponseFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 + 8 } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/path_response_frame_test.go000066400000000000000000000025371465664453100310120ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("PATH_RESPONSE frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { b := []byte{1, 2, 3, 4, 5, 6, 7, 8} f, l, err := parsePathResponseFrame(b, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) Expect(l).To(Equal(len(b))) }) It("errors on EOFs", func() { data := []byte{1, 2, 3, 4, 5, 6, 7, 8} _, l, err := parsePathResponseFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parsePathResponseFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := PathResponseFrame{Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte{pathResponseFrameType, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37})) }) It("has the correct length", func() { frame := PathResponseFrame{} Expect(frame.Length(protocol.Version1)).To(Equal(protocol.ByteCount(9))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/ping_frame.go000066400000000000000000000005431465664453100260310ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" ) // A PingFrame is a PING frame type PingFrame struct{} func (f *PingFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { return append(b, pingFrameType), nil } // Length of a written frame func (f *PingFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/ping_frame_test.go000066400000000000000000000010301465664453100270600ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("PING frame", func() { Context("when writing", func() { It("writes a sample frame", func() { frame := PingFrame{} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal([]byte{0x1})) }) It("has the correct length", func() { frame := PingFrame{} Expect(frame.Length(0)).To(Equal(protocol.ByteCount(1))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/pool.go000066400000000000000000000010651465664453100246730ustar00rootroot00000000000000package wire import ( "sync" "github.com/quic-go/quic-go/internal/protocol" ) var pool sync.Pool func init() { pool.New = func() interface{} { return &StreamFrame{ Data: make([]byte, 0, protocol.MaxPacketBufferSize), fromPool: true, } } } func GetStreamFrame() *StreamFrame { f := pool.Get().(*StreamFrame) return f } func putStreamFrame(f *StreamFrame) { if !f.fromPool { return } if protocol.ByteCount(cap(f.Data)) != protocol.MaxPacketBufferSize { panic("wire.PutStreamFrame called with packet of wrong size!") } pool.Put(f) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/pool_test.go000066400000000000000000000010341465664453100257260ustar00rootroot00000000000000package wire import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Pool", func() { It("gets and puts STREAM frames", func() { f := GetStreamFrame() putStreamFrame(f) }) It("panics when putting a STREAM frame with a wrong capacity", func() { f := GetStreamFrame() f.Data = []byte("foobar") Expect(func() { putStreamFrame(f) }).To(Panic()) }) It("accepts STREAM frames not from the buffer, but ignores them", func() { f := &StreamFrame{Data: []byte("foobar")} putStreamFrame(f) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/reset_stream_frame.go000066400000000000000000000030641465664453100275720ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" ) // A ResetStreamFrame is a RESET_STREAM frame in QUIC type ResetStreamFrame struct { StreamID protocol.StreamID ErrorCode qerr.StreamErrorCode FinalSize protocol.ByteCount } func parseResetStreamFrame(b []byte, _ protocol.Version) (*ResetStreamFrame, int, error) { startLen := len(b) var streamID protocol.StreamID var byteOffset protocol.ByteCount sid, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] streamID = protocol.StreamID(sid) errorCode, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] bo, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } byteOffset = protocol.ByteCount(bo) return &ResetStreamFrame{ StreamID: streamID, ErrorCode: qerr.StreamErrorCode(errorCode), FinalSize: byteOffset, }, startLen - len(b) + l, nil } func (f *ResetStreamFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, resetStreamFrameType) b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.ErrorCode)) b = quicvarint.Append(b, uint64(f.FinalSize)) return b, nil } // Length of a written frame func (f *ResetStreamFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamID))+quicvarint.Len(uint64(f.ErrorCode))+quicvarint.Len(uint64(f.FinalSize))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/reset_stream_frame_test.go000066400000000000000000000043021465664453100306250ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("RESET_STREAM frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { data := encodeVarInt(0xdeadbeef) // stream ID data = append(data, encodeVarInt(0x1337)...) // error code data = append(data, encodeVarInt(0x987654321)...) // byte offset frame, l, err := parseResetStreamFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(frame.FinalSize).To(Equal(protocol.ByteCount(0x987654321))) Expect(frame.ErrorCode).To(Equal(qerr.StreamErrorCode(0x1337))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdeadbeef) // stream ID data = append(data, encodeVarInt(0x1337)...) // error code data = append(data, encodeVarInt(0x987654321)...) // byte offset _, l, err := parseResetStreamFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseResetStreamFrame(data[:i], protocol.Version1) Expect(err).To(HaveOccurred()) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := ResetStreamFrame{ StreamID: 0x1337, FinalSize: 0x11223344decafbad, ErrorCode: 0xcafe, } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{resetStreamFrameType} expected = append(expected, encodeVarInt(0x1337)...) expected = append(expected, encodeVarInt(0xcafe)...) expected = append(expected, encodeVarInt(0x11223344decafbad)...) Expect(b).To(Equal(expected)) }) It("has the correct length", func() { rst := ResetStreamFrame{ StreamID: 0x1337, FinalSize: 0x1234567, ErrorCode: 0xde, } expectedLen := 1 + quicvarint.Len(0x1337) + quicvarint.Len(0x1234567) + 2 Expect(rst.Length(protocol.Version1)).To(BeEquivalentTo(expectedLen)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/retire_connection_id_frame.go000066400000000000000000000015601465664453100312610ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame type RetireConnectionIDFrame struct { SequenceNumber uint64 } func parseRetireConnectionIDFrame(b []byte, _ protocol.Version) (*RetireConnectionIDFrame, int, error) { seq, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } return &RetireConnectionIDFrame{SequenceNumber: seq}, l, nil } func (f *RetireConnectionIDFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, retireConnectionIDFrameType) b = quicvarint.Append(b, f.SequenceNumber) return b, nil } // Length of a written frame func (f *RetireConnectionIDFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(f.SequenceNumber)) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/retire_connection_id_frame_test.go000066400000000000000000000030201465664453100323110ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("NEW_CONNECTION_ID frame", func() { Context("when parsing", func() { It("accepts a sample frame", func() { data := encodeVarInt(0xdeadbeef) // sequence number frame, l, err := parseRetireConnectionIDFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.SequenceNumber).To(Equal(uint64(0xdeadbeef))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdeadbeef) // sequence number _, l, err := parseRetireConnectionIDFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseRetireConnectionIDFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := &RetireConnectionIDFrame{SequenceNumber: 0x1337} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{retireConnectionIDFrameType} expected = append(expected, encodeVarInt(0x1337)...) Expect(b).To(Equal(expected)) }) It("has the correct length", func() { frame := &RetireConnectionIDFrame{SequenceNumber: 0xdecafbad} b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(b).To(HaveLen(int(frame.Length(protocol.Version1)))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/short_header.go000066400000000000000000000037141465664453100263740ustar00rootroot00000000000000package wire import ( "errors" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) // ParseShortHeader parses a short header packet. // It must be called after header protection was removed. // Otherwise, the check for the reserved bits will (most likely) fail. func ParseShortHeader(data []byte, connIDLen int) (length int, _ protocol.PacketNumber, _ protocol.PacketNumberLen, _ protocol.KeyPhaseBit, _ error) { if len(data) == 0 { return 0, 0, 0, 0, io.EOF } if data[0]&0x80 > 0 { return 0, 0, 0, 0, errors.New("not a short header packet") } if data[0]&0x40 == 0 { return 0, 0, 0, 0, errors.New("not a QUIC packet") } pnLen := protocol.PacketNumberLen(data[0]&0b11) + 1 if len(data) < 1+int(pnLen)+connIDLen { return 0, 0, 0, 0, io.EOF } pos := 1 + connIDLen pn, err := readPacketNumber(data[pos:], pnLen) if err != nil { return 0, 0, 0, 0, err } kp := protocol.KeyPhaseZero if data[0]&0b100 > 0 { kp = protocol.KeyPhaseOne } if data[0]&0x18 != 0 { err = ErrInvalidReservedBits } return 1 + connIDLen + int(pnLen), pn, pnLen, kp, err } // AppendShortHeader writes a short header. func AppendShortHeader(b []byte, connID protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) ([]byte, error) { typeByte := 0x40 | uint8(pnLen-1) if kp == protocol.KeyPhaseOne { typeByte |= byte(1 << 2) } b = append(b, typeByte) b = append(b, connID.Bytes()...) return appendPacketNumber(b, pn, pnLen) } func ShortHeaderLen(dest protocol.ConnectionID, pnLen protocol.PacketNumberLen) protocol.ByteCount { return 1 + protocol.ByteCount(dest.Len()) + protocol.ByteCount(pnLen) } func LogShortHeader(logger utils.Logger, dest protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) { logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %d, PacketNumberLen: %d, KeyPhase: %s}", dest, pn, pnLen, kp) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/short_header_test.go000066400000000000000000000071431465664453100274330ustar00rootroot00000000000000package wire import ( "bytes" "io" "log" "os" "testing" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Short Header", func() { Context("Parsing", func() { It("parses", func() { data := []byte{ 0b01000110, 0xde, 0xad, 0xbe, 0xef, 0x13, 0x37, 0x99, } l, pn, pnLen, kp, err := ParseShortHeader(data, 4) Expect(err).ToNot(HaveOccurred()) Expect(l).To(Equal(len(data))) Expect(kp).To(Equal(protocol.KeyPhaseOne)) Expect(pn).To(Equal(protocol.PacketNumber(0x133799))) Expect(pnLen).To(Equal(protocol.PacketNumberLen3)) }) It("errors when the QUIC bit is not set", func() { data := []byte{ 0b00000101, 0xde, 0xad, 0xbe, 0xef, 0x13, 0x37, } _, _, _, _, err := ParseShortHeader(data, 4) Expect(err).To(MatchError("not a QUIC packet")) }) It("errors, but returns the header, when the reserved bits are set", func() { data := []byte{ 0b01010101, 0xde, 0xad, 0xbe, 0xef, 0x13, 0x37, } _, pn, _, _, err := ParseShortHeader(data, 4) Expect(err).To(MatchError(ErrInvalidReservedBits)) Expect(pn).To(Equal(protocol.PacketNumber(0x1337))) }) It("errors when passed a long header packet", func() { _, _, _, _, err := ParseShortHeader([]byte{0x80}, 4) Expect(err).To(MatchError("not a short header packet")) }) It("errors on EOF", func() { data := []byte{ 0b01000110, 0xde, 0xad, 0xbe, 0xef, 0x13, 0x37, 0x99, } _, _, _, _, err := ParseShortHeader(data, 4) Expect(err).ToNot(HaveOccurred()) for i := range data { _, _, _, _, err := ParseShortHeader(data[:i], 4) Expect(err).To(MatchError(io.EOF)) } }) }) It("determines the length", func() { Expect(ShortHeaderLen(protocol.ParseConnectionID([]byte{1, 2, 3, 4}), protocol.PacketNumberLen3)).To(BeEquivalentTo(8)) Expect(ShortHeaderLen(protocol.ParseConnectionID([]byte{}), protocol.PacketNumberLen1)).To(BeEquivalentTo(2)) }) Context("writing", func() { It("writes a short header packet", func() { connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) b, err := AppendShortHeader(nil, connID, 1337, 4, protocol.KeyPhaseOne) Expect(err).ToNot(HaveOccurred()) l, pn, pnLen, kp, err := ParseShortHeader(b, 4) Expect(err).ToNot(HaveOccurred()) Expect(pn).To(Equal(protocol.PacketNumber(1337))) Expect(pnLen).To(Equal(protocol.PacketNumberLen4)) Expect(kp).To(Equal(protocol.KeyPhaseOne)) Expect(l).To(Equal(len(b))) }) }) Context("logging", func() { var ( buf *bytes.Buffer logger utils.Logger ) BeforeEach(func() { buf = &bytes.Buffer{} logger = utils.DefaultLogger logger.SetLogLevel(utils.LogLevelDebug) log.SetOutput(buf) }) AfterEach(func() { log.SetOutput(os.Stdout) }) It("logs Short Headers containing a connection ID", func() { connID := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}) LogShortHeader(logger, connID, 1337, protocol.PacketNumberLen4, protocol.KeyPhaseOne) Expect(buf.String()).To(ContainSubstring("Short Header{DestConnectionID: deadbeefcafe1337, PacketNumber: 1337, PacketNumberLen: 4, KeyPhase: 1}")) }) }) }) func BenchmarkWriteShortHeader(b *testing.B) { b.ReportAllocs() buf := make([]byte, 100) connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6}) for i := 0; i < b.N; i++ { var err error buf, err = AppendShortHeader(buf, connID, 1337, protocol.PacketNumberLen4, protocol.KeyPhaseOne) if err != nil { b.Fatalf("failed to write short header: %s", err) } buf = buf[:0] } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/stop_sending_frame.go000066400000000000000000000024001465664453100275620ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" ) // A StopSendingFrame is a STOP_SENDING frame type StopSendingFrame struct { StreamID protocol.StreamID ErrorCode qerr.StreamErrorCode } // parseStopSendingFrame parses a STOP_SENDING frame func parseStopSendingFrame(b []byte, _ protocol.Version) (*StopSendingFrame, int, error) { startLen := len(b) streamID, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] errorCode, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] return &StopSendingFrame{ StreamID: protocol.StreamID(streamID), ErrorCode: qerr.StreamErrorCode(errorCode), }, startLen - len(b), nil } // Length of a written frame func (f *StopSendingFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamID))+quicvarint.Len(uint64(f.ErrorCode))) } func (f *StopSendingFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, stopSendingFrameType) b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.ErrorCode)) return b, nil } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/stop_sending_frame_test.go000066400000000000000000000035551465664453100306350ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("STOP_SENDING frame", func() { Context("when parsing", func() { It("parses a sample frame", func() { data := encodeVarInt(0xdecafbad) // stream ID data = append(data, encodeVarInt(0x1337)...) // error code frame, l, err := parseStopSendingFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdecafbad))) Expect(frame.ErrorCode).To(Equal(qerr.StreamErrorCode(0x1337))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdecafbad) // stream ID data = append(data, encodeVarInt(0x123456)...) // error code _, l, err := parseStopSendingFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseStopSendingFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes", func() { frame := &StopSendingFrame{ StreamID: 0xdeadbeefcafe, ErrorCode: 0xdecafbad, } b, err := frame.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{stopSendingFrameType} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) expected = append(expected, encodeVarInt(0xdecafbad)...) Expect(b).To(Equal(expected)) }) It("has the correct min length", func() { frame := &StopSendingFrame{ StreamID: 0xdeadbeef, ErrorCode: 0x1234567, } Expect(frame.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0xdeadbeef) + quicvarint.Len(0x1234567))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/stream_data_blocked_frame.go000066400000000000000000000023231465664453100310410ustar00rootroot00000000000000package wire import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame type StreamDataBlockedFrame struct { StreamID protocol.StreamID MaximumStreamData protocol.ByteCount } func parseStreamDataBlockedFrame(b []byte, _ protocol.Version) (*StreamDataBlockedFrame, int, error) { startLen := len(b) sid, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] offset, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } return &StreamDataBlockedFrame{ StreamID: protocol.StreamID(sid), MaximumStreamData: protocol.ByteCount(offset), }, startLen - len(b) + l, nil } func (f *StreamDataBlockedFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { b = append(b, 0x15) b = quicvarint.Append(b, uint64(f.StreamID)) b = quicvarint.Append(b, uint64(f.MaximumStreamData)) return b, nil } // Length of a written frame func (f *StreamDataBlockedFrame) Length(protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamID))+quicvarint.Len(uint64(f.MaximumStreamData))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/stream_data_blocked_frame_test.go000066400000000000000000000035301465664453100321010ustar00rootroot00000000000000package wire import ( "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("STREAM_DATA_BLOCKED frame", func() { Context("parsing", func() { It("accepts sample frame", func() { data := encodeVarInt(0xdeadbeef) // stream ID data = append(data, encodeVarInt(0xdecafbad)...) // offset frame, l, err := parseStreamDataBlockedFrame(data, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(frame.MaximumStreamData).To(Equal(protocol.ByteCount(0xdecafbad))) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0xdeadbeef) data = append(data, encodeVarInt(0xc0010ff)...) _, l, err := parseStreamDataBlockedFrame(data, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseStreamDataBlockedFrame(data[:i], protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) }) Context("writing", func() { It("has proper min length", func() { f := &StreamDataBlockedFrame{ StreamID: 0x1337, MaximumStreamData: 0xdeadbeef, } Expect(f.Length(0)).To(BeEquivalentTo(1 + quicvarint.Len(0x1337) + quicvarint.Len(0xdeadbeef))) }) It("writes a sample frame", func() { f := &StreamDataBlockedFrame{ StreamID: 0xdecafbad, MaximumStreamData: 0x1337, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{streamDataBlockedFrameType} expected = append(expected, encodeVarInt(uint64(f.StreamID))...) expected = append(expected, encodeVarInt(uint64(f.MaximumStreamData))...) Expect(b).To(Equal(expected)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/stream_frame.go000066400000000000000000000114001465664453100263610ustar00rootroot00000000000000package wire import ( "errors" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A StreamFrame of QUIC type StreamFrame struct { StreamID protocol.StreamID Offset protocol.ByteCount Data []byte Fin bool DataLenPresent bool fromPool bool } func parseStreamFrame(b []byte, typ uint64, _ protocol.Version) (*StreamFrame, int, error) { startLen := len(b) hasOffset := typ&0b100 > 0 fin := typ&0b1 > 0 hasDataLen := typ&0b10 > 0 streamID, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] var offset uint64 if hasOffset { offset, l, err = quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] } var dataLen uint64 if hasDataLen { var err error var l int dataLen, l, err = quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } b = b[l:] if dataLen > uint64(len(b)) { return nil, 0, io.EOF } } else { // The rest of the packet is data dataLen = uint64(len(b)) } var frame *StreamFrame if dataLen < protocol.MinStreamFrameBufferSize { frame = &StreamFrame{Data: make([]byte, dataLen)} } else { frame = GetStreamFrame() // The STREAM frame can't be larger than the StreamFrame we obtained from the buffer, // since those StreamFrames have a buffer length of the maximum packet size. if dataLen > uint64(cap(frame.Data)) { return nil, 0, io.EOF } frame.Data = frame.Data[:dataLen] } frame.StreamID = protocol.StreamID(streamID) frame.Offset = protocol.ByteCount(offset) frame.Fin = fin frame.DataLenPresent = hasDataLen if dataLen != 0 { copy(frame.Data, b) } if frame.Offset+frame.DataLen() > protocol.MaxByteCount { return nil, 0, errors.New("stream data overflows maximum offset") } return frame, startLen - len(b) + int(dataLen), nil } func (f *StreamFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { if len(f.Data) == 0 && !f.Fin { return nil, errors.New("StreamFrame: attempting to write empty frame without FIN") } typ := byte(0x8) if f.Fin { typ ^= 0b1 } hasOffset := f.Offset != 0 if f.DataLenPresent { typ ^= 0b10 } if hasOffset { typ ^= 0b100 } b = append(b, typ) b = quicvarint.Append(b, uint64(f.StreamID)) if hasOffset { b = quicvarint.Append(b, uint64(f.Offset)) } if f.DataLenPresent { b = quicvarint.Append(b, uint64(f.DataLen())) } b = append(b, f.Data...) return b, nil } // Length returns the total length of the STREAM frame func (f *StreamFrame) Length(protocol.Version) protocol.ByteCount { length := 1 + quicvarint.Len(uint64(f.StreamID)) if f.Offset != 0 { length += quicvarint.Len(uint64(f.Offset)) } if f.DataLenPresent { length += quicvarint.Len(uint64(f.DataLen())) } return protocol.ByteCount(length) + f.DataLen() } // DataLen gives the length of data in bytes func (f *StreamFrame) DataLen() protocol.ByteCount { return protocol.ByteCount(len(f.Data)) } // MaxDataLen returns the maximum data length // If 0 is returned, writing will fail (a STREAM frame must contain at least 1 byte of data). func (f *StreamFrame) MaxDataLen(maxSize protocol.ByteCount, _ protocol.Version) protocol.ByteCount { headerLen := 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamID))) if f.Offset != 0 { headerLen += protocol.ByteCount(quicvarint.Len(uint64(f.Offset))) } if f.DataLenPresent { // Pretend that the data size will be 1 byte. // If it turns out that varint encoding the length will consume 2 bytes, we need to adjust the data length afterward headerLen++ } if headerLen > maxSize { return 0 } maxDataLen := maxSize - headerLen if f.DataLenPresent && quicvarint.Len(uint64(maxDataLen)) != 1 { maxDataLen-- } return maxDataLen } // MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes. // It returns if the frame was actually split. // The frame might not be split if: // * the size is large enough to fit the whole frame // * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil. func (f *StreamFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.Version) (*StreamFrame, bool /* was splitting required */) { if maxSize >= f.Length(version) { return nil, false } n := f.MaxDataLen(maxSize, version) if n == 0 { return nil, true } new := GetStreamFrame() new.StreamID = f.StreamID new.Offset = f.Offset new.Fin = false new.DataLenPresent = f.DataLenPresent // swap the data slices new.Data, f.Data = f.Data, new.Data new.fromPool, f.fromPool = f.fromPool, new.fromPool f.Data = f.Data[:protocol.ByteCount(len(new.Data))-n] copy(f.Data, new.Data[n:]) new.Data = new.Data[:n] f.Offset += n return new, true } func (f *StreamFrame) PutBack() { putStreamFrame(f) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/stream_frame_test.go000066400000000000000000000360141465664453100274300ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("STREAM frame", func() { Context("when parsing", func() { It("parses a frame with OFF bit", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, encodeVarInt(0xdecafbad)...) // offset data = append(data, []byte("foobar")...) frame, l, err := parseStreamFrame(data, 0x8^0x4, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345))) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(frame.Fin).To(BeFalse()) Expect(frame.Offset).To(Equal(protocol.ByteCount(0xdecafbad))) Expect(l).To(Equal(len(data))) }) It("respects the LEN when parsing the frame", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, encodeVarInt(4)...) // data length data = append(data, []byte("foobar")...) frame, l, err := parseStreamFrame(data, 0x8^0x2, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345))) Expect(frame.Data).To(Equal([]byte("foob"))) Expect(frame.Fin).To(BeFalse()) Expect(frame.Offset).To(BeZero()) Expect(l).To(Equal(len(data) - 2)) }) It("parses a frame with FIN bit", func() { data := encodeVarInt(9) // stream ID data = append(data, []byte("foobar")...) frame, l, err := parseStreamFrame(data, 0x8^0x1, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(9))) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(frame.Fin).To(BeTrue()) Expect(frame.Offset).To(BeZero()) Expect(l).To(Equal(len(data))) }) It("allows empty frames", func() { data := encodeVarInt(0x1337) // stream ID data = append(data, encodeVarInt(0x12345)...) // offset f, l, err := parseStreamFrame(data, 0x8^0x4, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.StreamID).To(Equal(protocol.StreamID(0x1337))) Expect(f.Offset).To(Equal(protocol.ByteCount(0x12345))) Expect(f.Data).To(BeEmpty()) Expect(f.Fin).To(BeFalse()) Expect(l).To(Equal(len(data))) }) It("rejects frames that overflow the maximum offset", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, encodeVarInt(uint64(protocol.MaxByteCount-5))...) // offset data = append(data, []byte("foobar")...) _, _, err := parseStreamFrame(data, 0x8^0x4, protocol.Version1) Expect(err).To(MatchError("stream data overflows maximum offset")) }) It("rejects frames that claim to be longer than the packet buffer size", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, encodeVarInt(uint64(protocol.MaxPacketBufferSize)+1)...) // data length data = append(data, make([]byte, protocol.MaxPacketBufferSize+1)...) _, _, err := parseStreamFrame(data, 0x8^0x2, protocol.Version1) Expect(err).To(Equal(io.EOF)) }) It("rejects frames that claim to be longer than the remaining size", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, encodeVarInt(7)...) // data length data = append(data, []byte("foobar")...) _, _, err := parseStreamFrame(data, 0x8^0x2, protocol.Version1) Expect(err).To(Equal(io.EOF)) }) It("errors on EOFs", func() { typ := uint64(0x8 ^ 0x4 ^ 0x2) data := encodeVarInt(0x12345) // stream ID data = append(data, encodeVarInt(0xdecafbad)...) // offset data = append(data, encodeVarInt(6)...) // data length data = append(data, []byte("foobar")...) _, _, err := parseStreamFrame(data, typ, protocol.Version1) Expect(err).NotTo(HaveOccurred()) for i := range data { _, _, err = parseStreamFrame(data[:i], typ, protocol.Version1) Expect(err).To(HaveOccurred()) } }) }) Context("using the buffer", func() { It("uses the buffer for long STREAM frames", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize)...) frame, l, err := parseStreamFrame(data, 0x8, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345))) Expect(frame.Data).To(Equal(bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize))) Expect(frame.DataLen()).To(BeEquivalentTo(protocol.MinStreamFrameBufferSize)) Expect(frame.Fin).To(BeFalse()) Expect(frame.fromPool).To(BeTrue()) Expect(l).To(Equal(len(data))) Expect(frame.PutBack).ToNot(Panic()) }) It("doesn't use the buffer for short STREAM frames", func() { data := encodeVarInt(0x12345) // stream ID data = append(data, bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize-1)...) frame, l, err := parseStreamFrame(data, 0x8, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0x12345))) Expect(frame.Data).To(Equal(bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize-1))) Expect(frame.DataLen()).To(BeEquivalentTo(protocol.MinStreamFrameBufferSize - 1)) Expect(frame.Fin).To(BeFalse()) Expect(frame.fromPool).To(BeFalse()) Expect(l).To(Equal(len(data))) Expect(frame.PutBack).ToNot(Panic()) }) }) Context("when writing", func() { It("writes a frame without offset", func() { f := &StreamFrame{ StreamID: 0x1337, Data: []byte("foobar"), } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("writes a frame with offset", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x123456, Data: []byte("foobar"), } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8 ^ 0x4} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, encodeVarInt(0x123456)...) // offset expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("writes a frame with FIN bit", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x123456, Fin: true, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8 ^ 0x4 ^ 0x1} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, encodeVarInt(0x123456)...) // offset Expect(b).To(Equal(expected)) }) It("writes a frame with data length", func() { f := &StreamFrame{ StreamID: 0x1337, Data: []byte("foobar"), DataLenPresent: true, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8 ^ 0x2} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, encodeVarInt(6)...) // data length expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("writes a frame with data length and offset", func() { f := &StreamFrame{ StreamID: 0x1337, Data: []byte("foobar"), DataLenPresent: true, Offset: 0x123456, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8 ^ 0x4 ^ 0x2} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, encodeVarInt(0x123456)...) // offset expected = append(expected, encodeVarInt(6)...) // data length expected = append(expected, []byte("foobar")...) Expect(b).To(Equal(expected)) }) It("refuses to write an empty frame without FIN", func() { f := &StreamFrame{ StreamID: 0x42, Offset: 0x1337, } _, err := f.Append(nil, protocol.Version1) Expect(err).To(MatchError("StreamFrame: attempting to write empty frame without FIN")) }) }) Context("length", func() { It("has the right length for a frame without offset and data length", func() { f := &StreamFrame{ StreamID: 0x1337, Data: []byte("foobar"), } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0x1337) + 6)) }) It("has the right length for a frame with offset", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x42, Data: []byte("foobar"), } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0x1337) + quicvarint.Len(0x42) + 6)) }) It("has the right length for a frame with data length", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x1234567, DataLenPresent: true, Data: []byte("foobar"), } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(1 + quicvarint.Len(0x1337) + quicvarint.Len(0x1234567) + quicvarint.Len(6) + 6)) }) }) Context("max data length", func() { const maxSize = 3000 It("always returns a data length such that the resulting frame has the right size, if data length is not present", func() { data := make([]byte, maxSize) f := &StreamFrame{ StreamID: 0x1337, Offset: 0xdeadbeef, } for i := 1; i < 3000; i++ { f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), protocol.Version1) if maxDataLen == 0 { // 0 means that no valid STREAM frame can be written // check that writing a minimal size STREAM frame (i.e. with 1 byte data) is actually larger than the desired size f.Data = []byte{0} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(len(b)).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(len(b)).To(Equal(i)) } }) It("always returns a data length such that the resulting frame has the right size, if data length is present", func() { data := make([]byte, maxSize) f := &StreamFrame{ StreamID: 0x1337, Offset: 0xdeadbeef, DataLenPresent: true, } var frameOneByteTooSmallCounter int for i := 1; i < 3000; i++ { f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), protocol.Version1) if maxDataLen == 0 { // 0 means that no valid STREAM frame can be written // check that writing a minimal size STREAM frame (i.e. with 1 byte data) is actually larger than the desired size f.Data = []byte{0} b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(len(b)).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) // There's *one* pathological case, where a data length of x can be encoded into 1 byte // but a data lengths of x+1 needs 2 bytes // In that case, it's impossible to create a STREAM frame of the desired size if len(b) == i-1 { frameOneByteTooSmallCounter++ continue } Expect(len(b)).To(Equal(i)) } Expect(frameOneByteTooSmallCounter).To(Equal(1)) }) }) Context("splitting", func() { It("doesn't split if the frame is short enough", func() { f := &StreamFrame{ StreamID: 0x1337, DataLenPresent: true, Offset: 0xdeadbeef, Data: make([]byte, 100), } frame, needsSplit := f.MaybeSplitOffFrame(f.Length(protocol.Version1), protocol.Version1) Expect(needsSplit).To(BeFalse()) Expect(frame).To(BeNil()) Expect(f.DataLen()).To(BeEquivalentTo(100)) frame, needsSplit = f.MaybeSplitOffFrame(f.Length(protocol.Version1)-1, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(frame.DataLen()).To(BeEquivalentTo(99)) f.PutBack() }) It("keeps the data len", func() { f := &StreamFrame{ StreamID: 0x1337, DataLenPresent: true, Data: make([]byte, 100), } frame, needsSplit := f.MaybeSplitOffFrame(66, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(frame).ToNot(BeNil()) Expect(f.DataLenPresent).To(BeTrue()) Expect(frame.DataLenPresent).To(BeTrue()) }) It("adjusts the offset", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x100, Data: []byte("foobar"), } frame, needsSplit := f.MaybeSplitOffFrame(f.Length(protocol.Version1)-3, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(frame).ToNot(BeNil()) Expect(frame.Offset).To(Equal(protocol.ByteCount(0x100))) Expect(frame.Data).To(Equal([]byte("foo"))) Expect(f.Offset).To(Equal(protocol.ByteCount(0x100 + 3))) Expect(f.Data).To(Equal([]byte("bar"))) }) It("preserves the FIN bit", func() { f := &StreamFrame{ StreamID: 0x1337, Fin: true, Offset: 0xdeadbeef, Data: make([]byte, 100), } frame, needsSplit := f.MaybeSplitOffFrame(50, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(frame).ToNot(BeNil()) Expect(frame.Offset).To(BeNumerically("<", f.Offset)) Expect(f.Fin).To(BeTrue()) Expect(frame.Fin).To(BeFalse()) }) It("produces frames of the correct length, without data len", func() { const size = 1000 f := &StreamFrame{ StreamID: 0xdecafbad, Offset: 0x1234, Data: []byte{0}, } minFrameSize := f.Length(protocol.Version1) for i := protocol.ByteCount(0); i < minFrameSize; i++ { f, needsSplit := f.MaybeSplitOffFrame(i, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(f).To(BeNil()) } for i := minFrameSize; i < size; i++ { f.fromPool = false f.Data = make([]byte, size) f, needsSplit := f.MaybeSplitOffFrame(i, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(f.Length(protocol.Version1)).To(Equal(i)) } }) It("produces frames of the correct length, with data len", func() { const size = 1000 f := &StreamFrame{ StreamID: 0xdecafbad, Offset: 0x1234, DataLenPresent: true, Data: []byte{0}, } minFrameSize := f.Length(protocol.Version1) for i := protocol.ByteCount(0); i < minFrameSize; i++ { f, needsSplit := f.MaybeSplitOffFrame(i, protocol.Version1) Expect(needsSplit).To(BeTrue()) Expect(f).To(BeNil()) } var frameOneByteTooSmallCounter int for i := minFrameSize; i < size; i++ { f.fromPool = false f.Data = make([]byte, size) newFrame, needsSplit := f.MaybeSplitOffFrame(i, protocol.Version1) Expect(needsSplit).To(BeTrue()) // There's *one* pathological case, where a data length of x can be encoded into 1 byte // but a data lengths of x+1 needs 2 bytes // In that case, it's impossible to create a STREAM frame of the desired size if newFrame.Length(protocol.Version1) == i-1 { frameOneByteTooSmallCounter++ continue } Expect(newFrame.Length(protocol.Version1)).To(Equal(i)) } Expect(frameOneByteTooSmallCounter).To(Equal(1)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/streams_blocked_frame.go000066400000000000000000000025371465664453100302420ustar00rootroot00000000000000package wire import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" ) // A StreamsBlockedFrame is a STREAMS_BLOCKED frame type StreamsBlockedFrame struct { Type protocol.StreamType StreamLimit protocol.StreamNum } func parseStreamsBlockedFrame(b []byte, typ uint64, _ protocol.Version) (*StreamsBlockedFrame, int, error) { f := &StreamsBlockedFrame{} switch typ { case bidiStreamBlockedFrameType: f.Type = protocol.StreamTypeBidi case uniStreamBlockedFrameType: f.Type = protocol.StreamTypeUni } streamLimit, l, err := quicvarint.Parse(b) if err != nil { return nil, 0, replaceUnexpectedEOF(err) } f.StreamLimit = protocol.StreamNum(streamLimit) if f.StreamLimit > protocol.MaxStreamCount { return nil, 0, fmt.Errorf("%d exceeds the maximum stream count", f.StreamLimit) } return f, l, nil } func (f *StreamsBlockedFrame) Append(b []byte, _ protocol.Version) ([]byte, error) { switch f.Type { case protocol.StreamTypeBidi: b = append(b, bidiStreamBlockedFrameType) case protocol.StreamTypeUni: b = append(b, uniStreamBlockedFrameType) } b = quicvarint.Append(b, uint64(f.StreamLimit)) return b, nil } // Length of a written frame func (f *StreamsBlockedFrame) Length(_ protocol.Version) protocol.ByteCount { return 1 + protocol.ByteCount(quicvarint.Len(uint64(f.StreamLimit))) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/streams_blocked_frame_test.go000066400000000000000000000070671465664453100313040ustar00rootroot00000000000000package wire import ( "fmt" "io" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("STREAMS_BLOCKED frame", func() { Context("parsing", func() { It("accepts a frame for bidirectional streams", func() { data := encodeVarInt(0x1337) f, l, err := parseStreamsBlockedFrame(data, bidiStreamBlockedFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeBidi)) Expect(f.StreamLimit).To(BeEquivalentTo(0x1337)) Expect(l).To(Equal(len(data))) }) It("accepts a frame for unidirectional streams", func() { data := encodeVarInt(0x7331) f, l, err := parseStreamsBlockedFrame(data, uniStreamBlockedFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeUni)) Expect(f.StreamLimit).To(BeEquivalentTo(0x7331)) Expect(l).To(Equal(len(data))) }) It("errors on EOFs", func() { data := encodeVarInt(0x12345678) _, l, err := parseStreamsBlockedFrame(data, bidiStreamBlockedFrameType, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(l).To(Equal(len(data))) for i := range data { _, _, err := parseStreamsBlockedFrame(data[:i], bidiStreamBlockedFrameType, protocol.Version1) Expect(err).To(MatchError(io.EOF)) } }) for _, t := range []protocol.StreamType{protocol.StreamTypeUni, protocol.StreamTypeBidi} { streamType := t It("accepts a frame containing the maximum stream count", func() { f := &StreamsBlockedFrame{ Type: streamType, StreamLimit: protocol.MaxStreamCount, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] frame, l, err := parseStreamsBlockedFrame(b, typ, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(l).To(Equal(len(b))) }) It("errors when receiving a too large stream count", func() { f := &StreamsBlockedFrame{ Type: streamType, StreamLimit: protocol.MaxStreamCount + 1, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) typ, l, err := quicvarint.Parse(b) Expect(err).ToNot(HaveOccurred()) b = b[l:] _, _, err = parseStreamsBlockedFrame(b, typ, protocol.Version1) Expect(err).To(MatchError(fmt.Sprintf("%d exceeds the maximum stream count", protocol.MaxStreamCount+1))) }) } }) Context("writing", func() { It("writes a frame for bidirectional streams", func() { f := StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: 0xdeadbeefcafe, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{bidiStreamBlockedFrameType} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) Expect(b).To(Equal(expected)) }) It("writes a frame for unidirectional streams", func() { f := StreamsBlockedFrame{ Type: protocol.StreamTypeUni, StreamLimit: 0xdeadbeefcafe, } b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) expected := []byte{uniStreamBlockedFrameType} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) Expect(b).To(Equal(expected)) }) It("has the correct min length", func() { frame := StreamsBlockedFrame{StreamLimit: 0x123456} Expect(frame.Length(0)).To(Equal(1 + protocol.ByteCount(quicvarint.Len(0x123456)))) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/transport_parameter_test.go000066400000000000000000001052401465664453100310550ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "math" "net/netip" "testing" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func getRandomValueUpTo(max int64) uint64 { maxVals := []int64{math.MaxUint8 / 4, math.MaxUint16 / 4, math.MaxUint32 / 4, math.MaxUint64 / 4} m := maxVals[int(rand.Int31n(4))] if m > max { m = max } return uint64(rand.Int63n(m)) } func getRandomValue() uint64 { return getRandomValueUpTo(quicvarint.Max) } var _ = Describe("Transport Parameters", func() { BeforeEach(func() { rand.Seed(uint64(GinkgoRandomSeed())) }) appendInitialSourceConnectionID := func(b []byte) []byte { b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) b = quicvarint.Append(b, 6) return append(b, []byte("foobar")...) } It("has a string representation", func() { rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) p := &TransportParameters{ InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42 * time.Second, OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), RetrySourceConnectionID: &rcid, AckDelayExponent: 14, MaxAckDelay: 37 * time.Millisecond, StatelessResetToken: &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00}, ActiveConnectionIDLimit: 123, MaxDatagramFrameSize: 876, } Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalDestinationConnectionID: deadbeef, InitialSourceConnectionID: decafbad, RetrySourceConnectionID: deadc0de, InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37ms, ActiveConnectionIDLimit: 123, StatelessResetToken: 0x112233445566778899aabbccddeeff00, MaxDatagramFrameSize: 876}")) }) It("has a string representation, if there's no stateless reset token, no Retry source connection id and no datagram support", func() { p := &TransportParameters{ InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42 * time.Second, OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), InitialSourceConnectionID: protocol.ParseConnectionID([]byte{}), AckDelayExponent: 14, MaxAckDelay: 37 * time.Second, ActiveConnectionIDLimit: 89, MaxDatagramFrameSize: protocol.InvalidByteCount, } Expect(p.String()).To(Equal("&wire.TransportParameters{OriginalDestinationConnectionID: deadbeef, InitialSourceConnectionID: (empty), InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42s, AckDelayExponent: 14, MaxAckDelay: 37s, ActiveConnectionIDLimit: 89}")) }) It("marshals and unmarshals", func() { var token protocol.StatelessResetToken rand.Read(token[:]) rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) params := &TransportParameters{ InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), InitialMaxData: protocol.ByteCount(getRandomValue()), MaxIdleTimeout: 0xcafe * time.Second, MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), DisableActiveMigration: true, StatelessResetToken: &token, OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), RetrySourceConnectionID: &rcid, AckDelayExponent: 13, MaxAckDelay: 42 * time.Millisecond, ActiveConnectionIDLimit: 2 + getRandomValueUpTo(quicvarint.Max-2), MaxUDPPayloadSize: 1200 + protocol.ByteCount(getRandomValueUpTo(quicvarint.Max-1200)), MaxDatagramFrameSize: protocol.ByteCount(getRandomValue()), } data := params.Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal)) Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote)) Expect(p.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni)) Expect(p.InitialMaxData).To(Equal(params.InitialMaxData)) Expect(p.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum)) Expect(p.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum)) Expect(p.MaxIdleTimeout).To(Equal(params.MaxIdleTimeout)) Expect(p.DisableActiveMigration).To(Equal(params.DisableActiveMigration)) Expect(p.StatelessResetToken).To(Equal(params.StatelessResetToken)) Expect(p.OriginalDestinationConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}))) Expect(p.InitialSourceConnectionID).To(Equal(protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}))) Expect(p.RetrySourceConnectionID).To(Equal(&rcid)) Expect(p.AckDelayExponent).To(Equal(uint8(13))) Expect(p.MaxAckDelay).To(Equal(42 * time.Millisecond)) Expect(p.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit)) Expect(p.MaxUDPPayloadSize).To(Equal(params.MaxUDPPayloadSize)) Expect(p.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize)) }) It("marshals additional transport parameters (used for testing large ClientHellos)", func() { origAdditionalTransportParametersClient := AdditionalTransportParametersClient defer func() { AdditionalTransportParametersClient = origAdditionalTransportParametersClient }() AdditionalTransportParametersClient = map[uint64][]byte{1337: []byte("foobar")} result := quicvarint.Append([]byte{}, 1337) result = quicvarint.Append(result, 6) result = append(result, []byte("foobar")...) params := &TransportParameters{} Expect(bytes.Contains(params.Marshal(protocol.PerspectiveClient), result)).To(BeTrue()) Expect(bytes.Contains(params.Marshal(protocol.PerspectiveServer), result)).To(BeFalse()) }) It("doesn't marshal a retry_source_connection_id, if no Retry was performed", func() { data := (&TransportParameters{ StatelessResetToken: &protocol.StatelessResetToken{}, ActiveConnectionIDLimit: 2, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.RetrySourceConnectionID).To(BeNil()) }) It("marshals a zero-length retry_source_connection_id", func() { rcid := protocol.ParseConnectionID([]byte{}) data := (&TransportParameters{ RetrySourceConnectionID: &rcid, StatelessResetToken: &protocol.StatelessResetToken{}, ActiveConnectionIDLimit: 2, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.RetrySourceConnectionID).ToNot(BeNil()) Expect(p.RetrySourceConnectionID.Len()).To(BeZero()) }) It("errors when the stateless_reset_token has the wrong length", func() { b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID)) b = quicvarint.Append(b, 15) b = append(b, make([]byte, 15)...) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "wrong length for stateless_reset_token: 15 (expected 16)", })) }) It("errors when the max_udp_payload_size is too small", func() { b := quicvarint.Append(nil, uint64(maxUDPPayloadSizeParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(1199))) b = quicvarint.Append(b, 1199) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for max_udp_payload_size: 1199 (minimum 1200)", })) }) It("errors when disable_active_migration has content", func() { b := quicvarint.Append(nil, uint64(disableActiveMigrationParameterID)) b = quicvarint.Append(b, 6) b = append(b, []byte("foobar")...) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "wrong length for disable_active_migration: 6 (expected empty)", })) }) It("errors when the server doesn't set the original_destination_connection_id", func() { b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID)) b = quicvarint.Append(b, 16) b = append(b, make([]byte, 16)...) b = appendInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "missing original_destination_connection_id", })) }) It("errors when the initial_source_connection_id is missing", func() { Expect((&TransportParameters{}).Unmarshal([]byte{}, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "missing initial_source_connection_id", })) }) It("errors when the max_ack_delay is too large", func() { data := (&TransportParameters{ MaxAckDelay: 1 << 14 * time.Millisecond, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for max_ack_delay: 16384ms (maximum 16383ms)", })) }) It("doesn't send the max_ack_delay, if it has the default value", func() { const num = 1000 var defaultLen, dataLen int // marshal 1000 times to average out the greasing transport parameter maxAckDelay := protocol.DefaultMaxAckDelay + time.Millisecond for i := 0; i < num; i++ { dataDefault := (&TransportParameters{ MaxAckDelay: protocol.DefaultMaxAckDelay, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) defaultLen += len(dataDefault) data := (&TransportParameters{ MaxAckDelay: maxAckDelay, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) dataLen += len(data) } entryLen := quicvarint.Len(uint64(ackDelayExponentParameterID)) /* parameter id */ + quicvarint.Len(uint64(quicvarint.Len(uint64(maxAckDelay.Milliseconds())))) /*length */ + quicvarint.Len(uint64(maxAckDelay.Milliseconds())) /* value */ Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1)) }) It("errors when the active_connection_id_limit is too small", func() { data := (&TransportParameters{ ActiveConnectionIDLimit: 1, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for active_connection_id_limit: 1 (minimum 2)", })) }) It("errors when the ack_delay_exponenent is too large", func() { data := (&TransportParameters{ AckDelayExponent: 21, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for ack_delay_exponent: 21 (maximum 20)", })) }) It("doesn't send the ack_delay_exponent, if it has the default value", func() { const num = 1000 var defaultLen, dataLen int // marshal 1000 times to average out the greasing transport parameter for i := 0; i < num; i++ { dataDefault := (&TransportParameters{ AckDelayExponent: protocol.DefaultAckDelayExponent, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) defaultLen += len(dataDefault) data := (&TransportParameters{ AckDelayExponent: protocol.DefaultAckDelayExponent + 1, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) dataLen += len(data) } entryLen := quicvarint.Len(uint64(ackDelayExponentParameterID)) /* parameter id */ + quicvarint.Len(uint64(quicvarint.Len(protocol.DefaultAckDelayExponent+1))) /* length */ + quicvarint.Len(protocol.DefaultAckDelayExponent+1) /* value */ Expect(float32(dataLen) / num).To(BeNumerically("~", float32(defaultLen)/num+float32(entryLen), 1)) }) It("sets the default value for the ack_delay_exponent and max_active_connection_id_limit, when no values were sent", func() { data := (&TransportParameters{ AckDelayExponent: protocol.DefaultAckDelayExponent, StatelessResetToken: &protocol.StatelessResetToken{}, ActiveConnectionIDLimit: protocol.DefaultActiveConnectionIDLimit, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.AckDelayExponent).To(BeEquivalentTo(protocol.DefaultAckDelayExponent)) Expect(p.ActiveConnectionIDLimit).To(BeEquivalentTo(protocol.DefaultActiveConnectionIDLimit)) }) It("errors when the varint value has the wrong length", func() { b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID)) b = quicvarint.Append(b, 2) val := uint64(0xdeadbeef) Expect(quicvarint.Len(val)).ToNot(BeEquivalentTo(2)) b = quicvarint.Append(b, val) b = appendInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: fmt.Sprintf("inconsistent transport parameter length for transport parameter %#x", initialMaxStreamDataBidiLocalParameterID), })) }) It("errors if initial_max_streams_bidi is too large", func() { b := quicvarint.Append(nil, uint64(initialMaxStreamsBidiParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1)))) b = quicvarint.Append(b, uint64(protocol.MaxStreamCount+1)) b = appendInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "initial_max_streams_bidi too large: 1152921504606846977 (maximum 1152921504606846976)", })) }) It("errors if initial_max_streams_uni is too large", func() { b := quicvarint.Append(nil, uint64(initialMaxStreamsUniParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1)))) b = quicvarint.Append(b, uint64(protocol.MaxStreamCount+1)) b = appendInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "initial_max_streams_uni too large: 1152921504606846977 (maximum 1152921504606846976)", })) }) It("handles huge max_ack_delay values", func() { val := uint64(math.MaxUint64) / 5 b := quicvarint.Append(nil, uint64(maxAckDelayParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(val))) b = quicvarint.Append(b, val) b = appendInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for max_ack_delay: 3689348814741910323ms (maximum 16383ms)", })) }) It("skips unknown parameters", func() { // write a known parameter b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337))) b = quicvarint.Append(b, 0x1337) // write an unknown parameter b = quicvarint.Append(b, 0x42) b = quicvarint.Append(b, 6) b = append(b, []byte("foobar")...) // write a known parameter b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiRemoteParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(0x42))) b = quicvarint.Append(b, 0x42) b = appendInitialSourceConnectionID(b) p := &TransportParameters{} Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(Succeed()) Expect(p.InitialMaxStreamDataBidiLocal).To(Equal(protocol.ByteCount(0x1337))) Expect(p.InitialMaxStreamDataBidiRemote).To(Equal(protocol.ByteCount(0x42))) }) It("rejects duplicate parameters", func() { // write first parameter b := quicvarint.Append(nil, uint64(initialMaxStreamDataBidiLocalParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337))) b = quicvarint.Append(b, 0x1337) // write a second parameter b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiRemoteParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(0x42))) b = quicvarint.Append(b, 0x42) // write first parameter again b = quicvarint.Append(b, uint64(initialMaxStreamDataBidiLocalParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(0x1337))) b = quicvarint.Append(b, 0x1337) b = appendInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: fmt.Sprintf("received duplicate transport parameter %#x", initialMaxStreamDataBidiLocalParameterID), })) }) It("errors if there's not enough data to read", func() { b := quicvarint.Append(nil, 0x42) b = quicvarint.Append(b, 7) b = append(b, []byte("foobar")...) p := &TransportParameters{} Expect(p.Unmarshal(b, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "remaining length (6) smaller than parameter length (7)", })) }) It("errors if the client sent a stateless_reset_token", func() { b := quicvarint.Append(nil, uint64(statelessResetTokenParameterID)) b = quicvarint.Append(b, uint64(quicvarint.Len(16))) b = append(b, make([]byte, 16)...) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "client sent a stateless_reset_token", })) }) It("errors if the client sent the original_destination_connection_id", func() { b := quicvarint.Append(nil, uint64(originalDestinationConnectionIDParameterID)) b = quicvarint.Append(b, 6) b = append(b, []byte("foobar")...) Expect((&TransportParameters{}).Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "client sent an original_destination_connection_id", })) }) Context("preferred address", func() { var pa *PreferredAddress BeforeEach(func() { pa = &PreferredAddress{ IPv4: netip.AddrPortFrom(netip.AddrFrom4([4]byte{127, 0, 0, 1}), 42), IPv6: netip.AddrPortFrom(netip.AddrFrom16([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 13), ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), StatelessResetToken: protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, } }) It("marshals and unmarshals", func() { data := (&TransportParameters{ PreferredAddress: pa, StatelessResetToken: &protocol.StatelessResetToken{}, ActiveConnectionIDLimit: 2, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.PreferredAddress.IPv4).To(Equal(pa.IPv4)) Expect(p.PreferredAddress.IPv6).To(Equal(pa.IPv6)) Expect(p.PreferredAddress.ConnectionID).To(Equal(pa.ConnectionID)) Expect(p.PreferredAddress.StatelessResetToken).To(Equal(pa.StatelessResetToken)) }) It("errors if the client sent a preferred_address", func() { b := quicvarint.Append(nil, uint64(preferredAddressParameterID)) b = quicvarint.Append(b, 6) b = append(b, []byte("foobar")...) p := &TransportParameters{} Expect(p.Unmarshal(b, protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "client sent a preferred_address", })) }) It("errors on zero-length connection IDs", func() { pa.ConnectionID = protocol.ParseConnectionID([]byte{}) data := (&TransportParameters{ PreferredAddress: pa, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid connection ID length: 0", })) }) It("errors on EOF", func() { raw := []byte{ 127, 0, 0, 1, // IPv4 0, 42, // IPv4 Port 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // IPv6 13, 37, // IPv6 Port, 4, // conn ID len 0xde, 0xad, 0xbe, 0xef, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, // stateless reset token } for i := 1; i < len(raw); i++ { b := quicvarint.Append(nil, uint64(preferredAddressParameterID)) b = append(b, raw[:i]...) p := &TransportParameters{} Expect(p.Unmarshal(b, protocol.PerspectiveServer)).ToNot(Succeed()) } }) }) Context("saving and retrieving from a session ticket", func() { It("saves and retrieves the parameters", func() { params := &TransportParameters{ InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), InitialMaxData: protocol.ByteCount(getRandomValue()), MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), ActiveConnectionIDLimit: 2 + getRandomValueUpTo(quicvarint.Max-2), MaxDatagramFrameSize: protocol.ByteCount(getRandomValueUpTo(int64(MaxDatagramSize))), } Expect(params.ValidFor0RTT(params)).To(BeTrue()) b := params.MarshalForSessionTicket(nil) var tp TransportParameters Expect(tp.UnmarshalFromSessionTicket(b)).To(Succeed()) Expect(tp.InitialMaxStreamDataBidiLocal).To(Equal(params.InitialMaxStreamDataBidiLocal)) Expect(tp.InitialMaxStreamDataBidiRemote).To(Equal(params.InitialMaxStreamDataBidiRemote)) Expect(tp.InitialMaxStreamDataUni).To(Equal(params.InitialMaxStreamDataUni)) Expect(tp.InitialMaxData).To(Equal(params.InitialMaxData)) Expect(tp.MaxBidiStreamNum).To(Equal(params.MaxBidiStreamNum)) Expect(tp.MaxUniStreamNum).To(Equal(params.MaxUniStreamNum)) Expect(tp.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit)) Expect(tp.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize)) }) It("rejects the parameters if it can't parse them", func() { var p TransportParameters Expect(p.UnmarshalFromSessionTicket([]byte("foobar"))).ToNot(Succeed()) }) It("rejects the parameters if the version changed", func() { var p TransportParameters data := p.MarshalForSessionTicket(nil) b := quicvarint.Append(nil, transportParameterMarshalingVersion+1) b = append(b, data[quicvarint.Len(transportParameterMarshalingVersion):]...) Expect(p.UnmarshalFromSessionTicket(b)).To(MatchError(fmt.Sprintf("unknown transport parameter marshaling version: %d", transportParameterMarshalingVersion+1))) }) Context("rejects the parameters if they changed", func() { var p TransportParameters saved := &TransportParameters{ InitialMaxStreamDataBidiLocal: 1, InitialMaxStreamDataBidiRemote: 2, InitialMaxStreamDataUni: 3, InitialMaxData: 4, MaxBidiStreamNum: 5, MaxUniStreamNum: 6, ActiveConnectionIDLimit: 7, MaxDatagramFrameSize: 1000, } BeforeEach(func() { p = *saved Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() { p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() { p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() { p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() { p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() { p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() { p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxData was reduced", func() { p.InitialMaxData = saved.InitialMaxData - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxData was increased", func() { p.InitialMaxData = saved.InitialMaxData + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the MaxBidiStreamNum was reduced", func() { p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("accepts the parameters if the MaxBidiStreamNum was increased", func() { p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the MaxUniStreamNum changed", func() { p.MaxUniStreamNum = saved.MaxUniStreamNum - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("accepts the parameters if the MaxUniStreamNum was increased", func() { p.MaxUniStreamNum = saved.MaxUniStreamNum + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the ActiveConnectionIDLimit changed", func() { p.ActiveConnectionIDLimit = 0 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) It("accepts the parameters if the MaxDatagramFrameSize was increased", func() { p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1 Expect(p.ValidFor0RTT(saved)).To(BeTrue()) }) It("rejects the parameters if the MaxDatagramFrameSize reduced", func() { p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1 Expect(p.ValidFor0RTT(saved)).To(BeFalse()) }) }) Context("client checks the parameters after successfully sending 0-RTT data", func() { var p TransportParameters saved := &TransportParameters{ InitialMaxStreamDataBidiLocal: 1, InitialMaxStreamDataBidiRemote: 2, InitialMaxStreamDataUni: 3, InitialMaxData: 4, MaxBidiStreamNum: 5, MaxUniStreamNum: 6, ActiveConnectionIDLimit: 7, MaxDatagramFrameSize: 1000, } BeforeEach(func() { p = *saved Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxStreamDataBidiLocal was reduced", func() { p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxStreamDataBidiLocal was increased", func() { p.InitialMaxStreamDataBidiLocal = saved.InitialMaxStreamDataBidiLocal + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxStreamDataBidiRemote was reduced", func() { p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxStreamDataBidiRemote was increased", func() { p.InitialMaxStreamDataBidiRemote = saved.InitialMaxStreamDataBidiRemote + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxStreamDataUni was reduced", func() { p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxStreamDataUni was increased", func() { p.InitialMaxStreamDataUni = saved.InitialMaxStreamDataUni + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the InitialMaxData was reduced", func() { p.InitialMaxData = saved.InitialMaxData - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the InitialMaxData was increased", func() { p.InitialMaxData = saved.InitialMaxData + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the MaxBidiStreamNum was reduced", func() { p.MaxBidiStreamNum = saved.MaxBidiStreamNum - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the MaxBidiStreamNum was increased", func() { p.MaxBidiStreamNum = saved.MaxBidiStreamNum + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the MaxUniStreamNum reduced", func() { p.MaxUniStreamNum = saved.MaxUniStreamNum - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the MaxUniStreamNum was increased", func() { p.MaxUniStreamNum = saved.MaxUniStreamNum + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the ActiveConnectionIDLimit reduced", func() { p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the ActiveConnectionIDLimit increased", func() { p.ActiveConnectionIDLimit = saved.ActiveConnectionIDLimit + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) It("rejects the parameters if the MaxDatagramFrameSize reduced", func() { p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize - 1 Expect(p.ValidForUpdate(saved)).To(BeFalse()) }) It("doesn't reject the parameters if the MaxDatagramFrameSize increased", func() { p.MaxDatagramFrameSize = saved.MaxDatagramFrameSize + 1 Expect(p.ValidForUpdate(saved)).To(BeTrue()) }) }) }) }) func BenchmarkTransportParameters(b *testing.B) { b.Run("without preferred address", func(b *testing.B) { benchmarkTransportParameters(b, false) }) b.Run("with preferred address", func(b *testing.B) { benchmarkTransportParameters(b, true) }) } func benchmarkTransportParameters(b *testing.B, withPreferredAddress bool) { var token protocol.StatelessResetToken rand.Read(token[:]) rcid := protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}) params := &TransportParameters{ InitialMaxStreamDataBidiLocal: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataBidiRemote: protocol.ByteCount(getRandomValue()), InitialMaxStreamDataUni: protocol.ByteCount(getRandomValue()), InitialMaxData: protocol.ByteCount(getRandomValue()), MaxIdleTimeout: 0xcafe * time.Second, MaxBidiStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), MaxUniStreamNum: protocol.StreamNum(getRandomValueUpTo(int64(protocol.MaxStreamCount))), DisableActiveMigration: true, StatelessResetToken: &token, OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}), RetrySourceConnectionID: &rcid, AckDelayExponent: 13, MaxAckDelay: 42 * time.Millisecond, ActiveConnectionIDLimit: 2 + getRandomValueUpTo(quicvarint.Max-2), MaxDatagramFrameSize: protocol.ByteCount(getRandomValue()), } var token2 protocol.StatelessResetToken rand.Read(token2[:]) if withPreferredAddress { var ip4 [4]byte var ip6 [16]byte rand.Read(ip4[:]) rand.Read(ip6[:]) params.PreferredAddress = &PreferredAddress{ IPv4: netip.AddrPortFrom(netip.AddrFrom4(ip4), 1234), IPv6: netip.AddrPortFrom(netip.AddrFrom16(ip6), 4321), ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), StatelessResetToken: token2, } } data := params.Marshal(protocol.PerspectiveServer) b.ResetTimer() b.ReportAllocs() var p TransportParameters for i := 0; i < b.N; i++ { if err := p.Unmarshal(data, protocol.PerspectiveServer); err != nil { b.Fatal(err) } // check a few fields if p.DisableActiveMigration != params.DisableActiveMigration || p.InitialMaxStreamDataBidiLocal != params.InitialMaxStreamDataBidiLocal || *p.StatelessResetToken != *params.StatelessResetToken || p.AckDelayExponent != params.AckDelayExponent { b.Fatalf("params mismatch: %v vs %v", p, params) } if withPreferredAddress && *p.PreferredAddress != *params.PreferredAddress { b.Fatalf("preferred address mismatch: %v vs %v", p.PreferredAddress, params.PreferredAddress) } } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/transport_parameters.go000066400000000000000000000525301465664453100302040ustar00rootroot00000000000000package wire import ( "crypto/rand" "encoding/binary" "errors" "fmt" "io" "net/netip" "slices" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/quicvarint" ) // AdditionalTransportParametersClient are additional transport parameters that will be added // to the client's transport parameters. // This is not intended for production use, but _only_ to increase the size of the ClientHello beyond // the usual size of less than 1 MTU. var AdditionalTransportParametersClient map[uint64][]byte const transportParameterMarshalingVersion = 1 type transportParameterID uint64 const ( originalDestinationConnectionIDParameterID transportParameterID = 0x0 maxIdleTimeoutParameterID transportParameterID = 0x1 statelessResetTokenParameterID transportParameterID = 0x2 maxUDPPayloadSizeParameterID transportParameterID = 0x3 initialMaxDataParameterID transportParameterID = 0x4 initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5 initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6 initialMaxStreamDataUniParameterID transportParameterID = 0x7 initialMaxStreamsBidiParameterID transportParameterID = 0x8 initialMaxStreamsUniParameterID transportParameterID = 0x9 ackDelayExponentParameterID transportParameterID = 0xa maxAckDelayParameterID transportParameterID = 0xb disableActiveMigrationParameterID transportParameterID = 0xc preferredAddressParameterID transportParameterID = 0xd activeConnectionIDLimitParameterID transportParameterID = 0xe initialSourceConnectionIDParameterID transportParameterID = 0xf retrySourceConnectionIDParameterID transportParameterID = 0x10 // RFC 9221 maxDatagramFrameSizeParameterID transportParameterID = 0x20 ) // PreferredAddress is the value encoding in the preferred_address transport parameter type PreferredAddress struct { IPv4, IPv6 netip.AddrPort ConnectionID protocol.ConnectionID StatelessResetToken protocol.StatelessResetToken } // TransportParameters are parameters sent to the peer during the handshake type TransportParameters struct { InitialMaxStreamDataBidiLocal protocol.ByteCount InitialMaxStreamDataBidiRemote protocol.ByteCount InitialMaxStreamDataUni protocol.ByteCount InitialMaxData protocol.ByteCount MaxAckDelay time.Duration AckDelayExponent uint8 DisableActiveMigration bool MaxUDPPayloadSize protocol.ByteCount MaxUniStreamNum protocol.StreamNum MaxBidiStreamNum protocol.StreamNum MaxIdleTimeout time.Duration PreferredAddress *PreferredAddress OriginalDestinationConnectionID protocol.ConnectionID InitialSourceConnectionID protocol.ConnectionID RetrySourceConnectionID *protocol.ConnectionID // use a pointer here to distinguish zero-length connection IDs from missing transport parameters StatelessResetToken *protocol.StatelessResetToken ActiveConnectionIDLimit uint64 MaxDatagramFrameSize protocol.ByteCount } // Unmarshal the transport parameters func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error { if err := p.unmarshal(data, sentBy, false); err != nil { return &qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), } } return nil } func (p *TransportParameters) unmarshal(b []byte, sentBy protocol.Perspective, fromSessionTicket bool) error { // needed to check that every parameter is only sent at most once parameterIDs := make([]transportParameterID, 0, 32) var ( readOriginalDestinationConnectionID bool readInitialSourceConnectionID bool readActiveConnectionIDLimit bool ) p.AckDelayExponent = protocol.DefaultAckDelayExponent p.MaxAckDelay = protocol.DefaultMaxAckDelay p.MaxDatagramFrameSize = protocol.InvalidByteCount for len(b) > 0 { paramIDInt, l, err := quicvarint.Parse(b) if err != nil { return err } paramID := transportParameterID(paramIDInt) b = b[l:] paramLen, l, err := quicvarint.Parse(b) if err != nil { return err } b = b[l:] if uint64(len(b)) < paramLen { return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", len(b), paramLen) } parameterIDs = append(parameterIDs, paramID) switch paramID { case activeConnectionIDLimitParameterID: readActiveConnectionIDLimit = true fallthrough case maxIdleTimeoutParameterID, maxUDPPayloadSizeParameterID, initialMaxDataParameterID, initialMaxStreamDataBidiLocalParameterID, initialMaxStreamDataBidiRemoteParameterID, initialMaxStreamDataUniParameterID, initialMaxStreamsBidiParameterID, initialMaxStreamsUniParameterID, maxAckDelayParameterID, maxDatagramFrameSizeParameterID, ackDelayExponentParameterID: if err := p.readNumericTransportParameter(b, paramID, int(paramLen)); err != nil { return err } b = b[paramLen:] case preferredAddressParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent a preferred_address") } if err := p.readPreferredAddress(b, int(paramLen)); err != nil { return err } b = b[paramLen:] case disableActiveMigrationParameterID: if paramLen != 0 { return fmt.Errorf("wrong length for disable_active_migration: %d (expected empty)", paramLen) } p.DisableActiveMigration = true case statelessResetTokenParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent a stateless_reset_token") } if paramLen != 16 { return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen) } var token protocol.StatelessResetToken if len(b) < len(token) { return io.EOF } copy(token[:], b) b = b[len(token):] p.StatelessResetToken = &token case originalDestinationConnectionIDParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent an original_destination_connection_id") } if paramLen > protocol.MaxConnIDLen { return protocol.ErrInvalidConnectionIDLen } p.OriginalDestinationConnectionID = protocol.ParseConnectionID(b[:paramLen]) b = b[paramLen:] readOriginalDestinationConnectionID = true case initialSourceConnectionIDParameterID: if paramLen > protocol.MaxConnIDLen { return protocol.ErrInvalidConnectionIDLen } p.InitialSourceConnectionID = protocol.ParseConnectionID(b[:paramLen]) b = b[paramLen:] readInitialSourceConnectionID = true case retrySourceConnectionIDParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent a retry_source_connection_id") } if paramLen > protocol.MaxConnIDLen { return protocol.ErrInvalidConnectionIDLen } connID := protocol.ParseConnectionID(b[:paramLen]) b = b[paramLen:] p.RetrySourceConnectionID = &connID default: b = b[paramLen:] } } if !readActiveConnectionIDLimit { p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit } if !fromSessionTicket { if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID { return errors.New("missing original_destination_connection_id") } if p.MaxUDPPayloadSize == 0 { p.MaxUDPPayloadSize = protocol.MaxByteCount } if !readInitialSourceConnectionID { return errors.New("missing initial_source_connection_id") } } // check that every transport parameter was sent at most once slices.SortFunc(parameterIDs, func(a, b transportParameterID) int { if a < b { return -1 } return 1 }) for i := 0; i < len(parameterIDs)-1; i++ { if parameterIDs[i] == parameterIDs[i+1] { return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i]) } } return nil } func (p *TransportParameters) readPreferredAddress(b []byte, expectedLen int) error { remainingLen := len(b) pa := &PreferredAddress{} if len(b) < 4+2+16+2+1 { return io.EOF } var ipv4 [4]byte copy(ipv4[:], b[:4]) port4 := binary.BigEndian.Uint16(b[4:]) b = b[4+2:] pa.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(ipv4), port4) var ipv6 [16]byte copy(ipv6[:], b[:16]) port6 := binary.BigEndian.Uint16(b[16:]) pa.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(ipv6), port6) b = b[16+2:] connIDLen := int(b[0]) b = b[1:] if connIDLen == 0 || connIDLen > protocol.MaxConnIDLen { return fmt.Errorf("invalid connection ID length: %d", connIDLen) } if len(b) < connIDLen+len(pa.StatelessResetToken) { return io.EOF } pa.ConnectionID = protocol.ParseConnectionID(b[:connIDLen]) b = b[connIDLen:] copy(pa.StatelessResetToken[:], b) b = b[len(pa.StatelessResetToken):] if bytesRead := remainingLen - len(b); bytesRead != expectedLen { return fmt.Errorf("expected preferred_address to be %d long, read %d bytes", expectedLen, bytesRead) } p.PreferredAddress = pa return nil } func (p *TransportParameters) readNumericTransportParameter(b []byte, paramID transportParameterID, expectedLen int) error { val, l, err := quicvarint.Parse(b) if err != nil { return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err) } if l != expectedLen { return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID) } //nolint:exhaustive // This only covers the numeric transport parameters. switch paramID { case initialMaxStreamDataBidiLocalParameterID: p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val) case initialMaxStreamDataBidiRemoteParameterID: p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val) case initialMaxStreamDataUniParameterID: p.InitialMaxStreamDataUni = protocol.ByteCount(val) case initialMaxDataParameterID: p.InitialMaxData = protocol.ByteCount(val) case initialMaxStreamsBidiParameterID: p.MaxBidiStreamNum = protocol.StreamNum(val) if p.MaxBidiStreamNum > protocol.MaxStreamCount { return fmt.Errorf("initial_max_streams_bidi too large: %d (maximum %d)", p.MaxBidiStreamNum, protocol.MaxStreamCount) } case initialMaxStreamsUniParameterID: p.MaxUniStreamNum = protocol.StreamNum(val) if p.MaxUniStreamNum > protocol.MaxStreamCount { return fmt.Errorf("initial_max_streams_uni too large: %d (maximum %d)", p.MaxUniStreamNum, protocol.MaxStreamCount) } case maxIdleTimeoutParameterID: p.MaxIdleTimeout = max(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond) case maxUDPPayloadSizeParameterID: if val < 1200 { return fmt.Errorf("invalid value for max_udp_payload_size: %d (minimum 1200)", val) } p.MaxUDPPayloadSize = protocol.ByteCount(val) case ackDelayExponentParameterID: if val > protocol.MaxAckDelayExponent { return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent) } p.AckDelayExponent = uint8(val) case maxAckDelayParameterID: if val > uint64(protocol.MaxMaxAckDelay/time.Millisecond) { return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", val, protocol.MaxMaxAckDelay/time.Millisecond) } p.MaxAckDelay = time.Duration(val) * time.Millisecond case activeConnectionIDLimitParameterID: if val < 2 { return fmt.Errorf("invalid value for active_connection_id_limit: %d (minimum 2)", val) } p.ActiveConnectionIDLimit = val case maxDatagramFrameSizeParameterID: p.MaxDatagramFrameSize = protocol.ByteCount(val) default: return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID) } return nil } // Marshal the transport parameters func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { // Typical Transport Parameters consume around 110 bytes, depending on the exact values, // especially the lengths of the Connection IDs. // Allocate 256 bytes, so we won't have to grow the slice in any case. b := make([]byte, 0, 256) // add a greased value random := make([]byte, 18) rand.Read(random) b = quicvarint.Append(b, 27+31*uint64(random[0])) length := random[1] % 16 b = quicvarint.Append(b, uint64(length)) b = append(b, random[2:2+length]...) // initial_max_stream_data_bidi_local b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) // initial_max_stream_data_bidi_remote b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) // initial_max_stream_data_uni b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) // initial_max_data b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) // initial_max_bidi_streams b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) // initial_max_uni_streams b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) // idle_timeout b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond)) // max_udp_payload_size if p.MaxUDPPayloadSize > 0 { b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(p.MaxUDPPayloadSize)) } // max_ack_delay // Only send it if is different from the default value. if p.MaxAckDelay != protocol.DefaultMaxAckDelay { b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond)) } // ack_delay_exponent // Only send it if is different from the default value. if p.AckDelayExponent != protocol.DefaultAckDelayExponent { b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent)) } // disable_active_migration if p.DisableActiveMigration { b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID)) b = quicvarint.Append(b, 0) } if pers == protocol.PerspectiveServer { // stateless_reset_token if p.StatelessResetToken != nil { b = quicvarint.Append(b, uint64(statelessResetTokenParameterID)) b = quicvarint.Append(b, 16) b = append(b, p.StatelessResetToken[:]...) } // original_destination_connection_id b = quicvarint.Append(b, uint64(originalDestinationConnectionIDParameterID)) b = quicvarint.Append(b, uint64(p.OriginalDestinationConnectionID.Len())) b = append(b, p.OriginalDestinationConnectionID.Bytes()...) // preferred_address if p.PreferredAddress != nil { b = quicvarint.Append(b, uint64(preferredAddressParameterID)) b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16) ip4 := p.PreferredAddress.IPv4.Addr().As4() b = append(b, ip4[:]...) b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv4.Port()) ip6 := p.PreferredAddress.IPv6.Addr().As16() b = append(b, ip6[:]...) b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv6.Port()) b = append(b, uint8(p.PreferredAddress.ConnectionID.Len())) b = append(b, p.PreferredAddress.ConnectionID.Bytes()...) b = append(b, p.PreferredAddress.StatelessResetToken[:]...) } } // active_connection_id_limit if p.ActiveConnectionIDLimit != protocol.DefaultActiveConnectionIDLimit { b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) } // initial_source_connection_id b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len())) b = append(b, p.InitialSourceConnectionID.Bytes()...) // retry_source_connection_id if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil { b = quicvarint.Append(b, uint64(retrySourceConnectionIDParameterID)) b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len())) b = append(b, p.RetrySourceConnectionID.Bytes()...) } if p.MaxDatagramFrameSize != protocol.InvalidByteCount { b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) } if pers == protocol.PerspectiveClient && len(AdditionalTransportParametersClient) > 0 { for k, v := range AdditionalTransportParametersClient { b = quicvarint.Append(b, k) b = quicvarint.Append(b, uint64(len(v))) b = append(b, v...) } } return b } func (p *TransportParameters) marshalVarintParam(b []byte, id transportParameterID, val uint64) []byte { b = quicvarint.Append(b, uint64(id)) b = quicvarint.Append(b, uint64(quicvarint.Len(val))) return quicvarint.Append(b, val) } // MarshalForSessionTicket marshals the transport parameters we save in the session ticket. // When sending a 0-RTT enabled TLS session tickets, we need to save the transport parameters. // The client will remember the transport parameters used in the last session, // and apply those to the 0-RTT data it sends. // Saving the transport parameters in the ticket gives the server the option to reject 0-RTT // if the transport parameters changed. // Since the session ticket is encrypted, the serialization format is defined by the server. // For convenience, we use the same format that we also use for sending the transport parameters. func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte { b = quicvarint.Append(b, transportParameterMarshalingVersion) // initial_max_stream_data_bidi_local b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) // initial_max_stream_data_bidi_remote b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) // initial_max_stream_data_uni b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) // initial_max_data b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) // initial_max_bidi_streams b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) // initial_max_uni_streams b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) // max_datagram_frame_size if p.MaxDatagramFrameSize != protocol.InvalidByteCount { b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) } // active_connection_id_limit return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) } // UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket. func (p *TransportParameters) UnmarshalFromSessionTicket(b []byte) error { version, l, err := quicvarint.Parse(b) if err != nil { return err } if version != transportParameterMarshalingVersion { return fmt.Errorf("unknown transport parameter marshaling version: %d", version) } return p.unmarshal(b[l:], protocol.PerspectiveServer, true) } // ValidFor0RTT checks if the transport parameters match those saved in the session ticket. func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { return false } return p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && p.InitialMaxData >= saved.InitialMaxData && p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && p.MaxUniStreamNum >= saved.MaxUniStreamNum && p.ActiveConnectionIDLimit == saved.ActiveConnectionIDLimit } // ValidForUpdate checks that the new transport parameters don't reduce limits after resuming a 0-RTT connection. // It is only used on the client side. func (p *TransportParameters) ValidForUpdate(saved *TransportParameters) bool { if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { return false } return p.ActiveConnectionIDLimit >= saved.ActiveConnectionIDLimit && p.InitialMaxData >= saved.InitialMaxData && p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && p.MaxUniStreamNum >= saved.MaxUniStreamNum } // String returns a string representation, intended for logging. func (p *TransportParameters) String() string { logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, " logParams := []interface{}{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID} if p.RetrySourceConnectionID != nil { logString += "RetrySourceConnectionID: %s, " logParams = append(logParams, p.RetrySourceConnectionID) } logString += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d" logParams = append(logParams, []interface{}{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...) if p.StatelessResetToken != nil { // the client never sends a stateless reset token logString += ", StatelessResetToken: %#x" logParams = append(logParams, *p.StatelessResetToken) } if p.MaxDatagramFrameSize != protocol.InvalidByteCount { logString += ", MaxDatagramFrameSize: %d" logParams = append(logParams, p.MaxDatagramFrameSize) } logString += "}" return fmt.Sprintf(logString, logParams...) } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/version_negotiation.go000066400000000000000000000041151465664453100300060ustar00rootroot00000000000000package wire import ( "crypto/rand" "encoding/binary" "errors" "github.com/quic-go/quic-go/internal/protocol" ) // ParseVersionNegotiationPacket parses a Version Negotiation packet. func ParseVersionNegotiationPacket(b []byte) (dest, src protocol.ArbitraryLenConnectionID, _ []protocol.Version, _ error) { n, dest, src, err := ParseArbitraryLenConnectionIDs(b) if err != nil { return nil, nil, nil, err } b = b[n:] if len(b) == 0 { //nolint:stylecheck return nil, nil, nil, errors.New("Version Negotiation packet has empty version list") } if len(b)%4 != 0 { //nolint:stylecheck return nil, nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length") } versions := make([]protocol.Version, len(b)/4) for i := 0; len(b) > 0; i++ { versions[i] = protocol.Version(binary.BigEndian.Uint32(b[:4])) b = b[4:] } return dest, src, versions, nil } // ComposeVersionNegotiation composes a Version Negotiation func ComposeVersionNegotiation(destConnID, srcConnID protocol.ArbitraryLenConnectionID, versions []protocol.Version) []byte { greasedVersions := protocol.GetGreasedVersions(versions) expectedLen := 1 /* type byte */ + 4 /* version field */ + 1 /* dest connection ID length field */ + destConnID.Len() + 1 /* src connection ID length field */ + srcConnID.Len() + len(greasedVersions)*4 buf := make([]byte, 1+4 /* type byte and version field */, expectedLen) _, _ = rand.Read(buf[:1]) // ignore the error here. It is not critical to have perfect random here. // Setting the "QUIC bit" (0x40) is not required by the RFC, // but it allows clients to demultiplex QUIC with a long list of other protocols. // See RFC 9443 and https://mailarchive.ietf.org/arch/msg/quic/oR4kxGKY6mjtPC1CZegY1ED4beg/ for details. buf[0] |= 0xc0 // The next 4 bytes are left at 0 (version number). buf = append(buf, uint8(destConnID.Len())) buf = append(buf, destConnID.Bytes()...) buf = append(buf, uint8(srcConnID.Len())) buf = append(buf, srcConnID.Bytes()...) for _, v := range greasedVersions { buf = binary.BigEndian.AppendUint32(buf, uint32(v)) } return buf } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/version_negotiation_test.go000066400000000000000000000075331465664453100310540ustar00rootroot00000000000000package wire import ( "encoding/binary" "testing" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/protocol" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Version Negotiation Packets", func() { randConnID := func(l int) protocol.ArbitraryLenConnectionID { b := make(protocol.ArbitraryLenConnectionID, l) _, err := rand.Read(b) Expect(err).ToNot(HaveOccurred()) return b } It("parses a Version Negotiation packet", func() { srcConnID := randConnID(rand.Intn(255) + 1) destConnID := randConnID(rand.Intn(255) + 1) versions := []protocol.Version{0x22334455, 0x33445566} data := []byte{0x80, 0, 0, 0, 0} data = append(data, uint8(len(destConnID))) data = append(data, destConnID...) data = append(data, uint8(len(srcConnID))) data = append(data, srcConnID...) for _, v := range versions { data = append(data, []byte{0, 0, 0, 0}...) binary.BigEndian.PutUint32(data[len(data)-4:], uint32(v)) } Expect(IsVersionNegotiationPacket(data)).To(BeTrue()) dest, src, supportedVersions, err := ParseVersionNegotiationPacket(data) Expect(err).ToNot(HaveOccurred()) Expect(dest).To(Equal(destConnID)) Expect(src).To(Equal(srcConnID)) Expect(supportedVersions).To(Equal(versions)) }) It("errors if it contains versions of the wrong length", func() { connID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8} versions := []protocol.Version{0x22334455, 0x33445566} data := ComposeVersionNegotiation(connID, connID, versions) _, _, _, err := ParseVersionNegotiationPacket(data[:len(data)-2]) Expect(err).To(MatchError("Version Negotiation packet has a version list with an invalid length")) }) It("errors if the version list is empty", func() { connID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8} versions := []protocol.Version{0x22334455} data := ComposeVersionNegotiation(connID, connID, versions) // remove 8 bytes (two versions), since ComposeVersionNegotiation also added a reserved version number data = data[:len(data)-8] _, _, _, err := ParseVersionNegotiationPacket(data) Expect(err).To(MatchError("Version Negotiation packet has empty version list")) }) It("adds a reserved version", func() { srcConnID := protocol.ArbitraryLenConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37} destConnID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8} versions := []protocol.Version{1001, 1003} data := ComposeVersionNegotiation(destConnID, srcConnID, versions) Expect(IsLongHeaderPacket(data[0])).To(BeTrue()) Expect(data[0] & 0x40).ToNot(BeZero()) v, err := ParseVersion(data) Expect(err).ToNot(HaveOccurred()) Expect(v).To(BeZero()) dest, src, supportedVersions, err := ParseVersionNegotiationPacket(data) Expect(err).ToNot(HaveOccurred()) Expect(dest).To(Equal(destConnID)) Expect(src).To(Equal(srcConnID)) // the supported versions should include one reserved version number Expect(supportedVersions).To(HaveLen(len(versions) + 1)) for _, v := range versions { Expect(supportedVersions).To(ContainElement(v)) } var reservedVersion protocol.Version versionLoop: for _, ver := range supportedVersions { for _, v := range versions { if v == ver { continue versionLoop } } reservedVersion = ver } Expect(reservedVersion).ToNot(BeZero()) Expect(reservedVersion&0x0f0f0f0f == 0x0a0a0a0a).To(BeTrue()) // check that it's a greased version number }) }) func BenchmarkComposeVersionNegotiationPacket(b *testing.B) { b.ReportAllocs() supportedVersions := []protocol.Version{protocol.Version2, protocol.Version1, 0x1337} destConnID := protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0xa, 0xb, 0xc, 0xd} srcConnID := protocol.ArbitraryLenConnectionID{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} for i := 0; i < b.N; i++ { ComposeVersionNegotiation(destConnID, srcConnID, supportedVersions) } } golang-github-lucas-clemente-quic-go-0.46.0/internal/wire/wire_suite_test.go000066400000000000000000000010621465664453100271350ustar00rootroot00000000000000package wire import ( "encoding/binary" "testing" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/quicvarint" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestWire(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Wire Suite") } func encodeVarInt(i uint64) []byte { return quicvarint.Append(nil, i) } func appendVersion(data []byte, v protocol.Version) []byte { offset := len(data) data = append(data, []byte{0, 0, 0, 0}...) binary.BigEndian.PutUint32(data[offset:], uint32(v)) return data } golang-github-lucas-clemente-quic-go-0.46.0/interop/000077500000000000000000000000001465664453100222675ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/interop/Dockerfile000066400000000000000000000024211465664453100242600ustar00rootroot00000000000000FROM martenseemann/quic-network-simulator-endpoint:latest AS builder ARG TARGETPLATFORM RUN echo "TARGETPLATFORM: ${TARGETPLATFORM}" RUN apt-get update && apt-get install -y wget tar git ENV GOVERSION=1.22.0 RUN platform=$(echo ${TARGETPLATFORM} | tr '/' '-') && \ filename="go${GOVERSION}.${platform}.tar.gz" && \ wget https://dl.google.com/go/${filename} && \ tar xfz ${filename} && \ rm ${filename} ENV PATH="/go/bin:${PATH}" # build with --build-arg CACHEBUST=$(date +%s) ARG CACHEBUST=1 # build other branches / commits / tags using --build-arg GITREF="" ARG GITREF="master" RUN git clone https://github.com/quic-go/quic-go WORKDIR /quic-go RUN git checkout ${GITREF} RUN go get ./... RUN git rev-parse HEAD | tee commit.txt RUN go build -o server -ldflags="-X github.com/quic-go/quic-go/qlog.quicGoVersion=$(git describe --always --long --dirty)" interop/server/main.go RUN go build -o client -ldflags="-X github.com/quic-go/quic-go/qlog.quicGoVersion=$(git describe --always --long --dirty)" interop/client/main.go FROM martenseemann/quic-network-simulator-endpoint:latest WORKDIR /quic-go COPY --from=builder /quic-go/commit.txt /quic-go/server /quic-go/client ./ COPY run_endpoint.sh . RUN chmod +x run_endpoint.sh ENTRYPOINT [ "./run_endpoint.sh" ] golang-github-lucas-clemente-quic-go-0.46.0/interop/client/000077500000000000000000000000001465664453100235455ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/interop/client/main.go000066400000000000000000000113421465664453100250210ustar00rootroot00000000000000package main import ( "crypto/tls" "errors" "flag" "fmt" "io" "log" "net/http" "os" "strings" "time" "golang.org/x/sync/errgroup" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/interop/http09" "github.com/quic-go/quic-go/interop/utils" ) var errUnsupported = errors.New("unsupported test case") var tlsConf *tls.Config func main() { logFile, err := os.Create("/logs/log.txt") if err != nil { fmt.Printf("Could not create log file: %s\n", err.Error()) os.Exit(1) } defer logFile.Close() log.SetOutput(logFile) keyLog, err := utils.GetSSLKeyLog() if err != nil { fmt.Printf("Could not create key log: %s\n", err.Error()) os.Exit(1) } if keyLog != nil { defer keyLog.Close() } tlsConf = &tls.Config{ InsecureSkipVerify: true, KeyLogWriter: keyLog, } testcase := os.Getenv("TESTCASE") if err := runTestcase(testcase); err != nil { if err == errUnsupported { fmt.Printf("unsupported test case: %s\n", testcase) os.Exit(127) } fmt.Printf("Downloading files failed: %s\n", err.Error()) os.Exit(1) } } func runTestcase(testcase string) error { flag.Parse() urls := flag.Args() quicConf := &quic.Config{Tracer: utils.NewQLOGConnectionTracer} if testcase == "http3" { r := &http3.RoundTripper{ TLSClientConfig: tlsConf, QUICConfig: quicConf, } defer r.Close() return downloadFiles(r, urls, false) } r := &http09.RoundTripper{ TLSClientConfig: tlsConf, QuicConfig: quicConf, } defer r.Close() switch testcase { case "handshake", "transfer", "retry": case "keyupdate": handshake.FirstKeyUpdateInterval = 100 case "chacha20": reset := qtls.SetCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256) defer reset() case "multiconnect": return runMultiConnectTest(r, urls) case "versionnegotiation": return runVersionNegotiationTest(r, urls) case "resumption": return runResumptionTest(r, urls, false) case "zerortt": return runResumptionTest(r, urls, true) default: return errUnsupported } return downloadFiles(r, urls, false) } func runVersionNegotiationTest(r *http09.RoundTripper, urls []string) error { if len(urls) != 1 { return errors.New("expected at least 2 URLs") } protocol.SupportedVersions = []protocol.Version{0x1a2a3a4a} err := downloadFile(r, urls[0], false) if err == nil { return errors.New("expected version negotiation to fail") } if !strings.Contains(err.Error(), "No compatible QUIC version found") { return fmt.Errorf("expect version negotiation error, got: %s", err.Error()) } return nil } func runMultiConnectTest(r *http09.RoundTripper, urls []string) error { for _, url := range urls { if err := downloadFile(r, url, false); err != nil { return err } if err := r.Close(); err != nil { return err } } return nil } type sessionCache struct { tls.ClientSessionCache put chan<- struct{} } func newSessionCache(c tls.ClientSessionCache) (tls.ClientSessionCache, <-chan struct{}) { put := make(chan struct{}, 100) return &sessionCache{ClientSessionCache: c, put: put}, put } func (c *sessionCache) Put(key string, cs *tls.ClientSessionState) { c.ClientSessionCache.Put(key, cs) c.put <- struct{}{} } func runResumptionTest(r *http09.RoundTripper, urls []string, use0RTT bool) error { if len(urls) < 2 { return errors.New("expected at least 2 URLs") } var put <-chan struct{} tlsConf.ClientSessionCache, put = newSessionCache(tls.NewLRUClientSessionCache(1)) // do the first transfer if err := downloadFiles(r, urls[:1], false); err != nil { return err } // wait for the session ticket to arrive select { case <-time.NewTimer(10 * time.Second).C: return errors.New("expected to receive a session ticket within 10 seconds") case <-put: } if err := r.Close(); err != nil { return err } // reestablish the connection, using the session ticket that the server (hopefully provided) defer r.Close() return downloadFiles(r, urls[1:], use0RTT) } func downloadFiles(cl http.RoundTripper, urls []string, use0RTT bool) error { var g errgroup.Group for _, u := range urls { url := u g.Go(func() error { return downloadFile(cl, url, use0RTT) }) } return g.Wait() } func downloadFile(cl http.RoundTripper, url string, use0RTT bool) error { method := http.MethodGet if use0RTT { method = http09.MethodGet0RTT } req, err := http.NewRequest(method, url, nil) if err != nil { return err } rsp, err := cl.RoundTrip(req) if err != nil { return err } defer rsp.Body.Close() file, err := os.Create("/downloads" + req.URL.Path) if err != nil { return err } defer file.Close() _, err = io.Copy(file, rsp.Body) return err } golang-github-lucas-clemente-quic-go-0.46.0/interop/http09/000077500000000000000000000000001465664453100234175ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/interop/http09/client.go000066400000000000000000000065731465664453100252370ustar00rootroot00000000000000package http09 import ( "context" "crypto/tls" "errors" "io" "log" "net" "net/http" "strings" "sync" "golang.org/x/net/idna" "github.com/quic-go/quic-go" ) // MethodGet0RTT allows a GET request to be sent using 0-RTT. // Note that 0-RTT data doesn't provide replay protection. const MethodGet0RTT = "GET_0RTT" // RoundTripper performs HTTP/0.9 roundtrips over QUIC. type RoundTripper struct { mutex sync.Mutex TLSClientConfig *tls.Config QuicConfig *quic.Config clients map[string]*client } var _ http.RoundTripper = &RoundTripper{} // RoundTrip performs a HTTP/0.9 request. // It only supports GET requests. func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { if req.Method != http.MethodGet && req.Method != MethodGet0RTT { return nil, errors.New("only GET requests supported") } log.Printf("Requesting %s.\n", req.URL) r.mutex.Lock() hostname := authorityAddr("https", hostnameFromRequest(req)) if r.clients == nil { r.clients = make(map[string]*client) } c, ok := r.clients[hostname] if !ok { tlsConf := &tls.Config{} if r.TLSClientConfig != nil { tlsConf = r.TLSClientConfig.Clone() } tlsConf.NextProtos = []string{h09alpn} c = &client{ hostname: hostname, tlsConf: tlsConf, quicConf: r.QuicConfig, } r.clients[hostname] = c } r.mutex.Unlock() return c.RoundTrip(req) } // Close closes the roundtripper. func (r *RoundTripper) Close() error { r.mutex.Lock() defer r.mutex.Unlock() for id, c := range r.clients { if err := c.Close(); err != nil { return err } delete(r.clients, id) } return nil } type client struct { hostname string tlsConf *tls.Config quicConf *quic.Config once sync.Once conn quic.EarlyConnection dialErr error } func (c *client) RoundTrip(req *http.Request) (*http.Response, error) { c.once.Do(func() { c.conn, c.dialErr = quic.DialAddrEarly(context.Background(), c.hostname, c.tlsConf, c.quicConf) }) if c.dialErr != nil { return nil, c.dialErr } if req.Method != MethodGet0RTT { <-c.conn.HandshakeComplete() } return c.doRequest(req) } func (c *client) doRequest(req *http.Request) (*http.Response, error) { str, err := c.conn.OpenStreamSync(context.Background()) if err != nil { return nil, err } cmd := "GET " + req.URL.Path + "\r\n" if _, err := str.Write([]byte(cmd)); err != nil { return nil, err } if err := str.Close(); err != nil { return nil, err } rsp := &http.Response{ Proto: "HTTP/0.9", ProtoMajor: 0, ProtoMinor: 9, Request: req, Body: io.NopCloser(str), } return rsp, nil } func (c *client) Close() error { if c.conn == nil { return nil } return c.conn.CloseWithError(0, "") } func hostnameFromRequest(req *http.Request) string { if req.URL != nil { return req.URL.Host } return "" } // authorityAddr returns a given authority (a host/IP, or host:port / ip:port) // and returns a host:port. The port 443 is added if needed. func authorityAddr(scheme string, authority string) (addr string) { host, port, err := net.SplitHostPort(authority) if err != nil { // authority didn't have a port port = "443" if scheme == "http" { port = "80" } host = authority } if a, err := idna.ToASCII(host); err == nil { host = a } // IPv6 address literal, without a port: if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") { return host + ":" + port } return net.JoinHostPort(host, port) } golang-github-lucas-clemente-quic-go-0.46.0/interop/http09/http09_suite_test.go000066400000000000000000000002771465664453100273540ustar00rootroot00000000000000package http09 import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) func TestHttp09(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "HTTP/0.9 Suite") } golang-github-lucas-clemente-quic-go-0.46.0/interop/http09/http_test.go000066400000000000000000000041501465664453100257640ustar00rootroot00000000000000package http09 import ( "crypto/tls" "fmt" "io" "net" "net/http" "net/http/httptest" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/testdata" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("HTTP 0.9 integration tests", func() { var ( server *Server saddr net.Addr done chan struct{} ) http.HandleFunc("/helloworld", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte("Hello World!")) }) BeforeEach(func() { server = &Server{ Server: &http.Server{TLSConfig: testdata.GetTLSConfig()}, } done = make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) _ = server.ListenAndServe() }() var ln *quic.EarlyListener Eventually(func() *quic.EarlyListener { server.mutex.Lock() defer server.mutex.Unlock() ln = server.listener return server.listener }, 5*time.Second).ShouldNot(BeNil()) saddr = ln.Addr() saddr.(*net.UDPAddr).IP = net.IP{127, 0, 0, 1} }) AfterEach(func() { Expect(server.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("performs request", func() { rt := &RoundTripper{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} defer rt.Close() req := httptest.NewRequest( http.MethodGet, fmt.Sprintf("https://%s/helloworld", saddr), nil, ) rsp, err := rt.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("Hello World!"))) }) It("allows setting of headers", func() { http.HandleFunc("/headers", func(w http.ResponseWriter, r *http.Request) { w.Header().Add("foo", "bar") w.WriteHeader(1337) _, _ = w.Write([]byte("done")) }) rt := &RoundTripper{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} defer rt.Close() req := httptest.NewRequest( http.MethodGet, fmt.Sprintf("https://%s/headers", saddr), nil, ) rsp, err := rt.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("done"))) }) }) golang-github-lucas-clemente-quic-go-0.46.0/interop/http09/server.go000066400000000000000000000060141465664453100252550ustar00rootroot00000000000000package http09 import ( "context" "errors" "io" "log" "net" "net/http" "net/url" "runtime" "strings" "sync" "github.com/quic-go/quic-go" ) const h09alpn = "hq-interop" type responseWriter struct { io.Writer headers http.Header } var _ http.ResponseWriter = &responseWriter{} func (w *responseWriter) Header() http.Header { if w.headers == nil { w.headers = make(http.Header) } return w.headers } func (w *responseWriter) WriteHeader(int) {} // Server is a HTTP/0.9 server listening for QUIC connections. type Server struct { *http.Server ForceRetry bool QuicConfig *quic.Config mutex sync.Mutex listener *quic.EarlyListener } // Close closes the server. func (s *Server) Close() error { s.mutex.Lock() defer s.mutex.Unlock() return s.listener.Close() } // ListenAndServe listens and serves HTTP/0.9 over QUIC. func (s *Server) ListenAndServe() error { if s.Server == nil { return errors.New("use of http3.Server without http.Server") } udpAddr, err := net.ResolveUDPAddr("udp", s.Addr) if err != nil { return err } conn, err := net.ListenUDP("udp", udpAddr) if err != nil { return err } tlsConf := s.TLSConfig.Clone() tlsConf.NextProtos = []string{h09alpn} tr := quic.Transport{Conn: conn} if s.ForceRetry { tr.VerifySourceAddress = func(net.Addr) bool { return true } } ln, err := tr.ListenEarly(tlsConf, s.QuicConfig) if err != nil { return err } s.mutex.Lock() s.listener = ln s.mutex.Unlock() for { conn, err := ln.Accept(context.Background()) if err != nil { return err } go s.handleConn(conn) } } func (s *Server) handleConn(conn quic.Connection) { for { str, err := conn.AcceptStream(context.Background()) if err != nil { log.Printf("Error accepting stream: %s\n", err.Error()) return } go func() { if err := s.handleStream(str); err != nil { log.Printf("Handling stream failed: %s\n", err.Error()) } }() } } func (s *Server) handleStream(str quic.Stream) error { reqBytes, err := io.ReadAll(str) if err != nil { return err } request := string(reqBytes) request = strings.TrimRight(request, "\r\n") request = strings.TrimRight(request, " ") log.Printf("Received request: %s\n", request) if request[:5] != "GET /" { str.CancelWrite(42) return nil } u, err := url.Parse(request[4:]) if err != nil { return err } u.Scheme = "https" req := &http.Request{ Method: http.MethodGet, Proto: "HTTP/0.9", ProtoMajor: 0, ProtoMinor: 9, Body: str, URL: u, } handler := s.Handler if handler == nil { handler = http.DefaultServeMux } var panicked bool func() { defer func() { if p := recover(); p != nil { // Copied from net/http/server.go const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] log.Printf("http: panic serving: %v\n%s", p, buf) panicked = true } }() handler.ServeHTTP(&responseWriter{Writer: str}, req) }() if panicked { if _, err := str.Write([]byte("500")); err != nil { return err } } return str.Close() } golang-github-lucas-clemente-quic-go-0.46.0/interop/run_endpoint.sh000066400000000000000000000007441465664453100253340ustar00rootroot00000000000000#!/bin/bash set -e # Set up the routing needed for the simulation. /setup.sh echo "Using commit:" `cat commit.txt` if [ "$ROLE" == "client" ]; then # Wait for the simulator to start up. /wait-for-it.sh sim:57832 -s -t 10 echo "Starting QUIC client..." echo "Client params: $CLIENT_PARAMS" echo "Test case: $TESTCASE" QUIC_GO_LOG_LEVEL=debug ./client $CLIENT_PARAMS $REQUESTS else echo "Running QUIC server." QUIC_GO_LOG_LEVEL=debug ./server "$@" fi golang-github-lucas-clemente-quic-go-0.46.0/interop/server/000077500000000000000000000000001465664453100235755ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/interop/server/main.go000066400000000000000000000041521465664453100250520ustar00rootroot00000000000000package main import ( "crypto/tls" "fmt" "log" "net/http" "os" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/internal/qtls" "github.com/quic-go/quic-go/interop/http09" "github.com/quic-go/quic-go/interop/utils" ) var tlsConf *tls.Config func main() { logFile, err := os.Create("/logs/log.txt") if err != nil { fmt.Printf("Could not create log file: %s\n", err.Error()) os.Exit(1) } defer logFile.Close() log.SetOutput(logFile) keyLog, err := utils.GetSSLKeyLog() if err != nil { fmt.Printf("Could not create key log: %s\n", err.Error()) os.Exit(1) } if keyLog != nil { defer keyLog.Close() } testcase := os.Getenv("TESTCASE") quicConf := &quic.Config{ Allow0RTT: testcase == "zerortt", Tracer: utils.NewQLOGConnectionTracer, } cert, err := tls.LoadX509KeyPair("/certs/cert.pem", "/certs/priv.key") if err != nil { fmt.Println(err) os.Exit(1) } tlsConf = &tls.Config{ Certificates: []tls.Certificate{cert}, KeyLogWriter: keyLog, } switch testcase { case "versionnegotiation", "handshake", "retry", "transfer", "resumption", "multiconnect", "zerortt": err = runHTTP09Server(quicConf, testcase == "retry") case "chacha20": reset := qtls.SetCipherSuite(tls.TLS_CHACHA20_POLY1305_SHA256) defer reset() err = runHTTP09Server(quicConf, false) case "http3": err = runHTTP3Server(quicConf) default: fmt.Printf("unsupported test case: %s\n", testcase) os.Exit(127) } if err != nil { fmt.Printf("Error running server: %s\n", err.Error()) os.Exit(1) } } func runHTTP09Server(quicConf *quic.Config, forceRetry bool) error { server := http09.Server{ Server: &http.Server{ Addr: ":443", TLSConfig: tlsConf, }, ForceRetry: forceRetry, QuicConfig: quicConf, } http.DefaultServeMux.Handle("/", http.FileServer(http.Dir("/www"))) return server.ListenAndServe() } func runHTTP3Server(quicConf *quic.Config) error { server := http3.Server{ Addr: ":443", TLSConfig: tlsConf, QUICConfig: quicConf, } http.DefaultServeMux.Handle("/", http.FileServer(http.Dir("/www"))) return server.ListenAndServe() } golang-github-lucas-clemente-quic-go-0.46.0/interop/utils/000077500000000000000000000000001465664453100234275ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/interop/utils/logging.go000066400000000000000000000024061465664453100254060ustar00rootroot00000000000000package utils import ( "bufio" "context" "fmt" "io" "log" "os" "strings" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" "github.com/quic-go/quic-go/qlog" ) // GetSSLKeyLog creates a file for the TLS key log func GetSSLKeyLog() (io.WriteCloser, error) { filename := os.Getenv("SSLKEYLOGFILE") if len(filename) == 0 { return nil, nil } f, err := os.Create(filename) if err != nil { return nil, err } return f, nil } // NewQLOGConnectionTracer create a qlog file in QLOGDIR func NewQLOGConnectionTracer(_ context.Context, p logging.Perspective, connID quic.ConnectionID) *logging.ConnectionTracer { qlogDir := os.Getenv("QLOGDIR") if len(qlogDir) == 0 { return nil } if _, err := os.Stat(qlogDir); os.IsNotExist(err) { if err := os.MkdirAll(qlogDir, 0o666); err != nil { log.Fatalf("failed to create qlog dir %s: %v", qlogDir, err) } } path := fmt.Sprintf("%s/%s.sqlog", strings.TrimRight(qlogDir, "/"), connID) f, err := os.Create(path) if err != nil { log.Printf("Failed to create qlog file %s: %s", path, err.Error()) return nil } log.Printf("Created qlog file: %s\n", path) return qlog.NewConnectionTracer(utils.NewBufferedWriteCloser(bufio.NewWriter(f), f), p, connID) } golang-github-lucas-clemente-quic-go-0.46.0/logging/000077500000000000000000000000001465664453100222355ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/logging/connection_tracer.go000066400000000000000000000205131465664453100262640ustar00rootroot00000000000000package logging import ( "net" "time" ) // A ConnectionTracer records events. type ConnectionTracer struct { StartedConnection func(local, remote net.Addr, srcConnID, destConnID ConnectionID) NegotiatedVersion func(chosen Version, clientVersions, serverVersions []Version) ClosedConnection func(error) SentTransportParameters func(*TransportParameters) ReceivedTransportParameters func(*TransportParameters) RestoredTransportParameters func(parameters *TransportParameters) // for 0-RTT SentLongHeaderPacket func(*ExtendedHeader, ByteCount, ECN, *AckFrame, []Frame) SentShortHeaderPacket func(*ShortHeader, ByteCount, ECN, *AckFrame, []Frame) ReceivedVersionNegotiationPacket func(dest, src ArbitraryLenConnectionID, _ []Version) ReceivedRetry func(*Header) ReceivedLongHeaderPacket func(*ExtendedHeader, ByteCount, ECN, []Frame) ReceivedShortHeaderPacket func(*ShortHeader, ByteCount, ECN, []Frame) BufferedPacket func(PacketType, ByteCount) DroppedPacket func(PacketType, PacketNumber, ByteCount, PacketDropReason) UpdatedMetrics func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) AcknowledgedPacket func(EncryptionLevel, PacketNumber) LostPacket func(EncryptionLevel, PacketNumber, PacketLossReason) UpdatedMTU func(mtu ByteCount, done bool) UpdatedCongestionState func(CongestionState) UpdatedPTOCount func(value uint32) UpdatedKeyFromTLS func(EncryptionLevel, Perspective) UpdatedKey func(keyPhase KeyPhase, remote bool) DroppedEncryptionLevel func(EncryptionLevel) DroppedKey func(keyPhase KeyPhase) SetLossTimer func(TimerType, EncryptionLevel, time.Time) LossTimerExpired func(TimerType, EncryptionLevel) LossTimerCanceled func() ECNStateUpdated func(state ECNState, trigger ECNStateTrigger) ChoseALPN func(protocol string) // Close is called when the connection is closed. Close func() Debug func(name, msg string) } // NewMultiplexedConnectionTracer creates a new connection tracer that multiplexes events to multiple tracers. func NewMultiplexedConnectionTracer(tracers ...*ConnectionTracer) *ConnectionTracer { if len(tracers) == 0 { return nil } if len(tracers) == 1 { return tracers[0] } return &ConnectionTracer{ StartedConnection: func(local, remote net.Addr, srcConnID, destConnID ConnectionID) { for _, t := range tracers { if t.StartedConnection != nil { t.StartedConnection(local, remote, srcConnID, destConnID) } } }, NegotiatedVersion: func(chosen Version, clientVersions, serverVersions []Version) { for _, t := range tracers { if t.NegotiatedVersion != nil { t.NegotiatedVersion(chosen, clientVersions, serverVersions) } } }, ClosedConnection: func(e error) { for _, t := range tracers { if t.ClosedConnection != nil { t.ClosedConnection(e) } } }, SentTransportParameters: func(tp *TransportParameters) { for _, t := range tracers { if t.SentTransportParameters != nil { t.SentTransportParameters(tp) } } }, ReceivedTransportParameters: func(tp *TransportParameters) { for _, t := range tracers { if t.ReceivedTransportParameters != nil { t.ReceivedTransportParameters(tp) } } }, RestoredTransportParameters: func(tp *TransportParameters) { for _, t := range tracers { if t.RestoredTransportParameters != nil { t.RestoredTransportParameters(tp) } } }, SentLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { for _, t := range tracers { if t.SentLongHeaderPacket != nil { t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) } } }, SentShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, ack *AckFrame, frames []Frame) { for _, t := range tracers { if t.SentShortHeaderPacket != nil { t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) } } }, ReceivedVersionNegotiationPacket: func(dest, src ArbitraryLenConnectionID, versions []Version) { for _, t := range tracers { if t.ReceivedVersionNegotiationPacket != nil { t.ReceivedVersionNegotiationPacket(dest, src, versions) } } }, ReceivedRetry: func(hdr *Header) { for _, t := range tracers { if t.ReceivedRetry != nil { t.ReceivedRetry(hdr) } } }, ReceivedLongHeaderPacket: func(hdr *ExtendedHeader, size ByteCount, ecn ECN, frames []Frame) { for _, t := range tracers { if t.ReceivedLongHeaderPacket != nil { t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) } } }, ReceivedShortHeaderPacket: func(hdr *ShortHeader, size ByteCount, ecn ECN, frames []Frame) { for _, t := range tracers { if t.ReceivedShortHeaderPacket != nil { t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) } } }, BufferedPacket: func(typ PacketType, size ByteCount) { for _, t := range tracers { if t.BufferedPacket != nil { t.BufferedPacket(typ, size) } } }, DroppedPacket: func(typ PacketType, pn PacketNumber, size ByteCount, reason PacketDropReason) { for _, t := range tracers { if t.DroppedPacket != nil { t.DroppedPacket(typ, pn, size, reason) } } }, UpdatedMetrics: func(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) { for _, t := range tracers { if t.UpdatedMetrics != nil { t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) } } }, AcknowledgedPacket: func(encLevel EncryptionLevel, pn PacketNumber) { for _, t := range tracers { if t.AcknowledgedPacket != nil { t.AcknowledgedPacket(encLevel, pn) } } }, LostPacket: func(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) { for _, t := range tracers { if t.LostPacket != nil { t.LostPacket(encLevel, pn, reason) } } }, UpdatedMTU: func(mtu ByteCount, done bool) { for _, t := range tracers { if t.UpdatedMTU != nil { t.UpdatedMTU(mtu, done) } } }, UpdatedCongestionState: func(state CongestionState) { for _, t := range tracers { if t.UpdatedCongestionState != nil { t.UpdatedCongestionState(state) } } }, UpdatedPTOCount: func(value uint32) { for _, t := range tracers { if t.UpdatedPTOCount != nil { t.UpdatedPTOCount(value) } } }, UpdatedKeyFromTLS: func(encLevel EncryptionLevel, perspective Perspective) { for _, t := range tracers { if t.UpdatedKeyFromTLS != nil { t.UpdatedKeyFromTLS(encLevel, perspective) } } }, UpdatedKey: func(generation KeyPhase, remote bool) { for _, t := range tracers { if t.UpdatedKey != nil { t.UpdatedKey(generation, remote) } } }, DroppedEncryptionLevel: func(encLevel EncryptionLevel) { for _, t := range tracers { if t.DroppedEncryptionLevel != nil { t.DroppedEncryptionLevel(encLevel) } } }, DroppedKey: func(generation KeyPhase) { for _, t := range tracers { if t.DroppedKey != nil { t.DroppedKey(generation) } } }, SetLossTimer: func(typ TimerType, encLevel EncryptionLevel, exp time.Time) { for _, t := range tracers { if t.SetLossTimer != nil { t.SetLossTimer(typ, encLevel, exp) } } }, LossTimerExpired: func(typ TimerType, encLevel EncryptionLevel) { for _, t := range tracers { if t.LossTimerExpired != nil { t.LossTimerExpired(typ, encLevel) } } }, LossTimerCanceled: func() { for _, t := range tracers { if t.LossTimerCanceled != nil { t.LossTimerCanceled() } } }, ECNStateUpdated: func(state ECNState, trigger ECNStateTrigger) { for _, t := range tracers { if t.ECNStateUpdated != nil { t.ECNStateUpdated(state, trigger) } } }, ChoseALPN: func(protocol string) { for _, t := range tracers { if t.ChoseALPN != nil { t.ChoseALPN(protocol) } } }, Close: func() { for _, t := range tracers { if t.Close != nil { t.Close() } } }, Debug: func(name, msg string) { for _, t := range tracers { if t.Debug != nil { t.Debug(name, msg) } } }, } } golang-github-lucas-clemente-quic-go-0.46.0/logging/frame.go000066400000000000000000000041621465664453100236610ustar00rootroot00000000000000package logging import "github.com/quic-go/quic-go/internal/wire" // A Frame is a QUIC frame type Frame interface{} // The AckRange is used within the AckFrame. // It is a range of packet numbers that is being acknowledged. type AckRange = wire.AckRange type ( // An AckFrame is an ACK frame. AckFrame = wire.AckFrame // A ConnectionCloseFrame is a CONNECTION_CLOSE frame. ConnectionCloseFrame = wire.ConnectionCloseFrame // A DataBlockedFrame is a DATA_BLOCKED frame. DataBlockedFrame = wire.DataBlockedFrame // A HandshakeDoneFrame is a HANDSHAKE_DONE frame. HandshakeDoneFrame = wire.HandshakeDoneFrame // A MaxDataFrame is a MAX_DATA frame. MaxDataFrame = wire.MaxDataFrame // A MaxStreamDataFrame is a MAX_STREAM_DATA frame. MaxStreamDataFrame = wire.MaxStreamDataFrame // A MaxStreamsFrame is a MAX_STREAMS_FRAME. MaxStreamsFrame = wire.MaxStreamsFrame // A NewConnectionIDFrame is a NEW_CONNECTION_ID frame. NewConnectionIDFrame = wire.NewConnectionIDFrame // A NewTokenFrame is a NEW_TOKEN frame. NewTokenFrame = wire.NewTokenFrame // A PathChallengeFrame is a PATH_CHALLENGE frame. PathChallengeFrame = wire.PathChallengeFrame // A PathResponseFrame is a PATH_RESPONSE frame. PathResponseFrame = wire.PathResponseFrame // A PingFrame is a PING frame. PingFrame = wire.PingFrame // A ResetStreamFrame is a RESET_STREAM frame. ResetStreamFrame = wire.ResetStreamFrame // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame. RetireConnectionIDFrame = wire.RetireConnectionIDFrame // A StopSendingFrame is a STOP_SENDING frame. StopSendingFrame = wire.StopSendingFrame // A StreamsBlockedFrame is a STREAMS_BLOCKED frame. StreamsBlockedFrame = wire.StreamsBlockedFrame // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame. StreamDataBlockedFrame = wire.StreamDataBlockedFrame ) // A CryptoFrame is a CRYPTO frame. type CryptoFrame struct { Offset ByteCount Length ByteCount } // A StreamFrame is a STREAM frame. type StreamFrame struct { StreamID StreamID Offset ByteCount Length ByteCount Fin bool } // A DatagramFrame is a DATAGRAM frame. type DatagramFrame struct { Length ByteCount } golang-github-lucas-clemente-quic-go-0.46.0/logging/interface.go000066400000000000000000000076131465664453100245330ustar00rootroot00000000000000// Package logging defines a logging interface for quic-go. // This package should not be considered stable package logging import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" ) type ( // A ByteCount is used to count bytes. ByteCount = protocol.ByteCount // ECN is the ECN value ECN = protocol.ECN // A ConnectionID is a QUIC Connection ID. ConnectionID = protocol.ConnectionID // An ArbitraryLenConnectionID is a QUIC Connection ID that can be up to 255 bytes long. ArbitraryLenConnectionID = protocol.ArbitraryLenConnectionID // The EncryptionLevel is the encryption level of a packet. EncryptionLevel = protocol.EncryptionLevel // The KeyPhase is the key phase of the 1-RTT keys. KeyPhase = protocol.KeyPhase // The KeyPhaseBit is the value of the key phase bit of the 1-RTT packets. KeyPhaseBit = protocol.KeyPhaseBit // The PacketNumber is the packet number of a packet. PacketNumber = protocol.PacketNumber // The Perspective is the role of a QUIC endpoint (client or server). Perspective = protocol.Perspective // A StatelessResetToken is a stateless reset token. StatelessResetToken = protocol.StatelessResetToken // The StreamID is the stream ID. StreamID = protocol.StreamID // The StreamNum is the number of the stream. StreamNum = protocol.StreamNum // The StreamType is the type of the stream (unidirectional or bidirectional). StreamType = protocol.StreamType // The VersionNumber is the QUIC version. // Deprecated: use Version instead. VersionNumber = protocol.Version // The Version is the QUIC version. Version = protocol.Version // The Header is the QUIC packet header, before removing header protection. Header = wire.Header // The ExtendedHeader is the QUIC Long Header packet header, after removing header protection. ExtendedHeader = wire.ExtendedHeader // The TransportParameters are QUIC transport parameters. TransportParameters = wire.TransportParameters // The PreferredAddress is the preferred address sent in the transport parameters. PreferredAddress = wire.PreferredAddress // A TransportError is a transport-level error code. TransportError = qerr.TransportErrorCode // An ApplicationError is an application-defined error code. ApplicationError = qerr.TransportErrorCode // The RTTStats contain statistics used by the congestion controller. RTTStats = utils.RTTStats ) const ( // ECNUnsupported means that no ECN value was set / received ECNUnsupported = protocol.ECNUnsupported // ECTNot is Not-ECT ECTNot = protocol.ECNNon // ECT0 is ECT(0) ECT0 = protocol.ECT0 // ECT1 is ECT(1) ECT1 = protocol.ECT1 // ECNCE is CE ECNCE = protocol.ECNCE ) const ( // KeyPhaseZero is key phase bit 0 KeyPhaseZero = protocol.KeyPhaseZero // KeyPhaseOne is key phase bit 1 KeyPhaseOne = protocol.KeyPhaseOne ) const ( // PerspectiveServer is used for a QUIC server PerspectiveServer = protocol.PerspectiveServer // PerspectiveClient is used for a QUIC client PerspectiveClient = protocol.PerspectiveClient ) const ( // EncryptionInitial is the Initial encryption level EncryptionInitial = protocol.EncryptionInitial // EncryptionHandshake is the Handshake encryption level EncryptionHandshake = protocol.EncryptionHandshake // Encryption1RTT is the 1-RTT encryption level Encryption1RTT = protocol.Encryption1RTT // Encryption0RTT is the 0-RTT encryption level Encryption0RTT = protocol.Encryption0RTT ) const ( // StreamTypeUni is a unidirectional stream StreamTypeUni = protocol.StreamTypeUni // StreamTypeBidi is a bidirectional stream StreamTypeBidi = protocol.StreamTypeBidi ) // The ShortHeader is the QUIC Short Header packet header, after removing header protection. type ShortHeader struct { DestConnectionID ConnectionID PacketNumber PacketNumber PacketNumberLen protocol.PacketNumberLen KeyPhase KeyPhaseBit } golang-github-lucas-clemente-quic-go-0.46.0/logging/logging_suite_test.go000066400000000000000000000005771465664453100264730ustar00rootroot00000000000000package logging_test import ( "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) func TestLogging(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Logging Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) golang-github-lucas-clemente-quic-go-0.46.0/logging/multiplex_test.go000066400000000000000000000253611465664453100256550ustar00rootroot00000000000000package logging_test import ( "errors" "net" "time" mocklogging "github.com/quic-go/quic-go/internal/mocks/logging" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" . "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Tracing", func() { Context("Tracer", func() { It("returns a nil tracer if no tracers are passed in", func() { Expect(NewMultiplexedTracer()).To(BeNil()) }) It("returns the raw tracer if only one tracer is passed in", func() { tr := &Tracer{} tracer := NewMultiplexedTracer(tr) Expect(tracer).To(Equal(tr)) }) Context("tracing events", func() { var ( tracer *Tracer tr1, tr2 *mocklogging.MockTracer ) BeforeEach(func() { var t1, t2 *Tracer t1, tr1 = mocklogging.NewMockTracer(mockCtrl) t2, tr2 = mocklogging.NewMockTracer(mockCtrl) tracer = NewMultiplexedTracer(t1, t2, &Tracer{}) }) It("traces the PacketSent event", func() { remote := &net.UDPAddr{IP: net.IPv4(4, 3, 2, 1)} hdr := &Header{DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3})} f := &MaxDataFrame{MaximumData: 1337} tr1.EXPECT().SentPacket(remote, hdr, ByteCount(1024), []Frame{f}) tr2.EXPECT().SentPacket(remote, hdr, ByteCount(1024), []Frame{f}) tracer.SentPacket(remote, hdr, 1024, []Frame{f}) }) It("traces the PacketSent event", func() { remote := &net.UDPAddr{IP: net.IPv4(4, 3, 2, 1)} src := ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} dest := ArbitraryLenConnectionID{1, 2, 3, 4} versions := []Version{1, 2, 3} tr1.EXPECT().SentVersionNegotiationPacket(remote, dest, src, versions) tr2.EXPECT().SentVersionNegotiationPacket(remote, dest, src, versions) tracer.SentVersionNegotiationPacket(remote, dest, src, versions) }) It("traces the PacketDropped event", func() { remote := &net.UDPAddr{IP: net.IPv4(4, 3, 2, 1)} tr1.EXPECT().DroppedPacket(remote, PacketTypeRetry, ByteCount(1024), PacketDropDuplicate) tr2.EXPECT().DroppedPacket(remote, PacketTypeRetry, ByteCount(1024), PacketDropDuplicate) tracer.DroppedPacket(remote, PacketTypeRetry, 1024, PacketDropDuplicate) }) It("traces the Debug event", func() { tr1.EXPECT().Debug("foo", "bar") tr2.EXPECT().Debug("foo", "bar") tracer.Debug("foo", "bar") }) It("traces the Close event", func() { tr1.EXPECT().Close() tr2.EXPECT().Close() tracer.Close() }) }) }) Context("Connection Tracer", func() { var ( tracer *ConnectionTracer tr1 *mocklogging.MockConnectionTracer tr2 *mocklogging.MockConnectionTracer ) BeforeEach(func() { var t1, t2 *ConnectionTracer t1, tr1 = mocklogging.NewMockConnectionTracer(mockCtrl) t2, tr2 = mocklogging.NewMockConnectionTracer(mockCtrl) tracer = NewMultiplexedConnectionTracer(t1, t2) }) It("traces the StartedConnection event", func() { local := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4)} remote := &net.UDPAddr{IP: net.IPv4(4, 3, 2, 1)} dest := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) src := protocol.ParseConnectionID([]byte{4, 3, 2, 1}) tr1.EXPECT().StartedConnection(local, remote, src, dest) tr2.EXPECT().StartedConnection(local, remote, src, dest) tracer.StartedConnection(local, remote, src, dest) }) It("traces the NegotiatedVersion event", func() { chosen := protocol.Version2 client := []protocol.Version{protocol.Version1} server := []protocol.Version{13, 37} tr1.EXPECT().NegotiatedVersion(chosen, client, server) tr2.EXPECT().NegotiatedVersion(chosen, client, server) tracer.NegotiatedVersion(chosen, client, server) }) It("traces the ClosedConnection event", func() { e := errors.New("test err") tr1.EXPECT().ClosedConnection(e) tr2.EXPECT().ClosedConnection(e) tracer.ClosedConnection(e) }) It("traces the SentTransportParameters event", func() { tp := &wire.TransportParameters{InitialMaxData: 1337} tr1.EXPECT().SentTransportParameters(tp) tr2.EXPECT().SentTransportParameters(tp) tracer.SentTransportParameters(tp) }) It("traces the ReceivedTransportParameters event", func() { tp := &wire.TransportParameters{InitialMaxData: 1337} tr1.EXPECT().ReceivedTransportParameters(tp) tr2.EXPECT().ReceivedTransportParameters(tp) tracer.ReceivedTransportParameters(tp) }) It("traces the RestoredTransportParameters event", func() { tp := &wire.TransportParameters{InitialMaxData: 1337} tr1.EXPECT().RestoredTransportParameters(tp) tr2.EXPECT().RestoredTransportParameters(tp) tracer.RestoredTransportParameters(tp) }) It("traces the SentLongHeaderPacket event", func() { hdr := &ExtendedHeader{Header: Header{DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3})}} ack := &AckFrame{AckRanges: []AckRange{{Smallest: 1, Largest: 10}}} ping := &PingFrame{} tr1.EXPECT().SentLongHeaderPacket(hdr, ByteCount(1337), ECTNot, ack, []Frame{ping}) tr2.EXPECT().SentLongHeaderPacket(hdr, ByteCount(1337), ECTNot, ack, []Frame{ping}) tracer.SentLongHeaderPacket(hdr, 1337, ECTNot, ack, []Frame{ping}) }) It("traces the SentShortHeaderPacket event", func() { hdr := &ShortHeader{DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3})} ack := &AckFrame{AckRanges: []AckRange{{Smallest: 1, Largest: 10}}} ping := &PingFrame{} tr1.EXPECT().SentShortHeaderPacket(hdr, ByteCount(1337), ECNCE, ack, []Frame{ping}) tr2.EXPECT().SentShortHeaderPacket(hdr, ByteCount(1337), ECNCE, ack, []Frame{ping}) tracer.SentShortHeaderPacket(hdr, 1337, ECNCE, ack, []Frame{ping}) }) It("traces the ReceivedVersionNegotiationPacket event", func() { src := ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13} dest := ArbitraryLenConnectionID{1, 2, 3, 4} tr1.EXPECT().ReceivedVersionNegotiationPacket(dest, src, []Version{1337}) tr2.EXPECT().ReceivedVersionNegotiationPacket(dest, src, []Version{1337}) tracer.ReceivedVersionNegotiationPacket(dest, src, []Version{1337}) }) It("traces the ReceivedRetry event", func() { hdr := &Header{DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3})} tr1.EXPECT().ReceivedRetry(hdr) tr2.EXPECT().ReceivedRetry(hdr) tracer.ReceivedRetry(hdr) }) It("traces the ReceivedLongHeaderPacket event", func() { hdr := &ExtendedHeader{Header: Header{DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3})}} ping := &PingFrame{} tr1.EXPECT().ReceivedLongHeaderPacket(hdr, ByteCount(1337), ECT1, []Frame{ping}) tr2.EXPECT().ReceivedLongHeaderPacket(hdr, ByteCount(1337), ECT1, []Frame{ping}) tracer.ReceivedLongHeaderPacket(hdr, 1337, ECT1, []Frame{ping}) }) It("traces the ReceivedShortHeaderPacket event", func() { hdr := &ShortHeader{DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3})} ping := &PingFrame{} tr1.EXPECT().ReceivedShortHeaderPacket(hdr, ByteCount(1337), ECT0, []Frame{ping}) tr2.EXPECT().ReceivedShortHeaderPacket(hdr, ByteCount(1337), ECT0, []Frame{ping}) tracer.ReceivedShortHeaderPacket(hdr, 1337, ECT0, []Frame{ping}) }) It("traces the BufferedPacket event", func() { tr1.EXPECT().BufferedPacket(PacketTypeHandshake, ByteCount(1337)) tr2.EXPECT().BufferedPacket(PacketTypeHandshake, ByteCount(1337)) tracer.BufferedPacket(PacketTypeHandshake, 1337) }) It("traces the DroppedPacket event", func() { tr1.EXPECT().DroppedPacket(PacketTypeInitial, PacketNumber(42), ByteCount(1337), PacketDropHeaderParseError) tr2.EXPECT().DroppedPacket(PacketTypeInitial, PacketNumber(42), ByteCount(1337), PacketDropHeaderParseError) tracer.DroppedPacket(PacketTypeInitial, 42, 1337, PacketDropHeaderParseError) }) It("traces the UpdatedMTU event", func() { tr1.EXPECT().UpdatedMTU(ByteCount(1337), true) tr2.EXPECT().UpdatedMTU(ByteCount(1337), true) tracer.UpdatedMTU(1337, true) }) It("traces the UpdatedCongestionState event", func() { tr1.EXPECT().UpdatedCongestionState(CongestionStateRecovery) tr2.EXPECT().UpdatedCongestionState(CongestionStateRecovery) tracer.UpdatedCongestionState(CongestionStateRecovery) }) It("traces the UpdatedMetrics event", func() { rttStats := &RTTStats{} rttStats.UpdateRTT(time.Second, 0, time.Now()) tr1.EXPECT().UpdatedMetrics(rttStats, ByteCount(1337), ByteCount(42), 13) tr2.EXPECT().UpdatedMetrics(rttStats, ByteCount(1337), ByteCount(42), 13) tracer.UpdatedMetrics(rttStats, 1337, 42, 13) }) It("traces the AcknowledgedPacket event", func() { tr1.EXPECT().AcknowledgedPacket(EncryptionHandshake, PacketNumber(42)) tr2.EXPECT().AcknowledgedPacket(EncryptionHandshake, PacketNumber(42)) tracer.AcknowledgedPacket(EncryptionHandshake, 42) }) It("traces the LostPacket event", func() { tr1.EXPECT().LostPacket(EncryptionHandshake, PacketNumber(42), PacketLossReorderingThreshold) tr2.EXPECT().LostPacket(EncryptionHandshake, PacketNumber(42), PacketLossReorderingThreshold) tracer.LostPacket(EncryptionHandshake, 42, PacketLossReorderingThreshold) }) It("traces the UpdatedPTOCount event", func() { tr1.EXPECT().UpdatedPTOCount(uint32(88)) tr2.EXPECT().UpdatedPTOCount(uint32(88)) tracer.UpdatedPTOCount(88) }) It("traces the UpdatedKeyFromTLS event", func() { tr1.EXPECT().UpdatedKeyFromTLS(EncryptionHandshake, PerspectiveClient) tr2.EXPECT().UpdatedKeyFromTLS(EncryptionHandshake, PerspectiveClient) tracer.UpdatedKeyFromTLS(EncryptionHandshake, PerspectiveClient) }) It("traces the UpdatedKey event", func() { tr1.EXPECT().UpdatedKey(KeyPhase(42), true) tr2.EXPECT().UpdatedKey(KeyPhase(42), true) tracer.UpdatedKey(KeyPhase(42), true) }) It("traces the DroppedEncryptionLevel event", func() { tr1.EXPECT().DroppedEncryptionLevel(EncryptionHandshake) tr2.EXPECT().DroppedEncryptionLevel(EncryptionHandshake) tracer.DroppedEncryptionLevel(EncryptionHandshake) }) It("traces the DroppedKey event", func() { tr1.EXPECT().DroppedKey(KeyPhase(123)) tr2.EXPECT().DroppedKey(KeyPhase(123)) tracer.DroppedKey(123) }) It("traces the SetLossTimer event", func() { now := time.Now() tr1.EXPECT().SetLossTimer(TimerTypePTO, EncryptionHandshake, now) tr2.EXPECT().SetLossTimer(TimerTypePTO, EncryptionHandshake, now) tracer.SetLossTimer(TimerTypePTO, EncryptionHandshake, now) }) It("traces the LossTimerExpired event", func() { tr1.EXPECT().LossTimerExpired(TimerTypePTO, EncryptionHandshake) tr2.EXPECT().LossTimerExpired(TimerTypePTO, EncryptionHandshake) tracer.LossTimerExpired(TimerTypePTO, EncryptionHandshake) }) It("traces the LossTimerCanceled event", func() { tr1.EXPECT().LossTimerCanceled() tr2.EXPECT().LossTimerCanceled() tracer.LossTimerCanceled() }) It("traces the Close event", func() { tr1.EXPECT().Close() tr2.EXPECT().Close() tracer.Close() }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/logging/packet_header.go000066400000000000000000000010721465664453100253430ustar00rootroot00000000000000package logging import ( "github.com/quic-go/quic-go/internal/protocol" ) // PacketTypeFromHeader determines the packet type from a *wire.Header. func PacketTypeFromHeader(hdr *Header) PacketType { if hdr.Version == 0 { return PacketTypeVersionNegotiation } switch hdr.Type { case protocol.PacketTypeInitial: return PacketTypeInitial case protocol.PacketTypeHandshake: return PacketTypeHandshake case protocol.PacketType0RTT: return PacketType0RTT case protocol.PacketTypeRetry: return PacketTypeRetry default: return PacketTypeNotDetermined } } golang-github-lucas-clemente-quic-go-0.46.0/logging/packet_header_test.go000066400000000000000000000026701465664453100264070ustar00rootroot00000000000000package logging_test import ( "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" . "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Packet Header", func() { Context("determining the packet type from the header", func() { It("recognizes Initial packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ Type: protocol.PacketTypeInitial, Version: protocol.Version1, })).To(Equal(PacketTypeInitial)) }) It("recognizes Handshake packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ Type: protocol.PacketTypeHandshake, Version: protocol.Version1, })).To(Equal(PacketTypeHandshake)) }) It("recognizes Retry packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ Type: protocol.PacketTypeRetry, Version: protocol.Version1, })).To(Equal(PacketTypeRetry)) }) It("recognizes 0-RTT packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ Type: protocol.PacketType0RTT, Version: protocol.Version1, })).To(Equal(PacketType0RTT)) }) It("recognizes Version Negotiation packets", func() { Expect(PacketTypeFromHeader(&wire.Header{})).To(Equal(PacketTypeVersionNegotiation)) }) It("handles unrecognized packet types", func() { Expect(PacketTypeFromHeader(&wire.Header{Version: protocol.Version1})).To(Equal(PacketTypeNotDetermined)) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/logging/tracer.go000066400000000000000000000031201465664453100240400ustar00rootroot00000000000000package logging import "net" // A Tracer traces events. type Tracer struct { SentPacket func(net.Addr, *Header, ByteCount, []Frame) SentVersionNegotiationPacket func(_ net.Addr, dest, src ArbitraryLenConnectionID, _ []Version) DroppedPacket func(net.Addr, PacketType, ByteCount, PacketDropReason) Debug func(name, msg string) Close func() } // NewMultiplexedTracer creates a new tracer that multiplexes events to multiple tracers. func NewMultiplexedTracer(tracers ...*Tracer) *Tracer { if len(tracers) == 0 { return nil } if len(tracers) == 1 { return tracers[0] } return &Tracer{ SentPacket: func(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) { for _, t := range tracers { if t.SentPacket != nil { t.SentPacket(remote, hdr, size, frames) } } }, SentVersionNegotiationPacket: func(remote net.Addr, dest, src ArbitraryLenConnectionID, versions []Version) { for _, t := range tracers { if t.SentVersionNegotiationPacket != nil { t.SentVersionNegotiationPacket(remote, dest, src, versions) } } }, DroppedPacket: func(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) { for _, t := range tracers { if t.DroppedPacket != nil { t.DroppedPacket(remote, typ, size, reason) } } }, Debug: func(name, msg string) { for _, t := range tracers { if t.Debug != nil { t.Debug(name, msg) } } }, Close: func() { for _, t := range tracers { if t.Close != nil { t.Close() } } }, } } golang-github-lucas-clemente-quic-go-0.46.0/logging/types.go000066400000000000000000000116331465664453100237340ustar00rootroot00000000000000package logging // PacketType is the packet type of a QUIC packet type PacketType uint8 const ( // PacketTypeInitial is the packet type of an Initial packet PacketTypeInitial PacketType = iota // PacketTypeHandshake is the packet type of a Handshake packet PacketTypeHandshake // PacketTypeRetry is the packet type of a Retry packet PacketTypeRetry // PacketType0RTT is the packet type of a 0-RTT packet PacketType0RTT // PacketTypeVersionNegotiation is the packet type of a Version Negotiation packet PacketTypeVersionNegotiation // PacketType1RTT is a 1-RTT packet PacketType1RTT // PacketTypeStatelessReset is a stateless reset PacketTypeStatelessReset // PacketTypeNotDetermined is the packet type when it could not be determined PacketTypeNotDetermined ) type PacketLossReason uint8 const ( // PacketLossReorderingThreshold: when a packet is deemed lost due to reordering threshold PacketLossReorderingThreshold PacketLossReason = iota // PacketLossTimeThreshold: when a packet is deemed lost due to time threshold PacketLossTimeThreshold ) type PacketDropReason uint8 const ( // PacketDropKeyUnavailable is used when a packet is dropped because keys are unavailable PacketDropKeyUnavailable PacketDropReason = iota // PacketDropUnknownConnectionID is used when a packet is dropped because the connection ID is unknown PacketDropUnknownConnectionID // PacketDropHeaderParseError is used when a packet is dropped because header parsing failed PacketDropHeaderParseError // PacketDropPayloadDecryptError is used when a packet is dropped because decrypting the payload failed PacketDropPayloadDecryptError // PacketDropProtocolViolation is used when a packet is dropped due to a protocol violation PacketDropProtocolViolation // PacketDropDOSPrevention is used when a packet is dropped to mitigate a DoS attack PacketDropDOSPrevention // PacketDropUnsupportedVersion is used when a packet is dropped because the version is not supported PacketDropUnsupportedVersion // PacketDropUnexpectedPacket is used when an unexpected packet is received PacketDropUnexpectedPacket // PacketDropUnexpectedSourceConnectionID is used when a packet with an unexpected source connection ID is received PacketDropUnexpectedSourceConnectionID // PacketDropUnexpectedVersion is used when a packet with an unexpected version is received PacketDropUnexpectedVersion // PacketDropDuplicate is used when a duplicate packet is received PacketDropDuplicate ) // TimerType is the type of the loss detection timer type TimerType uint8 const ( // TimerTypeACK is the timer type for the early retransmit timer TimerTypeACK TimerType = iota // TimerTypePTO is the timer type for the PTO retransmit timer TimerTypePTO ) // TimeoutReason is the reason why a connection is closed type TimeoutReason uint8 const ( // TimeoutReasonHandshake is used when the connection is closed due to a handshake timeout // This reason is not defined in the qlog draft, but very useful for debugging. TimeoutReasonHandshake TimeoutReason = iota // TimeoutReasonIdle is used when the connection is closed due to an idle timeout // This reason is not defined in the qlog draft, but very useful for debugging. TimeoutReasonIdle ) type CongestionState uint8 const ( // CongestionStateSlowStart is the slow start phase of Reno / Cubic CongestionStateSlowStart CongestionState = iota // CongestionStateCongestionAvoidance is the slow start phase of Reno / Cubic CongestionStateCongestionAvoidance // CongestionStateRecovery is the recovery phase of Reno / Cubic CongestionStateRecovery // CongestionStateApplicationLimited means that the congestion controller is application limited CongestionStateApplicationLimited ) // ECNState is the state of the ECN state machine (see Appendix A.4 of RFC 9000) type ECNState uint8 const ( // ECNStateTesting is the testing state ECNStateTesting ECNState = 1 + iota // ECNStateUnknown is the unknown state ECNStateUnknown // ECNStateFailed is the failed state ECNStateFailed // ECNStateCapable is the capable state ECNStateCapable ) // ECNStateTrigger is a trigger for an ECN state transition. type ECNStateTrigger uint8 const ( ECNTriggerNoTrigger ECNStateTrigger = iota // ECNFailedNoECNCounts is emitted when an ACK acknowledges ECN-marked packets, // but doesn't contain any ECN counts ECNFailedNoECNCounts // ECNFailedDecreasedECNCounts is emitted when an ACK frame decreases ECN counts ECNFailedDecreasedECNCounts // ECNFailedLostAllTestingPackets is emitted when all ECN testing packets are declared lost ECNFailedLostAllTestingPackets // ECNFailedMoreECNCountsThanSent is emitted when an ACK contains more ECN counts than ECN-marked packets were sent ECNFailedMoreECNCountsThanSent // ECNFailedTooFewECNCounts is emitted when an ACK contains fewer ECN counts than it acknowledges packets ECNFailedTooFewECNCounts // ECNFailedManglingDetected is emitted when the path marks all ECN-marked packets as CE ECNFailedManglingDetected ) golang-github-lucas-clemente-quic-go-0.46.0/metrics/000077500000000000000000000000001465664453100222555ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/metrics/connection_tracer.go000066400000000000000000000132241465664453100263050ustar00rootroot00000000000000package metrics import ( "context" "errors" "fmt" "net" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/logging" "github.com/prometheus/client_golang/prometheus" ) var ( connStarted = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, Name: "connections_started_total", Help: "Connections Started", }, []string{"dir"}, ) connClosed = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, Name: "connections_closed_total", Help: "Connections Closed", }, []string{"dir", "reason"}, ) connDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Namespace: metricNamespace, Name: "connection_duration_seconds", Help: "Duration of a Connection", Buckets: prometheus.ExponentialBuckets(1.0/16, 2, 25), // up to 24 days }, []string{"dir"}, ) connHandshakeDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Namespace: metricNamespace, Name: "handshake_duration_seconds", Help: "Duration of the QUIC Handshake", Buckets: prometheus.ExponentialBuckets(0.001, 1.3, 35), }, []string{"dir"}, ) ) // DefaultConnectionTracer returns a callback that creates a metrics ConnectionTracer. // The ConnectionTracer returned can be set on the quic.Config for a new connection. func DefaultConnectionTracer(_ context.Context, p logging.Perspective, _ logging.ConnectionID) *logging.ConnectionTracer { switch p { case logging.PerspectiveClient: return NewClientConnectionTracerWithRegisterer(prometheus.DefaultRegisterer) case logging.PerspectiveServer: return NewServerConnectionTracerWithRegisterer(prometheus.DefaultRegisterer) default: panic("invalid perspective") } } // NewClientConnectionTracerWithRegisterer creates a new connection tracer for a connection // dialed on the client side with a given Prometheus registerer. func NewClientConnectionTracerWithRegisterer(registerer prometheus.Registerer) *logging.ConnectionTracer { return newConnectionTracerWithRegisterer(registerer, true) } // NewServerConnectionTracerWithRegisterer creates a new connection tracer for a connection // accepted on the server side with a given Prometheus registerer. func NewServerConnectionTracerWithRegisterer(registerer prometheus.Registerer) *logging.ConnectionTracer { return newConnectionTracerWithRegisterer(registerer, false) } func newConnectionTracerWithRegisterer(registerer prometheus.Registerer, isClient bool) *logging.ConnectionTracer { for _, c := range [...]prometheus.Collector{ connStarted, connHandshakeDuration, connClosed, connDuration, } { if err := registerer.Register(c); err != nil { if ok := errors.As(err, &prometheus.AlreadyRegisteredError{}); !ok { panic(err) } } } direction := "incoming" if isClient { direction = "outgoing" } var ( startTime time.Time handshakeComplete bool ) return &logging.ConnectionTracer{ StartedConnection: func(_, _ net.Addr, _, _ logging.ConnectionID) { tags := getStringSlice() defer putStringSlice(tags) startTime = time.Now() *tags = append(*tags, direction) connStarted.WithLabelValues(*tags...).Inc() }, ClosedConnection: func(e error) { tags := getStringSlice() defer putStringSlice(tags) *tags = append(*tags, direction) // call connDuration.Observe before adding any more labels if handshakeComplete { connDuration.WithLabelValues(*tags...).Observe(time.Since(startTime).Seconds()) } var ( statelessResetErr *quic.StatelessResetError handshakeTimeoutErr *quic.HandshakeTimeoutError idleTimeoutErr *quic.IdleTimeoutError applicationErr *quic.ApplicationError transportErr *quic.TransportError versionNegotiationErr *quic.VersionNegotiationError ) var reason string switch { case errors.As(e, &statelessResetErr): reason = "stateless_reset" case errors.As(e, &handshakeTimeoutErr): reason = "handshake_timeout" case errors.As(e, &idleTimeoutErr): if handshakeComplete { reason = "idle_timeout" } else { reason = "handshake_timeout" } case errors.As(e, &applicationErr): if applicationErr.Remote { reason = "application_error (remote)" } else { reason = "application_error (local)" } case errors.As(e, &transportErr): switch { case transportErr.ErrorCode == qerr.ApplicationErrorErrorCode: if transportErr.Remote { reason = "application_error (remote)" } else { reason = "application_error (local)" } case transportErr.ErrorCode.IsCryptoError(): if transportErr.Remote { reason = "crypto_error (remote)" } else { reason = "crypto_error (local)" } default: if transportErr.Remote { reason = "transport_error (remote)" } else { reason = fmt.Sprintf("transport_error (local): %s", transportErr.ErrorCode) } } case errors.As(e, &versionNegotiationErr): reason = "version_mismatch" default: reason = "unknown" } *tags = append(*tags, reason) connClosed.WithLabelValues(*tags...).Inc() }, UpdatedKeyFromTLS: func(l logging.EncryptionLevel, p logging.Perspective) { // The client derives both 1-RTT keys when the handshake completes. // The server derives the 1-RTT read key when the handshake completes. if l != logging.Encryption1RTT || p != logging.PerspectiveClient { return } handshakeComplete = true tags := getStringSlice() defer putStringSlice(tags) *tags = append(*tags, direction) connHandshakeDuration.WithLabelValues(*tags...).Observe(time.Since(startTime).Seconds()) }, } } golang-github-lucas-clemente-quic-go-0.46.0/metrics/dashboards/000077500000000000000000000000001465664453100243675ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/metrics/dashboards/README.md000066400000000000000000000015001465664453100256420ustar00rootroot00000000000000# quic-go Prometheus / Grafana Local Development Setup For local development and debugging, it can be useful to spin up a local Prometheus and Grafana instance. Please refer to the [documentation](https://quic-go.net/docs/quic/metrics/) for how to configure quic-go to expose Prometheus metrics. The configuration files in this directory assume that the application exposes the Prometheus endpoint at `http://localhost:5001/prometheus`: ```go import "github.com/prometheus/client_golang/prometheus/promhttp" go func() { http.Handle("/prometheus", promhttp.Handler()) log.Fatal(http.ListenAndServe("localhost:5001", nil)) }() ``` Prometheus and Grafana can be started using Docker Compose: Running: ```shell docker compose up ``` [quic-go.json](./quic-go.json) contains the JSON model of an example Grafana dashboard. golang-github-lucas-clemente-quic-go-0.46.0/metrics/dashboards/datasources.yml000066400000000000000000000003171465664453100274300ustar00rootroot00000000000000apiVersion: 1 deleteDatasources: - name: Prometheus orgId: 1 datasources: - name: Prometheus orgId: 1 type: prometheus access: proxy url: http://prometheus:9090 editable: false golang-github-lucas-clemente-quic-go-0.46.0/metrics/dashboards/docker-compose.yml000066400000000000000000000011061465664453100300220ustar00rootroot00000000000000version: '3.8' volumes: prometheus_data: {} grafana_data: {} services: prometheus: image: prom/prometheus:latest container_name: prometheus volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml - prometheus_data:/prometheus command: - '--config.file=/etc/prometheus/prometheus.yml' expose: - 9090 grafana: image: grafana/grafana:latest container_name: grafana volumes: - grafana_data:/var/lib/grafana - ./datasources.yml:/etc/grafana/provisioning/datasources/prom.yml ports: - "3000:3000" golang-github-lucas-clemente-quic-go-0.46.0/metrics/dashboards/prometheus.yml000066400000000000000000000003041465664453100273020ustar00rootroot00000000000000global: scrape_interval: 15s scrape_configs: - job_name: 'quic-go' scrape_interval: 15s static_configs: - targets: ['host.docker.internal:5001'] metrics_path: '/prometheus' golang-github-lucas-clemente-quic-go-0.46.0/metrics/dashboards/quic-go.json000066400000000000000000000432201465664453100266270ustar00rootroot00000000000000{ "__inputs": [ { "name": "DS_PROMETHEUS", "label": "Prometheus", "description": "", "type": "datasource", "pluginId": "prometheus", "pluginName": "Prometheus" } ], "__elements": {}, "__requires": [ { "type": "grafana", "id": "grafana", "name": "Grafana", "version": "10.2.3" }, { "type": "datasource", "id": "prometheus", "name": "Prometheus", "version": "1.0.0" }, { "type": "panel", "id": "stat", "name": "Stat", "version": "" }, { "type": "panel", "id": "timeseries", "name": "Time series", "version": "" } ], "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "grafana", "uid": "-- Grafana --" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "id": null, "links": [], "liveNow": false, "panels": [ { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 }, "id": 7, "panels": [], "title": "Transport", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 1 }, "id": 3, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "single", "sort": "none" } }, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "sum(rate(quicgo_server_received_packets_dropped_total{instance=~\"$instance\"}[$__rate_interval])) by (reason)", "instant": false, "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Server Dropped Packets", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 1 }, "id": 12, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "single", "sort": "none" } }, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "sum(rate(quicgo_server_connections_rejected_total{instance=~\"$instance\"}[$__rate_interval])) by (reason)", "hide": true, "instant": false, "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Rejected Connections", "type": "timeseries" }, { "collapsed": false, "gridPos": { "h": 1, "w": 24, "x": 0, "y": 9 }, "id": 6, "panels": [], "title": "Connection", "type": "row" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 10 }, "id": 1, "options": { "colorMode": "value", "graphMode": "area", "justifyMode": "auto", "orientation": "auto", "reduceOptions": { "calcs": [ "lastNotNull" ], "fields": "", "values": false }, "showPercentChange": false, "textMode": "auto", "wideLayout": true }, "pluginVersion": "10.2.3", "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "sum (quicgo_connections_started_total{instance=~\"$instance\"}) by (dir) - sum (quicgo_connections_closed_total{instance=~\"$instance\"}) by (dir)", "instant": false, "legendFormat": "{{dir}}", "range": true, "refId": "A" } ], "title": "Currently Active Connections", "type": "stat" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 10 }, "id": 5, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "multi", "sort": "none" } }, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "histogram_quantile(0.5, sum(rate(quicgo_handshake_duration_seconds_bucket{instance=~\"$instance\"}[$__rate_interval])) by (le))", "instant": false, "legendFormat": "50th percentile", "range": true, "refId": "A" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "histogram_quantile(0.9, sum(rate(quicgo_handshake_duration_seconds_bucket{instance=~\"$instance\"}[$__rate_interval])) by (le))", "hide": false, "instant": false, "legendFormat": "90th percentile", "range": true, "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "histogram_quantile(0.95, sum(rate(quicgo_handshake_duration_seconds_bucket{instance=~\"$instance\"}[$__rate_interval])) by (le))", "hide": false, "instant": false, "legendFormat": "95th percentile", "range": true, "refId": "C" } ], "title": "Handshake Latency", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 18 }, "id": 11, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "single", "sort": "none" } }, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "sum(rate(quicgo_connections_closed_total{instance=~\"$instance\"}[$__rate_interval])) by (reason)", "instant": false, "legendFormat": "__auto", "range": true, "refId": "A" } ], "title": "Close Reason", "type": "timeseries" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "none", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "spanNulls": false, "stacking": { "group": "A", "mode": "none" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": null }, { "color": "red", "value": 80 } ] }, "unit": "s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 12, "y": 18 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "mode": "multi", "sort": "none" } }, "targets": [ { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "histogram_quantile(0.5, sum(rate(quicgo_connection_duration_seconds_bucket{instance=~\"$instance\"}[$__rate_interval])) by (le))\n", "hide": false, "instant": false, "legendFormat": "50th percentile", "range": true, "refId": "A" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "histogram_quantile(0.9, sum(rate(quicgo_connection_duration_seconds_bucket{instance=~\"$instance\"}[$__rate_interval])) by (le))\n", "hide": false, "instant": false, "legendFormat": "90th percentile", "range": true, "refId": "B" }, { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "editorMode": "code", "expr": "histogram_quantile(0.95, sum(rate(quicgo_connection_duration_seconds_bucket{instance=~\"$instance\"}[$__rate_interval])) by (le))\n", "hide": false, "instant": false, "legendFormat": "95th percentile", "range": true, "refId": "C" } ], "title": "Connection Durations", "type": "timeseries" } ], "refresh": "", "schemaVersion": 39, "tags": [], "templating": { "list": [ { "current": {}, "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "definition": "label_values(up,instance)", "hide": 0, "includeAll": true, "multi": true, "name": "instance", "options": [], "query": { "qryType": 1, "query": "label_values(up,instance)", "refId": "PrometheusVariableQueryEditor-VariableQuery" }, "refresh": 1, "regex": "", "skipUrlSync": false, "sort": 0, "type": "query" } ] }, "time": { "from": "now-30m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "quic-go", "uid": "afd27180-618a-42ab-99fd-0508776d9c29", "version": 15, "weekStart": "" }golang-github-lucas-clemente-quic-go-0.46.0/metrics/pool.go000066400000000000000000000007411465664453100235570ustar00rootroot00000000000000package metrics import ( "fmt" "sync" ) const capacity = 4 // The stringPool is used to avoid allocations when passing labels to Prometheus. var stringPool = sync.Pool{New: func() any { s := make([]string, 0, capacity) return &s }} func getStringSlice() *[]string { s := stringPool.Get().(*[]string) *s = (*s)[:0] return s } func putStringSlice(s *[]string) { if c := cap(*s); c < capacity { panic(fmt.Sprintf("unexpected slice cap: %d", c)) } stringPool.Put(s) } golang-github-lucas-clemente-quic-go-0.46.0/metrics/tracer.go000066400000000000000000000103451465664453100240670ustar00rootroot00000000000000package metrics import ( "errors" "fmt" "net" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/logging" "github.com/prometheus/client_golang/prometheus" ) const metricNamespace = "quicgo" func getIPVersion(addr net.Addr) string { udpAddr, ok := addr.(*net.UDPAddr) if !ok { return "" } if udpAddr.IP.To4() != nil { return "ipv4" } return "ipv6" } var ( connsRejected = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, Name: "server_connections_rejected_total", Help: "Connections Rejected", }, []string{"ip_version", "reason"}, ) packetDropped = prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: metricNamespace, Name: "server_received_packets_dropped_total", Help: "packets dropped", }, []string{"ip_version", "reason"}, ) ) // NewTracer creates a new tracer using the default Prometheus registerer. // The Tracer returned from this function can be used to collect metrics for // events happening before the establishment of a QUIC connection. // It can be set on the Tracer field of quic.Transport. func NewTracer() *logging.Tracer { return NewTracerWithRegisterer(prometheus.DefaultRegisterer) } // NewTracerWithRegisterer creates a new tracer using a given Prometheus registerer. func NewTracerWithRegisterer(registerer prometheus.Registerer) *logging.Tracer { for _, c := range [...]prometheus.Collector{ connsRejected, packetDropped, } { if err := registerer.Register(c); err != nil { if ok := errors.As(err, &prometheus.AlreadyRegisteredError{}); !ok { panic(err) } } } return &logging.Tracer{ SentPacket: func(addr net.Addr, hdr *logging.Header, _ logging.ByteCount, frames []logging.Frame) { tags := getStringSlice() defer putStringSlice(tags) var reason string switch { case hdr.Type == protocol.PacketTypeRetry: reason = "retry" case hdr.Type == protocol.PacketTypeInitial: var ccf *logging.ConnectionCloseFrame for _, f := range frames { cc, ok := f.(*logging.ConnectionCloseFrame) if ok { ccf = cc break } } // This should never happen. We only send Initials before creating the connection in order to // reject a connection attempt. if ccf == nil { return } if ccf.IsApplicationError { //nolint:exhaustive // Only a few error codes applicable. switch qerr.TransportErrorCode(ccf.ErrorCode) { case qerr.ConnectionRefused: reason = "connection_refused" case qerr.InvalidToken: reason = "invalid_token" default: // This shouldn't happen, the server doesn't send CONNECTION_CLOSE frames with different errors. reason = fmt.Sprintf("transport_error: %d", ccf.ErrorCode) } } else { // This shouldn't happen, the server doesn't send application-level CONNECTION_CLOSE frames. reason = "application_error" } } *tags = append(*tags, getIPVersion(addr)) *tags = append(*tags, reason) connsRejected.WithLabelValues(*tags...).Inc() }, SentVersionNegotiationPacket: func(addr net.Addr, _, _ logging.ArbitraryLenConnectionID, _ []logging.Version) { tags := getStringSlice() defer putStringSlice(tags) *tags = append(*tags, getIPVersion(addr)) *tags = append(*tags, "version_negotiation") connsRejected.WithLabelValues(*tags...).Inc() }, DroppedPacket: func(addr net.Addr, pt logging.PacketType, _ logging.ByteCount, reason logging.PacketDropReason) { tags := getStringSlice() defer putStringSlice(tags) var dropReason string //nolint:exhaustive // Only a few drop reasons applicable. switch reason { case logging.PacketDropDOSPrevention: if pt == logging.PacketType0RTT { dropReason = "0rtt_dos_prevention" } else { dropReason = "dos_prevention" } case logging.PacketDropHeaderParseError: dropReason = "header_parsing" case logging.PacketDropPayloadDecryptError: dropReason = "payload_decrypt" case logging.PacketDropUnexpectedPacket: dropReason = "unexpected_packet" default: dropReason = "unknown" } *tags = append(*tags, getIPVersion(addr)) *tags = append(*tags, dropReason) packetDropped.WithLabelValues(*tags...).Inc() }, } } golang-github-lucas-clemente-quic-go-0.46.0/mock_ack_frame_source_test.go000066400000000000000000000052101465664453100264740ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: AckFrameSource) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockAckFrameSource is a mock of AckFrameSource interface. type MockAckFrameSource struct { ctrl *gomock.Controller recorder *MockAckFrameSourceMockRecorder } // MockAckFrameSourceMockRecorder is the mock recorder for MockAckFrameSource. type MockAckFrameSourceMockRecorder struct { mock *MockAckFrameSource } // NewMockAckFrameSource creates a new mock instance. func NewMockAckFrameSource(ctrl *gomock.Controller) *MockAckFrameSource { mock := &MockAckFrameSource{ctrl: ctrl} mock.recorder = &MockAckFrameSourceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockAckFrameSource) EXPECT() *MockAckFrameSourceMockRecorder { return m.recorder } // GetAckFrame mocks base method. func (m *MockAckFrameSource) GetAckFrame(arg0 protocol.EncryptionLevel, arg1 bool) *wire.AckFrame { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckFrame", arg0, arg1) ret0, _ := ret[0].(*wire.AckFrame) return ret0 } // GetAckFrame indicates an expected call of GetAckFrame. func (mr *MockAckFrameSourceMockRecorder) GetAckFrame(arg0, arg1 any) *MockAckFrameSourceGetAckFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckFrame", reflect.TypeOf((*MockAckFrameSource)(nil).GetAckFrame), arg0, arg1) return &MockAckFrameSourceGetAckFrameCall{Call: call} } // MockAckFrameSourceGetAckFrameCall wrap *gomock.Call type MockAckFrameSourceGetAckFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockAckFrameSourceGetAckFrameCall) Return(arg0 *wire.AckFrame) *MockAckFrameSourceGetAckFrameCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockAckFrameSourceGetAckFrameCall) Do(f func(protocol.EncryptionLevel, bool) *wire.AckFrame) *MockAckFrameSourceGetAckFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockAckFrameSourceGetAckFrameCall) DoAndReturn(f func(protocol.EncryptionLevel, bool) *wire.AckFrame) *MockAckFrameSourceGetAckFrameCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_batch_conn_test.go000066400000000000000000000045101465664453100253040ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: sys_conn_oob.go // // Generated by this command: // // mockgen -typed -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "go.uber.org/mock/gomock" ipv4 "golang.org/x/net/ipv4" ) // MockBatchConn is a mock of batchConn interface. type MockBatchConn struct { ctrl *gomock.Controller recorder *MockBatchConnMockRecorder } // MockBatchConnMockRecorder is the mock recorder for MockBatchConn. type MockBatchConnMockRecorder struct { mock *MockBatchConn } // NewMockBatchConn creates a new mock instance. func NewMockBatchConn(ctrl *gomock.Controller) *MockBatchConn { mock := &MockBatchConn{ctrl: ctrl} mock.recorder = &MockBatchConnMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockBatchConn) EXPECT() *MockBatchConnMockRecorder { return m.recorder } // ReadBatch mocks base method. func (m *MockBatchConn) ReadBatch(ms []ipv4.Message, flags int) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadBatch", ms, flags) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadBatch indicates an expected call of ReadBatch. func (mr *MockBatchConnMockRecorder) ReadBatch(ms, flags any) *MockBatchConnReadBatchCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadBatch", reflect.TypeOf((*MockBatchConn)(nil).ReadBatch), ms, flags) return &MockBatchConnReadBatchCall{Call: call} } // MockBatchConnReadBatchCall wrap *gomock.Call type MockBatchConnReadBatchCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockBatchConnReadBatchCall) Return(arg0 int, arg1 error) *MockBatchConnReadBatchCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockBatchConnReadBatchCall) Do(f func([]ipv4.Message, int) (int, error)) *MockBatchConnReadBatchCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockBatchConnReadBatchCall) DoAndReturn(f func([]ipv4.Message, int) (int, error)) *MockBatchConnReadBatchCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_conn_runner_test.go000066400000000000000000000234031465664453100255360ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: ConnRunner) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockConnRunner is a mock of ConnRunner interface. type MockConnRunner struct { ctrl *gomock.Controller recorder *MockConnRunnerMockRecorder } // MockConnRunnerMockRecorder is the mock recorder for MockConnRunner. type MockConnRunnerMockRecorder struct { mock *MockConnRunner } // NewMockConnRunner creates a new mock instance. func NewMockConnRunner(ctrl *gomock.Controller) *MockConnRunner { mock := &MockConnRunner{ctrl: ctrl} mock.recorder = &MockConnRunnerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockConnRunner) EXPECT() *MockConnRunnerMockRecorder { return m.recorder } // Add mocks base method. func (m *MockConnRunner) Add(arg0 protocol.ConnectionID, arg1 packetHandler) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Add", arg0, arg1) ret0, _ := ret[0].(bool) return ret0 } // Add indicates an expected call of Add. func (mr *MockConnRunnerMockRecorder) Add(arg0, arg1 any) *MockConnRunnerAddCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockConnRunner)(nil).Add), arg0, arg1) return &MockConnRunnerAddCall{Call: call} } // MockConnRunnerAddCall wrap *gomock.Call type MockConnRunnerAddCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerAddCall) Return(arg0 bool) *MockConnRunnerAddCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerAddCall) Do(f func(protocol.ConnectionID, packetHandler) bool) *MockConnRunnerAddCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerAddCall) DoAndReturn(f func(protocol.ConnectionID, packetHandler) bool) *MockConnRunnerAddCall { c.Call = c.Call.DoAndReturn(f) return c } // AddResetToken mocks base method. func (m *MockConnRunner) AddResetToken(arg0 protocol.StatelessResetToken, arg1 packetHandler) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddResetToken", arg0, arg1) } // AddResetToken indicates an expected call of AddResetToken. func (mr *MockConnRunnerMockRecorder) AddResetToken(arg0, arg1 any) *MockConnRunnerAddResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResetToken", reflect.TypeOf((*MockConnRunner)(nil).AddResetToken), arg0, arg1) return &MockConnRunnerAddResetTokenCall{Call: call} } // MockConnRunnerAddResetTokenCall wrap *gomock.Call type MockConnRunnerAddResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerAddResetTokenCall) Return() *MockConnRunnerAddResetTokenCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerAddResetTokenCall) Do(f func(protocol.StatelessResetToken, packetHandler)) *MockConnRunnerAddResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerAddResetTokenCall) DoAndReturn(f func(protocol.StatelessResetToken, packetHandler)) *MockConnRunnerAddResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // GetStatelessResetToken mocks base method. func (m *MockConnRunner) GetStatelessResetToken(arg0 protocol.ConnectionID) protocol.StatelessResetToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStatelessResetToken", arg0) ret0, _ := ret[0].(protocol.StatelessResetToken) return ret0 } // GetStatelessResetToken indicates an expected call of GetStatelessResetToken. func (mr *MockConnRunnerMockRecorder) GetStatelessResetToken(arg0 any) *MockConnRunnerGetStatelessResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessResetToken", reflect.TypeOf((*MockConnRunner)(nil).GetStatelessResetToken), arg0) return &MockConnRunnerGetStatelessResetTokenCall{Call: call} } // MockConnRunnerGetStatelessResetTokenCall wrap *gomock.Call type MockConnRunnerGetStatelessResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerGetStatelessResetTokenCall) Return(arg0 protocol.StatelessResetToken) *MockConnRunnerGetStatelessResetTokenCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerGetStatelessResetTokenCall) Do(f func(protocol.ConnectionID) protocol.StatelessResetToken) *MockConnRunnerGetStatelessResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerGetStatelessResetTokenCall) DoAndReturn(f func(protocol.ConnectionID) protocol.StatelessResetToken) *MockConnRunnerGetStatelessResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // Remove mocks base method. func (m *MockConnRunner) Remove(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "Remove", arg0) } // Remove indicates an expected call of Remove. func (mr *MockConnRunnerMockRecorder) Remove(arg0 any) *MockConnRunnerRemoveCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockConnRunner)(nil).Remove), arg0) return &MockConnRunnerRemoveCall{Call: call} } // MockConnRunnerRemoveCall wrap *gomock.Call type MockConnRunnerRemoveCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerRemoveCall) Return() *MockConnRunnerRemoveCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerRemoveCall) Do(f func(protocol.ConnectionID)) *MockConnRunnerRemoveCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerRemoveCall) DoAndReturn(f func(protocol.ConnectionID)) *MockConnRunnerRemoveCall { c.Call = c.Call.DoAndReturn(f) return c } // RemoveResetToken mocks base method. func (m *MockConnRunner) RemoveResetToken(arg0 protocol.StatelessResetToken) { m.ctrl.T.Helper() m.ctrl.Call(m, "RemoveResetToken", arg0) } // RemoveResetToken indicates an expected call of RemoveResetToken. func (mr *MockConnRunnerMockRecorder) RemoveResetToken(arg0 any) *MockConnRunnerRemoveResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResetToken", reflect.TypeOf((*MockConnRunner)(nil).RemoveResetToken), arg0) return &MockConnRunnerRemoveResetTokenCall{Call: call} } // MockConnRunnerRemoveResetTokenCall wrap *gomock.Call type MockConnRunnerRemoveResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerRemoveResetTokenCall) Return() *MockConnRunnerRemoveResetTokenCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerRemoveResetTokenCall) Do(f func(protocol.StatelessResetToken)) *MockConnRunnerRemoveResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerRemoveResetTokenCall) DoAndReturn(f func(protocol.StatelessResetToken)) *MockConnRunnerRemoveResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // ReplaceWithClosed mocks base method. func (m *MockConnRunner) ReplaceWithClosed(arg0 []protocol.ConnectionID, arg1 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReplaceWithClosed", arg0, arg1) } // ReplaceWithClosed indicates an expected call of ReplaceWithClosed. func (mr *MockConnRunnerMockRecorder) ReplaceWithClosed(arg0, arg1 any) *MockConnRunnerReplaceWithClosedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceWithClosed", reflect.TypeOf((*MockConnRunner)(nil).ReplaceWithClosed), arg0, arg1) return &MockConnRunnerReplaceWithClosedCall{Call: call} } // MockConnRunnerReplaceWithClosedCall wrap *gomock.Call type MockConnRunnerReplaceWithClosedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerReplaceWithClosedCall) Return() *MockConnRunnerReplaceWithClosedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerReplaceWithClosedCall) Do(f func([]protocol.ConnectionID, []byte)) *MockConnRunnerReplaceWithClosedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerReplaceWithClosedCall) DoAndReturn(f func([]protocol.ConnectionID, []byte)) *MockConnRunnerReplaceWithClosedCall { c.Call = c.Call.DoAndReturn(f) return c } // Retire mocks base method. func (m *MockConnRunner) Retire(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "Retire", arg0) } // Retire indicates an expected call of Retire. func (mr *MockConnRunnerMockRecorder) Retire(arg0 any) *MockConnRunnerRetireCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Retire", reflect.TypeOf((*MockConnRunner)(nil).Retire), arg0) return &MockConnRunnerRetireCall{Call: call} } // MockConnRunnerRetireCall wrap *gomock.Call type MockConnRunnerRetireCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockConnRunnerRetireCall) Return() *MockConnRunnerRetireCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockConnRunnerRetireCall) Do(f func(protocol.ConnectionID)) *MockConnRunnerRetireCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockConnRunnerRetireCall) DoAndReturn(f func(protocol.ConnectionID)) *MockConnRunnerRetireCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_frame_source_test.go000066400000000000000000000134361465664453100256670ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: FrameSource) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockFrameSource is a mock of FrameSource interface. type MockFrameSource struct { ctrl *gomock.Controller recorder *MockFrameSourceMockRecorder } // MockFrameSourceMockRecorder is the mock recorder for MockFrameSource. type MockFrameSourceMockRecorder struct { mock *MockFrameSource } // NewMockFrameSource creates a new mock instance. func NewMockFrameSource(ctrl *gomock.Controller) *MockFrameSource { mock := &MockFrameSource{ctrl: ctrl} mock.recorder = &MockFrameSourceMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockFrameSource) EXPECT() *MockFrameSourceMockRecorder { return m.recorder } // AppendControlFrames mocks base method. func (m *MockFrameSource) AppendControlFrames(arg0 []ackhandler.Frame, arg1 protocol.ByteCount, arg2 protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendControlFrames", arg0, arg1, arg2) ret0, _ := ret[0].([]ackhandler.Frame) ret1, _ := ret[1].(protocol.ByteCount) return ret0, ret1 } // AppendControlFrames indicates an expected call of AppendControlFrames. func (mr *MockFrameSourceMockRecorder) AppendControlFrames(arg0, arg1, arg2 any) *MockFrameSourceAppendControlFramesCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendControlFrames", reflect.TypeOf((*MockFrameSource)(nil).AppendControlFrames), arg0, arg1, arg2) return &MockFrameSourceAppendControlFramesCall{Call: call} } // MockFrameSourceAppendControlFramesCall wrap *gomock.Call type MockFrameSourceAppendControlFramesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockFrameSourceAppendControlFramesCall) Return(arg0 []ackhandler.Frame, arg1 protocol.ByteCount) *MockFrameSourceAppendControlFramesCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockFrameSourceAppendControlFramesCall) Do(f func([]ackhandler.Frame, protocol.ByteCount, protocol.Version) ([]ackhandler.Frame, protocol.ByteCount)) *MockFrameSourceAppendControlFramesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockFrameSourceAppendControlFramesCall) DoAndReturn(f func([]ackhandler.Frame, protocol.ByteCount, protocol.Version) ([]ackhandler.Frame, protocol.ByteCount)) *MockFrameSourceAppendControlFramesCall { c.Call = c.Call.DoAndReturn(f) return c } // AppendStreamFrames mocks base method. func (m *MockFrameSource) AppendStreamFrames(arg0 []ackhandler.StreamFrame, arg1 protocol.ByteCount, arg2 protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendStreamFrames", arg0, arg1, arg2) ret0, _ := ret[0].([]ackhandler.StreamFrame) ret1, _ := ret[1].(protocol.ByteCount) return ret0, ret1 } // AppendStreamFrames indicates an expected call of AppendStreamFrames. func (mr *MockFrameSourceMockRecorder) AppendStreamFrames(arg0, arg1, arg2 any) *MockFrameSourceAppendStreamFramesCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendStreamFrames", reflect.TypeOf((*MockFrameSource)(nil).AppendStreamFrames), arg0, arg1, arg2) return &MockFrameSourceAppendStreamFramesCall{Call: call} } // MockFrameSourceAppendStreamFramesCall wrap *gomock.Call type MockFrameSourceAppendStreamFramesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockFrameSourceAppendStreamFramesCall) Return(arg0 []ackhandler.StreamFrame, arg1 protocol.ByteCount) *MockFrameSourceAppendStreamFramesCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockFrameSourceAppendStreamFramesCall) Do(f func([]ackhandler.StreamFrame, protocol.ByteCount, protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount)) *MockFrameSourceAppendStreamFramesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockFrameSourceAppendStreamFramesCall) DoAndReturn(f func([]ackhandler.StreamFrame, protocol.ByteCount, protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount)) *MockFrameSourceAppendStreamFramesCall { c.Call = c.Call.DoAndReturn(f) return c } // HasData mocks base method. func (m *MockFrameSource) HasData() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasData") ret0, _ := ret[0].(bool) return ret0 } // HasData indicates an expected call of HasData. func (mr *MockFrameSourceMockRecorder) HasData() *MockFrameSourceHasDataCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasData", reflect.TypeOf((*MockFrameSource)(nil).HasData)) return &MockFrameSourceHasDataCall{Call: call} } // MockFrameSourceHasDataCall wrap *gomock.Call type MockFrameSourceHasDataCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockFrameSourceHasDataCall) Return(arg0 bool) *MockFrameSourceHasDataCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockFrameSourceHasDataCall) Do(f func() bool) *MockFrameSourceHasDataCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockFrameSourceHasDataCall) DoAndReturn(f func() bool) *MockFrameSourceHasDataCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_mtu_discoverer_test.go000066400000000000000000000141331465664453100262420ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: MTUDiscoverer) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" time "time" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockMTUDiscoverer is a mock of MTUDiscoverer interface. type MockMTUDiscoverer struct { ctrl *gomock.Controller recorder *MockMTUDiscovererMockRecorder } // MockMTUDiscovererMockRecorder is the mock recorder for MockMTUDiscoverer. type MockMTUDiscovererMockRecorder struct { mock *MockMTUDiscoverer } // NewMockMTUDiscoverer creates a new mock instance. func NewMockMTUDiscoverer(ctrl *gomock.Controller) *MockMTUDiscoverer { mock := &MockMTUDiscoverer{ctrl: ctrl} mock.recorder = &MockMTUDiscovererMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMTUDiscoverer) EXPECT() *MockMTUDiscovererMockRecorder { return m.recorder } // CurrentSize mocks base method. func (m *MockMTUDiscoverer) CurrentSize() protocol.ByteCount { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CurrentSize") ret0, _ := ret[0].(protocol.ByteCount) return ret0 } // CurrentSize indicates an expected call of CurrentSize. func (mr *MockMTUDiscovererMockRecorder) CurrentSize() *MockMTUDiscovererCurrentSizeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CurrentSize", reflect.TypeOf((*MockMTUDiscoverer)(nil).CurrentSize)) return &MockMTUDiscovererCurrentSizeCall{Call: call} } // MockMTUDiscovererCurrentSizeCall wrap *gomock.Call type MockMTUDiscovererCurrentSizeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockMTUDiscovererCurrentSizeCall) Return(arg0 protocol.ByteCount) *MockMTUDiscovererCurrentSizeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockMTUDiscovererCurrentSizeCall) Do(f func() protocol.ByteCount) *MockMTUDiscovererCurrentSizeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockMTUDiscovererCurrentSizeCall) DoAndReturn(f func() protocol.ByteCount) *MockMTUDiscovererCurrentSizeCall { c.Call = c.Call.DoAndReturn(f) return c } // GetPing mocks base method. func (m *MockMTUDiscoverer) GetPing() (ackhandler.Frame, protocol.ByteCount) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetPing") ret0, _ := ret[0].(ackhandler.Frame) ret1, _ := ret[1].(protocol.ByteCount) return ret0, ret1 } // GetPing indicates an expected call of GetPing. func (mr *MockMTUDiscovererMockRecorder) GetPing() *MockMTUDiscovererGetPingCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPing", reflect.TypeOf((*MockMTUDiscoverer)(nil).GetPing)) return &MockMTUDiscovererGetPingCall{Call: call} } // MockMTUDiscovererGetPingCall wrap *gomock.Call type MockMTUDiscovererGetPingCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockMTUDiscovererGetPingCall) Return(arg0 ackhandler.Frame, arg1 protocol.ByteCount) *MockMTUDiscovererGetPingCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockMTUDiscovererGetPingCall) Do(f func() (ackhandler.Frame, protocol.ByteCount)) *MockMTUDiscovererGetPingCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockMTUDiscovererGetPingCall) DoAndReturn(f func() (ackhandler.Frame, protocol.ByteCount)) *MockMTUDiscovererGetPingCall { c.Call = c.Call.DoAndReturn(f) return c } // ShouldSendProbe mocks base method. func (m *MockMTUDiscoverer) ShouldSendProbe(arg0 time.Time) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ShouldSendProbe", arg0) ret0, _ := ret[0].(bool) return ret0 } // ShouldSendProbe indicates an expected call of ShouldSendProbe. func (mr *MockMTUDiscovererMockRecorder) ShouldSendProbe(arg0 any) *MockMTUDiscovererShouldSendProbeCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShouldSendProbe", reflect.TypeOf((*MockMTUDiscoverer)(nil).ShouldSendProbe), arg0) return &MockMTUDiscovererShouldSendProbeCall{Call: call} } // MockMTUDiscovererShouldSendProbeCall wrap *gomock.Call type MockMTUDiscovererShouldSendProbeCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockMTUDiscovererShouldSendProbeCall) Return(arg0 bool) *MockMTUDiscovererShouldSendProbeCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockMTUDiscovererShouldSendProbeCall) Do(f func(time.Time) bool) *MockMTUDiscovererShouldSendProbeCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockMTUDiscovererShouldSendProbeCall) DoAndReturn(f func(time.Time) bool) *MockMTUDiscovererShouldSendProbeCall { c.Call = c.Call.DoAndReturn(f) return c } // Start mocks base method. func (m *MockMTUDiscoverer) Start() { m.ctrl.T.Helper() m.ctrl.Call(m, "Start") } // Start indicates an expected call of Start. func (mr *MockMTUDiscovererMockRecorder) Start() *MockMTUDiscovererStartCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockMTUDiscoverer)(nil).Start)) return &MockMTUDiscovererStartCall{Call: call} } // MockMTUDiscovererStartCall wrap *gomock.Call type MockMTUDiscovererStartCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockMTUDiscovererStartCall) Return() *MockMTUDiscovererStartCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockMTUDiscovererStartCall) Do(f func()) *MockMTUDiscovererStartCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockMTUDiscovererStartCall) DoAndReturn(f func()) *MockMTUDiscovererStartCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_packer_test.go000066400000000000000000000324501465664453100244570ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: Packer) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" protocol "github.com/quic-go/quic-go/internal/protocol" qerr "github.com/quic-go/quic-go/internal/qerr" gomock "go.uber.org/mock/gomock" ) // MockPacker is a mock of Packer interface. type MockPacker struct { ctrl *gomock.Controller recorder *MockPackerMockRecorder } // MockPackerMockRecorder is the mock recorder for MockPacker. type MockPackerMockRecorder struct { mock *MockPacker } // NewMockPacker creates a new mock instance. func NewMockPacker(ctrl *gomock.Controller) *MockPacker { mock := &MockPacker{ctrl: ctrl} mock.recorder = &MockPackerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPacker) EXPECT() *MockPackerMockRecorder { return m.recorder } // AppendPacket mocks base method. func (m *MockPacker) AppendPacket(arg0 *packetBuffer, arg1 protocol.ByteCount, arg2 protocol.Version) (shortHeaderPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendPacket", arg0, arg1, arg2) ret0, _ := ret[0].(shortHeaderPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // AppendPacket indicates an expected call of AppendPacket. func (mr *MockPackerMockRecorder) AppendPacket(arg0, arg1, arg2 any) *MockPackerAppendPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendPacket", reflect.TypeOf((*MockPacker)(nil).AppendPacket), arg0, arg1, arg2) return &MockPackerAppendPacketCall{Call: call} } // MockPackerAppendPacketCall wrap *gomock.Call type MockPackerAppendPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerAppendPacketCall) Return(arg0 shortHeaderPacket, arg1 error) *MockPackerAppendPacketCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerAppendPacketCall) Do(f func(*packetBuffer, protocol.ByteCount, protocol.Version) (shortHeaderPacket, error)) *MockPackerAppendPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerAppendPacketCall) DoAndReturn(f func(*packetBuffer, protocol.ByteCount, protocol.Version) (shortHeaderPacket, error)) *MockPackerAppendPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // MaybePackProbePacket mocks base method. func (m *MockPacker) MaybePackProbePacket(arg0 protocol.EncryptionLevel, arg1 protocol.ByteCount, arg2 protocol.Version) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaybePackProbePacket", arg0, arg1, arg2) ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // MaybePackProbePacket indicates an expected call of MaybePackProbePacket. func (mr *MockPackerMockRecorder) MaybePackProbePacket(arg0, arg1, arg2 any) *MockPackerMaybePackProbePacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybePackProbePacket", reflect.TypeOf((*MockPacker)(nil).MaybePackProbePacket), arg0, arg1, arg2) return &MockPackerMaybePackProbePacketCall{Call: call} } // MockPackerMaybePackProbePacketCall wrap *gomock.Call type MockPackerMaybePackProbePacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerMaybePackProbePacketCall) Return(arg0 *coalescedPacket, arg1 error) *MockPackerMaybePackProbePacketCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerMaybePackProbePacketCall) Do(f func(protocol.EncryptionLevel, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerMaybePackProbePacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerMaybePackProbePacketCall) DoAndReturn(f func(protocol.EncryptionLevel, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerMaybePackProbePacketCall { c.Call = c.Call.DoAndReturn(f) return c } // PackAckOnlyPacket mocks base method. func (m *MockPacker) PackAckOnlyPacket(arg0 protocol.ByteCount, arg1 protocol.Version) (shortHeaderPacket, *packetBuffer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackAckOnlyPacket", arg0, arg1) ret0, _ := ret[0].(shortHeaderPacket) ret1, _ := ret[1].(*packetBuffer) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // PackAckOnlyPacket indicates an expected call of PackAckOnlyPacket. func (mr *MockPackerMockRecorder) PackAckOnlyPacket(arg0, arg1 any) *MockPackerPackAckOnlyPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackAckOnlyPacket", reflect.TypeOf((*MockPacker)(nil).PackAckOnlyPacket), arg0, arg1) return &MockPackerPackAckOnlyPacketCall{Call: call} } // MockPackerPackAckOnlyPacketCall wrap *gomock.Call type MockPackerPackAckOnlyPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerPackAckOnlyPacketCall) Return(arg0 shortHeaderPacket, arg1 *packetBuffer, arg2 error) *MockPackerPackAckOnlyPacketCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerPackAckOnlyPacketCall) Do(f func(protocol.ByteCount, protocol.Version) (shortHeaderPacket, *packetBuffer, error)) *MockPackerPackAckOnlyPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerPackAckOnlyPacketCall) DoAndReturn(f func(protocol.ByteCount, protocol.Version) (shortHeaderPacket, *packetBuffer, error)) *MockPackerPackAckOnlyPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // PackApplicationClose mocks base method. func (m *MockPacker) PackApplicationClose(arg0 *qerr.ApplicationError, arg1 protocol.ByteCount, arg2 protocol.Version) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackApplicationClose", arg0, arg1, arg2) ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackApplicationClose indicates an expected call of PackApplicationClose. func (mr *MockPackerMockRecorder) PackApplicationClose(arg0, arg1, arg2 any) *MockPackerPackApplicationCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackApplicationClose", reflect.TypeOf((*MockPacker)(nil).PackApplicationClose), arg0, arg1, arg2) return &MockPackerPackApplicationCloseCall{Call: call} } // MockPackerPackApplicationCloseCall wrap *gomock.Call type MockPackerPackApplicationCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerPackApplicationCloseCall) Return(arg0 *coalescedPacket, arg1 error) *MockPackerPackApplicationCloseCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerPackApplicationCloseCall) Do(f func(*qerr.ApplicationError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerPackApplicationCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerPackApplicationCloseCall) DoAndReturn(f func(*qerr.ApplicationError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerPackApplicationCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // PackCoalescedPacket mocks base method. func (m *MockPacker) PackCoalescedPacket(arg0 bool, arg1 protocol.ByteCount, arg2 protocol.Version) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackCoalescedPacket", arg0, arg1, arg2) ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackCoalescedPacket indicates an expected call of PackCoalescedPacket. func (mr *MockPackerMockRecorder) PackCoalescedPacket(arg0, arg1, arg2 any) *MockPackerPackCoalescedPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackCoalescedPacket", reflect.TypeOf((*MockPacker)(nil).PackCoalescedPacket), arg0, arg1, arg2) return &MockPackerPackCoalescedPacketCall{Call: call} } // MockPackerPackCoalescedPacketCall wrap *gomock.Call type MockPackerPackCoalescedPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerPackCoalescedPacketCall) Return(arg0 *coalescedPacket, arg1 error) *MockPackerPackCoalescedPacketCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerPackCoalescedPacketCall) Do(f func(bool, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerPackCoalescedPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerPackCoalescedPacketCall) DoAndReturn(f func(bool, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerPackCoalescedPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // PackConnectionClose mocks base method. func (m *MockPacker) PackConnectionClose(arg0 *qerr.TransportError, arg1 protocol.ByteCount, arg2 protocol.Version) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackConnectionClose", arg0, arg1, arg2) ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackConnectionClose indicates an expected call of PackConnectionClose. func (mr *MockPackerMockRecorder) PackConnectionClose(arg0, arg1, arg2 any) *MockPackerPackConnectionCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackConnectionClose", reflect.TypeOf((*MockPacker)(nil).PackConnectionClose), arg0, arg1, arg2) return &MockPackerPackConnectionCloseCall{Call: call} } // MockPackerPackConnectionCloseCall wrap *gomock.Call type MockPackerPackConnectionCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerPackConnectionCloseCall) Return(arg0 *coalescedPacket, arg1 error) *MockPackerPackConnectionCloseCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerPackConnectionCloseCall) Do(f func(*qerr.TransportError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerPackConnectionCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerPackConnectionCloseCall) DoAndReturn(f func(*qerr.TransportError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error)) *MockPackerPackConnectionCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // PackMTUProbePacket mocks base method. func (m *MockPacker) PackMTUProbePacket(arg0 ackhandler.Frame, arg1 protocol.ByteCount, arg2 protocol.Version) (shortHeaderPacket, *packetBuffer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackMTUProbePacket", arg0, arg1, arg2) ret0, _ := ret[0].(shortHeaderPacket) ret1, _ := ret[1].(*packetBuffer) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // PackMTUProbePacket indicates an expected call of PackMTUProbePacket. func (mr *MockPackerMockRecorder) PackMTUProbePacket(arg0, arg1, arg2 any) *MockPackerPackMTUProbePacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackMTUProbePacket", reflect.TypeOf((*MockPacker)(nil).PackMTUProbePacket), arg0, arg1, arg2) return &MockPackerPackMTUProbePacketCall{Call: call} } // MockPackerPackMTUProbePacketCall wrap *gomock.Call type MockPackerPackMTUProbePacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerPackMTUProbePacketCall) Return(arg0 shortHeaderPacket, arg1 *packetBuffer, arg2 error) *MockPackerPackMTUProbePacketCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockPackerPackMTUProbePacketCall) Do(f func(ackhandler.Frame, protocol.ByteCount, protocol.Version) (shortHeaderPacket, *packetBuffer, error)) *MockPackerPackMTUProbePacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerPackMTUProbePacketCall) DoAndReturn(f func(ackhandler.Frame, protocol.ByteCount, protocol.Version) (shortHeaderPacket, *packetBuffer, error)) *MockPackerPackMTUProbePacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SetToken mocks base method. func (m *MockPacker) SetToken(arg0 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetToken", arg0) } // SetToken indicates an expected call of SetToken. func (mr *MockPackerMockRecorder) SetToken(arg0 any) *MockPackerSetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetToken", reflect.TypeOf((*MockPacker)(nil).SetToken), arg0) return &MockPackerSetTokenCall{Call: call} } // MockPackerSetTokenCall wrap *gomock.Call type MockPackerSetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPackerSetTokenCall) Return() *MockPackerSetTokenCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPackerSetTokenCall) Do(f func([]byte)) *MockPackerSetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPackerSetTokenCall) DoAndReturn(f func([]byte)) *MockPackerSetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_packet_handler_manager_test.go000066400000000000000000000405071465664453100276520ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: PacketHandlerManager) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockPacketHandlerManager is a mock of PacketHandlerManager interface. type MockPacketHandlerManager struct { ctrl *gomock.Controller recorder *MockPacketHandlerManagerMockRecorder } // MockPacketHandlerManagerMockRecorder is the mock recorder for MockPacketHandlerManager. type MockPacketHandlerManagerMockRecorder struct { mock *MockPacketHandlerManager } // NewMockPacketHandlerManager creates a new mock instance. func NewMockPacketHandlerManager(ctrl *gomock.Controller) *MockPacketHandlerManager { mock := &MockPacketHandlerManager{ctrl: ctrl} mock.recorder = &MockPacketHandlerManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPacketHandlerManager) EXPECT() *MockPacketHandlerManagerMockRecorder { return m.recorder } // Add mocks base method. func (m *MockPacketHandlerManager) Add(arg0 protocol.ConnectionID, arg1 packetHandler) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Add", arg0, arg1) ret0, _ := ret[0].(bool) return ret0 } // Add indicates an expected call of Add. func (mr *MockPacketHandlerManagerMockRecorder) Add(arg0, arg1 any) *MockPacketHandlerManagerAddCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockPacketHandlerManager)(nil).Add), arg0, arg1) return &MockPacketHandlerManagerAddCall{Call: call} } // MockPacketHandlerManagerAddCall wrap *gomock.Call type MockPacketHandlerManagerAddCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerAddCall) Return(arg0 bool) *MockPacketHandlerManagerAddCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerAddCall) Do(f func(protocol.ConnectionID, packetHandler) bool) *MockPacketHandlerManagerAddCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerAddCall) DoAndReturn(f func(protocol.ConnectionID, packetHandler) bool) *MockPacketHandlerManagerAddCall { c.Call = c.Call.DoAndReturn(f) return c } // AddResetToken mocks base method. func (m *MockPacketHandlerManager) AddResetToken(arg0 protocol.StatelessResetToken, arg1 packetHandler) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddResetToken", arg0, arg1) } // AddResetToken indicates an expected call of AddResetToken. func (mr *MockPacketHandlerManagerMockRecorder) AddResetToken(arg0, arg1 any) *MockPacketHandlerManagerAddResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).AddResetToken), arg0, arg1) return &MockPacketHandlerManagerAddResetTokenCall{Call: call} } // MockPacketHandlerManagerAddResetTokenCall wrap *gomock.Call type MockPacketHandlerManagerAddResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerAddResetTokenCall) Return() *MockPacketHandlerManagerAddResetTokenCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerAddResetTokenCall) Do(f func(protocol.StatelessResetToken, packetHandler)) *MockPacketHandlerManagerAddResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerAddResetTokenCall) DoAndReturn(f func(protocol.StatelessResetToken, packetHandler)) *MockPacketHandlerManagerAddResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // AddWithConnID mocks base method. func (m *MockPacketHandlerManager) AddWithConnID(arg0, arg1 protocol.ConnectionID, arg2 packetHandler) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddWithConnID", arg0, arg1, arg2) ret0, _ := ret[0].(bool) return ret0 } // AddWithConnID indicates an expected call of AddWithConnID. func (mr *MockPacketHandlerManagerMockRecorder) AddWithConnID(arg0, arg1, arg2 any) *MockPacketHandlerManagerAddWithConnIDCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWithConnID", reflect.TypeOf((*MockPacketHandlerManager)(nil).AddWithConnID), arg0, arg1, arg2) return &MockPacketHandlerManagerAddWithConnIDCall{Call: call} } // MockPacketHandlerManagerAddWithConnIDCall wrap *gomock.Call type MockPacketHandlerManagerAddWithConnIDCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerAddWithConnIDCall) Return(arg0 bool) *MockPacketHandlerManagerAddWithConnIDCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerAddWithConnIDCall) Do(f func(protocol.ConnectionID, protocol.ConnectionID, packetHandler) bool) *MockPacketHandlerManagerAddWithConnIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerAddWithConnIDCall) DoAndReturn(f func(protocol.ConnectionID, protocol.ConnectionID, packetHandler) bool) *MockPacketHandlerManagerAddWithConnIDCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockPacketHandlerManager) Close(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "Close", arg0) } // Close indicates an expected call of Close. func (mr *MockPacketHandlerManagerMockRecorder) Close(arg0 any) *MockPacketHandlerManagerCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPacketHandlerManager)(nil).Close), arg0) return &MockPacketHandlerManagerCloseCall{Call: call} } // MockPacketHandlerManagerCloseCall wrap *gomock.Call type MockPacketHandlerManagerCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerCloseCall) Return() *MockPacketHandlerManagerCloseCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerCloseCall) Do(f func(error)) *MockPacketHandlerManagerCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerCloseCall) DoAndReturn(f func(error)) *MockPacketHandlerManagerCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // Get mocks base method. func (m *MockPacketHandlerManager) Get(arg0 protocol.ConnectionID) (packetHandler, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", arg0) ret0, _ := ret[0].(packetHandler) ret1, _ := ret[1].(bool) return ret0, ret1 } // Get indicates an expected call of Get. func (mr *MockPacketHandlerManagerMockRecorder) Get(arg0 any) *MockPacketHandlerManagerGetCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockPacketHandlerManager)(nil).Get), arg0) return &MockPacketHandlerManagerGetCall{Call: call} } // MockPacketHandlerManagerGetCall wrap *gomock.Call type MockPacketHandlerManagerGetCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerGetCall) Return(arg0 packetHandler, arg1 bool) *MockPacketHandlerManagerGetCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerGetCall) Do(f func(protocol.ConnectionID) (packetHandler, bool)) *MockPacketHandlerManagerGetCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerGetCall) DoAndReturn(f func(protocol.ConnectionID) (packetHandler, bool)) *MockPacketHandlerManagerGetCall { c.Call = c.Call.DoAndReturn(f) return c } // GetByResetToken mocks base method. func (m *MockPacketHandlerManager) GetByResetToken(arg0 protocol.StatelessResetToken) (packetHandler, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetByResetToken", arg0) ret0, _ := ret[0].(packetHandler) ret1, _ := ret[1].(bool) return ret0, ret1 } // GetByResetToken indicates an expected call of GetByResetToken. func (mr *MockPacketHandlerManagerMockRecorder) GetByResetToken(arg0 any) *MockPacketHandlerManagerGetByResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetByResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).GetByResetToken), arg0) return &MockPacketHandlerManagerGetByResetTokenCall{Call: call} } // MockPacketHandlerManagerGetByResetTokenCall wrap *gomock.Call type MockPacketHandlerManagerGetByResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerGetByResetTokenCall) Return(arg0 packetHandler, arg1 bool) *MockPacketHandlerManagerGetByResetTokenCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerGetByResetTokenCall) Do(f func(protocol.StatelessResetToken) (packetHandler, bool)) *MockPacketHandlerManagerGetByResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerGetByResetTokenCall) DoAndReturn(f func(protocol.StatelessResetToken) (packetHandler, bool)) *MockPacketHandlerManagerGetByResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // GetStatelessResetToken mocks base method. func (m *MockPacketHandlerManager) GetStatelessResetToken(arg0 protocol.ConnectionID) protocol.StatelessResetToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetStatelessResetToken", arg0) ret0, _ := ret[0].(protocol.StatelessResetToken) return ret0 } // GetStatelessResetToken indicates an expected call of GetStatelessResetToken. func (mr *MockPacketHandlerManagerMockRecorder) GetStatelessResetToken(arg0 any) *MockPacketHandlerManagerGetStatelessResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).GetStatelessResetToken), arg0) return &MockPacketHandlerManagerGetStatelessResetTokenCall{Call: call} } // MockPacketHandlerManagerGetStatelessResetTokenCall wrap *gomock.Call type MockPacketHandlerManagerGetStatelessResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerGetStatelessResetTokenCall) Return(arg0 protocol.StatelessResetToken) *MockPacketHandlerManagerGetStatelessResetTokenCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerGetStatelessResetTokenCall) Do(f func(protocol.ConnectionID) protocol.StatelessResetToken) *MockPacketHandlerManagerGetStatelessResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerGetStatelessResetTokenCall) DoAndReturn(f func(protocol.ConnectionID) protocol.StatelessResetToken) *MockPacketHandlerManagerGetStatelessResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // Remove mocks base method. func (m *MockPacketHandlerManager) Remove(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "Remove", arg0) } // Remove indicates an expected call of Remove. func (mr *MockPacketHandlerManagerMockRecorder) Remove(arg0 any) *MockPacketHandlerManagerRemoveCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockPacketHandlerManager)(nil).Remove), arg0) return &MockPacketHandlerManagerRemoveCall{Call: call} } // MockPacketHandlerManagerRemoveCall wrap *gomock.Call type MockPacketHandlerManagerRemoveCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerRemoveCall) Return() *MockPacketHandlerManagerRemoveCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerRemoveCall) Do(f func(protocol.ConnectionID)) *MockPacketHandlerManagerRemoveCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerRemoveCall) DoAndReturn(f func(protocol.ConnectionID)) *MockPacketHandlerManagerRemoveCall { c.Call = c.Call.DoAndReturn(f) return c } // RemoveResetToken mocks base method. func (m *MockPacketHandlerManager) RemoveResetToken(arg0 protocol.StatelessResetToken) { m.ctrl.T.Helper() m.ctrl.Call(m, "RemoveResetToken", arg0) } // RemoveResetToken indicates an expected call of RemoveResetToken. func (mr *MockPacketHandlerManagerMockRecorder) RemoveResetToken(arg0 any) *MockPacketHandlerManagerRemoveResetTokenCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).RemoveResetToken), arg0) return &MockPacketHandlerManagerRemoveResetTokenCall{Call: call} } // MockPacketHandlerManagerRemoveResetTokenCall wrap *gomock.Call type MockPacketHandlerManagerRemoveResetTokenCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerRemoveResetTokenCall) Return() *MockPacketHandlerManagerRemoveResetTokenCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerRemoveResetTokenCall) Do(f func(protocol.StatelessResetToken)) *MockPacketHandlerManagerRemoveResetTokenCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerRemoveResetTokenCall) DoAndReturn(f func(protocol.StatelessResetToken)) *MockPacketHandlerManagerRemoveResetTokenCall { c.Call = c.Call.DoAndReturn(f) return c } // ReplaceWithClosed mocks base method. func (m *MockPacketHandlerManager) ReplaceWithClosed(arg0 []protocol.ConnectionID, arg1 []byte) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReplaceWithClosed", arg0, arg1) } // ReplaceWithClosed indicates an expected call of ReplaceWithClosed. func (mr *MockPacketHandlerManagerMockRecorder) ReplaceWithClosed(arg0, arg1 any) *MockPacketHandlerManagerReplaceWithClosedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceWithClosed", reflect.TypeOf((*MockPacketHandlerManager)(nil).ReplaceWithClosed), arg0, arg1) return &MockPacketHandlerManagerReplaceWithClosedCall{Call: call} } // MockPacketHandlerManagerReplaceWithClosedCall wrap *gomock.Call type MockPacketHandlerManagerReplaceWithClosedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerReplaceWithClosedCall) Return() *MockPacketHandlerManagerReplaceWithClosedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerReplaceWithClosedCall) Do(f func([]protocol.ConnectionID, []byte)) *MockPacketHandlerManagerReplaceWithClosedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerReplaceWithClosedCall) DoAndReturn(f func([]protocol.ConnectionID, []byte)) *MockPacketHandlerManagerReplaceWithClosedCall { c.Call = c.Call.DoAndReturn(f) return c } // Retire mocks base method. func (m *MockPacketHandlerManager) Retire(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "Retire", arg0) } // Retire indicates an expected call of Retire. func (mr *MockPacketHandlerManagerMockRecorder) Retire(arg0 any) *MockPacketHandlerManagerRetireCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Retire", reflect.TypeOf((*MockPacketHandlerManager)(nil).Retire), arg0) return &MockPacketHandlerManagerRetireCall{Call: call} } // MockPacketHandlerManagerRetireCall wrap *gomock.Call type MockPacketHandlerManagerRetireCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerManagerRetireCall) Return() *MockPacketHandlerManagerRetireCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerManagerRetireCall) Do(f func(protocol.ConnectionID)) *MockPacketHandlerManagerRetireCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerManagerRetireCall) DoAndReturn(f func(protocol.ConnectionID)) *MockPacketHandlerManagerRetireCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_packet_handler_test.go000066400000000000000000000115321465664453100261540ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: PacketHandler) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" qerr "github.com/quic-go/quic-go/internal/qerr" gomock "go.uber.org/mock/gomock" ) // MockPacketHandler is a mock of PacketHandler interface. type MockPacketHandler struct { ctrl *gomock.Controller recorder *MockPacketHandlerMockRecorder } // MockPacketHandlerMockRecorder is the mock recorder for MockPacketHandler. type MockPacketHandlerMockRecorder struct { mock *MockPacketHandler } // NewMockPacketHandler creates a new mock instance. func NewMockPacketHandler(ctrl *gomock.Controller) *MockPacketHandler { mock := &MockPacketHandler{ctrl: ctrl} mock.recorder = &MockPacketHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPacketHandler) EXPECT() *MockPacketHandlerMockRecorder { return m.recorder } // closeWithTransportError mocks base method. func (m *MockPacketHandler) closeWithTransportError(arg0 qerr.TransportErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "closeWithTransportError", arg0) } // closeWithTransportError indicates an expected call of closeWithTransportError. func (mr *MockPacketHandlerMockRecorder) closeWithTransportError(arg0 any) *MockPacketHandlercloseWithTransportErrorCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeWithTransportError", reflect.TypeOf((*MockPacketHandler)(nil).closeWithTransportError), arg0) return &MockPacketHandlercloseWithTransportErrorCall{Call: call} } // MockPacketHandlercloseWithTransportErrorCall wrap *gomock.Call type MockPacketHandlercloseWithTransportErrorCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlercloseWithTransportErrorCall) Return() *MockPacketHandlercloseWithTransportErrorCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlercloseWithTransportErrorCall) Do(f func(qerr.TransportErrorCode)) *MockPacketHandlercloseWithTransportErrorCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlercloseWithTransportErrorCall) DoAndReturn(f func(qerr.TransportErrorCode)) *MockPacketHandlercloseWithTransportErrorCall { c.Call = c.Call.DoAndReturn(f) return c } // destroy mocks base method. func (m *MockPacketHandler) destroy(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "destroy", arg0) } // destroy indicates an expected call of destroy. func (mr *MockPacketHandlerMockRecorder) destroy(arg0 any) *MockPacketHandlerdestroyCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "destroy", reflect.TypeOf((*MockPacketHandler)(nil).destroy), arg0) return &MockPacketHandlerdestroyCall{Call: call} } // MockPacketHandlerdestroyCall wrap *gomock.Call type MockPacketHandlerdestroyCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerdestroyCall) Return() *MockPacketHandlerdestroyCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerdestroyCall) Do(f func(error)) *MockPacketHandlerdestroyCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerdestroyCall) DoAndReturn(f func(error)) *MockPacketHandlerdestroyCall { c.Call = c.Call.DoAndReturn(f) return c } // handlePacket mocks base method. func (m *MockPacketHandler) handlePacket(arg0 receivedPacket) { m.ctrl.T.Helper() m.ctrl.Call(m, "handlePacket", arg0) } // handlePacket indicates an expected call of handlePacket. func (mr *MockPacketHandlerMockRecorder) handlePacket(arg0 any) *MockPacketHandlerhandlePacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handlePacket", reflect.TypeOf((*MockPacketHandler)(nil).handlePacket), arg0) return &MockPacketHandlerhandlePacketCall{Call: call} } // MockPacketHandlerhandlePacketCall wrap *gomock.Call type MockPacketHandlerhandlePacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketHandlerhandlePacketCall) Return() *MockPacketHandlerhandlePacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockPacketHandlerhandlePacketCall) Do(f func(receivedPacket)) *MockPacketHandlerhandlePacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketHandlerhandlePacketCall) DoAndReturn(f func(receivedPacket)) *MockPacketHandlerhandlePacketCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_packetconn_test.go000066400000000000000000000227151465664453100253420ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: net (interfaces: PacketConn) // // Generated by this command: // // mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn // // Package quic is a generated GoMock package. package quic import ( net "net" reflect "reflect" time "time" gomock "go.uber.org/mock/gomock" ) // MockPacketConn is a mock of PacketConn interface. type MockPacketConn struct { ctrl *gomock.Controller recorder *MockPacketConnMockRecorder } // MockPacketConnMockRecorder is the mock recorder for MockPacketConn. type MockPacketConnMockRecorder struct { mock *MockPacketConn } // NewMockPacketConn creates a new mock instance. func NewMockPacketConn(ctrl *gomock.Controller) *MockPacketConn { mock := &MockPacketConn{ctrl: ctrl} mock.recorder = &MockPacketConnMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockPacketConn) EXPECT() *MockPacketConnMockRecorder { return m.recorder } // Close mocks base method. func (m *MockPacketConn) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockPacketConnMockRecorder) Close() *MockPacketConnCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPacketConn)(nil).Close)) return &MockPacketConnCloseCall{Call: call} } // MockPacketConnCloseCall wrap *gomock.Call type MockPacketConnCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnCloseCall) Return(arg0 error) *MockPacketConnCloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnCloseCall) Do(f func() error) *MockPacketConnCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnCloseCall) DoAndReturn(f func() error) *MockPacketConnCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // LocalAddr mocks base method. func (m *MockPacketConn) LocalAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LocalAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // LocalAddr indicates an expected call of LocalAddr. func (mr *MockPacketConnMockRecorder) LocalAddr() *MockPacketConnLocalAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockPacketConn)(nil).LocalAddr)) return &MockPacketConnLocalAddrCall{Call: call} } // MockPacketConnLocalAddrCall wrap *gomock.Call type MockPacketConnLocalAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnLocalAddrCall) Return(arg0 net.Addr) *MockPacketConnLocalAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnLocalAddrCall) Do(f func() net.Addr) *MockPacketConnLocalAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnLocalAddrCall) DoAndReturn(f func() net.Addr) *MockPacketConnLocalAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // ReadFrom mocks base method. func (m *MockPacketConn) ReadFrom(arg0 []byte) (int, net.Addr, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadFrom", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(net.Addr) ret2, _ := ret[2].(error) return ret0, ret1, ret2 } // ReadFrom indicates an expected call of ReadFrom. func (mr *MockPacketConnMockRecorder) ReadFrom(arg0 any) *MockPacketConnReadFromCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFrom", reflect.TypeOf((*MockPacketConn)(nil).ReadFrom), arg0) return &MockPacketConnReadFromCall{Call: call} } // MockPacketConnReadFromCall wrap *gomock.Call type MockPacketConnReadFromCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnReadFromCall) Return(arg0 int, arg1 net.Addr, arg2 error) *MockPacketConnReadFromCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnReadFromCall) Do(f func([]byte) (int, net.Addr, error)) *MockPacketConnReadFromCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnReadFromCall) DoAndReturn(f func([]byte) (int, net.Addr, error)) *MockPacketConnReadFromCall { c.Call = c.Call.DoAndReturn(f) return c } // SetDeadline mocks base method. func (m *MockPacketConn) SetDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetDeadline indicates an expected call of SetDeadline. func (mr *MockPacketConnMockRecorder) SetDeadline(arg0 any) *MockPacketConnSetDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockPacketConn)(nil).SetDeadline), arg0) return &MockPacketConnSetDeadlineCall{Call: call} } // MockPacketConnSetDeadlineCall wrap *gomock.Call type MockPacketConnSetDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnSetDeadlineCall) Return(arg0 error) *MockPacketConnSetDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnSetDeadlineCall) Do(f func(time.Time) error) *MockPacketConnSetDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnSetDeadlineCall) DoAndReturn(f func(time.Time) error) *MockPacketConnSetDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // SetReadDeadline mocks base method. func (m *MockPacketConn) SetReadDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockPacketConnMockRecorder) SetReadDeadline(arg0 any) *MockPacketConnSetReadDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockPacketConn)(nil).SetReadDeadline), arg0) return &MockPacketConnSetReadDeadlineCall{Call: call} } // MockPacketConnSetReadDeadlineCall wrap *gomock.Call type MockPacketConnSetReadDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnSetReadDeadlineCall) Return(arg0 error) *MockPacketConnSetReadDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnSetReadDeadlineCall) Do(f func(time.Time) error) *MockPacketConnSetReadDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnSetReadDeadlineCall) DoAndReturn(f func(time.Time) error) *MockPacketConnSetReadDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // SetWriteDeadline mocks base method. func (m *MockPacketConn) SetWriteDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetWriteDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetWriteDeadline indicates an expected call of SetWriteDeadline. func (mr *MockPacketConnMockRecorder) SetWriteDeadline(arg0 any) *MockPacketConnSetWriteDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockPacketConn)(nil).SetWriteDeadline), arg0) return &MockPacketConnSetWriteDeadlineCall{Call: call} } // MockPacketConnSetWriteDeadlineCall wrap *gomock.Call type MockPacketConnSetWriteDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnSetWriteDeadlineCall) Return(arg0 error) *MockPacketConnSetWriteDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnSetWriteDeadlineCall) Do(f func(time.Time) error) *MockPacketConnSetWriteDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnSetWriteDeadlineCall) DoAndReturn(f func(time.Time) error) *MockPacketConnSetWriteDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // WriteTo mocks base method. func (m *MockPacketConn) WriteTo(arg0 []byte, arg1 net.Addr) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteTo", arg0, arg1) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WriteTo indicates an expected call of WriteTo. func (mr *MockPacketConnMockRecorder) WriteTo(arg0, arg1 any) *MockPacketConnWriteToCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteTo", reflect.TypeOf((*MockPacketConn)(nil).WriteTo), arg0, arg1) return &MockPacketConnWriteToCall{Call: call} } // MockPacketConnWriteToCall wrap *gomock.Call type MockPacketConnWriteToCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockPacketConnWriteToCall) Return(arg0 int, arg1 error) *MockPacketConnWriteToCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockPacketConnWriteToCall) Do(f func([]byte, net.Addr) (int, error)) *MockPacketConnWriteToCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockPacketConnWriteToCall) DoAndReturn(f func([]byte, net.Addr) (int, error)) *MockPacketConnWriteToCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_quic_conn_test.go000066400000000000000000000627131465664453100251750ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: QUICConn) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_quic_conn_test.go github.com/quic-go/quic-go QUICConn // // Package quic is a generated GoMock package. package quic import ( context "context" net "net" reflect "reflect" qerr "github.com/quic-go/quic-go/internal/qerr" gomock "go.uber.org/mock/gomock" ) // MockQUICConn is a mock of QUICConn interface. type MockQUICConn struct { ctrl *gomock.Controller recorder *MockQUICConnMockRecorder } // MockQUICConnMockRecorder is the mock recorder for MockQUICConn. type MockQUICConnMockRecorder struct { mock *MockQUICConn } // NewMockQUICConn creates a new mock instance. func NewMockQUICConn(ctrl *gomock.Controller) *MockQUICConn { mock := &MockQUICConn{ctrl: ctrl} mock.recorder = &MockQUICConnMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQUICConn) EXPECT() *MockQUICConnMockRecorder { return m.recorder } // AcceptStream mocks base method. func (m *MockQUICConn) AcceptStream(arg0 context.Context) (Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AcceptStream", arg0) ret0, _ := ret[0].(Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // AcceptStream indicates an expected call of AcceptStream. func (mr *MockQUICConnMockRecorder) AcceptStream(arg0 any) *MockQUICConnAcceptStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptStream", reflect.TypeOf((*MockQUICConn)(nil).AcceptStream), arg0) return &MockQUICConnAcceptStreamCall{Call: call} } // MockQUICConnAcceptStreamCall wrap *gomock.Call type MockQUICConnAcceptStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnAcceptStreamCall) Return(arg0 Stream, arg1 error) *MockQUICConnAcceptStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnAcceptStreamCall) Do(f func(context.Context) (Stream, error)) *MockQUICConnAcceptStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnAcceptStreamCall) DoAndReturn(f func(context.Context) (Stream, error)) *MockQUICConnAcceptStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // AcceptUniStream mocks base method. func (m *MockQUICConn) AcceptUniStream(arg0 context.Context) (ReceiveStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AcceptUniStream", arg0) ret0, _ := ret[0].(ReceiveStream) ret1, _ := ret[1].(error) return ret0, ret1 } // AcceptUniStream indicates an expected call of AcceptUniStream. func (mr *MockQUICConnMockRecorder) AcceptUniStream(arg0 any) *MockQUICConnAcceptUniStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUniStream", reflect.TypeOf((*MockQUICConn)(nil).AcceptUniStream), arg0) return &MockQUICConnAcceptUniStreamCall{Call: call} } // MockQUICConnAcceptUniStreamCall wrap *gomock.Call type MockQUICConnAcceptUniStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnAcceptUniStreamCall) Return(arg0 ReceiveStream, arg1 error) *MockQUICConnAcceptUniStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnAcceptUniStreamCall) Do(f func(context.Context) (ReceiveStream, error)) *MockQUICConnAcceptUniStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnAcceptUniStreamCall) DoAndReturn(f func(context.Context) (ReceiveStream, error)) *MockQUICConnAcceptUniStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // CloseWithError mocks base method. func (m *MockQUICConn) CloseWithError(arg0 qerr.ApplicationErrorCode, arg1 string) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "CloseWithError", arg0, arg1) ret0, _ := ret[0].(error) return ret0 } // CloseWithError indicates an expected call of CloseWithError. func (mr *MockQUICConnMockRecorder) CloseWithError(arg0, arg1 any) *MockQUICConnCloseWithErrorCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseWithError", reflect.TypeOf((*MockQUICConn)(nil).CloseWithError), arg0, arg1) return &MockQUICConnCloseWithErrorCall{Call: call} } // MockQUICConnCloseWithErrorCall wrap *gomock.Call type MockQUICConnCloseWithErrorCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnCloseWithErrorCall) Return(arg0 error) *MockQUICConnCloseWithErrorCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnCloseWithErrorCall) Do(f func(qerr.ApplicationErrorCode, string) error) *MockQUICConnCloseWithErrorCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnCloseWithErrorCall) DoAndReturn(f func(qerr.ApplicationErrorCode, string) error) *MockQUICConnCloseWithErrorCall { c.Call = c.Call.DoAndReturn(f) return c } // ConnectionState mocks base method. func (m *MockQUICConn) ConnectionState() ConnectionState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConnectionState") ret0, _ := ret[0].(ConnectionState) return ret0 } // ConnectionState indicates an expected call of ConnectionState. func (mr *MockQUICConnMockRecorder) ConnectionState() *MockQUICConnConnectionStateCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockQUICConn)(nil).ConnectionState)) return &MockQUICConnConnectionStateCall{Call: call} } // MockQUICConnConnectionStateCall wrap *gomock.Call type MockQUICConnConnectionStateCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnConnectionStateCall) Return(arg0 ConnectionState) *MockQUICConnConnectionStateCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnConnectionStateCall) Do(f func() ConnectionState) *MockQUICConnConnectionStateCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnConnectionStateCall) DoAndReturn(f func() ConnectionState) *MockQUICConnConnectionStateCall { c.Call = c.Call.DoAndReturn(f) return c } // Context mocks base method. func (m *MockQUICConn) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockQUICConnMockRecorder) Context() *MockQUICConnContextCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockQUICConn)(nil).Context)) return &MockQUICConnContextCall{Call: call} } // MockQUICConnContextCall wrap *gomock.Call type MockQUICConnContextCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnContextCall) Return(arg0 context.Context) *MockQUICConnContextCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnContextCall) Do(f func() context.Context) *MockQUICConnContextCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnContextCall) DoAndReturn(f func() context.Context) *MockQUICConnContextCall { c.Call = c.Call.DoAndReturn(f) return c } // HandshakeComplete mocks base method. func (m *MockQUICConn) HandshakeComplete() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandshakeComplete") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // HandshakeComplete indicates an expected call of HandshakeComplete. func (mr *MockQUICConnMockRecorder) HandshakeComplete() *MockQUICConnHandshakeCompleteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandshakeComplete", reflect.TypeOf((*MockQUICConn)(nil).HandshakeComplete)) return &MockQUICConnHandshakeCompleteCall{Call: call} } // MockQUICConnHandshakeCompleteCall wrap *gomock.Call type MockQUICConnHandshakeCompleteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnHandshakeCompleteCall) Return(arg0 <-chan struct{}) *MockQUICConnHandshakeCompleteCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnHandshakeCompleteCall) Do(f func() <-chan struct{}) *MockQUICConnHandshakeCompleteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnHandshakeCompleteCall) DoAndReturn(f func() <-chan struct{}) *MockQUICConnHandshakeCompleteCall { c.Call = c.Call.DoAndReturn(f) return c } // LocalAddr mocks base method. func (m *MockQUICConn) LocalAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LocalAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // LocalAddr indicates an expected call of LocalAddr. func (mr *MockQUICConnMockRecorder) LocalAddr() *MockQUICConnLocalAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockQUICConn)(nil).LocalAddr)) return &MockQUICConnLocalAddrCall{Call: call} } // MockQUICConnLocalAddrCall wrap *gomock.Call type MockQUICConnLocalAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnLocalAddrCall) Return(arg0 net.Addr) *MockQUICConnLocalAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnLocalAddrCall) Do(f func() net.Addr) *MockQUICConnLocalAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnLocalAddrCall) DoAndReturn(f func() net.Addr) *MockQUICConnLocalAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // NextConnection mocks base method. func (m *MockQUICConn) NextConnection(arg0 context.Context) (Connection, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NextConnection", arg0) ret0, _ := ret[0].(Connection) ret1, _ := ret[1].(error) return ret0, ret1 } // NextConnection indicates an expected call of NextConnection. func (mr *MockQUICConnMockRecorder) NextConnection(arg0 any) *MockQUICConnNextConnectionCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextConnection", reflect.TypeOf((*MockQUICConn)(nil).NextConnection), arg0) return &MockQUICConnNextConnectionCall{Call: call} } // MockQUICConnNextConnectionCall wrap *gomock.Call type MockQUICConnNextConnectionCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnNextConnectionCall) Return(arg0 Connection, arg1 error) *MockQUICConnNextConnectionCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnNextConnectionCall) Do(f func(context.Context) (Connection, error)) *MockQUICConnNextConnectionCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnNextConnectionCall) DoAndReturn(f func(context.Context) (Connection, error)) *MockQUICConnNextConnectionCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenStream mocks base method. func (m *MockQUICConn) OpenStream() (Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenStream") ret0, _ := ret[0].(Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenStream indicates an expected call of OpenStream. func (mr *MockQUICConnMockRecorder) OpenStream() *MockQUICConnOpenStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockQUICConn)(nil).OpenStream)) return &MockQUICConnOpenStreamCall{Call: call} } // MockQUICConnOpenStreamCall wrap *gomock.Call type MockQUICConnOpenStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnOpenStreamCall) Return(arg0 Stream, arg1 error) *MockQUICConnOpenStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnOpenStreamCall) Do(f func() (Stream, error)) *MockQUICConnOpenStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnOpenStreamCall) DoAndReturn(f func() (Stream, error)) *MockQUICConnOpenStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenStreamSync mocks base method. func (m *MockQUICConn) OpenStreamSync(arg0 context.Context) (Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenStreamSync", arg0) ret0, _ := ret[0].(Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenStreamSync indicates an expected call of OpenStreamSync. func (mr *MockQUICConnMockRecorder) OpenStreamSync(arg0 any) *MockQUICConnOpenStreamSyncCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStreamSync", reflect.TypeOf((*MockQUICConn)(nil).OpenStreamSync), arg0) return &MockQUICConnOpenStreamSyncCall{Call: call} } // MockQUICConnOpenStreamSyncCall wrap *gomock.Call type MockQUICConnOpenStreamSyncCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnOpenStreamSyncCall) Return(arg0 Stream, arg1 error) *MockQUICConnOpenStreamSyncCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnOpenStreamSyncCall) Do(f func(context.Context) (Stream, error)) *MockQUICConnOpenStreamSyncCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnOpenStreamSyncCall) DoAndReturn(f func(context.Context) (Stream, error)) *MockQUICConnOpenStreamSyncCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenUniStream mocks base method. func (m *MockQUICConn) OpenUniStream() (SendStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenUniStream") ret0, _ := ret[0].(SendStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenUniStream indicates an expected call of OpenUniStream. func (mr *MockQUICConnMockRecorder) OpenUniStream() *MockQUICConnOpenUniStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStream", reflect.TypeOf((*MockQUICConn)(nil).OpenUniStream)) return &MockQUICConnOpenUniStreamCall{Call: call} } // MockQUICConnOpenUniStreamCall wrap *gomock.Call type MockQUICConnOpenUniStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnOpenUniStreamCall) Return(arg0 SendStream, arg1 error) *MockQUICConnOpenUniStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnOpenUniStreamCall) Do(f func() (SendStream, error)) *MockQUICConnOpenUniStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnOpenUniStreamCall) DoAndReturn(f func() (SendStream, error)) *MockQUICConnOpenUniStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenUniStreamSync mocks base method. func (m *MockQUICConn) OpenUniStreamSync(arg0 context.Context) (SendStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenUniStreamSync", arg0) ret0, _ := ret[0].(SendStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenUniStreamSync indicates an expected call of OpenUniStreamSync. func (mr *MockQUICConnMockRecorder) OpenUniStreamSync(arg0 any) *MockQUICConnOpenUniStreamSyncCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStreamSync", reflect.TypeOf((*MockQUICConn)(nil).OpenUniStreamSync), arg0) return &MockQUICConnOpenUniStreamSyncCall{Call: call} } // MockQUICConnOpenUniStreamSyncCall wrap *gomock.Call type MockQUICConnOpenUniStreamSyncCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnOpenUniStreamSyncCall) Return(arg0 SendStream, arg1 error) *MockQUICConnOpenUniStreamSyncCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnOpenUniStreamSyncCall) Do(f func(context.Context) (SendStream, error)) *MockQUICConnOpenUniStreamSyncCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnOpenUniStreamSyncCall) DoAndReturn(f func(context.Context) (SendStream, error)) *MockQUICConnOpenUniStreamSyncCall { c.Call = c.Call.DoAndReturn(f) return c } // ReceiveDatagram mocks base method. func (m *MockQUICConn) ReceiveDatagram(arg0 context.Context) ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReceiveDatagram", arg0) ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // ReceiveDatagram indicates an expected call of ReceiveDatagram. func (mr *MockQUICConnMockRecorder) ReceiveDatagram(arg0 any) *MockQUICConnReceiveDatagramCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveDatagram", reflect.TypeOf((*MockQUICConn)(nil).ReceiveDatagram), arg0) return &MockQUICConnReceiveDatagramCall{Call: call} } // MockQUICConnReceiveDatagramCall wrap *gomock.Call type MockQUICConnReceiveDatagramCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnReceiveDatagramCall) Return(arg0 []byte, arg1 error) *MockQUICConnReceiveDatagramCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnReceiveDatagramCall) Do(f func(context.Context) ([]byte, error)) *MockQUICConnReceiveDatagramCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnReceiveDatagramCall) DoAndReturn(f func(context.Context) ([]byte, error)) *MockQUICConnReceiveDatagramCall { c.Call = c.Call.DoAndReturn(f) return c } // RemoteAddr mocks base method. func (m *MockQUICConn) RemoteAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoteAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // RemoteAddr indicates an expected call of RemoteAddr. func (mr *MockQUICConnMockRecorder) RemoteAddr() *MockQUICConnRemoteAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockQUICConn)(nil).RemoteAddr)) return &MockQUICConnRemoteAddrCall{Call: call} } // MockQUICConnRemoteAddrCall wrap *gomock.Call type MockQUICConnRemoteAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnRemoteAddrCall) Return(arg0 net.Addr) *MockQUICConnRemoteAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnRemoteAddrCall) Do(f func() net.Addr) *MockQUICConnRemoteAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnRemoteAddrCall) DoAndReturn(f func() net.Addr) *MockQUICConnRemoteAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // SendDatagram mocks base method. func (m *MockQUICConn) SendDatagram(arg0 []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendDatagram", arg0) ret0, _ := ret[0].(error) return ret0 } // SendDatagram indicates an expected call of SendDatagram. func (mr *MockQUICConnMockRecorder) SendDatagram(arg0 any) *MockQUICConnSendDatagramCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendDatagram", reflect.TypeOf((*MockQUICConn)(nil).SendDatagram), arg0) return &MockQUICConnSendDatagramCall{Call: call} } // MockQUICConnSendDatagramCall wrap *gomock.Call type MockQUICConnSendDatagramCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnSendDatagramCall) Return(arg0 error) *MockQUICConnSendDatagramCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnSendDatagramCall) Do(f func([]byte) error) *MockQUICConnSendDatagramCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnSendDatagramCall) DoAndReturn(f func([]byte) error) *MockQUICConnSendDatagramCall { c.Call = c.Call.DoAndReturn(f) return c } // closeWithTransportError mocks base method. func (m *MockQUICConn) closeWithTransportError(arg0 qerr.TransportErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "closeWithTransportError", arg0) } // closeWithTransportError indicates an expected call of closeWithTransportError. func (mr *MockQUICConnMockRecorder) closeWithTransportError(arg0 any) *MockQUICConncloseWithTransportErrorCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeWithTransportError", reflect.TypeOf((*MockQUICConn)(nil).closeWithTransportError), arg0) return &MockQUICConncloseWithTransportErrorCall{Call: call} } // MockQUICConncloseWithTransportErrorCall wrap *gomock.Call type MockQUICConncloseWithTransportErrorCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConncloseWithTransportErrorCall) Return() *MockQUICConncloseWithTransportErrorCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConncloseWithTransportErrorCall) Do(f func(qerr.TransportErrorCode)) *MockQUICConncloseWithTransportErrorCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConncloseWithTransportErrorCall) DoAndReturn(f func(qerr.TransportErrorCode)) *MockQUICConncloseWithTransportErrorCall { c.Call = c.Call.DoAndReturn(f) return c } // destroy mocks base method. func (m *MockQUICConn) destroy(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "destroy", arg0) } // destroy indicates an expected call of destroy. func (mr *MockQUICConnMockRecorder) destroy(arg0 any) *MockQUICConndestroyCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "destroy", reflect.TypeOf((*MockQUICConn)(nil).destroy), arg0) return &MockQUICConndestroyCall{Call: call} } // MockQUICConndestroyCall wrap *gomock.Call type MockQUICConndestroyCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConndestroyCall) Return() *MockQUICConndestroyCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConndestroyCall) Do(f func(error)) *MockQUICConndestroyCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConndestroyCall) DoAndReturn(f func(error)) *MockQUICConndestroyCall { c.Call = c.Call.DoAndReturn(f) return c } // earlyConnReady mocks base method. func (m *MockQUICConn) earlyConnReady() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "earlyConnReady") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // earlyConnReady indicates an expected call of earlyConnReady. func (mr *MockQUICConnMockRecorder) earlyConnReady() *MockQUICConnearlyConnReadyCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "earlyConnReady", reflect.TypeOf((*MockQUICConn)(nil).earlyConnReady)) return &MockQUICConnearlyConnReadyCall{Call: call} } // MockQUICConnearlyConnReadyCall wrap *gomock.Call type MockQUICConnearlyConnReadyCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnearlyConnReadyCall) Return(arg0 <-chan struct{}) *MockQUICConnearlyConnReadyCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnearlyConnReadyCall) Do(f func() <-chan struct{}) *MockQUICConnearlyConnReadyCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnearlyConnReadyCall) DoAndReturn(f func() <-chan struct{}) *MockQUICConnearlyConnReadyCall { c.Call = c.Call.DoAndReturn(f) return c } // handlePacket mocks base method. func (m *MockQUICConn) handlePacket(arg0 receivedPacket) { m.ctrl.T.Helper() m.ctrl.Call(m, "handlePacket", arg0) } // handlePacket indicates an expected call of handlePacket. func (mr *MockQUICConnMockRecorder) handlePacket(arg0 any) *MockQUICConnhandlePacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handlePacket", reflect.TypeOf((*MockQUICConn)(nil).handlePacket), arg0) return &MockQUICConnhandlePacketCall{Call: call} } // MockQUICConnhandlePacketCall wrap *gomock.Call type MockQUICConnhandlePacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnhandlePacketCall) Return() *MockQUICConnhandlePacketCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnhandlePacketCall) Do(f func(receivedPacket)) *MockQUICConnhandlePacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnhandlePacketCall) DoAndReturn(f func(receivedPacket)) *MockQUICConnhandlePacketCall { c.Call = c.Call.DoAndReturn(f) return c } // run mocks base method. func (m *MockQUICConn) run() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "run") ret0, _ := ret[0].(error) return ret0 } // run indicates an expected call of run. func (mr *MockQUICConnMockRecorder) run() *MockQUICConnrunCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "run", reflect.TypeOf((*MockQUICConn)(nil).run)) return &MockQUICConnrunCall{Call: call} } // MockQUICConnrunCall wrap *gomock.Call type MockQUICConnrunCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockQUICConnrunCall) Return(arg0 error) *MockQUICConnrunCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockQUICConnrunCall) Do(f func() error) *MockQUICConnrunCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockQUICConnrunCall) DoAndReturn(f func() error) *MockQUICConnrunCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_raw_conn_test.go000066400000000000000000000203021465664453100250110ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: RawConn) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_raw_conn_test.go github.com/quic-go/quic-go RawConn // // Package quic is a generated GoMock package. package quic import ( net "net" reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockRawConn is a mock of RawConn interface. type MockRawConn struct { ctrl *gomock.Controller recorder *MockRawConnMockRecorder } // MockRawConnMockRecorder is the mock recorder for MockRawConn. type MockRawConnMockRecorder struct { mock *MockRawConn } // NewMockRawConn creates a new mock instance. func NewMockRawConn(ctrl *gomock.Controller) *MockRawConn { mock := &MockRawConn{ctrl: ctrl} mock.recorder = &MockRawConnMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockRawConn) EXPECT() *MockRawConnMockRecorder { return m.recorder } // Close mocks base method. func (m *MockRawConn) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockRawConnMockRecorder) Close() *MockRawConnCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockRawConn)(nil).Close)) return &MockRawConnCloseCall{Call: call} } // MockRawConnCloseCall wrap *gomock.Call type MockRawConnCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockRawConnCloseCall) Return(arg0 error) *MockRawConnCloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockRawConnCloseCall) Do(f func() error) *MockRawConnCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockRawConnCloseCall) DoAndReturn(f func() error) *MockRawConnCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // LocalAddr mocks base method. func (m *MockRawConn) LocalAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LocalAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // LocalAddr indicates an expected call of LocalAddr. func (mr *MockRawConnMockRecorder) LocalAddr() *MockRawConnLocalAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockRawConn)(nil).LocalAddr)) return &MockRawConnLocalAddrCall{Call: call} } // MockRawConnLocalAddrCall wrap *gomock.Call type MockRawConnLocalAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockRawConnLocalAddrCall) Return(arg0 net.Addr) *MockRawConnLocalAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockRawConnLocalAddrCall) Do(f func() net.Addr) *MockRawConnLocalAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockRawConnLocalAddrCall) DoAndReturn(f func() net.Addr) *MockRawConnLocalAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // ReadPacket mocks base method. func (m *MockRawConn) ReadPacket() (receivedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReadPacket") ret0, _ := ret[0].(receivedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // ReadPacket indicates an expected call of ReadPacket. func (mr *MockRawConnMockRecorder) ReadPacket() *MockRawConnReadPacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadPacket", reflect.TypeOf((*MockRawConn)(nil).ReadPacket)) return &MockRawConnReadPacketCall{Call: call} } // MockRawConnReadPacketCall wrap *gomock.Call type MockRawConnReadPacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockRawConnReadPacketCall) Return(arg0 receivedPacket, arg1 error) *MockRawConnReadPacketCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockRawConnReadPacketCall) Do(f func() (receivedPacket, error)) *MockRawConnReadPacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockRawConnReadPacketCall) DoAndReturn(f func() (receivedPacket, error)) *MockRawConnReadPacketCall { c.Call = c.Call.DoAndReturn(f) return c } // SetReadDeadline mocks base method. func (m *MockRawConn) SetReadDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockRawConnMockRecorder) SetReadDeadline(arg0 any) *MockRawConnSetReadDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockRawConn)(nil).SetReadDeadline), arg0) return &MockRawConnSetReadDeadlineCall{Call: call} } // MockRawConnSetReadDeadlineCall wrap *gomock.Call type MockRawConnSetReadDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockRawConnSetReadDeadlineCall) Return(arg0 error) *MockRawConnSetReadDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockRawConnSetReadDeadlineCall) Do(f func(time.Time) error) *MockRawConnSetReadDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockRawConnSetReadDeadlineCall) DoAndReturn(f func(time.Time) error) *MockRawConnSetReadDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // WritePacket mocks base method. func (m *MockRawConn) WritePacket(arg0 []byte, arg1 net.Addr, arg2 []byte, arg3 uint16, arg4 protocol.ECN) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WritePacket", arg0, arg1, arg2, arg3, arg4) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // WritePacket indicates an expected call of WritePacket. func (mr *MockRawConnMockRecorder) WritePacket(arg0, arg1, arg2, arg3, arg4 any) *MockRawConnWritePacketCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WritePacket", reflect.TypeOf((*MockRawConn)(nil).WritePacket), arg0, arg1, arg2, arg3, arg4) return &MockRawConnWritePacketCall{Call: call} } // MockRawConnWritePacketCall wrap *gomock.Call type MockRawConnWritePacketCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockRawConnWritePacketCall) Return(arg0 int, arg1 error) *MockRawConnWritePacketCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockRawConnWritePacketCall) Do(f func([]byte, net.Addr, []byte, uint16, protocol.ECN) (int, error)) *MockRawConnWritePacketCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockRawConnWritePacketCall) DoAndReturn(f func([]byte, net.Addr, []byte, uint16, protocol.ECN) (int, error)) *MockRawConnWritePacketCall { c.Call = c.Call.DoAndReturn(f) return c } // capabilities mocks base method. func (m *MockRawConn) capabilities() connCapabilities { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "capabilities") ret0, _ := ret[0].(connCapabilities) return ret0 } // capabilities indicates an expected call of capabilities. func (mr *MockRawConnMockRecorder) capabilities() *MockRawConncapabilitiesCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "capabilities", reflect.TypeOf((*MockRawConn)(nil).capabilities)) return &MockRawConncapabilitiesCall{Call: call} } // MockRawConncapabilitiesCall wrap *gomock.Call type MockRawConncapabilitiesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockRawConncapabilitiesCall) Return(arg0 connCapabilities) *MockRawConncapabilitiesCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockRawConncapabilitiesCall) Do(f func() connCapabilities) *MockRawConncapabilitiesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockRawConncapabilitiesCall) DoAndReturn(f func() connCapabilities) *MockRawConncapabilitiesCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_receive_stream_internal_test.go000066400000000000000000000244031465664453100301020ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: ReceiveStreamI) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_receive_stream_internal_test.go github.com/quic-go/quic-go ReceiveStreamI // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" qerr "github.com/quic-go/quic-go/internal/qerr" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockReceiveStreamI is a mock of ReceiveStreamI interface. type MockReceiveStreamI struct { ctrl *gomock.Controller recorder *MockReceiveStreamIMockRecorder } // MockReceiveStreamIMockRecorder is the mock recorder for MockReceiveStreamI. type MockReceiveStreamIMockRecorder struct { mock *MockReceiveStreamI } // NewMockReceiveStreamI creates a new mock instance. func NewMockReceiveStreamI(ctrl *gomock.Controller) *MockReceiveStreamI { mock := &MockReceiveStreamI{ctrl: ctrl} mock.recorder = &MockReceiveStreamIMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockReceiveStreamI) EXPECT() *MockReceiveStreamIMockRecorder { return m.recorder } // CancelRead mocks base method. func (m *MockReceiveStreamI) CancelRead(arg0 qerr.StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelRead", arg0) } // CancelRead indicates an expected call of CancelRead. func (mr *MockReceiveStreamIMockRecorder) CancelRead(arg0 any) *MockReceiveStreamICancelReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRead", reflect.TypeOf((*MockReceiveStreamI)(nil).CancelRead), arg0) return &MockReceiveStreamICancelReadCall{Call: call} } // MockReceiveStreamICancelReadCall wrap *gomock.Call type MockReceiveStreamICancelReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamICancelReadCall) Return() *MockReceiveStreamICancelReadCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamICancelReadCall) Do(f func(qerr.StreamErrorCode)) *MockReceiveStreamICancelReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamICancelReadCall) DoAndReturn(f func(qerr.StreamErrorCode)) *MockReceiveStreamICancelReadCall { c.Call = c.Call.DoAndReturn(f) return c } // Read mocks base method. func (m *MockReceiveStreamI) Read(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Read indicates an expected call of Read. func (mr *MockReceiveStreamIMockRecorder) Read(arg0 any) *MockReceiveStreamIReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReceiveStreamI)(nil).Read), arg0) return &MockReceiveStreamIReadCall{Call: call} } // MockReceiveStreamIReadCall wrap *gomock.Call type MockReceiveStreamIReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamIReadCall) Return(arg0 int, arg1 error) *MockReceiveStreamIReadCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamIReadCall) Do(f func([]byte) (int, error)) *MockReceiveStreamIReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamIReadCall) DoAndReturn(f func([]byte) (int, error)) *MockReceiveStreamIReadCall { c.Call = c.Call.DoAndReturn(f) return c } // SetReadDeadline mocks base method. func (m *MockReceiveStreamI) SetReadDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockReceiveStreamIMockRecorder) SetReadDeadline(arg0 any) *MockReceiveStreamISetReadDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockReceiveStreamI)(nil).SetReadDeadline), arg0) return &MockReceiveStreamISetReadDeadlineCall{Call: call} } // MockReceiveStreamISetReadDeadlineCall wrap *gomock.Call type MockReceiveStreamISetReadDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamISetReadDeadlineCall) Return(arg0 error) *MockReceiveStreamISetReadDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamISetReadDeadlineCall) Do(f func(time.Time) error) *MockReceiveStreamISetReadDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamISetReadDeadlineCall) DoAndReturn(f func(time.Time) error) *MockReceiveStreamISetReadDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // StreamID mocks base method. func (m *MockReceiveStreamI) StreamID() protocol.StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(protocol.StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockReceiveStreamIMockRecorder) StreamID() *MockReceiveStreamIStreamIDCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockReceiveStreamI)(nil).StreamID)) return &MockReceiveStreamIStreamIDCall{Call: call} } // MockReceiveStreamIStreamIDCall wrap *gomock.Call type MockReceiveStreamIStreamIDCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamIStreamIDCall) Return(arg0 protocol.StreamID) *MockReceiveStreamIStreamIDCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamIStreamIDCall) Do(f func() protocol.StreamID) *MockReceiveStreamIStreamIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamIStreamIDCall) DoAndReturn(f func() protocol.StreamID) *MockReceiveStreamIStreamIDCall { c.Call = c.Call.DoAndReturn(f) return c } // closeForShutdown mocks base method. func (m *MockReceiveStreamI) closeForShutdown(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "closeForShutdown", arg0) } // closeForShutdown indicates an expected call of closeForShutdown. func (mr *MockReceiveStreamIMockRecorder) closeForShutdown(arg0 any) *MockReceiveStreamIcloseForShutdownCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeForShutdown", reflect.TypeOf((*MockReceiveStreamI)(nil).closeForShutdown), arg0) return &MockReceiveStreamIcloseForShutdownCall{Call: call} } // MockReceiveStreamIcloseForShutdownCall wrap *gomock.Call type MockReceiveStreamIcloseForShutdownCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamIcloseForShutdownCall) Return() *MockReceiveStreamIcloseForShutdownCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamIcloseForShutdownCall) Do(f func(error)) *MockReceiveStreamIcloseForShutdownCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamIcloseForShutdownCall) DoAndReturn(f func(error)) *MockReceiveStreamIcloseForShutdownCall { c.Call = c.Call.DoAndReturn(f) return c } // handleResetStreamFrame mocks base method. func (m *MockReceiveStreamI) handleResetStreamFrame(arg0 *wire.ResetStreamFrame) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "handleResetStreamFrame", arg0) ret0, _ := ret[0].(error) return ret0 } // handleResetStreamFrame indicates an expected call of handleResetStreamFrame. func (mr *MockReceiveStreamIMockRecorder) handleResetStreamFrame(arg0 any) *MockReceiveStreamIhandleResetStreamFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleResetStreamFrame", reflect.TypeOf((*MockReceiveStreamI)(nil).handleResetStreamFrame), arg0) return &MockReceiveStreamIhandleResetStreamFrameCall{Call: call} } // MockReceiveStreamIhandleResetStreamFrameCall wrap *gomock.Call type MockReceiveStreamIhandleResetStreamFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamIhandleResetStreamFrameCall) Return(arg0 error) *MockReceiveStreamIhandleResetStreamFrameCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamIhandleResetStreamFrameCall) Do(f func(*wire.ResetStreamFrame) error) *MockReceiveStreamIhandleResetStreamFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamIhandleResetStreamFrameCall) DoAndReturn(f func(*wire.ResetStreamFrame) error) *MockReceiveStreamIhandleResetStreamFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // handleStreamFrame mocks base method. func (m *MockReceiveStreamI) handleStreamFrame(arg0 *wire.StreamFrame) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "handleStreamFrame", arg0) ret0, _ := ret[0].(error) return ret0 } // handleStreamFrame indicates an expected call of handleStreamFrame. func (mr *MockReceiveStreamIMockRecorder) handleStreamFrame(arg0 any) *MockReceiveStreamIhandleStreamFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStreamFrame", reflect.TypeOf((*MockReceiveStreamI)(nil).handleStreamFrame), arg0) return &MockReceiveStreamIhandleStreamFrameCall{Call: call} } // MockReceiveStreamIhandleStreamFrameCall wrap *gomock.Call type MockReceiveStreamIhandleStreamFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockReceiveStreamIhandleStreamFrameCall) Return(arg0 error) *MockReceiveStreamIhandleStreamFrameCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockReceiveStreamIhandleStreamFrameCall) Do(f func(*wire.StreamFrame) error) *MockReceiveStreamIhandleStreamFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockReceiveStreamIhandleStreamFrameCall) DoAndReturn(f func(*wire.StreamFrame) error) *MockReceiveStreamIhandleStreamFrameCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_sealing_manager_test.go000066400000000000000000000157371465664453100263370ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: SealingManager) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" handshake "github.com/quic-go/quic-go/internal/handshake" gomock "go.uber.org/mock/gomock" ) // MockSealingManager is a mock of SealingManager interface. type MockSealingManager struct { ctrl *gomock.Controller recorder *MockSealingManagerMockRecorder } // MockSealingManagerMockRecorder is the mock recorder for MockSealingManager. type MockSealingManagerMockRecorder struct { mock *MockSealingManager } // NewMockSealingManager creates a new mock instance. func NewMockSealingManager(ctrl *gomock.Controller) *MockSealingManager { mock := &MockSealingManager{ctrl: ctrl} mock.recorder = &MockSealingManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSealingManager) EXPECT() *MockSealingManagerMockRecorder { return m.recorder } // Get0RTTSealer mocks base method. func (m *MockSealingManager) Get0RTTSealer() (handshake.LongHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get0RTTSealer") ret0, _ := ret[0].(handshake.LongHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // Get0RTTSealer indicates an expected call of Get0RTTSealer. func (mr *MockSealingManagerMockRecorder) Get0RTTSealer() *MockSealingManagerGet0RTTSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get0RTTSealer", reflect.TypeOf((*MockSealingManager)(nil).Get0RTTSealer)) return &MockSealingManagerGet0RTTSealerCall{Call: call} } // MockSealingManagerGet0RTTSealerCall wrap *gomock.Call type MockSealingManagerGet0RTTSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSealingManagerGet0RTTSealerCall) Return(arg0 handshake.LongHeaderSealer, arg1 error) *MockSealingManagerGet0RTTSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSealingManagerGet0RTTSealerCall) Do(f func() (handshake.LongHeaderSealer, error)) *MockSealingManagerGet0RTTSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSealingManagerGet0RTTSealerCall) DoAndReturn(f func() (handshake.LongHeaderSealer, error)) *MockSealingManagerGet0RTTSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // Get1RTTSealer mocks base method. func (m *MockSealingManager) Get1RTTSealer() (handshake.ShortHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get1RTTSealer") ret0, _ := ret[0].(handshake.ShortHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // Get1RTTSealer indicates an expected call of Get1RTTSealer. func (mr *MockSealingManagerMockRecorder) Get1RTTSealer() *MockSealingManagerGet1RTTSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get1RTTSealer", reflect.TypeOf((*MockSealingManager)(nil).Get1RTTSealer)) return &MockSealingManagerGet1RTTSealerCall{Call: call} } // MockSealingManagerGet1RTTSealerCall wrap *gomock.Call type MockSealingManagerGet1RTTSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSealingManagerGet1RTTSealerCall) Return(arg0 handshake.ShortHeaderSealer, arg1 error) *MockSealingManagerGet1RTTSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSealingManagerGet1RTTSealerCall) Do(f func() (handshake.ShortHeaderSealer, error)) *MockSealingManagerGet1RTTSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSealingManagerGet1RTTSealerCall) DoAndReturn(f func() (handshake.ShortHeaderSealer, error)) *MockSealingManagerGet1RTTSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetHandshakeSealer mocks base method. func (m *MockSealingManager) GetHandshakeSealer() (handshake.LongHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetHandshakeSealer") ret0, _ := ret[0].(handshake.LongHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // GetHandshakeSealer indicates an expected call of GetHandshakeSealer. func (mr *MockSealingManagerMockRecorder) GetHandshakeSealer() *MockSealingManagerGetHandshakeSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandshakeSealer", reflect.TypeOf((*MockSealingManager)(nil).GetHandshakeSealer)) return &MockSealingManagerGetHandshakeSealerCall{Call: call} } // MockSealingManagerGetHandshakeSealerCall wrap *gomock.Call type MockSealingManagerGetHandshakeSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSealingManagerGetHandshakeSealerCall) Return(arg0 handshake.LongHeaderSealer, arg1 error) *MockSealingManagerGetHandshakeSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSealingManagerGetHandshakeSealerCall) Do(f func() (handshake.LongHeaderSealer, error)) *MockSealingManagerGetHandshakeSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSealingManagerGetHandshakeSealerCall) DoAndReturn(f func() (handshake.LongHeaderSealer, error)) *MockSealingManagerGetHandshakeSealerCall { c.Call = c.Call.DoAndReturn(f) return c } // GetInitialSealer mocks base method. func (m *MockSealingManager) GetInitialSealer() (handshake.LongHeaderSealer, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetInitialSealer") ret0, _ := ret[0].(handshake.LongHeaderSealer) ret1, _ := ret[1].(error) return ret0, ret1 } // GetInitialSealer indicates an expected call of GetInitialSealer. func (mr *MockSealingManagerMockRecorder) GetInitialSealer() *MockSealingManagerGetInitialSealerCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitialSealer", reflect.TypeOf((*MockSealingManager)(nil).GetInitialSealer)) return &MockSealingManagerGetInitialSealerCall{Call: call} } // MockSealingManagerGetInitialSealerCall wrap *gomock.Call type MockSealingManagerGetInitialSealerCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSealingManagerGetInitialSealerCall) Return(arg0 handshake.LongHeaderSealer, arg1 error) *MockSealingManagerGetInitialSealerCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSealingManagerGetInitialSealerCall) Do(f func() (handshake.LongHeaderSealer, error)) *MockSealingManagerGetInitialSealerCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSealingManagerGetInitialSealerCall) DoAndReturn(f func() (handshake.LongHeaderSealer, error)) *MockSealingManagerGetInitialSealerCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_send_conn_test.go000066400000000000000000000152501465664453100251570ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: SendConn) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn // // Package quic is a generated GoMock package. package quic import ( net "net" reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockSendConn is a mock of SendConn interface. type MockSendConn struct { ctrl *gomock.Controller recorder *MockSendConnMockRecorder } // MockSendConnMockRecorder is the mock recorder for MockSendConn. type MockSendConnMockRecorder struct { mock *MockSendConn } // NewMockSendConn creates a new mock instance. func NewMockSendConn(ctrl *gomock.Controller) *MockSendConn { mock := &MockSendConn{ctrl: ctrl} mock.recorder = &MockSendConnMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSendConn) EXPECT() *MockSendConnMockRecorder { return m.recorder } // Close mocks base method. func (m *MockSendConn) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockSendConnMockRecorder) Close() *MockSendConnCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSendConn)(nil).Close)) return &MockSendConnCloseCall{Call: call} } // MockSendConnCloseCall wrap *gomock.Call type MockSendConnCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendConnCloseCall) Return(arg0 error) *MockSendConnCloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendConnCloseCall) Do(f func() error) *MockSendConnCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendConnCloseCall) DoAndReturn(f func() error) *MockSendConnCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // LocalAddr mocks base method. func (m *MockSendConn) LocalAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LocalAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // LocalAddr indicates an expected call of LocalAddr. func (mr *MockSendConnMockRecorder) LocalAddr() *MockSendConnLocalAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockSendConn)(nil).LocalAddr)) return &MockSendConnLocalAddrCall{Call: call} } // MockSendConnLocalAddrCall wrap *gomock.Call type MockSendConnLocalAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendConnLocalAddrCall) Return(arg0 net.Addr) *MockSendConnLocalAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendConnLocalAddrCall) Do(f func() net.Addr) *MockSendConnLocalAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendConnLocalAddrCall) DoAndReturn(f func() net.Addr) *MockSendConnLocalAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // RemoteAddr mocks base method. func (m *MockSendConn) RemoteAddr() net.Addr { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoteAddr") ret0, _ := ret[0].(net.Addr) return ret0 } // RemoteAddr indicates an expected call of RemoteAddr. func (mr *MockSendConnMockRecorder) RemoteAddr() *MockSendConnRemoteAddrCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockSendConn)(nil).RemoteAddr)) return &MockSendConnRemoteAddrCall{Call: call} } // MockSendConnRemoteAddrCall wrap *gomock.Call type MockSendConnRemoteAddrCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendConnRemoteAddrCall) Return(arg0 net.Addr) *MockSendConnRemoteAddrCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendConnRemoteAddrCall) Do(f func() net.Addr) *MockSendConnRemoteAddrCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendConnRemoteAddrCall) DoAndReturn(f func() net.Addr) *MockSendConnRemoteAddrCall { c.Call = c.Call.DoAndReturn(f) return c } // Write mocks base method. func (m *MockSendConn) Write(arg0 []byte, arg1 uint16, arg2 protocol.ECN) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0, arg1, arg2) ret0, _ := ret[0].(error) return ret0 } // Write indicates an expected call of Write. func (mr *MockSendConnMockRecorder) Write(arg0, arg1, arg2 any) *MockSendConnWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockSendConn)(nil).Write), arg0, arg1, arg2) return &MockSendConnWriteCall{Call: call} } // MockSendConnWriteCall wrap *gomock.Call type MockSendConnWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendConnWriteCall) Return(arg0 error) *MockSendConnWriteCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendConnWriteCall) Do(f func([]byte, uint16, protocol.ECN) error) *MockSendConnWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendConnWriteCall) DoAndReturn(f func([]byte, uint16, protocol.ECN) error) *MockSendConnWriteCall { c.Call = c.Call.DoAndReturn(f) return c } // capabilities mocks base method. func (m *MockSendConn) capabilities() connCapabilities { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "capabilities") ret0, _ := ret[0].(connCapabilities) return ret0 } // capabilities indicates an expected call of capabilities. func (mr *MockSendConnMockRecorder) capabilities() *MockSendConncapabilitiesCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "capabilities", reflect.TypeOf((*MockSendConn)(nil).capabilities)) return &MockSendConncapabilitiesCall{Call: call} } // MockSendConncapabilitiesCall wrap *gomock.Call type MockSendConncapabilitiesCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendConncapabilitiesCall) Return(arg0 connCapabilities) *MockSendConncapabilitiesCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendConncapabilitiesCall) Do(f func() connCapabilities) *MockSendConncapabilitiesCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendConncapabilitiesCall) DoAndReturn(f func() connCapabilities) *MockSendConncapabilitiesCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_send_stream_internal_test.go000066400000000000000000000353241465664453100274150ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: SendStreamI) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_send_stream_internal_test.go github.com/quic-go/quic-go SendStreamI // // Package quic is a generated GoMock package. package quic import ( context "context" reflect "reflect" time "time" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" protocol "github.com/quic-go/quic-go/internal/protocol" qerr "github.com/quic-go/quic-go/internal/qerr" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockSendStreamI is a mock of SendStreamI interface. type MockSendStreamI struct { ctrl *gomock.Controller recorder *MockSendStreamIMockRecorder } // MockSendStreamIMockRecorder is the mock recorder for MockSendStreamI. type MockSendStreamIMockRecorder struct { mock *MockSendStreamI } // NewMockSendStreamI creates a new mock instance. func NewMockSendStreamI(ctrl *gomock.Controller) *MockSendStreamI { mock := &MockSendStreamI{ctrl: ctrl} mock.recorder = &MockSendStreamIMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSendStreamI) EXPECT() *MockSendStreamIMockRecorder { return m.recorder } // CancelWrite mocks base method. func (m *MockSendStreamI) CancelWrite(arg0 qerr.StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelWrite", arg0) } // CancelWrite indicates an expected call of CancelWrite. func (mr *MockSendStreamIMockRecorder) CancelWrite(arg0 any) *MockSendStreamICancelWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelWrite", reflect.TypeOf((*MockSendStreamI)(nil).CancelWrite), arg0) return &MockSendStreamICancelWriteCall{Call: call} } // MockSendStreamICancelWriteCall wrap *gomock.Call type MockSendStreamICancelWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamICancelWriteCall) Return() *MockSendStreamICancelWriteCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamICancelWriteCall) Do(f func(qerr.StreamErrorCode)) *MockSendStreamICancelWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamICancelWriteCall) DoAndReturn(f func(qerr.StreamErrorCode)) *MockSendStreamICancelWriteCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockSendStreamI) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockSendStreamIMockRecorder) Close() *MockSendStreamICloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSendStreamI)(nil).Close)) return &MockSendStreamICloseCall{Call: call} } // MockSendStreamICloseCall wrap *gomock.Call type MockSendStreamICloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamICloseCall) Return(arg0 error) *MockSendStreamICloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamICloseCall) Do(f func() error) *MockSendStreamICloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamICloseCall) DoAndReturn(f func() error) *MockSendStreamICloseCall { c.Call = c.Call.DoAndReturn(f) return c } // Context mocks base method. func (m *MockSendStreamI) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockSendStreamIMockRecorder) Context() *MockSendStreamIContextCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockSendStreamI)(nil).Context)) return &MockSendStreamIContextCall{Call: call} } // MockSendStreamIContextCall wrap *gomock.Call type MockSendStreamIContextCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIContextCall) Return(arg0 context.Context) *MockSendStreamIContextCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIContextCall) Do(f func() context.Context) *MockSendStreamIContextCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIContextCall) DoAndReturn(f func() context.Context) *MockSendStreamIContextCall { c.Call = c.Call.DoAndReturn(f) return c } // SetWriteDeadline mocks base method. func (m *MockSendStreamI) SetWriteDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetWriteDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetWriteDeadline indicates an expected call of SetWriteDeadline. func (mr *MockSendStreamIMockRecorder) SetWriteDeadline(arg0 any) *MockSendStreamISetWriteDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockSendStreamI)(nil).SetWriteDeadline), arg0) return &MockSendStreamISetWriteDeadlineCall{Call: call} } // MockSendStreamISetWriteDeadlineCall wrap *gomock.Call type MockSendStreamISetWriteDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamISetWriteDeadlineCall) Return(arg0 error) *MockSendStreamISetWriteDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamISetWriteDeadlineCall) Do(f func(time.Time) error) *MockSendStreamISetWriteDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamISetWriteDeadlineCall) DoAndReturn(f func(time.Time) error) *MockSendStreamISetWriteDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // StreamID mocks base method. func (m *MockSendStreamI) StreamID() protocol.StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(protocol.StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockSendStreamIMockRecorder) StreamID() *MockSendStreamIStreamIDCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockSendStreamI)(nil).StreamID)) return &MockSendStreamIStreamIDCall{Call: call} } // MockSendStreamIStreamIDCall wrap *gomock.Call type MockSendStreamIStreamIDCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIStreamIDCall) Return(arg0 protocol.StreamID) *MockSendStreamIStreamIDCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIStreamIDCall) Do(f func() protocol.StreamID) *MockSendStreamIStreamIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIStreamIDCall) DoAndReturn(f func() protocol.StreamID) *MockSendStreamIStreamIDCall { c.Call = c.Call.DoAndReturn(f) return c } // Write mocks base method. func (m *MockSendStreamI) Write(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *MockSendStreamIMockRecorder) Write(arg0 any) *MockSendStreamIWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockSendStreamI)(nil).Write), arg0) return &MockSendStreamIWriteCall{Call: call} } // MockSendStreamIWriteCall wrap *gomock.Call type MockSendStreamIWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIWriteCall) Return(arg0 int, arg1 error) *MockSendStreamIWriteCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIWriteCall) Do(f func([]byte) (int, error)) *MockSendStreamIWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIWriteCall) DoAndReturn(f func([]byte) (int, error)) *MockSendStreamIWriteCall { c.Call = c.Call.DoAndReturn(f) return c } // closeForShutdown mocks base method. func (m *MockSendStreamI) closeForShutdown(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "closeForShutdown", arg0) } // closeForShutdown indicates an expected call of closeForShutdown. func (mr *MockSendStreamIMockRecorder) closeForShutdown(arg0 any) *MockSendStreamIcloseForShutdownCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeForShutdown", reflect.TypeOf((*MockSendStreamI)(nil).closeForShutdown), arg0) return &MockSendStreamIcloseForShutdownCall{Call: call} } // MockSendStreamIcloseForShutdownCall wrap *gomock.Call type MockSendStreamIcloseForShutdownCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIcloseForShutdownCall) Return() *MockSendStreamIcloseForShutdownCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIcloseForShutdownCall) Do(f func(error)) *MockSendStreamIcloseForShutdownCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIcloseForShutdownCall) DoAndReturn(f func(error)) *MockSendStreamIcloseForShutdownCall { c.Call = c.Call.DoAndReturn(f) return c } // handleStopSendingFrame mocks base method. func (m *MockSendStreamI) handleStopSendingFrame(arg0 *wire.StopSendingFrame) { m.ctrl.T.Helper() m.ctrl.Call(m, "handleStopSendingFrame", arg0) } // handleStopSendingFrame indicates an expected call of handleStopSendingFrame. func (mr *MockSendStreamIMockRecorder) handleStopSendingFrame(arg0 any) *MockSendStreamIhandleStopSendingFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStopSendingFrame", reflect.TypeOf((*MockSendStreamI)(nil).handleStopSendingFrame), arg0) return &MockSendStreamIhandleStopSendingFrameCall{Call: call} } // MockSendStreamIhandleStopSendingFrameCall wrap *gomock.Call type MockSendStreamIhandleStopSendingFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIhandleStopSendingFrameCall) Return() *MockSendStreamIhandleStopSendingFrameCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIhandleStopSendingFrameCall) Do(f func(*wire.StopSendingFrame)) *MockSendStreamIhandleStopSendingFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIhandleStopSendingFrameCall) DoAndReturn(f func(*wire.StopSendingFrame)) *MockSendStreamIhandleStopSendingFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // hasData mocks base method. func (m *MockSendStreamI) hasData() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "hasData") ret0, _ := ret[0].(bool) return ret0 } // hasData indicates an expected call of hasData. func (mr *MockSendStreamIMockRecorder) hasData() *MockSendStreamIhasDataCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "hasData", reflect.TypeOf((*MockSendStreamI)(nil).hasData)) return &MockSendStreamIhasDataCall{Call: call} } // MockSendStreamIhasDataCall wrap *gomock.Call type MockSendStreamIhasDataCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIhasDataCall) Return(arg0 bool) *MockSendStreamIhasDataCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIhasDataCall) Do(f func() bool) *MockSendStreamIhasDataCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIhasDataCall) DoAndReturn(f func() bool) *MockSendStreamIhasDataCall { c.Call = c.Call.DoAndReturn(f) return c } // popStreamFrame mocks base method. func (m *MockSendStreamI) popStreamFrame(arg0 protocol.ByteCount, arg1 protocol.Version) (ackhandler.StreamFrame, bool, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "popStreamFrame", arg0, arg1) ret0, _ := ret[0].(ackhandler.StreamFrame) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(bool) return ret0, ret1, ret2 } // popStreamFrame indicates an expected call of popStreamFrame. func (mr *MockSendStreamIMockRecorder) popStreamFrame(arg0, arg1 any) *MockSendStreamIpopStreamFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "popStreamFrame", reflect.TypeOf((*MockSendStreamI)(nil).popStreamFrame), arg0, arg1) return &MockSendStreamIpopStreamFrameCall{Call: call} } // MockSendStreamIpopStreamFrameCall wrap *gomock.Call type MockSendStreamIpopStreamFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIpopStreamFrameCall) Return(arg0 ackhandler.StreamFrame, arg1, arg2 bool) *MockSendStreamIpopStreamFrameCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIpopStreamFrameCall) Do(f func(protocol.ByteCount, protocol.Version) (ackhandler.StreamFrame, bool, bool)) *MockSendStreamIpopStreamFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIpopStreamFrameCall) DoAndReturn(f func(protocol.ByteCount, protocol.Version) (ackhandler.StreamFrame, bool, bool)) *MockSendStreamIpopStreamFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // updateSendWindow mocks base method. func (m *MockSendStreamI) updateSendWindow(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "updateSendWindow", arg0) } // updateSendWindow indicates an expected call of updateSendWindow. func (mr *MockSendStreamIMockRecorder) updateSendWindow(arg0 any) *MockSendStreamIupdateSendWindowCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "updateSendWindow", reflect.TypeOf((*MockSendStreamI)(nil).updateSendWindow), arg0) return &MockSendStreamIupdateSendWindowCall{Call: call} } // MockSendStreamIupdateSendWindowCall wrap *gomock.Call type MockSendStreamIupdateSendWindowCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSendStreamIupdateSendWindowCall) Return() *MockSendStreamIupdateSendWindowCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSendStreamIupdateSendWindowCall) Do(f func(protocol.ByteCount)) *MockSendStreamIupdateSendWindowCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSendStreamIupdateSendWindowCall) DoAndReturn(f func(protocol.ByteCount)) *MockSendStreamIupdateSendWindowCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_sender_test.go000066400000000000000000000142211465664453100244660ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: Sender) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockSender is a mock of Sender interface. type MockSender struct { ctrl *gomock.Controller recorder *MockSenderMockRecorder } // MockSenderMockRecorder is the mock recorder for MockSender. type MockSenderMockRecorder struct { mock *MockSender } // NewMockSender creates a new mock instance. func NewMockSender(ctrl *gomock.Controller) *MockSender { mock := &MockSender{ctrl: ctrl} mock.recorder = &MockSenderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSender) EXPECT() *MockSenderMockRecorder { return m.recorder } // Available mocks base method. func (m *MockSender) Available() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Available") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // Available indicates an expected call of Available. func (mr *MockSenderMockRecorder) Available() *MockSenderAvailableCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Available", reflect.TypeOf((*MockSender)(nil).Available)) return &MockSenderAvailableCall{Call: call} } // MockSenderAvailableCall wrap *gomock.Call type MockSenderAvailableCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSenderAvailableCall) Return(arg0 <-chan struct{}) *MockSenderAvailableCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSenderAvailableCall) Do(f func() <-chan struct{}) *MockSenderAvailableCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSenderAvailableCall) DoAndReturn(f func() <-chan struct{}) *MockSenderAvailableCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockSender) Close() { m.ctrl.T.Helper() m.ctrl.Call(m, "Close") } // Close indicates an expected call of Close. func (mr *MockSenderMockRecorder) Close() *MockSenderCloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSender)(nil).Close)) return &MockSenderCloseCall{Call: call} } // MockSenderCloseCall wrap *gomock.Call type MockSenderCloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSenderCloseCall) Return() *MockSenderCloseCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSenderCloseCall) Do(f func()) *MockSenderCloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSenderCloseCall) DoAndReturn(f func()) *MockSenderCloseCall { c.Call = c.Call.DoAndReturn(f) return c } // Run mocks base method. func (m *MockSender) Run() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Run") ret0, _ := ret[0].(error) return ret0 } // Run indicates an expected call of Run. func (mr *MockSenderMockRecorder) Run() *MockSenderRunCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockSender)(nil).Run)) return &MockSenderRunCall{Call: call} } // MockSenderRunCall wrap *gomock.Call type MockSenderRunCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSenderRunCall) Return(arg0 error) *MockSenderRunCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSenderRunCall) Do(f func() error) *MockSenderRunCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSenderRunCall) DoAndReturn(f func() error) *MockSenderRunCall { c.Call = c.Call.DoAndReturn(f) return c } // Send mocks base method. func (m *MockSender) Send(arg0 *packetBuffer, arg1 uint16, arg2 protocol.ECN) { m.ctrl.T.Helper() m.ctrl.Call(m, "Send", arg0, arg1, arg2) } // Send indicates an expected call of Send. func (mr *MockSenderMockRecorder) Send(arg0, arg1, arg2 any) *MockSenderSendCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockSender)(nil).Send), arg0, arg1, arg2) return &MockSenderSendCall{Call: call} } // MockSenderSendCall wrap *gomock.Call type MockSenderSendCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSenderSendCall) Return() *MockSenderSendCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockSenderSendCall) Do(f func(*packetBuffer, uint16, protocol.ECN)) *MockSenderSendCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSenderSendCall) DoAndReturn(f func(*packetBuffer, uint16, protocol.ECN)) *MockSenderSendCall { c.Call = c.Call.DoAndReturn(f) return c } // WouldBlock mocks base method. func (m *MockSender) WouldBlock() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WouldBlock") ret0, _ := ret[0].(bool) return ret0 } // WouldBlock indicates an expected call of WouldBlock. func (mr *MockSenderMockRecorder) WouldBlock() *MockSenderWouldBlockCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WouldBlock", reflect.TypeOf((*MockSender)(nil).WouldBlock)) return &MockSenderWouldBlockCall{Call: call} } // MockSenderWouldBlockCall wrap *gomock.Call type MockSenderWouldBlockCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockSenderWouldBlockCall) Return(arg0 bool) *MockSenderWouldBlockCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockSenderWouldBlockCall) Do(f func() bool) *MockSenderWouldBlockCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockSenderWouldBlockCall) DoAndReturn(f func() bool) *MockSenderWouldBlockCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_stream_control_frame_getter_test.go000066400000000000000000000057411465664453100307740ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: StreamControlFrameGetter) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_control_frame_getter_test.go github.com/quic-go/quic-go StreamControlFrameGetter // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" gomock "go.uber.org/mock/gomock" ) // MockStreamControlFrameGetter is a mock of StreamControlFrameGetter interface. type MockStreamControlFrameGetter struct { ctrl *gomock.Controller recorder *MockStreamControlFrameGetterMockRecorder } // MockStreamControlFrameGetterMockRecorder is the mock recorder for MockStreamControlFrameGetter. type MockStreamControlFrameGetterMockRecorder struct { mock *MockStreamControlFrameGetter } // NewMockStreamControlFrameGetter creates a new mock instance. func NewMockStreamControlFrameGetter(ctrl *gomock.Controller) *MockStreamControlFrameGetter { mock := &MockStreamControlFrameGetter{ctrl: ctrl} mock.recorder = &MockStreamControlFrameGetterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStreamControlFrameGetter) EXPECT() *MockStreamControlFrameGetterMockRecorder { return m.recorder } // getControlFrame mocks base method. func (m *MockStreamControlFrameGetter) getControlFrame() (ackhandler.Frame, bool, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getControlFrame") ret0, _ := ret[0].(ackhandler.Frame) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(bool) return ret0, ret1, ret2 } // getControlFrame indicates an expected call of getControlFrame. func (mr *MockStreamControlFrameGetterMockRecorder) getControlFrame() *MockStreamControlFrameGettergetControlFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getControlFrame", reflect.TypeOf((*MockStreamControlFrameGetter)(nil).getControlFrame)) return &MockStreamControlFrameGettergetControlFrameCall{Call: call} } // MockStreamControlFrameGettergetControlFrameCall wrap *gomock.Call type MockStreamControlFrameGettergetControlFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamControlFrameGettergetControlFrameCall) Return(arg0 ackhandler.Frame, arg1, arg2 bool) *MockStreamControlFrameGettergetControlFrameCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamControlFrameGettergetControlFrameCall) Do(f func() (ackhandler.Frame, bool, bool)) *MockStreamControlFrameGettergetControlFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamControlFrameGettergetControlFrameCall) DoAndReturn(f func() (ackhandler.Frame, bool, bool)) *MockStreamControlFrameGettergetControlFrameCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_stream_internal_test.go000066400000000000000000000524251465664453100264050ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: StreamI) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_internal_test.go github.com/quic-go/quic-go StreamI // // Package quic is a generated GoMock package. package quic import ( context "context" reflect "reflect" time "time" ackhandler "github.com/quic-go/quic-go/internal/ackhandler" protocol "github.com/quic-go/quic-go/internal/protocol" qerr "github.com/quic-go/quic-go/internal/qerr" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockStreamI is a mock of StreamI interface. type MockStreamI struct { ctrl *gomock.Controller recorder *MockStreamIMockRecorder } // MockStreamIMockRecorder is the mock recorder for MockStreamI. type MockStreamIMockRecorder struct { mock *MockStreamI } // NewMockStreamI creates a new mock instance. func NewMockStreamI(ctrl *gomock.Controller) *MockStreamI { mock := &MockStreamI{ctrl: ctrl} mock.recorder = &MockStreamIMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStreamI) EXPECT() *MockStreamIMockRecorder { return m.recorder } // CancelRead mocks base method. func (m *MockStreamI) CancelRead(arg0 qerr.StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelRead", arg0) } // CancelRead indicates an expected call of CancelRead. func (mr *MockStreamIMockRecorder) CancelRead(arg0 any) *MockStreamICancelReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRead", reflect.TypeOf((*MockStreamI)(nil).CancelRead), arg0) return &MockStreamICancelReadCall{Call: call} } // MockStreamICancelReadCall wrap *gomock.Call type MockStreamICancelReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamICancelReadCall) Return() *MockStreamICancelReadCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamICancelReadCall) Do(f func(qerr.StreamErrorCode)) *MockStreamICancelReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamICancelReadCall) DoAndReturn(f func(qerr.StreamErrorCode)) *MockStreamICancelReadCall { c.Call = c.Call.DoAndReturn(f) return c } // CancelWrite mocks base method. func (m *MockStreamI) CancelWrite(arg0 qerr.StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelWrite", arg0) } // CancelWrite indicates an expected call of CancelWrite. func (mr *MockStreamIMockRecorder) CancelWrite(arg0 any) *MockStreamICancelWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelWrite", reflect.TypeOf((*MockStreamI)(nil).CancelWrite), arg0) return &MockStreamICancelWriteCall{Call: call} } // MockStreamICancelWriteCall wrap *gomock.Call type MockStreamICancelWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamICancelWriteCall) Return() *MockStreamICancelWriteCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamICancelWriteCall) Do(f func(qerr.StreamErrorCode)) *MockStreamICancelWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamICancelWriteCall) DoAndReturn(f func(qerr.StreamErrorCode)) *MockStreamICancelWriteCall { c.Call = c.Call.DoAndReturn(f) return c } // Close mocks base method. func (m *MockStreamI) Close() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Close") ret0, _ := ret[0].(error) return ret0 } // Close indicates an expected call of Close. func (mr *MockStreamIMockRecorder) Close() *MockStreamICloseCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStreamI)(nil).Close)) return &MockStreamICloseCall{Call: call} } // MockStreamICloseCall wrap *gomock.Call type MockStreamICloseCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamICloseCall) Return(arg0 error) *MockStreamICloseCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamICloseCall) Do(f func() error) *MockStreamICloseCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamICloseCall) DoAndReturn(f func() error) *MockStreamICloseCall { c.Call = c.Call.DoAndReturn(f) return c } // Context mocks base method. func (m *MockStreamI) Context() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Context") ret0, _ := ret[0].(context.Context) return ret0 } // Context indicates an expected call of Context. func (mr *MockStreamIMockRecorder) Context() *MockStreamIContextCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockStreamI)(nil).Context)) return &MockStreamIContextCall{Call: call} } // MockStreamIContextCall wrap *gomock.Call type MockStreamIContextCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIContextCall) Return(arg0 context.Context) *MockStreamIContextCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIContextCall) Do(f func() context.Context) *MockStreamIContextCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIContextCall) DoAndReturn(f func() context.Context) *MockStreamIContextCall { c.Call = c.Call.DoAndReturn(f) return c } // Read mocks base method. func (m *MockStreamI) Read(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Read indicates an expected call of Read. func (mr *MockStreamIMockRecorder) Read(arg0 any) *MockStreamIReadCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockStreamI)(nil).Read), arg0) return &MockStreamIReadCall{Call: call} } // MockStreamIReadCall wrap *gomock.Call type MockStreamIReadCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIReadCall) Return(arg0 int, arg1 error) *MockStreamIReadCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIReadCall) Do(f func([]byte) (int, error)) *MockStreamIReadCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIReadCall) DoAndReturn(f func([]byte) (int, error)) *MockStreamIReadCall { c.Call = c.Call.DoAndReturn(f) return c } // SetDeadline mocks base method. func (m *MockStreamI) SetDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetDeadline indicates an expected call of SetDeadline. func (mr *MockStreamIMockRecorder) SetDeadline(arg0 any) *MockStreamISetDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockStreamI)(nil).SetDeadline), arg0) return &MockStreamISetDeadlineCall{Call: call} } // MockStreamISetDeadlineCall wrap *gomock.Call type MockStreamISetDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamISetDeadlineCall) Return(arg0 error) *MockStreamISetDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamISetDeadlineCall) Do(f func(time.Time) error) *MockStreamISetDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamISetDeadlineCall) DoAndReturn(f func(time.Time) error) *MockStreamISetDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // SetReadDeadline mocks base method. func (m *MockStreamI) SetReadDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockStreamIMockRecorder) SetReadDeadline(arg0 any) *MockStreamISetReadDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockStreamI)(nil).SetReadDeadline), arg0) return &MockStreamISetReadDeadlineCall{Call: call} } // MockStreamISetReadDeadlineCall wrap *gomock.Call type MockStreamISetReadDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamISetReadDeadlineCall) Return(arg0 error) *MockStreamISetReadDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamISetReadDeadlineCall) Do(f func(time.Time) error) *MockStreamISetReadDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamISetReadDeadlineCall) DoAndReturn(f func(time.Time) error) *MockStreamISetReadDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // SetWriteDeadline mocks base method. func (m *MockStreamI) SetWriteDeadline(arg0 time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetWriteDeadline", arg0) ret0, _ := ret[0].(error) return ret0 } // SetWriteDeadline indicates an expected call of SetWriteDeadline. func (mr *MockStreamIMockRecorder) SetWriteDeadline(arg0 any) *MockStreamISetWriteDeadlineCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockStreamI)(nil).SetWriteDeadline), arg0) return &MockStreamISetWriteDeadlineCall{Call: call} } // MockStreamISetWriteDeadlineCall wrap *gomock.Call type MockStreamISetWriteDeadlineCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamISetWriteDeadlineCall) Return(arg0 error) *MockStreamISetWriteDeadlineCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamISetWriteDeadlineCall) Do(f func(time.Time) error) *MockStreamISetWriteDeadlineCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamISetWriteDeadlineCall) DoAndReturn(f func(time.Time) error) *MockStreamISetWriteDeadlineCall { c.Call = c.Call.DoAndReturn(f) return c } // StreamID mocks base method. func (m *MockStreamI) StreamID() protocol.StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(protocol.StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockStreamIMockRecorder) StreamID() *MockStreamIStreamIDCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockStreamI)(nil).StreamID)) return &MockStreamIStreamIDCall{Call: call} } // MockStreamIStreamIDCall wrap *gomock.Call type MockStreamIStreamIDCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIStreamIDCall) Return(arg0 protocol.StreamID) *MockStreamIStreamIDCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIStreamIDCall) Do(f func() protocol.StreamID) *MockStreamIStreamIDCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIStreamIDCall) DoAndReturn(f func() protocol.StreamID) *MockStreamIStreamIDCall { c.Call = c.Call.DoAndReturn(f) return c } // Write mocks base method. func (m *MockStreamI) Write(arg0 []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *MockStreamIMockRecorder) Write(arg0 any) *MockStreamIWriteCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockStreamI)(nil).Write), arg0) return &MockStreamIWriteCall{Call: call} } // MockStreamIWriteCall wrap *gomock.Call type MockStreamIWriteCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIWriteCall) Return(arg0 int, arg1 error) *MockStreamIWriteCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIWriteCall) Do(f func([]byte) (int, error)) *MockStreamIWriteCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIWriteCall) DoAndReturn(f func([]byte) (int, error)) *MockStreamIWriteCall { c.Call = c.Call.DoAndReturn(f) return c } // closeForShutdown mocks base method. func (m *MockStreamI) closeForShutdown(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "closeForShutdown", arg0) } // closeForShutdown indicates an expected call of closeForShutdown. func (mr *MockStreamIMockRecorder) closeForShutdown(arg0 any) *MockStreamIcloseForShutdownCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeForShutdown", reflect.TypeOf((*MockStreamI)(nil).closeForShutdown), arg0) return &MockStreamIcloseForShutdownCall{Call: call} } // MockStreamIcloseForShutdownCall wrap *gomock.Call type MockStreamIcloseForShutdownCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIcloseForShutdownCall) Return() *MockStreamIcloseForShutdownCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIcloseForShutdownCall) Do(f func(error)) *MockStreamIcloseForShutdownCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIcloseForShutdownCall) DoAndReturn(f func(error)) *MockStreamIcloseForShutdownCall { c.Call = c.Call.DoAndReturn(f) return c } // handleResetStreamFrame mocks base method. func (m *MockStreamI) handleResetStreamFrame(arg0 *wire.ResetStreamFrame) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "handleResetStreamFrame", arg0) ret0, _ := ret[0].(error) return ret0 } // handleResetStreamFrame indicates an expected call of handleResetStreamFrame. func (mr *MockStreamIMockRecorder) handleResetStreamFrame(arg0 any) *MockStreamIhandleResetStreamFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleResetStreamFrame", reflect.TypeOf((*MockStreamI)(nil).handleResetStreamFrame), arg0) return &MockStreamIhandleResetStreamFrameCall{Call: call} } // MockStreamIhandleResetStreamFrameCall wrap *gomock.Call type MockStreamIhandleResetStreamFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIhandleResetStreamFrameCall) Return(arg0 error) *MockStreamIhandleResetStreamFrameCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIhandleResetStreamFrameCall) Do(f func(*wire.ResetStreamFrame) error) *MockStreamIhandleResetStreamFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIhandleResetStreamFrameCall) DoAndReturn(f func(*wire.ResetStreamFrame) error) *MockStreamIhandleResetStreamFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // handleStopSendingFrame mocks base method. func (m *MockStreamI) handleStopSendingFrame(arg0 *wire.StopSendingFrame) { m.ctrl.T.Helper() m.ctrl.Call(m, "handleStopSendingFrame", arg0) } // handleStopSendingFrame indicates an expected call of handleStopSendingFrame. func (mr *MockStreamIMockRecorder) handleStopSendingFrame(arg0 any) *MockStreamIhandleStopSendingFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStopSendingFrame", reflect.TypeOf((*MockStreamI)(nil).handleStopSendingFrame), arg0) return &MockStreamIhandleStopSendingFrameCall{Call: call} } // MockStreamIhandleStopSendingFrameCall wrap *gomock.Call type MockStreamIhandleStopSendingFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIhandleStopSendingFrameCall) Return() *MockStreamIhandleStopSendingFrameCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIhandleStopSendingFrameCall) Do(f func(*wire.StopSendingFrame)) *MockStreamIhandleStopSendingFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIhandleStopSendingFrameCall) DoAndReturn(f func(*wire.StopSendingFrame)) *MockStreamIhandleStopSendingFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // handleStreamFrame mocks base method. func (m *MockStreamI) handleStreamFrame(arg0 *wire.StreamFrame) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "handleStreamFrame", arg0) ret0, _ := ret[0].(error) return ret0 } // handleStreamFrame indicates an expected call of handleStreamFrame. func (mr *MockStreamIMockRecorder) handleStreamFrame(arg0 any) *MockStreamIhandleStreamFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStreamFrame", reflect.TypeOf((*MockStreamI)(nil).handleStreamFrame), arg0) return &MockStreamIhandleStreamFrameCall{Call: call} } // MockStreamIhandleStreamFrameCall wrap *gomock.Call type MockStreamIhandleStreamFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIhandleStreamFrameCall) Return(arg0 error) *MockStreamIhandleStreamFrameCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIhandleStreamFrameCall) Do(f func(*wire.StreamFrame) error) *MockStreamIhandleStreamFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIhandleStreamFrameCall) DoAndReturn(f func(*wire.StreamFrame) error) *MockStreamIhandleStreamFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // hasData mocks base method. func (m *MockStreamI) hasData() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "hasData") ret0, _ := ret[0].(bool) return ret0 } // hasData indicates an expected call of hasData. func (mr *MockStreamIMockRecorder) hasData() *MockStreamIhasDataCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "hasData", reflect.TypeOf((*MockStreamI)(nil).hasData)) return &MockStreamIhasDataCall{Call: call} } // MockStreamIhasDataCall wrap *gomock.Call type MockStreamIhasDataCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIhasDataCall) Return(arg0 bool) *MockStreamIhasDataCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIhasDataCall) Do(f func() bool) *MockStreamIhasDataCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIhasDataCall) DoAndReturn(f func() bool) *MockStreamIhasDataCall { c.Call = c.Call.DoAndReturn(f) return c } // popStreamFrame mocks base method. func (m *MockStreamI) popStreamFrame(arg0 protocol.ByteCount, arg1 protocol.Version) (ackhandler.StreamFrame, bool, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "popStreamFrame", arg0, arg1) ret0, _ := ret[0].(ackhandler.StreamFrame) ret1, _ := ret[1].(bool) ret2, _ := ret[2].(bool) return ret0, ret1, ret2 } // popStreamFrame indicates an expected call of popStreamFrame. func (mr *MockStreamIMockRecorder) popStreamFrame(arg0, arg1 any) *MockStreamIpopStreamFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "popStreamFrame", reflect.TypeOf((*MockStreamI)(nil).popStreamFrame), arg0, arg1) return &MockStreamIpopStreamFrameCall{Call: call} } // MockStreamIpopStreamFrameCall wrap *gomock.Call type MockStreamIpopStreamFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIpopStreamFrameCall) Return(arg0 ackhandler.StreamFrame, arg1, arg2 bool) *MockStreamIpopStreamFrameCall { c.Call = c.Call.Return(arg0, arg1, arg2) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIpopStreamFrameCall) Do(f func(protocol.ByteCount, protocol.Version) (ackhandler.StreamFrame, bool, bool)) *MockStreamIpopStreamFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIpopStreamFrameCall) DoAndReturn(f func(protocol.ByteCount, protocol.Version) (ackhandler.StreamFrame, bool, bool)) *MockStreamIpopStreamFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // updateSendWindow mocks base method. func (m *MockStreamI) updateSendWindow(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "updateSendWindow", arg0) } // updateSendWindow indicates an expected call of updateSendWindow. func (mr *MockStreamIMockRecorder) updateSendWindow(arg0 any) *MockStreamIupdateSendWindowCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "updateSendWindow", reflect.TypeOf((*MockStreamI)(nil).updateSendWindow), arg0) return &MockStreamIupdateSendWindowCall{Call: call} } // MockStreamIupdateSendWindowCall wrap *gomock.Call type MockStreamIupdateSendWindowCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamIupdateSendWindowCall) Return() *MockStreamIupdateSendWindowCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamIupdateSendWindowCall) Do(f func(protocol.ByteCount)) *MockStreamIupdateSendWindowCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamIupdateSendWindowCall) DoAndReturn(f func(protocol.ByteCount)) *MockStreamIupdateSendWindowCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_stream_manager_test.go000066400000000000000000000473351465664453100262070ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: StreamManager) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_manager_test.go github.com/quic-go/quic-go StreamManager // // Package quic is a generated GoMock package. package quic import ( context "context" reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockStreamManager is a mock of StreamManager interface. type MockStreamManager struct { ctrl *gomock.Controller recorder *MockStreamManagerMockRecorder } // MockStreamManagerMockRecorder is the mock recorder for MockStreamManager. type MockStreamManagerMockRecorder struct { mock *MockStreamManager } // NewMockStreamManager creates a new mock instance. func NewMockStreamManager(ctrl *gomock.Controller) *MockStreamManager { mock := &MockStreamManager{ctrl: ctrl} mock.recorder = &MockStreamManagerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStreamManager) EXPECT() *MockStreamManagerMockRecorder { return m.recorder } // AcceptStream mocks base method. func (m *MockStreamManager) AcceptStream(arg0 context.Context) (Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AcceptStream", arg0) ret0, _ := ret[0].(Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // AcceptStream indicates an expected call of AcceptStream. func (mr *MockStreamManagerMockRecorder) AcceptStream(arg0 any) *MockStreamManagerAcceptStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptStream", reflect.TypeOf((*MockStreamManager)(nil).AcceptStream), arg0) return &MockStreamManagerAcceptStreamCall{Call: call} } // MockStreamManagerAcceptStreamCall wrap *gomock.Call type MockStreamManagerAcceptStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerAcceptStreamCall) Return(arg0 Stream, arg1 error) *MockStreamManagerAcceptStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerAcceptStreamCall) Do(f func(context.Context) (Stream, error)) *MockStreamManagerAcceptStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerAcceptStreamCall) DoAndReturn(f func(context.Context) (Stream, error)) *MockStreamManagerAcceptStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // AcceptUniStream mocks base method. func (m *MockStreamManager) AcceptUniStream(arg0 context.Context) (ReceiveStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AcceptUniStream", arg0) ret0, _ := ret[0].(ReceiveStream) ret1, _ := ret[1].(error) return ret0, ret1 } // AcceptUniStream indicates an expected call of AcceptUniStream. func (mr *MockStreamManagerMockRecorder) AcceptUniStream(arg0 any) *MockStreamManagerAcceptUniStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUniStream", reflect.TypeOf((*MockStreamManager)(nil).AcceptUniStream), arg0) return &MockStreamManagerAcceptUniStreamCall{Call: call} } // MockStreamManagerAcceptUniStreamCall wrap *gomock.Call type MockStreamManagerAcceptUniStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerAcceptUniStreamCall) Return(arg0 ReceiveStream, arg1 error) *MockStreamManagerAcceptUniStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerAcceptUniStreamCall) Do(f func(context.Context) (ReceiveStream, error)) *MockStreamManagerAcceptUniStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerAcceptUniStreamCall) DoAndReturn(f func(context.Context) (ReceiveStream, error)) *MockStreamManagerAcceptUniStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // CloseWithError mocks base method. func (m *MockStreamManager) CloseWithError(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "CloseWithError", arg0) } // CloseWithError indicates an expected call of CloseWithError. func (mr *MockStreamManagerMockRecorder) CloseWithError(arg0 any) *MockStreamManagerCloseWithErrorCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseWithError", reflect.TypeOf((*MockStreamManager)(nil).CloseWithError), arg0) return &MockStreamManagerCloseWithErrorCall{Call: call} } // MockStreamManagerCloseWithErrorCall wrap *gomock.Call type MockStreamManagerCloseWithErrorCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerCloseWithErrorCall) Return() *MockStreamManagerCloseWithErrorCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerCloseWithErrorCall) Do(f func(error)) *MockStreamManagerCloseWithErrorCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerCloseWithErrorCall) DoAndReturn(f func(error)) *MockStreamManagerCloseWithErrorCall { c.Call = c.Call.DoAndReturn(f) return c } // DeleteStream mocks base method. func (m *MockStreamManager) DeleteStream(arg0 protocol.StreamID) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DeleteStream", arg0) ret0, _ := ret[0].(error) return ret0 } // DeleteStream indicates an expected call of DeleteStream. func (mr *MockStreamManagerMockRecorder) DeleteStream(arg0 any) *MockStreamManagerDeleteStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStream", reflect.TypeOf((*MockStreamManager)(nil).DeleteStream), arg0) return &MockStreamManagerDeleteStreamCall{Call: call} } // MockStreamManagerDeleteStreamCall wrap *gomock.Call type MockStreamManagerDeleteStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerDeleteStreamCall) Return(arg0 error) *MockStreamManagerDeleteStreamCall { c.Call = c.Call.Return(arg0) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerDeleteStreamCall) Do(f func(protocol.StreamID) error) *MockStreamManagerDeleteStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerDeleteStreamCall) DoAndReturn(f func(protocol.StreamID) error) *MockStreamManagerDeleteStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // GetOrOpenReceiveStream mocks base method. func (m *MockStreamManager) GetOrOpenReceiveStream(arg0 protocol.StreamID) (receiveStreamI, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrOpenReceiveStream", arg0) ret0, _ := ret[0].(receiveStreamI) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrOpenReceiveStream indicates an expected call of GetOrOpenReceiveStream. func (mr *MockStreamManagerMockRecorder) GetOrOpenReceiveStream(arg0 any) *MockStreamManagerGetOrOpenReceiveStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrOpenReceiveStream", reflect.TypeOf((*MockStreamManager)(nil).GetOrOpenReceiveStream), arg0) return &MockStreamManagerGetOrOpenReceiveStreamCall{Call: call} } // MockStreamManagerGetOrOpenReceiveStreamCall wrap *gomock.Call type MockStreamManagerGetOrOpenReceiveStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerGetOrOpenReceiveStreamCall) Return(arg0 receiveStreamI, arg1 error) *MockStreamManagerGetOrOpenReceiveStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerGetOrOpenReceiveStreamCall) Do(f func(protocol.StreamID) (receiveStreamI, error)) *MockStreamManagerGetOrOpenReceiveStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerGetOrOpenReceiveStreamCall) DoAndReturn(f func(protocol.StreamID) (receiveStreamI, error)) *MockStreamManagerGetOrOpenReceiveStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // GetOrOpenSendStream mocks base method. func (m *MockStreamManager) GetOrOpenSendStream(arg0 protocol.StreamID) (sendStreamI, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetOrOpenSendStream", arg0) ret0, _ := ret[0].(sendStreamI) ret1, _ := ret[1].(error) return ret0, ret1 } // GetOrOpenSendStream indicates an expected call of GetOrOpenSendStream. func (mr *MockStreamManagerMockRecorder) GetOrOpenSendStream(arg0 any) *MockStreamManagerGetOrOpenSendStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrOpenSendStream", reflect.TypeOf((*MockStreamManager)(nil).GetOrOpenSendStream), arg0) return &MockStreamManagerGetOrOpenSendStreamCall{Call: call} } // MockStreamManagerGetOrOpenSendStreamCall wrap *gomock.Call type MockStreamManagerGetOrOpenSendStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerGetOrOpenSendStreamCall) Return(arg0 sendStreamI, arg1 error) *MockStreamManagerGetOrOpenSendStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerGetOrOpenSendStreamCall) Do(f func(protocol.StreamID) (sendStreamI, error)) *MockStreamManagerGetOrOpenSendStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerGetOrOpenSendStreamCall) DoAndReturn(f func(protocol.StreamID) (sendStreamI, error)) *MockStreamManagerGetOrOpenSendStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // HandleMaxStreamsFrame mocks base method. func (m *MockStreamManager) HandleMaxStreamsFrame(arg0 *wire.MaxStreamsFrame) { m.ctrl.T.Helper() m.ctrl.Call(m, "HandleMaxStreamsFrame", arg0) } // HandleMaxStreamsFrame indicates an expected call of HandleMaxStreamsFrame. func (mr *MockStreamManagerMockRecorder) HandleMaxStreamsFrame(arg0 any) *MockStreamManagerHandleMaxStreamsFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMaxStreamsFrame", reflect.TypeOf((*MockStreamManager)(nil).HandleMaxStreamsFrame), arg0) return &MockStreamManagerHandleMaxStreamsFrameCall{Call: call} } // MockStreamManagerHandleMaxStreamsFrameCall wrap *gomock.Call type MockStreamManagerHandleMaxStreamsFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerHandleMaxStreamsFrameCall) Return() *MockStreamManagerHandleMaxStreamsFrameCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerHandleMaxStreamsFrameCall) Do(f func(*wire.MaxStreamsFrame)) *MockStreamManagerHandleMaxStreamsFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerHandleMaxStreamsFrameCall) DoAndReturn(f func(*wire.MaxStreamsFrame)) *MockStreamManagerHandleMaxStreamsFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenStream mocks base method. func (m *MockStreamManager) OpenStream() (Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenStream") ret0, _ := ret[0].(Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenStream indicates an expected call of OpenStream. func (mr *MockStreamManagerMockRecorder) OpenStream() *MockStreamManagerOpenStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockStreamManager)(nil).OpenStream)) return &MockStreamManagerOpenStreamCall{Call: call} } // MockStreamManagerOpenStreamCall wrap *gomock.Call type MockStreamManagerOpenStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerOpenStreamCall) Return(arg0 Stream, arg1 error) *MockStreamManagerOpenStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerOpenStreamCall) Do(f func() (Stream, error)) *MockStreamManagerOpenStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerOpenStreamCall) DoAndReturn(f func() (Stream, error)) *MockStreamManagerOpenStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenStreamSync mocks base method. func (m *MockStreamManager) OpenStreamSync(arg0 context.Context) (Stream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenStreamSync", arg0) ret0, _ := ret[0].(Stream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenStreamSync indicates an expected call of OpenStreamSync. func (mr *MockStreamManagerMockRecorder) OpenStreamSync(arg0 any) *MockStreamManagerOpenStreamSyncCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStreamSync", reflect.TypeOf((*MockStreamManager)(nil).OpenStreamSync), arg0) return &MockStreamManagerOpenStreamSyncCall{Call: call} } // MockStreamManagerOpenStreamSyncCall wrap *gomock.Call type MockStreamManagerOpenStreamSyncCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerOpenStreamSyncCall) Return(arg0 Stream, arg1 error) *MockStreamManagerOpenStreamSyncCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerOpenStreamSyncCall) Do(f func(context.Context) (Stream, error)) *MockStreamManagerOpenStreamSyncCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerOpenStreamSyncCall) DoAndReturn(f func(context.Context) (Stream, error)) *MockStreamManagerOpenStreamSyncCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenUniStream mocks base method. func (m *MockStreamManager) OpenUniStream() (SendStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenUniStream") ret0, _ := ret[0].(SendStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenUniStream indicates an expected call of OpenUniStream. func (mr *MockStreamManagerMockRecorder) OpenUniStream() *MockStreamManagerOpenUniStreamCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStream", reflect.TypeOf((*MockStreamManager)(nil).OpenUniStream)) return &MockStreamManagerOpenUniStreamCall{Call: call} } // MockStreamManagerOpenUniStreamCall wrap *gomock.Call type MockStreamManagerOpenUniStreamCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerOpenUniStreamCall) Return(arg0 SendStream, arg1 error) *MockStreamManagerOpenUniStreamCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerOpenUniStreamCall) Do(f func() (SendStream, error)) *MockStreamManagerOpenUniStreamCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerOpenUniStreamCall) DoAndReturn(f func() (SendStream, error)) *MockStreamManagerOpenUniStreamCall { c.Call = c.Call.DoAndReturn(f) return c } // OpenUniStreamSync mocks base method. func (m *MockStreamManager) OpenUniStreamSync(arg0 context.Context) (SendStream, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "OpenUniStreamSync", arg0) ret0, _ := ret[0].(SendStream) ret1, _ := ret[1].(error) return ret0, ret1 } // OpenUniStreamSync indicates an expected call of OpenUniStreamSync. func (mr *MockStreamManagerMockRecorder) OpenUniStreamSync(arg0 any) *MockStreamManagerOpenUniStreamSyncCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStreamSync", reflect.TypeOf((*MockStreamManager)(nil).OpenUniStreamSync), arg0) return &MockStreamManagerOpenUniStreamSyncCall{Call: call} } // MockStreamManagerOpenUniStreamSyncCall wrap *gomock.Call type MockStreamManagerOpenUniStreamSyncCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerOpenUniStreamSyncCall) Return(arg0 SendStream, arg1 error) *MockStreamManagerOpenUniStreamSyncCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerOpenUniStreamSyncCall) Do(f func(context.Context) (SendStream, error)) *MockStreamManagerOpenUniStreamSyncCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerOpenUniStreamSyncCall) DoAndReturn(f func(context.Context) (SendStream, error)) *MockStreamManagerOpenUniStreamSyncCall { c.Call = c.Call.DoAndReturn(f) return c } // ResetFor0RTT mocks base method. func (m *MockStreamManager) ResetFor0RTT() { m.ctrl.T.Helper() m.ctrl.Call(m, "ResetFor0RTT") } // ResetFor0RTT indicates an expected call of ResetFor0RTT. func (mr *MockStreamManagerMockRecorder) ResetFor0RTT() *MockStreamManagerResetFor0RTTCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetFor0RTT", reflect.TypeOf((*MockStreamManager)(nil).ResetFor0RTT)) return &MockStreamManagerResetFor0RTTCall{Call: call} } // MockStreamManagerResetFor0RTTCall wrap *gomock.Call type MockStreamManagerResetFor0RTTCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerResetFor0RTTCall) Return() *MockStreamManagerResetFor0RTTCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerResetFor0RTTCall) Do(f func()) *MockStreamManagerResetFor0RTTCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerResetFor0RTTCall) DoAndReturn(f func()) *MockStreamManagerResetFor0RTTCall { c.Call = c.Call.DoAndReturn(f) return c } // UpdateLimits mocks base method. func (m *MockStreamManager) UpdateLimits(arg0 *wire.TransportParameters) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateLimits", arg0) } // UpdateLimits indicates an expected call of UpdateLimits. func (mr *MockStreamManagerMockRecorder) UpdateLimits(arg0 any) *MockStreamManagerUpdateLimitsCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLimits", reflect.TypeOf((*MockStreamManager)(nil).UpdateLimits), arg0) return &MockStreamManagerUpdateLimitsCall{Call: call} } // MockStreamManagerUpdateLimitsCall wrap *gomock.Call type MockStreamManagerUpdateLimitsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerUpdateLimitsCall) Return() *MockStreamManagerUpdateLimitsCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerUpdateLimitsCall) Do(f func(*wire.TransportParameters)) *MockStreamManagerUpdateLimitsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerUpdateLimitsCall) DoAndReturn(f func(*wire.TransportParameters)) *MockStreamManagerUpdateLimitsCall { c.Call = c.Call.DoAndReturn(f) return c } // UseResetMaps mocks base method. func (m *MockStreamManager) UseResetMaps() { m.ctrl.T.Helper() m.ctrl.Call(m, "UseResetMaps") } // UseResetMaps indicates an expected call of UseResetMaps. func (mr *MockStreamManagerMockRecorder) UseResetMaps() *MockStreamManagerUseResetMapsCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseResetMaps", reflect.TypeOf((*MockStreamManager)(nil).UseResetMaps)) return &MockStreamManagerUseResetMapsCall{Call: call} } // MockStreamManagerUseResetMapsCall wrap *gomock.Call type MockStreamManagerUseResetMapsCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamManagerUseResetMapsCall) Return() *MockStreamManagerUseResetMapsCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamManagerUseResetMapsCall) Do(f func()) *MockStreamManagerUseResetMapsCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamManagerUseResetMapsCall) DoAndReturn(f func()) *MockStreamManagerUseResetMapsCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_stream_sender_test.go000066400000000000000000000123211465664453100260400ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: StreamSender) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" protocol "github.com/quic-go/quic-go/internal/protocol" gomock "go.uber.org/mock/gomock" ) // MockStreamSender is a mock of StreamSender interface. type MockStreamSender struct { ctrl *gomock.Controller recorder *MockStreamSenderMockRecorder } // MockStreamSenderMockRecorder is the mock recorder for MockStreamSender. type MockStreamSenderMockRecorder struct { mock *MockStreamSender } // NewMockStreamSender creates a new mock instance. func NewMockStreamSender(ctrl *gomock.Controller) *MockStreamSender { mock := &MockStreamSender{ctrl: ctrl} mock.recorder = &MockStreamSenderMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStreamSender) EXPECT() *MockStreamSenderMockRecorder { return m.recorder } // onHasStreamControlFrame mocks base method. func (m *MockStreamSender) onHasStreamControlFrame(arg0 protocol.StreamID, arg1 streamControlFrameGetter) { m.ctrl.T.Helper() m.ctrl.Call(m, "onHasStreamControlFrame", arg0, arg1) } // onHasStreamControlFrame indicates an expected call of onHasStreamControlFrame. func (mr *MockStreamSenderMockRecorder) onHasStreamControlFrame(arg0, arg1 any) *MockStreamSenderonHasStreamControlFrameCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "onHasStreamControlFrame", reflect.TypeOf((*MockStreamSender)(nil).onHasStreamControlFrame), arg0, arg1) return &MockStreamSenderonHasStreamControlFrameCall{Call: call} } // MockStreamSenderonHasStreamControlFrameCall wrap *gomock.Call type MockStreamSenderonHasStreamControlFrameCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamSenderonHasStreamControlFrameCall) Return() *MockStreamSenderonHasStreamControlFrameCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamSenderonHasStreamControlFrameCall) Do(f func(protocol.StreamID, streamControlFrameGetter)) *MockStreamSenderonHasStreamControlFrameCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamSenderonHasStreamControlFrameCall) DoAndReturn(f func(protocol.StreamID, streamControlFrameGetter)) *MockStreamSenderonHasStreamControlFrameCall { c.Call = c.Call.DoAndReturn(f) return c } // onHasStreamData mocks base method. func (m *MockStreamSender) onHasStreamData(arg0 protocol.StreamID, arg1 sendStreamI) { m.ctrl.T.Helper() m.ctrl.Call(m, "onHasStreamData", arg0, arg1) } // onHasStreamData indicates an expected call of onHasStreamData. func (mr *MockStreamSenderMockRecorder) onHasStreamData(arg0, arg1 any) *MockStreamSenderonHasStreamDataCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "onHasStreamData", reflect.TypeOf((*MockStreamSender)(nil).onHasStreamData), arg0, arg1) return &MockStreamSenderonHasStreamDataCall{Call: call} } // MockStreamSenderonHasStreamDataCall wrap *gomock.Call type MockStreamSenderonHasStreamDataCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamSenderonHasStreamDataCall) Return() *MockStreamSenderonHasStreamDataCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamSenderonHasStreamDataCall) Do(f func(protocol.StreamID, sendStreamI)) *MockStreamSenderonHasStreamDataCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamSenderonHasStreamDataCall) DoAndReturn(f func(protocol.StreamID, sendStreamI)) *MockStreamSenderonHasStreamDataCall { c.Call = c.Call.DoAndReturn(f) return c } // onStreamCompleted mocks base method. func (m *MockStreamSender) onStreamCompleted(arg0 protocol.StreamID) { m.ctrl.T.Helper() m.ctrl.Call(m, "onStreamCompleted", arg0) } // onStreamCompleted indicates an expected call of onStreamCompleted. func (mr *MockStreamSenderMockRecorder) onStreamCompleted(arg0 any) *MockStreamSenderonStreamCompletedCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "onStreamCompleted", reflect.TypeOf((*MockStreamSender)(nil).onStreamCompleted), arg0) return &MockStreamSenderonStreamCompletedCall{Call: call} } // MockStreamSenderonStreamCompletedCall wrap *gomock.Call type MockStreamSenderonStreamCompletedCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockStreamSenderonStreamCompletedCall) Return() *MockStreamSenderonStreamCompletedCall { c.Call = c.Call.Return() return c } // Do rewrite *gomock.Call.Do func (c *MockStreamSenderonStreamCompletedCall) Do(f func(protocol.StreamID)) *MockStreamSenderonStreamCompletedCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockStreamSenderonStreamCompletedCall) DoAndReturn(f func(protocol.StreamID)) *MockStreamSenderonStreamCompletedCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mock_unpacker_test.go000066400000000000000000000107151465664453100250220ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/quic-go/quic-go (interfaces: Unpacker) // // Generated by this command: // // mockgen -typed -build_flags=-tags=gomock -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker // // Package quic is a generated GoMock package. package quic import ( reflect "reflect" time "time" protocol "github.com/quic-go/quic-go/internal/protocol" wire "github.com/quic-go/quic-go/internal/wire" gomock "go.uber.org/mock/gomock" ) // MockUnpacker is a mock of Unpacker interface. type MockUnpacker struct { ctrl *gomock.Controller recorder *MockUnpackerMockRecorder } // MockUnpackerMockRecorder is the mock recorder for MockUnpacker. type MockUnpackerMockRecorder struct { mock *MockUnpacker } // NewMockUnpacker creates a new mock instance. func NewMockUnpacker(ctrl *gomock.Controller) *MockUnpacker { mock := &MockUnpacker{ctrl: ctrl} mock.recorder = &MockUnpackerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockUnpacker) EXPECT() *MockUnpackerMockRecorder { return m.recorder } // UnpackLongHeader mocks base method. func (m *MockUnpacker) UnpackLongHeader(arg0 *wire.Header, arg1 []byte) (*unpackedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UnpackLongHeader", arg0, arg1) ret0, _ := ret[0].(*unpackedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // UnpackLongHeader indicates an expected call of UnpackLongHeader. func (mr *MockUnpackerMockRecorder) UnpackLongHeader(arg0, arg1 any) *MockUnpackerUnpackLongHeaderCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnpackLongHeader", reflect.TypeOf((*MockUnpacker)(nil).UnpackLongHeader), arg0, arg1) return &MockUnpackerUnpackLongHeaderCall{Call: call} } // MockUnpackerUnpackLongHeaderCall wrap *gomock.Call type MockUnpackerUnpackLongHeaderCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockUnpackerUnpackLongHeaderCall) Return(arg0 *unpackedPacket, arg1 error) *MockUnpackerUnpackLongHeaderCall { c.Call = c.Call.Return(arg0, arg1) return c } // Do rewrite *gomock.Call.Do func (c *MockUnpackerUnpackLongHeaderCall) Do(f func(*wire.Header, []byte) (*unpackedPacket, error)) *MockUnpackerUnpackLongHeaderCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockUnpackerUnpackLongHeaderCall) DoAndReturn(f func(*wire.Header, []byte) (*unpackedPacket, error)) *MockUnpackerUnpackLongHeaderCall { c.Call = c.Call.DoAndReturn(f) return c } // UnpackShortHeader mocks base method. func (m *MockUnpacker) UnpackShortHeader(arg0 time.Time, arg1 []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "UnpackShortHeader", arg0, arg1) ret0, _ := ret[0].(protocol.PacketNumber) ret1, _ := ret[1].(protocol.PacketNumberLen) ret2, _ := ret[2].(protocol.KeyPhaseBit) ret3, _ := ret[3].([]byte) ret4, _ := ret[4].(error) return ret0, ret1, ret2, ret3, ret4 } // UnpackShortHeader indicates an expected call of UnpackShortHeader. func (mr *MockUnpackerMockRecorder) UnpackShortHeader(arg0, arg1 any) *MockUnpackerUnpackShortHeaderCall { mr.mock.ctrl.T.Helper() call := mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnpackShortHeader", reflect.TypeOf((*MockUnpacker)(nil).UnpackShortHeader), arg0, arg1) return &MockUnpackerUnpackShortHeaderCall{Call: call} } // MockUnpackerUnpackShortHeaderCall wrap *gomock.Call type MockUnpackerUnpackShortHeaderCall struct { *gomock.Call } // Return rewrite *gomock.Call.Return func (c *MockUnpackerUnpackShortHeaderCall) Return(arg0 protocol.PacketNumber, arg1 protocol.PacketNumberLen, arg2 protocol.KeyPhaseBit, arg3 []byte, arg4 error) *MockUnpackerUnpackShortHeaderCall { c.Call = c.Call.Return(arg0, arg1, arg2, arg3, arg4) return c } // Do rewrite *gomock.Call.Do func (c *MockUnpackerUnpackShortHeaderCall) Do(f func(time.Time, []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error)) *MockUnpackerUnpackShortHeaderCall { c.Call = c.Call.Do(f) return c } // DoAndReturn rewrite *gomock.Call.DoAndReturn func (c *MockUnpackerUnpackShortHeaderCall) DoAndReturn(f func(time.Time, []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error)) *MockUnpackerUnpackShortHeaderCall { c.Call = c.Call.DoAndReturn(f) return c } golang-github-lucas-clemente-quic-go-0.46.0/mockgen.go000066400000000000000000000132311465664453100225610ustar00rootroot00000000000000//go:build gomock || generate package quic //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_conn_test.go github.com/quic-go/quic-go SendConn" type SendConn = sendConn //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_raw_conn_test.go github.com/quic-go/quic-go RawConn" type RawConn = rawConn //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sender_test.go github.com/quic-go/quic-go Sender" type Sender = sender //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_internal_test.go github.com/quic-go/quic-go StreamI" type StreamI = streamI //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_receive_stream_internal_test.go github.com/quic-go/quic-go ReceiveStreamI" type ReceiveStreamI = receiveStreamI //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_send_stream_internal_test.go github.com/quic-go/quic-go SendStreamI" type SendStreamI = sendStreamI //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_sender_test.go github.com/quic-go/quic-go StreamSender" type StreamSender = streamSender //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_control_frame_getter_test.go github.com/quic-go/quic-go StreamControlFrameGetter" type StreamControlFrameGetter = streamControlFrameGetter //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_frame_source_test.go github.com/quic-go/quic-go FrameSource" type FrameSource = frameSource //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_ack_frame_source_test.go github.com/quic-go/quic-go AckFrameSource" type AckFrameSource = ackFrameSource //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_stream_manager_test.go github.com/quic-go/quic-go StreamManager" type StreamManager = streamManager //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_sealing_manager_test.go github.com/quic-go/quic-go SealingManager" type SealingManager = sealingManager //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_unpacker_test.go github.com/quic-go/quic-go Unpacker" type Unpacker = unpacker //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packer_test.go github.com/quic-go/quic-go Packer" type Packer = packer //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_mtu_discoverer_test.go github.com/quic-go/quic-go MTUDiscoverer" type MTUDiscoverer = mtuDiscoverer //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_conn_runner_test.go github.com/quic-go/quic-go ConnRunner" type ConnRunner = connRunner //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_quic_conn_test.go github.com/quic-go/quic-go QUICConn" type QUICConn = quicConn //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_test.go github.com/quic-go/quic-go PacketHandler" type PacketHandler = packetHandler //go:generate sh -c "go run go.uber.org/mock/mockgen -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -build_flags=\"-tags=gomock\" -package quic -self_package github.com/quic-go/quic-go -destination mock_packet_handler_manager_test.go github.com/quic-go/quic-go PacketHandlerManager" type PacketHandlerManager = packetHandlerManager // Need to use source mode for the batchConn, since reflect mode follows type aliases. // See https://github.com/golang/mock/issues/244 for details. // //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -source sys_conn_oob.go -destination mock_batch_conn_test.go -mock_names batchConn=MockBatchConn" //go:generate sh -c "go run go.uber.org/mock/mockgen -typed -package quic -self_package github.com/quic-go/quic-go -self_package github.com/quic-go/quic-go -destination mock_packetconn_test.go net PacketConn" golang-github-lucas-clemente-quic-go-0.46.0/mtu_discoverer.go000066400000000000000000000162411465664453100241740ustar00rootroot00000000000000package quic import ( "time" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" ) type mtuDiscoverer interface { // Start starts the MTU discovery process. // It's unnecessary to call ShouldSendProbe before that. Start() ShouldSendProbe(now time.Time) bool CurrentSize() protocol.ByteCount GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount) } const ( // At some point, we have to stop searching for a higher MTU. // We're happy to send a packet that's 10 bytes smaller than the actual MTU. maxMTUDiff = 20 // send a probe packet every mtuProbeDelay RTTs mtuProbeDelay = 5 // Once maxLostMTUProbes MTU probe packets larger than a certain size are lost, // MTU discovery won't probe for larger MTUs than this size. // The algorithm used here is resilient to packet loss of (maxLostMTUProbes - 1) packets. maxLostMTUProbes = 3 ) // The Path MTU is found by sending a larger packet every now and then. // If the packet is acknowledged, we conclude that the path supports this larger packet size. // If the packet is lost, this can mean one of two things: // 1. The path doesn't support this larger packet size, or // 2. The packet was lost due to packet loss, independent of its size. // The algorithm used here is resilient to packet loss of (maxLostMTUProbes - 1) packets. // For simplicty, the following example use maxLostMTUProbes = 2. // // Initialization: // |------------------------------------------------------------------------------| // min max // // The first MTU probe packet will have size (min+max)/2. // Assume that this packet is acknowledged. We can now move the min marker, // and continue the search in the resulting interval. // // If 1st probe packet acknowledged: // |---------------------------------------|--------------------------------------| // min max // // If 1st probe packet lost: // |---------------------------------------|--------------------------------------| // min lost[0] max // // We can't conclude that the path doesn't support this packet size, since the loss of the probe // packet could have been unrelated to the packet size. A larger probe packet will be sent later on. // After a loss, the next probe packet has size (min+lost[0])/2. // Now assume this probe packet is acknowledged: // // 2nd probe packet acknowledged: // |------------------|--------------------|--------------------------------------| // min lost[0] max // // First of all, we conclude that the path supports at least this MTU. That's progress! // Second, we probe a bit more aggressively with the next probe packet: // After an acknowledgement, the next probe packet has size (min+max)/2. // This means we'll send a packet larger than the first probe packet (which was lost). // // If 3rd probe packet acknowledged: // |-------------------------------------------------|----------------------------| // min max // // We can conclude that the loss of the 1st probe packet was not due to its size, and // continue searching in a much smaller interval now. // // If 3rd probe packet lost: // |------------------|--------------------|---------|----------------------------| // min lost[0] max // // Since in our example numPTOProbes = 2, and we lost 2 packets smaller than max, we // conclude that this packet size is not supported on the path, and reduce the maximum // value of the search interval. // // MTU discovery concludes once the interval min and max has been narrowed down to maxMTUDiff. type mtuFinder struct { lastProbeTime time.Time mtuIncreased func(protocol.ByteCount) rttStats *utils.RTTStats inFlight protocol.ByteCount // the size of the probe packet currently in flight. InvalidByteCount if none is in flight min protocol.ByteCount limit protocol.ByteCount // on initialization, we treat the maximum size as the first "lost" packet lost [maxLostMTUProbes]protocol.ByteCount lastProbeWasLost bool tracer *logging.ConnectionTracer } var _ mtuDiscoverer = &mtuFinder{} func newMTUDiscoverer( rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount), tracer *logging.ConnectionTracer, ) *mtuFinder { f := &mtuFinder{ inFlight: protocol.InvalidByteCount, min: start, limit: max, rttStats: rttStats, mtuIncreased: mtuIncreased, tracer: tracer, } for i := range f.lost { if i == 0 { f.lost[i] = max continue } f.lost[i] = protocol.InvalidByteCount } return f } func (f *mtuFinder) done() bool { return f.max()-f.min <= maxMTUDiff+1 } func (f *mtuFinder) max() protocol.ByteCount { for i, v := range f.lost { if v == protocol.InvalidByteCount { return f.lost[i-1] } } return f.lost[len(f.lost)-1] } func (f *mtuFinder) Start() { f.lastProbeTime = time.Now() // makes sure the first probe packet is not sent immediately } func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { if f.lastProbeTime.IsZero() { return false } if f.inFlight != protocol.InvalidByteCount || f.done() { return false } return !now.Before(f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())) } func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) { var size protocol.ByteCount if f.lastProbeWasLost { size = (f.min + f.lost[0]) / 2 } else { size = (f.min + f.max()) / 2 } f.lastProbeTime = time.Now() f.inFlight = size return ackhandler.Frame{ Frame: &wire.PingFrame{}, Handler: &mtuFinderAckHandler{f}, }, size } func (f *mtuFinder) CurrentSize() protocol.ByteCount { return f.min } type mtuFinderAckHandler struct { *mtuFinder } var _ ackhandler.FrameHandler = &mtuFinderAckHandler{} func (h *mtuFinderAckHandler) OnAcked(wire.Frame) { size := h.inFlight if size == protocol.InvalidByteCount { panic("OnAcked callback called although there's no MTU probe packet in flight") } h.inFlight = protocol.InvalidByteCount h.min = size h.lastProbeWasLost = false // remove all values smaller than size from the lost array var j int for i, v := range h.lost { if size < v { j = i break } } if j > 0 { for i := 0; i < len(h.lost); i++ { if i+j < len(h.lost) { h.lost[i] = h.lost[i+j] } else { h.lost[i] = protocol.InvalidByteCount } } } if h.tracer != nil && h.tracer.UpdatedMTU != nil { h.tracer.UpdatedMTU(size, h.done()) } h.mtuIncreased(size) } func (h *mtuFinderAckHandler) OnLost(wire.Frame) { size := h.inFlight if size == protocol.InvalidByteCount { panic("OnLost callback called although there's no MTU probe packet in flight") } h.lastProbeWasLost = true h.inFlight = protocol.InvalidByteCount for i, v := range h.lost { if size < v { copy(h.lost[i+1:], h.lost[i:]) h.lost[i] = size break } } } golang-github-lucas-clemente-quic-go-0.46.0/mtu_discoverer_test.go000066400000000000000000000136031465664453100252320ustar00rootroot00000000000000package quic import ( "fmt" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("MTU Discoverer", func() { const ( rtt = 100 * time.Millisecond startMTU protocol.ByteCount = 1000 maxMTU protocol.ByteCount = 2000 ) var ( d *mtuFinder rttStats *utils.RTTStats now time.Time discoveredMTU protocol.ByteCount ) r := rand.New(rand.NewSource(uint64(GinkgoRandomSeed()))) BeforeEach(func() { rttStats = &utils.RTTStats{} rttStats.SetInitialRTT(rtt) Expect(rttStats.SmoothedRTT()).To(Equal(rtt)) d = newMTUDiscoverer( rttStats, startMTU, maxMTU, func(s protocol.ByteCount) { discoveredMTU = s }, nil, ) d.Start() now = time.Now() }) It("only allows a probe 5 RTTs after the handshake completes", func() { Expect(d.ShouldSendProbe(now)).To(BeFalse()) Expect(d.ShouldSendProbe(now.Add(rtt * 9 / 2))).To(BeFalse()) Expect(d.ShouldSendProbe(now.Add(rtt * 5))).To(BeTrue()) }) It("doesn't allow a probe if another probe is still in flight", func() { ping, _ := d.GetPing() Expect(d.ShouldSendProbe(now.Add(10 * rtt))).To(BeFalse()) ping.Handler.OnLost(ping.Frame) Expect(d.ShouldSendProbe(now.Add(10 * rtt))).To(BeTrue()) }) It("tries a lower size when a probe is lost", func() { ping, size := d.GetPing() Expect(size).To(Equal(protocol.ByteCount(1500))) ping.Handler.OnLost(ping.Frame) _, size = d.GetPing() Expect(size).To(Equal(protocol.ByteCount(1250))) }) It("tries a higher size and calls the callback when a probe is acknowledged", func() { ping, size := d.GetPing() Expect(size).To(Equal(protocol.ByteCount(1500))) ping.Handler.OnAcked(ping.Frame) Expect(discoveredMTU).To(Equal(protocol.ByteCount(1500))) _, size = d.GetPing() Expect(size).To(Equal(protocol.ByteCount(1750))) }) It("stops discovery after getting close enough to the MTU", func() { var sizes []protocol.ByteCount t := now.Add(5 * rtt) for d.ShouldSendProbe(t) { ping, size := d.GetPing() fmt.Println("sending", size) ping.Handler.OnAcked(ping.Frame) sizes = append(sizes, size) t = t.Add(5 * rtt) } Expect(sizes).To(Equal([]protocol.ByteCount{1500, 1750, 1875, 1937, 1968, 1984})) Expect(d.ShouldSendProbe(t.Add(10 * rtt))).To(BeFalse()) }) It("doesn't do discovery before being started", func() { d := newMTUDiscoverer(rttStats, startMTU, protocol.MaxByteCount, func(s protocol.ByteCount) {}, nil) for i := 0; i < 5; i++ { Expect(d.ShouldSendProbe(time.Now())).To(BeFalse()) } }) It("finds the MTU", MustPassRepeatedly(300), func() { maxMTU := protocol.ByteCount(r.Intn(int(3000-startMTU))) + startMTU + 1 currentMTU := startMTU var tracedMTU protocol.ByteCount var tracerDone bool d := newMTUDiscoverer( rttStats, startMTU, maxMTU, func(s protocol.ByteCount) { currentMTU = s }, &logging.ConnectionTracer{ UpdatedMTU: func(mtu logging.ByteCount, done bool) { tracedMTU = mtu tracerDone = done }, }, ) d.Start() now := time.Now() realMTU := protocol.ByteCount(r.Intn(int(maxMTU-startMTU))) + startMTU fmt.Fprintf(GinkgoWriter, "MTU: %d, max: %d\n", realMTU, maxMTU) t := now.Add(mtuProbeDelay * rtt) var probes []protocol.ByteCount for d.ShouldSendProbe(t) { if len(probes) > 24 { Fail(fmt.Sprintf("too many iterations: %v", probes)) } ping, size := d.GetPing() probes = append(probes, size) if size <= realMTU { ping.Handler.OnAcked(ping.Frame) } else { ping.Handler.OnLost(ping.Frame) } t = t.Add(mtuProbeDelay * rtt) } diff := realMTU - currentMTU Expect(diff).To(BeNumerically(">=", 0)) if maxMTU > currentMTU+maxMTU { Expect(tracedMTU).To(Equal(currentMTU)) Expect(tracerDone).To(BeTrue()) } fmt.Fprintf(GinkgoWriter, "MTU discovered: %d (diff: %d)\n", currentMTU, diff) fmt.Fprintf(GinkgoWriter, "probes sent (%d): %v\n", len(probes), probes) Expect(diff).To(BeNumerically("<=", maxMTUDiff)) }) const maxRandomLoss = maxLostMTUProbes - 1 It(fmt.Sprintf("finds the MTU, with up to %d packets lost", maxRandomLoss), MustPassRepeatedly(500), func() { maxMTU := protocol.ByteCount(r.Intn(int(3000-startMTU))) + startMTU + 1 currentMTU := startMTU var tracedMTU protocol.ByteCount var tracerDone bool d := newMTUDiscoverer( rttStats, startMTU, maxMTU, func(s protocol.ByteCount) { currentMTU = s }, &logging.ConnectionTracer{ UpdatedMTU: func(mtu logging.ByteCount, done bool) { tracedMTU = mtu tracerDone = done }, }, ) d.Start() now := time.Now() realMTU := protocol.ByteCount(r.Intn(int(maxMTU-startMTU))) + startMTU fmt.Fprintf(GinkgoWriter, "MTU: %d, max: %d\n", realMTU, maxMTU) t := now.Add(mtuProbeDelay * rtt) var probes, randomLosses []protocol.ByteCount for d.ShouldSendProbe(t) { if len(probes) > 32 { Fail(fmt.Sprintf("too many iterations: %v", probes)) } ping, size := d.GetPing() probes = append(probes, size) packetFits := size <= realMTU var acked bool if packetFits { randomLoss := r.Intn(maxLostMTUProbes) == 0 && len(randomLosses) < maxRandomLoss if randomLoss { randomLosses = append(randomLosses, size) } else { ping.Handler.OnAcked(ping.Frame) acked = true } } if !acked { ping.Handler.OnLost(ping.Frame) } t = t.Add(mtuProbeDelay * rtt) } diff := realMTU - currentMTU Expect(diff).To(BeNumerically(">=", 0)) if maxMTU > currentMTU+maxMTU { Expect(tracedMTU).To(Equal(currentMTU)) Expect(tracerDone).To(BeTrue()) } fmt.Fprintf(GinkgoWriter, "MTU discovered with random losses %v: %d (diff: %d)\n", randomLosses, currentMTU, diff) fmt.Fprintf(GinkgoWriter, "probes sent (%d): %v\n", len(probes), probes) Expect(diff).To(BeNumerically("<=", maxMTUDiff)) }) }) golang-github-lucas-clemente-quic-go-0.46.0/multiplexer.go000066400000000000000000000033631465664453100235150ustar00rootroot00000000000000package quic import ( "fmt" "net" "sync" "github.com/quic-go/quic-go/internal/utils" ) var ( connMuxerOnce sync.Once connMuxer multiplexer ) type indexableConn interface{ LocalAddr() net.Addr } type multiplexer interface { AddConn(conn indexableConn) RemoveConn(indexableConn) error } // The connMultiplexer listens on multiple net.PacketConns and dispatches // incoming packets to the connection handler. type connMultiplexer struct { mutex sync.Mutex conns map[string] /* LocalAddr().String() */ indexableConn logger utils.Logger } var _ multiplexer = &connMultiplexer{} func getMultiplexer() multiplexer { connMuxerOnce.Do(func() { connMuxer = &connMultiplexer{ conns: make(map[string]indexableConn), logger: utils.DefaultLogger.WithPrefix("muxer"), } }) return connMuxer } func (m *connMultiplexer) index(addr net.Addr) string { return addr.Network() + " " + addr.String() } func (m *connMultiplexer) AddConn(c indexableConn) { m.mutex.Lock() defer m.mutex.Unlock() connIndex := m.index(c.LocalAddr()) p, ok := m.conns[connIndex] if ok { // Panics if we're already listening on this connection. // This is a safeguard because we're introducing a breaking API change, see // https://github.com/quic-go/quic-go/issues/3727 for details. // We'll remove this at a later time, when most users of the library have made the switch. panic("connection already exists") // TODO: write a nice message } m.conns[connIndex] = p } func (m *connMultiplexer) RemoveConn(c indexableConn) error { m.mutex.Lock() defer m.mutex.Unlock() connIndex := m.index(c.LocalAddr()) if _, ok := m.conns[connIndex]; !ok { return fmt.Errorf("cannote remove connection, connection is unknown") } delete(m.conns, connIndex) return nil } golang-github-lucas-clemente-quic-go-0.46.0/multiplexer_test.go000066400000000000000000000014401465664453100245460ustar00rootroot00000000000000package quic import ( "net" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Multiplexer", func() { It("adds new packet conns", func() { conn1 := NewMockPacketConn(mockCtrl) conn1.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}) getMultiplexer().AddConn(conn1) conn2 := NewMockPacketConn(mockCtrl) conn2.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1235}) getMultiplexer().AddConn(conn2) }) It("panics when the same connection is added twice", func() { conn := NewMockPacketConn(mockCtrl) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 4321}).Times(2) getMultiplexer().AddConn(conn) Expect(func() { getMultiplexer().AddConn(conn) }).To(Panic()) }) }) golang-github-lucas-clemente-quic-go-0.46.0/oss-fuzz.sh000066400000000000000000000027271465664453100227530ustar00rootroot00000000000000#!/bin/bash # Install Go manually, since oss-fuzz ships with an outdated Go version. # See https://github.com/google/oss-fuzz/pull/10643. export CXX="${CXX} -lresolv" # required by Go 1.20 wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz \ && mkdir temp-go \ && rm -rf /root/.go/* \ && tar -C temp-go/ -xzf go1.22.0.linux-amd64.tar.gz \ && mv temp-go/go/* /root/.go/ \ && rm -rf temp-go go1.22.0.linux-amd64.tar.gz ( # fuzz qpack compile_go_fuzzer github.com/quic-go/qpack/fuzzing Fuzz qpack_fuzzer ) ( # fuzz quic-go compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/frames Fuzz frame_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/header Fuzz header_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/transportparameters Fuzz transportparameter_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/tokens Fuzz token_fuzzer compile_go_fuzzer github.com/quic-go/quic-go/fuzzing/handshake Fuzz handshake_fuzzer if [ $SANITIZER == "coverage" ]; then # no need for corpora if coverage exit 0 fi # generate seed corpora cd $GOPATH/src/github.com/quic-go/quic-go/ go generate -x ./fuzzing/... zip --quiet -r $OUT/header_fuzzer_seed_corpus.zip fuzzing/header/corpus zip --quiet -r $OUT/frame_fuzzer_seed_corpus.zip fuzzing/frames/corpus zip --quiet -r $OUT/transportparameter_fuzzer_seed_corpus.zip fuzzing/transportparameters/corpus zip --quiet -r $OUT/handshake_fuzzer_seed_corpus.zip fuzzing/handshake/corpus ) # for debugging ls -al $OUT golang-github-lucas-clemente-quic-go-0.46.0/packet_handler_map.go000066400000000000000000000154601465664453100247450ustar00rootroot00000000000000package quic import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "hash" "io" "net" "sync" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" ) type connCapabilities struct { // This connection has the Don't Fragment (DF) bit set. // This means it makes to run DPLPMTUD. DF bool // GSO (Generic Segmentation Offload) supported GSO bool // ECN (Explicit Congestion Notifications) supported ECN bool } // rawConn is a connection that allow reading of a receivedPackeh. type rawConn interface { ReadPacket() (receivedPacket, error) // WritePacket writes a packet on the wire. // gsoSize is the size of a single packet, or 0 to disable GSO. // It is invalid to set gsoSize if capabilities.GSO is not set. WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) LocalAddr() net.Addr SetReadDeadline(time.Time) error io.Closer capabilities() connCapabilities } type closePacket struct { payload []byte addr net.Addr info packetInfo } type packetHandlerMap struct { mutex sync.Mutex handlers map[protocol.ConnectionID]packetHandler resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler closed bool closeChan chan struct{} enqueueClosePacket func(closePacket) deleteRetiredConnsAfter time.Duration statelessResetMutex sync.Mutex statelessResetHasher hash.Hash logger utils.Logger } var _ packetHandlerManager = &packetHandlerMap{} func newPacketHandlerMap(key *StatelessResetKey, enqueueClosePacket func(closePacket), logger utils.Logger) *packetHandlerMap { h := &packetHandlerMap{ closeChan: make(chan struct{}), handlers: make(map[protocol.ConnectionID]packetHandler), resetTokens: make(map[protocol.StatelessResetToken]packetHandler), deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, enqueueClosePacket: enqueueClosePacket, logger: logger, } if key != nil { h.statelessResetHasher = hmac.New(sha256.New, key[:]) } if h.logger.Debug() { go h.logUsage() } return h } func (h *packetHandlerMap) logUsage() { ticker := time.NewTicker(2 * time.Second) var printedZero bool for { select { case <-h.closeChan: return case <-ticker.C: } h.mutex.Lock() numHandlers := len(h.handlers) numTokens := len(h.resetTokens) h.mutex.Unlock() // If the number tracked handlers and tokens is zero, only print it a single time. hasZero := numHandlers == 0 && numTokens == 0 if !hasZero || (hasZero && !printedZero) { h.logger.Debugf("Tracking %d connection IDs and %d reset tokens.\n", numHandlers, numTokens) printedZero = false if hasZero { printedZero = true } } } } func (h *packetHandlerMap) Get(id protocol.ConnectionID) (packetHandler, bool) { h.mutex.Lock() defer h.mutex.Unlock() handler, ok := h.handlers[id] return handler, ok } func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { h.mutex.Lock() defer h.mutex.Unlock() if _, ok := h.handlers[id]; ok { h.logger.Debugf("Not adding connection ID %s, as it already exists.", id) return false } h.handlers[id] = handler h.logger.Debugf("Adding connection ID %s.", id) return true } func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, handler packetHandler) bool { h.mutex.Lock() defer h.mutex.Unlock() if _, ok := h.handlers[clientDestConnID]; ok { h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) return false } h.handlers[clientDestConnID] = handler h.handlers[newConnID] = handler h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID) return true } func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { h.mutex.Lock() delete(h.handlers, id) h.mutex.Unlock() h.logger.Debugf("Removing connection ID %s.", id) } func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter) time.AfterFunc(h.deleteRetiredConnsAfter, func() { h.mutex.Lock() delete(h.handlers, id) h.mutex.Unlock() h.logger.Debugf("Removing connection ID %s after it has been retired.", id) }) } // ReplaceWithClosed is called when a connection is closed. // Depending on which side closed the connection, we need to: // * remote close: absorb delayed packets // * local close: retransmit the CONNECTION_CLOSE packet, in case it was lost func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, connClosePacket []byte) { var handler packetHandler if connClosePacket != nil { handler = newClosedLocalConn( func(addr net.Addr, info packetInfo) { h.enqueueClosePacket(closePacket{payload: connClosePacket, addr: addr, info: info}) }, h.logger, ) } else { handler = newClosedRemoteConn() } h.mutex.Lock() for _, id := range ids { h.handlers[id] = handler } h.mutex.Unlock() h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids) time.AfterFunc(h.deleteRetiredConnsAfter, func() { h.mutex.Lock() for _, id := range ids { delete(h.handlers, id) } h.mutex.Unlock() h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids) }) } func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) { h.mutex.Lock() h.resetTokens[token] = handler h.mutex.Unlock() } func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) { h.mutex.Lock() delete(h.resetTokens, token) h.mutex.Unlock() } func (h *packetHandlerMap) GetByResetToken(token protocol.StatelessResetToken) (packetHandler, bool) { h.mutex.Lock() defer h.mutex.Unlock() handler, ok := h.resetTokens[token] return handler, ok } func (h *packetHandlerMap) Close(e error) { h.mutex.Lock() if h.closed { h.mutex.Unlock() return } close(h.closeChan) var wg sync.WaitGroup for _, handler := range h.handlers { wg.Add(1) go func(handler packetHandler) { handler.destroy(e) wg.Done() }(handler) } h.closed = true h.mutex.Unlock() wg.Wait() } func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken { var token protocol.StatelessResetToken if h.statelessResetHasher == nil { // Return a random stateless reset token. // This token will be sent in the server's transport parameters. // By using a random token, an off-path attacker won't be able to disrupt the connection. rand.Read(token[:]) return token } h.statelessResetMutex.Lock() h.statelessResetHasher.Write(connID.Bytes()) copy(token[:], h.statelessResetHasher.Sum(nil)) h.statelessResetHasher.Reset() h.statelessResetMutex.Unlock() return token } golang-github-lucas-clemente-quic-go-0.46.0/packet_handler_map_test.go000066400000000000000000000133321465664453100260000ustar00rootroot00000000000000package quic import ( "crypto/rand" "errors" "net" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Packet Handler Map", func() { It("adds and gets", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) handler := NewMockPacketHandler(mockCtrl) Expect(m.Add(connID, handler)).To(BeTrue()) h, ok := m.Get(connID) Expect(ok).To(BeTrue()) Expect(h).To(Equal(handler)) }) It("refused to add duplicates", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) handler := NewMockPacketHandler(mockCtrl) Expect(m.Add(connID, handler)).To(BeTrue()) Expect(m.Add(connID, handler)).To(BeFalse()) }) It("removes", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) handler := NewMockPacketHandler(mockCtrl) Expect(m.Add(connID, handler)).To(BeTrue()) m.Remove(connID) _, ok := m.Get(connID) Expect(ok).To(BeFalse()) Expect(m.Add(connID, handler)).To(BeTrue()) }) It("retires", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) dur := scaleDuration(50 * time.Millisecond) m.deleteRetiredConnsAfter = dur connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) handler := NewMockPacketHandler(mockCtrl) Expect(m.Add(connID, handler)).To(BeTrue()) m.Retire(connID) _, ok := m.Get(connID) Expect(ok).To(BeTrue()) time.Sleep(dur) Eventually(func() bool { _, ok := m.Get(connID); return ok }).Should(BeFalse()) }) It("adds newly to-be-constructed handlers", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) connID1 := protocol.ParseConnectionID([]byte{1, 2, 3, 4}) connID2 := protocol.ParseConnectionID([]byte{4, 3, 2, 1}) h := NewMockPacketHandler(mockCtrl) Expect(m.AddWithConnID(connID1, connID2, h)).To(BeTrue()) // collision of the destination connection ID, this handler should not be added Expect(m.AddWithConnID(connID1, protocol.ParseConnectionID([]byte{1, 2, 3}), nil)).To(BeFalse()) }) It("adds, gets and removes reset tokens", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf} handler := NewMockPacketHandler(mockCtrl) m.AddResetToken(token, handler) h, ok := m.GetByResetToken(token) Expect(ok).To(BeTrue()) Expect(h).To(Equal(h)) m.RemoveResetToken(token) _, ok = m.GetByResetToken(token) Expect(ok).To(BeFalse()) }) It("generates stateless reset token, if no key is set", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) b := make([]byte, 8) rand.Read(b) connID := protocol.ParseConnectionID(b) token := m.GetStatelessResetToken(connID) for i := 0; i < 1000; i++ { to := m.GetStatelessResetToken(connID) Expect(to).ToNot(Equal(token)) token = to } }) It("generates stateless reset token, if a key is set", func() { var key StatelessResetKey rand.Read(key[:]) m := newPacketHandlerMap(&key, nil, utils.DefaultLogger) b := make([]byte, 8) rand.Read(b) connID := protocol.ParseConnectionID(b) token := m.GetStatelessResetToken(connID) Expect(token).ToNot(BeZero()) Expect(m.GetStatelessResetToken(connID)).To(Equal(token)) // generate a new connection ID rand.Read(b) connID2 := protocol.ParseConnectionID(b) Expect(m.GetStatelessResetToken(connID2)).ToNot(Equal(token)) }) It("replaces locally closed connections", func() { var closePackets []closePacket m := newPacketHandlerMap(nil, func(p closePacket) { closePackets = append(closePackets, p) }, utils.DefaultLogger) dur := scaleDuration(50 * time.Millisecond) m.deleteRetiredConnsAfter = dur handler := NewMockPacketHandler(mockCtrl) connID := protocol.ParseConnectionID([]byte{4, 3, 2, 1}) Expect(m.Add(connID, handler)).To(BeTrue()) m.ReplaceWithClosed([]protocol.ConnectionID{connID}, []byte("foobar")) h, ok := m.Get(connID) Expect(ok).To(BeTrue()) Expect(h).ToNot(Equal(handler)) addr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234} h.handlePacket(receivedPacket{remoteAddr: addr}) Expect(closePackets).To(HaveLen(1)) Expect(closePackets[0].addr).To(Equal(addr)) Expect(closePackets[0].payload).To(Equal([]byte("foobar"))) time.Sleep(dur) Eventually(func() bool { _, ok := m.Get(connID); return ok }).Should(BeFalse()) }) It("replaces remote closed connections", func() { var closePackets []closePacket m := newPacketHandlerMap(nil, func(p closePacket) { closePackets = append(closePackets, p) }, utils.DefaultLogger) dur := scaleDuration(50 * time.Millisecond) m.deleteRetiredConnsAfter = dur handler := NewMockPacketHandler(mockCtrl) connID := protocol.ParseConnectionID([]byte{4, 3, 2, 1}) Expect(m.Add(connID, handler)).To(BeTrue()) m.ReplaceWithClosed([]protocol.ConnectionID{connID}, nil) h, ok := m.Get(connID) Expect(ok).To(BeTrue()) Expect(h).ToNot(Equal(handler)) addr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234} h.handlePacket(receivedPacket{remoteAddr: addr}) Expect(closePackets).To(BeEmpty()) time.Sleep(dur) Eventually(func() bool { _, ok := m.Get(connID); return ok }).Should(BeFalse()) }) It("closes", func() { m := newPacketHandlerMap(nil, nil, utils.DefaultLogger) testErr := errors.New("shutdown") for i := 0; i < 10; i++ { conn := NewMockPacketHandler(mockCtrl) conn.EXPECT().destroy(testErr) b := make([]byte, 12) rand.Read(b) m.Add(protocol.ParseConnectionID(b), conn) } m.Close(testErr) // check that Close can be called multiple times m.Close(errors.New("close")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/packet_packer.go000066400000000000000000000777251465664453100237540ustar00rootroot00000000000000package quic import ( crand "crypto/rand" "encoding/binary" "errors" "fmt" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" ) var errNothingToPack = errors.New("nothing to pack") type packer interface { PackCoalescedPacket(onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) PackAckOnlyPacket(maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) AppendPacket(buf *packetBuffer, maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) MaybePackProbePacket(protocol.EncryptionLevel, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) PackConnectionClose(*qerr.TransportError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) PackApplicationClose(*qerr.ApplicationError, protocol.ByteCount, protocol.Version) (*coalescedPacket, error) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) SetToken([]byte) } type sealer interface { handshake.LongHeaderSealer } type payload struct { streamFrames []ackhandler.StreamFrame frames []ackhandler.Frame ack *wire.AckFrame length protocol.ByteCount } type longHeaderPacket struct { header *wire.ExtendedHeader ack *wire.AckFrame frames []ackhandler.Frame streamFrames []ackhandler.StreamFrame // only used for 0-RTT packets length protocol.ByteCount } type shortHeaderPacket struct { PacketNumber protocol.PacketNumber Frames []ackhandler.Frame StreamFrames []ackhandler.StreamFrame Ack *wire.AckFrame Length protocol.ByteCount IsPathMTUProbePacket bool // used for logging DestConnID protocol.ConnectionID PacketNumberLen protocol.PacketNumberLen KeyPhase protocol.KeyPhaseBit } func (p *shortHeaderPacket) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.Frames) } type coalescedPacket struct { buffer *packetBuffer longHdrPackets []*longHeaderPacket shortHdrPacket *shortHeaderPacket } // IsOnlyShortHeaderPacket says if this packet only contains a short header packet (and no long header packets). func (p *coalescedPacket) IsOnlyShortHeaderPacket() bool { return len(p.longHdrPackets) == 0 && p.shortHdrPacket != nil } func (p *longHeaderPacket) EncryptionLevel() protocol.EncryptionLevel { //nolint:exhaustive // Will never be called for Retry packets (and they don't have encrypted data). switch p.header.Type { case protocol.PacketTypeInitial: return protocol.EncryptionInitial case protocol.PacketTypeHandshake: return protocol.EncryptionHandshake case protocol.PacketType0RTT: return protocol.Encryption0RTT default: panic("can't determine encryption level") } } func (p *longHeaderPacket) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.frames) } type packetNumberManager interface { PeekPacketNumber(protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) PopPacketNumber(protocol.EncryptionLevel) protocol.PacketNumber } type sealingManager interface { GetInitialSealer() (handshake.LongHeaderSealer, error) GetHandshakeSealer() (handshake.LongHeaderSealer, error) Get0RTTSealer() (handshake.LongHeaderSealer, error) Get1RTTSealer() (handshake.ShortHeaderSealer, error) } type frameSource interface { HasData() bool AppendStreamFrames([]ackhandler.StreamFrame, protocol.ByteCount, protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) AppendControlFrames([]ackhandler.Frame, protocol.ByteCount, protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) } type ackFrameSource interface { GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame } type packetPacker struct { srcConnID protocol.ConnectionID getDestConnID func() protocol.ConnectionID perspective protocol.Perspective cryptoSetup sealingManager initialStream *cryptoStream handshakeStream *cryptoStream token []byte pnManager packetNumberManager framer frameSource acks ackFrameSource datagramQueue *datagramQueue retransmissionQueue *retransmissionQueue rand rand.Rand numNonAckElicitingAcks int } var _ packer = &packetPacker{} func newPacketPacker( srcConnID protocol.ConnectionID, getDestConnID func() protocol.ConnectionID, initialStream, handshakeStream *cryptoStream, packetNumberManager packetNumberManager, retransmissionQueue *retransmissionQueue, cryptoSetup sealingManager, framer frameSource, acks ackFrameSource, datagramQueue *datagramQueue, perspective protocol.Perspective, ) *packetPacker { var b [8]byte _, _ = crand.Read(b[:]) return &packetPacker{ cryptoSetup: cryptoSetup, getDestConnID: getDestConnID, srcConnID: srcConnID, initialStream: initialStream, handshakeStream: handshakeStream, retransmissionQueue: retransmissionQueue, datagramQueue: datagramQueue, perspective: perspective, framer: framer, acks: acks, rand: *rand.New(rand.NewSource(binary.BigEndian.Uint64(b[:]))), pnManager: packetNumberManager, } } // PackConnectionClose packs a packet that closes the connection with a transport error. func (p *packetPacker) PackConnectionClose(e *qerr.TransportError, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { var reason string // don't send details of crypto errors if !e.ErrorCode.IsCryptoError() { reason = e.ErrorMessage } return p.packConnectionClose(false, uint64(e.ErrorCode), e.FrameType, reason, maxPacketSize, v) } // PackApplicationClose packs a packet that closes the connection with an application error. func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage, maxPacketSize, v) } func (p *packetPacker) packConnectionClose( isApplicationError bool, errorCode uint64, frameType uint64, reason string, maxPacketSize protocol.ByteCount, v protocol.Version, ) (*coalescedPacket, error) { var sealers [4]sealer var hdrs [3]*wire.ExtendedHeader var payloads [4]payload var size protocol.ByteCount var connID protocol.ConnectionID var oneRTTPacketNumber protocol.PacketNumber var oneRTTPacketNumberLen protocol.PacketNumberLen var keyPhase protocol.KeyPhaseBit // only set for 1-RTT var numLongHdrPackets uint8 encLevels := [4]protocol.EncryptionLevel{protocol.EncryptionInitial, protocol.EncryptionHandshake, protocol.Encryption0RTT, protocol.Encryption1RTT} for i, encLevel := range encLevels { if p.perspective == protocol.PerspectiveServer && encLevel == protocol.Encryption0RTT { continue } ccf := &wire.ConnectionCloseFrame{ IsApplicationError: isApplicationError, ErrorCode: errorCode, FrameType: frameType, ReasonPhrase: reason, } // don't send application errors in Initial or Handshake packets if isApplicationError && (encLevel == protocol.EncryptionInitial || encLevel == protocol.EncryptionHandshake) { ccf.IsApplicationError = false ccf.ErrorCode = uint64(qerr.ApplicationErrorErrorCode) ccf.ReasonPhrase = "" } pl := payload{ frames: []ackhandler.Frame{{Frame: ccf}}, length: ccf.Length(v), } var sealer sealer var err error switch encLevel { case protocol.EncryptionInitial: sealer, err = p.cryptoSetup.GetInitialSealer() case protocol.EncryptionHandshake: sealer, err = p.cryptoSetup.GetHandshakeSealer() case protocol.Encryption0RTT: sealer, err = p.cryptoSetup.Get0RTTSealer() case protocol.Encryption1RTT: var s handshake.ShortHeaderSealer s, err = p.cryptoSetup.Get1RTTSealer() if err == nil { keyPhase = s.KeyPhase() } sealer = s } if err == handshake.ErrKeysNotYetAvailable || err == handshake.ErrKeysDropped { continue } if err != nil { return nil, err } sealers[i] = sealer var hdr *wire.ExtendedHeader if encLevel == protocol.Encryption1RTT { connID = p.getDestConnID() oneRTTPacketNumber, oneRTTPacketNumberLen = p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, pl) } else { hdr = p.getLongHeader(encLevel, v) hdrs[i] = hdr size += p.longHeaderPacketLength(hdr, pl, v) + protocol.ByteCount(sealer.Overhead()) numLongHdrPackets++ } payloads[i] = pl } buffer := getPacketBuffer() packet := &coalescedPacket{ buffer: buffer, longHdrPackets: make([]*longHeaderPacket, 0, numLongHdrPackets), } for i, encLevel := range encLevels { if sealers[i] == nil { continue } var paddingLen protocol.ByteCount if encLevel == protocol.EncryptionInitial { paddingLen = p.initialPaddingLen(payloads[i].frames, size, maxPacketSize) } if encLevel == protocol.Encryption1RTT { shp, err := p.appendShortHeaderPacket(buffer, connID, oneRTTPacketNumber, oneRTTPacketNumberLen, keyPhase, payloads[i], paddingLen, maxPacketSize, sealers[i], false, v) if err != nil { return nil, err } packet.shortHdrPacket = &shp } else { longHdrPacket, err := p.appendLongHeaderPacket(buffer, hdrs[i], payloads[i], paddingLen, encLevel, sealers[i], v) if err != nil { return nil, err } packet.longHdrPackets = append(packet.longHdrPackets, longHdrPacket) } } return packet, nil } // longHeaderPacketLength calculates the length of a serialized long header packet. // It takes into account that packets that have a tiny payload need to be padded, // such that len(payload) + packet number len >= 4 + AEAD overhead func (p *packetPacker) longHeaderPacketLength(hdr *wire.ExtendedHeader, pl payload, v protocol.Version) protocol.ByteCount { var paddingLen protocol.ByteCount pnLen := protocol.ByteCount(hdr.PacketNumberLen) if pl.length < 4-pnLen { paddingLen = 4 - pnLen - pl.length } return hdr.GetLength(v) + pl.length + paddingLen } // shortHeaderPacketLength calculates the length of a serialized short header packet. // It takes into account that packets that have a tiny payload need to be padded, // such that len(payload) + packet number len >= 4 + AEAD overhead func (p *packetPacker) shortHeaderPacketLength(connID protocol.ConnectionID, pnLen protocol.PacketNumberLen, pl payload) protocol.ByteCount { var paddingLen protocol.ByteCount if pl.length < 4-protocol.ByteCount(pnLen) { paddingLen = 4 - protocol.ByteCount(pnLen) - pl.length } return wire.ShortHeaderLen(connID, pnLen) + pl.length + paddingLen } // size is the expected size of the packet, if no padding was applied. func (p *packetPacker) initialPaddingLen(frames []ackhandler.Frame, currentSize, maxPacketSize protocol.ByteCount) protocol.ByteCount { // For the server, only ack-eliciting Initial packets need to be padded. if p.perspective == protocol.PerspectiveServer && !ackhandler.HasAckElicitingFrames(frames) { return 0 } if currentSize >= maxPacketSize { return 0 } return maxPacketSize - currentSize } // PackCoalescedPacket packs a new packet. // It packs an Initial / Handshake if there is data to send in these packet number spaces. // It should only be called before the handshake is confirmed. func (p *packetPacker) PackCoalescedPacket(onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { var ( initialHdr, handshakeHdr, zeroRTTHdr *wire.ExtendedHeader initialPayload, handshakePayload, zeroRTTPayload, oneRTTPayload payload oneRTTPacketNumber protocol.PacketNumber oneRTTPacketNumberLen protocol.PacketNumberLen ) // Try packing an Initial packet. initialSealer, err := p.cryptoSetup.GetInitialSealer() if err != nil && err != handshake.ErrKeysDropped { return nil, err } var size protocol.ByteCount if initialSealer != nil { initialHdr, initialPayload = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(initialSealer.Overhead()), protocol.EncryptionInitial, onlyAck, true, v) if initialPayload.length > 0 { size += p.longHeaderPacketLength(initialHdr, initialPayload, v) + protocol.ByteCount(initialSealer.Overhead()) } } // Add a Handshake packet. var handshakeSealer sealer if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) { var err error handshakeSealer, err = p.cryptoSetup.GetHandshakeSealer() if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { return nil, err } if handshakeSealer != nil { handshakeHdr, handshakePayload = p.maybeGetCryptoPacket(maxPacketSize-size-protocol.ByteCount(handshakeSealer.Overhead()), protocol.EncryptionHandshake, onlyAck, size == 0, v) if handshakePayload.length > 0 { s := p.longHeaderPacketLength(handshakeHdr, handshakePayload, v) + protocol.ByteCount(handshakeSealer.Overhead()) size += s } } } // Add a 0-RTT / 1-RTT packet. var zeroRTTSealer sealer var oneRTTSealer handshake.ShortHeaderSealer var connID protocol.ConnectionID var kp protocol.KeyPhaseBit if (onlyAck && size == 0) || (!onlyAck && size < maxPacketSize-protocol.MinCoalescedPacketSize) { var err error oneRTTSealer, err = p.cryptoSetup.Get1RTTSealer() if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { return nil, err } if err == nil { // 1-RTT kp = oneRTTSealer.KeyPhase() connID = p.getDestConnID() oneRTTPacketNumber, oneRTTPacketNumberLen = p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) hdrLen := wire.ShortHeaderLen(connID, oneRTTPacketNumberLen) oneRTTPayload = p.maybeGetShortHeaderPacket(oneRTTSealer, hdrLen, maxPacketSize-size, onlyAck, size == 0, v) if oneRTTPayload.length > 0 { size += p.shortHeaderPacketLength(connID, oneRTTPacketNumberLen, oneRTTPayload) + protocol.ByteCount(oneRTTSealer.Overhead()) } } else if p.perspective == protocol.PerspectiveClient && !onlyAck { // 0-RTT packets can't contain ACK frames var err error zeroRTTSealer, err = p.cryptoSetup.Get0RTTSealer() if err != nil && err != handshake.ErrKeysDropped && err != handshake.ErrKeysNotYetAvailable { return nil, err } if zeroRTTSealer != nil { zeroRTTHdr, zeroRTTPayload = p.maybeGetAppDataPacketFor0RTT(zeroRTTSealer, maxPacketSize-size, v) if zeroRTTPayload.length > 0 { size += p.longHeaderPacketLength(zeroRTTHdr, zeroRTTPayload, v) + protocol.ByteCount(zeroRTTSealer.Overhead()) } } } } if initialPayload.length == 0 && handshakePayload.length == 0 && zeroRTTPayload.length == 0 && oneRTTPayload.length == 0 { return nil, nil } buffer := getPacketBuffer() packet := &coalescedPacket{ buffer: buffer, longHdrPackets: make([]*longHeaderPacket, 0, 3), } if initialPayload.length > 0 { padding := p.initialPaddingLen(initialPayload.frames, size, maxPacketSize) cont, err := p.appendLongHeaderPacket(buffer, initialHdr, initialPayload, padding, protocol.EncryptionInitial, initialSealer, v) if err != nil { return nil, err } packet.longHdrPackets = append(packet.longHdrPackets, cont) } if handshakePayload.length > 0 { cont, err := p.appendLongHeaderPacket(buffer, handshakeHdr, handshakePayload, 0, protocol.EncryptionHandshake, handshakeSealer, v) if err != nil { return nil, err } packet.longHdrPackets = append(packet.longHdrPackets, cont) } if zeroRTTPayload.length > 0 { longHdrPacket, err := p.appendLongHeaderPacket(buffer, zeroRTTHdr, zeroRTTPayload, 0, protocol.Encryption0RTT, zeroRTTSealer, v) if err != nil { return nil, err } packet.longHdrPackets = append(packet.longHdrPackets, longHdrPacket) } else if oneRTTPayload.length > 0 { shp, err := p.appendShortHeaderPacket(buffer, connID, oneRTTPacketNumber, oneRTTPacketNumberLen, kp, oneRTTPayload, 0, maxPacketSize, oneRTTSealer, false, v) if err != nil { return nil, err } packet.shortHdrPacket = &shp } return packet, nil } // PackAckOnlyPacket packs a packet containing only an ACK in the application data packet number space. // It should be called after the handshake is confirmed. func (p *packetPacker) PackAckOnlyPacket(maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { buf := getPacketBuffer() packet, err := p.appendPacket(buf, true, maxPacketSize, v) return packet, buf, err } // AppendPacket packs a packet in the application data packet number space. // It should be called after the handshake is confirmed. func (p *packetPacker) AppendPacket(buf *packetBuffer, maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) { return p.appendPacket(buf, false, maxPacketSize, v) } func (p *packetPacker) appendPacket(buf *packetBuffer, onlyAck bool, maxPacketSize protocol.ByteCount, v protocol.Version) (shortHeaderPacket, error) { sealer, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return shortHeaderPacket{}, err } pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) connID := p.getDestConnID() hdrLen := wire.ShortHeaderLen(connID, pnLen) pl := p.maybeGetShortHeaderPacket(sealer, hdrLen, maxPacketSize, onlyAck, true, v) if pl.length == 0 { return shortHeaderPacket{}, errNothingToPack } kp := sealer.KeyPhase() return p.appendShortHeaderPacket(buf, connID, pn, pnLen, kp, pl, 0, maxPacketSize, sealer, false, v) } func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize protocol.ByteCount, encLevel protocol.EncryptionLevel, onlyAck, ackAllowed bool, v protocol.Version) (*wire.ExtendedHeader, payload) { if onlyAck { if ack := p.acks.GetAckFrame(encLevel, true); ack != nil { return p.getLongHeader(encLevel, v), payload{ ack: ack, length: ack.Length(v), } } return nil, payload{} } var s *cryptoStream var handler ackhandler.FrameHandler var hasRetransmission bool //nolint:exhaustive // Initial and Handshake are the only two encryption levels here. switch encLevel { case protocol.EncryptionInitial: s = p.initialStream handler = p.retransmissionQueue.InitialAckHandler() hasRetransmission = p.retransmissionQueue.HasInitialData() case protocol.EncryptionHandshake: s = p.handshakeStream handler = p.retransmissionQueue.HandshakeAckHandler() hasRetransmission = p.retransmissionQueue.HasHandshakeData() } hasData := s.HasData() var ack *wire.AckFrame if ackAllowed { ack = p.acks.GetAckFrame(encLevel, !hasRetransmission && !hasData) } if !hasData && !hasRetransmission && ack == nil { // nothing to send return nil, payload{} } var pl payload if ack != nil { pl.ack = ack pl.length = ack.Length(v) maxPacketSize -= pl.length } hdr := p.getLongHeader(encLevel, v) maxPacketSize -= hdr.GetLength(v) if hasRetransmission { for { var f ackhandler.Frame //nolint:exhaustive // 0-RTT packets can't contain any retransmission.s switch encLevel { case protocol.EncryptionInitial: f.Frame = p.retransmissionQueue.GetInitialFrame(maxPacketSize, v) f.Handler = p.retransmissionQueue.InitialAckHandler() case protocol.EncryptionHandshake: f.Frame = p.retransmissionQueue.GetHandshakeFrame(maxPacketSize, v) f.Handler = p.retransmissionQueue.HandshakeAckHandler() } if f.Frame == nil { break } pl.frames = append(pl.frames, f) frameLen := f.Frame.Length(v) pl.length += frameLen maxPacketSize -= frameLen } } else if s.HasData() { cf := s.PopCryptoFrame(maxPacketSize) pl.frames = []ackhandler.Frame{{Frame: cf, Handler: handler}} pl.length += cf.Length(v) } return hdr, pl } func (p *packetPacker) maybeGetAppDataPacketFor0RTT(sealer sealer, maxPacketSize protocol.ByteCount, v protocol.Version) (*wire.ExtendedHeader, payload) { if p.perspective != protocol.PerspectiveClient { return nil, payload{} } hdr := p.getLongHeader(protocol.Encryption0RTT, v) maxPayloadSize := maxPacketSize - hdr.GetLength(v) - protocol.ByteCount(sealer.Overhead()) return hdr, p.maybeGetAppDataPacket(maxPayloadSize, false, false, v) } func (p *packetPacker) maybeGetShortHeaderPacket(sealer handshake.ShortHeaderSealer, hdrLen protocol.ByteCount, maxPacketSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.Version) payload { maxPayloadSize := maxPacketSize - hdrLen - protocol.ByteCount(sealer.Overhead()) return p.maybeGetAppDataPacket(maxPayloadSize, onlyAck, ackAllowed, v) } func (p *packetPacker) maybeGetAppDataPacket(maxPayloadSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.Version) payload { pl := p.composeNextPacket(maxPayloadSize, onlyAck, ackAllowed, v) // check if we have anything to send if len(pl.frames) == 0 && len(pl.streamFrames) == 0 { if pl.ack == nil { return payload{} } // the packet only contains an ACK if p.numNonAckElicitingAcks >= protocol.MaxNonAckElicitingAcks { ping := &wire.PingFrame{} pl.frames = append(pl.frames, ackhandler.Frame{Frame: ping}) pl.length += ping.Length(v) p.numNonAckElicitingAcks = 0 } else { p.numNonAckElicitingAcks++ } } else { p.numNonAckElicitingAcks = 0 } return pl } func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, onlyAck, ackAllowed bool, v protocol.Version) payload { if onlyAck { if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, true); ack != nil { return payload{ack: ack, length: ack.Length(v)} } return payload{} } hasData := p.framer.HasData() hasRetransmission := p.retransmissionQueue.HasAppData() var hasAck bool var pl payload if ackAllowed { if ack := p.acks.GetAckFrame(protocol.Encryption1RTT, !hasRetransmission && !hasData); ack != nil { pl.ack = ack pl.length += ack.Length(v) hasAck = true } } if p.datagramQueue != nil { if f := p.datagramQueue.Peek(); f != nil { size := f.Length(v) if size <= maxFrameSize-pl.length { // DATAGRAM frame fits pl.frames = append(pl.frames, ackhandler.Frame{Frame: f}) pl.length += size p.datagramQueue.Pop() } else if !hasAck { // The DATAGRAM frame doesn't fit, and the packet doesn't contain an ACK. // Discard this frame. There's no point in retrying this in the next packet, // as it's unlikely that the available packet size will increase. p.datagramQueue.Pop() } // If the DATAGRAM frame was too large and the packet contained an ACK, we'll try to send it out later. } } if hasAck && !hasData && !hasRetransmission { return pl } if hasRetransmission { for { remainingLen := maxFrameSize - pl.length if remainingLen < protocol.MinStreamFrameSize { break } f := p.retransmissionQueue.GetAppDataFrame(remainingLen, v) if f == nil { break } pl.frames = append(pl.frames, ackhandler.Frame{Frame: f, Handler: p.retransmissionQueue.AppDataAckHandler()}) pl.length += f.Length(v) } } if hasData { var lengthAdded protocol.ByteCount startLen := len(pl.frames) pl.frames, lengthAdded = p.framer.AppendControlFrames(pl.frames, maxFrameSize-pl.length, v) pl.length += lengthAdded // add handlers for the control frames that were added for i := startLen; i < len(pl.frames); i++ { if pl.frames[i].Handler != nil { continue } switch pl.frames[i].Frame.(type) { case *wire.PathChallengeFrame, *wire.PathResponseFrame: // Path probing is currently not supported, therefore we don't need to set the OnAcked callback yet. // PATH_CHALLENGE and PATH_RESPONSE are never retransmitted. default: pl.frames[i].Handler = p.retransmissionQueue.AppDataAckHandler() } } pl.streamFrames, lengthAdded = p.framer.AppendStreamFrames(pl.streamFrames, maxFrameSize-pl.length, v) pl.length += lengthAdded } return pl } func (p *packetPacker) MaybePackProbePacket(encLevel protocol.EncryptionLevel, maxPacketSize protocol.ByteCount, v protocol.Version) (*coalescedPacket, error) { if encLevel == protocol.Encryption1RTT { s, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return nil, err } kp := s.KeyPhase() connID := p.getDestConnID() pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) hdrLen := wire.ShortHeaderLen(connID, pnLen) pl := p.maybeGetAppDataPacket(maxPacketSize-protocol.ByteCount(s.Overhead())-hdrLen, false, true, v) if pl.length == 0 { return nil, nil } buffer := getPacketBuffer() packet := &coalescedPacket{buffer: buffer} shp, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, 0, maxPacketSize, s, false, v) if err != nil { return nil, err } packet.shortHdrPacket = &shp return packet, nil } var hdr *wire.ExtendedHeader var pl payload var sealer handshake.LongHeaderSealer //nolint:exhaustive // Probe packets are never sent for 0-RTT. switch encLevel { case protocol.EncryptionInitial: var err error sealer, err = p.cryptoSetup.GetInitialSealer() if err != nil { return nil, err } hdr, pl = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionInitial, false, true, v) case protocol.EncryptionHandshake: var err error sealer, err = p.cryptoSetup.GetHandshakeSealer() if err != nil { return nil, err } hdr, pl = p.maybeGetCryptoPacket(maxPacketSize-protocol.ByteCount(sealer.Overhead()), protocol.EncryptionHandshake, false, true, v) default: panic("unknown encryption level") } if pl.length == 0 { return nil, nil } buffer := getPacketBuffer() packet := &coalescedPacket{buffer: buffer} size := p.longHeaderPacketLength(hdr, pl, v) + protocol.ByteCount(sealer.Overhead()) var padding protocol.ByteCount if encLevel == protocol.EncryptionInitial { padding = p.initialPaddingLen(pl.frames, size, maxPacketSize) } longHdrPacket, err := p.appendLongHeaderPacket(buffer, hdr, pl, padding, encLevel, sealer, v) if err != nil { return nil, err } packet.longHdrPackets = []*longHeaderPacket{longHdrPacket} return packet, nil } func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount, v protocol.Version) (shortHeaderPacket, *packetBuffer, error) { pl := payload{ frames: []ackhandler.Frame{ping}, length: ping.Frame.Length(v), } buffer := getPacketBuffer() s, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return shortHeaderPacket{}, nil, err } connID := p.getDestConnID() pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) padding := size - p.shortHeaderPacketLength(connID, pnLen, pl) - protocol.ByteCount(s.Overhead()) kp := s.KeyPhase() packet, err := p.appendShortHeaderPacket(buffer, connID, pn, pnLen, kp, pl, padding, size, s, true, v) return packet, buffer, err } func (p *packetPacker) getLongHeader(encLevel protocol.EncryptionLevel, v protocol.Version) *wire.ExtendedHeader { pn, pnLen := p.pnManager.PeekPacketNumber(encLevel) hdr := &wire.ExtendedHeader{ PacketNumber: pn, PacketNumberLen: pnLen, } hdr.Version = v hdr.SrcConnectionID = p.srcConnID hdr.DestConnectionID = p.getDestConnID() //nolint:exhaustive // 1-RTT packets are not long header packets. switch encLevel { case protocol.EncryptionInitial: hdr.Type = protocol.PacketTypeInitial hdr.Token = p.token case protocol.EncryptionHandshake: hdr.Type = protocol.PacketTypeHandshake case protocol.Encryption0RTT: hdr.Type = protocol.PacketType0RTT } return hdr } func (p *packetPacker) appendLongHeaderPacket(buffer *packetBuffer, header *wire.ExtendedHeader, pl payload, padding protocol.ByteCount, encLevel protocol.EncryptionLevel, sealer sealer, v protocol.Version) (*longHeaderPacket, error) { var paddingLen protocol.ByteCount pnLen := protocol.ByteCount(header.PacketNumberLen) if pl.length < 4-pnLen { paddingLen = 4 - pnLen - pl.length } paddingLen += padding header.Length = pnLen + protocol.ByteCount(sealer.Overhead()) + pl.length + paddingLen startLen := len(buffer.Data) raw := buffer.Data[startLen:] raw, err := header.Append(raw, v) if err != nil { return nil, err } payloadOffset := protocol.ByteCount(len(raw)) raw, err = p.appendPacketPayload(raw, pl, paddingLen, v) if err != nil { return nil, err } raw = p.encryptPacket(raw, sealer, header.PacketNumber, payloadOffset, pnLen) buffer.Data = buffer.Data[:len(buffer.Data)+len(raw)] if pn := p.pnManager.PopPacketNumber(encLevel); pn != header.PacketNumber { return nil, fmt.Errorf("packetPacker BUG: Peeked and Popped packet numbers do not match: expected %d, got %d", pn, header.PacketNumber) } return &longHeaderPacket{ header: header, ack: pl.ack, frames: pl.frames, streamFrames: pl.streamFrames, length: protocol.ByteCount(len(raw)), }, nil } func (p *packetPacker) appendShortHeaderPacket( buffer *packetBuffer, connID protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit, pl payload, padding, maxPacketSize protocol.ByteCount, sealer sealer, isMTUProbePacket bool, v protocol.Version, ) (shortHeaderPacket, error) { var paddingLen protocol.ByteCount if pl.length < 4-protocol.ByteCount(pnLen) { paddingLen = 4 - protocol.ByteCount(pnLen) - pl.length } paddingLen += padding startLen := len(buffer.Data) raw := buffer.Data[startLen:] raw, err := wire.AppendShortHeader(raw, connID, pn, pnLen, kp) if err != nil { return shortHeaderPacket{}, err } payloadOffset := protocol.ByteCount(len(raw)) raw, err = p.appendPacketPayload(raw, pl, paddingLen, v) if err != nil { return shortHeaderPacket{}, err } if !isMTUProbePacket { if size := protocol.ByteCount(len(raw) + sealer.Overhead()); size > maxPacketSize { return shortHeaderPacket{}, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, maxPacketSize) } } raw = p.encryptPacket(raw, sealer, pn, payloadOffset, protocol.ByteCount(pnLen)) buffer.Data = buffer.Data[:len(buffer.Data)+len(raw)] if newPN := p.pnManager.PopPacketNumber(protocol.Encryption1RTT); newPN != pn { return shortHeaderPacket{}, fmt.Errorf("packetPacker BUG: Peeked and Popped packet numbers do not match: expected %d, got %d", pn, newPN) } return shortHeaderPacket{ PacketNumber: pn, PacketNumberLen: pnLen, KeyPhase: kp, StreamFrames: pl.streamFrames, Frames: pl.frames, Ack: pl.ack, Length: protocol.ByteCount(len(raw)), DestConnID: connID, IsPathMTUProbePacket: isMTUProbePacket, }, nil } // appendPacketPayload serializes the payload of a packet into the raw byte slice. // It modifies the order of payload.frames. func (p *packetPacker) appendPacketPayload(raw []byte, pl payload, paddingLen protocol.ByteCount, v protocol.Version) ([]byte, error) { payloadOffset := len(raw) if pl.ack != nil { var err error raw, err = pl.ack.Append(raw, v) if err != nil { return nil, err } } if paddingLen > 0 { raw = append(raw, make([]byte, paddingLen)...) } // Randomize the order of the control frames. // This makes sure that the receiver doesn't rely on the order in which frames are packed. if len(pl.frames) > 1 { p.rand.Shuffle(len(pl.frames), func(i, j int) { pl.frames[i], pl.frames[j] = pl.frames[j], pl.frames[i] }) } for _, f := range pl.frames { var err error raw, err = f.Frame.Append(raw, v) if err != nil { return nil, err } } for _, f := range pl.streamFrames { var err error raw, err = f.Frame.Append(raw, v) if err != nil { return nil, err } } if payloadSize := protocol.ByteCount(len(raw)-payloadOffset) - paddingLen; payloadSize != pl.length { return nil, fmt.Errorf("PacketPacker BUG: payload size inconsistent (expected %d, got %d bytes)", pl.length, payloadSize) } return raw, nil } func (p *packetPacker) encryptPacket(raw []byte, sealer sealer, pn protocol.PacketNumber, payloadOffset, pnLen protocol.ByteCount) []byte { _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], pn, raw[:payloadOffset]) raw = raw[:len(raw)+sealer.Overhead()] // apply header protection pnOffset := payloadOffset - pnLen sealer.EncryptHeader(raw[pnOffset+4:pnOffset+4+16], &raw[0], raw[pnOffset:payloadOffset]) return raw } func (p *packetPacker) SetToken(token []byte) { p.token = token } golang-github-lucas-clemente-quic-go-0.46.0/packet_packer_test.go000066400000000000000000002357171465664453100250100ustar00rootroot00000000000000package quic import ( "bytes" "errors" "fmt" "time" "golang.org/x/exp/rand" "github.com/quic-go/quic-go/internal/ackhandler" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/mocks" mockackhandler "github.com/quic-go/quic-go/internal/mocks/ackhandler" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Packet packer", func() { const maxPacketSize protocol.ByteCount = 1357 var ( packer *packetPacker retransmissionQueue *retransmissionQueue datagramQueue *datagramQueue framer *MockFrameSource ackFramer *MockAckFrameSource initialStream *cryptoStream handshakeStream *cryptoStream sealingManager *MockSealingManager pnManager *mockackhandler.MockSentPacketHandler ) connID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}) parsePacket := func(data []byte) (hdrs []*wire.ExtendedHeader, more []byte) { for len(data) > 0 { if !wire.IsLongHeaderPacket(data[0]) { break } hdr, _, more, err := wire.ParsePacket(data) Expect(err).ToNot(HaveOccurred()) extHdr, err := hdr.ParseExtended(data) Expect(err).ToNot(HaveOccurred()) // ExpectWithOffset(1, extHdr.Length).To(BeEquivalentTo(r.Len() - len(more) + int(extHdr.PacketNumberLen))) ExpectWithOffset(1, extHdr.Length+protocol.ByteCount(extHdr.PacketNumberLen)).To(BeNumerically(">=", 4)) data = more hdrs = append(hdrs, extHdr) } return hdrs, data } parseShortHeaderPacket := func(data []byte) { l, _, pnLen, _, err := wire.ParseShortHeader(data, connID.Len()) ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, len(data)-l+int(pnLen)).To(BeNumerically(">=", 4)) } expectAppendStreamFrames := func(frames ...ackhandler.StreamFrame) { framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(fs []ackhandler.StreamFrame, _ protocol.ByteCount, v protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) { var length protocol.ByteCount for _, f := range frames { length += f.Frame.Length(v) } return append(fs, frames...), length }) } expectAppendControlFrames := func(frames ...ackhandler.Frame) { framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(fs []ackhandler.Frame, _ protocol.ByteCount, v protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) { var length protocol.ByteCount for _, f := range frames { length += f.Frame.Length(v) } return append(fs, frames...), length }) } BeforeEach(func() { rand.Seed(uint64(GinkgoRandomSeed())) retransmissionQueue = newRetransmissionQueue() mockSender := NewMockStreamSender(mockCtrl) mockSender.EXPECT().onHasStreamData(gomock.Any(), gomock.Any()).AnyTimes() initialStream = newCryptoStream() handshakeStream = newCryptoStream() framer = NewMockFrameSource(mockCtrl) ackFramer = NewMockAckFrameSource(mockCtrl) sealingManager = NewMockSealingManager(mockCtrl) pnManager = mockackhandler.NewMockSentPacketHandler(mockCtrl) datagramQueue = newDatagramQueue(func() {}, utils.DefaultLogger) packer = newPacketPacker(protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), func() protocol.ConnectionID { return connID }, initialStream, handshakeStream, pnManager, retransmissionQueue, sealingManager, framer, ackFramer, datagramQueue, protocol.PerspectiveServer) }) Context("generating a packet header", func() { It("uses the Long Header format", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen3) h := packer.getLongHeader(protocol.EncryptionHandshake, protocol.Version1) Expect(h.PacketNumber).To(Equal(protocol.PacketNumber(0x42))) Expect(h.PacketNumberLen).To(Equal(protocol.PacketNumberLen3)) Expect(h.Version).To(Equal(protocol.Version1)) }) It("sets source and destination connection ID", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) srcConnID := protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}) destConnID := protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}) packer.srcConnID = srcConnID packer.getDestConnID = func() protocol.ConnectionID { return destConnID } h := packer.getLongHeader(protocol.EncryptionHandshake, protocol.Version1) Expect(h.SrcConnectionID).To(Equal(srcConnID)) Expect(h.DestConnectionID).To(Equal(destConnID)) }) }) Context("encrypting packets", func() { It("encrypts a packet", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x1337)) sealer := mocks.NewMockShortHeaderSealer(mockCtrl) sealer.EXPECT().Overhead().Return(4).AnyTimes() var hdrRaw []byte gomock.InOrder( sealer.EXPECT().KeyPhase().Return(protocol.KeyPhaseOne), sealer.EXPECT().Seal(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x1337), gomock.Any()).DoAndReturn(func(_, src []byte, _ protocol.PacketNumber, aad []byte) []byte { hdrRaw = append([]byte{}, aad...) return append(src, []byte{0xde, 0xca, 0xfb, 0xad}...) }), sealer.EXPECT().EncryptHeader(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(sample []byte, firstByte *byte, pnBytes []byte) { Expect(firstByte).To(Equal(&hdrRaw[0])) Expect(pnBytes).To(Equal(hdrRaw[len(hdrRaw)-2:])) *firstByte ^= 0xff // invert the first byte // invert the packet number bytes for i := range pnBytes { pnBytes[i] ^= 0xff } }), ) framer.EXPECT().HasData().Return(true) sealingManager.EXPECT().GetInitialSealer().Return(nil, nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, nil) sealingManager.EXPECT().Get1RTTSealer().Return(sealer, nil) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) expectAppendControlFrames() f := &wire.StreamFrame{Data: []byte{0xde, 0xca, 0xfb, 0xad}} expectAppendStreamFrames(ackhandler.StreamFrame{Frame: f}) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(BeEmpty()) Expect(p.shortHdrPacket).ToNot(BeNil()) Expect(p.shortHdrPacket.Frames).To(BeEmpty()) Expect(p.shortHdrPacket.StreamFrames).To(HaveLen(1)) Expect(p.shortHdrPacket.StreamFrames[0].Frame).To(Equal(f)) hdrRawEncrypted := append([]byte{}, hdrRaw...) hdrRawEncrypted[0] ^= 0xff hdrRawEncrypted[len(hdrRaw)-2] ^= 0xff hdrRawEncrypted[len(hdrRaw)-1] ^= 0xff Expect(p.buffer.Data[0:len(hdrRaw)]).To(Equal(hdrRawEncrypted)) Expect(p.buffer.Data[p.buffer.Len()-4:]).To(Equal([]byte{0xde, 0xca, 0xfb, 0xad})) }) }) Context("packing packets", func() { // getSealer gets a sealer that's expected to seal exactly one packet getSealer := func() *mocks.MockShortHeaderSealer { sealer := mocks.NewMockShortHeaderSealer(mockCtrl) sealer.EXPECT().KeyPhase().Return(protocol.KeyPhaseOne).AnyTimes() sealer.EXPECT().Overhead().Return(7).AnyTimes() sealer.EXPECT().EncryptHeader(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sealer.EXPECT().Seal(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(dst, src []byte, pn protocol.PacketNumber, associatedData []byte) []byte { return append(src, bytes.Repeat([]byte{'s'}, sealer.Overhead())...) }).AnyTimes() return sealer } Context("packing ACK packets", func() { It("doesn't pack a packet if there's no ACK to send", func() { sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true) p, err := packer.PackCoalescedPacket(true, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).To(BeNil()) }) It("packs Initial ACK-only packets, and pads them (for the client)", func() { packer.perspective = protocol.PerspectiveClient pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}} ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true).Return(ack) p, err := packer.PackCoalescedPacket(true, maxPacketSize, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].ack).To(Equal(ack)) Expect(p.longHdrPackets[0].frames).To(BeEmpty()) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) parsePacket(p.buffer.Data) }) It("packs Initial ACK-only packets, and doesn't pads them (for the server)", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}} ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true).Return(ack) p, err := packer.PackCoalescedPacket(true, maxPacketSize, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].ack).To(Equal(ack)) Expect(p.longHdrPackets[0].frames).To(BeEmpty()) Expect(p.buffer.Len()).To(BeNumerically("<", 100)) parsePacket(p.buffer.Data) }) It("packs 1-RTT ACK-only packets, before handshake confirmation", func() { sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}} ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true).Return(ack) p, err := packer.PackCoalescedPacket(true, maxPacketSize, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(BeEmpty()) Expect(p.shortHdrPacket).ToNot(BeNil()) Expect(p.shortHdrPacket.Ack).To(Equal(ack)) Expect(p.shortHdrPacket.Frames).To(BeEmpty()) parsePacket(p.buffer.Data) }) It("packs 1-RTT ACK-only packets, after handshake confirmation", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}} ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true).Return(ack) p, buffer, err := packer.PackAckOnlyPacket(maxPacketSize, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.Ack).To(Equal(ack)) Expect(p.Frames).To(BeEmpty()) parsePacket(buffer.Data) }) }) Context("packing 0-RTT packets", func() { BeforeEach(func() { packer.perspective = protocol.PerspectiveClient sealingManager.EXPECT().GetInitialSealer().Return(nil, nil).AnyTimes() sealingManager.EXPECT().GetHandshakeSealer().Return(nil, nil).AnyTimes() sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable).AnyTimes() ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true).AnyTimes() ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, true).AnyTimes() ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true).AnyTimes() }) It("packs a 0-RTT packet", func() { sealingManager.EXPECT().Get0RTTSealer().Return(getSealer(), nil).AnyTimes() pnManager.EXPECT().PeekPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(0x42)) cf := ackhandler.Frame{Frame: &wire.MaxDataFrame{MaximumData: 0x1337}} framer.EXPECT().HasData().Return(true) expectAppendControlFrames(cf) // TODO: check sizes framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any(), protocol.Version1).DoAndReturn(func(frames []ackhandler.StreamFrame, _ protocol.ByteCount, _ protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) { return frames, 0 }) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(p).ToNot(BeNil()) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].header.Type).To(Equal(protocol.PacketType0RTT)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.Encryption0RTT)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(Equal(cf.Frame)) Expect(p.longHdrPackets[0].frames[0].Handler).ToNot(BeNil()) }) It("doesn't add an ACK-only 0-RTT packet", func() { // ACK frames cannot be sent in 0-RTT packets p, err := packer.PackCoalescedPacket(true, protocol.MaxByteCount, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).To(BeNil()) }) }) Context("packing CONNECTION_CLOSE", func() { It("clears the reason phrase for crypto errors", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) quicErr := qerr.NewLocalCryptoError(0x42, errors.New("crypto error")) quicErr.FrameType = 0x1234 p, err := packer.PackConnectionClose(quicErr, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].header.Type).To(Equal(protocol.PacketTypeHandshake)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.longHdrPackets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(0x100 + 0x42)) Expect(ccf.FrameType).To(BeEquivalentTo(0x1234)) Expect(ccf.ReasonPhrase).To(BeEmpty()) }) It("packs a CONNECTION_CLOSE in 1-RTT", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) // expect no framer.PopStreamFrames p, err := packer.PackConnectionClose(&qerr.TransportError{ ErrorCode: qerr.CryptoBufferExceeded, ErrorMessage: "test error", }, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(BeEmpty()) Expect(p.shortHdrPacket.Frames).To(HaveLen(1)) Expect(p.shortHdrPacket.Frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.shortHdrPacket.Frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.CryptoBufferExceeded)) Expect(ccf.ReasonPhrase).To(Equal("test error")) }) It("packs a CONNECTION_CLOSE in all available encryption levels, and replaces application errors in Initial and Handshake", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1)) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(2)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(3), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(3)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) p, err := packer.PackApplicationClose(&qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: "test error", }, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(2)) Expect(p.longHdrPackets[0].header.Type).To(Equal(protocol.PacketTypeInitial)) Expect(p.longHdrPackets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.longHdrPackets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.ApplicationErrorErrorCode)) Expect(ccf.ReasonPhrase).To(BeEmpty()) Expect(p.longHdrPackets[1].header.Type).To(Equal(protocol.PacketTypeHandshake)) Expect(p.longHdrPackets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) Expect(p.longHdrPackets[1].frames).To(HaveLen(1)) Expect(p.longHdrPackets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.longHdrPackets[1].frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.ApplicationErrorErrorCode)) Expect(ccf.ReasonPhrase).To(BeEmpty()) Expect(p.shortHdrPacket).ToNot(BeNil()) Expect(p.shortHdrPacket.PacketNumber).To(Equal(protocol.PacketNumber(3))) Expect(p.shortHdrPacket.Frames).To(HaveLen(1)) Expect(p.shortHdrPacket.Frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.shortHdrPacket.Frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeTrue()) Expect(ccf.ErrorCode).To(BeEquivalentTo(0x1337)) Expect(ccf.ReasonPhrase).To(Equal("test error")) }) It("packs a CONNECTION_CLOSE in all available encryption levels, as a client", func() { packer.perspective = protocol.PerspectiveClient pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(1)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(2)) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get0RTTSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) p, err := packer.PackApplicationClose(&qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: "test error", }, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.buffer.Len()).To(BeNumerically("<", protocol.MinInitialPacketSize)) Expect(p.longHdrPackets[0].header.Type).To(Equal(protocol.PacketTypeHandshake)) Expect(p.longHdrPackets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.longHdrPackets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.ApplicationErrorErrorCode)) Expect(ccf.ReasonPhrase).To(BeEmpty()) Expect(p.shortHdrPacket).ToNot(BeNil()) Expect(p.shortHdrPacket.PacketNumber).To(Equal(protocol.PacketNumber(2))) Expect(p.shortHdrPacket.Frames).To(HaveLen(1)) Expect(p.shortHdrPacket.Frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.shortHdrPacket.Frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeTrue()) Expect(ccf.ErrorCode).To(BeEquivalentTo(0x1337)) Expect(ccf.ReasonPhrase).To(Equal("test error")) }) It("packs a CONNECTION_CLOSE in all available encryption levels and pads, as a client", func() { packer.perspective = protocol.PerspectiveClient pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(1)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(2), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(2)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get0RTTSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) p, err := packer.PackApplicationClose(&qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: "test error", }, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(2)) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets[0].header.Type).To(Equal(protocol.PacketTypeInitial)) Expect(p.longHdrPackets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.longHdrPackets[0].frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.ApplicationErrorErrorCode)) Expect(ccf.ReasonPhrase).To(BeEmpty()) Expect(p.longHdrPackets[1].header.Type).To(Equal(protocol.PacketType0RTT)) Expect(p.longHdrPackets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) Expect(p.longHdrPackets[1].frames).To(HaveLen(1)) Expect(p.longHdrPackets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.longHdrPackets[1].frames[0].Frame.(*wire.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeTrue()) Expect(ccf.ErrorCode).To(BeEquivalentTo(0x1337)) Expect(ccf.ReasonPhrase).To(Equal("test error")) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(2)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdrs[1].Type).To(Equal(protocol.PacketType0RTT)) Expect(more).To(BeEmpty()) }) }) Context("packing normal packets", func() { It("returns nil when no packet is queued", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) // don't expect any calls to PopPacketNumber sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true) framer.EXPECT().HasData() _, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).To(MatchError(errNothingToPack)) }) It("appends single packets", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) expectAppendControlFrames() f := &wire.StreamFrame{ StreamID: 5, Data: []byte{0xde, 0xca, 0xfb, 0xad}, } expectAppendStreamFrames(ackhandler.StreamFrame{Frame: f}) buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("foobar")...) p, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) b, err := f.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Frames).To(BeEmpty()) Expect(p.StreamFrames).To(HaveLen(1)) Expect(p.StreamFrames[0].Frame.StreamID).To(Equal(f.StreamID)) Expect(buffer.Data[:6]).To(Equal([]byte("foobar"))) // make sure the packet was actually appended Expect(buffer.Data).To(ContainSubstring(string(b))) }) It("packs a single ACK", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 42, Smallest: 1}}} framer.EXPECT().HasData() ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true).Return(ack) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) p, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).NotTo(HaveOccurred()) Expect(p.Ack).To(Equal(ack)) }) It("packs control frames, and sets OnLost / OnAcked handlers", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) frames := []ackhandler.Frame{ {Frame: &wire.ResetStreamFrame{}, Handler: &mtuFinderAckHandler{}}, // set any non-nil ackhandler.FrameHandler {Frame: &wire.MaxDataFrame{}}, } expectAppendControlFrames(frames...) expectAppendStreamFrames() buffer := getPacketBuffer() p, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Frames).To(HaveLen(2)) var sawResetStream, sawMaxData bool for _, frame := range p.Frames { switch frame.Frame.(type) { case *wire.ResetStreamFrame: sawResetStream = true Expect(frame.Handler).To(Equal(&mtuFinderAckHandler{})) case *wire.MaxDataFrame: sawMaxData = true Expect(frame.Handler).ToNot(BeNil()) Expect(frame.Handler).ToNot(Equal(&mtuFinderAckHandler{})) } } Expect(sawResetStream).To(BeTrue()) Expect(sawMaxData).To(BeTrue()) Expect(buffer.Len()).ToNot(BeZero()) }) It("packs PATH_CHALLENGE and PATH_RESPONSE frames", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) frames := []ackhandler.Frame{ {Frame: &wire.PathChallengeFrame{}}, {Frame: &wire.PathResponseFrame{}}, {Frame: &wire.DataBlockedFrame{}}, } expectAppendControlFrames(frames...) expectAppendStreamFrames() buffer := getPacketBuffer() p, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Frames).To(HaveLen(3)) for i, f := range p.Frames { Expect(f).To(BeAssignableToTypeOf(frames[i])) switch f.Frame.(type) { case *wire.PathChallengeFrame, *wire.PathResponseFrame: // This means that the frame won't be retransmitted. Expect(f.Handler).To(BeNil()) default: Expect(f.Handler).ToNot(BeNil()) } } Expect(buffer.Len()).ToNot(BeZero()) }) It("packs DATAGRAM frames", func() { ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) f := &wire.DatagramFrame{ DataLenPresent: true, Data: []byte("foobar"), } done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) datagramQueue.Add(f) }() // make sure the DATAGRAM has actually been queued time.Sleep(scaleDuration(20 * time.Millisecond)) framer.EXPECT().HasData() buffer := getPacketBuffer() p, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Frames).To(HaveLen(1)) Expect(p.Frames[0].Frame).To(Equal(f)) Expect(buffer.Data).ToNot(BeEmpty()) Eventually(done).Should(BeClosed()) }) It("doesn't pack a DATAGRAM frame if the ACK frame is too large", func() { ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true).Return(&wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 100}}}) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) f := &wire.DatagramFrame{ DataLenPresent: true, Data: make([]byte, maxPacketSize-10), } done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) datagramQueue.Add(f) }() // make sure the DATAGRAM has actually been queued time.Sleep(scaleDuration(20 * time.Millisecond)) framer.EXPECT().HasData() buffer := getPacketBuffer() p, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Ack).ToNot(BeNil()) Expect(p.Frames).To(BeEmpty()) Expect(buffer.Data).ToNot(BeEmpty()) Expect(datagramQueue.Peek()).To(Equal(f)) // make sure the frame is still there datagramQueue.CloseWithError(nil) Eventually(done).Should(BeClosed()) }) It("discards a DATAGRAM frame if it doesn't fit into a packet that doesn't contain an ACK", func() { ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) f := &wire.DatagramFrame{ DataLenPresent: true, Data: make([]byte, maxPacketSize+10), // won't fit } done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) datagramQueue.Add(f) }() // make sure the DATAGRAM has actually been queued time.Sleep(scaleDuration(20 * time.Millisecond)) framer.EXPECT().HasData() buffer := getPacketBuffer() p, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).To(MatchError(errNothingToPack)) Expect(p.Frames).To(BeEmpty()) Expect(p.Ack).To(BeNil()) Expect(datagramQueue.Peek()).To(BeNil()) Eventually(done).Should(BeClosed()) }) It("accounts for the space consumed by control frames", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) var maxSize protocol.ByteCount gomock.InOrder( framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any(), protocol.Version1).DoAndReturn(func(fs []ackhandler.Frame, maxLen protocol.ByteCount, _ protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) { maxSize = maxLen return fs, 444 }), framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any(), protocol.Version1).Do(func(fs []ackhandler.StreamFrame, maxLen protocol.ByteCount, _ protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) { Expect(maxLen).To(Equal(maxSize - 444)) return fs, 0 }), ) _, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).To(MatchError(errNothingToPack)) }) It("pads if payload length + packet number length is smaller than 4, for Long Header packets", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealer := getSealer() sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(sealer, nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) packer.retransmissionQueue.addHandshake(&wire.PingFrame{}) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) packet, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.longHdrPackets).To(HaveLen(1)) Expect(packet.IsOnlyShortHeaderPacket()).To(BeFalse()) // cut off the tag that the mock sealer added // packet.buffer.Data = packet.buffer.Data[:packet.buffer.Len()-protocol.ByteCount(sealer.Overhead())] hdr, _, _, err := wire.ParsePacket(packet.buffer.Data) Expect(err).ToNot(HaveOccurred()) data := packet.buffer.Data extHdr, err := hdr.ParseExtended(data) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen1)) data = data[extHdr.ParsedLen():] Expect(data).To(HaveLen(4 - 1 /* packet number length */ + sealer.Overhead())) // the first bytes of the payload should be a 2 PADDING frames... Expect(data[0]).To(Equal(byte(0))) Expect(data[1]).To(Equal(byte(0))) data = data[2:] // ... followed by the PING frameParser := wire.NewFrameParser(false) l, frame, err := frameParser.ParseNext(data, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(len(data) - l).To(Equal(sealer.Overhead())) }) It("pads if payload length + packet number length is smaller than 4", func() { f := &wire.StreamFrame{ StreamID: 0x10, // small stream ID, such that only a single byte is consumed Fin: true, } Expect(f.Length(protocol.Version1)).To(BeEquivalentTo(2)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealer := getSealer() sealingManager.EXPECT().Get1RTTSealer().Return(sealer, nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.StreamFrame{Frame: f}) buffer := getPacketBuffer() _, err := packer.AppendPacket(buffer, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) // cut off the tag that the mock sealer added buffer.Data = buffer.Data[:buffer.Len()-protocol.ByteCount(sealer.Overhead())] data := buffer.Data l, _, pnLen, _, err := wire.ParseShortHeader(data, connID.Len()) Expect(err).ToNot(HaveOccurred()) r := bytes.NewReader(data[l:]) Expect(pnLen).To(Equal(protocol.PacketNumberLen1)) Expect(r.Len()).To(Equal(4 - 1 /* packet number length */)) // the first byte of the payload should be a PADDING frame... firstPayloadByte, err := r.ReadByte() Expect(err).ToNot(HaveOccurred()) Expect(firstPayloadByte).To(Equal(byte(0))) // ... followed by the STREAM frame frameParser := wire.NewFrameParser(true) l, frame, err := frameParser.ParseNext(buffer.Data[len(data)-r.Len():], protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&wire.StreamFrame{})) sf := frame.(*wire.StreamFrame) Expect(sf.StreamID).To(Equal(f.StreamID)) Expect(sf.Fin).To(Equal(f.Fin)) Expect(sf.Data).To(BeEmpty()) Expect(r.Len() - l).To(BeZero()) }) It("packs multiple small STREAM frames into single packet", func() { f1 := &wire.StreamFrame{ StreamID: 5, Data: []byte("frame 1"), DataLenPresent: true, } f2 := &wire.StreamFrame{ StreamID: 5, Data: []byte("frame 2"), DataLenPresent: true, } f3 := &wire.StreamFrame{ StreamID: 3, Data: []byte("frame 3"), DataLenPresent: true, } pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.StreamFrame{Frame: f1}, ackhandler.StreamFrame{Frame: f2}, ackhandler.StreamFrame{Frame: f3}) p, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Frames).To(BeEmpty()) Expect(p.StreamFrames).To(HaveLen(3)) Expect(p.StreamFrames[0].Frame.Data).To(Equal([]byte("frame 1"))) Expect(p.StreamFrames[1].Frame.Data).To(Equal([]byte("frame 2"))) Expect(p.StreamFrames[2].Frame.Data).To(Equal([]byte("frame 3"))) }) Context("making ACK packets ack-eliciting", func() { sendMaxNumNonAckElicitingAcks := func() { for i := 0; i < protocol.MaxNonAckElicitingAcks; i++ { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Return(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}) expectAppendControlFrames() expectAppendStreamFrames() p, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Ack).ToNot(BeNil()) Expect(p.Frames).To(BeEmpty()) } } It("adds a PING frame when it's supposed to send a ack-eliciting packet", func() { sendMaxNumNonAckElicitingAcks() pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Return(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}) expectAppendControlFrames() expectAppendStreamFrames() p, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) var hasPing bool for _, f := range p.Frames { if _, ok := f.Frame.(*wire.PingFrame); ok { hasPing = true Expect(f.Handler).To(BeNil()) // make sure the PING is not retransmitted if lost } } Expect(hasPing).To(BeTrue()) // make sure the next packet doesn't contain another PING pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Return(&wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}) expectAppendControlFrames() expectAppendStreamFrames() p, err = packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Ack).ToNot(BeNil()) Expect(p.Frames).To(BeEmpty()) }) It("waits until there's something to send before adding a PING frame", func() { sendMaxNumNonAckElicitingAcks() // nothing to send pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) expectAppendControlFrames() expectAppendStreamFrames() ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) _, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).To(MatchError(errNothingToPack)) // now add some frame to send expectAppendControlFrames() expectAppendStreamFrames() pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}} ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Return(ack) p, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Ack).To(Equal(ack)) var hasPing bool for _, f := range p.Frames { if _, ok := f.Frame.(*wire.PingFrame); ok { hasPing = true Expect(f.Handler).To(BeNil()) // make sure the PING is not retransmitted if lost } } Expect(hasPing).To(BeTrue()) }) It("doesn't send a PING if it already sent another ack-eliciting frame", func() { sendMaxNumNonAckElicitingAcks() pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) expectAppendStreamFrames() expectAppendControlFrames(ackhandler.Frame{Frame: &wire.MaxDataFrame{}}) p, err := packer.AppendPacket(getPacketBuffer(), maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Frames).ToNot(ContainElement(&wire.PingFrame{})) }) }) }) Context("packing crypto packets", func() { It("sets the length", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) handshakeStream.Write([]byte("foobar")) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.IsOnlyShortHeaderPacket()).To(BeFalse()) parsePacket(p.buffer.Data) }) It("packs an Initial packet and pads it", func() { packer.perspective = protocol.PerspectiveClient pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get0RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) initialStream.Write([]byte("initial")) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(1)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(more).To(BeEmpty()) }) It("packs a maximum size Handshake packet", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) handshakeStream.Write(bytes.Repeat([]byte{'f'}, 2000)) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) parsePacket(p.buffer.Data) }) It("packs a coalesced packet with Initial / Handshake, and pads it", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24)) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) // don't EXPECT any calls for a Handshake ACK frame initialStream.Write([]byte("initial")) handshakeStream.Write([]byte("handshake")) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(2)) Expect(p.shortHdrPacket).To(BeNil()) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.longHdrPackets[1].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.longHdrPackets[1].frames).To(HaveLen(1)) Expect(p.longHdrPackets[1].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("handshake"))) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(2)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdrs[1].Type).To(Equal(protocol.PacketTypeHandshake)) Expect(more).To(BeEmpty()) }) It("packs a coalesced packet with Initial / super short Handshake, and pads it", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24)) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) // don't EXPECT any calls for a Handshake ACK frame initialStream.Write([]byte("initial")) packer.retransmissionQueue.addHandshake(&wire.PingFrame{}) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(2)) Expect(p.shortHdrPacket).To(BeNil()) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.longHdrPackets[1].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.longHdrPackets[1].frames).To(HaveLen(1)) Expect(p.longHdrPackets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(2)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdrs[1].Type).To(Equal(protocol.PacketTypeHandshake)) Expect(more).To(BeEmpty()) }) It("packs a coalesced packet with super short Initial / super short Handshake, and pads it", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24)) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, gomock.Any()) packer.retransmissionQueue.addInitial(&wire.PingFrame{}) packer.retransmissionQueue.addHandshake(&wire.PingFrame{}) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(2)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(p.longHdrPackets[1].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.longHdrPackets[1].frames).To(HaveLen(1)) Expect(p.longHdrPackets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(2)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdrs[1].Type).To(Equal(protocol.PacketTypeHandshake)) Expect(more).To(BeEmpty()) }) It("packs a coalesced packet with Initial / super short 1-RTT, and pads it", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) initialStream.Write([]byte("initial")) expectAppendControlFrames() expectAppendStreamFrames() framer.EXPECT().HasData().Return(true) packer.retransmissionQueue.addAppData(&wire.PingFrame{}) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.IsOnlyShortHeaderPacket()).To(BeFalse()) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.shortHdrPacket).ToNot(BeNil()) Expect(p.shortHdrPacket.Frames).To(HaveLen(1)) Expect(p.shortHdrPacket.Frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(1)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(more).ToNot(BeEmpty()) parseShortHeaderPacket(more) }) It("packs a coalesced packet with Initial / 0-RTT, and pads it", func() { packer.perspective = protocol.PerspectiveClient pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x24)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption0RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get0RTTSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) // don't EXPECT any calls for a Handshake ACK frame initialStream.Write([]byte("initial")) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.StreamFrame{Frame: &wire.StreamFrame{Data: []byte("foobar")}}) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(2)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.longHdrPackets[0].streamFrames).To(BeEmpty()) Expect(p.longHdrPackets[1].EncryptionLevel()).To(Equal(protocol.Encryption0RTT)) Expect(p.longHdrPackets[1].frames).To(BeEmpty()) Expect(p.longHdrPackets[1].streamFrames).To(HaveLen(1)) Expect(p.longHdrPackets[1].streamFrames[0].Frame.Data).To(Equal([]byte("foobar"))) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(2)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdrs[1].Type).To(Equal(protocol.PacketType0RTT)) Expect(more).To(BeEmpty()) }) It("packs a coalesced packet with Handshake / 1-RTT", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x24)) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) framer.EXPECT().HasData().Return(true) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) // don't EXPECT any calls for a 1-RTT ACK frame handshakeStream.Write([]byte("handshake")) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.StreamFrame{Frame: &wire.StreamFrame{Data: []byte("foobar")}}) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically("<", 100)) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("handshake"))) Expect(p.shortHdrPacket).ToNot(BeNil()) Expect(p.shortHdrPacket.Frames).To(BeEmpty()) Expect(p.shortHdrPacket.StreamFrames).To(HaveLen(1)) Expect(p.shortHdrPacket.StreamFrames[0].Frame.Data).To(Equal([]byte("foobar"))) hdrs, more := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(1)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeHandshake)) Expect(more).ToNot(BeEmpty()) parseShortHeaderPacket(more) }) It("doesn't add a coalesced packet if the remaining size is smaller than MaxCoalescedPacketSize", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x24), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x24)) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) // don't EXPECT any calls to GetHandshakeSealer and Get1RTTSealer ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) handshakeStream.Write(bytes.Repeat([]byte{'f'}, int(maxPacketSize-protocol.MinCoalescedPacketSize-(&wire.CryptoFrame{}).Length(protocol.Version1)))) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.shortHdrPacket).To(BeNil()) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(len(p.buffer.Data)).To(And( BeNumerically("<", maxPacketSize), BeNumerically(">", maxPacketSize-protocol.MinCoalescedPacketSize), )) parsePacket(p.buffer.Data) }) It("pads if payload length + packet number length is smaller than 4, for Long Header packets", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) sealer := getSealer() sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(sealer, nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) packer.retransmissionQueue.addHandshake(&wire.PingFrame{}) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) packet, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.longHdrPackets).To(HaveLen(1)) Expect(packet.shortHdrPacket).To(BeNil()) // cut off the tag that the mock sealer added // packet.buffer.Data = packet.buffer.Data[:packet.buffer.Len()-protocol.ByteCount(sealer.Overhead())] hdr, _, _, err := wire.ParsePacket(packet.buffer.Data) Expect(err).ToNot(HaveOccurred()) data := packet.buffer.Data extHdr, err := hdr.ParseExtended(data) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen1)) data = data[extHdr.ParsedLen():] Expect(data).To(HaveLen(4 - 1 /* packet number length */ + sealer.Overhead())) // the first bytes of the payload should be a 2 PADDING frames... Expect(data[0]).To(Equal(byte(0))) Expect(data[1]).To(Equal(byte(0))) data = data[2:] // ... followed by the PING frameParser := wire.NewFrameParser(false) l, frame, err := frameParser.ParseNext(data, protocol.Encryption1RTT, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(len(data) - l).To(Equal(sealer.Overhead())) }) It("adds retransmissions", func() { f := &wire.CryptoFrame{Data: []byte("Initial")} retransmissionQueue.addInitial(f) retransmissionQueue.addHandshake(&wire.CryptoFrame{Data: []byte("Handshake")}) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.longHdrPackets[0].frames[0].Frame).To(Equal(f)) Expect(p.longHdrPackets[0].frames[0].Handler).ToNot(BeNil()) }) It("sends an Initial packet containing only an ACK", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 20}}} ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true).Return(ack) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].ack).To(Equal(ack)) }) It("doesn't pack anything if there's nothing to send at Initial and Handshake keys are not yet available", func() { sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).To(BeNil()) }) It("sends a Handshake packet containing only an ACK", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 10, Largest: 20}}} ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, true).Return(ack) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].ack).To(Equal(ack)) }) for _, pers := range []protocol.Perspective{protocol.PerspectiveServer, protocol.PerspectiveClient} { perspective := pers It(fmt.Sprintf("pads Initial packets to the required minimum packet size, for the %s", perspective), func() { token := []byte("initial token") packer.SetToken(token) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get0RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) initialStream.Write([]byte("foobar")) packer.perspective = protocol.PerspectiveClient p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].header.Token).To(Equal(token)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) cf := p.longHdrPackets[0].frames[0].Frame.(*wire.CryptoFrame) Expect(cf.Data).To(Equal([]byte("foobar"))) }) } It("adds an ACK frame", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 42, Largest: 1337}}} pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) sealingManager.EXPECT().GetHandshakeSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get0RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false).Return(ack) initialStream.Write([]byte("foobar")) packer.perspective = protocol.PerspectiveClient p, err := packer.PackCoalescedPacket(false, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.longHdrPackets).To(HaveLen(1)) Expect(p.longHdrPackets[0].ack).To(Equal(ack)) Expect(p.longHdrPackets[0].frames).To(HaveLen(1)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) }) }) Context("packing probe packets", func() { for _, pers := range []protocol.Perspective{protocol.PerspectiveServer, protocol.PerspectiveClient} { perspective := pers It(fmt.Sprintf("packs an Initial probe packet and pads it, for the %s", perspective), func() { packer.perspective = perspective f := &wire.CryptoFrame{Data: []byte("Initial")} retransmissionQueue.addInitial(f) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) p, err := packer.MaybePackProbePacket(protocol.EncryptionInitial, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(HaveLen(1)) packet := p.longHdrPackets[0] Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(Equal(f)) parsePacket(p.buffer.Data) }) It(fmt.Sprintf("packs an Initial probe packet with 1 byte payload, for the %s", perspective), func() { packer.perspective = perspective retransmissionQueue.addInitial(&wire.PingFrame{}) sealingManager.EXPECT().GetInitialSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, false) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) p, err := packer.MaybePackProbePacket(protocol.EncryptionInitial, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(HaveLen(1)) packet := p.longHdrPackets[0] Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) parsePacket(p.buffer.Data) }) } It("packs a Handshake probe packet", func() { f := &wire.CryptoFrame{Data: []byte("Handshake")} retransmissionQueue.addHandshake(f) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) p, err := packer.MaybePackProbePacket(protocol.EncryptionHandshake, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(HaveLen(1)) packet := p.longHdrPackets[0] Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(Equal(f)) parsePacket(p.buffer.Data) }) It("packs a full size Handshake probe packet", func() { f := &wire.CryptoFrame{Data: make([]byte, 2000)} retransmissionQueue.addHandshake(f) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) p, err := packer.MaybePackProbePacket(protocol.EncryptionHandshake, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.longHdrPackets).To(HaveLen(1)) packet := p.longHdrPackets[0] Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) Expect(packet.length).To(Equal(maxPacketSize)) parsePacket(p.buffer.Data) }) It("packs a 1-RTT probe packet", func() { f := &wire.StreamFrame{Data: []byte("1-RTT")} retransmissionQueue.addInitial(f) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) framer.EXPECT().HasData().Return(true) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.StreamFrame{Frame: f}) p, err := packer.MaybePackProbePacket(protocol.Encryption1RTT, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.IsOnlyShortHeaderPacket()).To(BeTrue()) Expect(p.longHdrPackets).To(BeEmpty()) Expect(p.shortHdrPacket).ToNot(BeNil()) packet := p.shortHdrPacket Expect(packet.Frames).To(BeEmpty()) Expect(packet.StreamFrames).To(HaveLen(1)) Expect(packet.StreamFrames[0].Frame).To(Equal(f)) }) It("packs a full size 1-RTT probe packet", func() { f := &wire.StreamFrame{Data: make([]byte, 2000)} retransmissionQueue.addInitial(f) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42)) framer.EXPECT().HasData().Return(true) expectAppendControlFrames() framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any(), protocol.Version1).DoAndReturn(func(fs []ackhandler.StreamFrame, maxSize protocol.ByteCount, v protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) { sf, split := f.MaybeSplitOffFrame(maxSize, v) Expect(split).To(BeTrue()) return append(fs, ackhandler.StreamFrame{Frame: sf}), sf.Length(v) }) p, err := packer.MaybePackProbePacket(protocol.Encryption1RTT, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.IsOnlyShortHeaderPacket()).To(BeTrue()) Expect(p.longHdrPackets).To(BeEmpty()) Expect(p.shortHdrPacket).ToNot(BeNil()) packet := p.shortHdrPacket Expect(packet.Frames).To(BeEmpty()) Expect(packet.StreamFrames).To(HaveLen(1)) Expect(packet.Length).To(Equal(maxPacketSize)) }) It("returns nil if there's no probe data to send", func() { sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true) framer.EXPECT().HasData() packet, err := packer.MaybePackProbePacket(protocol.Encryption1RTT, maxPacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(packet).To(BeNil()) }) It("packs an MTU probe packet", func() { sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil) pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x43)) ping := ackhandler.Frame{Frame: &wire.PingFrame{}} const probePacketSize = maxPacketSize + 42 p, buffer, err := packer.PackMTUProbePacket(ping, probePacketSize, protocol.Version1) Expect(err).ToNot(HaveOccurred()) Expect(p.Length).To(BeEquivalentTo(probePacketSize)) Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(0x43))) Expect(buffer.Data).To(HaveLen(int(probePacketSize))) Expect(p.IsPathMTUProbePacket).To(BeTrue()) }) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/packet_unpacker.go000066400000000000000000000170251465664453100243020ustar00rootroot00000000000000package quic import ( "fmt" "time" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" ) type headerDecryptor interface { DecryptHeader(sample []byte, firstByte *byte, pnBytes []byte) } type headerParseError struct { err error } func (e *headerParseError) Unwrap() error { return e.err } func (e *headerParseError) Error() string { return e.err.Error() } type unpackedPacket struct { hdr *wire.ExtendedHeader encryptionLevel protocol.EncryptionLevel data []byte } // The packetUnpacker unpacks QUIC packets. type packetUnpacker struct { cs handshake.CryptoSetup shortHdrConnIDLen int } var _ unpacker = &packetUnpacker{} func newPacketUnpacker(cs handshake.CryptoSetup, shortHdrConnIDLen int) *packetUnpacker { return &packetUnpacker{ cs: cs, shortHdrConnIDLen: shortHdrConnIDLen, } } // UnpackLongHeader unpacks a Long Header packet. // If the reserved bits are invalid, the error is wire.ErrInvalidReservedBits. // If any other error occurred when parsing the header, the error is of type headerParseError. // If decrypting the payload fails for any reason, the error is the error returned by the AEAD. func (u *packetUnpacker) UnpackLongHeader(hdr *wire.Header, data []byte) (*unpackedPacket, error) { var encLevel protocol.EncryptionLevel var extHdr *wire.ExtendedHeader var decrypted []byte //nolint:exhaustive // Retry packets can't be unpacked. switch hdr.Type { case protocol.PacketTypeInitial: encLevel = protocol.EncryptionInitial opener, err := u.cs.GetInitialOpener() if err != nil { return nil, err } extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data) if err != nil { return nil, err } case protocol.PacketTypeHandshake: encLevel = protocol.EncryptionHandshake opener, err := u.cs.GetHandshakeOpener() if err != nil { return nil, err } extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data) if err != nil { return nil, err } case protocol.PacketType0RTT: encLevel = protocol.Encryption0RTT opener, err := u.cs.Get0RTTOpener() if err != nil { return nil, err } extHdr, decrypted, err = u.unpackLongHeaderPacket(opener, hdr, data) if err != nil { return nil, err } default: return nil, fmt.Errorf("unknown packet type: %s", hdr.Type) } if len(decrypted) == 0 { return nil, &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "empty packet", } } return &unpackedPacket{ hdr: extHdr, encryptionLevel: encLevel, data: decrypted, }, nil } func (u *packetUnpacker) UnpackShortHeader(rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { opener, err := u.cs.Get1RTTOpener() if err != nil { return 0, 0, 0, nil, err } pn, pnLen, kp, decrypted, err := u.unpackShortHeaderPacket(opener, rcvTime, data) if err != nil { return 0, 0, 0, nil, err } if len(decrypted) == 0 { return 0, 0, 0, nil, &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "empty packet", } } return pn, pnLen, kp, decrypted, nil } func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpener, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, []byte, error) { extHdr, parseErr := u.unpackLongHeader(opener, hdr, data) // If the reserved bits are set incorrectly, we still need to continue unpacking. // This avoids a timing side-channel, which otherwise might allow an attacker // to gain information about the header encryption. if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { return nil, nil, parseErr } extHdrLen := extHdr.ParsedLen() extHdr.PacketNumber = opener.DecodePacketNumber(extHdr.PacketNumber, extHdr.PacketNumberLen) decrypted, err := opener.Open(data[extHdrLen:extHdrLen], data[extHdrLen:], extHdr.PacketNumber, data[:extHdrLen]) if err != nil { return nil, nil, err } if parseErr != nil { return nil, nil, parseErr } return extHdr, decrypted, nil } func (u *packetUnpacker) unpackShortHeaderPacket(opener handshake.ShortHeaderOpener, rcvTime time.Time, data []byte) (protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, []byte, error) { l, pn, pnLen, kp, parseErr := u.unpackShortHeader(opener, data) // If the reserved bits are set incorrectly, we still need to continue unpacking. // This avoids a timing side-channel, which otherwise might allow an attacker // to gain information about the header encryption. if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { return 0, 0, 0, nil, &headerParseError{parseErr} } pn = opener.DecodePacketNumber(pn, pnLen) decrypted, err := opener.Open(data[l:l], data[l:], rcvTime, pn, kp, data[:l]) if err != nil { return 0, 0, 0, nil, err } return pn, pnLen, kp, decrypted, parseErr } func (u *packetUnpacker) unpackShortHeader(hd headerDecryptor, data []byte) (int, protocol.PacketNumber, protocol.PacketNumberLen, protocol.KeyPhaseBit, error) { hdrLen := 1 /* first header byte */ + u.shortHdrConnIDLen if len(data) < hdrLen+4+16 { return 0, 0, 0, 0, fmt.Errorf("packet too small, expected at least 20 bytes after the header, got %d", len(data)-hdrLen) } origPNBytes := make([]byte, 4) copy(origPNBytes, data[hdrLen:hdrLen+4]) // 2. decrypt the header, assuming a 4 byte packet number hd.DecryptHeader( data[hdrLen+4:hdrLen+4+16], &data[0], data[hdrLen:hdrLen+4], ) // 3. parse the header (and learn the actual length of the packet number) l, pn, pnLen, kp, parseErr := wire.ParseShortHeader(data, u.shortHdrConnIDLen) if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { return l, pn, pnLen, kp, parseErr } // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier if pnLen != protocol.PacketNumberLen4 { copy(data[hdrLen+int(pnLen):hdrLen+4], origPNBytes[int(pnLen):]) } return l, pn, pnLen, kp, parseErr } // The error is either nil, a wire.ErrInvalidReservedBits or of type headerParseError. func (u *packetUnpacker) unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, error) { extHdr, err := unpackLongHeader(hd, hdr, data) if err != nil && err != wire.ErrInvalidReservedBits { return nil, &headerParseError{err: err} } return extHdr, err } func unpackLongHeader(hd headerDecryptor, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, error) { hdrLen := hdr.ParsedLen() if protocol.ByteCount(len(data)) < hdrLen+4+16 { //nolint:stylecheck return nil, fmt.Errorf("Packet too small. Expected at least 20 bytes after the header, got %d", protocol.ByteCount(len(data))-hdrLen) } // The packet number can be up to 4 bytes long, but we won't know the length until we decrypt it. // 1. save a copy of the 4 bytes origPNBytes := make([]byte, 4) copy(origPNBytes, data[hdrLen:hdrLen+4]) // 2. decrypt the header, assuming a 4 byte packet number hd.DecryptHeader( data[hdrLen+4:hdrLen+4+16], &data[0], data[hdrLen:hdrLen+4], ) // 3. parse the header (and learn the actual length of the packet number) extHdr, parseErr := hdr.ParseExtended(data) if parseErr != nil && parseErr != wire.ErrInvalidReservedBits { return nil, parseErr } // 4. if the packet number is shorter than 4 bytes, replace the remaining bytes with the copy we saved earlier if extHdr.PacketNumberLen != protocol.PacketNumberLen4 { copy(data[extHdr.ParsedLen():hdrLen+4], origPNBytes[int(extHdr.PacketNumberLen):]) } return extHdr, parseErr } golang-github-lucas-clemente-quic-go-0.46.0/packet_unpacker_test.go000066400000000000000000000342641465664453100253450ustar00rootroot00000000000000package quic import ( "errors" "time" "github.com/quic-go/quic-go/internal/handshake" "github.com/quic-go/quic-go/internal/mocks" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/wire" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" ) var _ = Describe("Packet Unpacker", func() { var ( unpacker *packetUnpacker cs *mocks.MockCryptoSetup connID = protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}) payload = []byte("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.") ) getLongHeader := func(extHdr *wire.ExtendedHeader) (*wire.Header, []byte) { b, err := extHdr.Append(nil, protocol.Version1) Expect(err).ToNot(HaveOccurred()) ExpectWithOffset(1, err).ToNot(HaveOccurred()) hdrLen := len(b) if extHdr.Length > protocol.ByteCount(extHdr.PacketNumberLen) { b = append(b, make([]byte, int(extHdr.Length)-int(extHdr.PacketNumberLen))...) } hdr, _, _, err := wire.ParsePacket(b) ExpectWithOffset(1, err).ToNot(HaveOccurred()) return hdr, b[:hdrLen] } getShortHeader := func(connID protocol.ConnectionID, pn protocol.PacketNumber, pnLen protocol.PacketNumberLen, kp protocol.KeyPhaseBit) []byte { b, err := wire.AppendShortHeader(nil, connID, pn, pnLen, kp) Expect(err).ToNot(HaveOccurred()) return b } BeforeEach(func() { cs = mocks.NewMockCryptoSetup(mockCtrl) unpacker = newPacketUnpacker(cs, 4) }) It("errors when the packet is too small to obtain the header decryption sample, for long headers", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 1337, PacketNumberLen: protocol.PacketNumberLen2, } hdr, hdrRaw := getLongHeader(extHdr) data := append(hdrRaw, make([]byte, 2 /* fill up packet number */ +15 /* need 16 bytes */)...) opener := mocks.NewMockLongHeaderOpener(mockCtrl) cs.EXPECT().GetHandshakeOpener().Return(opener, nil) _, err := unpacker.UnpackLongHeader(hdr, data) Expect(err).To(BeAssignableToTypeOf(&headerParseError{})) var headerErr *headerParseError Expect(errors.As(err, &headerErr)).To(BeTrue()) Expect(err).To(MatchError("Packet too small. Expected at least 20 bytes after the header, got 19")) }) It("errors when the packet is too small to obtain the header decryption sample, for short headers", func() { b, err := wire.AppendShortHeader(nil, connID, 1337, protocol.PacketNumberLen2, protocol.KeyPhaseOne) Expect(err).ToNot(HaveOccurred()) data := append(b, make([]byte, 2 /* fill up packet number */ +15 /* need 16 bytes */)...) opener := mocks.NewMockShortHeaderOpener(mockCtrl) cs.EXPECT().Get1RTTOpener().Return(opener, nil) _, _, _, _, err = unpacker.UnpackShortHeader(time.Now(), data) Expect(err).To(BeAssignableToTypeOf(&headerParseError{})) Expect(err).To(MatchError("packet too small, expected at least 20 bytes after the header, got 19")) }) It("opens Initial packets", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeInitial, Length: 3 + 6, // packet number len + payload DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 2, PacketNumberLen: 3, } hdr, hdrRaw := getLongHeader(extHdr) opener := mocks.NewMockLongHeaderOpener(mockCtrl) gomock.InOrder( cs.EXPECT().GetInitialOpener().Return(opener, nil), opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(2), protocol.PacketNumberLen3).Return(protocol.PacketNumber(1234)), opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(1234), hdrRaw).Return([]byte("decrypted"), nil), ) packet, err := unpacker.UnpackLongHeader(hdr, append(hdrRaw, payload...)) Expect(err).ToNot(HaveOccurred()) Expect(packet.encryptionLevel).To(Equal(protocol.EncryptionInitial)) Expect(packet.data).To(Equal([]byte("decrypted"))) }) It("opens 0-RTT packets", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketType0RTT, Length: 3 + 6, // packet number len + payload DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 20, PacketNumberLen: 2, } hdr, hdrRaw := getLongHeader(extHdr) opener := mocks.NewMockLongHeaderOpener(mockCtrl) gomock.InOrder( cs.EXPECT().Get0RTTOpener().Return(opener, nil), opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(20), protocol.PacketNumberLen2).Return(protocol.PacketNumber(321)), opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(321), hdrRaw).Return([]byte("decrypted"), nil), ) packet, err := unpacker.UnpackLongHeader(hdr, append(hdrRaw, payload...)) Expect(err).ToNot(HaveOccurred()) Expect(packet.encryptionLevel).To(Equal(protocol.Encryption0RTT)) Expect(packet.data).To(Equal([]byte("decrypted"))) }) It("opens short header packets", func() { hdrRaw := getShortHeader(connID, 99, protocol.PacketNumberLen4, protocol.KeyPhaseOne) opener := mocks.NewMockShortHeaderOpener(mockCtrl) now := time.Now() gomock.InOrder( cs.EXPECT().Get1RTTOpener().Return(opener, nil), opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(99), protocol.PacketNumberLen4).Return(protocol.PacketNumber(321)), opener.EXPECT().Open(gomock.Any(), payload, now, protocol.PacketNumber(321), protocol.KeyPhaseOne, hdrRaw).Return([]byte("decrypted"), nil), ) pn, pnLen, kp, data, err := unpacker.UnpackShortHeader(now, append(hdrRaw, payload...)) Expect(err).ToNot(HaveOccurred()) Expect(pn).To(Equal(protocol.PacketNumber(321))) Expect(pnLen).To(Equal(protocol.PacketNumberLen4)) Expect(kp).To(Equal(protocol.KeyPhaseOne)) Expect(data).To(Equal([]byte("decrypted"))) }) It("returns the error when getting the opener fails", func() { hdrRaw := getShortHeader(connID, 0x1337, protocol.PacketNumberLen2, protocol.KeyPhaseOne) cs.EXPECT().Get1RTTOpener().Return(nil, handshake.ErrKeysNotYetAvailable) _, _, _, _, err := unpacker.UnpackShortHeader(time.Now(), append(hdrRaw, payload...)) Expect(err).To(MatchError(handshake.ErrKeysNotYetAvailable)) }) It("errors on empty packets, for long header packets", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: connID, Version: Version1, }, KeyPhase: protocol.KeyPhaseOne, PacketNumberLen: protocol.PacketNumberLen4, } hdr, hdrRaw := getLongHeader(extHdr) opener := mocks.NewMockLongHeaderOpener(mockCtrl) gomock.InOrder( cs.EXPECT().GetHandshakeOpener().Return(opener, nil), opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(321)), opener.EXPECT().Open(gomock.Any(), payload, protocol.PacketNumber(321), hdrRaw).Return([]byte(""), nil), ) _, err := unpacker.UnpackLongHeader(hdr, append(hdrRaw, payload...)) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "empty packet", })) }) It("errors on empty packets, for short header packets", func() { hdrRaw := getShortHeader(connID, 0x42, protocol.PacketNumberLen4, protocol.KeyPhaseOne) opener := mocks.NewMockShortHeaderOpener(mockCtrl) now := time.Now() gomock.InOrder( cs.EXPECT().Get1RTTOpener().Return(opener, nil), opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()), opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()).Return(protocol.PacketNumber(321)), opener.EXPECT().Open(gomock.Any(), payload, now, protocol.PacketNumber(321), protocol.KeyPhaseOne, hdrRaw).Return([]byte(""), nil), ) _, _, _, _, err := unpacker.UnpackShortHeader(now, append(hdrRaw, payload...)) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "empty packet", })) }) It("returns the error when unpacking fails", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, Length: 3, // packet number len DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 2, PacketNumberLen: 3, } hdr, hdrRaw := getLongHeader(extHdr) opener := mocks.NewMockLongHeaderOpener(mockCtrl) cs.EXPECT().GetHandshakeOpener().Return(opener, nil) opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) unpackErr := &qerr.TransportError{ErrorCode: qerr.CryptoBufferExceeded} opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, unpackErr) _, err := unpacker.UnpackLongHeader(hdr, append(hdrRaw, payload...)) Expect(err).To(MatchError(unpackErr)) }) It("defends against the timing side-channel when the reserved bits are wrong, for long header packets", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getLongHeader(extHdr) hdrRaw[0] |= 0xc opener := mocks.NewMockLongHeaderOpener(mockCtrl) opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) cs.EXPECT().GetHandshakeOpener().Return(opener, nil) opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("payload"), nil) _, err := unpacker.UnpackLongHeader(hdr, append(hdrRaw, payload...)) Expect(err).To(MatchError(wire.ErrInvalidReservedBits)) }) It("defends against the timing side-channel when the reserved bits are wrong, for short header packets", func() { hdrRaw := getShortHeader(connID, 0x1337, protocol.PacketNumberLen2, protocol.KeyPhaseZero) hdrRaw[0] |= 0x18 opener := mocks.NewMockShortHeaderOpener(mockCtrl) opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) cs.EXPECT().Get1RTTOpener().Return(opener, nil) opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return([]byte("payload"), nil) _, _, _, _, err := unpacker.UnpackShortHeader(time.Now(), append(hdrRaw, payload...)) Expect(err).To(MatchError(wire.ErrInvalidReservedBits)) }) It("returns the decryption error, when unpacking a packet with wrong reserved bits fails, for long headers", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getLongHeader(extHdr) hdrRaw[0] |= 0x18 opener := mocks.NewMockLongHeaderOpener(mockCtrl) opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) cs.EXPECT().GetHandshakeOpener().Return(opener, nil) opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) _, err := unpacker.UnpackLongHeader(hdr, append(hdrRaw, payload...)) Expect(err).To(MatchError(handshake.ErrDecryptionFailed)) }) It("returns the decryption error, when unpacking a packet with wrong reserved bits fails, for short headers", func() { hdrRaw := getShortHeader(connID, 0x1337, protocol.PacketNumberLen2, protocol.KeyPhaseZero) hdrRaw[0] |= 0x18 opener := mocks.NewMockShortHeaderOpener(mockCtrl) opener.EXPECT().DecryptHeader(gomock.Any(), gomock.Any(), gomock.Any()) cs.EXPECT().Get1RTTOpener().Return(opener, nil) opener.EXPECT().DecodePacketNumber(gomock.Any(), gomock.Any()) opener.EXPECT().Open(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) _, _, _, _, err := unpacker.UnpackShortHeader(time.Now(), append(hdrRaw, payload...)) Expect(err).To(MatchError(handshake.ErrDecryptionFailed)) }) It("decrypts the header", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeHandshake, Length: 3, // packet number len DestConnectionID: connID, Version: protocol.Version1, }, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getLongHeader(extHdr) origHdrRaw := append([]byte{}, hdrRaw...) // save a copy of the header firstHdrByte := hdrRaw[0] hdrRaw[0] ^= 0xff // invert the first byte hdrRaw[len(hdrRaw)-2] ^= 0xff // invert the packet number hdrRaw[len(hdrRaw)-1] ^= 0xff // invert the packet number Expect(hdrRaw[0]).ToNot(Equal(firstHdrByte)) opener := mocks.NewMockLongHeaderOpener(mockCtrl) cs.EXPECT().GetHandshakeOpener().Return(opener, nil) gomock.InOrder( // we're using a 2 byte packet number, so the sample starts at the 3rd payload byte opener.EXPECT().DecryptHeader( []byte{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}, &hdrRaw[0], append(hdrRaw[len(hdrRaw)-2:], []byte{1, 2}...)).Do(func(_ []byte, firstByte *byte, pnBytes []byte) { *firstByte ^= 0xff // invert the first byte back for i := range pnBytes { pnBytes[i] ^= 0xff // invert the packet number bytes } }), opener.EXPECT().DecodePacketNumber(protocol.PacketNumber(0x1337), protocol.PacketNumberLen2).Return(protocol.PacketNumber(0x7331)), opener.EXPECT().Open(gomock.Any(), gomock.Any(), protocol.PacketNumber(0x7331), origHdrRaw).Return([]byte{0}, nil), ) data := hdrRaw for i := 1; i <= 100; i++ { data = append(data, uint8(i)) } packet, err := unpacker.UnpackLongHeader(hdr, data) Expect(err).ToNot(HaveOccurred()) Expect(packet.hdr.PacketNumber).To(Equal(protocol.PacketNumber(0x7331))) }) }) golang-github-lucas-clemente-quic-go-0.46.0/qlog/000077500000000000000000000000001465664453100215515ustar00rootroot00000000000000golang-github-lucas-clemente-quic-go-0.46.0/qlog/connection_tracer.go000066400000000000000000000355251465664453100256110ustar00rootroot00000000000000package qlog import ( "io" "net" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" "github.com/francoispqt/gojay" ) type connectionTracer struct { w writer lastMetrics *metrics perspective logging.Perspective } // NewConnectionTracer creates a new tracer to record a qlog for a connection. func NewConnectionTracer(w io.WriteCloser, p logging.Perspective, odcid protocol.ConnectionID) *logging.ConnectionTracer { tr := &trace{ VantagePoint: vantagePoint{Type: p.String()}, CommonFields: commonFields{ ODCID: &odcid, GroupID: &odcid, ReferenceTime: time.Now(), }, } t := connectionTracer{ w: *newWriter(w, tr), perspective: p, } go t.w.Run() return &logging.ConnectionTracer{ StartedConnection: func(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { t.StartedConnection(local, remote, srcConnID, destConnID) }, NegotiatedVersion: func(chosen logging.Version, clientVersions, serverVersions []logging.Version) { t.NegotiatedVersion(chosen, clientVersions, serverVersions) }, ClosedConnection: func(e error) { t.ClosedConnection(e) }, SentTransportParameters: func(tp *wire.TransportParameters) { t.SentTransportParameters(tp) }, ReceivedTransportParameters: func(tp *wire.TransportParameters) { t.ReceivedTransportParameters(tp) }, RestoredTransportParameters: func(tp *wire.TransportParameters) { t.RestoredTransportParameters(tp) }, SentLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { t.SentLongHeaderPacket(hdr, size, ecn, ack, frames) }, SentShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame) { t.SentShortHeaderPacket(hdr, size, ecn, ack, frames) }, ReceivedLongHeaderPacket: func(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { t.ReceivedLongHeaderPacket(hdr, size, ecn, frames) }, ReceivedShortHeaderPacket: func(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { t.ReceivedShortHeaderPacket(hdr, size, ecn, frames) }, ReceivedRetry: func(hdr *wire.Header) { t.ReceivedRetry(hdr) }, ReceivedVersionNegotiationPacket: func(dest, src logging.ArbitraryLenConnectionID, versions []logging.Version) { t.ReceivedVersionNegotiationPacket(dest, src, versions) }, BufferedPacket: func(pt logging.PacketType, size protocol.ByteCount) { t.BufferedPacket(pt, size) }, DroppedPacket: func(pt logging.PacketType, pn logging.PacketNumber, size logging.ByteCount, reason logging.PacketDropReason) { t.DroppedPacket(pt, pn, size, reason) }, UpdatedMetrics: func(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { t.UpdatedMetrics(rttStats, cwnd, bytesInFlight, packetsInFlight) }, LostPacket: func(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { t.LostPacket(encLevel, pn, lossReason) }, UpdatedMTU: func(mtu logging.ByteCount, done bool) { t.UpdatedMTU(mtu, done) }, UpdatedCongestionState: func(state logging.CongestionState) { t.UpdatedCongestionState(state) }, UpdatedPTOCount: func(value uint32) { t.UpdatedPTOCount(value) }, UpdatedKeyFromTLS: func(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { t.UpdatedKeyFromTLS(encLevel, pers) }, UpdatedKey: func(keyPhase protocol.KeyPhase, remote bool) { t.UpdatedKey(keyPhase, remote) }, DroppedEncryptionLevel: func(encLevel protocol.EncryptionLevel) { t.DroppedEncryptionLevel(encLevel) }, DroppedKey: func(keyPhase protocol.KeyPhase) { t.DroppedKey(keyPhase) }, SetLossTimer: func(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { t.SetLossTimer(tt, encLevel, timeout) }, LossTimerExpired: func(tt logging.TimerType, encLevel protocol.EncryptionLevel) { t.LossTimerExpired(tt, encLevel) }, LossTimerCanceled: func() { t.LossTimerCanceled() }, ECNStateUpdated: func(state logging.ECNState, trigger logging.ECNStateTrigger) { t.ECNStateUpdated(state, trigger) }, ChoseALPN: func(protocol string) { t.recordEvent(time.Now(), eventALPNInformation{chosenALPN: protocol}) }, Debug: func(name, msg string) { t.Debug(name, msg) }, Close: func() { t.Close() }, } } func (t *connectionTracer) recordEvent(eventTime time.Time, details eventDetails) { t.w.RecordEvent(eventTime, details) } func (t *connectionTracer) Close() { t.w.Close() } func (t *connectionTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID protocol.ConnectionID) { // ignore this event if we're not dealing with UDP addresses here localAddr, ok := local.(*net.UDPAddr) if !ok { return } remoteAddr, ok := remote.(*net.UDPAddr) if !ok { return } t.recordEvent(time.Now(), &eventConnectionStarted{ SrcAddr: localAddr, DestAddr: remoteAddr, SrcConnectionID: srcConnID, DestConnectionID: destConnID, }) } func (t *connectionTracer) NegotiatedVersion(chosen logging.Version, client, server []logging.Version) { var clientVersions, serverVersions []version if len(client) > 0 { clientVersions = make([]version, len(client)) for i, v := range client { clientVersions[i] = version(v) } } if len(server) > 0 { serverVersions = make([]version, len(server)) for i, v := range server { serverVersions[i] = version(v) } } t.recordEvent(time.Now(), &eventVersionNegotiated{ clientVersions: clientVersions, serverVersions: serverVersions, chosenVersion: version(chosen), }) } func (t *connectionTracer) ClosedConnection(e error) { t.recordEvent(time.Now(), &eventConnectionClosed{e: e}) } func (t *connectionTracer) SentTransportParameters(tp *wire.TransportParameters) { t.recordTransportParameters(t.perspective, tp) } func (t *connectionTracer) ReceivedTransportParameters(tp *wire.TransportParameters) { t.recordTransportParameters(t.perspective.Opposite(), tp) } func (t *connectionTracer) RestoredTransportParameters(tp *wire.TransportParameters) { ev := t.toTransportParameters(tp) ev.Restore = true t.recordEvent(time.Now(), ev) } func (t *connectionTracer) recordTransportParameters(sentBy protocol.Perspective, tp *wire.TransportParameters) { ev := t.toTransportParameters(tp) ev.Owner = ownerLocal if sentBy != t.perspective { ev.Owner = ownerRemote } ev.SentBy = sentBy t.recordEvent(time.Now(), ev) } func (t *connectionTracer) toTransportParameters(tp *wire.TransportParameters) *eventTransportParameters { var pa *preferredAddress if tp.PreferredAddress != nil { pa = &preferredAddress{ IPv4: tp.PreferredAddress.IPv4, IPv6: tp.PreferredAddress.IPv6, ConnectionID: tp.PreferredAddress.ConnectionID, StatelessResetToken: tp.PreferredAddress.StatelessResetToken, } } return &eventTransportParameters{ OriginalDestinationConnectionID: tp.OriginalDestinationConnectionID, InitialSourceConnectionID: tp.InitialSourceConnectionID, RetrySourceConnectionID: tp.RetrySourceConnectionID, StatelessResetToken: tp.StatelessResetToken, DisableActiveMigration: tp.DisableActiveMigration, MaxIdleTimeout: tp.MaxIdleTimeout, MaxUDPPayloadSize: tp.MaxUDPPayloadSize, AckDelayExponent: tp.AckDelayExponent, MaxAckDelay: tp.MaxAckDelay, ActiveConnectionIDLimit: tp.ActiveConnectionIDLimit, InitialMaxData: tp.InitialMaxData, InitialMaxStreamDataBidiLocal: tp.InitialMaxStreamDataBidiLocal, InitialMaxStreamDataBidiRemote: tp.InitialMaxStreamDataBidiRemote, InitialMaxStreamDataUni: tp.InitialMaxStreamDataUni, InitialMaxStreamsBidi: int64(tp.MaxBidiStreamNum), InitialMaxStreamsUni: int64(tp.MaxUniStreamNum), PreferredAddress: pa, MaxDatagramFrameSize: tp.MaxDatagramFrameSize, } } func (t *connectionTracer) SentLongHeaderPacket( hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame, ) { t.sentPacket(*transformLongHeader(hdr), size, hdr.Length, ecn, ack, frames) } func (t *connectionTracer) SentShortHeaderPacket( hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame, ) { t.sentPacket(*transformShortHeader(hdr), size, 0, ecn, ack, frames) } func (t *connectionTracer) sentPacket( hdr gojay.MarshalerJSONObject, size, payloadLen logging.ByteCount, ecn logging.ECN, ack *logging.AckFrame, frames []logging.Frame, ) { numFrames := len(frames) if ack != nil { numFrames++ } fs := make([]frame, 0, numFrames) if ack != nil { fs = append(fs, frame{Frame: ack}) } for _, f := range frames { fs = append(fs, frame{Frame: f}) } t.recordEvent(time.Now(), &eventPacketSent{ Header: hdr, Length: size, PayloadLength: payloadLen, ECN: ecn, Frames: fs, }) } func (t *connectionTracer) ReceivedLongHeaderPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { fs := make([]frame, len(frames)) for i, f := range frames { fs[i] = frame{Frame: f} } header := *transformLongHeader(hdr) t.recordEvent(time.Now(), &eventPacketReceived{ Header: header, Length: size, PayloadLength: hdr.Length, ECN: ecn, Frames: fs, }) } func (t *connectionTracer) ReceivedShortHeaderPacket(hdr *logging.ShortHeader, size logging.ByteCount, ecn logging.ECN, frames []logging.Frame) { fs := make([]frame, len(frames)) for i, f := range frames { fs[i] = frame{Frame: f} } header := *transformShortHeader(hdr) t.recordEvent(time.Now(), &eventPacketReceived{ Header: header, Length: size, PayloadLength: size - wire.ShortHeaderLen(hdr.DestConnectionID, hdr.PacketNumberLen), ECN: ecn, Frames: fs, }) } func (t *connectionTracer) ReceivedRetry(hdr *wire.Header) { t.recordEvent(time.Now(), &eventRetryReceived{ Header: *transformHeader(hdr), }) } func (t *connectionTracer) ReceivedVersionNegotiationPacket(dest, src logging.ArbitraryLenConnectionID, versions []logging.Version) { ver := make([]version, len(versions)) for i, v := range versions { ver[i] = version(v) } t.recordEvent(time.Now(), &eventVersionNegotiationReceived{ Header: packetHeaderVersionNegotiation{ SrcConnectionID: src, DestConnectionID: dest, }, SupportedVersions: ver, }) } func (t *connectionTracer) BufferedPacket(pt logging.PacketType, size protocol.ByteCount) { t.recordEvent(time.Now(), &eventPacketBuffered{ PacketType: pt, PacketSize: size, }) } func (t *connectionTracer) DroppedPacket(pt logging.PacketType, pn logging.PacketNumber, size protocol.ByteCount, reason logging.PacketDropReason) { t.recordEvent(time.Now(), &eventPacketDropped{ PacketType: pt, PacketNumber: pn, PacketSize: size, Trigger: packetDropReason(reason), }) } func (t *connectionTracer) UpdatedMetrics(rttStats *utils.RTTStats, cwnd, bytesInFlight protocol.ByteCount, packetsInFlight int) { m := &metrics{ MinRTT: rttStats.MinRTT(), SmoothedRTT: rttStats.SmoothedRTT(), LatestRTT: rttStats.LatestRTT(), RTTVariance: rttStats.MeanDeviation(), CongestionWindow: cwnd, BytesInFlight: bytesInFlight, PacketsInFlight: packetsInFlight, } t.recordEvent(time.Now(), &eventMetricsUpdated{ Last: t.lastMetrics, Current: m, }) t.lastMetrics = m } func (t *connectionTracer) AcknowledgedPacket(protocol.EncryptionLevel, protocol.PacketNumber) {} func (t *connectionTracer) LostPacket(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { t.recordEvent(time.Now(), &eventPacketLost{ PacketType: getPacketTypeFromEncryptionLevel(encLevel), PacketNumber: pn, Trigger: packetLossReason(lossReason), }) } func (t *connectionTracer) UpdatedMTU(mtu protocol.ByteCount, done bool) { t.recordEvent(time.Now(), &eventMTUUpdated{mtu: mtu, done: done}) } func (t *connectionTracer) UpdatedCongestionState(state logging.CongestionState) { t.recordEvent(time.Now(), &eventCongestionStateUpdated{state: congestionState(state)}) } func (t *connectionTracer) UpdatedPTOCount(value uint32) { t.recordEvent(time.Now(), &eventUpdatedPTO{Value: value}) } func (t *connectionTracer) UpdatedKeyFromTLS(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { t.recordEvent(time.Now(), &eventKeyUpdated{ Trigger: keyUpdateTLS, KeyType: encLevelToKeyType(encLevel, pers), }) } func (t *connectionTracer) UpdatedKey(generation protocol.KeyPhase, remote bool) { trigger := keyUpdateLocal if remote { trigger = keyUpdateRemote } now := time.Now() t.recordEvent(now, &eventKeyUpdated{ Trigger: trigger, KeyType: keyTypeClient1RTT, KeyPhase: generation, }) t.recordEvent(now, &eventKeyUpdated{ Trigger: trigger, KeyType: keyTypeServer1RTT, KeyPhase: generation, }) } func (t *connectionTracer) DroppedEncryptionLevel(encLevel protocol.EncryptionLevel) { now := time.Now() if encLevel == protocol.Encryption0RTT { t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, t.perspective)}) } else { t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveServer)}) t.recordEvent(now, &eventKeyDiscarded{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveClient)}) } } func (t *connectionTracer) DroppedKey(generation protocol.KeyPhase) { now := time.Now() t.recordEvent(now, &eventKeyDiscarded{ KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveServer), KeyPhase: generation, }) t.recordEvent(now, &eventKeyDiscarded{ KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveClient), KeyPhase: generation, }) } func (t *connectionTracer) SetLossTimer(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { now := time.Now() t.recordEvent(now, &eventLossTimerSet{ TimerType: timerType(tt), EncLevel: encLevel, Delta: timeout.Sub(now), }) } func (t *connectionTracer) LossTimerExpired(tt logging.TimerType, encLevel protocol.EncryptionLevel) { t.recordEvent(time.Now(), &eventLossTimerExpired{ TimerType: timerType(tt), EncLevel: encLevel, }) } func (t *connectionTracer) LossTimerCanceled() { t.recordEvent(time.Now(), &eventLossTimerCanceled{}) } func (t *connectionTracer) ECNStateUpdated(state logging.ECNState, trigger logging.ECNStateTrigger) { t.recordEvent(time.Now(), &eventECNStateUpdated{state: state, trigger: trigger}) } func (t *connectionTracer) Debug(name, msg string) { t.recordEvent(time.Now(), &eventGeneric{ name: name, msg: msg, }) } golang-github-lucas-clemente-quic-go-0.46.0/qlog/connection_tracer_test.go000066400000000000000000001144661465664453100266520ustar00rootroot00000000000000package qlog import ( "bytes" "io" "net" "net/netip" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type nopWriteCloserImpl struct{ io.Writer } func (nopWriteCloserImpl) Close() error { return nil } func nopWriteCloser(w io.Writer) io.WriteCloser { return &nopWriteCloserImpl{Writer: w} } type entry struct { Time time.Time Name string Event map[string]interface{} } func exportAndParse(buf *bytes.Buffer) []entry { m := make(map[string]interface{}) line, err := buf.ReadBytes('\n') Expect(err).ToNot(HaveOccurred()) Expect(unmarshal(line, &m)).To(Succeed()) Expect(m).To(HaveKey("trace")) var entries []entry trace := m["trace"].(map[string]interface{}) Expect(trace).To(HaveKey("common_fields")) commonFields := trace["common_fields"].(map[string]interface{}) Expect(commonFields).To(HaveKey("reference_time")) referenceTime := time.Unix(0, int64(commonFields["reference_time"].(float64)*1e6)) Expect(trace).ToNot(HaveKey("events")) for buf.Len() > 0 { line, err := buf.ReadBytes('\n') Expect(err).ToNot(HaveOccurred()) ev := make(map[string]interface{}) Expect(unmarshal(line, &ev)).To(Succeed()) Expect(ev).To(HaveLen(3)) Expect(ev).To(HaveKey("time")) Expect(ev).To(HaveKey("name")) Expect(ev).To(HaveKey("data")) entries = append(entries, entry{ Time: referenceTime.Add(time.Duration(ev["time"].(float64)*1e6) * time.Nanosecond), Name: ev["name"].(string), Event: ev["data"].(map[string]interface{}), }) } return entries } func exportAndParseSingle(buf *bytes.Buffer) entry { entries := exportAndParse(buf) Expect(entries).To(HaveLen(1)) return entries[0] } var _ = Describe("Tracing", func() { var ( tracer *logging.ConnectionTracer buf *bytes.Buffer ) BeforeEach(func() { buf = &bytes.Buffer{} tracer = NewConnectionTracer( nopWriteCloser(buf), logging.PerspectiveServer, protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), ) }) It("exports a trace that has the right metadata", func() { tracer.Close() m := make(map[string]interface{}) Expect(unmarshal(buf.Bytes(), &m)).To(Succeed()) Expect(m).To(HaveKeyWithValue("qlog_version", "0.3")) Expect(m).To(HaveKey("title")) Expect(m).To(HaveKey("trace")) trace := m["trace"].(map[string]interface{}) Expect(trace).To(HaveKey("common_fields")) commonFields := trace["common_fields"].(map[string]interface{}) Expect(commonFields).To(HaveKeyWithValue("ODCID", "deadbeef")) Expect(commonFields).To(HaveKeyWithValue("group_id", "deadbeef")) Expect(commonFields).To(HaveKey("reference_time")) referenceTime := time.Unix(0, int64(commonFields["reference_time"].(float64)*1e6)) Expect(referenceTime).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(commonFields).To(HaveKeyWithValue("time_format", "relative")) Expect(trace).To(HaveKey("vantage_point")) vantagePoint := trace["vantage_point"].(map[string]interface{}) Expect(vantagePoint).To(HaveKeyWithValue("type", "server")) }) Context("Events", func() { It("records connection starts", func() { tracer.StartedConnection( &net.UDPAddr{IP: net.IPv4(192, 168, 13, 37), Port: 42}, &net.UDPAddr{IP: net.IPv4(192, 168, 12, 34), Port: 24}, protocol.ParseConnectionID([]byte{1, 2, 3, 4}), protocol.ParseConnectionID([]byte{5, 6, 7, 8}), ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_started")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("ip_version", "ipv4")) Expect(ev).To(HaveKeyWithValue("src_ip", "192.168.13.37")) Expect(ev).To(HaveKeyWithValue("src_port", float64(42))) Expect(ev).To(HaveKeyWithValue("dst_ip", "192.168.12.34")) Expect(ev).To(HaveKeyWithValue("dst_port", float64(24))) Expect(ev).To(HaveKeyWithValue("src_cid", "01020304")) Expect(ev).To(HaveKeyWithValue("dst_cid", "05060708")) }) It("records the version, if no version negotiation happened", func() { tracer.NegotiatedVersion(0x1337, nil, nil) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:version_information")) ev := entry.Event Expect(ev).To(HaveLen(1)) Expect(ev).To(HaveKeyWithValue("chosen_version", "1337")) }) It("records the version, if version negotiation happened", func() { tracer.NegotiatedVersion(0x1337, []logging.Version{1, 2, 3}, []logging.Version{4, 5, 6}) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:version_information")) ev := entry.Event Expect(ev).To(HaveLen(3)) Expect(ev).To(HaveKeyWithValue("chosen_version", "1337")) Expect(ev).To(HaveKey("client_versions")) Expect(ev["client_versions"].([]interface{})).To(Equal([]interface{}{"1", "2", "3"})) Expect(ev).To(HaveKey("server_versions")) Expect(ev["server_versions"].([]interface{})).To(Equal([]interface{}{"4", "5", "6"})) }) It("records idle timeouts", func() { tracer.ClosedConnection(&quic.IdleTimeoutError{}) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_closed")) ev := entry.Event Expect(ev).To(HaveLen(2)) Expect(ev).To(HaveKeyWithValue("owner", "local")) Expect(ev).To(HaveKeyWithValue("trigger", "idle_timeout")) }) It("records handshake timeouts", func() { tracer.ClosedConnection(&quic.HandshakeTimeoutError{}) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_closed")) ev := entry.Event Expect(ev).To(HaveLen(2)) Expect(ev).To(HaveKeyWithValue("owner", "local")) Expect(ev).To(HaveKeyWithValue("trigger", "handshake_timeout")) }) It("records a received stateless reset packet", func() { tracer.ClosedConnection(&quic.StatelessResetError{ Token: protocol.StatelessResetToken{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_closed")) ev := entry.Event Expect(ev).To(HaveLen(3)) Expect(ev).To(HaveKeyWithValue("owner", "remote")) Expect(ev).To(HaveKeyWithValue("trigger", "stateless_reset")) Expect(ev).To(HaveKeyWithValue("stateless_reset_token", "00112233445566778899aabbccddeeff")) }) It("records connection closing due to version negotiation failure", func() { tracer.ClosedConnection(&quic.VersionNegotiationError{}) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_closed")) ev := entry.Event Expect(ev).To(HaveLen(1)) Expect(ev).To(HaveKeyWithValue("trigger", "version_mismatch")) }) It("records application errors", func() { tracer.ClosedConnection(&quic.ApplicationError{ Remote: true, ErrorCode: 1337, ErrorMessage: "foobar", }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_closed")) ev := entry.Event Expect(ev).To(HaveLen(3)) Expect(ev).To(HaveKeyWithValue("owner", "remote")) Expect(ev).To(HaveKeyWithValue("application_code", float64(1337))) Expect(ev).To(HaveKeyWithValue("reason", "foobar")) }) It("records transport errors", func() { tracer.ClosedConnection(&quic.TransportError{ ErrorCode: qerr.AEADLimitReached, ErrorMessage: "foobar", }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:connection_closed")) ev := entry.Event Expect(ev).To(HaveLen(3)) Expect(ev).To(HaveKeyWithValue("owner", "local")) Expect(ev).To(HaveKeyWithValue("connection_code", "aead_limit_reached")) Expect(ev).To(HaveKeyWithValue("reason", "foobar")) }) It("records sent transport parameters", func() { rcid := protocol.ParseConnectionID([]byte{0xde, 0xca, 0xfb, 0xad}) tracer.SentTransportParameters(&logging.TransportParameters{ InitialMaxStreamDataBidiLocal: 1000, InitialMaxStreamDataBidiRemote: 2000, InitialMaxStreamDataUni: 3000, InitialMaxData: 4000, MaxBidiStreamNum: 10, MaxUniStreamNum: 20, MaxAckDelay: 123 * time.Millisecond, AckDelayExponent: 12, DisableActiveMigration: true, MaxUDPPayloadSize: 1234, MaxIdleTimeout: 321 * time.Millisecond, StatelessResetToken: &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00}, OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}), InitialSourceConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), RetrySourceConnectionID: &rcid, ActiveConnectionIDLimit: 7, MaxDatagramFrameSize: protocol.InvalidByteCount, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_set")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("owner", "local")) Expect(ev).To(HaveKeyWithValue("original_destination_connection_id", "deadc0de")) Expect(ev).To(HaveKeyWithValue("initial_source_connection_id", "deadbeef")) Expect(ev).To(HaveKeyWithValue("retry_source_connection_id", "decafbad")) Expect(ev).To(HaveKeyWithValue("stateless_reset_token", "112233445566778899aabbccddeeff00")) Expect(ev).To(HaveKeyWithValue("max_idle_timeout", float64(321))) Expect(ev).To(HaveKeyWithValue("max_udp_payload_size", float64(1234))) Expect(ev).To(HaveKeyWithValue("ack_delay_exponent", float64(12))) Expect(ev).To(HaveKeyWithValue("active_connection_id_limit", float64(7))) Expect(ev).To(HaveKeyWithValue("initial_max_data", float64(4000))) Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_local", float64(1000))) Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_remote", float64(2000))) Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_uni", float64(3000))) Expect(ev).To(HaveKeyWithValue("initial_max_streams_bidi", float64(10))) Expect(ev).To(HaveKeyWithValue("initial_max_streams_uni", float64(20))) Expect(ev).ToNot(HaveKey("preferred_address")) Expect(ev).ToNot(HaveKey("max_datagram_frame_size")) }) It("records the server's transport parameters, without a stateless reset token", func() { tracer.SentTransportParameters(&logging.TransportParameters{ OriginalDestinationConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xc0, 0xde}), ActiveConnectionIDLimit: 7, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_set")) ev := entry.Event Expect(ev).ToNot(HaveKey("stateless_reset_token")) }) It("records transport parameters without retry_source_connection_id", func() { tracer.SentTransportParameters(&logging.TransportParameters{ StatelessResetToken: &protocol.StatelessResetToken{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00}, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_set")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("owner", "local")) Expect(ev).ToNot(HaveKey("retry_source_connection_id")) }) It("records transport parameters with a preferred address", func() { tracer.SentTransportParameters(&logging.TransportParameters{ PreferredAddress: &logging.PreferredAddress{ IPv4: netip.AddrPortFrom(netip.AddrFrom4([4]byte{12, 34, 56, 78}), 123), IPv6: netip.AddrPortFrom(netip.AddrFrom16([16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}), 456), ConnectionID: protocol.ParseConnectionID([]byte{8, 7, 6, 5, 4, 3, 2, 1}), StatelessResetToken: protocol.StatelessResetToken{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, }, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_set")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("owner", "local")) Expect(ev).To(HaveKey("preferred_address")) pa := ev["preferred_address"].(map[string]interface{}) Expect(pa).To(HaveKeyWithValue("ip_v4", "12.34.56.78")) Expect(pa).To(HaveKeyWithValue("port_v4", float64(123))) Expect(pa).To(HaveKeyWithValue("ip_v6", "102:304:506:708:90a:b0c:d0e:f10")) Expect(pa).To(HaveKeyWithValue("port_v6", float64(456))) Expect(pa).To(HaveKeyWithValue("connection_id", "0807060504030201")) Expect(pa).To(HaveKeyWithValue("stateless_reset_token", "0f0e0d0c0b0a09080706050403020100")) }) It("records transport parameters that enable the datagram extension", func() { tracer.SentTransportParameters(&logging.TransportParameters{ MaxDatagramFrameSize: 1337, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_set")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("max_datagram_frame_size", float64(1337))) }) It("records received transport parameters", func() { tracer.ReceivedTransportParameters(&logging.TransportParameters{}) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_set")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("owner", "remote")) Expect(ev).ToNot(HaveKey("original_destination_connection_id")) }) It("records restored transport parameters", func() { tracer.RestoredTransportParameters(&logging.TransportParameters{ InitialMaxStreamDataBidiLocal: 100, InitialMaxStreamDataBidiRemote: 200, InitialMaxStreamDataUni: 300, InitialMaxData: 400, MaxIdleTimeout: 123 * time.Millisecond, }) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:parameters_restored")) ev := entry.Event Expect(ev).ToNot(HaveKey("owner")) Expect(ev).ToNot(HaveKey("original_destination_connection_id")) Expect(ev).ToNot(HaveKey("stateless_reset_token")) Expect(ev).ToNot(HaveKey("retry_source_connection_id")) Expect(ev).ToNot(HaveKey("initial_source_connection_id")) Expect(ev).To(HaveKeyWithValue("max_idle_timeout", float64(123))) Expect(ev).To(HaveKeyWithValue("initial_max_data", float64(400))) Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_local", float64(100))) Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_bidi_remote", float64(200))) Expect(ev).To(HaveKeyWithValue("initial_max_stream_data_uni", float64(300))) }) It("records a sent long header packet, without an ACK", func() { tracer.SentLongHeaderPacket( &logging.ExtendedHeader{ Header: logging.Header{ Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{4, 3, 2, 1}), Length: 1337, Version: protocol.Version1, }, PacketNumber: 1337, }, 987, logging.ECNCE, nil, []logging.Frame{ &logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}, &logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}, }, ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_sent")) ev := entry.Event Expect(ev).To(HaveKey("raw")) raw := ev["raw"].(map[string]interface{}) Expect(raw).To(HaveKeyWithValue("length", float64(987))) Expect(raw).To(HaveKeyWithValue("payload_length", float64(1337))) Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake")) Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337))) Expect(hdr).To(HaveKeyWithValue("scid", "04030201")) Expect(ev).To(HaveKey("frames")) Expect(ev).To(HaveKeyWithValue("ecn", "CE")) frames := ev["frames"].([]interface{}) Expect(frames).To(HaveLen(2)) Expect(frames[0].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "max_stream_data")) Expect(frames[1].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "stream")) }) It("records a sent short header packet, without an ACK", func() { tracer.SentShortHeaderPacket( &logging.ShortHeader{ DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4}), PacketNumber: 1337, }, 123, logging.ECNUnsupported, &logging.AckFrame{AckRanges: []logging.AckRange{{Smallest: 1, Largest: 10}}}, []logging.Frame{&logging.MaxDataFrame{MaximumData: 987}}, ) tracer.Close() entry := exportAndParseSingle(buf) ev := entry.Event raw := ev["raw"].(map[string]interface{}) Expect(raw).To(HaveKeyWithValue("length", float64(123))) Expect(raw).ToNot(HaveKey("payload_length")) Expect(ev).To(HaveKey("header")) Expect(ev).ToNot(HaveKey("ecn")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveKeyWithValue("packet_type", "1RTT")) Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337))) Expect(ev).To(HaveKey("frames")) frames := ev["frames"].([]interface{}) Expect(frames).To(HaveLen(2)) Expect(frames[0].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "ack")) Expect(frames[1].(map[string]interface{})).To(HaveKeyWithValue("frame_type", "max_data")) }) It("records a received Long Header packet", func() { tracer.ReceivedLongHeaderPacket( &logging.ExtendedHeader{ Header: logging.Header{ Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{4, 3, 2, 1}), Token: []byte{0xde, 0xad, 0xbe, 0xef}, Length: 1234, Version: protocol.Version1, }, PacketNumber: 1337, }, 789, logging.ECT0, []logging.Frame{ &logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}, &logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}, }, ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_received")) ev := entry.Event Expect(ev).To(HaveKey("raw")) raw := ev["raw"].(map[string]interface{}) Expect(raw).To(HaveKeyWithValue("length", float64(789))) Expect(raw).To(HaveKeyWithValue("payload_length", float64(1234))) Expect(ev).To(HaveKeyWithValue("ecn", "ECT(0)")) Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveKeyWithValue("packet_type", "initial")) Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337))) Expect(hdr).To(HaveKeyWithValue("scid", "04030201")) Expect(hdr).To(HaveKey("token")) token := hdr["token"].(map[string]interface{}) Expect(token).To(HaveKeyWithValue("data", "deadbeef")) Expect(ev).To(HaveKey("frames")) Expect(ev["frames"].([]interface{})).To(HaveLen(2)) }) It("records a received Short Header packet", func() { shdr := &logging.ShortHeader{ DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), PacketNumber: 1337, PacketNumberLen: protocol.PacketNumberLen3, KeyPhase: protocol.KeyPhaseZero, } tracer.ReceivedShortHeaderPacket( shdr, 789, logging.ECT1, []logging.Frame{ &logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}, &logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}, }, ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_received")) ev := entry.Event Expect(ev).To(HaveKey("raw")) raw := ev["raw"].(map[string]interface{}) Expect(raw).To(HaveKeyWithValue("length", float64(789))) Expect(raw).To(HaveKeyWithValue("payload_length", float64(789-(1+8+3)))) Expect(ev).To(HaveKeyWithValue("ecn", "ECT(1)")) Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveKeyWithValue("packet_type", "1RTT")) Expect(hdr).To(HaveKeyWithValue("packet_number", float64(1337))) Expect(hdr).To(HaveKeyWithValue("key_phase_bit", "0")) Expect(ev).To(HaveKey("frames")) Expect(ev["frames"].([]interface{})).To(HaveLen(2)) }) It("records a received Retry packet", func() { tracer.ReceivedRetry( &logging.Header{ Type: protocol.PacketTypeRetry, DestConnectionID: protocol.ParseConnectionID([]byte{1, 2, 3, 4, 5, 6, 7, 8}), SrcConnectionID: protocol.ParseConnectionID([]byte{4, 3, 2, 1}), Token: []byte{0xde, 0xad, 0xbe, 0xef}, Version: protocol.Version1, }, ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_received")) ev := entry.Event Expect(ev).ToNot(HaveKey("raw")) Expect(ev).To(HaveKey("header")) header := ev["header"].(map[string]interface{}) Expect(header).To(HaveKeyWithValue("packet_type", "retry")) Expect(header).ToNot(HaveKey("packet_number")) Expect(header).To(HaveKey("version")) Expect(header).To(HaveKey("dcid")) Expect(header).To(HaveKey("scid")) Expect(header).To(HaveKey("token")) token := header["token"].(map[string]interface{}) Expect(token).To(HaveKeyWithValue("data", "deadbeef")) Expect(ev).ToNot(HaveKey("frames")) }) It("records a received Version Negotiation packet", func() { tracer.ReceivedVersionNegotiationPacket( protocol.ArbitraryLenConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, protocol.ArbitraryLenConnectionID{4, 3, 2, 1}, []protocol.Version{0xdeadbeef, 0xdecafbad}, ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_received")) ev := entry.Event Expect(ev).To(HaveKey("header")) Expect(ev).ToNot(HaveKey("frames")) Expect(ev).To(HaveKey("supported_versions")) Expect(ev["supported_versions"].([]interface{})).To(Equal([]interface{}{"deadbeef", "decafbad"})) header := ev["header"] Expect(header).To(HaveKeyWithValue("packet_type", "version_negotiation")) Expect(header).ToNot(HaveKey("packet_number")) Expect(header).ToNot(HaveKey("version")) Expect(header).To(HaveKeyWithValue("dcid", "0102030405060708")) Expect(header).To(HaveKeyWithValue("scid", "04030201")) }) It("records buffered packets", func() { tracer.BufferedPacket(logging.PacketTypeHandshake, 1337) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_buffered")) ev := entry.Event Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveLen(1)) Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake")) Expect(ev).To(HaveKey("raw")) Expect(ev["raw"].(map[string]interface{})).To(HaveKeyWithValue("length", float64(1337))) Expect(ev).To(HaveKeyWithValue("trigger", "keys_unavailable")) }) It("records dropped packets", func() { tracer.DroppedPacket(logging.PacketTypeRetry, protocol.InvalidPacketNumber, 1337, logging.PacketDropPayloadDecryptError) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_dropped")) ev := entry.Event Expect(ev).To(HaveKey("raw")) Expect(ev["raw"].(map[string]interface{})).To(HaveKeyWithValue("length", float64(1337))) Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveLen(1)) Expect(hdr).To(HaveKeyWithValue("packet_type", "retry")) Expect(ev).To(HaveKeyWithValue("trigger", "payload_decrypt_error")) }) It("records dropped packets with a packet number", func() { tracer.DroppedPacket(logging.PacketTypeHandshake, 42, 1337, logging.PacketDropDuplicate) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:packet_dropped")) ev := entry.Event Expect(ev).To(HaveKey("raw")) Expect(ev["raw"].(map[string]interface{})).To(HaveKeyWithValue("length", float64(1337))) Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveLen(2)) Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake")) Expect(hdr).To(HaveKeyWithValue("packet_number", float64(42))) Expect(ev).To(HaveKeyWithValue("trigger", "duplicate")) }) It("records metrics updates", func() { now := time.Now() rttStats := utils.NewRTTStats() rttStats.UpdateRTT(15*time.Millisecond, 0, now) rttStats.UpdateRTT(20*time.Millisecond, 0, now) rttStats.UpdateRTT(25*time.Millisecond, 0, now) Expect(rttStats.MinRTT()).To(Equal(15 * time.Millisecond)) Expect(rttStats.SmoothedRTT()).To(And( BeNumerically(">", 15*time.Millisecond), BeNumerically("<", 25*time.Millisecond), )) Expect(rttStats.LatestRTT()).To(Equal(25 * time.Millisecond)) tracer.UpdatedMetrics( rttStats, 4321, 1234, 42, ) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:metrics_updated")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("min_rtt", float64(15))) Expect(ev).To(HaveKeyWithValue("latest_rtt", float64(25))) Expect(ev).To(HaveKey("smoothed_rtt")) Expect(time.Duration(ev["smoothed_rtt"].(float64)) * time.Millisecond).To(BeNumerically("~", rttStats.SmoothedRTT(), time.Millisecond)) Expect(ev).To(HaveKey("rtt_variance")) Expect(time.Duration(ev["rtt_variance"].(float64)) * time.Millisecond).To(BeNumerically("~", rttStats.MeanDeviation(), time.Millisecond)) Expect(ev).To(HaveKeyWithValue("congestion_window", float64(4321))) Expect(ev).To(HaveKeyWithValue("bytes_in_flight", float64(1234))) Expect(ev).To(HaveKeyWithValue("packets_in_flight", float64(42))) }) It("only logs the diff between two metrics updates", func() { now := time.Now() rttStats := utils.NewRTTStats() rttStats.UpdateRTT(15*time.Millisecond, 0, now) rttStats.UpdateRTT(20*time.Millisecond, 0, now) rttStats.UpdateRTT(25*time.Millisecond, 0, now) Expect(rttStats.MinRTT()).To(Equal(15 * time.Millisecond)) rttStats2 := utils.NewRTTStats() rttStats2.UpdateRTT(15*time.Millisecond, 0, now) rttStats2.UpdateRTT(15*time.Millisecond, 0, now) rttStats2.UpdateRTT(15*time.Millisecond, 0, now) Expect(rttStats2.MinRTT()).To(Equal(15 * time.Millisecond)) Expect(rttStats.LatestRTT()).To(Equal(25 * time.Millisecond)) tracer.UpdatedMetrics( rttStats, 4321, 1234, 42, ) tracer.UpdatedMetrics( rttStats2, 4321, 12345, // changed 42, ) tracer.Close() entries := exportAndParse(buf) Expect(entries).To(HaveLen(2)) Expect(entries[0].Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entries[0].Name).To(Equal("recovery:metrics_updated")) Expect(entries[0].Event).To(HaveLen(7)) Expect(entries[1].Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entries[1].Name).To(Equal("recovery:metrics_updated")) ev := entries[1].Event Expect(ev).ToNot(HaveKey("min_rtt")) Expect(ev).ToNot(HaveKey("congestion_window")) Expect(ev).ToNot(HaveKey("packets_in_flight")) Expect(ev).To(HaveKeyWithValue("bytes_in_flight", float64(12345))) Expect(ev).To(HaveKeyWithValue("smoothed_rtt", float64(15))) }) It("records lost packets", func() { tracer.LostPacket(protocol.EncryptionHandshake, 42, logging.PacketLossReorderingThreshold) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:packet_lost")) ev := entry.Event Expect(ev).To(HaveKey("header")) hdr := ev["header"].(map[string]interface{}) Expect(hdr).To(HaveLen(2)) Expect(hdr).To(HaveKeyWithValue("packet_type", "handshake")) Expect(hdr).To(HaveKeyWithValue("packet_number", float64(42))) Expect(ev).To(HaveKeyWithValue("trigger", "reordering_threshold")) }) It("records MTU discovery updates", func() { tracer.UpdatedMTU(1337, true) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:mtu_updated")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("mtu", float64(1337))) Expect(ev).To(HaveKeyWithValue("done", true)) }) It("records congestion state updates", func() { tracer.UpdatedCongestionState(logging.CongestionStateCongestionAvoidance) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:congestion_state_updated")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("new", "congestion_avoidance")) }) It("records PTO changes", func() { tracer.UpdatedPTOCount(42) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:metrics_updated")) Expect(entry.Event).To(HaveKeyWithValue("pto_count", float64(42))) }) It("records TLS key updates", func() { tracer.UpdatedKeyFromTLS(protocol.EncryptionHandshake, protocol.PerspectiveClient) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("security:key_updated")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("key_type", "client_handshake_secret")) Expect(ev).To(HaveKeyWithValue("trigger", "tls")) Expect(ev).ToNot(HaveKey("key_phase")) Expect(ev).ToNot(HaveKey("old")) Expect(ev).ToNot(HaveKey("new")) }) It("records TLS key updates, for 1-RTT keys", func() { tracer.UpdatedKeyFromTLS(protocol.Encryption1RTT, protocol.PerspectiveServer) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("security:key_updated")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("key_type", "server_1rtt_secret")) Expect(ev).To(HaveKeyWithValue("trigger", "tls")) Expect(ev).To(HaveKeyWithValue("key_phase", float64(0))) Expect(ev).ToNot(HaveKey("old")) Expect(ev).ToNot(HaveKey("new")) }) It("records QUIC key updates", func() { tracer.UpdatedKey(1337, true) tracer.Close() entries := exportAndParse(buf) Expect(entries).To(HaveLen(2)) var keyTypes []string for _, entry := range entries { Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("security:key_updated")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("key_phase", float64(1337))) Expect(ev).To(HaveKeyWithValue("trigger", "remote_update")) Expect(ev).To(HaveKey("key_type")) keyTypes = append(keyTypes, ev["key_type"].(string)) } Expect(keyTypes).To(ContainElement("server_1rtt_secret")) Expect(keyTypes).To(ContainElement("client_1rtt_secret")) }) It("records dropped encryption levels", func() { tracer.DroppedEncryptionLevel(protocol.EncryptionInitial) tracer.Close() entries := exportAndParse(buf) Expect(entries).To(HaveLen(2)) var keyTypes []string for _, entry := range entries { Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("security:key_discarded")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("trigger", "tls")) Expect(ev).To(HaveKey("key_type")) keyTypes = append(keyTypes, ev["key_type"].(string)) } Expect(keyTypes).To(ContainElement("server_initial_secret")) Expect(keyTypes).To(ContainElement("client_initial_secret")) }) It("records dropped 0-RTT keys", func() { tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) tracer.Close() entries := exportAndParse(buf) Expect(entries).To(HaveLen(1)) entry := entries[0] Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("security:key_discarded")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("trigger", "tls")) Expect(ev).To(HaveKeyWithValue("key_type", "server_0rtt_secret")) }) It("records dropped keys", func() { tracer.DroppedKey(42) tracer.Close() entries := exportAndParse(buf) Expect(entries).To(HaveLen(2)) var keyTypes []string for _, entry := range entries { Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("security:key_discarded")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("key_phase", float64(42))) Expect(ev).ToNot(HaveKey("trigger")) Expect(ev).To(HaveKey("key_type")) keyTypes = append(keyTypes, ev["key_type"].(string)) } Expect(keyTypes).To(ContainElement("server_1rtt_secret")) Expect(keyTypes).To(ContainElement("client_1rtt_secret")) }) It("records when the timer is set", func() { timeout := time.Now().Add(137 * time.Millisecond) tracer.SetLossTimer(logging.TimerTypePTO, protocol.EncryptionHandshake, timeout) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:loss_timer_updated")) ev := entry.Event Expect(ev).To(HaveLen(4)) Expect(ev).To(HaveKeyWithValue("event_type", "set")) Expect(ev).To(HaveKeyWithValue("timer_type", "pto")) Expect(ev).To(HaveKeyWithValue("packet_number_space", "handshake")) Expect(ev).To(HaveKey("delta")) delta := time.Duration(ev["delta"].(float64)*1e6) * time.Nanosecond Expect(entry.Time.Add(delta)).To(BeTemporally("~", timeout, scaleDuration(10*time.Microsecond))) }) It("records when the loss timer expires", func() { tracer.LossTimerExpired(logging.TimerTypeACK, protocol.Encryption1RTT) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:loss_timer_updated")) ev := entry.Event Expect(ev).To(HaveLen(3)) Expect(ev).To(HaveKeyWithValue("event_type", "expired")) Expect(ev).To(HaveKeyWithValue("timer_type", "ack")) Expect(ev).To(HaveKeyWithValue("packet_number_space", "application_data")) }) It("records when the timer is canceled", func() { tracer.LossTimerCanceled() tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:loss_timer_updated")) ev := entry.Event Expect(ev).To(HaveLen(1)) Expect(ev).To(HaveKeyWithValue("event_type", "cancelled")) }) It("records an ECN state transition, without a trigger", func() { tracer.ECNStateUpdated(logging.ECNStateUnknown, logging.ECNTriggerNoTrigger) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:ecn_state_updated")) ev := entry.Event Expect(ev).To(HaveLen(1)) Expect(ev).To(HaveKeyWithValue("new", "unknown")) }) It("records an ECN state transition, with a trigger", func() { tracer.ECNStateUpdated(logging.ECNStateFailed, logging.ECNFailedNoECNCounts) tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("recovery:ecn_state_updated")) ev := entry.Event Expect(ev).To(HaveLen(2)) Expect(ev).To(HaveKeyWithValue("new", "failed")) Expect(ev).To(HaveKeyWithValue("trigger", "ACK doesn't contain ECN marks")) }) It("records a generic event", func() { tracer.Debug("foo", "bar") tracer.Close() entry := exportAndParseSingle(buf) Expect(entry.Time).To(BeTemporally("~", time.Now(), scaleDuration(10*time.Millisecond))) Expect(entry.Name).To(Equal("transport:foo")) ev := entry.Event Expect(ev).To(HaveLen(1)) Expect(ev).To(HaveKeyWithValue("details", "bar")) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/qlog/event.go000066400000000000000000000503141465664453100232240ustar00rootroot00000000000000package qlog import ( "errors" "fmt" "net" "net/netip" "time" "github.com/quic-go/quic-go" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" "github.com/francoispqt/gojay" ) func milliseconds(dur time.Duration) float64 { return float64(dur.Nanoseconds()) / 1e6 } type eventDetails interface { Category() category Name() string gojay.MarshalerJSONObject } type event struct { RelativeTime time.Duration eventDetails } var _ gojay.MarshalerJSONObject = event{} func (e event) IsNil() bool { return false } func (e event) MarshalJSONObject(enc *gojay.Encoder) { enc.Float64Key("time", milliseconds(e.RelativeTime)) enc.StringKey("name", e.Category().String()+":"+e.Name()) enc.ObjectKey("data", e.eventDetails) } type versions []version func (v versions) IsNil() bool { return false } func (v versions) MarshalJSONArray(enc *gojay.Encoder) { for _, e := range v { enc.AddString(e.String()) } } type rawInfo struct { Length logging.ByteCount // full packet length, including header and AEAD authentication tag PayloadLength logging.ByteCount // length of the packet payload, excluding AEAD tag } func (i rawInfo) IsNil() bool { return false } func (i rawInfo) MarshalJSONObject(enc *gojay.Encoder) { enc.Uint64Key("length", uint64(i.Length)) enc.Uint64KeyOmitEmpty("payload_length", uint64(i.PayloadLength)) } type eventConnectionStarted struct { SrcAddr *net.UDPAddr DestAddr *net.UDPAddr SrcConnectionID protocol.ConnectionID DestConnectionID protocol.ConnectionID } var _ eventDetails = &eventConnectionStarted{} func (e eventConnectionStarted) Category() category { return categoryTransport } func (e eventConnectionStarted) Name() string { return "connection_started" } func (e eventConnectionStarted) IsNil() bool { return false } func (e eventConnectionStarted) MarshalJSONObject(enc *gojay.Encoder) { if e.SrcAddr.IP.To4() != nil { enc.StringKey("ip_version", "ipv4") } else { enc.StringKey("ip_version", "ipv6") } enc.StringKey("src_ip", e.SrcAddr.IP.String()) enc.IntKey("src_port", e.SrcAddr.Port) enc.StringKey("dst_ip", e.DestAddr.IP.String()) enc.IntKey("dst_port", e.DestAddr.Port) enc.StringKey("src_cid", e.SrcConnectionID.String()) enc.StringKey("dst_cid", e.DestConnectionID.String()) } type eventVersionNegotiated struct { clientVersions, serverVersions []version chosenVersion version } func (e eventVersionNegotiated) Category() category { return categoryTransport } func (e eventVersionNegotiated) Name() string { return "version_information" } func (e eventVersionNegotiated) IsNil() bool { return false } func (e eventVersionNegotiated) MarshalJSONObject(enc *gojay.Encoder) { if len(e.clientVersions) > 0 { enc.ArrayKey("client_versions", versions(e.clientVersions)) } if len(e.serverVersions) > 0 { enc.ArrayKey("server_versions", versions(e.serverVersions)) } enc.StringKey("chosen_version", e.chosenVersion.String()) } type eventConnectionClosed struct { e error } func (e eventConnectionClosed) Category() category { return categoryTransport } func (e eventConnectionClosed) Name() string { return "connection_closed" } func (e eventConnectionClosed) IsNil() bool { return false } func (e eventConnectionClosed) MarshalJSONObject(enc *gojay.Encoder) { var ( statelessResetErr *quic.StatelessResetError handshakeTimeoutErr *quic.HandshakeTimeoutError idleTimeoutErr *quic.IdleTimeoutError applicationErr *quic.ApplicationError transportErr *quic.TransportError versionNegotiationErr *quic.VersionNegotiationError ) switch { case errors.As(e.e, &statelessResetErr): enc.StringKey("owner", ownerRemote.String()) enc.StringKey("trigger", "stateless_reset") enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", statelessResetErr.Token)) case errors.As(e.e, &handshakeTimeoutErr): enc.StringKey("owner", ownerLocal.String()) enc.StringKey("trigger", "handshake_timeout") case errors.As(e.e, &idleTimeoutErr): enc.StringKey("owner", ownerLocal.String()) enc.StringKey("trigger", "idle_timeout") case errors.As(e.e, &applicationErr): owner := ownerLocal if applicationErr.Remote { owner = ownerRemote } enc.StringKey("owner", owner.String()) enc.Uint64Key("application_code", uint64(applicationErr.ErrorCode)) enc.StringKey("reason", applicationErr.ErrorMessage) case errors.As(e.e, &transportErr): owner := ownerLocal if transportErr.Remote { owner = ownerRemote } enc.StringKey("owner", owner.String()) enc.StringKey("connection_code", transportError(transportErr.ErrorCode).String()) enc.StringKey("reason", transportErr.ErrorMessage) case errors.As(e.e, &versionNegotiationErr): enc.StringKey("trigger", "version_mismatch") } } type eventPacketSent struct { Header gojay.MarshalerJSONObject // either a shortHeader or a packetHeader Length logging.ByteCount PayloadLength logging.ByteCount Frames frames IsCoalesced bool ECN logging.ECN Trigger string } var _ eventDetails = eventPacketSent{} func (e eventPacketSent) Category() category { return categoryTransport } func (e eventPacketSent) Name() string { return "packet_sent" } func (e eventPacketSent) IsNil() bool { return false } func (e eventPacketSent) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", e.Header) enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) enc.ArrayKeyOmitEmpty("frames", e.Frames) enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) if e.ECN != logging.ECNUnsupported { enc.StringKey("ecn", ecn(e.ECN).String()) } enc.StringKeyOmitEmpty("trigger", e.Trigger) } type eventPacketReceived struct { Header gojay.MarshalerJSONObject // either a shortHeader or a packetHeader Length logging.ByteCount PayloadLength logging.ByteCount Frames frames ECN logging.ECN IsCoalesced bool Trigger string } var _ eventDetails = eventPacketReceived{} func (e eventPacketReceived) Category() category { return categoryTransport } func (e eventPacketReceived) Name() string { return "packet_received" } func (e eventPacketReceived) IsNil() bool { return false } func (e eventPacketReceived) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", e.Header) enc.ObjectKey("raw", rawInfo{Length: e.Length, PayloadLength: e.PayloadLength}) enc.ArrayKeyOmitEmpty("frames", e.Frames) enc.BoolKeyOmitEmpty("is_coalesced", e.IsCoalesced) if e.ECN != logging.ECNUnsupported { enc.StringKey("ecn", ecn(e.ECN).String()) } enc.StringKeyOmitEmpty("trigger", e.Trigger) } type eventRetryReceived struct { Header packetHeader } func (e eventRetryReceived) Category() category { return categoryTransport } func (e eventRetryReceived) Name() string { return "packet_received" } func (e eventRetryReceived) IsNil() bool { return false } func (e eventRetryReceived) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", e.Header) } type eventVersionNegotiationReceived struct { Header packetHeaderVersionNegotiation SupportedVersions []version } func (e eventVersionNegotiationReceived) Category() category { return categoryTransport } func (e eventVersionNegotiationReceived) Name() string { return "packet_received" } func (e eventVersionNegotiationReceived) IsNil() bool { return false } func (e eventVersionNegotiationReceived) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", e.Header) enc.ArrayKey("supported_versions", versions(e.SupportedVersions)) } type eventVersionNegotiationSent struct { Header packetHeaderVersionNegotiation SupportedVersions []version } func (e eventVersionNegotiationSent) Category() category { return categoryTransport } func (e eventVersionNegotiationSent) Name() string { return "packet_sent" } func (e eventVersionNegotiationSent) IsNil() bool { return false } func (e eventVersionNegotiationSent) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", e.Header) enc.ArrayKey("supported_versions", versions(e.SupportedVersions)) } type eventPacketBuffered struct { PacketType logging.PacketType PacketSize protocol.ByteCount } func (e eventPacketBuffered) Category() category { return categoryTransport } func (e eventPacketBuffered) Name() string { return "packet_buffered" } func (e eventPacketBuffered) IsNil() bool { return false } func (e eventPacketBuffered) MarshalJSONObject(enc *gojay.Encoder) { //nolint:gosimple enc.ObjectKey("header", packetHeaderWithType{PacketType: e.PacketType, PacketNumber: protocol.InvalidPacketNumber}) enc.ObjectKey("raw", rawInfo{Length: e.PacketSize}) enc.StringKey("trigger", "keys_unavailable") } type eventPacketDropped struct { PacketType logging.PacketType PacketSize protocol.ByteCount PacketNumber logging.PacketNumber Trigger packetDropReason } func (e eventPacketDropped) Category() category { return categoryTransport } func (e eventPacketDropped) Name() string { return "packet_dropped" } func (e eventPacketDropped) IsNil() bool { return false } func (e eventPacketDropped) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", packetHeaderWithType{ PacketType: e.PacketType, PacketNumber: e.PacketNumber, }) enc.ObjectKey("raw", rawInfo{Length: e.PacketSize}) enc.StringKey("trigger", e.Trigger.String()) } type metrics struct { MinRTT time.Duration SmoothedRTT time.Duration LatestRTT time.Duration RTTVariance time.Duration CongestionWindow protocol.ByteCount BytesInFlight protocol.ByteCount PacketsInFlight int } type eventMTUUpdated struct { mtu protocol.ByteCount done bool } func (e eventMTUUpdated) Category() category { return categoryRecovery } func (e eventMTUUpdated) Name() string { return "mtu_updated" } func (e eventMTUUpdated) IsNil() bool { return false } func (e eventMTUUpdated) MarshalJSONObject(enc *gojay.Encoder) { enc.Uint64Key("mtu", uint64(e.mtu)) enc.BoolKey("done", e.done) } type eventMetricsUpdated struct { Last *metrics Current *metrics } func (e eventMetricsUpdated) Category() category { return categoryRecovery } func (e eventMetricsUpdated) Name() string { return "metrics_updated" } func (e eventMetricsUpdated) IsNil() bool { return false } func (e eventMetricsUpdated) MarshalJSONObject(enc *gojay.Encoder) { if e.Last == nil || e.Last.MinRTT != e.Current.MinRTT { enc.FloatKey("min_rtt", milliseconds(e.Current.MinRTT)) } if e.Last == nil || e.Last.SmoothedRTT != e.Current.SmoothedRTT { enc.FloatKey("smoothed_rtt", milliseconds(e.Current.SmoothedRTT)) } if e.Last == nil || e.Last.LatestRTT != e.Current.LatestRTT { enc.FloatKey("latest_rtt", milliseconds(e.Current.LatestRTT)) } if e.Last == nil || e.Last.RTTVariance != e.Current.RTTVariance { enc.FloatKey("rtt_variance", milliseconds(e.Current.RTTVariance)) } if e.Last == nil || e.Last.CongestionWindow != e.Current.CongestionWindow { enc.Uint64Key("congestion_window", uint64(e.Current.CongestionWindow)) } if e.Last == nil || e.Last.BytesInFlight != e.Current.BytesInFlight { enc.Uint64Key("bytes_in_flight", uint64(e.Current.BytesInFlight)) } if e.Last == nil || e.Last.PacketsInFlight != e.Current.PacketsInFlight { enc.Uint64KeyOmitEmpty("packets_in_flight", uint64(e.Current.PacketsInFlight)) } } type eventUpdatedPTO struct { Value uint32 } func (e eventUpdatedPTO) Category() category { return categoryRecovery } func (e eventUpdatedPTO) Name() string { return "metrics_updated" } func (e eventUpdatedPTO) IsNil() bool { return false } func (e eventUpdatedPTO) MarshalJSONObject(enc *gojay.Encoder) { enc.Uint32Key("pto_count", e.Value) } type eventPacketLost struct { PacketType logging.PacketType PacketNumber protocol.PacketNumber Trigger packetLossReason } func (e eventPacketLost) Category() category { return categoryRecovery } func (e eventPacketLost) Name() string { return "packet_lost" } func (e eventPacketLost) IsNil() bool { return false } func (e eventPacketLost) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("header", packetHeaderWithTypeAndPacketNumber{ PacketType: e.PacketType, PacketNumber: e.PacketNumber, }) enc.StringKey("trigger", e.Trigger.String()) } type eventKeyUpdated struct { Trigger keyUpdateTrigger KeyType keyType KeyPhase protocol.KeyPhase // we don't log the keys here, so we don't need `old` and `new`. } func (e eventKeyUpdated) Category() category { return categorySecurity } func (e eventKeyUpdated) Name() string { return "key_updated" } func (e eventKeyUpdated) IsNil() bool { return false } func (e eventKeyUpdated) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("trigger", e.Trigger.String()) enc.StringKey("key_type", e.KeyType.String()) if e.KeyType == keyTypeClient1RTT || e.KeyType == keyTypeServer1RTT { enc.Uint64Key("key_phase", uint64(e.KeyPhase)) } } type eventKeyDiscarded struct { KeyType keyType KeyPhase protocol.KeyPhase } func (e eventKeyDiscarded) Category() category { return categorySecurity } func (e eventKeyDiscarded) Name() string { return "key_discarded" } func (e eventKeyDiscarded) IsNil() bool { return false } func (e eventKeyDiscarded) MarshalJSONObject(enc *gojay.Encoder) { if e.KeyType != keyTypeClient1RTT && e.KeyType != keyTypeServer1RTT { enc.StringKey("trigger", "tls") } enc.StringKey("key_type", e.KeyType.String()) if e.KeyType == keyTypeClient1RTT || e.KeyType == keyTypeServer1RTT { enc.Uint64Key("key_phase", uint64(e.KeyPhase)) } } type eventTransportParameters struct { Restore bool Owner owner SentBy protocol.Perspective OriginalDestinationConnectionID protocol.ConnectionID InitialSourceConnectionID protocol.ConnectionID RetrySourceConnectionID *protocol.ConnectionID StatelessResetToken *protocol.StatelessResetToken DisableActiveMigration bool MaxIdleTimeout time.Duration MaxUDPPayloadSize protocol.ByteCount AckDelayExponent uint8 MaxAckDelay time.Duration ActiveConnectionIDLimit uint64 InitialMaxData protocol.ByteCount InitialMaxStreamDataBidiLocal protocol.ByteCount InitialMaxStreamDataBidiRemote protocol.ByteCount InitialMaxStreamDataUni protocol.ByteCount InitialMaxStreamsBidi int64 InitialMaxStreamsUni int64 PreferredAddress *preferredAddress MaxDatagramFrameSize protocol.ByteCount } func (e eventTransportParameters) Category() category { return categoryTransport } func (e eventTransportParameters) Name() string { if e.Restore { return "parameters_restored" } return "parameters_set" } func (e eventTransportParameters) IsNil() bool { return false } func (e eventTransportParameters) MarshalJSONObject(enc *gojay.Encoder) { if !e.Restore { enc.StringKey("owner", e.Owner.String()) if e.SentBy == protocol.PerspectiveServer { enc.StringKey("original_destination_connection_id", e.OriginalDestinationConnectionID.String()) if e.StatelessResetToken != nil { enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", e.StatelessResetToken[:])) } if e.RetrySourceConnectionID != nil { enc.StringKey("retry_source_connection_id", (*e.RetrySourceConnectionID).String()) } } enc.StringKey("initial_source_connection_id", e.InitialSourceConnectionID.String()) } enc.BoolKey("disable_active_migration", e.DisableActiveMigration) enc.FloatKeyOmitEmpty("max_idle_timeout", milliseconds(e.MaxIdleTimeout)) enc.Int64KeyNullEmpty("max_udp_payload_size", int64(e.MaxUDPPayloadSize)) enc.Uint8KeyOmitEmpty("ack_delay_exponent", e.AckDelayExponent) enc.FloatKeyOmitEmpty("max_ack_delay", milliseconds(e.MaxAckDelay)) enc.Uint64KeyOmitEmpty("active_connection_id_limit", e.ActiveConnectionIDLimit) enc.Int64KeyOmitEmpty("initial_max_data", int64(e.InitialMaxData)) enc.Int64KeyOmitEmpty("initial_max_stream_data_bidi_local", int64(e.InitialMaxStreamDataBidiLocal)) enc.Int64KeyOmitEmpty("initial_max_stream_data_bidi_remote", int64(e.InitialMaxStreamDataBidiRemote)) enc.Int64KeyOmitEmpty("initial_max_stream_data_uni", int64(e.InitialMaxStreamDataUni)) enc.Int64KeyOmitEmpty("initial_max_streams_bidi", e.InitialMaxStreamsBidi) enc.Int64KeyOmitEmpty("initial_max_streams_uni", e.InitialMaxStreamsUni) if e.PreferredAddress != nil { enc.ObjectKey("preferred_address", e.PreferredAddress) } if e.MaxDatagramFrameSize != protocol.InvalidByteCount { enc.Int64Key("max_datagram_frame_size", int64(e.MaxDatagramFrameSize)) } } type preferredAddress struct { IPv4, IPv6 netip.AddrPort ConnectionID protocol.ConnectionID StatelessResetToken protocol.StatelessResetToken } var _ gojay.MarshalerJSONObject = &preferredAddress{} func (a preferredAddress) IsNil() bool { return false } func (a preferredAddress) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("ip_v4", a.IPv4.Addr().String()) enc.Uint16Key("port_v4", a.IPv4.Port()) enc.StringKey("ip_v6", a.IPv6.Addr().String()) enc.Uint16Key("port_v6", a.IPv6.Port()) enc.StringKey("connection_id", a.ConnectionID.String()) enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", a.StatelessResetToken)) } type eventLossTimerSet struct { TimerType timerType EncLevel protocol.EncryptionLevel Delta time.Duration } func (e eventLossTimerSet) Category() category { return categoryRecovery } func (e eventLossTimerSet) Name() string { return "loss_timer_updated" } func (e eventLossTimerSet) IsNil() bool { return false } func (e eventLossTimerSet) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("event_type", "set") enc.StringKey("timer_type", e.TimerType.String()) enc.StringKey("packet_number_space", encLevelToPacketNumberSpace(e.EncLevel)) enc.Float64Key("delta", milliseconds(e.Delta)) } type eventLossTimerExpired struct { TimerType timerType EncLevel protocol.EncryptionLevel } func (e eventLossTimerExpired) Category() category { return categoryRecovery } func (e eventLossTimerExpired) Name() string { return "loss_timer_updated" } func (e eventLossTimerExpired) IsNil() bool { return false } func (e eventLossTimerExpired) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("event_type", "expired") enc.StringKey("timer_type", e.TimerType.String()) enc.StringKey("packet_number_space", encLevelToPacketNumberSpace(e.EncLevel)) } type eventLossTimerCanceled struct{} func (e eventLossTimerCanceled) Category() category { return categoryRecovery } func (e eventLossTimerCanceled) Name() string { return "loss_timer_updated" } func (e eventLossTimerCanceled) IsNil() bool { return false } func (e eventLossTimerCanceled) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("event_type", "cancelled") } type eventCongestionStateUpdated struct { state congestionState } func (e eventCongestionStateUpdated) Category() category { return categoryRecovery } func (e eventCongestionStateUpdated) Name() string { return "congestion_state_updated" } func (e eventCongestionStateUpdated) IsNil() bool { return false } func (e eventCongestionStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("new", e.state.String()) } type eventECNStateUpdated struct { state logging.ECNState trigger logging.ECNStateTrigger } func (e eventECNStateUpdated) Category() category { return categoryRecovery } func (e eventECNStateUpdated) Name() string { return "ecn_state_updated" } func (e eventECNStateUpdated) IsNil() bool { return false } func (e eventECNStateUpdated) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("new", ecnState(e.state).String()) enc.StringKeyOmitEmpty("trigger", ecnStateTrigger(e.trigger).String()) } type eventGeneric struct { name string msg string } func (e eventGeneric) Category() category { return categoryTransport } func (e eventGeneric) Name() string { return e.name } func (e eventGeneric) IsNil() bool { return false } func (e eventGeneric) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("details", e.msg) } type eventALPNInformation struct { chosenALPN string } func (e eventALPNInformation) Category() category { return categoryTransport } func (e eventALPNInformation) Name() string { return "alpn_information" } func (e eventALPNInformation) IsNil() bool { return false } func (e eventALPNInformation) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("chosen_alpn", e.chosenALPN) } golang-github-lucas-clemente-quic-go-0.46.0/qlog/event_test.go000066400000000000000000000023351465664453100242630ustar00rootroot00000000000000package qlog import ( "bytes" "encoding/json" "time" "github.com/francoispqt/gojay" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) type mevent struct{} var _ eventDetails = mevent{} func (mevent) Category() category { return categoryConnectivity } func (mevent) Name() string { return "mevent" } func (mevent) IsNil() bool { return false } func (mevent) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("event", "details") } var _ = Describe("Events", func() { It("marshals the fields before the event details", func() { buf := &bytes.Buffer{} enc := gojay.NewEncoder(buf) Expect(enc.Encode(event{ RelativeTime: 1337 * time.Microsecond, eventDetails: mevent{}, })).To(Succeed()) var decoded interface{} Expect(json.Unmarshal(buf.Bytes(), &decoded)).To(Succeed()) Expect(decoded).To(HaveLen(3)) Expect(decoded).To(HaveKeyWithValue("time", 1.337)) Expect(decoded).To(HaveKeyWithValue("name", "connectivity:mevent")) Expect(decoded).To(HaveKey("data")) data := decoded.(map[string]interface{})["data"].(map[string]interface{}) Expect(data).To(HaveLen(1)) Expect(data).To(HaveKeyWithValue("event", "details")) }) }) golang-github-lucas-clemente-quic-go-0.46.0/qlog/frame.go000066400000000000000000000160561465664453100232020ustar00rootroot00000000000000package qlog import ( "fmt" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" "github.com/francoispqt/gojay" ) type frame struct { Frame logging.Frame } var _ gojay.MarshalerJSONObject = frame{} var _ gojay.MarshalerJSONArray = frames{} func (f frame) MarshalJSONObject(enc *gojay.Encoder) { switch frame := f.Frame.(type) { case *logging.PingFrame: marshalPingFrame(enc, frame) case *logging.AckFrame: marshalAckFrame(enc, frame) case *logging.ResetStreamFrame: marshalResetStreamFrame(enc, frame) case *logging.StopSendingFrame: marshalStopSendingFrame(enc, frame) case *logging.CryptoFrame: marshalCryptoFrame(enc, frame) case *logging.NewTokenFrame: marshalNewTokenFrame(enc, frame) case *logging.StreamFrame: marshalStreamFrame(enc, frame) case *logging.MaxDataFrame: marshalMaxDataFrame(enc, frame) case *logging.MaxStreamDataFrame: marshalMaxStreamDataFrame(enc, frame) case *logging.MaxStreamsFrame: marshalMaxStreamsFrame(enc, frame) case *logging.DataBlockedFrame: marshalDataBlockedFrame(enc, frame) case *logging.StreamDataBlockedFrame: marshalStreamDataBlockedFrame(enc, frame) case *logging.StreamsBlockedFrame: marshalStreamsBlockedFrame(enc, frame) case *logging.NewConnectionIDFrame: marshalNewConnectionIDFrame(enc, frame) case *logging.RetireConnectionIDFrame: marshalRetireConnectionIDFrame(enc, frame) case *logging.PathChallengeFrame: marshalPathChallengeFrame(enc, frame) case *logging.PathResponseFrame: marshalPathResponseFrame(enc, frame) case *logging.ConnectionCloseFrame: marshalConnectionCloseFrame(enc, frame) case *logging.HandshakeDoneFrame: marshalHandshakeDoneFrame(enc, frame) case *logging.DatagramFrame: marshalDatagramFrame(enc, frame) default: panic("unknown frame type") } } func (f frame) IsNil() bool { return false } type frames []frame func (fs frames) IsNil() bool { return fs == nil } func (fs frames) MarshalJSONArray(enc *gojay.Encoder) { for _, f := range fs { enc.Object(f) } } func marshalPingFrame(enc *gojay.Encoder, _ *wire.PingFrame) { enc.StringKey("frame_type", "ping") } type ackRanges []wire.AckRange func (ars ackRanges) MarshalJSONArray(enc *gojay.Encoder) { for _, r := range ars { enc.Array(ackRange(r)) } } func (ars ackRanges) IsNil() bool { return false } type ackRange wire.AckRange func (ar ackRange) MarshalJSONArray(enc *gojay.Encoder) { enc.AddInt64(int64(ar.Smallest)) if ar.Smallest != ar.Largest { enc.AddInt64(int64(ar.Largest)) } } func (ar ackRange) IsNil() bool { return false } func marshalAckFrame(enc *gojay.Encoder, f *logging.AckFrame) { enc.StringKey("frame_type", "ack") enc.FloatKeyOmitEmpty("ack_delay", milliseconds(f.DelayTime)) enc.ArrayKey("acked_ranges", ackRanges(f.AckRanges)) if hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0; hasECN { enc.Uint64Key("ect0", f.ECT0) enc.Uint64Key("ect1", f.ECT1) enc.Uint64Key("ce", f.ECNCE) } } func marshalResetStreamFrame(enc *gojay.Encoder, f *logging.ResetStreamFrame) { enc.StringKey("frame_type", "reset_stream") enc.Int64Key("stream_id", int64(f.StreamID)) enc.Int64Key("error_code", int64(f.ErrorCode)) enc.Int64Key("final_size", int64(f.FinalSize)) } func marshalStopSendingFrame(enc *gojay.Encoder, f *logging.StopSendingFrame) { enc.StringKey("frame_type", "stop_sending") enc.Int64Key("stream_id", int64(f.StreamID)) enc.Int64Key("error_code", int64(f.ErrorCode)) } func marshalCryptoFrame(enc *gojay.Encoder, f *logging.CryptoFrame) { enc.StringKey("frame_type", "crypto") enc.Int64Key("offset", int64(f.Offset)) enc.Int64Key("length", int64(f.Length)) } func marshalNewTokenFrame(enc *gojay.Encoder, f *logging.NewTokenFrame) { enc.StringKey("frame_type", "new_token") enc.ObjectKey("token", &token{Raw: f.Token}) } func marshalStreamFrame(enc *gojay.Encoder, f *logging.StreamFrame) { enc.StringKey("frame_type", "stream") enc.Int64Key("stream_id", int64(f.StreamID)) enc.Int64Key("offset", int64(f.Offset)) enc.IntKey("length", int(f.Length)) enc.BoolKeyOmitEmpty("fin", f.Fin) } func marshalMaxDataFrame(enc *gojay.Encoder, f *logging.MaxDataFrame) { enc.StringKey("frame_type", "max_data") enc.Int64Key("maximum", int64(f.MaximumData)) } func marshalMaxStreamDataFrame(enc *gojay.Encoder, f *logging.MaxStreamDataFrame) { enc.StringKey("frame_type", "max_stream_data") enc.Int64Key("stream_id", int64(f.StreamID)) enc.Int64Key("maximum", int64(f.MaximumStreamData)) } func marshalMaxStreamsFrame(enc *gojay.Encoder, f *logging.MaxStreamsFrame) { enc.StringKey("frame_type", "max_streams") enc.StringKey("stream_type", streamType(f.Type).String()) enc.Int64Key("maximum", int64(f.MaxStreamNum)) } func marshalDataBlockedFrame(enc *gojay.Encoder, f *logging.DataBlockedFrame) { enc.StringKey("frame_type", "data_blocked") enc.Int64Key("limit", int64(f.MaximumData)) } func marshalStreamDataBlockedFrame(enc *gojay.Encoder, f *logging.StreamDataBlockedFrame) { enc.StringKey("frame_type", "stream_data_blocked") enc.Int64Key("stream_id", int64(f.StreamID)) enc.Int64Key("limit", int64(f.MaximumStreamData)) } func marshalStreamsBlockedFrame(enc *gojay.Encoder, f *logging.StreamsBlockedFrame) { enc.StringKey("frame_type", "streams_blocked") enc.StringKey("stream_type", streamType(f.Type).String()) enc.Int64Key("limit", int64(f.StreamLimit)) } func marshalNewConnectionIDFrame(enc *gojay.Encoder, f *logging.NewConnectionIDFrame) { enc.StringKey("frame_type", "new_connection_id") enc.Int64Key("sequence_number", int64(f.SequenceNumber)) enc.Int64Key("retire_prior_to", int64(f.RetirePriorTo)) enc.IntKey("length", f.ConnectionID.Len()) enc.StringKey("connection_id", f.ConnectionID.String()) enc.StringKey("stateless_reset_token", fmt.Sprintf("%x", f.StatelessResetToken)) } func marshalRetireConnectionIDFrame(enc *gojay.Encoder, f *logging.RetireConnectionIDFrame) { enc.StringKey("frame_type", "retire_connection_id") enc.Int64Key("sequence_number", int64(f.SequenceNumber)) } func marshalPathChallengeFrame(enc *gojay.Encoder, f *logging.PathChallengeFrame) { enc.StringKey("frame_type", "path_challenge") enc.StringKey("data", fmt.Sprintf("%x", f.Data[:])) } func marshalPathResponseFrame(enc *gojay.Encoder, f *logging.PathResponseFrame) { enc.StringKey("frame_type", "path_response") enc.StringKey("data", fmt.Sprintf("%x", f.Data[:])) } func marshalConnectionCloseFrame(enc *gojay.Encoder, f *logging.ConnectionCloseFrame) { errorSpace := "transport" if f.IsApplicationError { errorSpace = "application" } enc.StringKey("frame_type", "connection_close") enc.StringKey("error_space", errorSpace) if errName := transportError(f.ErrorCode).String(); len(errName) > 0 { enc.StringKey("error_code", errName) } else { enc.Uint64Key("error_code", f.ErrorCode) } enc.Uint64Key("raw_error_code", f.ErrorCode) enc.StringKey("reason", f.ReasonPhrase) } func marshalHandshakeDoneFrame(enc *gojay.Encoder, _ *logging.HandshakeDoneFrame) { enc.StringKey("frame_type", "handshake_done") } func marshalDatagramFrame(enc *gojay.Encoder, f *logging.DatagramFrame) { enc.StringKey("frame_type", "datagram") enc.Int64Key("length", int64(f.Length)) } golang-github-lucas-clemente-quic-go-0.46.0/qlog/frame_test.go000066400000000000000000000176741465664453100242500ustar00rootroot00000000000000package qlog import ( "bytes" "encoding/json" "time" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/qerr" "github.com/quic-go/quic-go/logging" "github.com/francoispqt/gojay" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Frames", func() { check := func(f logging.Frame, expected map[string]interface{}) { buf := &bytes.Buffer{} enc := gojay.NewEncoder(buf) ExpectWithOffset(1, enc.Encode(frame{Frame: f})).To(Succeed()) data := buf.Bytes() ExpectWithOffset(1, json.Valid(data)).To(BeTrue()) checkEncoding(data, expected) } It("marshals PING frames", func() { check( &logging.PingFrame{}, map[string]interface{}{ "frame_type": "ping", }, ) }) It("marshals ACK frames with a range acknowledging a single packet", func() { check( &logging.AckFrame{ DelayTime: 86 * time.Millisecond, AckRanges: []logging.AckRange{{Smallest: 120, Largest: 120}}, }, map[string]interface{}{ "frame_type": "ack", "ack_delay": 86, "acked_ranges": [][]float64{{120}}, }, ) }) It("marshals ACK frames without a delay", func() { check( &logging.AckFrame{ AckRanges: []logging.AckRange{{Smallest: 120, Largest: 120}}, }, map[string]interface{}{ "frame_type": "ack", "acked_ranges": [][]float64{{120}}, }, ) }) It("marshals ACK frames with ECN counts", func() { check( &logging.AckFrame{ AckRanges: []logging.AckRange{{Smallest: 120, Largest: 120}}, ECT0: 10, ECT1: 100, ECNCE: 1000, }, map[string]interface{}{ "frame_type": "ack", "acked_ranges": [][]float64{{120}}, "ect0": 10, "ect1": 100, "ce": 1000, }, ) }) It("marshals ACK frames with a range acknowledging ranges of packets", func() { check( &logging.AckFrame{ DelayTime: 86 * time.Millisecond, AckRanges: []logging.AckRange{ {Smallest: 5, Largest: 50}, {Smallest: 100, Largest: 120}, }, }, map[string]interface{}{ "frame_type": "ack", "ack_delay": 86, "acked_ranges": [][]float64{ {5, 50}, {100, 120}, }, }, ) }) It("marshals RESET_STREAM frames", func() { check( &logging.ResetStreamFrame{ StreamID: 987, FinalSize: 1234, ErrorCode: 42, }, map[string]interface{}{ "frame_type": "reset_stream", "stream_id": 987, "error_code": 42, "final_size": 1234, }, ) }) It("marshals STOP_SENDING frames", func() { check( &logging.StopSendingFrame{ StreamID: 987, ErrorCode: 42, }, map[string]interface{}{ "frame_type": "stop_sending", "stream_id": 987, "error_code": 42, }, ) }) It("marshals CRYPTO frames", func() { check( &logging.CryptoFrame{ Offset: 1337, Length: 6, }, map[string]interface{}{ "frame_type": "crypto", "offset": 1337, "length": 6, }, ) }) It("marshals NEW_TOKEN frames", func() { check( &logging.NewTokenFrame{ Token: []byte{0xde, 0xad, 0xbe, 0xef}, }, map[string]interface{}{ "frame_type": "new_token", "token": map[string]interface{}{"data": "deadbeef"}, }, ) }) It("marshals STREAM frames with FIN", func() { check( &logging.StreamFrame{ StreamID: 42, Offset: 1337, Fin: true, Length: 9876, }, map[string]interface{}{ "frame_type": "stream", "stream_id": 42, "offset": 1337, "fin": true, "length": 9876, }, ) }) It("marshals STREAM frames without FIN", func() { check( &logging.StreamFrame{ StreamID: 42, Offset: 1337, Length: 3, }, map[string]interface{}{ "frame_type": "stream", "stream_id": 42, "offset": 1337, "length": 3, }, ) }) It("marshals MAX_DATA frames", func() { check( &logging.MaxDataFrame{ MaximumData: 1337, }, map[string]interface{}{ "frame_type": "max_data", "maximum": 1337, }, ) }) It("marshals MAX_STREAM_DATA frames", func() { check( &logging.MaxStreamDataFrame{ StreamID: 1234, MaximumStreamData: 1337, }, map[string]interface{}{ "frame_type": "max_stream_data", "stream_id": 1234, "maximum": 1337, }, ) }) It("marshals MAX_STREAMS frames", func() { check( &logging.MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: 42, }, map[string]interface{}{ "frame_type": "max_streams", "stream_type": "bidirectional", "maximum": 42, }, ) }) It("marshals DATA_BLOCKED frames", func() { check( &logging.DataBlockedFrame{ MaximumData: 1337, }, map[string]interface{}{ "frame_type": "data_blocked", "limit": 1337, }, ) }) It("marshals STREAM_DATA_BLOCKED frames", func() { check( &logging.StreamDataBlockedFrame{ StreamID: 42, MaximumStreamData: 1337, }, map[string]interface{}{ "frame_type": "stream_data_blocked", "stream_id": 42, "limit": 1337, }, ) }) It("marshals STREAMS_BLOCKED frames", func() { check( &logging.StreamsBlockedFrame{ Type: protocol.StreamTypeUni, StreamLimit: 123, }, map[string]interface{}{ "frame_type": "streams_blocked", "stream_type": "unidirectional", "limit": 123, }, ) }) It("marshals NEW_CONNECTION_ID frames", func() { check( &logging.NewConnectionIDFrame{ SequenceNumber: 42, RetirePriorTo: 24, ConnectionID: protocol.ParseConnectionID([]byte{0xde, 0xad, 0xbe, 0xef}), StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}, }, map[string]interface{}{ "frame_type": "new_connection_id", "sequence_number": 42, "retire_prior_to": 24, "length": 4, "connection_id": "deadbeef", "stateless_reset_token": "000102030405060708090a0b0c0d0e0f", }, ) }) It("marshals RETIRE_CONNECTION_ID frames", func() { check( &logging.RetireConnectionIDFrame{ SequenceNumber: 1337, }, map[string]interface{}{ "frame_type": "retire_connection_id", "sequence_number": 1337, }, ) }) It("marshals PATH_CHALLENGE frames", func() { check( &logging.PathChallengeFrame{ Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xc0, 0x01}, }, map[string]interface{}{ "frame_type": "path_challenge", "data": "deadbeefcafec001", }, ) }) It("marshals PATH_RESPONSE frames", func() { check( &logging.PathResponseFrame{ Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xc0, 0x01}, }, map[string]interface{}{ "frame_type": "path_response", "data": "deadbeefcafec001", }, ) }) It("marshals CONNECTION_CLOSE frames, for application error codes", func() { check( &logging.ConnectionCloseFrame{ IsApplicationError: true, ErrorCode: 1337, ReasonPhrase: "lorem ipsum", }, map[string]interface{}{ "frame_type": "connection_close", "error_space": "application", "error_code": 1337, "raw_error_code": 1337, "reason": "lorem ipsum", }, ) }) It("marshals CONNECTION_CLOSE frames, for transport error codes", func() { check( &logging.ConnectionCloseFrame{ ErrorCode: uint64(qerr.FlowControlError), ReasonPhrase: "lorem ipsum", }, map[string]interface{}{ "frame_type": "connection_close", "error_space": "transport", "error_code": "flow_control_error", "raw_error_code": int(qerr.FlowControlError), "reason": "lorem ipsum", }, ) }) It("marshals HANDSHAKE_DONE frames", func() { check( &logging.HandshakeDoneFrame{}, map[string]interface{}{ "frame_type": "handshake_done", }, ) }) It("marshals DATAGRAM frames", func() { check( &logging.DatagramFrame{Length: 1337}, map[string]interface{}{ "frame_type": "datagram", "length": 1337, }, ) }) }) golang-github-lucas-clemente-quic-go-0.46.0/qlog/packet_header.go000066400000000000000000000112221465664453100246550ustar00rootroot00000000000000package qlog import ( "fmt" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/logging" "github.com/francoispqt/gojay" ) func getPacketTypeFromEncryptionLevel(encLevel protocol.EncryptionLevel) logging.PacketType { switch encLevel { case protocol.EncryptionInitial: return logging.PacketTypeInitial case protocol.EncryptionHandshake: return logging.PacketTypeHandshake case protocol.Encryption0RTT: return logging.PacketType0RTT case protocol.Encryption1RTT: return logging.PacketType1RTT default: panic("unknown encryption level") } } type token struct { Raw []byte } var _ gojay.MarshalerJSONObject = &token{} func (t token) IsNil() bool { return false } func (t token) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("data", fmt.Sprintf("%x", t.Raw)) } // PacketHeader is a QUIC packet header. // TODO: make this a long header type packetHeader struct { PacketType logging.PacketType KeyPhaseBit logging.KeyPhaseBit PacketNumber logging.PacketNumber Version logging.Version SrcConnectionID logging.ConnectionID DestConnectionID logging.ConnectionID Token *token } func transformHeader(hdr *logging.Header) *packetHeader { h := &packetHeader{ PacketType: logging.PacketTypeFromHeader(hdr), SrcConnectionID: hdr.SrcConnectionID, DestConnectionID: hdr.DestConnectionID, Version: hdr.Version, } if len(hdr.Token) > 0 { h.Token = &token{Raw: hdr.Token} } return h } func transformLongHeader(hdr *logging.ExtendedHeader) *packetHeader { h := transformHeader(&hdr.Header) h.PacketNumber = hdr.PacketNumber h.KeyPhaseBit = hdr.KeyPhase return h } func (h packetHeader) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("packet_type", packetType(h.PacketType).String()) if h.PacketType != logging.PacketTypeRetry { enc.Int64Key("packet_number", int64(h.PacketNumber)) } if h.Version != 0 { enc.StringKey("version", version(h.Version).String()) } if h.PacketType != logging.PacketType1RTT { enc.IntKey("scil", h.SrcConnectionID.Len()) if h.SrcConnectionID.Len() > 0 { enc.StringKey("scid", h.SrcConnectionID.String()) } } enc.IntKey("dcil", h.DestConnectionID.Len()) if h.DestConnectionID.Len() > 0 { enc.StringKey("dcid", h.DestConnectionID.String()) } if h.KeyPhaseBit == logging.KeyPhaseZero || h.KeyPhaseBit == logging.KeyPhaseOne { enc.StringKey("key_phase_bit", h.KeyPhaseBit.String()) } if h.Token != nil { enc.ObjectKey("token", h.Token) } } type packetHeaderVersionNegotiation struct { SrcConnectionID logging.ArbitraryLenConnectionID DestConnectionID logging.ArbitraryLenConnectionID } func (h packetHeaderVersionNegotiation) IsNil() bool { return false } func (h packetHeaderVersionNegotiation) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("packet_type", "version_negotiation") enc.IntKey("scil", h.SrcConnectionID.Len()) enc.StringKey("scid", h.SrcConnectionID.String()) enc.IntKey("dcil", h.DestConnectionID.Len()) enc.StringKey("dcid", h.DestConnectionID.String()) } // a minimal header that only outputs the packet type, and potentially a packet number type packetHeaderWithType struct { PacketType logging.PacketType PacketNumber logging.PacketNumber } func (h packetHeaderWithType) IsNil() bool { return false } func (h packetHeaderWithType) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("packet_type", packetType(h.PacketType).String()) if h.PacketNumber != protocol.InvalidPacketNumber { enc.Int64Key("packet_number", int64(h.PacketNumber)) } } // a minimal header that only outputs the packet type type packetHeaderWithTypeAndPacketNumber struct { PacketType logging.PacketType PacketNumber logging.PacketNumber } func (h packetHeaderWithTypeAndPacketNumber) IsNil() bool { return false } func (h packetHeaderWithTypeAndPacketNumber) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("packet_type", packetType(h.PacketType).String()) enc.Int64Key("packet_number", int64(h.PacketNumber)) } type shortHeader struct { DestConnectionID logging.ConnectionID PacketNumber logging.PacketNumber KeyPhaseBit logging.KeyPhaseBit } func transformShortHeader(hdr *logging.ShortHeader) *shortHeader { return &shortHeader{ DestConnectionID: hdr.DestConnectionID, PacketNumber: hdr.PacketNumber, KeyPhaseBit: hdr.KeyPhase, } } func (h shortHeader) IsNil() bool { return false } func (h shortHeader) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("packet_type", packetType(logging.PacketType1RTT).String()) if h.DestConnectionID.Len() > 0 { enc.StringKey("dcid", h.DestConnectionID.String()) } enc.Int64Key("packet_number", int64(h.PacketNumber)) enc.StringKey("key_phase_bit", h.KeyPhaseBit.String()) } golang-github-lucas-clemente-quic-go-0.46.0/qlog/packet_header_test.go000066400000000000000000000075571465664453100257340ustar00rootroot00000000000000package qlog import ( "bytes" "encoding/json" "github.com/francoispqt/gojay" "github.com/quic-go/quic-go/internal/protocol" "github.com/quic-go/quic-go/internal/wire" "github.com/quic-go/quic-go/logging" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Packet Header", func() { It("determines the packet type from the encryption level", func() { Expect(getPacketTypeFromEncryptionLevel(protocol.EncryptionInitial)).To(BeEquivalentTo(logging.PacketTypeInitial)) Expect(getPacketTypeFromEncryptionLevel(protocol.EncryptionHandshake)).To(BeEquivalentTo(logging.PacketTypeHandshake)) Expect(getPacketTypeFromEncryptionLevel(protocol.Encryption0RTT)).To(BeEquivalentTo(logging.PacketType0RTT)) Expect(getPacketTypeFromEncryptionLevel(protocol.Encryption1RTT)).To(BeEquivalentTo(logging.PacketType1RTT)) }) Context("marshalling", func() { check := func(hdr *wire.ExtendedHeader, expected map[string]interface{}) { buf := &bytes.Buffer{} enc := gojay.NewEncoder(buf) ExpectWithOffset(1, enc.Encode(transformLongHeader(hdr))).To(Succeed()) data := buf.Bytes() ExpectWithOffset(1, json.Valid(data)).To(BeTrue()) checkEncoding(data, expected) } It("marshals a header with a payload length", func() { check( &wire.ExtendedHeader{ PacketNumber: 42, Header: wire.Header{ Type: protocol.PacketTypeInitial, Length: 123, Version: protocol.Version(0xdecafbad), }, }, map[string]interface{}{ "packet_type": "initial", "packet_number": 42, "dcil": 0, "scil": 0, "version": "decafbad", }, ) }) It("marshals an Initial with a token", func() { check( &wire.ExtendedHeader{ PacketNumber: 4242, Header: wire.Header{ Type: protocol.PacketTypeInitial, Length: 123, Version: protocol.Version(0xdecafbad), Token: []byte{0xde, 0xad, 0xbe, 0xef}, }, }, map[string]interface{}{ "packet_type": "initial", "packet_number": 4242, "dcil": 0, "scil": 0, "version": "decafbad", "token": map[string]interface{}{"data": "deadbeef"}, }, ) }) It("marshals a Retry packet", func() { check( &wire.ExtendedHeader{ Header: wire.Header{ Type: protocol.PacketTypeRetry, SrcConnectionID: protocol.ParseConnectionID([]byte{0x11, 0x22, 0x33, 0x44}), Version: protocol.Version(0xdecafbad), Token: []byte{0xde, 0xad, 0xbe, 0xef}, }, }, map[string]interface{}{ "packet_type": "retry", "dcil": 0, "scil": 4, "scid": "11223344", "token": map[string]interface{}{"data": "deadbeef"}, "version": "decafbad", }, ) }) It("marshals a packet with packet number 0", func() { check( &wire.ExtendedHeader{ PacketNumber: 0, Header: wire.Header{ Type: protocol.PacketTypeHandshake, Version: protocol.Version(0xdecafbad), }, }, map[string]interface{}{ "packet_type": "handshake", "packet_number": 0, "dcil": 0, "scil": 0, "version": "decafbad", }, ) }) It("marshals a header with a source connection ID", func() { check( &wire.ExtendedHeader{ PacketNumber: 42, Header: wire.Header{ Type: protocol.PacketTypeHandshake, SrcConnectionID: protocol.ParseConnectionID([]byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}), Version: protocol.Version(0xdecafbad), }, }, map[string]interface{}{ "packet_type": "handshake", "packet_number": 42, "dcil": 0, "scil": 16, "scid": "00112233445566778899aabbccddeeff", "version": "decafbad", }, ) }) }) }) golang-github-lucas-clemente-quic-go-0.46.0/qlog/qlog_dir.go000066400000000000000000000034761465664453100237120ustar00rootroot00000000000000package qlog import ( "bufio" "context" "fmt" "log" "os" "strings" "github.com/quic-go/quic-go/internal/utils" "github.com/quic-go/quic-go/logging" ) // DefaultTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable. // Deprecated: use DefaultConnectionTracer instead. func DefaultTracer(ctx context.Context, p logging.Perspective, connID logging.ConnectionID) *logging.ConnectionTracer { return DefaultConnectionTracer(ctx, p, connID) } // DefaultConnectionTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable. // File names are _.sqlog. // Returns nil if QLOGDIR is not set. func DefaultConnectionTracer(_ context.Context, p logging.Perspective, connID logging.ConnectionID) *logging.ConnectionTracer { var label string switch p { case logging.PerspectiveClient: label = "client" case logging.PerspectiveServer: label = "server" } return qlogDirTracer(p, connID, label) } // qlogDirTracer creates a qlog file in the qlog directory specified by the QLOGDIR environment variable. // File names are _