pax_global_header00006660000000000000000000000064141714545160014522gustar00rootroot0000000000000052 comment=496fbe9fda4bb094b1cbdb162a52130b6beda6b1 quic-go-0.25.0/000077500000000000000000000000001417145451600131525ustar00rootroot00000000000000quic-go-0.25.0/.circleci/000077500000000000000000000000001417145451600150055ustar00rootroot00000000000000quic-go-0.25.0/.circleci/config.yml000066400000000000000000000027551417145451600170060ustar00rootroot00000000000000version: 2.1 executors: test-go117: docker: - image: "cimg/go:1.17" environment: runrace: true TIMESCALE_FACTOR: 3 test-go116: docker: - image: "cimg/go:1.16" environment: runrace: true TIMESCALE_FACTOR: 3 jobs: "test": &test executor: test-go117 steps: - checkout - run: name: "Setup build environment" command: go install github.com/onsi/ginkgo/ginkgo - run: name: "Build infos" command: go version - run: name: "Run benchmark tests" command: ginkgo -randomizeAllSpecs -trace benchmark -- -size=10 - run: name: "Run benchmark tests with race detector" command: ginkgo -race -randomizeAllSpecs -trace benchmark -- -size=5 - run: name: "Run tools tests" command: ginkgo -race -r -v -randomizeAllSpecs -trace integrationtests/tools - run: name: "Run self integration tests" command: ginkgo -v -randomizeAllSpecs -trace integrationtests/self - run: name: "Run self integration tests with race detector" command: ginkgo -race -v -randomizeAllSpecs -trace integrationtests/self - run: name: "Run self integration tests with qlog" command: ginkgo -v -randomizeAllSpecs -trace integrationtests/self -- -qlog go117: <<: *test go116: <<: *test executor: test-go116 workflows: workflow: jobs: - go116 - go117 quic-go-0.25.0/.githooks/000077500000000000000000000000001417145451600150575ustar00rootroot00000000000000quic-go-0.25.0/.githooks/README.md000066400000000000000000000002311417145451600163320ustar00rootroot00000000000000# Git Hooks This directory contains useful Git hooks for working with quic-go. Install them by running ```bash git config core.hooksPath .githooks ``` quic-go-0.25.0/.githooks/pre-commit000077500000000000000000000011761417145451600170660ustar00rootroot00000000000000#!/bin/bash # Check that test files don't contain focussed test cases. errored=false for f in $(git diff --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 # Check that all Go files are properly gofumpt-ed. output=$(gofumpt -d $(git diff --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 quic-go-0.25.0/.github/000077500000000000000000000000001417145451600145125ustar00rootroot00000000000000quic-go-0.25.0/.github/workflows/000077500000000000000000000000001417145451600165475ustar00rootroot00000000000000quic-go-0.25.0/.github/workflows/build-interop-docker.yml000066400000000000000000000007761417145451600233260ustar00rootroot00000000000000name: Build interop Docker image on: push: branches: [ interop ] jobs: interop: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: docker/build-push-action@v1 with: always_pull: true path: interop/ username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} repository: martenseemann/quic-go-interop tags: latest tag_with_ref: true add_git_labels: true quic-go-0.25.0/.github/workflows/cross-compile.sh000077500000000000000000000015061417145451600216670ustar00rootroot00000000000000#!/bin/bash set -e GOVERSION=$(go version | cut -d " " -f 3 | cut -b 3-6) for dist in $(go tool dist list); do goos=$(echo $dist | cut -d "/" -f1) goarch=$(echo $dist | cut -d "/" -f2) # cross-compiling for android is a pain... if [[ "$goos" == "android" ]]; then continue; fi # Go 1.14 lacks syscall.IPV6_RECVTCLASS if [[ $GOVERSION == "1.14" && $goos == "darwin" && $goarch == "arm" ]]; then continue; fi # darwin/arm64 requires Cgo for Go < 1.16 if [[ $GOVERSION != "1.16" && "$goos" == "darwin" && $goarch == "arm64" ]]; then continue; 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 continue; fi echo "$dist" GOOS=$goos GOARCH=$goarch go build -o main example/main.go rm main done quic-go-0.25.0/.github/workflows/cross-compile.yml000066400000000000000000000013161417145451600220520ustar00rootroot00000000000000on: [push, pull_request] jobs: crosscompile: strategy: fail-fast: false matrix: go: [ "1.16.x", "1.17.x" ] runs-on: ubuntu-latest name: "Cross Compilation (Go ${{matrix.go}})" steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: stable: '!contains(${{ matrix.go }}, "beta") && !contains(${{ matrix.go }}, "rc")' 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: .github/workflows/cross-compile.sh quic-go-0.25.0/.github/workflows/go-generate.sh000077500000000000000000000012451417145451600213050ustar00rootroot00000000000000#!/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 # delete all files generated by Genny grep --include \*.go -lrIZ "This file was automatically generated by genny." . | xargs --null rm # first generate Genny files to make the code compile grep --include \*.go -lrI "//go:generate genny" | xargs -L 1 go generate # now generate everything go generate ./... cd .. # don't compare fuzzing corpora diff --exclude=corpus -ruN orig generated quic-go-0.25.0/.github/workflows/go-generate.yml000066400000000000000000000010461417145451600214700ustar00rootroot00000000000000on: [push, pull_request] jobs: gogenerate: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: go-version: "1.17.x" - name: Install dependencies run: go build - name: Install code generators run: | go install -v github.com/cheekybits/genny go install -v github.com/golang/mock/mockgen go install -v golang.org/x/tools/cmd/goimports - name: Run code generators run: .github/workflows/go-generate.sh quic-go-0.25.0/.github/workflows/integration.yml000066400000000000000000000030561417145451600216210ustar00rootroot00000000000000on: [push, pull_request] jobs: integration: strategy: fail-fast: false matrix: go: [ "1.16.x", "1.17.x", "1.18.0-beta1" ] runs-on: ubuntu-latest env: DEBUG: false # set this to true to export qlogs and save them as artifacts TIMESCALE_FACTOR: 3 name: Integration Tests (Go ${{ matrix.go }}) steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: stable: '!contains(${{ matrix.go }}, "beta") && !contains(${{ matrix.go }}, "rc")' go-version: ${{ matrix.go }} - run: go version - name: Install Ginkgo run: go install github.com/onsi/ginkgo/ginkgo - name: Install dependencies run: go install - name: set qlogger if: env.DEBUG == 'true' run: echo "QLOGFLAG=-- -qlog" >> $GITHUB_ENV - name: Run tests run: | ginkgo -r -v -randomizeAllSpecs -randomizeSuites -trace -skipPackage self integrationtests ginkgo -r -v -randomizeAllSpecs -randomizeSuites -trace integrationtests/self ${{ env.QLOGFLAG }} - name: Run tests (32 bit) env: GOARCH: 386 run: | ginkgo -r -v -randomizeAllSpecs -randomizeSuites -trace -skipPackage self integrationtests ginkgo -r -v -randomizeAllSpecs -randomizeSuites -trace integrationtests/self ${{ env.QLOGFLAG }} - name: save qlogs if: ${{ always() && env.DEBUG == 'true' }} uses: actions/upload-artifact@v2 with: name: qlogs path: integrationtests/self/*.qlog quic-go-0.25.0/.github/workflows/lint.yml000066400000000000000000000015151417145451600202420ustar00rootroot00000000000000on: [push, pull_request] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: go-version: "1.17.x" - name: Check that no non-test files import Ginkgo or Gomega run: .github/workflows/no_ginkgo.sh - name: Check that go.mod is tidied 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: Check that go mod vendor works run: | cd integrationtests/gomodvendor go mod vendor golangci-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: golangci-lint uses: golangci/golangci-lint-action@v2 with: version: v1.41.1 quic-go-0.25.0/.github/workflows/no_ginkgo.sh000077500000000000000000000007231417145451600210620ustar00rootroot00000000000000#!/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" 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 quic-go-0.25.0/.github/workflows/unit.yml000066400000000000000000000031051417145451600202500ustar00rootroot00000000000000on: [push, pull_request] jobs: unit: strategy: fail-fast: false matrix: os: [ "ubuntu", "windows", "macos" ] go: [ "1.16.x", "1.17.x", "1.18.0-beta1" ] runs-on: ${{ matrix.os }}-latest name: Unit tests (${{ matrix.os}}, Go ${{ matrix.go }}) steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: stable: '!contains(${{ matrix.go }}, "beta") && !contains(${{ matrix.go }}, "rc")' go-version: ${{ matrix.go }} - run: go version - name: Install Ginkgo run: go install github.com/onsi/ginkgo/ginkgo - name: Run tests env: TIMESCALE_FACTOR: 10 run: ginkgo -r -v -cover -randomizeAllSpecs -randomizeSuites -trace -skipPackage integrationtests,benchmark - name: Run tests (32 bit) if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX. env: TIMESCALE_FACTOR: 10 GOARCH: 386 run: ginkgo -r -v -cover -coverprofile coverage.txt -outputdir . -randomizeAllSpecs -randomizeSuites -trace -skipPackage integrationtests,benchmark - name: Run tests with race detector if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow env: TIMESCALE_FACTOR: 20 run: ginkgo -r -v -race -randomizeAllSpecs -randomizeSuites -trace -skipPackage integrationtests,benchmark - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 with: file: coverage.txt env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} quic-go-0.25.0/.gitignore000066400000000000000000000003221417145451600151370ustar00rootroot00000000000000debug debug.test main mockgen_tmp.go *.qtr *.qlog *.txt race.[0-9]* fuzzing/*/*.zip fuzzing/*/coverprofile fuzzing/*/crashers fuzzing/*/sonarprofile fuzzing/*/suppressions fuzzing/*/corpus/ gomock_reflect_*/ quic-go-0.25.0/.golangci.yml000066400000000000000000000015061417145451600155400ustar00rootroot00000000000000run: skip-files: - internal/qtls/structs_equal_test.go linters-settings: depguard: type: blacklist packages: - github.com/marten-seemann/qtls packages-with-error-message: - github.com/marten-seemann/qtls: "importing qtls only allowed in internal/qtls" misspell: ignore-words: - ect linters: disable-all: true enable: - asciicheck - deadcode - depguard - exhaustive - exportloopref - goimports - gofmt # redundant, since gofmt *should* be a no-op after gofumpt - gofumpt - gosimple - ineffassign - misspell - prealloc - scopelint - staticcheck - stylecheck - structcheck - unconvert - unparam - unused - varcheck - vet issues: exclude-rules: - path: internal/qtls linters: - depguard quic-go-0.25.0/Changelog.md000066400000000000000000000106311417145451600153640ustar00rootroot00000000000000# 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/lucas-clemente/quic-go) for details. - Changed the log level environment variable to only accept strings ("DEBUG", "INFO", "ERROR"), see [the wiki](https://github.com/lucas-clemente/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 quic-go-0.25.0/LICENSE000066400000000000000000000021031417145451600141530ustar00rootroot00000000000000MIT 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. quic-go-0.25.0/README.md000066400000000000000000000113061417145451600144320ustar00rootroot00000000000000# A QUIC implementation in pure Go [![PkgGoDev](https://pkg.go.dev/badge/github.com/lucas-clemente/quic-go)](https://pkg.go.dev/github.com/lucas-clemente/quic-go) [![Travis Build Status](https://img.shields.io/travis/lucas-clemente/quic-go/master.svg?style=flat-square&label=Travis+build)](https://travis-ci.org/lucas-clemente/quic-go) [![CircleCI Build Status](https://img.shields.io/circleci/project/github/lucas-clemente/quic-go.svg?style=flat-square&label=CircleCI+build)](https://circleci.com/gh/lucas-clemente/quic-go) [![Windows Build Status](https://img.shields.io/appveyor/ci/lucas-clemente/quic-go/master.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/lucas-clemente/quic-go/branch/master) [![Code Coverage](https://img.shields.io/codecov/c/github/lucas-clemente/quic-go/master.svg?style=flat-square)](https://codecov.io/gh/lucas-clemente/quic-go/) quic-go is an implementation of the [QUIC protocol, RFC 9000](https://datatracker.ietf.org/doc/html/rfc9000) protocol in Go. In addition to RFC 9000, it currently implements the [IETF QUIC draft-29](https://tools.ietf.org/html/draft-ietf-quic-transport-29). Support for draft-29 will eventually be dropped, as it is phased out of the ecosystem. ## Guides *We currently support Go 1.16.x and Go 1.17.x.* Running tests: go test ./... ### QUIC without HTTP/3 Take a look at [this echo example](example/echo/echo.go). ## Usage ### As a server See the [example server](example/main.go). Starting a QUIC server is very similar to the standard lib http in go: ```go http.Handle("/", http.FileServer(http.Dir(wwwDir))) http3.ListenAndServeQUIC("localhost:4242", "/path/to/cert/chain.pem", "/path/to/privkey.pem", nil) ``` ### As a client See the [example client](example/client/main.go). Use a `http3.RoundTripper` as a `Transport` in a `http.Client`. ```go http.Client{ Transport: &http3.RoundTripper{}, } ``` ## Projects using quic-go | Project | Description | Stars | |------------------------------------------------------|--------------------------------------------------------------------------------------------------------|-------| | [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) | | [go-ipfs](https://github.com/ipfs/go-ipfs) | IPFS implementation in go | ![GitHub Repo stars](https://img.shields.io/github/stars/ipfs/go-ipfs?style=flat-square) | | [nextdns](https://github.com/nextdns/nextdns) | NextDNS CLI client (DoH Proxy) | ![GitHub Repo stars](https://img.shields.io/github/stars/nextdns/nextdns?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) | | [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) | ## 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/lucas-clemente/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. quic-go-0.25.0/benchmark/000077500000000000000000000000001417145451600151045ustar00rootroot00000000000000quic-go-0.25.0/benchmark/benchmark_suite_test.go000066400000000000000000000006511417145451600216370ustar00rootroot00000000000000package benchmark import ( "flag" "math/rand" "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestBenchmark(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Benchmark Suite") } var size int // file size in MB, will be read from flags func init() { flag.IntVar(&size, "size", 50, "data length (in MB)") } var _ = BeforeSuite(func() { rand.Seed(GinkgoRandomSeed()) flag.Parse() }) quic-go-0.25.0/benchmark/benchmark_test.go000066400000000000000000000052131417145451600204250ustar00rootroot00000000000000package benchmark import ( "bytes" "context" "crypto/tls" "fmt" "io" "math/rand" "net" quic "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/testdata" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Benchmarks", func() { for i := range protocol.SupportedVersions { version := protocol.SupportedVersions[i] Context(fmt.Sprintf("with version %s", version), func() { var data []byte var dataLen int BeforeEach(func() { dataLen = size * /* MB */ 1e6 data = make([]byte, dataLen) rand.Read(data) // no need to check for an error. math.Rand.Read never errors }) Measure("transferring a file", func(b Benchmarker) { var ln quic.Listener serverAddr := make(chan net.Addr) handshakeChan := make(chan struct{}) // start the server go func() { defer GinkgoRecover() var err error tlsConf := testdata.GetTLSConfig() tlsConf.NextProtos = []string{"benchmark"} ln, err = quic.ListenAddr( "localhost:0", tlsConf, &quic.Config{Versions: []protocol.VersionNumber{version}}, ) Expect(err).ToNot(HaveOccurred()) serverAddr <- ln.Addr() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) // wait for the client to complete the handshake before sending the data // this should not be necessary, but due to timing issues on the CIs, this is necessary to avoid sending too many undecryptable packets <-handshakeChan str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) err = str.Close() Expect(err).ToNot(HaveOccurred()) }() // start the client addr := <-serverAddr sess, err := quic.DialAddr( addr.String(), &tls.Config{InsecureSkipVerify: true, NextProtos: []string{"benchmark"}}, &quic.Config{Versions: []protocol.VersionNumber{version}}, ) Expect(err).ToNot(HaveOccurred()) close(handshakeChan) str, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) buf := &bytes.Buffer{} // measure the time it takes to download the dataLen bytes // note we're measuring the time for the transfer, i.e. excluding the handshake runtime := b.Time("transfer time", func() { _, err := io.Copy(buf, str) Expect(err).NotTo(HaveOccurred()) }) Expect(buf.Bytes()).To(Equal(data)) b.RecordValue("transfer rate [MB/s]", float64(dataLen)/1e6/runtime.Seconds()) ln.Close() sess.CloseWithError(0, "") }, 3) }) } }) quic-go-0.25.0/buffer_pool.go000066400000000000000000000034241417145451600160060ustar00rootroot00000000000000package quic import ( "sync" "github.com/lucas-clemente/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) putBack() { if cap(b.Data) != int(protocol.MaxPacketBufferSize) { panic("putPacketBuffer called with packet of wrong size!") } bufferPool.Put(b) } var bufferPool sync.Pool func getPacketBuffer() *packetBuffer { buf := bufferPool.Get().(*packetBuffer) buf.refCount = 1 buf.Data = buf.Data[:0] return buf } func init() { bufferPool.New = func() interface{} { return &packetBuffer{ Data: make([]byte, 0, protocol.MaxPacketBufferSize), } } } quic-go-0.25.0/buffer_pool_test.go000066400000000000000000000023631417145451600170460ustar00rootroot00000000000000package quic import ( "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Buffer Pool", func() { It("returns buffers of cap", func() { buf := getPacketBuffer() Expect(buf.Data).To(HaveCap(int(protocol.MaxPacketBufferSize))) }) It("releases buffers", func() { buf := getPacketBuffer() buf.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()) }) }) quic-go-0.25.0/client.go000066400000000000000000000222731417145451600147650ustar00rootroot00000000000000package quic import ( "context" "crypto/tls" "errors" "fmt" "net" "strings" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" ) type client struct { conn sendConn // If the client is created with DialAddr, we create a packet conn. // If it is started with Dial, we take a packet conn as a parameter. createdPacketConn bool use0RTT bool packetHandlers packetHandlerManager tlsConf *tls.Config config *Config srcConnID protocol.ConnectionID destConnID protocol.ConnectionID initialPacketNumber protocol.PacketNumber hasNegotiatedVersion bool version protocol.VersionNumber handshakeChan chan struct{} session quicSession tracer logging.ConnectionTracer tracingID uint64 logger utils.Logger } var ( // make it possible to mock connection ID generation in the tests generateConnectionID = protocol.GenerateConnectionID generateConnectionIDForInitial = protocol.GenerateConnectionIDForInitial ) // DialAddr establishes a new QUIC connection to a server. // It uses a new UDP connection and closes this connection when the QUIC session is closed. // The hostname for SNI is taken from the given address. // The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. func DialAddr( addr string, tlsConf *tls.Config, config *Config, ) (Session, error) { return DialAddrContext(context.Background(), addr, tlsConf, config) } // DialAddrEarly establishes a new 0-RTT QUIC connection to a server. // It uses a new UDP connection and closes this connection when the QUIC session is closed. // The hostname for SNI is taken from the given address. // The tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. func DialAddrEarly( addr string, tlsConf *tls.Config, config *Config, ) (EarlySession, error) { return DialAddrEarlyContext(context.Background(), addr, tlsConf, config) } // DialAddrEarlyContext establishes a new 0-RTT QUIC connection to a server using provided context. // See DialAddrEarly for details func DialAddrEarlyContext( ctx context.Context, addr string, tlsConf *tls.Config, config *Config, ) (EarlySession, error) { sess, err := dialAddrContext(ctx, addr, tlsConf, config, true) if err != nil { return nil, err } utils.Logger.WithPrefix(utils.DefaultLogger, "client").Debugf("Returning early session") return sess, nil } // DialAddrContext establishes a new QUIC connection to a server using the provided context. // See DialAddr for details. func DialAddrContext( ctx context.Context, addr string, tlsConf *tls.Config, config *Config, ) (Session, error) { return dialAddrContext(ctx, addr, tlsConf, config, false) } func dialAddrContext( ctx context.Context, addr string, tlsConf *tls.Config, config *Config, use0RTT bool, ) (quicSession, error) { udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err } udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return nil, err } return dialContext(ctx, udpConn, udpAddr, addr, tlsConf, config, use0RTT, true) } // 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 same PacketConn can be used for multiple calls to Dial and // Listen, QUIC connection IDs are used for demultiplexing the different // connections. The host parameter is used for SNI. The tls.Config must define // an application protocol (using NextProtos). func Dial( pconn net.PacketConn, remoteAddr net.Addr, host string, tlsConf *tls.Config, config *Config, ) (Session, error) { return dialContext(context.Background(), pconn, remoteAddr, host, tlsConf, config, false, false) } // DialEarly establishes a new 0-RTT QUIC connection to a server using a net.PacketConn. // The same PacketConn can be used for multiple calls to Dial and Listen, // QUIC connection IDs are used for demultiplexing the different connections. // The host parameter is used for SNI. // The tls.Config must define an application protocol (using NextProtos). func DialEarly( pconn net.PacketConn, remoteAddr net.Addr, host string, tlsConf *tls.Config, config *Config, ) (EarlySession, error) { return DialEarlyContext(context.Background(), pconn, remoteAddr, host, tlsConf, config) } // DialEarlyContext establishes a new 0-RTT QUIC connection to a server using a net.PacketConn using the provided context. // See DialEarly for details. func DialEarlyContext( ctx context.Context, pconn net.PacketConn, remoteAddr net.Addr, host string, tlsConf *tls.Config, config *Config, ) (EarlySession, error) { return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, true, false) } // DialContext establishes a new QUIC connection to a server using a net.PacketConn using the provided context. // See Dial for details. func DialContext( ctx context.Context, pconn net.PacketConn, remoteAddr net.Addr, host string, tlsConf *tls.Config, config *Config, ) (Session, error) { return dialContext(ctx, pconn, remoteAddr, host, tlsConf, config, false, false) } func dialContext( ctx context.Context, pconn net.PacketConn, remoteAddr net.Addr, host string, tlsConf *tls.Config, config *Config, use0RTT bool, createdPacketConn bool, ) (quicSession, error) { if tlsConf == nil { return nil, errors.New("quic: tls.Config not set") } if err := validateConfig(config); err != nil { return nil, err } config = populateClientConfig(config, createdPacketConn) packetHandlers, err := getMultiplexer().AddConn(pconn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) if err != nil { return nil, err } c, err := newClient(pconn, remoteAddr, config, tlsConf, host, use0RTT, createdPacketConn) if err != nil { return nil, err } c.packetHandlers = packetHandlers c.tracingID = nextSessionTracingID() if c.config.Tracer != nil { c.tracer = c.config.Tracer.TracerForConnection( context.WithValue(ctx, SessionTracingKey, c.tracingID), protocol.PerspectiveClient, c.destConnID, ) } if c.tracer != nil { c.tracer.StartedConnection(c.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID) } if err := c.dial(ctx); err != nil { return nil, err } return c.session, nil } func newClient( pconn net.PacketConn, remoteAddr net.Addr, config *Config, tlsConf *tls.Config, host string, use0RTT bool, createdPacketConn bool, ) (*client, error) { if tlsConf == nil { tlsConf = &tls.Config{} } if tlsConf.ServerName == "" { sni := host if strings.IndexByte(sni, ':') != -1 { var err error sni, _, err = net.SplitHostPort(sni) if err != nil { return nil, err } } tlsConf.ServerName = sni } // check that all versions are actually supported if config != nil { for _, v := range config.Versions { if !protocol.IsValidVersion(v) { return nil, fmt.Errorf("%s is not a valid QUIC version", v) } } } srcConnID, err := generateConnectionID(config.ConnectionIDLength) if err != nil { return nil, err } destConnID, err := generateConnectionIDForInitial() if err != nil { return nil, err } c := &client{ srcConnID: srcConnID, destConnID: destConnID, conn: newSendPconn(pconn, remoteAddr), createdPacketConn: createdPacketConn, use0RTT: use0RTT, 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.conn.LocalAddr(), c.conn.RemoteAddr(), c.srcConnID, c.destConnID, c.version) c.session = newClientSession( c.conn, c.packetHandlers, c.destConnID, c.srcConnID, c.config, c.tlsConf, c.initialPacketNumber, c.use0RTT, c.hasNegotiatedVersion, c.tracer, c.tracingID, c.logger, c.version, ) c.packetHandlers.Add(c.srcConnID, c.session) errorChan := make(chan error, 1) go func() { err := c.session.run() // returns as soon as the session is closed if e := (&errCloseForRecreating{}); !errors.As(err, &e) && c.createdPacketConn { c.packetHandlers.Destroy() } errorChan <- err }() // only set when we're using 0-RTT // Otherwise, earlySessionChan will be nil. Receiving from a nil chan blocks forever. var earlySessionChan <-chan struct{} if c.use0RTT { earlySessionChan = c.session.earlySessionReady() } select { case <-ctx.Done(): c.session.shutdown() return ctx.Err() case err := <-errorChan: var recreateErr *errCloseForRecreating if errors.As(err, &recreateErr) { c.initialPacketNumber = recreateErr.nextPacketNumber c.version = recreateErr.nextVersion c.hasNegotiatedVersion = true return c.dial(ctx) } return err case <-earlySessionChan: // ready to send 0-RTT data return nil case <-c.session.HandshakeComplete().Done(): // handshake successfully completed return nil } } quic-go-0.25.0/client_test.go000066400000000000000000000443201417145451600160210ustar00rootroot00000000000000package quic import ( "context" "crypto/tls" "errors" "net" "os" "time" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Client", func() { var ( cl *client packetConn *MockPacketConn addr net.Addr connID protocol.ConnectionID mockMultiplexer *MockMultiplexer origMultiplexer multiplexer tlsConf *tls.Config tracer *mocklogging.MockConnectionTracer config *Config originalClientSessConstructor func( conn sendConn, runner sessionRunner, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, conf *Config, tlsConf *tls.Config, initialPacketNumber protocol.PacketNumber, enable0RTT bool, hasNegotiatedVersion bool, tracer logging.ConnectionTracer, tracingID uint64, logger utils.Logger, v protocol.VersionNumber, ) quicSession ) BeforeEach(func() { tlsConf = &tls.Config{NextProtos: []string{"proto1"}} connID = protocol.ConnectionID{0, 0, 0, 0, 0, 0, 0x13, 0x37} originalClientSessConstructor = newClientSession tracer = mocklogging.NewMockConnectionTracer(mockCtrl) tr := mocklogging.NewMockTracer(mockCtrl) tr.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveClient, gomock.Any()).Return(tracer).MaxTimes(1) config = &Config{Tracer: tr, Versions: []protocol.VersionNumber{protocol.VersionTLS}} Eventually(areSessionsRunning).Should(BeFalse()) // sess = NewMockQuicSession(mockCtrl) addr = &net.UDPAddr{IP: net.IPv4(192, 168, 100, 200), Port: 1337} packetConn = NewMockPacketConn(mockCtrl) packetConn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes() cl = &client{ srcConnID: connID, destConnID: connID, version: protocol.VersionTLS, conn: newSendPconn(packetConn, addr), tracer: tracer, logger: utils.DefaultLogger, } getMultiplexer() // make the sync.Once execute // replace the clientMuxer. getClientMultiplexer will now return the MockMultiplexer mockMultiplexer = NewMockMultiplexer(mockCtrl) origMultiplexer = connMuxer connMuxer = mockMultiplexer }) AfterEach(func() { connMuxer = origMultiplexer newClientSession = originalClientSessConstructor }) AfterEach(func() { if s, ok := cl.session.(*session); ok { s.shutdown() } Eventually(areSessionsRunning).Should(BeFalse()) }) Context("Dialing", func() { var origGenerateConnectionID func(int) (protocol.ConnectionID, error) var origGenerateConnectionIDForInitial func() (protocol.ConnectionID, error) BeforeEach(func() { origGenerateConnectionID = generateConnectionID origGenerateConnectionIDForInitial = generateConnectionIDForInitial generateConnectionID = func(int) (protocol.ConnectionID, error) { return connID, nil } generateConnectionIDForInitial = func() (protocol.ConnectionID, error) { return connID, nil } }) AfterEach(func() { generateConnectionID = origGenerateConnectionID generateConnectionIDForInitial = origGenerateConnectionIDForInitial }) It("resolves the address", func() { if os.Getenv("APPVEYOR") == "True" { Skip("This test is flaky on AppVeyor.") } manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) manager.EXPECT().Destroy() mockMultiplexer.EXPECT().AddConn(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) remoteAddrChan := make(chan string, 1) newClientSession = func( conn sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { remoteAddrChan <- conn.RemoteAddr().String() sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run() sess.EXPECT().HandshakeComplete().Return(context.Background()) return sess } _, err := DialAddr("localhost:17890", tlsConf, &Config{HandshakeIdleTimeout: time.Millisecond}) Expect(err).ToNot(HaveOccurred()) Eventually(remoteAddrChan).Should(Receive(Equal("127.0.0.1:17890"))) }) It("uses the tls.Config.ServerName as the hostname, if present", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) manager.EXPECT().Destroy() mockMultiplexer.EXPECT().AddConn(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) hostnameChan := make(chan string, 1) newClientSession = func( _ sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, tlsConf *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { hostnameChan <- tlsConf.ServerName sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run() sess.EXPECT().HandshakeComplete().Return(context.Background()) return sess } tlsConf.ServerName = "foobar" _, err := DialAddr("localhost:17890", tlsConf, nil) Expect(err).ToNot(HaveOccurred()) Eventually(hostnameChan).Should(Receive(Equal("foobar"))) }) It("allows passing host without port as server name", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) hostnameChan := make(chan string, 1) newClientSession = func( _ sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, tlsConf *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { hostnameChan <- tlsConf.ServerName sess := NewMockQuicSession(mockCtrl) sess.EXPECT().HandshakeComplete().Return(context.Background()) sess.EXPECT().run() return sess } tracer.EXPECT().StartedConnection(packetConn.LocalAddr(), addr, gomock.Any(), gomock.Any()) _, err := Dial( packetConn, addr, "test.com", tlsConf, config, ) Expect(err).ToNot(HaveOccurred()) Eventually(hostnameChan).Should(Receive(Equal("test.com"))) }) It("returns after the handshake is complete", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) run := make(chan struct{}) newClientSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, _ *tls.Config, _ protocol.PacketNumber, enable0RTT bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { Expect(enable0RTT).To(BeFalse()) sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run().Do(func() { close(run) }) ctx, cancel := context.WithCancel(context.Background()) cancel() sess.EXPECT().HandshakeComplete().Return(ctx) return sess } tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) s, err := Dial( packetConn, addr, "localhost:1337", tlsConf, config, ) Expect(err).ToNot(HaveOccurred()) Expect(s).ToNot(BeNil()) Eventually(run).Should(BeClosed()) }) It("returns early sessions", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) readyChan := make(chan struct{}) done := make(chan struct{}) newClientSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, _ *tls.Config, _ protocol.PacketNumber, enable0RTT bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { Expect(enable0RTT).To(BeTrue()) sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run().Do(func() { <-done }) sess.EXPECT().HandshakeComplete().Return(context.Background()) sess.EXPECT().earlySessionReady().Return(readyChan) return sess } go func() { defer GinkgoRecover() defer close(done) tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) s, err := DialEarly( packetConn, addr, "localhost:1337", tlsConf, config, ) Expect(err).ToNot(HaveOccurred()) Expect(s).ToNot(BeNil()) }() Consistently(done).ShouldNot(BeClosed()) close(readyChan) 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()) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) testErr := errors.New("early handshake error") newClientSession = func( _ sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run().Return(testErr) sess.EXPECT().HandshakeComplete().Return(context.Background()) return sess } tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) _, err := Dial( packetConn, addr, "localhost:1337", tlsConf, config, ) Expect(err).To(MatchError(testErr)) }) It("closes the session when the context is canceled", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(gomock.Any(), gomock.Any()) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) sessionRunning := make(chan struct{}) defer close(sessionRunning) sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run().Do(func() { <-sessionRunning }) sess.EXPECT().HandshakeComplete().Return(context.Background()) newClientSession = func( _ sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { return sess } ctx, cancel := context.WithCancel(context.Background()) dialed := make(chan struct{}) go func() { defer GinkgoRecover() tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) _, err := DialContext( ctx, packetConn, addr, "localhost:1337", tlsConf, config, ) Expect(err).To(MatchError(context.Canceled)) close(dialed) }() Consistently(dialed).ShouldNot(BeClosed()) sess.EXPECT().shutdown() cancel() Eventually(dialed).Should(BeClosed()) }) It("closes the connection when it was created by DialAddr", func() { if os.Getenv("APPVEYOR") == "True" { Skip("This test is flaky on AppVeyor.") } manager := NewMockPacketHandlerManager(mockCtrl) mockMultiplexer.EXPECT().AddConn(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) manager.EXPECT().Add(gomock.Any(), gomock.Any()) var conn sendConn run := make(chan struct{}) sessionCreated := make(chan struct{}) sess := NewMockQuicSession(mockCtrl) newClientSession = func( connP sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, _ *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { conn = connP close(sessionCreated) return sess } sess.EXPECT().run().Do(func() { <-run }) sess.EXPECT().HandshakeComplete().Return(context.Background()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := DialAddr("localhost:1337", tlsConf, nil) Expect(err).ToNot(HaveOccurred()) close(done) }() Eventually(sessionCreated).Should(BeClosed()) // check that the connection is not closed Expect(conn.Write([]byte("foobar"))).To(Succeed()) manager.EXPECT().Destroy() close(run) time.Sleep(50 * time.Millisecond) Eventually(done).Should(BeClosed()) }) 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, ConnectionIDLength: 13, StatelessResetKey: []byte("foobar"), TokenStore: tokenStore, EnableDatagrams: true, } c := populateClientConfig(config, false) 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.ConnectionIDLength).To(Equal(13)) Expect(c.StatelessResetKey).To(Equal([]byte("foobar"))) Expect(c.TokenStore).To(Equal(tokenStore)) Expect(c.EnableDatagrams).To(BeTrue()) }) It("errors when the Config contains an invalid version", func() { manager := NewMockPacketHandlerManager(mockCtrl) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) version := protocol.VersionNumber(0x1234) _, err := Dial(packetConn, nil, "localhost:1234", tlsConf, &Config{Versions: []protocol.VersionNumber{version}}) Expect(err).To(MatchError("0x1234 is not a valid QUIC version")) }) It("disables bidirectional streams", func() { config := &Config{ MaxIncomingStreams: -1, MaxIncomingUniStreams: 4321, } c := populateClientConfig(config, false) Expect(c.MaxIncomingStreams).To(BeZero()) Expect(c.MaxIncomingUniStreams).To(BeEquivalentTo(4321)) }) It("disables unidirectional streams", func() { config := &Config{ MaxIncomingStreams: 1234, MaxIncomingUniStreams: -1, } c := populateClientConfig(config, false) Expect(c.MaxIncomingStreams).To(BeEquivalentTo(1234)) Expect(c.MaxIncomingUniStreams).To(BeZero()) }) It("uses 0-byte connection IDs when dialing an address", func() { c := populateClientConfig(&Config{}, true) Expect(c.ConnectionIDLength).To(BeZero()) }) It("fills in default values if options are not set in the Config", func() { c := populateClientConfig(&Config{}, false) Expect(c.Versions).To(Equal(protocol.SupportedVersions)) Expect(c.HandshakeIdleTimeout).To(Equal(protocol.DefaultHandshakeIdleTimeout)) Expect(c.MaxIdleTimeout).To(Equal(protocol.DefaultIdleTimeout)) }) }) It("creates new sessions with the right parameters", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(connID, gomock.Any()) mockMultiplexer.EXPECT().AddConn(packetConn, gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) config := &Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}} c := make(chan struct{}) var cconn sendConn var version protocol.VersionNumber var conf *Config newClientSession = func( connP sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, configP *Config, _ *tls.Config, _ protocol.PacketNumber, _ bool, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, versionP protocol.VersionNumber, ) quicSession { cconn = connP version = versionP conf = configP close(c) // TODO: check connection IDs? sess := NewMockQuicSession(mockCtrl) sess.EXPECT().run() sess.EXPECT().HandshakeComplete().Return(context.Background()) return sess } _, err := Dial(packetConn, addr, "localhost:1337", tlsConf, config) Expect(err).ToNot(HaveOccurred()) Eventually(c).Should(BeClosed()) Expect(cconn.(*spconn).PacketConn).To(Equal(packetConn)) Expect(version).To(Equal(config.Versions[0])) Expect(conf.Versions).To(Equal(config.Versions)) }) It("creates a new session after version negotiation", func() { manager := NewMockPacketHandlerManager(mockCtrl) manager.EXPECT().Add(connID, gomock.Any()).Times(2) manager.EXPECT().Destroy() mockMultiplexer.EXPECT().AddConn(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(manager, nil) var counter int newClientSession = func( _ sendConn, _ sessionRunner, _ protocol.ConnectionID, _ protocol.ConnectionID, configP *Config, _ *tls.Config, pn protocol.PacketNumber, _ bool, hasNegotiatedVersion bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, versionP protocol.VersionNumber, ) quicSession { sess := NewMockQuicSession(mockCtrl) sess.EXPECT().HandshakeComplete().Return(context.Background()) if counter == 0 { Expect(pn).To(BeZero()) Expect(hasNegotiatedVersion).To(BeFalse()) sess.EXPECT().run().Return(&errCloseForRecreating{ nextPacketNumber: 109, nextVersion: 789, }) } else { Expect(pn).To(Equal(protocol.PacketNumber(109))) Expect(hasNegotiatedVersion).To(BeTrue()) sess.EXPECT().run() } counter++ return sess } tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) _, err := DialAddr("localhost:7890", tlsConf, config) Expect(err).ToNot(HaveOccurred()) Expect(counter).To(Equal(2)) }) }) }) quic-go-0.25.0/closed_session.go000066400000000000000000000056511417145451600165240ustar00rootroot00000000000000package quic import ( "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) // A closedLocalSession is a session that we closed locally. // When receiving packets for such a session, we need to retransmit the packet containing the CONNECTION_CLOSE frame, // with an exponential backoff. type closedLocalSession struct { conn sendConn connClosePacket []byte closeOnce sync.Once closeChan chan struct{} // is closed when the session is closed or destroyed receivedPackets chan *receivedPacket counter uint64 // number of packets received perspective protocol.Perspective logger utils.Logger } var _ packetHandler = &closedLocalSession{} // newClosedLocalSession creates a new closedLocalSession and runs it. func newClosedLocalSession( conn sendConn, connClosePacket []byte, perspective protocol.Perspective, logger utils.Logger, ) packetHandler { s := &closedLocalSession{ conn: conn, connClosePacket: connClosePacket, perspective: perspective, logger: logger, closeChan: make(chan struct{}), receivedPackets: make(chan *receivedPacket, 64), } go s.run() return s } func (s *closedLocalSession) run() { for { select { case p := <-s.receivedPackets: s.handlePacketImpl(p) case <-s.closeChan: return } } } func (s *closedLocalSession) handlePacket(p *receivedPacket) { select { case s.receivedPackets <- p: default: } } func (s *closedLocalSession) handlePacketImpl(_ *receivedPacket) { s.counter++ // exponential backoff // only send a CONNECTION_CLOSE for the 1st, 2nd, 4th, 8th, 16th, ... packet arriving for n := s.counter; n > 1; n = n / 2 { if n%2 != 0 { return } } s.logger.Debugf("Received %d packets after sending CONNECTION_CLOSE. Retransmitting.", s.counter) if err := s.conn.Write(s.connClosePacket); err != nil { s.logger.Debugf("Error retransmitting CONNECTION_CLOSE: %s", err) } } func (s *closedLocalSession) shutdown() { s.destroy(nil) } func (s *closedLocalSession) destroy(error) { s.closeOnce.Do(func() { close(s.closeChan) }) } func (s *closedLocalSession) getPerspective() protocol.Perspective { return s.perspective } // A closedRemoteSession is a session that was closed remotely. // For such a session, we might receive reordered packets that were sent before the CONNECTION_CLOSE. // We can just ignore those packets. type closedRemoteSession struct { perspective protocol.Perspective } var _ packetHandler = &closedRemoteSession{} func newClosedRemoteSession(pers protocol.Perspective) packetHandler { return &closedRemoteSession{perspective: pers} } func (s *closedRemoteSession) handlePacket(*receivedPacket) {} func (s *closedRemoteSession) shutdown() {} func (s *closedRemoteSession) destroy(error) {} func (s *closedRemoteSession) getPerspective() protocol.Perspective { return s.perspective } quic-go-0.25.0/closed_session_test.go000066400000000000000000000027111417145451600175550ustar00rootroot00000000000000package quic import ( "errors" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Closed local session", func() { var ( sess packetHandler mconn *MockSendConn ) BeforeEach(func() { mconn = NewMockSendConn(mockCtrl) sess = newClosedLocalSession(mconn, []byte("close"), protocol.PerspectiveClient, utils.DefaultLogger) }) AfterEach(func() { Eventually(areClosedSessionsRunning).Should(BeFalse()) }) It("tells its perspective", func() { Expect(sess.getPerspective()).To(Equal(protocol.PerspectiveClient)) // stop the session sess.shutdown() }) It("repeats the packet containing the CONNECTION_CLOSE frame", func() { written := make(chan []byte) mconn.EXPECT().Write(gomock.Any()).Do(func(p []byte) { written <- p }).AnyTimes() for i := 1; i <= 20; i++ { sess.handlePacket(&receivedPacket{}) if i == 1 || i == 2 || i == 4 || i == 8 || i == 16 { Eventually(written).Should(Receive(Equal([]byte("close")))) // receive the CONNECTION_CLOSE } else { Consistently(written, 10*time.Millisecond).Should(HaveLen(0)) } } // stop the session sess.shutdown() }) It("destroys sessions", func() { Eventually(areClosedSessionsRunning).Should(BeTrue()) sess.destroy(errors.New("destroy")) Eventually(areClosedSessionsRunning).Should(BeFalse()) }) }) quic-go-0.25.0/codecov.yml000066400000000000000000000010671417145451600153230ustar00rootroot00000000000000coverage: round: nearest ignore: - streams_map_incoming_bidi.go - streams_map_incoming_uni.go - streams_map_outgoing_bidi.go - streams_map_outgoing_uni.go - http3/gzip_reader.go - interop/ - internal/ackhandler/packet_linkedlist.go - internal/utils/byteinterval_linkedlist.go - internal/utils/newconnectionid_linkedlist.go - internal/utils/packetinterval_linkedlist.go - internal/utils/linkedlist/linkedlist.go - fuzzing/ - metrics/ status: project: default: threshold: 0.5 patch: false quic-go-0.25.0/config.go000066400000000000000000000102671417145451600147540ustar00rootroot00000000000000package quic import ( "errors" "time" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/protocol" ) // Clone clones a Config func (c *Config) Clone() *Config { copy := *c return © } func (c *Config) handshakeTimeout() time.Duration { return utils.MaxDuration(protocol.DefaultHandshakeTimeout, 2*c.HandshakeIdleTimeout) } func validateConfig(config *Config) error { if config == nil { return nil } if config.MaxIncomingStreams > 1<<60 { return errors.New("invalid value for Config.MaxIncomingStreams") } if config.MaxIncomingUniStreams > 1<<60 { return errors.New("invalid value for Config.MaxIncomingUniStreams") } return nil } // populateServerConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil func populateServerConfig(config *Config) *Config { config = populateConfig(config) if config.ConnectionIDLength == 0 { config.ConnectionIDLength = protocol.DefaultConnectionIDLength } if config.AcceptToken == nil { config.AcceptToken = defaultAcceptToken } return config } // populateClientConfig populates fields in the quic.Config with their default values, if none are set // it may be called with nil func populateClientConfig(config *Config, createdPacketConn bool) *Config { config = populateConfig(config) if config.ConnectionIDLength == 0 && !createdPacketConn { config.ConnectionIDLength = protocol.DefaultConnectionIDLength } return config } 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 } return &Config{ Versions: versions, HandshakeIdleTimeout: handshakeIdleTimeout, MaxIdleTimeout: idleTimeout, AcceptToken: config.AcceptToken, KeepAlive: config.KeepAlive, InitialStreamReceiveWindow: initialStreamReceiveWindow, MaxStreamReceiveWindow: maxStreamReceiveWindow, InitialConnectionReceiveWindow: initialConnectionReceiveWindow, MaxConnectionReceiveWindow: maxConnectionReceiveWindow, AllowConnectionWindowIncrease: config.AllowConnectionWindowIncrease, MaxIncomingStreams: maxIncomingStreams, MaxIncomingUniStreams: maxIncomingUniStreams, ConnectionIDLength: config.ConnectionIDLength, StatelessResetKey: config.StatelessResetKey, TokenStore: config.TokenStore, EnableDatagrams: config.EnableDatagrams, DisablePathMTUDiscovery: config.DisablePathMTUDiscovery, DisableVersionNegotiationPackets: config.DisableVersionNegotiationPackets, Tracer: config.Tracer, } } quic-go-0.25.0/config_test.go000066400000000000000000000143441417145451600160130ustar00rootroot00000000000000package quic import ( "fmt" "net" "reflect" "time" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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() { Expect(validateConfig(populateServerConfig(&Config{}))).To(Succeed()) }) It("errors on too large values for MaxIncomingStreams", func() { Expect(validateConfig(&Config{MaxIncomingStreams: 1<<60 + 1})).To(MatchError("invalid value for Config.MaxIncomingStreams")) }) It("errors on too large values for MaxIncomingUniStreams", func() { Expect(validateConfig(&Config{MaxIncomingUniStreams: 1<<60 + 1})).To(MatchError("invalid value for Config.MaxIncomingUniStreams")) }) }) 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 "AcceptToken", "GetLogWriter", "AllowConnectionWindowIncrease": // Can't compare functions. case "Versions": f.Set(reflect.ValueOf([]VersionNumber{1, 2, 3})) case "ConnectionIDLength": f.Set(reflect.ValueOf(8)) 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([]byte{1, 2, 3, 4})) case "KeepAlive": f.Set(reflect.ValueOf(true)) case "EnableDatagrams": f.Set(reflect.ValueOf(true)) case "DisableVersionNegotiationPackets": f.Set(reflect.ValueOf(true)) case "DisablePathMTUDiscovery": f.Set(reflect.ValueOf(true)) case "Tracer": f.Set(reflect.ValueOf(mocklogging.NewMockTracer(mockCtrl))) default: Fail(fmt.Sprintf("all fields must be accounted for, but saw unknown field %q", fn)) } } return c } It("uses 10s handshake timeout for short handshake idle timeouts", func() { c := &Config{HandshakeIdleTimeout: time.Second} Expect(c.handshakeTimeout()).To(Equal(protocol.DefaultHandshakeTimeout)) }) It("uses twice the handshake idle timeouts for the handshake timeout, for long handshake idle timeouts", 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 calledAcceptToken, calledAllowConnectionWindowIncrease bool c1 := &Config{ AcceptToken: func(_ net.Addr, _ *Token) bool { calledAcceptToken = true; return true }, AllowConnectionWindowIncrease: func(Session, uint64) bool { calledAllowConnectionWindowIncrease = true; return true }, } c2 := c1.Clone() c2.AcceptToken(&net.UDPAddr{}, &Token{}) Expect(calledAcceptToken).To(BeTrue()) c2.AllowConnectionWindowIncrease(nil, 1234) Expect(calledAllowConnectionWindowIncrease).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, AcceptToken: func(_ net.Addr, _ *Token) bool { return true }, } c2 := c1.Clone() c2.MaxIncomingStreams = 200 c2.AcceptToken = func(_ net.Addr, _ *Token) bool { return false } Expect(c1.MaxIncomingStreams).To(BeEquivalentTo(100)) Expect(c1.AcceptToken(&net.UDPAddr{}, nil)).To(BeTrue()) }) }) Context("populating", func() { It("populates function fields", func() { var calledAcceptToken bool c1 := &Config{ AcceptToken: func(_ net.Addr, _ *Token) bool { calledAcceptToken = true; return true }, } c2 := populateConfig(c1) c2.AcceptToken(&net.UDPAddr{}, &Token{}) Expect(calledAcceptToken).To(BeTrue()) }) 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.DisableVersionNegotiationPackets).To(BeFalse()) Expect(c.DisablePathMTUDiscovery).To(BeFalse()) }) It("populates empty fields with default values, for the server", func() { c := populateServerConfig(&Config{}) Expect(c.ConnectionIDLength).To(Equal(protocol.DefaultConnectionIDLength)) Expect(c.AcceptToken).ToNot(BeNil()) }) It("sets a default connection ID length if we didn't create the conn, for the client", func() { c := populateClientConfig(&Config{}, false) Expect(c.ConnectionIDLength).To(Equal(protocol.DefaultConnectionIDLength)) }) It("doesn't set a default connection ID length if we created the conn, for the client", func() { c := populateClientConfig(&Config{}, true) Expect(c.ConnectionIDLength).To(BeZero()) }) }) }) quic-go-0.25.0/conn.go000066400000000000000000000034701417145451600144420ustar00rootroot00000000000000package quic import ( "io" "net" "syscall" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) type connection interface { ReadPacket() (*receivedPacket, error) WritePacket(b []byte, addr net.Addr, oob []byte) (int, error) LocalAddr() net.Addr io.Closer } // If the PacketConn passed to Dial or Listen satisfies this interface, quic-go will read the ECN bits from the IP header. // In this case, ReadMsgUDP() will be used instead of ReadFrom() to read packets. type OOBCapablePacketConn interface { net.PacketConn SyscallConn() (syscall.RawConn, error) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) } var _ OOBCapablePacketConn = &net.UDPConn{} func wrapConn(pc net.PacketConn) (connection, error) { c, ok := pc.(OOBCapablePacketConn) if !ok { utils.DefaultLogger.Infof("PacketConn is not a net.UDPConn. Disabling optimizations possible on UDP connections.") return &basicConn{PacketConn: pc}, nil } return newConn(c) } type basicConn struct { net.PacketConn } var _ connection = &basicConn{} func (c *basicConn) ReadPacket() (*receivedPacket, error) { buffer := getPacketBuffer() // The packet size should not exceed protocol.MaxPacketBufferSize bytes // If it does, we only read a truncated packet, which will then end up undecryptable buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize] n, addr, err := c.PacketConn.ReadFrom(buffer.Data) if err != nil { return nil, err } return &receivedPacket{ remoteAddr: addr, rcvTime: time.Now(), data: buffer.Data[:n], buffer: buffer, }, nil } func (c *basicConn) WritePacket(b []byte, addr net.Addr, _ []byte) (n int, err error) { return c.PacketConn.WriteTo(b, addr) } quic-go-0.25.0/conn_generic.go000066400000000000000000000005711417145451600161350ustar00rootroot00000000000000//go:build !darwin && !linux && !freebsd && !windows // +build !darwin,!linux,!freebsd,!windows package quic import "net" const disablePathMTUDiscovery = false func newConn(c net.PacketConn) (connection, error) { return &basicConn{PacketConn: c}, nil } func inspectReadBuffer(interface{}) (int, error) { return 0, nil } func (i *packetInfo) OOB() []byte { return nil } quic-go-0.25.0/conn_helper_darwin.go000066400000000000000000000007121417145451600173410ustar00rootroot00000000000000//go:build darwin // +build darwin package quic import "golang.org/x/sys/unix" const ( msgTypeIPTOS = unix.IP_RECVTOS disablePathMTUDiscovery = false ) const ( ipv4RECVPKTINFO = unix.IP_RECVPKTINFO ipv6RECVPKTINFO = 0x3d ) const ( msgTypeIPv4PKTINFO = unix.IP_PKTINFO msgTypeIPv6PKTINFO = 0x2e ) // ReadBatch only returns a single packet on OSX, // see https://godoc.org/golang.org/x/net/ipv4#PacketConn.ReadBatch. const batchSize = 1 quic-go-0.25.0/conn_helper_freebsd.go000066400000000000000000000004711417145451600174710ustar00rootroot00000000000000//go:build freebsd // +build freebsd package quic import "golang.org/x/sys/unix" const ( msgTypeIPTOS = unix.IP_RECVTOS disablePathMTUDiscovery = false ) const ( ipv4RECVPKTINFO = 0x7 ipv6RECVPKTINFO = 0x24 ) const ( msgTypeIPv4PKTINFO = 0x7 msgTypeIPv6PKTINFO = 0x2e ) const batchSize = 8 quic-go-0.25.0/conn_helper_linux.go000066400000000000000000000007031417145451600172140ustar00rootroot00000000000000//go:build linux // +build linux package quic import "golang.org/x/sys/unix" const ( msgTypeIPTOS = unix.IP_TOS disablePathMTUDiscovery = false ) const ( ipv4RECVPKTINFO = unix.IP_PKTINFO ipv6RECVPKTINFO = unix.IPV6_RECVPKTINFO ) const ( msgTypeIPv4PKTINFO = unix.IP_PKTINFO msgTypeIPv6PKTINFO = unix.IPV6_PKTINFO ) const batchSize = 8 // needs to smaller than MaxUint8 (otherwise the type of oobConn.readPos has to be changed) quic-go-0.25.0/conn_id_generator.go000066400000000000000000000106441417145451600171650ustar00rootroot00000000000000package quic import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type connIDGenerator struct { connIDLen int highestSeq uint64 activeSrcConnIDs map[uint64]protocol.ConnectionID initialClientDestConnID protocol.ConnectionID addConnectionID func(protocol.ConnectionID) getStatelessResetToken func(protocol.ConnectionID) protocol.StatelessResetToken removeConnectionID func(protocol.ConnectionID) retireConnectionID func(protocol.ConnectionID) replaceWithClosed func(protocol.ConnectionID, packetHandler) queueControlFrame func(wire.Frame) version protocol.VersionNumber } 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, packetHandler), queueControlFrame func(wire.Frame), version protocol.VersionNumber, ) *connIDGenerator { m := &connIDGenerator{ connIDLen: initialConnectionID.Len(), activeSrcConnIDs: make(map[uint64]protocol.ConnectionID), addConnectionID: addConnectionID, getStatelessResetToken: getStatelessResetToken, removeConnectionID: removeConnectionID, retireConnectionID: retireConnectionID, replaceWithClosed: replaceWithClosed, queueControlFrame: queueControlFrame, version: version, } m.activeSrcConnIDs[0] = initialConnectionID m.initialClientDestConnID = initialClientDestConnID return m } func (m *connIDGenerator) SetMaxActiveConnIDs(limit uint64) error { if m.connIDLen == 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 < utils.MinUint64(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.Equal(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 := protocol.GenerateConnectionID(m.connIDLen) 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(handler packetHandler) { if m.initialClientDestConnID != nil { m.replaceWithClosed(m.initialClientDestConnID, handler) } for _, connID := range m.activeSrcConnIDs { m.replaceWithClosed(connID, handler) } } quic-go-0.25.0/conn_id_generator_test.go000066400000000000000000000164511417145451600202260ustar00rootroot00000000000000package quic import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID Generator", func() { var ( addedConnIDs []protocol.ConnectionID retiredConnIDs []protocol.ConnectionID removedConnIDs []protocol.ConnectionID replacedWithClosed map[string]packetHandler queuedFrames []wire.Frame g *connIDGenerator ) initialConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7} initialClientDestConnID := protocol.ConnectionID{0xa, 0xb, 0xc, 0xd, 0xe} connIDToToken := func(c protocol.ConnectionID) protocol.StatelessResetToken { return protocol.StatelessResetToken{c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0], c[0]} } BeforeEach(func() { addedConnIDs = nil retiredConnIDs = nil removedConnIDs = nil queuedFrames = nil replacedWithClosed = make(map[string]packetHandler) 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(c protocol.ConnectionID, h packetHandler) { replacedWithClosed[string(c)] = h }, func(f wire.Frame) { queuedFrames = append(queuedFrames, f) }, protocol.VersionDraft29, ) }) 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 we 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 session for all connection IDs", func() { Expect(g.SetMaxActiveConnIDs(5)).To(Succeed()) Expect(queuedFrames).To(HaveLen(4)) sess := NewMockPacketHandler(mockCtrl) g.ReplaceWithClosed(sess) Expect(replacedWithClosed).To(HaveLen(6)) // initial conn ID, initial client dest conn id, and newly issued ones Expect(replacedWithClosed).To(HaveKeyWithValue(string(initialClientDestConnID), sess)) Expect(replacedWithClosed).To(HaveKeyWithValue(string(initialConnID), sess)) for _, f := range queuedFrames { nf := f.(*wire.NewConnectionIDFrame) Expect(replacedWithClosed).To(HaveKeyWithValue(string(nf.ConnectionID), sess)) } }) }) quic-go-0.25.0/conn_id_manager.go000066400000000000000000000150221417145451600166040ustar00rootroot00000000000000package quic import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type connIDManager struct { queue utils.NewConnectionIDList 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 *utils.NewConnectionIDElement 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(utils.NewConnectionID{ 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.Equal(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(utils.NewConnectionID{ SequenceNumber: seq, ConnectionID: connID, StatelessResetToken: resetToken, }, el) break } } return nil } func (h *connIDManager) updateConnectionID() { h.queueControlFrame(&wire.RetireConnectionIDFrame{ SequenceNumber: h.activeSequenceNumber, }) h.highestRetired = utils.MaxUint64(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 } quic-go-0.25.0/conn_id_manager_test.go000066400000000000000000000337341417145451600176550ustar00rootroot00000000000000package quic import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID Manager", func() { var ( m *connIDManager frameQueue []wire.Frame tokenAdded *protocol.StatelessResetToken removedTokens []protocol.StatelessResetToken ) initialConnID := protocol.ConnectionID{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 nil, 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.ConnectionID{1, 2, 3, 4, 5}) Expect(m.Get()).To(Equal(protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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(BeNil()) }) It("accepts duplicates", func() { f1 := &wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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(BeNil()) }) It("ignores duplicates for the currently used connection ID", func() { f := &wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ConnectionID{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.ConnectionID{1, 2, 3, 4})) c, _ := get() Expect(c).To(BeNil()) // Now send the same connection ID again. It should not be queued. Expect(m.Add(f)).To(Succeed()) c, _ = get() Expect(c).To(BeNil()) }) It("rejects duplicates with different connection IDs", func() { Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 42, ConnectionID: protocol.ConnectionID{1, 2, 3, 4}, })).To(Succeed()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 42, ConnectionID: protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{1, 2, 3, 4}, })).To(Succeed()) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 13, ConnectionID: protocol.ConnectionID{2, 3, 4, 5}, })).To(Succeed()) Expect(frameQueue).To(BeEmpty()) Expect(m.Add(&wire.NewConnectionIDFrame{ RetirePriorTo: 14, SequenceNumber: 17, ConnectionID: protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{0xde, 0xad, 0xbe, 0xef})) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 9, ConnectionID: protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{1, 1, 1, 1})) var counter int for i := 0; i < 50*protocol.PacketsPerConnectionID; i++ { m.SentPacket() connID := m.Get() if !connID.Equal(lastConnID) { counter++ lastConnID = connID Expect(removedTokens).To(HaveLen(1)) removedTokens = nil Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: uint64(s), ConnectionID: protocol.ConnectionID{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.ConnectionID{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.ConnectionID{10, 10, 10, 10})) for { m.SentPacket() if m.Get().Equal(protocol.ConnectionID{11, 11, 11, 11}) { break } } // The active conn ID is now {11, 11, 11, 11} Expect(m.queue.Front().Value.ConnectionID).To(Equal(protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{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.ConnectionID{1, 1, 1, 1})) for i := 0; i < 2*protocol.PacketsPerConnectionID; i++ { m.SentPacket() } Expect(m.Get()).To(Equal(protocol.ConnectionID{1, 1, 1, 1})) Expect(m.Add(&wire.NewConnectionIDFrame{ SequenceNumber: 1337, ConnectionID: protocol.ConnectionID{1, 3, 3, 7}, })).To(Succeed()) Expect(m.Get()).To(Equal(protocol.ConnectionID{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.ConnectionID{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.ConnectionID{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})) }) }) quic-go-0.25.0/conn_oob.go000066400000000000000000000167641417145451600153130ustar00rootroot00000000000000//go:build darwin || linux || freebsd // +build darwin linux freebsd package quic import ( "encoding/binary" "errors" "fmt" "net" "syscall" "time" "golang.org/x/net/ipv4" "golang.org/x/net/ipv6" "golang.org/x/sys/unix" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) const ( ecnMask = 0x3 oobBufferSize = 128 ) // Contrary to what the naming suggests, the ipv{4,6}.Message is not dependent on the IP version. // They're both just aliases for x/net/internal/socket.Message. // This means we can use this struct to read from a socket that receives both IPv4 and IPv6 messages. var _ ipv4.Message = ipv6.Message{} type batchConn interface { ReadBatch(ms []ipv4.Message, flags int) (int, error) } func inspectReadBuffer(c interface{}) (int, error) { conn, ok := c.(interface { SyscallConn() (syscall.RawConn, error) }) if !ok { return 0, errors.New("doesn't have a SyscallConn") } rawConn, err := conn.SyscallConn() if err != nil { return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err) } var size int var serr error if err := rawConn.Control(func(fd uintptr) { size, serr = unix.GetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_RCVBUF) }); err != nil { return 0, err } return size, serr } type oobConn struct { OOBCapablePacketConn batchConn batchConn readPos uint8 // Packets received from the kernel, but not yet returned by ReadPacket(). messages []ipv4.Message buffers [batchSize]*packetBuffer } var _ connection = &oobConn{} func newConn(c OOBCapablePacketConn) (*oobConn, error) { rawConn, err := c.SyscallConn() if err != nil { return nil, err } needsPacketInfo := false if udpAddr, ok := c.LocalAddr().(*net.UDPAddr); ok && udpAddr.IP.IsUnspecified() { needsPacketInfo = true } // We don't know if this a IPv4-only, IPv6-only or a IPv4-and-IPv6 connection. // Try enabling receiving of ECN and packet info for both IP versions. // We expect at least one of those syscalls to succeed. var errECNIPv4, errECNIPv6, errPIIPv4, errPIIPv6 error if err := rawConn.Control(func(fd uintptr) { errECNIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_RECVTOS, 1) errECNIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_RECVTCLASS, 1) if needsPacketInfo { errPIIPv4 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, ipv4RECVPKTINFO, 1) errPIIPv6 = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, ipv6RECVPKTINFO, 1) } }); err != nil { return nil, err } switch { case errECNIPv4 == nil && errECNIPv6 == nil: utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4 and IPv6.") case errECNIPv4 == nil && errECNIPv6 != nil: utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv4.") case errECNIPv4 != nil && errECNIPv6 == nil: utils.DefaultLogger.Debugf("Activating reading of ECN bits for IPv6.") case errECNIPv4 != nil && errECNIPv6 != nil: return nil, errors.New("activating ECN failed for both IPv4 and IPv6") } if needsPacketInfo { switch { case errPIIPv4 == nil && errPIIPv6 == nil: utils.DefaultLogger.Debugf("Activating reading of packet info for IPv4 and IPv6.") case errPIIPv4 == nil && errPIIPv6 != nil: utils.DefaultLogger.Debugf("Activating reading of packet info bits for IPv4.") case errPIIPv4 != nil && errPIIPv6 == nil: utils.DefaultLogger.Debugf("Activating reading of packet info bits for IPv6.") case errPIIPv4 != nil && errPIIPv6 != nil: return nil, errors.New("activating packet info failed for both IPv4 and IPv6") } } // Allows callers to pass in a connection that already satisfies batchConn interface // to make use of the optimisation. Otherwise, ipv4.NewPacketConn would unwrap the file descriptor // via SyscallConn(), and read it that way, which might not be what the caller wants. var bc batchConn if ibc, ok := c.(batchConn); ok { bc = ibc } else { bc = ipv4.NewPacketConn(c) } oobConn := &oobConn{ OOBCapablePacketConn: c, batchConn: bc, messages: make([]ipv4.Message, batchSize), readPos: batchSize, } for i := 0; i < batchSize; i++ { oobConn.messages[i].OOB = make([]byte, oobBufferSize) } return oobConn, nil } func (c *oobConn) ReadPacket() (*receivedPacket, error) { if len(c.messages) == int(c.readPos) { // all messages read. Read the next batch of messages. c.messages = c.messages[:batchSize] // replace buffers data buffers up to the packet that has been consumed during the last ReadBatch call for i := uint8(0); i < c.readPos; i++ { buffer := getPacketBuffer() buffer.Data = buffer.Data[:protocol.MaxPacketBufferSize] c.buffers[i] = buffer c.messages[i].Buffers = [][]byte{c.buffers[i].Data} } c.readPos = 0 n, err := c.batchConn.ReadBatch(c.messages, 0) if n == 0 || err != nil { return nil, err } c.messages = c.messages[:n] } msg := c.messages[c.readPos] buffer := c.buffers[c.readPos] c.readPos++ ctrlMsgs, err := unix.ParseSocketControlMessage(msg.OOB[:msg.NN]) if err != nil { return nil, err } var ecn protocol.ECN var destIP net.IP var ifIndex uint32 for _, ctrlMsg := range ctrlMsgs { if ctrlMsg.Header.Level == unix.IPPROTO_IP { switch ctrlMsg.Header.Type { case msgTypeIPTOS: ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask) case msgTypeIPv4PKTINFO: // struct in_pktinfo { // unsigned int ipi_ifindex; /* Interface index */ // struct in_addr ipi_spec_dst; /* Local address */ // struct in_addr ipi_addr; /* Header Destination // address */ // }; ip := make([]byte, 4) if len(ctrlMsg.Data) == 12 { ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data) copy(ip, ctrlMsg.Data[8:12]) } else if len(ctrlMsg.Data) == 4 { // FreeBSD copy(ip, ctrlMsg.Data) } destIP = net.IP(ip) } } if ctrlMsg.Header.Level == unix.IPPROTO_IPV6 { switch ctrlMsg.Header.Type { case unix.IPV6_TCLASS: ecn = protocol.ECN(ctrlMsg.Data[0] & ecnMask) case msgTypeIPv6PKTINFO: // struct in6_pktinfo { // struct in6_addr ipi6_addr; /* src/dst IPv6 address */ // unsigned int ipi6_ifindex; /* send/recv interface index */ // }; if len(ctrlMsg.Data) == 20 { ip := make([]byte, 16) copy(ip, ctrlMsg.Data[:16]) destIP = net.IP(ip) ifIndex = binary.LittleEndian.Uint32(ctrlMsg.Data[16:]) } } } } var info *packetInfo if destIP != nil { info = &packetInfo{ addr: destIP, ifIndex: ifIndex, } } return &receivedPacket{ remoteAddr: msg.Addr, rcvTime: time.Now(), data: msg.Buffers[0][:msg.N], ecn: ecn, info: info, buffer: buffer, }, nil } func (c *oobConn) WritePacket(b []byte, addr net.Addr, oob []byte) (n int, err error) { n, _, err = c.OOBCapablePacketConn.WriteMsgUDP(b, oob, addr.(*net.UDPAddr)) return n, err } func (info *packetInfo) OOB() []byte { if info == nil { return nil } if ip4 := info.addr.To4(); ip4 != nil { // struct in_pktinfo { // unsigned int ipi_ifindex; /* Interface index */ // struct in_addr ipi_spec_dst; /* Local address */ // struct in_addr ipi_addr; /* Header Destination address */ // }; cm := ipv4.ControlMessage{ Src: ip4, IfIndex: int(info.ifIndex), } return cm.Marshal() } else if len(info.addr) == 16 { // struct in6_pktinfo { // struct in6_addr ipi6_addr; /* src/dst IPv6 address */ // unsigned int ipi6_ifindex; /* send/recv interface index */ // }; cm := ipv6.ControlMessage{ Src: info.addr, IfIndex: int(info.ifIndex), } return cm.Marshal() } return nil } quic-go-0.25.0/conn_oob_test.go000066400000000000000000000161611417145451600163410ustar00rootroot00000000000000//go:build !windows // +build !windows package quic import ( "fmt" "net" "time" "golang.org/x/net/ipv4" "golang.org/x/sys/unix" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("OOB Conn Test", func() { runServer := func(network, address string) (*net.UDPConn, <-chan *receivedPacket) { addr, err := net.ResolveUDPAddr(network, address) Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP(network, addr) Expect(err).ToNot(HaveOccurred()) oobConn, err := newConn(udpConn) Expect(err).ToNot(HaveOccurred()) packetChan := make(chan *receivedPacket) go func() { defer GinkgoRecover() for { p, err := oobConn.ReadPacket() if err != nil { return } packetChan <- p } }() return udpConn, packetChan } Context("ECN conn", func() { sendPacketWithECN := func(network string, addr *net.UDPAddr, setECN func(uintptr)) net.Addr { conn, err := net.DialUDP(network, nil, addr) ExpectWithOffset(1, err).ToNot(HaveOccurred()) rawConn, err := conn.SyscallConn() ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, rawConn.Control(func(fd uintptr) { setECN(fd) })).To(Succeed()) _, err = conn.Write([]byte("foobar")) ExpectWithOffset(1, err).ToNot(HaveOccurred()) return conn.LocalAddr() } It("reads ECN flags on IPv4", func() { conn, packetChan := runServer("udp4", "localhost:0") defer conn.Close() sentFrom := sendPacketWithECN( "udp4", conn.LocalAddr().(*net.UDPAddr), func(fd uintptr) { Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 2)).To(Succeed()) }, ) var p *receivedPacket Eventually(packetChan).Should(Receive(&p)) Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond))) Expect(p.data).To(Equal([]byte("foobar"))) Expect(p.remoteAddr).To(Equal(sentFrom)) Expect(p.ecn).To(Equal(protocol.ECT0)) }) It("reads ECN flags on IPv6", func() { conn, packetChan := runServer("udp6", "[::]:0") defer conn.Close() sentFrom := sendPacketWithECN( "udp6", conn.LocalAddr().(*net.UDPAddr), func(fd uintptr) { Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 3)).To(Succeed()) }, ) var p *receivedPacket Eventually(packetChan).Should(Receive(&p)) Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond))) Expect(p.data).To(Equal([]byte("foobar"))) Expect(p.remoteAddr).To(Equal(sentFrom)) Expect(p.ecn).To(Equal(protocol.ECNCE)) }) It("reads ECN flags on a connection that supports both IPv4 and IPv6", func() { conn, packetChan := runServer("udp", "0.0.0.0:0") defer conn.Close() port := conn.LocalAddr().(*net.UDPAddr).Port // IPv4 sendPacketWithECN( "udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port}, func(fd uintptr) { Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_TOS, 3)).To(Succeed()) }, ) var p *receivedPacket Eventually(packetChan).Should(Receive(&p)) Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeTrue()) Expect(p.ecn).To(Equal(protocol.ECNCE)) // IPv6 sendPacketWithECN( "udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: port}, func(fd uintptr) { Expect(unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_TCLASS, 1)).To(Succeed()) }, ) Eventually(packetChan).Should(Receive(&p)) Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeFalse()) Expect(p.ecn).To(Equal(protocol.ECT1)) }) }) Context("Packet Info conn", func() { sendPacket := func(network string, addr *net.UDPAddr) net.Addr { conn, err := net.DialUDP(network, nil, addr) ExpectWithOffset(1, err).ToNot(HaveOccurred()) _, err = conn.Write([]byte("foobar")) ExpectWithOffset(1, err).ToNot(HaveOccurred()) return conn.LocalAddr() } It("reads packet info on IPv4", func() { conn, packetChan := runServer("udp4", ":0") defer conn.Close() addr := conn.LocalAddr().(*net.UDPAddr) ip := net.ParseIP("127.0.0.1").To4() addr.IP = ip sentFrom := sendPacket("udp4", addr) var p *receivedPacket Eventually(packetChan).Should(Receive(&p)) Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond))) Expect(p.data).To(Equal([]byte("foobar"))) Expect(p.remoteAddr).To(Equal(sentFrom)) Expect(p.info).To(Not(BeNil())) Expect(p.info.addr.To4()).To(Equal(ip)) }) It("reads packet info on IPv6", func() { conn, packetChan := runServer("udp6", ":0") defer conn.Close() addr := conn.LocalAddr().(*net.UDPAddr) ip := net.ParseIP("::1") addr.IP = ip sentFrom := sendPacket("udp6", addr) var p *receivedPacket Eventually(packetChan).Should(Receive(&p)) Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(20*time.Millisecond))) Expect(p.data).To(Equal([]byte("foobar"))) Expect(p.remoteAddr).To(Equal(sentFrom)) Expect(p.info).To(Not(BeNil())) Expect(p.info.addr).To(Equal(ip)) }) It("reads packet info on a connection that supports both IPv4 and IPv6", func() { conn, packetChan := runServer("udp", ":0") defer conn.Close() port := conn.LocalAddr().(*net.UDPAddr).Port // IPv4 ip4 := net.ParseIP("127.0.0.1").To4() sendPacket("udp4", &net.UDPAddr{IP: ip4, Port: port}) var p *receivedPacket Eventually(packetChan).Should(Receive(&p)) Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeTrue()) Expect(p.info).To(Not(BeNil())) Expect(p.info.addr.To4()).To(Equal(ip4)) // IPv6 ip6 := net.ParseIP("::1") sendPacket("udp6", &net.UDPAddr{IP: net.IPv6loopback, Port: port}) Eventually(packetChan).Should(Receive(&p)) Expect(utils.IsIPv4(p.remoteAddr.(*net.UDPAddr).IP)).To(BeFalse()) Expect(p.info).To(Not(BeNil())) Expect(p.info.addr).To(Equal(ip6)) }) }) Context("Batch Reading", func() { var batchConn *MockBatchConn BeforeEach(func() { batchConn = NewMockBatchConn(mockCtrl) }) It("reads multiple messages in one batch", func() { const numMsgRead = batchSize/2 + 1 var counter int batchConn.EXPECT().ReadBatch(gomock.Any(), gomock.Any()).DoAndReturn(func(ms []ipv4.Message, flags int) (int, error) { Expect(ms).To(HaveLen(batchSize)) for i := 0; i < numMsgRead; i++ { Expect(ms[i].Buffers).To(HaveLen(1)) Expect(ms[i].Buffers[0]).To(HaveLen(int(protocol.MaxPacketBufferSize))) data := []byte(fmt.Sprintf("message %d", counter)) counter++ ms[i].Buffers[0] = data ms[i].N = len(data) } return numMsgRead, nil }).Times(2) addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) oobConn, err := newConn(udpConn) Expect(err).ToNot(HaveOccurred()) oobConn.batchConn = batchConn for i := 0; i < batchSize+1; i++ { p, err := oobConn.ReadPacket() Expect(err).ToNot(HaveOccurred()) Expect(string(p.data)).To(Equal(fmt.Sprintf("message %d", i))) } }) }) }) quic-go-0.25.0/conn_test.go000066400000000000000000000015701417145451600155000ustar00rootroot00000000000000package quic import ( "net" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Basic Conn Test", func() { It("reads a packet", func() { c := NewMockPacketConn(mockCtrl) addr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234} c.EXPECT().ReadFrom(gomock.Any()).DoAndReturn(func(b []byte) (int, net.Addr, error) { data := []byte("foobar") Expect(b).To(HaveLen(int(protocol.MaxPacketBufferSize))) return copy(b, data), addr, nil }) conn, err := wrapConn(c) Expect(err).ToNot(HaveOccurred()) p, err := conn.ReadPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.data).To(Equal([]byte("foobar"))) Expect(p.rcvTime).To(BeTemporally("~", time.Now(), scaleDuration(100*time.Millisecond))) Expect(p.remoteAddr).To(Equal(addr)) }) }) quic-go-0.25.0/conn_windows.go000066400000000000000000000025001417145451600162050ustar00rootroot00000000000000//go:build windows // +build windows package quic import ( "errors" "fmt" "net" "syscall" "golang.org/x/sys/windows" ) const ( disablePathMTUDiscovery = true IP_DONTFRAGMENT = 14 ) func newConn(c OOBCapablePacketConn) (connection, error) { rawConn, err := c.SyscallConn() if err != nil { return nil, fmt.Errorf("couldn't get syscall.RawConn: %w", err) } if err := rawConn.Control(func(fd uintptr) { // This should succeed if the connection is a IPv4 or a dual-stack connection. // It will fail for IPv6 connections. // TODO: properly handle error. _ = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, IP_DONTFRAGMENT, 1) }); err != nil { return nil, err } return &basicConn{PacketConn: c}, nil } func inspectReadBuffer(c net.PacketConn) (int, error) { conn, ok := c.(interface { SyscallConn() (syscall.RawConn, error) }) if !ok { return 0, errors.New("doesn't have a SyscallConn") } rawConn, err := conn.SyscallConn() if err != nil { return 0, fmt.Errorf("couldn't get syscall.RawConn: %w", err) } var size int var serr error if err := rawConn.Control(func(fd uintptr) { size, serr = windows.GetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_RCVBUF) }); err != nil { return 0, err } return size, serr } func (i *packetInfo) OOB() []byte { return nil } quic-go-0.25.0/conn_windows_test.go000066400000000000000000000014431417145451600172510ustar00rootroot00000000000000//go:build windows // +build windows package quic import ( "net" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Windows Conn Test", func() { It("works on IPv4", func() { addr, err := net.ResolveUDPAddr("udp4", "localhost:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp4", addr) Expect(err).ToNot(HaveOccurred()) conn, err := newConn(udpConn) Expect(err).ToNot(HaveOccurred()) Expect(conn.Close()).To(Succeed()) }) It("works on IPv6", func() { addr, err := net.ResolveUDPAddr("udp6", "[::1]:0") Expect(err).ToNot(HaveOccurred()) udpConn, err := net.ListenUDP("udp6", addr) Expect(err).ToNot(HaveOccurred()) conn, err := newConn(udpConn) Expect(err).ToNot(HaveOccurred()) Expect(conn.Close()).To(Succeed()) }) }) quic-go-0.25.0/crypto_stream.go000066400000000000000000000060151417145451600163760ustar00rootroot00000000000000package quic import ( "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type cryptoStream interface { // for receiving data HandleCryptoFrame(*wire.CryptoFrame) error GetCryptoData() []byte Finish() error // for sending data io.Writer HasData() bool PopCryptoFrame(protocol.ByteCount) *wire.CryptoFrame } type cryptoStreamImpl struct { queue *frameSorter msgBuf []byte highestOffset protocol.ByteCount finished bool writeOffset protocol.ByteCount writeBuf []byte } func newCryptoStream() cryptoStream { return &cryptoStreamImpl{queue: newFrameSorter()} } func (s *cryptoStreamImpl) 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 = utils.MaxByteCount(s.highestOffset, highestOffset) if err := s.queue.Push(f.Data, f.Offset, nil); err != nil { return err } for { _, data, _ := s.queue.Pop() if data == nil { return nil } s.msgBuf = append(s.msgBuf, data...) } } // GetCryptoData retrieves data that was received in CRYPTO frames func (s *cryptoStreamImpl) GetCryptoData() []byte { if len(s.msgBuf) < 4 { return nil } msgLen := 4 + int(s.msgBuf[1])<<16 + int(s.msgBuf[2])<<8 + int(s.msgBuf[3]) if len(s.msgBuf) < msgLen { return nil } msg := make([]byte, msgLen) copy(msg, s.msgBuf[:msgLen]) s.msgBuf = s.msgBuf[msgLen:] return msg } func (s *cryptoStreamImpl) 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 *cryptoStreamImpl) Write(p []byte) (int, error) { s.writeBuf = append(s.writeBuf, p...) return len(p), nil } func (s *cryptoStreamImpl) HasData() bool { return len(s.writeBuf) > 0 } func (s *cryptoStreamImpl) PopCryptoFrame(maxLen protocol.ByteCount) *wire.CryptoFrame { f := &wire.CryptoFrame{Offset: s.writeOffset} n := utils.MinByteCount(f.MaxDataLen(maxLen), protocol.ByteCount(len(s.writeBuf))) f.Data = s.writeBuf[:n] s.writeBuf = s.writeBuf[n:] s.writeOffset += n return f } quic-go-0.25.0/crypto_stream_manager.go000066400000000000000000000030561417145451600200720ustar00rootroot00000000000000package quic import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) type cryptoDataHandler interface { HandleMessage([]byte, protocol.EncryptionLevel) bool } type cryptoStreamManager struct { cryptoHandler cryptoDataHandler initialStream cryptoStream handshakeStream cryptoStream oneRTTStream cryptoStream } func newCryptoStreamManager( cryptoHandler cryptoDataHandler, initialStream cryptoStream, handshakeStream cryptoStream, oneRTTStream cryptoStream, ) *cryptoStreamManager { return &cryptoStreamManager{ cryptoHandler: cryptoHandler, initialStream: initialStream, handshakeStream: handshakeStream, oneRTTStream: oneRTTStream, } } func (m *cryptoStreamManager) HandleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) (bool /* encryption level changed */, 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 false, fmt.Errorf("received CRYPTO frame with unexpected encryption level: %s", encLevel) } if err := str.HandleCryptoFrame(frame); err != nil { return false, err } for { data := str.GetCryptoData() if data == nil { return false, nil } if encLevelFinished := m.cryptoHandler.HandleMessage(data, encLevel); encLevelFinished { return true, str.Finish() } } } quic-go-0.25.0/crypto_stream_manager_test.go000066400000000000000000000111341417145451600211250ustar00rootroot00000000000000package quic import ( "errors" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Crypto Stream Manager", func() { var ( csm *cryptoStreamManager cs *MockCryptoDataHandler initialStream *MockCryptoStream handshakeStream *MockCryptoStream oneRTTStream *MockCryptoStream ) BeforeEach(func() { initialStream = NewMockCryptoStream(mockCtrl) handshakeStream = NewMockCryptoStream(mockCtrl) oneRTTStream = NewMockCryptoStream(mockCtrl) cs = NewMockCryptoDataHandler(mockCtrl) csm = newCryptoStreamManager(cs, initialStream, handshakeStream, oneRTTStream) }) It("passes messages to the initial stream", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} initialStream.EXPECT().HandleCryptoFrame(cf) initialStream.EXPECT().GetCryptoData().Return([]byte("foobar")) initialStream.EXPECT().GetCryptoData() cs.EXPECT().HandleMessage([]byte("foobar"), protocol.EncryptionInitial) encLevelChanged, err := csm.HandleCryptoFrame(cf, protocol.EncryptionInitial) Expect(err).ToNot(HaveOccurred()) Expect(encLevelChanged).To(BeFalse()) }) It("passes messages to the handshake stream", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} handshakeStream.EXPECT().HandleCryptoFrame(cf) handshakeStream.EXPECT().GetCryptoData().Return([]byte("foobar")) handshakeStream.EXPECT().GetCryptoData() cs.EXPECT().HandleMessage([]byte("foobar"), protocol.EncryptionHandshake) encLevelChanged, err := csm.HandleCryptoFrame(cf, protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) Expect(encLevelChanged).To(BeFalse()) }) It("passes messages to the 1-RTT stream", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} oneRTTStream.EXPECT().HandleCryptoFrame(cf) oneRTTStream.EXPECT().GetCryptoData().Return([]byte("foobar")) oneRTTStream.EXPECT().GetCryptoData() cs.EXPECT().HandleMessage([]byte("foobar"), protocol.Encryption1RTT) encLevelChanged, err := csm.HandleCryptoFrame(cf, protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(encLevelChanged).To(BeFalse()) }) It("doesn't call the message handler, if there's no message", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} handshakeStream.EXPECT().HandleCryptoFrame(cf) handshakeStream.EXPECT().GetCryptoData() // don't return any data to handle // don't EXPECT any calls to HandleMessage() encLevelChanged, err := csm.HandleCryptoFrame(cf, protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) Expect(encLevelChanged).To(BeFalse()) }) It("processes all messages", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} handshakeStream.EXPECT().HandleCryptoFrame(cf) handshakeStream.EXPECT().GetCryptoData().Return([]byte("foo")) handshakeStream.EXPECT().GetCryptoData().Return([]byte("bar")) handshakeStream.EXPECT().GetCryptoData() cs.EXPECT().HandleMessage([]byte("foo"), protocol.EncryptionHandshake) cs.EXPECT().HandleMessage([]byte("bar"), protocol.EncryptionHandshake) encLevelChanged, err := csm.HandleCryptoFrame(cf, protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) Expect(encLevelChanged).To(BeFalse()) }) It("finishes the crypto stream, when the crypto setup is done with this encryption level", func() { cf := &wire.CryptoFrame{Data: []byte("foobar")} gomock.InOrder( handshakeStream.EXPECT().HandleCryptoFrame(cf), handshakeStream.EXPECT().GetCryptoData().Return([]byte("foobar")), cs.EXPECT().HandleMessage([]byte("foobar"), protocol.EncryptionHandshake).Return(true), handshakeStream.EXPECT().Finish(), ) encLevelChanged, err := csm.HandleCryptoFrame(cf, protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) Expect(encLevelChanged).To(BeTrue()) }) It("returns errors that occur when finishing a stream", func() { testErr := errors.New("test error") cf := &wire.CryptoFrame{Data: []byte("foobar")} gomock.InOrder( handshakeStream.EXPECT().HandleCryptoFrame(cf), handshakeStream.EXPECT().GetCryptoData().Return([]byte("foobar")), cs.EXPECT().HandleMessage([]byte("foobar"), protocol.EncryptionHandshake).Return(true), handshakeStream.EXPECT().Finish().Return(testErr), ) _, err := csm.HandleCryptoFrame(cf, protocol.EncryptionHandshake) Expect(err).To(MatchError(err)) }) 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")) }) }) quic-go-0.25.0/crypto_stream_test.go000066400000000000000000000132431417145451600174360ustar00rootroot00000000000000package quic import ( "crypto/rand" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func createHandshakeMessage(len int) []byte { msg := make([]byte, 4+len) rand.Read(msg[:1]) // random message type msg[1] = uint8(len >> 16) msg[2] = uint8(len >> 8) msg[3] = uint8(len) rand.Read(msg[4:]) return msg } var _ = Describe("Crypto Stream", func() { var str cryptoStream BeforeEach(func() { str = newCryptoStream() }) Context("handling incoming data", func() { It("handles in-order CRYPTO frames", func() { msg := createHandshakeMessage(6) err := str.HandleCryptoFrame(&wire.CryptoFrame{Data: msg}) Expect(err).ToNot(HaveOccurred()) Expect(str.GetCryptoData()).To(Equal(msg)) Expect(str.GetCryptoData()).To(BeNil()) }) It("handles multiple messages in one CRYPTO frame", func() { msg1 := createHandshakeMessage(6) msg2 := createHandshakeMessage(10) msg := append(append([]byte{}, msg1...), msg2...) err := str.HandleCryptoFrame(&wire.CryptoFrame{Data: msg}) Expect(err).ToNot(HaveOccurred()) Expect(str.GetCryptoData()).To(Equal(msg1)) Expect(str.GetCryptoData()).To(Equal(msg2)) 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 messages split over multiple CRYPTO frames", func() { msg := createHandshakeMessage(6) err := str.HandleCryptoFrame(&wire.CryptoFrame{ Data: msg[:4], }) Expect(err).ToNot(HaveOccurred()) Expect(str.GetCryptoData()).To(BeNil()) err = str.HandleCryptoFrame(&wire.CryptoFrame{ Offset: 4, Data: msg[4:], }) Expect(err).ToNot(HaveOccurred()) Expect(str.GetCryptoData()).To(Equal(msg)) Expect(str.GetCryptoData()).To(BeNil()) }) It("handles out-of-order CRYPTO frames", func() { msg := createHandshakeMessage(6) err := str.HandleCryptoFrame(&wire.CryptoFrame{ Offset: 4, Data: msg[4:], }) Expect(err).ToNot(HaveOccurred()) Expect(str.GetCryptoData()).To(BeNil()) err = str.HandleCryptoFrame(&wire.CryptoFrame{ Data: msg[:4], }) Expect(err).ToNot(HaveOccurred()) Expect(str.GetCryptoData()).To(Equal(msg)) Expect(str.GetCryptoData()).To(BeNil()) }) Context("finishing", func() { It("errors if there's still data to read after finishing", func() { Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Data: createHandshakeMessage(5), 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.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: createHandshakeMessage(5), })).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() { msg := createHandshakeMessage(15) Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Data: msg, })).To(Succeed()) Expect(str.GetCryptoData()).To(Equal(msg)) Expect(str.Finish()).To(Succeed()) Expect(str.HandleCryptoFrame(&wire.CryptoFrame{ Offset: protocol.ByteCount(len(msg) - 6), Data: []byte("foobar"), })).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.VersionWhatever) _, 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"))) }) }) }) quic-go-0.25.0/datagram_queue.go000066400000000000000000000034741417145451600164750ustar00rootroot00000000000000package quic import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type datagramQueue struct { sendQueue chan *wire.DatagramFrame rcvQueue chan []byte closeErr error closed chan struct{} hasData func() dequeued chan struct{} logger utils.Logger } func newDatagramQueue(hasData func(), logger utils.Logger) *datagramQueue { return &datagramQueue{ hasData: hasData, sendQueue: make(chan *wire.DatagramFrame, 1), rcvQueue: make(chan []byte, protocol.DatagramRcvQueueLen), dequeued: make(chan struct{}), closed: make(chan struct{}), logger: logger, } } // AddAndWait queues a new DATAGRAM frame for sending. // It blocks until the frame has been dequeued. func (h *datagramQueue) AddAndWait(f *wire.DatagramFrame) error { select { case h.sendQueue <- f: h.hasData() case <-h.closed: return h.closeErr } select { case <-h.dequeued: return nil case <-h.closed: return h.closeErr } } // Get dequeues a DATAGRAM frame for sending. func (h *datagramQueue) Get() *wire.DatagramFrame { select { case f := <-h.sendQueue: h.dequeued <- struct{}{} return f default: return nil } } // HandleDatagramFrame handles a received DATAGRAM frame. func (h *datagramQueue) HandleDatagramFrame(f *wire.DatagramFrame) { data := make([]byte, len(f.Data)) copy(data, f.Data) select { case h.rcvQueue <- data: default: h.logger.Debugf("Discarding DATAGRAM frame (%d bytes payload)", len(f.Data)) } } // Receive gets a received DATAGRAM frame. func (h *datagramQueue) Receive() ([]byte, error) { select { case data := <-h.rcvQueue: return data, nil case <-h.closed: return nil, h.closeErr } } func (h *datagramQueue) CloseWithError(e error) { h.closeErr = e close(h.closed) } quic-go-0.25.0/datagram_queue_test.go000066400000000000000000000050151417145451600175250ustar00rootroot00000000000000package quic import ( "errors" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "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.Get()).To(BeNil()) }) It("queues a datagram", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) Expect(queue.AddAndWait(&wire.DatagramFrame{Data: []byte("foobar")})).To(Succeed()) }() Eventually(queued).Should(HaveLen(1)) Consistently(done).ShouldNot(BeClosed()) f := queue.Get() Expect(f).ToNot(BeNil()) Expect(f.Data).To(Equal([]byte("foobar"))) Eventually(done).Should(BeClosed()) Expect(queue.Get()).To(BeNil()) }) It("closes", func() { errChan := make(chan error, 1) go func() { defer GinkgoRecover() errChan <- queue.AddAndWait(&wire.DatagramFrame{Data: []byte("foobar")}) }() Consistently(errChan).ShouldNot(Receive()) queue.CloseWithError(errors.New("test error")) Eventually(errChan).Should(Receive(MatchError("test error"))) }) }) 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() Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foo"))) data, err = queue.Receive() 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() 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("closes", func() { errChan := make(chan error, 1) go func() { defer GinkgoRecover() _, err := queue.Receive() errChan <- err }() Consistently(errChan).ShouldNot(Receive()) queue.CloseWithError(errors.New("test error")) Eventually(errChan).Should(Receive(MatchError("test error"))) }) }) }) quic-go-0.25.0/docs/000077500000000000000000000000001417145451600141025ustar00rootroot00000000000000quic-go-0.25.0/docs/quic.png000066400000000000000000000416231417145451600155570ustar00rootroot00000000000000‰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`‚quic-go-0.25.0/docs/quic.sketch000066400000000000000000002600001417145451600162440ustar00rootroot00000000000000SQLite 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å{å†å‹åå’quic-go-0.25.0/docs/quic.svg000066400000000000000000000250721417145451600155720ustar00rootroot00000000000000 quic Created with Sketch. QUI C quic-go-0.25.0/errors.go000066400000000000000000000035641417145451600150250ustar00rootroot00000000000000package quic import ( "fmt" "github.com/lucas-clemente/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 } func (e *StreamError) Is(target error) bool { _, ok := target.(*StreamError) return ok } func (e *StreamError) Error() string { return fmt.Sprintf("stream %d canceled with error code %d", e.StreamID, e.ErrorCode) } quic-go-0.25.0/example/000077500000000000000000000000001417145451600146055ustar00rootroot00000000000000quic-go-0.25.0/example/Dockerfile000066400000000000000000000002061417145451600165750ustar00rootroot00000000000000FROM scratch VOLUME /certs VOLUME /www EXPOSE 6121 ADD main /main CMD ["/main", "-bind=0.0.0.0", "-certpath=/certs/", "-www=/www"] quic-go-0.25.0/example/client/000077500000000000000000000000001417145451600160635ustar00rootroot00000000000000quic-go-0.25.0/example/client/main.go000066400000000000000000000045711417145451600173450ustar00rootroot00000000000000package main import ( "bufio" "bytes" "crypto/tls" "crypto/x509" "flag" "fmt" "io" "log" "net/http" "os" "sync" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/http3" "github.com/lucas-clemente/quic-go/internal/testdata" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" "github.com/lucas-clemente/quic-go/qlog" ) func main() { verbose := flag.Bool("v", false, "verbose") 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") enableQlog := flag.Bool("qlog", false, "output a qlog (in the same directory)") flag.Parse() urls := flag.Args() logger := utils.DefaultLogger if *verbose { logger.SetLogLevel(utils.LogLevelDebug) } else { logger.SetLogLevel(utils.LogLevelInfo) } logger.SetLogTimeFormat("") 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) var qconf quic.Config if *enableQlog { qconf.Tracer = qlog.NewTracer(func(_ logging.Perspective, connID []byte) io.WriteCloser { filename := fmt.Sprintf("client_%x.qlog", connID) f, err := os.Create(filename) if err != nil { log.Fatal(err) } log.Printf("Creating qlog file %s.\n", filename) return utils.NewBufferedWriteCloser(bufio.NewWriter(f), f) }) } roundTripper := &http3.RoundTripper{ TLSClientConfig: &tls.Config{ RootCAs: pool, InsecureSkipVerify: *insecure, KeyLogWriter: keyLog, }, QuicConfig: &qconf, } defer roundTripper.Close() hclient := &http.Client{ Transport: roundTripper, } var wg sync.WaitGroup wg.Add(len(urls)) for _, addr := range urls { logger.Infof("GET %s", addr) go func(addr string) { rsp, err := hclient.Get(addr) if err != nil { log.Fatal(err) } logger.Infof("Got response for %s: %#v", addr, rsp) body := &bytes.Buffer{} _, err = io.Copy(body, rsp.Body) if err != nil { log.Fatal(err) } if *quiet { logger.Infof("Response Body: %d bytes", body.Len()) } else { logger.Infof("Response Body:") logger.Infof("%s", body.Bytes()) } wg.Done() }(addr) } wg.Wait() } quic-go-0.25.0/example/echo/000077500000000000000000000000001417145451600155235ustar00rootroot00000000000000quic-go-0.25.0/example/echo/echo.go000066400000000000000000000047561417145451600170040ustar00rootroot00000000000000package main import ( "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "encoding/pem" "fmt" "io" "log" "math/big" quic "github.com/lucas-clemente/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 } sess, err := listener.Accept(context.Background()) if err != nil { return err } stream, err := sess.AcceptStream(context.Background()) if err != nil { panic(err) } // 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"}, } session, err := quic.DialAddr(addr, tlsConf, nil) if err != nil { return err } stream, err := session.OpenStreamSync(context.Background()) if err != nil { return err } 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"}, } } quic-go-0.25.0/example/main.go000066400000000000000000000122341417145451600160620ustar00rootroot00000000000000package main import ( "bufio" "crypto/md5" "errors" "flag" "fmt" "io" "io/ioutil" "log" "mime/multipart" "net/http" "os" "strconv" "strings" "sync" _ "net/http/pprof" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/http3" "github.com/lucas-clemente/quic-go/internal/testdata" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" "github.com/lucas-clemente/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 := ioutil.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") } } utils.DefaultLogger.Infof("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) verbose := flag.Bool("v", false, "verbose") bs := binds{} flag.Var(&bs, "bind", "bind to") www := flag.String("www", "", "www data") tcp := flag.Bool("tcp", false, "also listen on TCP") enableQlog := flag.Bool("qlog", false, "output a qlog (in the same directory)") flag.Parse() logger := utils.DefaultLogger if *verbose { logger.SetLogLevel(utils.LogLevelDebug) } else { logger.SetLogLevel(utils.LogLevelInfo) } logger.SetLogTimeFormat("") if len(bs) == 0 { bs = binds{"localhost:6121"} } handler := setupHandler(*www) quicConf := &quic.Config{} if *enableQlog { quicConf.Tracer = qlog.NewTracer(func(_ logging.Perspective, connID []byte) io.WriteCloser { filename := fmt.Sprintf("server_%x.qlog", connID) f, err := os.Create(filename) if err != nil { log.Fatal(err) } log.Printf("Creating qlog file %s.\n", filename) return utils.NewBufferedWriteCloser(bufio.NewWriter(f), f) }) } var wg sync.WaitGroup wg.Add(len(bs)) for _, b := range bs { bCap := b go func() { var err error if *tcp { certFile, keyFile := testdata.GetCertificatePaths() err = http3.ListenAndServe(bCap, certFile, keyFile, handler) } else { server := http3.Server{ Server: &http.Server{Handler: handler, Addr: bCap}, QuicConfig: quicConf, } err = server.ListenAndServeTLS(testdata.GetCertificatePaths()) } if err != nil { fmt.Println(err) } wg.Done() }() } wg.Wait() } quic-go-0.25.0/frame_sorter.go000066400000000000000000000134231417145451600161740ustar00rootroot00000000000000package quic import ( "errors" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) type frameSorterEntry struct { Data []byte DoneCb func() } type frameSorter struct { queue map[protocol.ByteCount]frameSorterEntry readPos protocol.ByteCount gaps *utils.ByteIntervalList } var errDuplicateStreamData = errors.New("duplicate stream data") func newFrameSorter() *frameSorter { s := frameSorter{ gaps: utils.NewByteIntervalList(), queue: make(map[protocol.ByteCount]frameSorterEntry), } s.gaps.PushFront(utils.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 *utils.ByteIntervalElement 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(utils.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) (*utils.ByteIntervalElement, 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 *utils.ByteIntervalElement, offset protocol.ByteCount) (*utils.ByteIntervalElement, 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 } quic-go-0.25.0/frame_sorter_test.go000066400000000000000000001170101417145451600172300ustar00rootroot00000000000000package quic import ( "bytes" "fmt" "math" "math/rand" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("frame sorter", func() { var s *frameSorter checkGaps := func(expectedGaps []utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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([]utils.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(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([]utils.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([]utils.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([]utils.ByteInterval{{Start: num * dataLen, End: protocol.MaxByteCount}}) Expect(getData()).To(Equal(data)) Expect(s.queue).To(BeEmpty()) checkCallbacks() }) }) } }) }) quic-go-0.25.0/framer.go000066400000000000000000000116261417145451600147630ustar00rootroot00000000000000package quic import ( "errors" "sync" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/quicvarint" ) type framer interface { HasData() bool QueueControlFrame(wire.Frame) AppendControlFrames([]ackhandler.Frame, protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) AddActiveStream(protocol.StreamID) AppendStreamFrames([]ackhandler.Frame, protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) Handle0RTTRejection() error } type framerI struct { mutex sync.Mutex streamGetter streamGetter version protocol.VersionNumber activeStreams map[protocol.StreamID]struct{} streamQueue []protocol.StreamID controlFrameMutex sync.Mutex controlFrames []wire.Frame } var _ framer = &framerI{} func newFramer( streamGetter streamGetter, v protocol.VersionNumber, ) framer { return &framerI{ streamGetter: streamGetter, activeStreams: make(map[protocol.StreamID]struct{}), version: v, } } func (f *framerI) HasData() bool { f.mutex.Lock() hasData := len(f.streamQueue) > 0 f.mutex.Unlock() if hasData { return true } f.controlFrameMutex.Lock() hasData = len(f.controlFrames) > 0 f.controlFrameMutex.Unlock() return hasData } func (f *framerI) QueueControlFrame(frame wire.Frame) { f.controlFrameMutex.Lock() f.controlFrames = append(f.controlFrames, frame) f.controlFrameMutex.Unlock() } func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { var length protocol.ByteCount f.controlFrameMutex.Lock() for len(f.controlFrames) > 0 { frame := f.controlFrames[len(f.controlFrames)-1] frameLen := frame.Length(f.version) if length+frameLen > maxLen { break } frames = append(frames, ackhandler.Frame{Frame: frame}) length += frameLen f.controlFrames = f.controlFrames[:len(f.controlFrames)-1] } f.controlFrameMutex.Unlock() return frames, length } func (f *framerI) AddActiveStream(id protocol.StreamID) { f.mutex.Lock() if _, ok := f.activeStreams[id]; !ok { f.streamQueue = append(f.streamQueue, id) f.activeStreams[id] = struct{}{} } f.mutex.Unlock() } func (f *framerI) AppendStreamFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { var length protocol.ByteCount var lastFrame *ackhandler.Frame f.mutex.Lock() // pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet numActiveStreams := len(f.streamQueue) for i := 0; i < numActiveStreams; i++ { if protocol.MinStreamFrameSize+length > maxLen { break } id := f.streamQueue[0] f.streamQueue = f.streamQueue[1:] // This should never return an error. Better check it anyway. // The stream will only be in the streamQueue, if it enqueued itself there. str, err := f.streamGetter.GetOrOpenSendStream(id) // The stream can be nil if it completed after it said it had data. if str == nil || err != nil { delete(f.activeStreams, id) 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 += quicvarint.Len(uint64(remainingLen)) frame, hasMoreData := str.popStreamFrame(remainingLen) if hasMoreData { // put the stream back in the queue (at the end) f.streamQueue = append(f.streamQueue, id) } else { // no more data to send. Stream is not active any more delete(f.activeStreams, id) } // The frame can be nil // * if the receiveStream was canceled after it said it had data // * the remaining size doesn't allow us to add another STREAM frame if frame == nil { continue } frames = append(frames, *frame) length += frame.Length(f.version) lastFrame = frame } f.mutex.Unlock() if lastFrame != nil { lastFrameLen := lastFrame.Length(f.version) // account for the smaller size of the last STREAM frame lastFrame.Frame.(*wire.StreamFrame).DataLenPresent = false length += lastFrame.Length(f.version) - lastFrameLen } return frames, length } func (f *framerI) Handle0RTTRejection() error { f.mutex.Lock() defer f.mutex.Unlock() f.controlFrameMutex.Lock() f.streamQueue = f.streamQueue[:0] 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: return errors.New("didn't expect MAX_DATA / MAX_STREAM_DATA / MAX_STREAMS frame to be sent in 0-RTT") case *wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame: continue default: f.controlFrames[j] = f.controlFrames[i] j++ } } f.controlFrames = f.controlFrames[:j] f.controlFrameMutex.Unlock() return nil } quic-go-0.25.0/framer_test.go000066400000000000000000000370221417145451600160200ustar00rootroot00000000000000package quic import ( "bytes" "math/rand" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Framer", func() { const ( id1 = protocol.StreamID(10) id2 = protocol.StreamID(11) ) var ( framer framer stream1, stream2 *MockSendStreamI streamGetter *MockStreamGetter version protocol.VersionNumber ) BeforeEach(func() { streamGetter = NewMockStreamGetter(mockCtrl) stream1 = NewMockSendStreamI(mockCtrl) stream1.EXPECT().StreamID().Return(protocol.StreamID(5)).AnyTimes() stream2 = NewMockSendStreamI(mockCtrl) stream2.EXPECT().StreamID().Return(protocol.StreamID(6)).AnyTimes() framer = newFramer(streamGetter, version) }) Context("handling control frames", func() { It("adds control frames", func() { mdf := &wire.MaxDataFrame{MaximumData: 0x42} msf := &wire.MaxStreamsFrame{MaxStreamNum: 0x1337} framer.QueueControlFrame(mdf) framer.QueueControlFrame(msf) frames, length := framer.AppendControlFrames(nil, 1000) Expect(frames).To(HaveLen(2)) fs := []wire.Frame{frames[0].Frame, frames[1].Frame} Expect(fs).To(ContainElement(mdf)) Expect(fs).To(ContainElement(msf)) Expect(length).To(Equal(mdf.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) Expect(frames).To(HaveLen(1)) Expect(framer.HasData()).To(BeFalse()) }) It("appends to the slice given", func() { ping := &wire.PingFrame{} mdf := &wire.MaxDataFrame{MaximumData: 0x42} framer.QueueControlFrame(mdf) frames, length := framer.AppendControlFrames([]ackhandler.Frame{{Frame: ping}}, 1000) Expect(frames).To(HaveLen(2)) Expect(frames[0].Frame).To(Equal(ping)) Expect(frames[1].Frame).To(Equal(mdf)) Expect(length).To(Equal(mdf.Length(version))) }) 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) Expect(frames).To(HaveLen(numFrames)) Expect(length).To(BeNumerically(">", maxSize-bfLen)) frames, length = framer.AppendControlFrames(nil, maxSize) 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.ConnectionID{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) } Expect(framer.Handle0RTTRejection()).To(Succeed()) fs, length := framer.AppendControlFrames(nil, protocol.MaxByteCount) Expect(fs).To(HaveLen(2)) Expect(length).To(Equal(ping.Length(version) + ncid.Length(version))) }) }) Context("popping STREAM frames", func() { It("returns nil when popping an empty framer", func() { Expect(framer.AppendStreamFrames(nil, 1000)).To(BeEmpty()) }) It("returns STREAM frames", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) f := &wire.StreamFrame{ StreamID: id1, Data: []byte("foobar"), Offset: 42, DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f}, false) framer.AddActiveStream(id1) fs, length := framer.AppendStreamFrames(nil, 1000) Expect(fs).To(HaveLen(1)) Expect(fs[0].Frame.(*wire.StreamFrame).DataLenPresent).To(BeFalse()) Expect(length).To(Equal(f.Length(version))) }) It("says if it has data", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2) Expect(framer.HasData()).To(BeFalse()) framer.AddActiveStream(id1) 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()).Return(&ackhandler.Frame{Frame: f1}, true) stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f2}, false) frames, _ := framer.AppendStreamFrames(nil, protocol.MaxByteCount) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f1)) Expect(framer.HasData()).To(BeTrue()) frames, _ = framer.AppendStreamFrames(nil, protocol.MaxByteCount) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f2)) Expect(framer.HasData()).To(BeFalse()) }) It("appends to a frame slice", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) f := &wire.StreamFrame{ StreamID: id1, Data: []byte("foobar"), DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f}, false) framer.AddActiveStream(id1) mdf := &wire.MaxDataFrame{MaximumData: 1337} frames := []ackhandler.Frame{{Frame: mdf}} fs, length := framer.AppendStreamFrames(frames, 1000) Expect(fs).To(HaveLen(2)) Expect(fs[0].Frame).To(Equal(mdf)) Expect(fs[1].Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar"))) Expect(fs[1].Frame.(*wire.StreamFrame).DataLenPresent).To(BeFalse()) Expect(length).To(Equal(f.Length(version))) }) It("skips a stream that was reported active, but was completed shortly after", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(nil, nil) streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil) f := &wire.StreamFrame{ StreamID: id2, Data: []byte("foobar"), DataLenPresent: true, } stream2.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f}, false) framer.AddActiveStream(id1) framer.AddActiveStream(id2) frames, _ := framer.AppendStreamFrames(nil, 1000) 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() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil) f := &wire.StreamFrame{ StreamID: id2, Data: []byte("foobar"), DataLenPresent: true, } stream1.EXPECT().popStreamFrame(gomock.Any()).Return(nil, false) stream2.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f}, false) framer.AddActiveStream(id1) framer.AddActiveStream(id2) frames, _ := framer.AppendStreamFrames(nil, 1000) 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() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2) f1 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobar")} f2 := &wire.StreamFrame{StreamID: id1, Data: []byte("foobaz")} stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f1}, true) stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f2}, false) framer.AddActiveStream(id1) // only add it once frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f1)) frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize) 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) Expect(frames).To(BeNil()) }) It("re-queues a stream at the end, if it has enough data", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil).Times(2) streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil) 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()).Return(&ackhandler.Frame{Frame: f11}, true) stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f12}, false) stream2.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f2}, false) framer.AddActiveStream(id1) // only add it once framer.AddActiveStream(id2) // first a frame from stream 1 frames, _ := framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f11)) // then a frame from stream 2 frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f2)) // then another frame from stream 1 frames, _ = framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize) Expect(frames).To(HaveLen(1)) Expect(frames[0].Frame).To(Equal(f12)) }) It("only dequeues data from each stream once per packet", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil) 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()).Return(&ackhandler.Frame{Frame: f1}, true) stream2.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f2}, true) framer.AddActiveStream(id1) framer.AddActiveStream(id2) frames, length := framer.AppendStreamFrames(nil, 1000) 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() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil) f1 := &wire.StreamFrame{Data: []byte("foobar")} f2 := &wire.StreamFrame{Data: []byte("foobaz")} stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f1}, false) stream2.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f2}, false) framer.AddActiveStream(id2) framer.AddActiveStream(id1) frames, _ := framer.AppendStreamFrames(nil, 1000) 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() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) f := &wire.StreamFrame{Data: []byte("foobar")} stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f}, false) // only one call to this function framer.AddActiveStream(id1) framer.AddActiveStream(id1) frames, _ := framer.AppendStreamFrames(nil, 1000) Expect(frames).To(HaveLen(1)) }) It("does not pop empty frames", func() { fs, length := framer.AppendStreamFrames(nil, 500) Expect(fs).To(BeEmpty()) Expect(length).To(BeZero()) }) It("pops maximum size STREAM frames", func() { for i := protocol.MinStreamFrameSize; i < 2000; i++ { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) stream1.EXPECT().popStreamFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) (*ackhandler.Frame, bool) { f := &wire.StreamFrame{ StreamID: id1, DataLenPresent: true, } f.Data = make([]byte, f.MaxDataLen(size, version)) Expect(f.Length(version)).To(Equal(size)) return &ackhandler.Frame{Frame: f}, false }) framer.AddActiveStream(id1) frames, _ := framer.AppendStreamFrames(nil, i) Expect(frames).To(HaveLen(1)) f := frames[0].Frame.(*wire.StreamFrame) 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++ { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) streamGetter.EXPECT().GetOrOpenSendStream(id2).Return(stream2, nil) stream1.EXPECT().popStreamFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) (*ackhandler.Frame, bool) { f := &wire.StreamFrame{ StreamID: id2, DataLenPresent: true, } f.Data = make([]byte, f.MaxDataLen(protocol.MinStreamFrameSize, version)) return &ackhandler.Frame{Frame: f}, false }) stream2.EXPECT().popStreamFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) (*ackhandler.Frame, bool) { f := &wire.StreamFrame{ StreamID: id2, DataLenPresent: true, } f.Data = make([]byte, f.MaxDataLen(size, version)) Expect(f.Length(version)).To(Equal(size)) return &ackhandler.Frame{Frame: f}, false }) framer.AddActiveStream(id1) framer.AddActiveStream(id2) frames, _ := framer.AppendStreamFrames(nil, i) Expect(frames).To(HaveLen(2)) f1 := frames[0].Frame.(*wire.StreamFrame) f2 := frames[1].Frame.(*wire.StreamFrame) 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() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) f := &wire.StreamFrame{Data: []byte("foobar")} stream1.EXPECT().popStreamFrame(gomock.Any()).Return(&ackhandler.Frame{Frame: f}, false) framer.AddActiveStream(id1) framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize) }) It("does not pop frames smaller than the minimum size", func() { // don't expect a call to PopStreamFrame() framer.AppendStreamFrames(nil, protocol.MinStreamFrameSize-1) }) It("stops iterating when the remaining size is smaller than the minimum STREAM frame size", func() { streamGetter.EXPECT().GetOrOpenSendStream(id1).Return(stream1, nil) // 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()).Return(&ackhandler.Frame{Frame: f}, false) framer.AddActiveStream(id1) fs, length := framer.AppendStreamFrames(nil, 500) 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) Expect(framer.Handle0RTTRejection()).To(Succeed()) fs, length := framer.AppendStreamFrames(nil, protocol.MaxByteCount) Expect(fs).To(BeEmpty()) Expect(length).To(BeZero()) }) }) }) quic-go-0.25.0/fuzzing/000077500000000000000000000000001417145451600146465ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/frames/000077500000000000000000000000001417145451600161235ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/frames/cmd/000077500000000000000000000000001417145451600166665ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/frames/cmd/corpus.go000066400000000000000000000174351417145451600205420ustar00rootroot00000000000000package main import ( "bytes" "log" "math/rand" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/fuzzing/internal/helper" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) const version = protocol.VersionTLS 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: getRandomData(4), StatelessResetToken: token1, }, &wire.NewConnectionIDFrame{ SequenceNumber: seq2, RetirePriorTo: seq2, ConnectionID: 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 := &bytes.Buffer{} if err := f.Write(b, version); err != nil { log.Fatal(err) } if err := helper.WriteCorpusFileWithPrefix("corpus", b.Bytes(), 1); err != nil { log.Fatal(err) } } for i := 0; i < 30; i++ { frames := getFrames() b := &bytes.Buffer{} for j := 0; j < rand.Intn(30)+2; j++ { if rand.Intn(10) == 0 { // write a PADDING frame b.WriteByte(0x0) } f := frames[rand.Intn(len(frames))] if err := f.Write(b, version); err != nil { log.Fatal(err) } if rand.Intn(10) == 0 { // write a PADDING frame b.WriteByte(0x0) } } if err := helper.WriteCorpusFileWithPrefix("corpus", b.Bytes(), 1); err != nil { log.Fatal(err) } } } quic-go-0.25.0/fuzzing/frames/fuzz.go000066400000000000000000000035521417145451600174550ustar00rootroot00000000000000package frames import ( "bytes" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) const version = protocol.VersionTLS // 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, version) parser.SetAckDelayExponent(protocol.DefaultAckDelayExponent) r := bytes.NewReader(data) initialLen := r.Len() var frames []wire.Frame for r.Len() > 0 { f, err := parser.ParseNext(r, encLevel) if err != nil { break } frames = append(frames, f) } parsedLen := initialLen - r.Len() if len(frames) == 0 { return 0 } b := &bytes.Buffer{} for _, f := range frames { if f == nil { // PADDING frame b.WriteByte(0x0) 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 } } lenBefore := b.Len() if err := f.Write(b, version); err != nil { panic(fmt.Sprintf("Error writing frame %#v: %s", f, err)) } frameLen := b.Len() - lenBefore if f.Length(version) != protocol.ByteCount(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 b.Len() > parsedLen { panic(fmt.Sprintf("Serialized length (%d) is longer than parsed length (%d)", b.Len(), parsedLen)) } return 1 } quic-go-0.25.0/fuzzing/handshake/000077500000000000000000000000001417145451600165745ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/handshake/cmd/000077500000000000000000000000001417145451600173375ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/handshake/cmd/corpus.go000066400000000000000000000075441417145451600212130ustar00rootroot00000000000000package main import ( "crypto/tls" "log" fuzzhandshake "github.com/lucas-clemente/quic-go/fuzzing/handshake" "github.com/lucas-clemente/quic-go/fuzzing/internal/helper" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/testdata" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type chunk struct { data []byte encLevel protocol.EncryptionLevel } type stream struct { chunkChan chan<- chunk encLevel protocol.EncryptionLevel } func (s *stream) Write(b []byte) (int, error) { data := append([]byte{}, b...) select { case s.chunkChan <- chunk{data: data, encLevel: s.encLevel}: default: panic("chunkChan too small") } return len(b), nil } func initStreams() (chan chunk, *stream /* initial */, *stream /* handshake */) { chunkChan := make(chan chunk, 10) initialStream := &stream{chunkChan: chunkChan, encLevel: protocol.EncryptionInitial} handshakeStream := &stream{chunkChan: chunkChan, encLevel: protocol.EncryptionHandshake} return chunkChan, initialStream, handshakeStream } type handshakeRunner interface { OnReceivedParams(*wire.TransportParameters) OnHandshakeComplete() OnError(error) DropKeys(protocol.EncryptionLevel) } type runner struct { client, server *handshake.CryptoSetup } var _ handshakeRunner = &runner{} func newRunner(client, server *handshake.CryptoSetup) *runner { return &runner{client: client, server: server} } func (r *runner) OnReceivedParams(*wire.TransportParameters) {} func (r *runner) OnHandshakeComplete() {} func (r *runner) OnError(err error) { log.Fatal("runner error:", err) (*r.client).Close() (*r.server).Close() } func (r *runner) DropKeys(protocol.EncryptionLevel) {} const alpn = "fuzz" func main() { cChunkChan, cInitialStream, cHandshakeStream := initStreams() var client, server handshake.CryptoSetup runner := newRunner(&client, &server) client, _ = handshake.NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{}, runner, &tls.Config{ ServerName: "localhost", NextProtos: []string{alpn}, RootCAs: testdata.GetRootCA(), ClientSessionCache: tls.NewLRUClientSessionCache(1), }, false, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) sChunkChan, sInitialStream, sHandshakeStream := initStreams() config := testdata.GetTLSConfig() config.NextProtos = []string{alpn} server = handshake.NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{}, runner, config, false, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) serverHandshakeCompleted := make(chan struct{}) go func() { defer close(serverHandshakeCompleted) server.RunHandshake() }() clientHandshakeCompleted := make(chan struct{}) go func() { defer close(clientHandshakeCompleted) client.RunHandshake() }() done := make(chan struct{}) go func() { <-serverHandshakeCompleted <-clientHandshakeCompleted close(done) }() var messages [][]byte messageLoop: for { select { case c := <-cChunkChan: messages = append(messages, c.data) server.HandleMessage(c.data, c.encLevel) case c := <-sChunkChan: messages = append(messages, c.data) client.HandleMessage(c.data, c.encLevel) case <-done: break messageLoop } } 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) } } } quic-go-0.25.0/fuzzing/handshake/fuzz.go000066400000000000000000000316431417145451600201300ustar00rootroot00000000000000package handshake import ( "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "errors" "fmt" "io/ioutil" "log" "math" mrand "math/rand" "sync" "time" "github.com/lucas-clemente/quic-go/fuzzing/internal/helper" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/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) } } func appendSuites(suites []uint16, rand uint8) []uint16 { const ( s1 = tls.TLS_AES_128_GCM_SHA256 s2 = tls.TLS_AES_256_GCM_SHA384 s3 = tls.TLS_CHACHA20_POLY1305_SHA256 ) switch rand % 4 { default: return suites case 1: return append(suites, s1) case 2: return append(suites, s2) case 3: return append(suites, s3) } } // consumes 2 bits func getSuites(rand uint8) []uint16 { suites := make([]uint16, 0, 3) for i := 1; i <= 3; i++ { suites = appendSuites(suites, rand>>i%4) } return suites } // 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 } } type chunk struct { data []byte encLevel protocol.EncryptionLevel } type stream struct { chunkChan chan<- chunk encLevel protocol.EncryptionLevel } func (s *stream) Write(b []byte) (int, error) { data := append([]byte{}, b...) select { case s.chunkChan <- chunk{data: data, encLevel: s.encLevel}: default: panic("chunkChan too small") } return len(b), nil } func initStreams() (chan chunk, *stream /* initial */, *stream /* handshake */) { chunkChan := make(chan chunk, 10) initialStream := &stream{chunkChan: chunkChan, encLevel: protocol.EncryptionInitial} handshakeStream := &stream{chunkChan: chunkChan, encLevel: protocol.EncryptionHandshake} return chunkChan, initialStream, handshakeStream } type handshakeRunner interface { OnReceivedParams(*wire.TransportParameters) OnHandshakeComplete() OnError(error) DropKeys(protocol.EncryptionLevel) } type runner struct { sync.Mutex errored bool client, server *handshake.CryptoSetup } var _ handshakeRunner = &runner{} func newRunner(client, server *handshake.CryptoSetup) *runner { return &runner{client: client, server: server} } func (r *runner) OnReceivedParams(*wire.TransportParameters) {} func (r *runner) OnHandshakeComplete() {} func (r *runner) OnError(err error) { r.Lock() defer r.Unlock() if r.errored { return } r.errored = true (*r.client).Close() (*r.server).Close() } func (r *runner) Errored() bool { r.Lock() defer r.Unlock() return r.errored } func (r *runner) DropKeys(protocol.EncryptionLevel) {} 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 maxEncLevel(cs handshake.CryptoSetup, encLevel protocol.EncryptionLevel) protocol.EncryptionLevel { //nolint:exhaustive switch encLevel { case protocol.EncryptionInitial: return protocol.EncryptionInitial case protocol.EncryptionHandshake: // Handshake opener not available. We can't possibly read a Handshake handshake message. if opener, err := cs.GetHandshakeOpener(); err != nil || opener == nil { return protocol.EncryptionInitial } return protocol.EncryptionHandshake case protocol.Encryption1RTT: // 1-RTT opener not available. We can't possibly read a post-handshake message. if opener, err := cs.Get1RTTOpener(); err != nil || opener == nil { return maxEncLevel(cs, protocol.EncryptionHandshake) } return protocol.Encryption1RTT default: panic("unexpected encryption level") } } func getTransportParameters(seed uint8) *wire.TransportParameters { const maxVarInt = math.MaxUint64 / 4 r := mrand.New(mrand.NewSource(int64(seed))) return &wire.TransportParameters{ 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{ 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{ Certificates: []tls.Certificate{*cert}, NextProtos: []string{alpn}, SessionTicketKey: sessionTicketKey, } 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) clientConf.CipherSuites = getSuites(runConfig[0] >> 6) serverConf.ClientAuth = getClientAuth(runConfig[1] & 0b00000111) serverConf.CipherSuites = getSuites(runConfig[1] >> 6) serverConf.SessionTicketsDisabled = helper.NthBit(runConfig[1], 3) clientConf.PreferServerCipherSuites = helper.NthBit(runConfig[1], 4) 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 = ioutil.Discard } if helper.NthBit(runConfig[3], 7) { clientConf.KeyLogWriter = ioutil.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) cChunkChan, cInitialStream, cHandshakeStream := initStreams() var client, server handshake.CryptoSetup runner := newRunner(&client, &server) client, _ = handshake.NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, clientTP, runner, clientConf, enable0RTTClient, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) sChunkChan, sInitialStream, sHandshakeStream := initStreams() server = handshake.NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, serverTP, runner, serverConf, enable0RTTServer, utils.NewRTTStats(), nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) if len(data) == 0 { return -1 } serverHandshakeCompleted := make(chan struct{}) go func() { defer close(serverHandshakeCompleted) server.RunHandshake() }() clientHandshakeCompleted := make(chan struct{}) go func() { defer close(clientHandshakeCompleted) client.RunHandshake() }() done := make(chan struct{}) go func() { <-serverHandshakeCompleted <-clientHandshakeCompleted close(done) }() messageLoop: for { select { case c := <-cChunkChan: b := c.data encLevel := c.encLevel if len(b) > 0 && b[0] == messageToReplace { fmt.Printf("replacing %s message to the server with %s\n", messageType(b[0]), messageType(data[0])) b = data encLevel = maxEncLevel(server, messageToReplaceEncLevel) } server.HandleMessage(b, encLevel) case c := <-sChunkChan: b := c.data encLevel := c.encLevel if len(b) > 0 && b[0] == messageToReplace { fmt.Printf("replacing %s message to the client with %s\n", messageType(b[0]), messageType(data[0])) b = data encLevel = maxEncLevel(client, messageToReplaceEncLevel) } client.HandleMessage(b, encLevel) case <-done: // test done break messageLoop } if runner.Errored() { break messageLoop } } <-done _ = client.ConnectionState() _ = server.ConnectionState() if runner.Errored() { return 0 } 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 { client.HandleMessage(data, messageToReplaceEncLevel) } if sendPostHandshakeMessageToServer { server.HandleMessage(data, messageToReplaceEncLevel) } return 1 } quic-go-0.25.0/fuzzing/header/000077500000000000000000000000001417145451600160765ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/header/cmd/000077500000000000000000000000001417145451600166415ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/header/cmd/corpus.go000066400000000000000000000103721417145451600205060ustar00rootroot00000000000000package main import ( "bytes" "log" "math/rand" "github.com/lucas-clemente/quic-go/fuzzing/header" "github.com/lucas-clemente/quic-go/fuzzing/internal/helper" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) const version = protocol.VersionTLS func getRandomData(l int) []byte { b := make([]byte, l) rand.Read(b) return b } func getVNP(src, dest protocol.ConnectionID, numVersions int) []byte { versions := make([]protocol.VersionNumber, numVersions) for i := 0; i < numVersions; i++ { versions[i] = protocol.VersionNumber(rand.Uint32()) } data, err := wire.ComposeVersionNegotiation(src, dest, versions) if err != nil { log.Fatal(err) } return data } func main() { headers := []wire.Header{ { // Initial without token IsLongHeader: true, SrcConnectionID: protocol.ConnectionID(getRandomData(3)), DestConnectionID: protocol.ConnectionID(getRandomData(8)), Type: protocol.PacketTypeInitial, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Initial without token, with zero-length src conn id IsLongHeader: true, DestConnectionID: protocol.ConnectionID(getRandomData(8)), Type: protocol.PacketTypeInitial, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Initial with Token IsLongHeader: true, SrcConnectionID: protocol.ConnectionID(getRandomData(10)), DestConnectionID: protocol.ConnectionID(getRandomData(19)), Type: protocol.PacketTypeInitial, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, Token: getRandomData(25), }, { // Handshake packet IsLongHeader: true, SrcConnectionID: protocol.ConnectionID(getRandomData(5)), DestConnectionID: protocol.ConnectionID(getRandomData(10)), Type: protocol.PacketTypeHandshake, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Handshake packet, with zero-length src conn id IsLongHeader: true, DestConnectionID: protocol.ConnectionID(getRandomData(12)), Type: protocol.PacketTypeHandshake, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // 0-RTT packet IsLongHeader: true, SrcConnectionID: protocol.ConnectionID(getRandomData(8)), DestConnectionID: protocol.ConnectionID(getRandomData(9)), Type: protocol.PacketType0RTT, Length: protocol.ByteCount(rand.Intn(1000)), Version: version, }, { // Retry Packet, with empty orig dest conn id IsLongHeader: true, SrcConnectionID: protocol.ConnectionID(getRandomData(8)), DestConnectionID: protocol.ConnectionID(getRandomData(9)), Type: protocol.PacketTypeRetry, Token: getRandomData(1000), Version: version, }, { // Short-Header DestConnectionID: protocol.ConnectionID(getRandomData(8)), }, } for _, h := range headers { extHdr := &wire.ExtendedHeader{ Header: h, PacketNumberLen: protocol.PacketNumberLen(rand.Intn(4) + 1), PacketNumber: protocol.PacketNumber(rand.Uint64()), } b := &bytes.Buffer{} if err := extHdr.Write(b, version); err != nil { log.Fatal(err) } if h.Type == protocol.PacketTypeRetry { b.Write([]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}) } if h.Length > 0 { b.Write(make([]byte, h.Length)) } if err := helper.WriteCorpusFileWithPrefix("corpus", b.Bytes(), header.PrefixLen); err != nil { log.Fatal(err) } } vnps := [][]byte{ getVNP( protocol.ConnectionID(getRandomData(8)), protocol.ConnectionID(getRandomData(10)), 4, ), getVNP( protocol.ConnectionID(getRandomData(10)), protocol.ConnectionID(getRandomData(5)), 0, ), getVNP( protocol.ConnectionID(getRandomData(3)), protocol.ConnectionID(getRandomData(19)), 100, ), getVNP( protocol.ConnectionID(getRandomData(3)), nil, 20, ), getVNP( nil, protocol.ConnectionID(getRandomData(10)), 5, ), } for _, vnp := range vnps { if err := helper.WriteCorpusFileWithPrefix("corpus", vnp, header.PrefixLen); err != nil { log.Fatal(err) } } } quic-go-0.25.0/fuzzing/header/fuzz.go000066400000000000000000000052011417145451600174210ustar00rootroot00000000000000package header import ( "bytes" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) const version = protocol.VersionTLS // 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 } is0RTTPacket := wire.Is0RTTPacket(data) hdr, _, _, err := wire.ParsePacket(data, connIDLen) if err != nil { return 0 } if !hdr.DestConnectionID.Equal(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(bytes.NewReader(data), version) 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.IsLongHeader && hdr.Length > 16383 { return 1 } b := &bytes.Buffer{} if err := extHdr.Write(b, version); 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(b.Len()) { panic(fmt.Sprintf("inconsistent header length: %#v. Expected %d, got %d", extHdr, expLen, b.Len())) } } return 1 } func fuzzVNP(data []byte) int { connID, err := wire.ParseConnectionID(data, 0) if err != nil { return 0 } hdr, versions, err := wire.ParseVersionNegotiationPacket(bytes.NewReader(data)) if err != nil { return 0 } if !hdr.DestConnectionID.Equal(connID) { panic("connection IDs don't match") } if len(versions) == 0 { panic("no versions") } if _, err := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, versions); err != nil { panic(err) } return 1 } quic-go-0.25.0/fuzzing/internal/000077500000000000000000000000001417145451600164625ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/internal/helper/000077500000000000000000000000001417145451600177415ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/internal/helper/helper.go000066400000000000000000000044041417145451600215510ustar00rootroot00000000000000package helper import ( "crypto" "crypto/rand" "crypto/sha1" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "encoding/hex" "io/ioutil" "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 ioutil.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 } quic-go-0.25.0/fuzzing/internal/helper/helper_suite_test.go000066400000000000000000000002721417145451600240200ustar00rootroot00000000000000package helper import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestHelper(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Helper Suite") } quic-go-0.25.0/fuzzing/internal/helper/helper_test.go000066400000000000000000000041221417145451600226050ustar00rootroot00000000000000package helper import ( "fmt" "io/ioutil" "os" "path/filepath" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("exporting", func() { var dir string BeforeEach(func() { var err error dir, err = ioutil.TempDir("", "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 := ioutil.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 := ioutil.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()) }) }) quic-go-0.25.0/fuzzing/tokens/000077500000000000000000000000001417145451600161515ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/tokens/fuzz.go000066400000000000000000000067251417145451600175100ustar00rootroot00000000000000package tokens import ( "encoding/binary" "fmt" "math/rand" "net" "time" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" ) func Fuzz(data []byte) int { if len(data) < 8 { return -1 } seed := binary.BigEndian.Uint64(data[:8]) data = data[8:] tg, err := handshake.NewTokenGenerator(rand.New(rand.NewSource(int64(seed)))) if err != nil { panic(err) } 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 != nil || token.RetrySrcConnectionID != nil { panic("didn't expect connection IDs") } checkAddr(token.RemoteAddr, addr) 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.ConnectionID(data[:origDestConnIDLen]) data = data[origDestConnIDLen:] if len(data) < retrySrcConnIDLen { return -1 } retrySrcConnID := protocol.ConnectionID(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.Equal(origDestConnID) { panic("orig dest conn ID doesn't match") } if !token.RetrySrcConnectionID.Equal(retrySrcConnID) { panic("retry src conn ID doesn't match") } checkAddr(token.RemoteAddr, addr) return 1 } func checkAddr(tokenAddr string, addr net.Addr) { if udpAddr, ok := addr.(*net.UDPAddr); ok { // For UDP addresses, we encode only the IP (not the port). if ip := udpAddr.IP.String(); tokenAddr != ip { fmt.Printf("%s vs %s", tokenAddr, ip) panic("wrong remote address for a net.UDPAddr") } return } if tokenAddr != addr.String() { fmt.Printf("%s vs %s", tokenAddr, addr.String()) panic("wrong remote address") } } quic-go-0.25.0/fuzzing/transportparameters/000077500000000000000000000000001417145451600207665ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/transportparameters/cmd/000077500000000000000000000000001417145451600215315ustar00rootroot00000000000000quic-go-0.25.0/fuzzing/transportparameters/cmd/corpus.go000066400000000000000000000055241417145451600234010ustar00rootroot00000000000000package main import ( "bytes" "log" "math" "math/rand" "net" "time" "github.com/lucas-clemente/quic-go/fuzzing/internal/helper" "github.com/lucas-clemente/quic-go/fuzzing/transportparameters" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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(), } if rand.Int()%2 == 0 { tp.OriginalDestinationConnectionID = protocol.ConnectionID(getRandomData(rand.Intn(50))) } if rand.Int()%2 == 0 { tp.InitialSourceConnectionID = protocol.ConnectionID(getRandomData(rand.Intn(50))) } if rand.Int()%2 == 0 { connID := protocol.ConnectionID(getRandomData(rand.Intn(50))) 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[:]) tp.PreferredAddress = &wire.PreferredAddress{ IPv4: net.IPv4(uint8(rand.Int()), uint8(rand.Int()), uint8(rand.Int()), uint8(rand.Int())), IPv4Port: uint16(rand.Int()), IPv6: net.IP(getRandomData(16)), IPv6Port: uint16(rand.Int()), ConnectionID: protocol.ConnectionID(getRandomData(rand.Intn(25))), 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 { b := &bytes.Buffer{} tp.MarshalForSessionTicket(b) data = b.Bytes() } if err := helper.WriteCorpusFileWithPrefix("corpus", data, transportparameters.PrefixLen); err != nil { log.Fatal(err) } } } quic-go-0.25.0/fuzzing/transportparameters/fuzz.go000066400000000000000000000027351417145451600223220ustar00rootroot00000000000000package transportparameters import ( "bytes" "fmt" "github.com/lucas-clemente/quic-go/fuzzing/internal/helper" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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, isServer bool) int { perspective := protocol.PerspectiveClient if isServer { perspective = protocol.PerspectiveServer } tp := &wire.TransportParameters{} if err := tp.Unmarshal(data, perspective); err != nil { return 0 } _ = tp.String() tp2 := &wire.TransportParameters{} if err := tp2.Unmarshal(tp.Marshal(perspective), perspective); err != nil { fmt.Printf("%#v\n", tp) panic(err) } return 1 } func fuzzTransportParametersForSessionTicket(data []byte) int { tp := &wire.TransportParameters{} if err := tp.UnmarshalFromSessionTicket(bytes.NewReader(data)); err != nil { return 0 } buf := &bytes.Buffer{} tp.MarshalForSessionTicket(buf) tp2 := &wire.TransportParameters{} if err := tp2.UnmarshalFromSessionTicket(bytes.NewReader(buf.Bytes())); err != nil { panic(err) } return 1 } quic-go-0.25.0/go.mod000066400000000000000000000012731417145451600142630ustar00rootroot00000000000000module github.com/lucas-clemente/quic-go go 1.16 require ( github.com/cheekybits/genny v1.0.0 github.com/francoispqt/gojay v1.2.13 github.com/golang/mock v1.6.0 github.com/marten-seemann/qpack v0.2.1 github.com/marten-seemann/qtls-go1-16 v0.1.4 github.com/marten-seemann/qtls-go1-17 v0.1.0 github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.13.0 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20210510120138-977fb7262007 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) quic-go-0.25.0/go.sum000066400000000000000000000701371417145451600143150ustar00rootroot00000000000000cloud.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/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/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 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/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/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.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2iHl5yhJRpnco= github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk= github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8= github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM= github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI= 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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= 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_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 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.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= 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.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 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.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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 v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 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= quic-go-0.25.0/http3/000077500000000000000000000000001417145451600142145ustar00rootroot00000000000000quic-go-0.25.0/http3/body.go000066400000000000000000000037721417145451600155110ustar00rootroot00000000000000package http3 import ( "fmt" "io" "github.com/lucas-clemente/quic-go" ) // The body of a http.Request or http.Response. type body struct { str quic.Stream // 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 onFrameError func() bytesRemainingInFrame uint64 } var _ io.ReadCloser = &body{} func newRequestBody(str quic.Stream, onFrameError func()) *body { return &body{ str: str, onFrameError: onFrameError, } } func newResponseBody(str quic.Stream, done chan<- struct{}, onFrameError func()) *body { return &body{ str: str, onFrameError: onFrameError, reqDone: done, } } func (r *body) Read(b []byte) (int, error) { n, err := r.readImpl(b) if err != nil { r.requestDone() } return n, err } func (r *body) readImpl(b []byte) (int, error) { if r.bytesRemainingInFrame == 0 { parseLoop: for { frame, err := parseNextFrame(r.str) if err != nil { return 0, err } switch f := frame.(type) { case *headersFrame: // skip HEADERS frames continue case *dataFrame: r.bytesRemainingInFrame = f.Length break parseLoop default: r.onFrameError() // 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 r.bytesRemainingInFrame < uint64(len(b)) { n, err = r.str.Read(b[:r.bytesRemainingInFrame]) } else { n, err = r.str.Read(b) } r.bytesRemainingInFrame -= uint64(n) return n, err } func (r *body) requestDone() { if r.reqDoneClosed || r.reqDone == nil { return } close(r.reqDone) r.reqDoneClosed = true } func (r *body) Close() error { r.requestDone() // If the EOF was read, CancelRead() is a no-op. r.str.CancelRead(quic.StreamErrorCode(errorRequestCanceled)) return nil } quic-go-0.25.0/http3/body_test.go000066400000000000000000000117501417145451600165430ustar00rootroot00000000000000package http3 import ( "bytes" "fmt" "io" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go" mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type bodyType uint8 const ( bodyTypeRequest bodyType = iota bodyTypeResponse ) func (t bodyType) String() string { if t == bodyTypeRequest { return "request" } return "response" } var _ = Describe("Body", func() { var ( rb *body str *mockquic.MockStream buf *bytes.Buffer reqDone chan struct{} errorCbCalled bool ) errorCb := func() { errorCbCalled = true } getDataFrame := func(data []byte) []byte { b := &bytes.Buffer{} (&dataFrame{Length: uint64(len(data))}).Write(b) b.Write(data) return b.Bytes() } BeforeEach(func() { buf = &bytes.Buffer{} errorCbCalled = false }) for _, bt := range []bodyType{bodyTypeRequest, bodyTypeResponse} { bodyType := bt Context(fmt.Sprintf("using a %s body", bodyType), func() { BeforeEach(func() { str = mockquic.NewMockStream(mockCtrl) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(b []byte) (int, error) { return buf.Write(b) }).AnyTimes() str.EXPECT().Read(gomock.Any()).DoAndReturn(func(b []byte) (int, error) { return buf.Read(b) }).AnyTimes() switch bodyType { case bodyTypeRequest: rb = newRequestBody(str, errorCb) case bodyTypeResponse: reqDone = make(chan struct{}) rb = newResponseBody(str, reqDone, errorCb) } }) It("reads DATA frames in a single run", func() { buf.Write(getDataFrame([]byte("foobar"))) b := make([]byte, 6) n, err := rb.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 := rb.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b).To(Equal([]byte("foo"))) n, err = rb.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 := rb.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 := rb.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte("foob"))) n, err = rb.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 := rb.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b[:n]).To(Equal([]byte("foo"))) n, err = rb.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) Expect(b[:n]).To(Equal([]byte("bar"))) }) It("skips HEADERS frames", func() { buf.Write(getDataFrame([]byte("foo"))) (&headersFrame{Length: 10}).Write(buf) buf.Write(make([]byte, 10)) buf.Write(getDataFrame([]byte("bar"))) b := make([]byte, 6) n, err := io.ReadFull(rb, b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) Expect(b).To(Equal([]byte("foobar"))) }) It("errors when it can't parse the frame", func() { buf.Write([]byte("invalid")) _, err := rb.Read([]byte{0}) Expect(err).To(HaveOccurred()) }) It("errors on unexpected frames, and calls the error callback", func() { (&settingsFrame{}).Write(buf) _, err := rb.Read([]byte{0}) Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame")) Expect(errorCbCalled).To(BeTrue()) }) if bodyType == bodyTypeResponse { It("closes the reqDone channel when Read errors", func() { buf.Write([]byte("invalid")) _, err := rb.Read([]byte{0}) Expect(err).To(HaveOccurred()) Expect(reqDone).To(BeClosed()) }) It("allows multiple calls to Read, when Read errors", func() { buf.Write([]byte("invalid")) _, 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.EXPECT().CancelRead(quic.StreamErrorCode(errorRequestCanceled)) Expect(rb.Close()).To(Succeed()) }) It("allows multiple calls to Close", func() { str.EXPECT().CancelRead(quic.StreamErrorCode(errorRequestCanceled)).MaxTimes(2) Expect(rb.Close()).To(Succeed()) Expect(reqDone).To(BeClosed()) Expect(rb.Close()).To(Succeed()) }) } }) } }) quic-go-0.25.0/http3/client.go000066400000000000000000000242071417145451600160260ustar00rootroot00000000000000package http3 import ( "bytes" "context" "crypto/tls" "errors" "fmt" "io" "net/http" "strconv" "sync" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/quicvarint" "github.com/marten-seemann/qpack" ) // 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" 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 KeepAlive: true, Versions: []protocol.VersionNumber{protocol.VersionTLS}, } var dialAddr = quic.DialAddrEarly type roundTripperOpts struct { DisableCompression bool EnableDatagram bool MaxHeaderBytes int64 } // client is a HTTP3 client doing requests type client struct { tlsConf *tls.Config config *quic.Config opts *roundTripperOpts dialOnce sync.Once dialer func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error) handshakeErr error requestWriter *requestWriter decoder *qpack.Decoder hostname string session quic.EarlySession logger utils.Logger } func newClient( hostname string, tlsConf *tls.Config, opts *roundTripperOpts, quicConfig *quic.Config, dialer func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error), ) (*client, error) { if quicConfig == nil { quicConfig = defaultQuicConfig.Clone() } else if len(quicConfig.Versions) == 0 { quicConfig = quicConfig.Clone() quicConfig.Versions = []quic.VersionNumber{defaultQuicConfig.Versions[0]} } if len(quicConfig.Versions) != 1 { return nil, errors.New("can only use a single QUIC version for dialing a HTTP/3 connection") } quicConfig.MaxIncomingStreams = -1 // don't allow any bidirectional streams quicConfig.EnableDatagrams = opts.EnableDatagram logger := utils.DefaultLogger.WithPrefix("h3 client") if tlsConf == nil { tlsConf = &tls.Config{} } else { tlsConf = tlsConf.Clone() } // Replace existing ALPNs by H3 tlsConf.NextProtos = []string{versionToALPN(quicConfig.Versions[0])} return &client{ hostname: authorityAddr("https", hostname), tlsConf: tlsConf, requestWriter: newRequestWriter(logger), decoder: qpack.NewDecoder(func(hf qpack.HeaderField) {}), config: quicConfig, opts: opts, dialer: dialer, logger: logger, }, nil } func (c *client) dial() error { var err error if c.dialer != nil { c.session, err = c.dialer("udp", c.hostname, c.tlsConf, c.config) } else { c.session, err = dialAddr(c.hostname, c.tlsConf, c.config) } if err != nil { return err } // send the SETTINGs frame, using 0-RTT data, if possible go func() { if err := c.setupSession(); err != nil { c.logger.Debugf("Setting up session failed: %s", err) c.session.CloseWithError(quic.ApplicationErrorCode(errorInternalError), "") } }() go c.handleUnidirectionalStreams() return nil } func (c *client) setupSession() error { // open the control stream str, err := c.session.OpenUniStream() if err != nil { return err } buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) // send the SETTINGS frame (&settingsFrame{Datagram: c.opts.EnableDatagram}).Write(buf) _, err = str.Write(buf.Bytes()) return err } func (c *client) handleUnidirectionalStreams() { for { str, err := c.session.AcceptUniStream(context.Background()) if err != nil { c.logger.Debugf("accepting unidirectional stream failed: %s", err) return } go func() { streamType, err := quicvarint.Read(quicvarint.NewReader(str)) if err != nil { c.logger.Debugf("reading stream type on stream %d failed: %s", str.StreamID(), err) return } // We're only interested in the control stream here. switch streamType { case streamTypeControlStream: case streamTypeQPACKEncoderStream, streamTypeQPACKDecoderStream: // Our QPACK implementation doesn't use the dynamic table yet. // TODO: check that only one stream of each type is opened. return case streamTypePushStream: // We never increased the Push ID, so we don't expect any push streams. c.session.CloseWithError(quic.ApplicationErrorCode(errorIDError), "") return default: str.CancelRead(quic.StreamErrorCode(errorStreamCreationError)) return } f, err := parseNextFrame(str) if err != nil { c.session.CloseWithError(quic.ApplicationErrorCode(errorFrameError), "") return } sf, ok := f.(*settingsFrame) if !ok { c.session.CloseWithError(quic.ApplicationErrorCode(errorMissingSettings), "") return } 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.opts.EnableDatagram && !c.session.ConnectionState().SupportsDatagrams { c.session.CloseWithError(quic.ApplicationErrorCode(errorSettingsError), "missing QUIC Datagram support") } }() } } func (c *client) Close() error { if c.session == nil { return nil } return c.session.CloseWithError(quic.ApplicationErrorCode(errorNoError), "") } func (c *client) maxHeaderBytes() uint64 { if c.opts.MaxHeaderBytes <= 0 { return defaultMaxResponseHeaderBytes } return uint64(c.opts.MaxHeaderBytes) } // RoundTrip executes a request and returns a response func (c *client) RoundTrip(req *http.Request) (*http.Response, error) { if authorityAddr("https", hostnameFromRequest(req)) != c.hostname { return nil, fmt.Errorf("http3 client BUG: RoundTrip called for the wrong client (expected %s, got %s)", c.hostname, req.Host) } c.dialOnce.Do(func() { c.handshakeErr = c.dial() }) if c.handshakeErr != nil { return nil, c.handshakeErr } // Immediately send out this request, if this is a 0-RTT request. if req.Method == MethodGet0RTT { req.Method = http.MethodGet } else { // wait for the handshake to complete select { case <-c.session.HandshakeComplete().Done(): case <-req.Context().Done(): return nil, req.Context().Err() } } str, err := c.session.OpenStreamSync(req.Context()) if err != nil { return nil, err } // Request Cancellation: // This go routine keeps running even after RoundTrip() returns. // It is shut down when the application is done processing the body. reqDone := make(chan struct{}) go func() { select { case <-req.Context().Done(): str.CancelWrite(quic.StreamErrorCode(errorRequestCanceled)) str.CancelRead(quic.StreamErrorCode(errorRequestCanceled)) case <-reqDone: } }() rsp, rerr := c.doRequest(req, str, reqDone) if rerr.err != nil { // if any error occurred close(reqDone) if rerr.streamErr != 0 { // if it was a stream error str.CancelWrite(quic.StreamErrorCode(rerr.streamErr)) } if rerr.connErr != 0 { // if it was a connection error var reason string if rerr.err != nil { reason = rerr.err.Error() } c.session.CloseWithError(quic.ApplicationErrorCode(rerr.connErr), reason) } } return rsp, rerr.err } func (c *client) doRequest( req *http.Request, str quic.Stream, reqDone chan struct{}, ) (*http.Response, requestError) { var requestGzip bool if !c.opts.DisableCompression && req.Method != "HEAD" && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" { requestGzip = true } if err := c.requestWriter.WriteRequest(str, req, requestGzip); err != nil { return nil, newStreamError(errorInternalError, err) } frame, err := parseNextFrame(str) if err != nil { return nil, newStreamError(errorFrameError, err) } hf, ok := frame.(*headersFrame) if !ok { return nil, newConnError(errorFrameUnexpected, errors.New("expected first frame to be a HEADERS frame")) } if hf.Length > c.maxHeaderBytes() { return nil, newStreamError(errorFrameError, fmt.Errorf("HEADERS frame too large: %d bytes (max: %d)", hf.Length, c.maxHeaderBytes())) } headerBlock := make([]byte, hf.Length) if _, err := io.ReadFull(str, headerBlock); err != nil { return nil, newStreamError(errorRequestIncomplete, err) } hfs, err := c.decoder.DecodeFull(headerBlock) if err != nil { // TODO: use the right error code return nil, newConnError(errorGeneralProtocolError, err) } connState := qtls.ToTLSConnectionState(c.session.ConnectionState().TLS) res := &http.Response{ Proto: "HTTP/3", ProtoMajor: 3, Header: http.Header{}, TLS: &connState, } for _, hf := range hfs { switch hf.Name { case ":status": status, err := strconv.Atoi(hf.Value) if err != nil { return nil, newStreamError(errorGeneralProtocolError, errors.New("malformed non-numeric status pseudo header")) } res.StatusCode = status res.Status = hf.Value + " " + http.StatusText(status) default: res.Header.Add(hf.Name, hf.Value) } } respBody := newResponseBody(str, reqDone, func() { c.session.CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), "") }) // 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 == 204 isSuccessfulConnect := req.Method == http.MethodConnect && 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 requestGzip && res.Header.Get("Content-Encoding") == "gzip" { res.Header.Del("Content-Encoding") res.Header.Del("Content-Length") res.ContentLength = -1 res.Body = newGzipReader(respBody) res.Uncompressed = true } else { res.Body = respBody } return res, requestError{} } quic-go-0.25.0/http3/client_test.go000066400000000000000000000706571417145451600170770ustar00rootroot00000000000000package http3 import ( "bytes" "compress/gzip" "context" "crypto/tls" "errors" "fmt" "io" "io/ioutil" "net/http" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go" mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic" "github.com/lucas-clemente/quic-go/quicvarint" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/marten-seemann/qpack" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Client", func() { var ( client *client req *http.Request origDialAddr = dialAddr handshakeCtx context.Context // an already canceled context ) BeforeEach(func() { origDialAddr = dialAddr hostname := "quic.clemente.io:1337" var err error client, err = newClient(hostname, nil, &roundTripperOpts{MaxHeaderBytes: 1337}, nil, nil) Expect(err).ToNot(HaveOccurred()) Expect(client.hostname).To(Equal(hostname)) req, err = http.NewRequest("GET", "https://localhost:1337", nil) Expect(err).ToNot(HaveOccurred()) ctx, cancel := context.WithCancel(context.Background()) cancel() handshakeCtx = ctx }) AfterEach(func() { dialAddr = origDialAddr }) It("rejects quic.Configs that allow multiple QUIC versions", func() { qconf := &quic.Config{ Versions: []quic.VersionNumber{protocol.VersionDraft29, protocol.Version1}, } _, err := newClient("localhost:1337", nil, &roundTripperOpts{}, qconf, nil) 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() { client, err := newClient("localhost:1337", nil, &roundTripperOpts{}, nil, nil) Expect(err).ToNot(HaveOccurred()) var dialAddrCalled bool dialAddr = func(_ string, tlsConf *tls.Config, quicConf *quic.Config) (quic.EarlySession, error) { Expect(quicConf).To(Equal(defaultQuicConfig)) Expect(tlsConf.NextProtos).To(Equal([]string{nextProtoH3})) Expect(quicConf.Versions).To(Equal([]protocol.VersionNumber{protocol.Version1})) dialAddrCalled = true return nil, errors.New("test done") } client.RoundTrip(req) Expect(dialAddrCalled).To(BeTrue()) }) It("adds the port to the hostname, if none is given", func() { client, err := newClient("quic.clemente.io", nil, &roundTripperOpts{}, nil, nil) Expect(err).ToNot(HaveOccurred()) var dialAddrCalled bool dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlySession, error) { 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()) client.RoundTrip(req) Expect(dialAddrCalled).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: time.Nanosecond} client, err := newClient("localhost:1337", tlsConf, &roundTripperOpts{}, quicConf, nil) Expect(err).ToNot(HaveOccurred()) var dialAddrCalled bool dialAddr = func( hostname string, tlsConfP *tls.Config, quicConfP *quic.Config, ) (quic.EarlySession, error) { Expect(hostname).To(Equal("localhost:1337")) 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") } client.RoundTrip(req) 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} var dialerCalled bool dialer := func(network, address string, tlsConfP *tls.Config, quicConfP *quic.Config) (quic.EarlySession, error) { Expect(network).To(Equal("udp")) Expect(address).To(Equal("localhost:1337")) Expect(tlsConfP.ServerName).To(Equal("foo.bar")) Expect(quicConfP.MaxIdleTimeout).To(Equal(quicConf.MaxIdleTimeout)) dialerCalled = true return nil, testErr } client, err := newClient("localhost:1337", tlsConf, &roundTripperOpts{}, quicConf, dialer) Expect(err).ToNot(HaveOccurred()) _, err = client.RoundTrip(req) Expect(err).To(MatchError(testErr)) Expect(dialerCalled).To(BeTrue()) }) It("enables HTTP/3 Datagrams", func() { testErr := errors.New("handshake error") client, err := newClient("localhost:1337", nil, &roundTripperOpts{EnableDatagram: true}, nil, nil) Expect(err).ToNot(HaveOccurred()) dialAddr = func(hostname string, _ *tls.Config, quicConf *quic.Config) (quic.EarlySession, error) { Expect(quicConf.EnableDatagrams).To(BeTrue()) return nil, testErr } _, err = client.RoundTrip(req) Expect(err).To(MatchError(testErr)) }) It("errors when dialing fails", func() { testErr := errors.New("handshake error") client, err := newClient("localhost:1337", nil, &roundTripperOpts{}, nil, nil) Expect(err).ToNot(HaveOccurred()) dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlySession, error) { return nil, testErr } _, err = client.RoundTrip(req) Expect(err).To(MatchError(testErr)) }) It("closes correctly if session was not created", func() { client, err := newClient("localhost:1337", nil, &roundTripperOpts{}, nil, nil) Expect(err).ToNot(HaveOccurred()) Expect(client.Close()).To(Succeed()) }) Context("validating the address", func() { It("refuses to do requests for the wrong host", func() { req, err := http.NewRequest("https", "https://quic.clemente.io:1336/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) _, err = client.RoundTrip(req) Expect(err).To(MatchError("http3 client BUG: RoundTrip called for the wrong client (expected quic.clemente.io:1337, got quic.clemente.io:1336)")) }) It("allows requests using a different scheme", func() { testErr := errors.New("handshake error") req, err := http.NewRequest("masque", "masque://quic.clemente.io:1337/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlySession, error) { return nil, testErr } _, err = client.RoundTrip(req) Expect(err).To(MatchError(testErr)) }) }) Context("control stream handling", func() { var ( request *http.Request sess *mockquic.MockEarlySession settingsFrameWritten chan struct{} ) testDone := make(chan struct{}) BeforeEach(func() { settingsFrameWritten = make(chan struct{}) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()).Do(func(b []byte) { defer GinkgoRecover() close(settingsFrameWritten) }) sess = mockquic.NewMockEarlySession(mockCtrl) sess.EXPECT().OpenUniStream().Return(controlStr, nil) sess.EXPECT().HandshakeComplete().Return(handshakeCtx) sess.EXPECT().OpenStreamSync(gomock.Any()).Return(nil, errors.New("done")) dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlySession, error) { return sess, nil } 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("parses the SETTINGS frame", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) (&settingsFrame{}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to sess.CloseWithError }) 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() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamType) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return str, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to str.CancelRead }) } It("resets streams other than the control stream and the QPACK streams", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, 1337) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() done := make(chan struct{}) str.EXPECT().CancelRead(quic.StreamErrorCode(errorStreamCreationError)).Do(func(code quic.StreamErrorCode) { close(done) }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return str, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) }) It("errors when the first frame on the control stream is not a SETTINGS frame", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) (&dataFrame{}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorMissingSettings)) close(done) }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) }) It("errors when parsing the frame on the control stream fails", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) b := &bytes.Buffer{} (&settingsFrame{}).Write(b) buf.Write(b.Bytes()[:b.Len()-1]) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorFrameError)) close(done) }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) }) It("errors when parsing the server opens a push stream", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypePushStream) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorIDError)) close(done) }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) }) It("errors when the server advertises datagram support (and we enabled support for it)", func() { client.opts.EnableDatagram = true buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) (&settingsFrame{Datagram: true}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) sess.EXPECT().ConnectionState().Return(quic.ConnectionState{SupportsDatagrams: false}) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, reason string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorSettingsError)) Expect(reason).To(Equal("missing QUIC Datagram support")) close(done) }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("done")) Eventually(done).Should(BeClosed()) }) }) Context("Doing requests", func() { var ( request *http.Request str *mockquic.MockStream sess *mockquic.MockEarlySession settingsFrameWritten chan struct{} ) testDone := make(chan struct{}) getHeadersFrame := func(headers map[string]string) []byte { buf := &bytes.Buffer{} headerBuf := &bytes.Buffer{} enc := qpack.NewEncoder(headerBuf) for name, value := range headers { Expect(enc.WriteField(qpack.HeaderField{Name: name, Value: value})).To(Succeed()) } Expect(enc.Close()).To(Succeed()) (&headersFrame{Length: uint64(headerBuf.Len())}).Write(buf) buf.Write(headerBuf.Bytes()) return buf.Bytes() } decodeHeader := func(str io.Reader) map[string]string { fields := make(map[string]string) decoder := qpack.NewDecoder(nil) frame, err := parseNextFrame(str) 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 } getResponse := func(status int) []byte { buf := &bytes.Buffer{} rstr := mockquic.NewMockStream(mockCtrl) rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes() rw := newResponseWriter(rstr, utils.DefaultLogger) rw.WriteHeader(status) rw.Flush() return buf.Bytes() } BeforeEach(func() { settingsFrameWritten = make(chan struct{}) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()).Do(func(b []byte) { defer GinkgoRecover() r := bytes.NewReader(b) streamType, err := quicvarint.Read(r) Expect(err).ToNot(HaveOccurred()) Expect(streamType).To(BeEquivalentTo(streamTypeControlStream)) close(settingsFrameWritten) }) // SETTINGS frame str = mockquic.NewMockStream(mockCtrl) sess = mockquic.NewMockEarlySession(mockCtrl) sess.EXPECT().OpenUniStream().Return(controlStr, nil) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) dialAddr = func(hostname string, _ *tls.Config, _ *quic.Config) (quic.EarlySession, error) { return sess, nil } 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("errors if it can't open a stream", func() { testErr := errors.New("stream open error") sess.EXPECT().OpenStreamSync(context.Background()).Return(nil, testErr) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).MaxTimes(1) sess.EXPECT().HandshakeComplete().Return(handshakeCtx) _, err := client.RoundTrip(request) Expect(err).To(MatchError(testErr)) }) It("performs a 0-RTT request", func() { testErr := errors.New("stream open error") request.Method = MethodGet0RTT // don't EXPECT any calls to HandshakeComplete() sess.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().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { return 0, testErr }) _, err := client.RoundTrip(request) Expect(err).To(MatchError(testErr)) Expect(decodeHeader(buf)).To(HaveKeyWithValue(":method", "GET")) }) It("returns a response", func() { rspBuf := bytes.NewBuffer(getResponse(418)) gomock.InOrder( sess.EXPECT().HandshakeComplete().Return(handshakeCtx), sess.EXPECT().OpenStreamSync(context.Background()).Return(str, nil), sess.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 := client.RoundTrip(request) Expect(err).ToNot(HaveOccurred()) Expect(rsp.Proto).To(Equal("HTTP/3")) Expect(rsp.ProtoMajor).To(Equal(3)) Expect(rsp.StatusCode).To(Equal(418)) }) Context("requests containing a Body", func() { var strBuf *bytes.Buffer BeforeEach(func() { strBuf = &bytes.Buffer{} gomock.InOrder( sess.EXPECT().HandshakeComplete().Return(handshakeCtx), sess.EXPECT().OpenStreamSync(context.Background()).Return(str, nil), ) body := &mockBody{} body.SetData([]byte("request body")) var err error request, 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() { close(done) }), str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1), // when reading the response errors ) // the response body is sent asynchronously, while already reading the response str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { <-done return 0, errors.New("test done") }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("test done")) hfs := decodeHeader(strBuf) Expect(hfs).To(HaveKeyWithValue(":method", "POST")) Expect(hfs).To(HaveKeyWithValue(":path", "/upload")) }) It("returns the error that occurred when reading the body", func() { request.Body.(*mockBody).readErr = errors.New("testErr") done := make(chan struct{}) gomock.InOrder( str.EXPECT().CancelWrite(quic.StreamErrorCode(errorRequestCanceled)).Do(func(quic.StreamErrorCode) { close(done) }), str.EXPECT().CancelWrite(gomock.Any()), ) // the response body is sent asynchronously, while already reading the response str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { <-done return 0, errors.New("test done") }) _, err := client.RoundTrip(request) Expect(err).To(MatchError("test done")) }) It("sets the Content-Length", func() { done := make(chan struct{}) buf := &bytes.Buffer{} buf.Write(getHeadersFrame(map[string]string{ ":status": "200", "Content-Length": "1337", })) (&dataFrame{Length: 0x6}).Write(buf) buf.Write([]byte("foobar")) str.EXPECT().Close().Do(func() { close(done) }) sess.EXPECT().ConnectionState().Return(quic.ConnectionState{}) str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1) // when reading the response errors // the response body is sent asynchronously, while already reading the response str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() req, err := client.RoundTrip(request) Expect(err).ToNot(HaveOccurred()) Expect(req.ContentLength).To(BeEquivalentTo(1337)) Eventually(done).Should(BeClosed()) }) It("closes the connection when the first frame is not a HEADERS frame", func() { buf := &bytes.Buffer{} (&dataFrame{Length: 0x42}).Write(buf) sess.EXPECT().CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), gomock.Any()) closed := make(chan struct{}) str.EXPECT().Close().Do(func() { close(closed) }) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() _, err := client.RoundTrip(request) Expect(err).To(MatchError("expected first frame to be a HEADERS frame")) Eventually(closed).Should(BeClosed()) }) It("cancels the stream when the HEADERS frame is too large", func() { buf := &bytes.Buffer{} (&headersFrame{Length: 1338}).Write(buf) str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError)) closed := make(chan struct{}) str.EXPECT().Close().Do(func() { close(closed) }) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() _, err := client.RoundTrip(request) Expect(err).To(MatchError("HEADERS frame too large: 1338 bytes (max: 1337)")) Eventually(closed).Should(BeClosed()) }) }) Context("request cancellations", func() { It("cancels a request while waiting for the handshake to complete", func() { ctx, cancel := context.WithCancel(context.Background()) req := request.WithContext(ctx) sess.EXPECT().HandshakeComplete().Return(context.Background()) errChan := make(chan error) go func() { _, err := client.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 := request.WithContext(ctx) sess.EXPECT().HandshakeComplete().Return(handshakeCtx) sess.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(errorRequestCanceled)).Do(func(quic.StreamErrorCode) { close(canceled) }), str.EXPECT().CancelRead(quic.StreamErrorCode(errorRequestCanceled)).Do(func(quic.StreamErrorCode) { close(done) }), ) str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1) str.EXPECT().Read(gomock.Any()).DoAndReturn(func([]byte) (int, error) { cancel() <-canceled return 0, errors.New("test done") }) _, err := client.RoundTrip(req) Expect(err).To(MatchError("test done")) Eventually(done).Should(BeClosed()) }) It("cancels a request after the response arrived", func() { rspBuf := bytes.NewBuffer(getResponse(404)) ctx, cancel := context.WithCancel(context.Background()) req := request.WithContext(ctx) sess.EXPECT().HandshakeComplete().Return(handshakeCtx) sess.EXPECT().OpenStreamSync(ctx).Return(str, nil) sess.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(errorRequestCanceled)) str.EXPECT().CancelRead(quic.StreamErrorCode(errorRequestCanceled)).Do(func(quic.StreamErrorCode) { close(done) }) _, err := client.RoundTrip(req) Expect(err).ToNot(HaveOccurred()) cancel() Eventually(done).Should(BeClosed()) }) }) Context("gzip compression", func() { BeforeEach(func() { sess.EXPECT().HandshakeComplete().Return(handshakeCtx) }) It("adds the gzip header to requests", func() { sess.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) buf := &bytes.Buffer{} str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write) gomock.InOrder( str.EXPECT().Close(), str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1), // when the Read errors ) str.EXPECT().Read(gomock.Any()).Return(0, errors.New("test done")) _, err := client.RoundTrip(request) Expect(err).To(MatchError("test done")) hfs := decodeHeader(buf) Expect(hfs).To(HaveKeyWithValue("accept-encoding", "gzip")) }) It("doesn't add gzip if the header disable it", func() { client, err := newClient("quic.clemente.io:1337", nil, &roundTripperOpts{DisableCompression: true}, nil, nil) Expect(err).ToNot(HaveOccurred()) sess.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) buf := &bytes.Buffer{} str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write) gomock.InOrder( str.EXPECT().Close(), str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1), // when the Read errors ) str.EXPECT().Read(gomock.Any()).Return(0, errors.New("test done")) _, err = client.RoundTrip(request) Expect(err).To(MatchError("test done")) hfs := decodeHeader(buf) Expect(hfs).ToNot(HaveKey("accept-encoding")) }) It("decompresses the response", func() { sess.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) sess.EXPECT().ConnectionState().Return(quic.ConnectionState{}) buf := &bytes.Buffer{} rstr := mockquic.NewMockStream(mockCtrl) rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes() rw := newResponseWriter(rstr, utils.DefaultLogger) 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 := client.RoundTrip(request) Expect(err).ToNot(HaveOccurred()) data, err := ioutil.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() { sess.EXPECT().OpenStreamSync(context.Background()).Return(str, nil) sess.EXPECT().ConnectionState().Return(quic.ConnectionState{}) buf := &bytes.Buffer{} rstr := mockquic.NewMockStream(mockCtrl) rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes() rw := newResponseWriter(rstr, utils.DefaultLogger) 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 := client.RoundTrip(request) Expect(err).ToNot(HaveOccurred()) data, err := ioutil.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(string(data)).To(Equal("not gzipped")) Expect(rsp.Header.Get("Content-Encoding")).To(BeEmpty()) }) }) }) }) quic-go-0.25.0/http3/error_codes.go000066400000000000000000000037021417145451600170530ustar00rootroot00000000000000package http3 import ( "fmt" "github.com/lucas-clemente/quic-go" ) type errorCode quic.ApplicationErrorCode const ( errorNoError errorCode = 0x100 errorGeneralProtocolError errorCode = 0x101 errorInternalError errorCode = 0x102 errorStreamCreationError errorCode = 0x103 errorClosedCriticalStream errorCode = 0x104 errorFrameUnexpected errorCode = 0x105 errorFrameError errorCode = 0x106 errorExcessiveLoad errorCode = 0x107 errorIDError errorCode = 0x108 errorSettingsError errorCode = 0x109 errorMissingSettings errorCode = 0x10a errorRequestRejected errorCode = 0x10b errorRequestCanceled errorCode = 0x10c errorRequestIncomplete errorCode = 0x10d errorMessageError errorCode = 0x10e errorConnectError errorCode = 0x10f errorVersionFallback errorCode = 0x110 ) func (e errorCode) String() string { switch e { case errorNoError: return "H3_NO_ERROR" case errorGeneralProtocolError: return "H3_GENERAL_PROTOCOL_ERROR" case errorInternalError: return "H3_INTERNAL_ERROR" case errorStreamCreationError: return "H3_STREAM_CREATION_ERROR" case errorClosedCriticalStream: return "H3_CLOSED_CRITICAL_STREAM" case errorFrameUnexpected: return "H3_FRAME_UNEXPECTED" case errorFrameError: return "H3_FRAME_ERROR" case errorExcessiveLoad: return "H3_EXCESSIVE_LOAD" case errorIDError: return "H3_ID_ERROR" case errorSettingsError: return "H3_SETTINGS_ERROR" case errorMissingSettings: return "H3_MISSING_SETTINGS" case errorRequestRejected: return "H3_REQUEST_REJECTED" case errorRequestCanceled: return "H3_REQUEST_CANCELLED" case errorRequestIncomplete: return "H3_INCOMPLETE_REQUEST" case errorMessageError: return "H3_MESSAGE_ERROR" case errorConnectError: return "H3_CONNECT_ERROR" case errorVersionFallback: return "H3_VERSION_FALLBACK" default: return fmt.Sprintf("unknown error code: %#x", uint16(e)) } } quic-go-0.25.0/http3/error_codes_test.go000066400000000000000000000022471417145451600201150ustar00rootroot00000000000000package http3 import ( "go/ast" "go/parser" "go/token" "path" "runtime" "strconv" . "github.com/onsi/ginkgo" . "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(errorCode(val).String()).ToNot(Equal("unknown error code")) } }) It("has a string representation for unknown error codes", func() { Expect(errorCode(0x1337).String()).To(Equal("unknown error code: 0x1337")) }) }) quic-go-0.25.0/http3/frames.go000066400000000000000000000057501417145451600160270ustar00rootroot00000000000000package http3 import ( "bytes" "fmt" "io" "io/ioutil" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) type frame interface{} func parseNextFrame(r io.Reader) (frame, error) { qr := quicvarint.NewReader(r) t, err := quicvarint.Read(qr) if err != nil { return nil, err } 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(r, l) case 0x3: // CANCEL_PUSH fallthrough case 0x5: // PUSH_PROMISE fallthrough case 0x7: // GOAWAY fallthrough case 0xd: // MAX_PUSH_ID fallthrough case 0xe: // DUPLICATE_PUSH fallthrough default: // skip over unknown frames if _, err := io.CopyN(ioutil.Discard, qr, int64(l)); err != nil { return nil, err } return parseNextFrame(qr) } } type dataFrame struct { Length uint64 } func (f *dataFrame) Write(b *bytes.Buffer) { quicvarint.Write(b, 0x0) quicvarint.Write(b, f.Length) } type headersFrame struct { Length uint64 } func (f *headersFrame) Write(b *bytes.Buffer) { quicvarint.Write(b, 0x1) quicvarint.Write(b, f.Length) } const settingDatagram = 0x276 type settingsFrame struct { Datagram bool 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 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 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 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) Write(b *bytes.Buffer) { quicvarint.Write(b, 0x4) var l protocol.ByteCount for id, val := range f.other { l += quicvarint.Len(id) + quicvarint.Len(val) } if f.Datagram { l += quicvarint.Len(settingDatagram) + quicvarint.Len(1) } quicvarint.Write(b, uint64(l)) if f.Datagram { quicvarint.Write(b, settingDatagram) quicvarint.Write(b, 1) } for id, val := range f.other { quicvarint.Write(b, id) quicvarint.Write(b, val) } } quic-go-0.25.0/http3/frames_test.go000066400000000000000000000132451417145451600170640ustar00rootroot00000000000000package http3 import ( "bytes" "fmt" "io" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Frames", func() { appendVarInt := func(b []byte, val uint64) []byte { buf := &bytes.Buffer{} quicvarint.Write(buf, val) return append(b, buf.Bytes()...) } It("skips unknown frames", func() { data := appendVarInt(nil, 0xdeadbeef) // type byte data = appendVarInt(data, 0x42) data = append(data, make([]byte, 0x42)...) buf := bytes.NewBuffer(data) (&dataFrame{Length: 0x1234}).Write(buf) frame, err := parseNextFrame(buf) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1234))) }) Context("DATA frames", func() { It("parses", func() { data := appendVarInt(nil, 0) // type byte data = appendVarInt(data, 0x1337) frame, err := parseNextFrame(bytes.NewReader(data)) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1337))) }) It("writes", func() { buf := &bytes.Buffer{} (&dataFrame{Length: 0xdeadbeef}).Write(buf) frame, err := parseNextFrame(buf) 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 := appendVarInt(nil, 1) // type byte data = appendVarInt(data, 0x1337) frame, err := parseNextFrame(bytes.NewReader(data)) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&headersFrame{})) Expect(frame.(*headersFrame).Length).To(Equal(uint64(0x1337))) }) It("writes", func() { buf := &bytes.Buffer{} (&headersFrame{Length: 0xdeadbeef}).Write(buf) frame, err := parseNextFrame(buf) 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 := appendVarInt(nil, 13) settings = appendVarInt(settings, 37) settings = appendVarInt(settings, 0xdead) settings = appendVarInt(settings, 0xbeef) data := appendVarInt(nil, 4) // type byte data = appendVarInt(data, uint64(len(settings))) data = append(data, settings...) frame, err := parseNextFrame(bytes.NewReader(data)) 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 := appendVarInt(nil, 13) settings = appendVarInt(settings, 37) settings = appendVarInt(settings, 13) settings = appendVarInt(settings, 38) data := appendVarInt(nil, 4) // type byte data = appendVarInt(data, uint64(len(settings))) data = append(data, settings...) _, err := parseNextFrame(bytes.NewReader(data)) Expect(err).To(MatchError("duplicate setting: 13")) }) It("writes", func() { sf := &settingsFrame{other: map[uint64]uint64{ 1: 2, 99: 999, 13: 37, }} buf := &bytes.Buffer{} sf.Write(buf) frame, err := parseNextFrame(buf) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(sf)) }) It("errors on EOF", func() { sf := &settingsFrame{other: map[uint64]uint64{ 13: 37, 0xdeadbeef: 0xdecafbad, }} buf := &bytes.Buffer{} sf.Write(buf) data := buf.Bytes() _, err := parseNextFrame(bytes.NewReader(data)) Expect(err).ToNot(HaveOccurred()) for i := range data { b := make([]byte, i) copy(b, data[:i]) _, err := parseNextFrame(bytes.NewReader(b)) Expect(err).To(MatchError(io.EOF)) } }) Context("H3_DATAGRAM", func() { It("reads the H3_DATAGRAM value", func() { settings := appendVarInt(nil, settingDatagram) settings = appendVarInt(settings, 1) data := appendVarInt(nil, 4) // type byte data = appendVarInt(data, uint64(len(settings))) data = append(data, settings...) f, err := parseNextFrame(bytes.NewReader(data)) Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeAssignableToTypeOf(&settingsFrame{})) sf := f.(*settingsFrame) Expect(sf.Datagram).To(BeTrue()) }) It("rejects duplicate H3_DATAGRAM entries", func() { settings := appendVarInt(nil, settingDatagram) settings = appendVarInt(settings, 1) settings = appendVarInt(settings, settingDatagram) settings = appendVarInt(settings, 1) data := appendVarInt(nil, 4) // type byte data = appendVarInt(data, uint64(len(settings))) data = append(data, settings...) _, err := parseNextFrame(bytes.NewReader(data)) Expect(err).To(MatchError(fmt.Sprintf("duplicate setting: %d", settingDatagram))) }) It("rejects invalid values for the H3_DATAGRAM entry", func() { settings := appendVarInt(nil, settingDatagram) settings = appendVarInt(settings, 1337) data := appendVarInt(nil, 4) // type byte data = appendVarInt(data, uint64(len(settings))) data = append(data, settings...) _, err := parseNextFrame(bytes.NewReader(data)) Expect(err).To(MatchError("invalid value for H3_DATAGRAM: 1337")) }) It("writes the H3_DATAGRAM setting", func() { sf := &settingsFrame{Datagram: true} buf := &bytes.Buffer{} sf.Write(buf) frame, err := parseNextFrame(buf) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(sf)) }) }) }) }) quic-go-0.25.0/http3/gzip_reader.go000066400000000000000000000014361417145451600170420ustar00rootroot00000000000000package 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() } quic-go-0.25.0/http3/http3_suite_test.go000066400000000000000000000013241417145451600200550ustar00rootroot00000000000000package http3 import ( "os" "strconv" "testing" "time" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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 } quic-go-0.25.0/http3/request.go000066400000000000000000000036651417145451600162450ustar00rootroot00000000000000package http3 import ( "crypto/tls" "errors" "net/http" "net/url" "strconv" "strings" "github.com/marten-seemann/qpack" ) func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) { var path, authority, method, contentLengthStr string httpHeaders := http.Header{} for _, h := range headers { switch h.Name { case ":path": path = h.Value case ":method": method = h.Value case ":authority": authority = h.Value case "content-length": contentLengthStr = h.Value default: if !h.IsPseudo() { httpHeaders.Add(h.Name, h.Value) } } } // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4 if len(httpHeaders["Cookie"]) > 0 { httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; ")) } isConnect := method == http.MethodConnect if isConnect { if path != "" || authority == "" { return nil, errors.New(":path must be empty and :authority must not be empty") } } else if len(path) == 0 || len(authority) == 0 || len(method) == 0 { return nil, errors.New(":path, :authority and :method must not be empty") } var u *url.URL var requestURI string var err error if isConnect { u = &url.URL{Host: authority} requestURI = authority } else { u, err = url.ParseRequestURI(path) if err != nil { return nil, err } requestURI = path } var contentLength int64 if len(contentLengthStr) > 0 { contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64) if err != nil { return nil, err } } return &http.Request{ Method: method, URL: u, Proto: "HTTP/3", ProtoMajor: 3, ProtoMinor: 0, Header: httpHeaders, Body: nil, ContentLength: contentLength, Host: authority, RequestURI: requestURI, TLS: &tls.ConnectionState{}, }, nil } func hostnameFromRequest(req *http.Request) string { if req.URL != nil { return req.URL.Host } return "" } quic-go-0.25.0/http3/request_test.go000066400000000000000000000120341417145451600172720ustar00rootroot00000000000000package http3 import ( "net/http" "net/url" "github.com/marten-seemann/qpack" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Request", func() { It("populates request", 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")) Expect(req.ProtoMajor).To(Equal(3)) Expect(req.ProtoMinor).To(BeZero()) Expect(req.ContentLength).To(Equal(int64(42))) Expect(req.Header).To(BeEmpty()) Expect(req.Body).To(BeNil()) Expect(req.Host).To(Equal("quic.clemente.io")) Expect(req.RequestURI).To(Equal("/foo")) Expect(req.TLS).ToNot(BeNil()) }) 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("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.RequestURI).To(Equal("quic.clemente.io")) }) 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 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("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 req.URL.Host", func() { req := &http.Request{URL: url} Expect(hostnameFromRequest(req)).To(Equal("quic.clemente.io:1337")) }) It("uses req.URL.Host even if req.Host is available", func() { req := &http.Request{ Host: "www.example.org", URL: url, } Expect(hostnameFromRequest(req)).To(Equal("quic.clemente.io:1337")) }) It("returns an empty hostname if nothing is set", func() { Expect(hostnameFromRequest(&http.Request{})).To(BeEmpty()) }) }) }) quic-go-0.25.0/http3/request_writer.go000066400000000000000000000206061417145451600176330ustar00rootroot00000000000000package http3 import ( "bytes" "fmt" "io" "net" "net/http" "strconv" "strings" "sync" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/marten-seemann/qpack" "golang.org/x/net/http/httpguts" "golang.org/x/net/http2/hpack" "golang.org/x/net/idna" ) const bodyCopyBufferSize = 8 * 1024 type requestWriter struct { mutex sync.Mutex encoder *qpack.Encoder headerBuf *bytes.Buffer logger utils.Logger } func newRequestWriter(logger utils.Logger) *requestWriter { headerBuf := &bytes.Buffer{} encoder := qpack.NewEncoder(headerBuf) return &requestWriter{ encoder: encoder, headerBuf: headerBuf, logger: logger, } } func (w *requestWriter) WriteRequest(str quic.Stream, req *http.Request, gzip bool) error { buf := &bytes.Buffer{} if err := w.writeHeaders(buf, req, gzip); err != nil { return err } if _, err := str.Write(buf.Bytes()); err != nil { return err } // TODO: add support for trailers if req.Body == nil { str.Close() return nil } // send the request body asynchronously go func() { defer req.Body.Close() b := make([]byte, bodyCopyBufferSize) for { n, rerr := req.Body.Read(b) if n == 0 { if rerr == nil { continue } else if rerr == io.EOF { break } } buf := &bytes.Buffer{} (&dataFrame{Length: uint64(n)}).Write(buf) if _, err := str.Write(buf.Bytes()); err != nil { w.logger.Errorf("Error writing request: %s", err) return } if _, err := str.Write(b[:n]); err != nil { w.logger.Errorf("Error writing request: %s", err) return } if rerr != nil { if rerr == io.EOF { break } str.CancelWrite(quic.StreamErrorCode(errorRequestCanceled)) w.logger.Errorf("Error writing request: %s", rerr) return } } str.Close() }() return nil } func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool) error { w.mutex.Lock() defer w.mutex.Unlock() defer w.encoder.Close() if err := w.encodeHeaders(req, gzip, "", actualContentLength(req)); err != nil { return err } buf := &bytes.Buffer{} hf := headersFrame{Length: uint64(w.headerBuf.Len())} hf.Write(buf) if _, err := wr.Write(buf.Bytes()); err != nil { return err } if _, err := wr.Write(w.headerBuf.Bytes()); err != nil { return err } w.headerBuf.Reset() return nil } // copied from net/transport.go 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 } var path string if req.Method != "CONNECT" { 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 != "CONNECT" { f(":path", path) f(":scheme", req.URL.Scheme) } 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(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) } // 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 } } quic-go-0.25.0/http3/request_writer_test.go000066400000000000000000000106631417145451600206740ustar00rootroot00000000000000package http3 import ( "bytes" "io" "net/http" "strconv" "github.com/marten-seemann/qpack" "github.com/golang/mock/gomock" mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type foobarReader struct{} func (r *foobarReader) Read(b []byte) (int, error) { return copy(b, []byte("foobar")), io.EOF } var _ = Describe("Request Writer", func() { var ( rw *requestWriter str *mockquic.MockStream strBuf *bytes.Buffer ) decode := func(str io.Reader) map[string]string { frame, err := parseNextFrame(str) 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(utils.DefaultLogger) strBuf = &bytes.Buffer{} str = mockquic.NewMockStream(mockCtrl) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return strBuf.Write(p) }).AnyTimes() }) It("writes a GET request", func() { str.EXPECT().Close() req, err := http.NewRequest("GET", "https://quic.clemente.io/index.html?foo=bar", nil) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequest(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("writes a POST request", func() { closed := make(chan struct{}) str.EXPECT().Close().Do(func() { close(closed) }) postData := bytes.NewReader([]byte("foobar")) req, err := http.NewRequest("POST", "https://quic.clemente.io/upload.html", postData) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequest(str, req, false)).To(Succeed()) Eventually(closed).Should(BeClosed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue(":method", "POST")) Expect(headerFields).To(HaveKey("content-length")) contentLength, err := strconv.Atoi(headerFields["content-length"]) Expect(err).ToNot(HaveOccurred()) Expect(contentLength).To(BeNumerically(">", 0)) frame, err := parseNextFrame(strBuf) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(BeEquivalentTo(6)) }) It("writes a POST request, if the Body returns an EOF immediately", func() { closed := make(chan struct{}) str.EXPECT().Close().Do(func() { close(closed) }) req, err := http.NewRequest("POST", "https://quic.clemente.io/upload.html", &foobarReader{}) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequest(str, req, false)).To(Succeed()) Eventually(closed).Should(BeClosed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue(":method", "POST")) frame, err := parseNextFrame(strBuf) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame.(*dataFrame).Length).To(BeEquivalentTo(6)) }) It("sends cookies", func() { str.EXPECT().Close() req, err := http.NewRequest("GET", "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.WriteRequest(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() { str.EXPECT().Close() req, err := http.NewRequest("GET", "https://quic.clemente.io/", nil) Expect(err).ToNot(HaveOccurred()) Expect(rw.WriteRequest(str, req, true)).To(Succeed()) headerFields := decode(strBuf) Expect(headerFields).To(HaveKeyWithValue("accept-encoding", "gzip")) }) }) quic-go-0.25.0/http3/response_writer.go000066400000000000000000000063331417145451600200020ustar00rootroot00000000000000package http3 import ( "bufio" "bytes" "net/http" "strconv" "strings" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/marten-seemann/qpack" ) // DataStreamer lets the caller take over the stream. After a call to DataStream // the HTTP server library will not do anything else with the connection. // // It becomes the caller's responsibility to manage and close the stream. // // After a call to DataStream, the original Request.Body must not be used. type DataStreamer interface { DataStream() quic.Stream } type responseWriter struct { stream quic.Stream // needed for DataStream() bufferedStream *bufio.Writer header http.Header status int // status code passed to WriteHeader headerWritten bool dataStreamUsed bool // set when DataSteam() is called logger utils.Logger } var ( _ http.ResponseWriter = &responseWriter{} _ http.Flusher = &responseWriter{} _ DataStreamer = &responseWriter{} ) func newResponseWriter(stream quic.Stream, logger utils.Logger) *responseWriter { return &responseWriter{ header: http.Header{}, stream: stream, bufferedStream: bufio.NewWriter(stream), logger: logger, } } func (w *responseWriter) Header() http.Header { return w.header } func (w *responseWriter) WriteHeader(status int) { if w.headerWritten { return } if status < 100 || status >= 200 { w.headerWritten = true } w.status = status var headers bytes.Buffer enc := qpack.NewEncoder(&headers) enc.WriteField(qpack.HeaderField{Name: ":status", Value: strconv.Itoa(status)}) for k, v := range w.header { for index := range v { enc.WriteField(qpack.HeaderField{Name: strings.ToLower(k), Value: v[index]}) } } buf := &bytes.Buffer{} (&headersFrame{Length: uint64(headers.Len())}).Write(buf) w.logger.Infof("Responding with %d", status) if _, err := w.bufferedStream.Write(buf.Bytes()); err != nil { w.logger.Errorf("could not write headers frame: %s", err.Error()) } if _, err := w.bufferedStream.Write(headers.Bytes()); err != nil { w.logger.Errorf("could not write header frame payload: %s", err.Error()) } if !w.headerWritten { w.Flush() } } func (w *responseWriter) Write(p []byte) (int, error) { if !w.headerWritten { w.WriteHeader(200) } if !bodyAllowedForStatus(w.status) { return 0, http.ErrBodyNotAllowed } df := &dataFrame{Length: uint64(len(p))} buf := &bytes.Buffer{} df.Write(buf) if _, err := w.bufferedStream.Write(buf.Bytes()); err != nil { return 0, err } return w.bufferedStream.Write(p) } func (w *responseWriter) Flush() { if err := w.bufferedStream.Flush(); err != nil { w.logger.Errorf("could not flush to stream: %s", err.Error()) } } func (w *responseWriter) usedDataStream() bool { return w.dataStreamUsed } func (w *responseWriter) DataStream() quic.Stream { w.dataStreamUsed = true w.Flush() return w.stream } // 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 == 204: return false case status == 304: return false } return true } quic-go-0.25.0/http3/response_writer_test.go000066400000000000000000000113171417145451600210370ustar00rootroot00000000000000package http3 import ( "bytes" "io" "net/http" mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/golang/mock/gomock" "github.com/marten-seemann/qpack" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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() rw = newResponseWriter(str, utils.DefaultLogger) }) decodeHeader := func(str io.Reader) map[string][]string { rw.Flush() fields := make(map[string][]string) decoder := qpack.NewDecoder(nil) frame, err := parseNextFrame(str) 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 { frame, err := parseNextFrame(str) 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(1)) Expect(fields).To(HaveKeyWithValue(":status", []string{"418"})) }) 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(200) rw.WriteHeader(500) fields := decodeHeader(strBuf) Expect(fields).To(HaveLen(1)) Expect(fields).To(HaveKeyWithValue(":status", []string{"200"})) }) 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(2)) Expect(fields).To(HaveKeyWithValue(":status", []string{"200"})) 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)) }) }) quic-go-0.25.0/http3/roundtrip.go000066400000000000000000000133461417145451600166000ustar00rootroot00000000000000package http3 import ( "crypto/tls" "errors" "fmt" "io" "net/http" "strings" "sync" quic "github.com/lucas-clemente/quic-go" "golang.org/x/net/http/httpguts" ) type roundTripCloser interface { http.RoundTripper io.Closer } // RoundTripper implements the http.RoundTripper interface type RoundTripper struct { mutex sync.Mutex // 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 // 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 // Enable support for HTTP/3 datagrams. // If set to true, QuicConfig.EnableDatagram will be set. // See https://www.ietf.org/archive/id/draft-schinazi-masque-h3-datagram-02.html. EnableDatagrams bool // Dial specifies an optional dial function for creating QUIC // connections for requests. // If Dial is nil, quic.DialAddrEarly will be used. Dial func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error) // 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 clients map[string]roundTripCloser } // 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, RoundTrip will return ErrNoCachedConn. OnlyCachedConn bool // SkipSchemeCheck controls whether we check if the scheme is https. // This allows the use of different schemes, e.g. masque://target.example.com:443/. SkipSchemeCheck bool } var _ roundTripCloser = &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) { if req.URL == nil { closeRequestBody(req) return nil, errors.New("http3: nil Request.URL") } 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") } if req.URL.Scheme == "https" { 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) } } } } else if !opt.SkipSchemeCheck { closeRequestBody(req) return nil, fmt.Errorf("http3: unsupported protocol scheme: %s", req.URL.Scheme) } if req.Method != "" && !validMethod(req.Method) { closeRequestBody(req) return nil, fmt.Errorf("http3: invalid method %q", req.Method) } hostname := authorityAddr("https", hostnameFromRequest(req)) cl, err := r.getClient(hostname, opt.OnlyCachedConn) if err != nil { return nil, err } return cl.RoundTrip(req) } // RoundTrip does a round trip. func (r *RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return r.RoundTripOpt(req, RoundTripOpt{}) } func (r *RoundTripper) getClient(hostname string, onlyCached bool) (http.RoundTripper, error) { r.mutex.Lock() defer r.mutex.Unlock() if r.clients == nil { r.clients = make(map[string]roundTripCloser) } client, ok := r.clients[hostname] if !ok { if onlyCached { return nil, ErrNoCachedConn } var err error client, err = newClient( hostname, r.TLSClientConfig, &roundTripperOpts{ EnableDatagram: r.EnableDatagrams, DisableCompression: r.DisableCompression, MaxHeaderBytes: r.MaxResponseHeaderBytes, }, r.QuicConfig, r.Dial, ) if err != nil { return nil, err } r.clients[hostname] = client } return client, nil } // Close closes the QUIC connections that this RoundTripper has used func (r *RoundTripper) Close() error { r.mutex.Lock() defer r.mutex.Unlock() for _, client := range r.clients { if err := client.Close(); err != nil { return err } } r.clients = 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) } quic-go-0.25.0/http3/roundtrip_test.go000066400000000000000000000202341417145451600176310ustar00rootroot00000000000000package http3 import ( "bytes" "context" "crypto/tls" "errors" "io" "net/http" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go" mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type mockClient struct { closed bool } func (m *mockClient) RoundTrip(req *http.Request) (*http.Response, error) { return &http.Response{Request: req}, nil } func (m *mockClient) Close() error { m.closed = true return nil } var _ roundTripCloser = &mockClient{} 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 ( rt *RoundTripper req1 *http.Request session *mockquic.MockEarlySession handshakeCtx context.Context // an already canceled context ) BeforeEach(func() { rt = &RoundTripper{} var err error req1, err = http.NewRequest("GET", "https://www.example.org/file1.html", nil) Expect(err).ToNot(HaveOccurred()) ctx, cancel := context.WithCancel(context.Background()) cancel() handshakeCtx = ctx }) Context("dialing hosts", func() { origDialAddr := dialAddr BeforeEach(func() { session = mockquic.NewMockEarlySession(mockCtrl) origDialAddr = dialAddr dialAddr = func(addr string, tlsConf *tls.Config, config *quic.Config) (quic.EarlySession, error) { // return an error when trying to open a stream // we don't want to test all the dial logic here, just that dialing happens at all return session, nil } }) AfterEach(func() { dialAddr = origDialAddr }) It("creates new clients", func() { closed := make(chan struct{}) testErr := errors.New("test err") req, err := http.NewRequest("GET", "https://quic.clemente.io/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) session.EXPECT().OpenUniStream().AnyTimes().Return(nil, testErr) session.EXPECT().HandshakeComplete().Return(handshakeCtx) session.EXPECT().OpenStreamSync(context.Background()).Return(nil, testErr) session.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-closed return nil, errors.New("test done") }).MaxTimes(1) session.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(quic.ApplicationErrorCode, string) { close(closed) }) _, err = rt.RoundTrip(req) Expect(err).To(MatchError(testErr)) Expect(rt.clients).To(HaveLen(1)) Eventually(closed).Should(BeClosed()) }) It("uses the quic.Config, if provided", func() { config := &quic.Config{HandshakeIdleTimeout: time.Millisecond} var receivedConfig *quic.Config dialAddr = func(addr string, tlsConf *tls.Config, config *quic.Config) (quic.EarlySession, error) { receivedConfig = config return nil, errors.New("handshake error") } rt.QuicConfig = config _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("handshake error")) Expect(receivedConfig.HandshakeIdleTimeout).To(Equal(config.HandshakeIdleTimeout)) }) It("uses the custom dialer, if provided", func() { var dialed bool dialer := func(_, _ string, tlsCfgP *tls.Config, cfg *quic.Config) (quic.EarlySession, error) { dialed = true return nil, errors.New("handshake error") } rt.Dial = dialer _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("handshake error")) Expect(dialed).To(BeTrue()) }) It("reuses existing clients", func() { closed := make(chan struct{}) testErr := errors.New("test err") session.EXPECT().OpenUniStream().AnyTimes().Return(nil, testErr) session.EXPECT().HandshakeComplete().Return(handshakeCtx).Times(2) session.EXPECT().OpenStreamSync(context.Background()).Return(nil, testErr).Times(2) session.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-closed return nil, errors.New("test done") }).MaxTimes(1) session.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(quic.ApplicationErrorCode, string) { close(closed) }) req, err := http.NewRequest("GET", "https://quic.clemente.io/file1.html", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req) Expect(err).To(MatchError(testErr)) Expect(rt.clients).To(HaveLen(1)) req2, err := http.NewRequest("GET", "https://quic.clemente.io/file2.html", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req2) Expect(err).To(MatchError(testErr)) Expect(rt.clients).To(HaveLen(1)) Eventually(closed).Should(BeClosed()) }) It("doesn't create new clients if RoundTripOpt.OnlyCachedConn is set", func() { req, err := http.NewRequest("GET", "https://quic.clemente.io/foobar.html", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTripOpt(req, RoundTripOpt{OnlyCachedConn: true}) Expect(err).To(MatchError(ErrNoCachedConn)) }) }) Context("validating request", func() { 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("allow non-https schemes if SkipSchemeCheck is set", func() { req, err := http.NewRequest("GET", "masque://www.example.org/", nil) Expect(err).ToNot(HaveOccurred()) _, err = rt.RoundTrip(req) Expect(err).To(MatchError("http3: unsupported protocol scheme: masque")) _, err = rt.RoundTripOpt(req, RoundTripOpt{SkipSchemeCheck: true, OnlyCachedConn: true}) Expect(err).To(MatchError("http3: no cached connection was available")) }) It("rejects requests without a URL", func() { req1.URL = nil req1.Body = &mockBody{} _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("http3: nil Request.URL")) Expect(req1.Body.(*mockBody).closed).To(BeTrue()) }) It("rejects request without a URL Host", func() { req1.URL.Host = "" req1.Body = &mockBody{} _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("http3: no Host in request URL")) Expect(req1.Body.(*mockBody).closed).To(BeTrue()) }) It("doesn't try to close the body if the request doesn't have one", func() { req1.URL = nil Expect(req1.Body).To(BeNil()) _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("http3: nil Request.URL")) }) It("rejects requests without a header", func() { req1.Header = nil req1.Body = &mockBody{} _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("http3: nil Request.Header")) Expect(req1.Body.(*mockBody).closed).To(BeTrue()) }) It("rejects requests with invalid header name fields", func() { req1.Header.Add("foobär", "value") _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("http3: invalid http header field name \"foobär\"")) }) It("rejects requests with invalid header name values", func() { req1.Header.Add("foo", string([]byte{0x7})) _, err := rt.RoundTrip(req1) Expect(err.Error()).To(ContainSubstring("http3: invalid http header field value")) }) It("rejects requests with an invalid request method", func() { req1.Method = "foobär" req1.Body = &mockBody{} _, err := rt.RoundTrip(req1) Expect(err).To(MatchError("http3: invalid method \"foobär\"")) Expect(req1.Body.(*mockBody).closed).To(BeTrue()) }) }) Context("closing", func() { It("closes", func() { rt.clients = make(map[string]roundTripCloser) cl := &mockClient{} rt.clients["foo.bar"] = cl err := rt.Close() Expect(err).ToNot(HaveOccurred()) Expect(len(rt.clients)).To(BeZero()) Expect(cl.closed).To(BeTrue()) }) It("closes a RoundTripper that has never been used", func() { Expect(len(rt.clients)).To(BeZero()) err := rt.Close() Expect(err).ToNot(HaveOccurred()) Expect(len(rt.clients)).To(BeZero()) }) }) }) quic-go-0.25.0/http3/server.go000066400000000000000000000371771417145451600160700ustar00rootroot00000000000000package http3 import ( "bytes" "context" "crypto/tls" "errors" "fmt" "io" "net" "net/http" "runtime" "strings" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/quicvarint" "github.com/marten-seemann/qpack" ) // allows mocking of quic.Listen and quic.ListenAddr var ( quicListen = quic.ListenEarly quicListenAddr = quic.ListenAddrEarly ) const ( nextProtoH3Draft29 = "h3-29" nextProtoH3 = "h3" ) const ( streamTypeControlStream = 0 streamTypePushStream = 1 streamTypeQPACKEncoderStream = 2 streamTypeQPACKDecoderStream = 3 ) func versionToALPN(v protocol.VersionNumber) string { if v == protocol.Version1 { return nextProtoH3 } if v == protocol.VersionTLS || v == protocol.VersionDraft29 { return nextProtoH3Draft29 } return "" } // 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"} type requestError struct { err error streamErr errorCode connErr errorCode } func newStreamError(code errorCode, err error) requestError { return requestError{err: err, streamErr: code} } func newConnError(code errorCode, err error) requestError { return requestError{err: err, connErr: code} } // Server is a HTTP/3 server. type Server struct { *http.Server // By providing a quic.Config, it is possible to set parameters of the QUIC connection. // If nil, it uses reasonable default values. QuicConfig *quic.Config // Enable support for HTTP/3 datagrams. // If set to true, QuicConfig.EnableDatagram will be set. // See https://www.ietf.org/archive/id/draft-schinazi-masque-h3-datagram-02.html. EnableDatagrams bool // The port to use in Alt-Svc response headers. // 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 uint32 mutex sync.Mutex listeners map[*quic.EarlyListener]struct{} closed utils.AtomicBool loggerOnce sync.Once logger utils.Logger } // ListenAndServe listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. func (s *Server) ListenAndServe() error { if s.Server == nil { return errors.New("use of http3.Server without http.Server") } return s.serveImpl(s.TLSConfig, nil) } // ListenAndServeTLS listens on the UDP address s.Addr and calls s.Handler to handle HTTP/3 requests on incoming connections. 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. config := &tls.Config{ Certificates: certs, } return s.serveImpl(config, nil) } // Serve an existing UDP connection. // It is possible to reuse the same connection for outgoing connections. // Closing the server does not close the packet conn. func (s *Server) Serve(conn net.PacketConn) error { return s.serveImpl(s.TLSConfig, conn) } func (s *Server) serveImpl(tlsConf *tls.Config, conn net.PacketConn) error { if s.closed.Get() { return http.ErrServerClosed } if s.Server == nil { return errors.New("use of http3.Server without http.Server") } s.loggerOnce.Do(func() { s.logger = utils.DefaultLogger.WithPrefix("server") }) // The tls.Config we pass to Listen needs to have the GetConfigForClient callback set. // That way, we can get the QUIC version and set the correct ALPN value. baseConf := &tls.Config{ GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { // determine the ALPN from the QUIC version used proto := nextProtoH3Draft29 if qconn, ok := ch.Conn.(handshake.ConnWithVersion); ok { if qconn.GetQUICVersion() == protocol.Version1 { proto = nextProtoH3 } } 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 } config = config.Clone() config.NextProtos = []string{proto} return config, nil }, } var ln quic.EarlyListener var err error quicConf := s.QuicConfig if quicConf == nil { quicConf = &quic.Config{} } else { quicConf = s.QuicConfig.Clone() } if s.EnableDatagrams { quicConf.EnableDatagrams = true } if conn == nil { ln, err = quicListenAddr(s.Addr, baseConf, quicConf) } else { ln, err = quicListen(conn, baseConf, quicConf) } if err != nil { return err } s.addListener(&ln) defer s.removeListener(&ln) for { sess, err := ln.Accept(context.Background()) if err != nil { return err } go s.handleConn(sess) } } // 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 *quic.EarlyListener) { s.mutex.Lock() if s.listeners == nil { s.listeners = make(map[*quic.EarlyListener]struct{}) } s.listeners[l] = struct{}{} s.mutex.Unlock() } func (s *Server) removeListener(l *quic.EarlyListener) { s.mutex.Lock() delete(s.listeners, l) s.mutex.Unlock() } func (s *Server) handleConn(sess quic.EarlySession) { decoder := qpack.NewDecoder(nil) // send a SETTINGS frame str, err := sess.OpenUniStream() if err != nil { s.logger.Debugf("Opening the control stream failed.") return } buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) // stream type (&settingsFrame{Datagram: s.EnableDatagrams}).Write(buf) str.Write(buf.Bytes()) go s.handleUnidirectionalStreams(sess) // Process all requests immediately. // It's the client's responsibility to decide which requests are eligible for 0-RTT. for { str, err := sess.AcceptStream(context.Background()) if err != nil { s.logger.Debugf("Accepting stream failed: %s", err) return } go func() { rerr := s.handleRequest(sess, str, decoder, func() { sess.CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), "") }) if rerr.err != nil || rerr.streamErr != 0 || rerr.connErr != 0 { s.logger.Debugf("Handling request failed: %s", err) if rerr.streamErr != 0 { str.CancelWrite(quic.StreamErrorCode(rerr.streamErr)) } if rerr.connErr != 0 { var reason string if rerr.err != nil { reason = rerr.err.Error() } sess.CloseWithError(quic.ApplicationErrorCode(rerr.connErr), reason) } return } str.Close() }() } } func (s *Server) handleUnidirectionalStreams(sess quic.EarlySession) { for { str, err := sess.AcceptUniStream(context.Background()) if err != nil { s.logger.Debugf("accepting unidirectional stream failed: %s", err) return } go func(str quic.ReceiveStream) { streamType, err := quicvarint.Read(quicvarint.NewReader(str)) if err != nil { s.logger.Debugf("reading stream type on stream %d failed: %s", str.StreamID(), err) return } // We're only interested in the control stream here. switch streamType { case streamTypeControlStream: case streamTypeQPACKEncoderStream, streamTypeQPACKDecoderStream: // Our QPACK implementation doesn't use the dynamic table yet. // TODO: check that only one stream of each type is opened. return case streamTypePushStream: // only the server can push sess.CloseWithError(quic.ApplicationErrorCode(errorStreamCreationError), "") return default: str.CancelRead(quic.StreamErrorCode(errorStreamCreationError)) return } f, err := parseNextFrame(str) if err != nil { sess.CloseWithError(quic.ApplicationErrorCode(errorFrameError), "") return } sf, ok := f.(*settingsFrame) if !ok { sess.CloseWithError(quic.ApplicationErrorCode(errorMissingSettings), "") return } if !sf.Datagram { return } // If datagram support was enabled on our side as well as on the client 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 s.EnableDatagrams && !sess.ConnectionState().SupportsDatagrams { sess.CloseWithError(quic.ApplicationErrorCode(errorSettingsError), "missing QUIC Datagram support") } }(str) } } func (s *Server) maxHeaderBytes() uint64 { if s.Server.MaxHeaderBytes <= 0 { return http.DefaultMaxHeaderBytes } return uint64(s.Server.MaxHeaderBytes) } func (s *Server) handleRequest(sess quic.Session, str quic.Stream, decoder *qpack.Decoder, onFrameError func()) requestError { frame, err := parseNextFrame(str) if err != nil { return newStreamError(errorRequestIncomplete, err) } hf, ok := frame.(*headersFrame) if !ok { return newConnError(errorFrameUnexpected, errors.New("expected first frame to be a HEADERS frame")) } if hf.Length > s.maxHeaderBytes() { return newStreamError(errorFrameError, fmt.Errorf("HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes())) } headerBlock := make([]byte, hf.Length) if _, err := io.ReadFull(str, headerBlock); err != nil { return newStreamError(errorRequestIncomplete, err) } hfs, err := decoder.DecodeFull(headerBlock) if err != nil { // TODO: use the right error code return newConnError(errorGeneralProtocolError, err) } req, err := requestFromHeaders(hfs) if err != nil { // TODO: use the right error code return newStreamError(errorGeneralProtocolError, err) } req.RemoteAddr = sess.RemoteAddr().String() req.Body = newRequestBody(str, onFrameError) if s.logger.Debug() { s.logger.Infof("%s %s%s, on stream %d", req.Method, req.Host, req.RequestURI, str.StreamID()) } else { s.logger.Infof("%s %s%s", req.Method, req.Host, req.RequestURI) } ctx := str.Context() ctx = context.WithValue(ctx, ServerContextKey, s) ctx = context.WithValue(ctx, http.LocalAddrContextKey, sess.LocalAddr()) req = req.WithContext(ctx) r := newResponseWriter(str, s.logger) defer func() { if !r.usedDataStream() { r.Flush() } }() 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)] s.logger.Errorf("http: panic serving: %v\n%s", p, buf) panicked = true } }() handler.ServeHTTP(r, req) }() if !r.usedDataStream() { if panicked { r.WriteHeader(500) } else { r.WriteHeader(200) } // If the EOF was read by the handler, CancelRead() is a no-op. str.CancelRead(quic.StreamErrorCode(errorNoError)) } return requestError{} } // 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.closed.Set(true) s.mutex.Lock() defer s.mutex.Unlock() 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 } // SetQuicHeaders can be used to set the proper headers that announce that this server supports QUIC. // The values that are set depend on the port information from s.Server.Addr, and currently look like this (if Addr has port 443): // Alt-Svc: quic=":443"; ma=2592000; v="33,32,31,30" func (s *Server) SetQuicHeaders(hdr http.Header) error { port := atomic.LoadUint32(&s.Port) if port == 0 { // Extract port from s.Server.Addr _, portStr, err := net.SplitHostPort(s.Server.Addr) if err != nil { return err } portInt, err := net.LookupPort("tcp", portStr) if err != nil { return err } port = uint32(portInt) atomic.StoreUint32(&s.Port, port) } // 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 } altSvc := make([]string, 0, len(supportedVersions)) for _, version := range supportedVersions { v := versionToALPN(version) if len(v) > 0 { altSvc = append(altSvc, fmt.Sprintf(`%s=":%d"; ma=2592000`, v, port)) } } hdr.Add("Alt-Svc", strings.Join(altSvc, ",")) return nil } // 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{ Server: &http.Server{ Addr: addr, Handler: handler, }, } return server.ListenAndServeTLS(certFile, keyFile) } // ListenAndServe listens on the given network address for both, TLS 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 ListenAndServe(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, } // 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() tcpAddr, err := net.ResolveTCPAddr("tcp", addr) if err != nil { return err } tcpConn, err := net.ListenTCP("tcp", tcpAddr) if err != nil { return err } defer tcpConn.Close() tlsConn := tls.NewListener(tcpConn, config) defer tlsConn.Close() // Start the servers httpServer := &http.Server{ Addr: addr, TLSConfig: config, } quicServer := &Server{ Server: httpServer, } if handler == nil { handler = http.DefaultServeMux } httpServer.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { quicServer.SetQuicHeaders(w.Header()) handler.ServeHTTP(w, r) }) hErr := make(chan error) qErr := make(chan error) go func() { hErr <- httpServer.Serve(tlsConn) }() 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 } } quic-go-0.25.0/http3/server_test.go000066400000000000000000000740251417145451600171200ustar00rootroot00000000000000package http3 import ( "bytes" "context" "crypto/tls" "errors" "fmt" "io" "net" "net/http" "time" "github.com/lucas-clemente/quic-go" mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/testdata" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/quicvarint" "github.com/golang/mock/gomock" "github.com/marten-seemann/qpack" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type mockConn struct { net.Conn version protocol.VersionNumber } func newMockConn(version protocol.VersionNumber) net.Conn { return &mockConn{version: version} } func (c *mockConn) GetQUICVersion() protocol.VersionNumber { return c.version } var _ = Describe("Server", func() { var ( s *Server origQuicListenAddr = quicListenAddr ) BeforeEach(func() { s = &Server{ Server: &http.Server{ TLSConfig: testdata.GetTLSConfig(), }, logger: utils.DefaultLogger, } origQuicListenAddr = quicListenAddr }) AfterEach(func() { quicListenAddr = origQuicListenAddr }) Context("handling requests", func() { var ( qpackDecoder *qpack.Decoder str *mockquic.MockStream sess *mockquic.MockEarlySession exampleGetRequest *http.Request examplePostRequest *http.Request ) reqContext := context.Background() decodeHeader := func(str io.Reader) map[string][]string { fields := make(map[string][]string) decoder := qpack.NewDecoder(nil) frame, err := parseNextFrame(str) 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() closed := make(chan struct{}) str.EXPECT().Close().Do(func() { close(closed) }) rw := newRequestWriter(utils.DefaultLogger) Expect(rw.WriteRequest(str, req, false)).To(Succeed()) Eventually(closed).Should(BeClosed()) 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) sess = mockquic.NewMockEarlySession(mockCtrl) addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} sess.EXPECT().RemoteAddr().Return(addr).AnyTimes() sess.EXPECT().LocalAddr().AnyTimes() }) 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().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) Expect(s.handleRequest(sess, str, qpackDecoder, nil)).To(Equal(requestError{})) 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")) Expect(req.Context().Value(ServerContextKey)).To(Equal(s)) }) 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().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) serr := s.handleRequest(sess, str, qpackDecoder, nil) Expect(serr.err).ToNot(HaveOccurred()) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) }) It("handles a panicking handler", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { panic("foobar") }) responseBuf := &bytes.Buffer{} setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(gomock.Any()) serr := s.handleRequest(sess, str, qpackDecoder, nil) Expect(serr.err).ToNot(HaveOccurred()) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"500"})) }) It("doesn't close the stream if the handler called DataStream()", func() { s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { str := w.(DataStreamer).DataStream() str.Write([]byte("foobar")) }) setRequest(encodeRequest(exampleGetRequest)) str.EXPECT().Context().Return(reqContext) str.EXPECT().Write([]byte("foobar")) // don't EXPECT CancelRead() serr := s.handleRequest(sess, str, qpackDecoder, nil) Expect(serr.err).ToNot(HaveOccurred()) }) Context("control stream handling", func() { var sess *mockquic.MockEarlySession testDone := make(chan struct{}) BeforeEach(func() { sess = mockquic.NewMockEarlySession(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()) sess.EXPECT().OpenUniStream().Return(controlStr, nil) sess.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) sess.EXPECT().RemoteAddr().Return(&net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337}).AnyTimes() sess.EXPECT().LocalAddr().AnyTimes() }) AfterEach(func() { testDone <- struct{}{} }) It("parses the SETTINGS frame", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) (&settingsFrame{}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) s.handleConn(sess) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to sess.CloseWithError }) 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() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamType) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return str, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) s.handleConn(sess) time.Sleep(scaleDuration(20 * time.Millisecond)) // don't EXPECT any calls to str.CancelRead }) } It("reset streams other than the control stream and the QPACK streams", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, 1337) str := mockquic.NewMockStream(mockCtrl) str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() done := make(chan struct{}) str.EXPECT().CancelRead(quic.StreamErrorCode(errorStreamCreationError)).Do(func(code quic.StreamErrorCode) { close(done) }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return str, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) s.handleConn(sess) Eventually(done).Should(BeClosed()) }) It("errors when the first frame on the control stream is not a SETTINGS frame", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) (&dataFrame{}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorMissingSettings)) close(done) }) s.handleConn(sess) Eventually(done).Should(BeClosed()) }) It("errors when parsing the frame on the control stream fails", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) b := &bytes.Buffer{} (&settingsFrame{}).Write(b) buf.Write(b.Bytes()[:b.Len()-1]) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorFrameError)) close(done) }) s.handleConn(sess) Eventually(done).Should(BeClosed()) }) It("errors when the client opens a push stream", func() { buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypePushStream) (&dataFrame{}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorStreamCreationError)) close(done) }) s.handleConn(sess) Eventually(done).Should(BeClosed()) }) It("errors when the client advertises datagram support (and we enabled support for it)", func() { s.EnableDatagrams = true buf := &bytes.Buffer{} quicvarint.Write(buf, streamTypeControlStream) (&settingsFrame{Datagram: true}).Write(buf) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { return controlStr, nil }) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) sess.EXPECT().ConnectionState().Return(quic.ConnectionState{SupportsDatagrams: false}) done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, reason string) { defer GinkgoRecover() Expect(code).To(BeEquivalentTo(errorSettingsError)) Expect(reason).To(Equal("missing QUIC Datagram support")) close(done) }) s.handleConn(sess) Eventually(done).Should(BeClosed()) }) }) Context("stream- and connection-level errors", func() { var sess *mockquic.MockEarlySession testDone := make(chan struct{}) BeforeEach(func() { testDone = make(chan struct{}) addr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} sess = mockquic.NewMockEarlySession(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl) controlStr.EXPECT().Write(gomock.Any()) sess.EXPECT().OpenUniStream().Return(controlStr, nil) sess.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { <-testDone return nil, errors.New("test done") }) sess.EXPECT().AcceptStream(gomock.Any()).Return(str, nil) sess.EXPECT().AcceptStream(gomock.Any()).Return(nil, errors.New("done")) sess.EXPECT().RemoteAddr().Return(addr).AnyTimes() sess.EXPECT().LocalAddr().AnyTimes() }) AfterEach(func() { testDone <- struct{}{} }) It("cancels reading when client sends a body in GET request", func() { handlerCalled := make(chan struct{}) s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { close(handlerCalled) }) requestData := encodeRequest(exampleGetRequest) buf := &bytes.Buffer{} (&dataFrame{Length: 6}).Write(buf) // add a body buf.Write([]byte("foobar")) responseBuf := &bytes.Buffer{} setRequest(append(requestData, buf.Bytes()...)) done := make(chan struct{}) str.EXPECT().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(errorNoError)) str.EXPECT().Close().Do(func() { close(done) }) s.handleConn(sess) Eventually(done).Should(BeClosed()) hfs := decodeHeader(responseBuf) Expect(hfs).To(HaveKeyWithValue(":status", []string{"200"})) }) It("errors when the client sends a too large header frame", func() { s.Server.MaxHeaderBytes = 20 s.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { Fail("Handler should not be called.") }) requestData := encodeRequest(exampleGetRequest) buf := &bytes.Buffer{} (&dataFrame{Length: 6}).Write(buf) // add a body buf.Write([]byte("foobar")) responseBuf := &bytes.Buffer{} setRequest(append(requestData, buf.Bytes()...)) done := make(chan struct{}) str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError)).Do(func(quic.StreamErrorCode) { close(done) }) s.handleConn(sess) 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().CancelWrite(quic.StreamErrorCode(errorRequestIncomplete)).Do(func(quic.StreamErrorCode) { close(done) }) s.handleConn(sess) 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) }) buf := &bytes.Buffer{} (&dataFrame{}).Write(buf) setRequest(buf.Bytes()) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() done := make(chan struct{}) sess.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(code quic.ApplicationErrorCode, _ string) { Expect(code).To(Equal(quic.ApplicationErrorCode(errorFrameUnexpected))) close(done) }) s.handleConn(sess) Eventually(done).Should(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) }) // use 2*DefaultMaxHeaderBytes here. qpack will compress the requiest, // 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().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() done := make(chan struct{}) str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError)).Do(func(quic.StreamErrorCode) { close(done) }) s.handleConn(sess) 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().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(errorNoError)) serr := s.handleRequest(sess, str, qpackDecoder, nil) Expect(serr.err).ToNot(HaveOccurred()) 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() Expect(r.Context().Done()).To(BeClosed()) Expect(r.Context().Err()).To(MatchError(context.Canceled)) close(handlerCalled) }) setRequest(encodeRequest(examplePostRequest)) reqContext, cancel := context.WithCancel(context.Background()) cancel() str.EXPECT().Context().Return(reqContext) str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { return len(p), nil }).AnyTimes() str.EXPECT().CancelRead(quic.StreamErrorCode(errorNoError)) serr := s.handleRequest(sess, str, qpackDecoder, nil) Expect(serr.err).ToNot(HaveOccurred()) Eventually(handlerCalled).Should(BeClosed()) }) }) Context("setting http headers", func() { BeforeEach(func() { s.QuicConfig = &quic.Config{Versions: []protocol.VersionNumber{protocol.VersionDraft29}} }) expected := http.Header{ "Alt-Svc": {`h3-29=":443"; ma=2592000`}, } It("sets proper headers with numeric port", func() { s.Server.Addr = ":443" hdr := http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(expected)) }) It("sets proper headers with full addr", func() { s.Server.Addr = "127.0.0.1:443" hdr := http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(expected)) }) It("sets proper headers with string port", func() { s.Server.Addr = ":https" hdr := http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(expected)) }) It("works multiple times", func() { s.Server.Addr = ":https" hdr := http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(expected)) hdr = http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(expected)) }) It("works if the quic.Config sets QUIC versions", func() { s.Server.Addr = ":443" s.QuicConfig.Versions = []quic.VersionNumber{quic.Version1, quic.VersionDraft29} hdr := http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(http.Header{"Alt-Svc": {`h3=":443"; ma=2592000,h3-29=":443"; ma=2592000`}})) }) It("uses s.Port if set to a non-zero value", func() { s.Server.Addr = ":443" s.Port = 8443 hdr := http.Header{} Expect(s.SetQuicHeaders(hdr)).To(Succeed()) Expect(hdr).To(Equal(http.Header{"Alt-Svc": {`h3-29=":8443"; ma=2592000`}})) }) }) It("errors when ListenAndServe is called with s.Server nil", func() { Expect((&Server{}).ListenAndServe()).To(MatchError("use of http3.Server without http.Server")) }) It("errors when ListenAndServeTLS is called with s.Server nil", func() { Expect((&Server{}).ListenAndServeTLS(testdata.GetCertificatePaths())).To(MatchError("use of http3.Server without http.Server")) }) It("should nop-Close() when s.server is nil", func() { Expect((&Server{}).Close()).To(Succeed()) }) It("errors when ListenAndServe is called after Close", func() { serv := &Server{Server: &http.Server{}} Expect(serv.Close()).To(Succeed()) Expect(serv.ListenAndServe()).To(MatchError(http.ErrServerClosed)) }) Context("Serve", func() { origQuicListen := quicListen AfterEach(func() { quicListen = origQuicListen }) It("serves a packet conn", func() { ln := mockquic.NewMockEarlyListener(mockCtrl) conn := &net.UDPConn{} quicListen = func(c net.PacketConn, tlsConf *tls.Config, config *quic.Config) (quic.EarlyListener, error) { Expect(c).To(Equal(conn)) return ln, nil } s := &Server{Server: &http.Server{}} s.TLSConfig = &tls.Config{} stopAccept := make(chan struct{}) ln.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.Session, error) { <-stopAccept return nil, errors.New("closed") }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) s.Serve(conn) }() Consistently(done).ShouldNot(BeClosed()) ln.EXPECT().Close().Do(func() { close(stopAccept) }) Expect(s.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("serves two packet conns", func() { ln1 := mockquic.NewMockEarlyListener(mockCtrl) ln2 := mockquic.NewMockEarlyListener(mockCtrl) lns := make(chan quic.EarlyListener, 2) lns <- ln1 lns <- ln2 conn1 := &net.UDPConn{} conn2 := &net.UDPConn{} quicListen = func(c net.PacketConn, tlsConf *tls.Config, config *quic.Config) (quic.EarlyListener, error) { return <-lns, nil } s := &Server{Server: &http.Server{}} s.TLSConfig = &tls.Config{} stopAccept1 := make(chan struct{}) ln1.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.Session, error) { <-stopAccept1 return nil, errors.New("closed") }) stopAccept2 := make(chan struct{}) ln2.EXPECT().Accept(gomock.Any()).DoAndReturn(func(context.Context) (quic.Session, error) { <-stopAccept2 return nil, errors.New("closed") }) 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() { close(stopAccept1) }) ln2.EXPECT().Close().Do(func() { close(stopAccept2) }) Expect(s.Close()).To(Succeed()) Eventually(done1).Should(BeClosed()) Eventually(done2).Should(BeClosed()) }) }) Context("ListenAndServe", func() { BeforeEach(func() { s.Server.Addr = "localhost:0" }) AfterEach(func() { Expect(s.Close()).To(Succeed()) }) checkGetConfigForClientVersions := func(conf *tls.Config) { c, err := conf.GetConfigForClient(&tls.ClientHelloInfo{Conn: newMockConn(protocol.VersionDraft29)}) ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, c.NextProtos).To(Equal([]string{nextProtoH3Draft29})) c, err = conf.GetConfigForClient(&tls.ClientHelloInfo{Conn: newMockConn(protocol.Version1)}) ExpectWithOffset(1, err).ToNot(HaveOccurred()) ExpectWithOffset(1, c.NextProtos).To(Equal([]string{nextProtoH3})) } 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) (quic.EarlyListener, error) { receivedConf = config return nil, errors.New("listen err") } s.QuicConfig = conf Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf).To(Equal(conf)) }) It("sets the GetConfigForClient and replaces the ALPN token to the tls.Config, if the GetConfigForClient callback is not set", func() { tlsConf := &tls.Config{ ClientAuth: tls.RequireAndVerifyClientCert, NextProtos: []string{"foo", "bar"}, } var receivedConf *tls.Config quicListenAddr = func(addr string, tlsConf *tls.Config, _ *quic.Config) (quic.EarlyListener, error) { receivedConf = tlsConf return nil, errors.New("listen err") } s.TLSConfig = tlsConf Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf.NextProtos).To(BeEmpty()) Expect(receivedConf.ClientAuth).To(BeZero()) // make sure the original tls.Config was not modified Expect(tlsConf.NextProtos).To(Equal([]string{"foo", "bar"})) // make sure that the config returned from the GetConfigForClient callback sets the fields of the original config conf, err := receivedConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(conf.ClientAuth).To(Equal(tls.RequireAndVerifyClientCert)) checkGetConfigForClientVersions(receivedConf) }) 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) (quic.EarlyListener, error) { receivedConf = tlsConf return nil, errors.New("listen err") } Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf).ToNot(BeNil()) checkGetConfigForClientVersions(receivedConf) }) It("sets the ALPN for tls.Configs returned by the tls.GetConfigForClient", func() { tlsConf := &tls.Config{ GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { return &tls.Config{ ClientAuth: tls.RequireAndVerifyClientCert, NextProtos: []string{"foo", "bar"}, }, nil }, } var receivedConf *tls.Config quicListenAddr = func(addr string, conf *tls.Config, _ *quic.Config) (quic.EarlyListener, error) { receivedConf = conf return nil, errors.New("listen err") } s.TLSConfig = tlsConf Expect(s.ListenAndServe()).To(HaveOccurred()) // check that the original config was not modified conf, err := tlsConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(conf.NextProtos).To(Equal([]string{"foo", "bar"})) // check that the config returned by the GetConfigForClient callback uses the returned config conf, err = receivedConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(conf.ClientAuth).To(Equal(tls.RequireAndVerifyClientCert)) checkGetConfigForClientVersions(receivedConf) }) It("sets the ALPN for tls.Configs returned by the tls.GetConfigForClient, if it returns a static tls.Config", func() { tlsClientConf := &tls.Config{NextProtos: []string{"foo", "bar"}} tlsConf := &tls.Config{ GetConfigForClient: func(ch *tls.ClientHelloInfo) (*tls.Config, error) { return tlsClientConf, nil }, } var receivedConf *tls.Config quicListenAddr = func(addr string, conf *tls.Config, _ *quic.Config) (quic.EarlyListener, error) { receivedConf = conf return nil, errors.New("listen err") } s.TLSConfig = tlsConf Expect(s.ListenAndServe()).To(HaveOccurred()) // check that the original config was not modified conf, err := tlsConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(conf.NextProtos).To(Equal([]string{"foo", "bar"})) checkGetConfigForClientVersions(receivedConf) }) It("works if GetConfigForClient returns a nil tls.Config", func() { tlsConf := &tls.Config{GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { return nil, nil }} var receivedConf *tls.Config quicListenAddr = func(addr string, conf *tls.Config, _ *quic.Config) (quic.EarlyListener, error) { receivedConf = conf return nil, errors.New("listen err") } s.TLSConfig = tlsConf Expect(s.ListenAndServe()).To(HaveOccurred()) conf, err := receivedConf.GetConfigForClient(&tls.ClientHelloInfo{}) Expect(err).ToNot(HaveOccurred()) Expect(conf).ToNot(BeNil()) checkGetConfigForClientVersions(receivedConf) }) }) 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) (quic.EarlyListener, 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) (quic.EarlyListener, error) { receivedConf = config return nil, errors.New("listen err") } Expect(s.ListenAndServe()).To(HaveOccurred()) Expect(receivedConf.EnableDatagrams).To(BeTrue()) }) }) quic-go-0.25.0/integrationtests/000077500000000000000000000000001417145451600165605ustar00rootroot00000000000000quic-go-0.25.0/integrationtests/gomodvendor/000077500000000000000000000000001417145451600211035ustar00rootroot00000000000000quic-go-0.25.0/integrationtests/gomodvendor/.gitignore000066400000000000000000000000101417145451600230620ustar00rootroot00000000000000vendor/ quic-go-0.25.0/integrationtests/gomodvendor/go.mod000066400000000000000000000003431417145451600222110ustar00rootroot00000000000000module test go 1.15 // The version doesn't matter here, as we're replacing it with the currently checked out code anyway. require github.com/lucas-clemente/quic-go v0.21.0 replace github.com/lucas-clemente/quic-go => ../../ quic-go-0.25.0/integrationtests/gomodvendor/go.sum000066400000000000000000000567351417145451600222560ustar00rootroot00000000000000cloud.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/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/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= 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.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/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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/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/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 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/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/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lucas-clemente/quic-go v0.21.0 h1:ZdC8UBxUSBdPlEv1+4y4SqIBy54VA8bRxN7DmkQ0URs= github.com/lucas-clemente/quic-go v0.21.0/go.mod h1:BWkfkkOSJD1AxFNBqdjBZi6FznZ96bhdcvZiA+LDrY8= 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/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qtls-go1-15 v0.1.4 h1:RehYMOyRW8hPVEja1KBVsFVNSm35Jj9Mvs5yNoZZ28A= github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= github.com/marten-seemann/qtls-go1-16 v0.1.3 h1:XEZ1xGorVy9u+lJq+WXNE+hiqRYLNvJGYmwfwKQN2gU= github.com/marten-seemann/qtls-go1-16 v0.1.3/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= github.com/marten-seemann/qtls-go1-17 v0.1.0-alpha.1 h1:LRFa3YRSlOAf9y56Szfhlh60CQrIMBSK/rneZD1gtuk= github.com/marten-seemann/qtls-go1-17 v0.1.0-alpha.1/go.mod h1:lQDiKZDfPagLmg1zMtEgoBMSTAORq6M08lBogD5FtBY= 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/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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= 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.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 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.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201231184435-2d18734c6014 h1:joucsQqXmyBVxViHCPFjG3hx8JzIFSaym3l3MM/Jsdg= golang.org/x/sys v0.0.0-20201231184435-2d18734c6014/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 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/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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 v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= quic-go-0.25.0/integrationtests/gomodvendor/main.go000066400000000000000000000003101417145451600223500ustar00rootroot00000000000000package main import "github.com/lucas-clemente/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{} } quic-go-0.25.0/integrationtests/self/000077500000000000000000000000001417145451600175115ustar00rootroot00000000000000quic-go-0.25.0/integrationtests/self/cancelation_test.go000066400000000000000000000537731417145451600233760ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "math/rand" "net" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Stream Cancelations", func() { const numStreams = 80 Context("canceling the read side", func() { var server quic.Listener // The server accepts a single session, 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 int32 go func() { defer GinkgoRecover() var wg sync.WaitGroup wg.Add(numStreams) sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) if _, err := str.Write(data); err != nil { Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) atomic.AddInt32(&canceledCounter, 1) return } if err := str.Close(); err != nil { Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) atomic.AddInt32(&canceledCounter, 1) return } }() } wg.Wait() numCanceledStreamsChan <- atomic.LoadInt32(&canceledCounter) }() return numCanceledStreamsChan } AfterEach(func() { Expect(server.Close()).To(Succeed()) }) It("downloads when the client immediately cancels most streams", func() { serverCanceledCounterChan := runServer(PRData) sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var canceledCounter int32 var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) // cancel around 2/3 of the streams if rand.Int31()%3 != 0 { atomic.AddInt32(&canceledCounter, 1) str.CancelRead(quic.StreamErrorCode(str.StreamID())) 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(sess.CloseWithError(0, "")).To(Succeed()) clientCanceledCounter := atomic.LoadInt32(&canceledCounter) // 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) sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), ) Expect(err).ToNot(HaveOccurred()) var canceledCounter int32 var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.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])) atomic.AddInt32(&canceledCounter, 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(sess.CloseWithError(0, "")).To(Succeed()) clientCanceledCounter := atomic.LoadInt32(&canceledCounter) // 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/lucas-clemente/quic-go/issues/3239. serverCanceledCounterChan := runServer(make([]byte, 100)) // make sure the FIN is sent with the STREAM frame sess, err := quic.DialAddr( 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 int32 for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.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 { atomic.AddInt32(&counter, 1) Expect(err.Error()).To(ContainSubstring("canceled with error code 1234")) return } }() go str.CancelRead(1234) Eventually(done).Should(BeClosed()) }() } wg.Wait() Expect(sess.CloseWithError(0, "")).To(Succeed()) numCanceled := atomic.LoadInt32(&counter) 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 */ { sess, err := quic.DialAddr( 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 int32 wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) if err != nil { atomic.AddInt32(&counter, 1) Expect(err).To(MatchError(&quic.StreamError{ StreamID: str.StreamID(), ErrorCode: quic.StreamErrorCode(str.StreamID()), })) return } Expect(data).To(Equal(PRData)) }() } wg.Wait() streamCount := atomic.LoadInt32(&counter) 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(sess.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 int32 go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() str, err := sess.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())) atomic.AddInt32(&canceledCounter, 1) return } _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } }() clientCanceledStreams := runClient(server) Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter))) }) 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 int32 go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() str, err := sess.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())) atomic.AddInt32(&canceledCounter, 1) return } _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } }() clientCanceledStreams := runClient(server) Expect(clientCanceledStreams).To(Equal(atomic.LoadInt32(&canceledCounter))) }) }) 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) sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.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) }() sess, err := quic.DialAddr( 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 int32 wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.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 } atomic.AddInt32(&counter, 1) Expect(data).To(Equal(PRData)) }() } wg.Wait() count := atomic.LoadInt32(&counter) Expect(count).To(BeNumerically(">", numStreams/15)) fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams) Expect(sess.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) sess, 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 := sess.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() }() sess, err := quic.DialAddr( 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 int32 wg.Add(numStreams) for i := 0; i < numStreams; i++ { go func() { defer GinkgoRecover() defer wg.Done() str, err := sess.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 } atomic.AddInt32(&counter, 1) Expect(data).To(Equal(PRData)) }() } wg.Wait() Eventually(done).Should(BeClosed()) count := atomic.LoadInt32(&counter) Expect(count).To(BeNumerically(">", numStreams/15)) fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams) Expect(sess.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() sess, 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 := sess.OpenUniStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() } }() sess, err := quic.DialAddr( 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 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 := sess.AcceptUniStream(ctx) if err != nil { if err.Error() == "context canceled" { atomic.AddInt32(&counter, 1) } return } data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) wg.Done() }() } wg.Wait() count := atomic.LoadInt32(&counter) fmt.Fprintf(GinkgoWriter, "Canceled AcceptStream %d times\n", count) Expect(count).To(BeNumerically(">", numStreams/2)) Expect(sess.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 int32 go func() { defer GinkgoRecover() defer close(msg) sess, 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 := sess.OpenUniStreamSync(ctx) if err != nil { Expect(err).To(MatchError(context.DeadlineExceeded)) atomic.AddInt32(&numCanceled, 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) } }() sess, err := quic.DialAddr( 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 := sess.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 := atomic.LoadInt32(&numCanceled) fmt.Fprintf(GinkgoWriter, "Canceled OpenStreamSync %d times\n", count) Expect(count).To(BeNumerically(">=", numStreams-maxIncomingStreams)) Expect(sess.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()) 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 session is closed regularly. Expect(err).To(BeAssignableToTypeOf(&quic.ApplicationError{})) return } handleStream(str) } }() sess, err := quic.DialAddr( 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 := sess.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) handleStream(str) } // We don't expect to accept any stream here. // We're just making sure the session stays open and there's no error. ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) defer cancel() _, err = sess.AcceptStream(ctx) Expect(err).To(MatchError(context.DeadlineExceeded)) wg.Wait() Expect(sess.CloseWithError(0, "")).To(Succeed()) Eventually(serverRunning).Should(BeClosed()) }) }) quic-go-0.25.0/integrationtests/self/conn_id_test.go000066400000000000000000000047171417145451600225210ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "math/rand" "net" quic "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Connection ID lengths tests", func() { randomConnIDLen := func() int { return 4 + int(rand.Int31n(15)) } runServer := func(conf *quic.Config) quic.Listener { GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID for the server\n", conf.ConnectionIDLength))) ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), conf) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() for { sess, err := ln.Accept(context.Background()) if err != nil { return } go func() { defer GinkgoRecover() str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) }() } }() return ln } runClient := func(addr net.Addr, conf *quic.Config) { GinkgoWriter.Write([]byte(fmt.Sprintf("Using %d byte connection ID for the client\n", conf.ConnectionIDLength))) cl, err := quic.DialAddr( fmt.Sprintf("localhost:%d", addr.(*net.UDPAddr).Port), getTLSClientConfig(), conf, ) 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() { serverConf := getQuicConfig(&quic.Config{ ConnectionIDLength: randomConnIDLen(), Versions: []protocol.VersionNumber{protocol.VersionTLS}, }) clientConf := getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{protocol.VersionTLS}, }) ln := runServer(serverConf) defer ln.Close() runClient(ln.Addr(), clientConf) }) It("downloads a file when both client and server use a random connection ID length", func() { serverConf := getQuicConfig(&quic.Config{ ConnectionIDLength: randomConnIDLen(), Versions: []protocol.VersionNumber{protocol.VersionTLS}, }) clientConf := getQuicConfig(&quic.Config{ ConnectionIDLength: randomConnIDLen(), Versions: []protocol.VersionNumber{protocol.VersionTLS}, }) ln := runServer(serverConf) defer ln.Close() runClient(ln.Addr(), clientConf) }) }) quic-go-0.25.0/integrationtests/self/datagram_test.go000066400000000000000000000074751417145451600226740ustar00rootroot00000000000000package self_test import ( "context" "encoding/binary" "fmt" mrand "math/rand" "net" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Datagram test", func() { for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { const num = 100 var ( proxy *quicproxy.QuicProxy serverConn, clientConn *net.UDPConn dropped, total int32 ) startServerAndProxy := 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: true, Versions: []protocol.VersionNumber{version}, }), ) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(sess.ConnectionState().SupportsDatagrams).To(BeTrue()) var wg sync.WaitGroup wg.Add(num) for i := 0; i < num; i++ { go func(i int) { defer GinkgoRecover() defer wg.Done() b := make([]byte, 8) binary.BigEndian.PutUint64(b, uint64(i)) Expect(sess.SendMessage(b)).To(Succeed()) }(i) } wg.Wait() }() 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 packet[0]&0x80 == 1 { return false } drop := mrand.Int()%10 == 0 if drop { atomic.AddInt32(&dropped, 1) } atomic.AddInt32(&total, 1) return drop }, }) Expect(err).ToNot(HaveOccurred()) } BeforeEach(func() { addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) clientConn, err = net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { Expect(proxy.Close()).To(Succeed()) }) It("sends datagrams", func() { startServerAndProxy() raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxy.LocalPort())) Expect(err).ToNot(HaveOccurred()) sess, err := quic.Dial( clientConn, raddr, fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ EnableDatagrams: true, Versions: []protocol.VersionNumber{version}, }), ) Expect(err).ToNot(HaveOccurred()) Expect(sess.ConnectionState().SupportsDatagrams).To(BeTrue()) var counter int for { // Close the session if no message is received for 100 ms. timer := time.AfterFunc(scaleDuration(100*time.Millisecond), func() { sess.CloseWithError(0, "") }) if _, err := sess.ReceiveMessage(); err != nil { break } timer.Stop() counter++ } numDropped := int(atomic.LoadInt32(&dropped)) expVal := num - numDropped fmt.Fprintf(GinkgoWriter, "Dropped %d out of %d packets.\n", numDropped, atomic.LoadInt32(&total)) fmt.Fprintf(GinkgoWriter, "Received %d out of %d sent datagrams.\n", counter, num) Expect(counter).To(And( BeNumerically(">", expVal*9/10), BeNumerically("<", num), )) }) }) } }) quic-go-0.25.0/integrationtests/self/deadline_test.go000066400000000000000000000137321417145451600226520ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "time" "github.com/lucas-clemente/quic-go" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Stream deadline tests", func() { setup := func() (quic.Listener, quic.Stream, quic.Stream) { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) Expect(err).ToNot(HaveOccurred()) strChan := make(chan quic.SendStream) go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) _, err = str.Read([]byte{0}) Expect(err).ToNot(HaveOccurred()) strChan <- str }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(nil), ) Expect(err).ToNot(HaveOccurred()) clientStr, err := sess.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()) var serverStr quic.Stream Eventually(strChan).Should(Receive(&serverStr)) return server, serverStr, clientStr } Context("read deadlines", func() { It("completes a transfer when the deadline is set", func() { server, serverStr, clientStr := setup() defer server.Close() const timeout = 20 * 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 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() { server, serverStr, clientStr := setup() defer server.Close() const timeout = 20 * 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() { server, serverStr, clientStr := setup() defer server.Close() const timeout = 20 * 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() { server, serverStr, clientStr := setup() defer server.Close() const timeout = 20 * 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()) }) }) }) quic-go-0.25.0/integrationtests/self/drop_test.go000066400000000000000000000102021417145451600220360ustar00rootroot00000000000000package self_test import ( "context" "fmt" "math/rand" "net" "sync/atomic" "time" quic "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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, version protocol.VersionNumber) { var err error ln, err = quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) 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 _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { 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 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 { atomic.AddInt32(&numDroppedPackets, 1) } return drop }, version) done := make(chan struct{}) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.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(sess.CloseWithError(0, "")).To(Succeed()) }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") str, err := sess.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 := atomic.LoadInt32(&numDroppedPackets) fmt.Fprintf(GinkgoWriter, "Dropped %d packets.\n", numDropped) Expect(numDropped).To(BeNumerically(">", 0)) }) } } }) } }) quic-go-0.25.0/integrationtests/self/early_data_test.go000066400000000000000000000042461417145451600232120ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "time" quic "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("early data", func() { const rtt = 80 * time.Millisecond for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { It("sends 0.5-RTT data", func() { ln, err := quic.ListenAddrEarly( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.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(sess.HandshakeComplete().Done()).ToNot(BeClosed()) Eventually(sess.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() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("early data"))) sess.CloseWithError(0, "") Eventually(done).Should(BeClosed()) }) }) } }) quic-go-0.25.0/integrationtests/self/handshake_drop_test.go000066400000000000000000000171451417145451600240610ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "io" mrand "math/rand" "net" "sync/atomic" "time" quic "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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(protocol.VersionNumber) } var _ = Describe("Handshake drop tests", func() { var ( proxy *quicproxy.QuicProxy ln quic.Listener ) data := GeneratePRData(5000) const timeout = 2 * time.Minute startListenerAndProxy := func(dropCallback quicproxy.DropCallback, doRetry bool, longCertChain bool, version protocol.VersionNumber) { conf := getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, Versions: []protocol.VersionNumber{version}, }) if !doRetry { conf.AcceptToken = func(net.Addr, *quic.Token) bool { return true } } var tlsConf *tls.Config if longCertChain { tlsConf = getTLSConfigWithLongCertChain() } else { tlsConf = getTLSConfig() } var err error ln, err = quic.ListenAddr("localhost:0", 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()) } stochasticDropper := func(freq int) bool { return mrand.Int63n(int64(freq)) == 0 } clientSpeaksFirst := &applicationProtocol{ name: "client speaks first", run: func(version protocol.VersionNumber) { serverSessionChan := make(chan quic.Session) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") str, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) b, err := io.ReadAll(gbytes.TimeoutReader(str, timeout)) Expect(err).ToNot(HaveOccurred()) Expect(b).To(Equal(data)) serverSessionChan <- sess }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, Versions: []protocol.VersionNumber{version}, }), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) var serverSession quic.Session Eventually(serverSessionChan, timeout).Should(Receive(&serverSession)) sess.CloseWithError(0, "") serverSession.CloseWithError(0, "") }, } serverSpeaksFirst := &applicationProtocol{ name: "server speaks first", run: func(version protocol.VersionNumber) { serverSessionChan := make(chan quic.Session) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(data) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) serverSessionChan <- sess }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, Versions: []protocol.VersionNumber{version}, }), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.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 serverSession quic.Session Eventually(serverSessionChan, timeout).Should(Receive(&serverSession)) sess.CloseWithError(0, "") serverSession.CloseWithError(0, "") }, } nobodySpeaks := &applicationProtocol{ name: "nobody speaks", run: func(version protocol.VersionNumber) { serverSessionChan := make(chan quic.Session) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) serverSessionChan <- sess }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: timeout, HandshakeIdleTimeout: timeout, Versions: []protocol.VersionNumber{version}, }), ) Expect(err).ToNot(HaveOccurred()) var serverSession quic.Session Eventually(serverSessionChan, timeout).Should(Receive(&serverSession)) // both server and client accepted a session. Close now. sess.CloseWithError(0, "") serverSession.CloseWithError(0, "") }, } AfterEach(func() { Expect(ln.Close()).To(Succeed()) Expect(proxy.Close()).To(Succeed()) }) for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { 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 int32 startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { var p int32 //nolint:exhaustive switch d { case quicproxy.DirectionIncoming: p = atomic.AddInt32(&incoming, 1) case quicproxy.DirectionOutgoing: p = atomic.AddInt32(&outgoing, 1) } return p == 1 && d.Is(direction) }, doRetry, longCertChain, version) app.run(version) }) It(fmt.Sprintf("establishes a connection when the second packet is lost in %s direction", direction), func() { var incoming, outgoing int32 startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { var p int32 //nolint:exhaustive switch d { case quicproxy.DirectionIncoming: p = atomic.AddInt32(&incoming, 1) case quicproxy.DirectionOutgoing: p = atomic.AddInt32(&outgoing, 1) } return p == 2 && d.Is(direction) }, doRetry, longCertChain, version) app.run(version) }) It(fmt.Sprintf("establishes a connection when 1/3 of the packets are lost in %s direction", direction), func() { startListenerAndProxy(func(d quicproxy.Direction, _ []byte) bool { return d.Is(direction) && stochasticDropper(3) }, doRetry, longCertChain, version) app.run(version) }) }) } }) } }) } } }) } }) quic-go-0.25.0/integrationtests/self/handshake_rtt_test.go000066400000000000000000000103371417145451600237220ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "net" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Handshake RTT tests", func() { var ( proxy *quicproxy.QuicProxy server quic.Listener serverConfig *quic.Config serverTLSConfig *tls.Config testStartedAt time.Time acceptStopped chan struct{} ) rtt := 400 * time.Millisecond BeforeEach(func() { acceptStopped = make(chan struct{}) serverConfig = getQuicConfig(nil) serverTLSConfig = getTLSConfig() }) AfterEach(func() { Expect(proxy.Close()).To(Succeed()) Expect(server.Close()).To(Succeed()) <-acceptStopped }) runServerAndProxy := func() { var err error // start the server server, err = quic.ListenAddr("localhost:0", serverTLSConfig, serverConfig) Expect(err).ToNot(HaveOccurred()) // start the proxy proxy, err = quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: server.Addr().String(), DelayPacket: func(_ quicproxy.Direction, _ []byte) time.Duration { return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) testStartedAt = time.Now() go func() { defer GinkgoRecover() defer close(acceptStopped) for { if _, err := server.Accept(context.Background()); err != nil { return } } }() } expectDurationInRTTs := func(num int) { testDuration := time.Since(testStartedAt) 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.Versions = protocol.SupportedVersions[:1] runServerAndProxy() clientConfig := getQuicConfig(&quic.Config{Versions: protocol.SupportedVersions[1:2]}) _, err := quic.DialAddr( proxy.LocalAddr().String(), getTLSClientConfig(), clientConfig, ) Expect(err).To(HaveOccurred()) expectDurationInRTTs(1) }) var clientConfig *quic.Config BeforeEach(func() { serverConfig.Versions = []protocol.VersionNumber{protocol.VersionTLS} clientConfig = getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}}) clientConfig := getTLSClientConfig() clientConfig.InsecureSkipVerify = true }) // 1 RTT for verifying the source address // 1 RTT for the TLS handshake It("is forward-secure after 2 RTTs", func() { runServerAndProxy() _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), clientConfig, ) Expect(err).ToNot(HaveOccurred()) expectDurationInRTTs(2) }) It("establishes a connection in 1 RTT when the server doesn't require a token", func() { serverConfig.AcceptToken = func(_ net.Addr, _ *quic.Token) bool { return true } runServerAndProxy() _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), clientConfig, ) Expect(err).ToNot(HaveOccurred()) expectDurationInRTTs(1) }) It("establishes a connection in 2 RTTs if a HelloRetryRequest is performed", func() { serverConfig.AcceptToken = func(_ net.Addr, _ *quic.Token) bool { return true } serverTLSConfig.CurvePreferences = []tls.CurveID{tls.CurveP384} runServerAndProxy() _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), clientConfig, ) Expect(err).ToNot(HaveOccurred()) expectDurationInRTTs(2) }) It("doesn't complete the handshake when the server never accepts the token", func() { serverConfig.AcceptToken = func(_ net.Addr, _ *quic.Token) bool { return false } clientConfig.HandshakeIdleTimeout = 500 * time.Millisecond runServerAndProxy() _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalAddr().(*net.UDPAddr).Port), getTLSClientConfig(), clientConfig, ) Expect(err).To(HaveOccurred()) nerr, ok := err.(net.Error) Expect(ok).To(BeTrue()) Expect(nerr.Timeout()).To(BeTrue()) }) }) quic-go-0.25.0/integrationtests/self/handshake_test.go000066400000000000000000000444331417145451600230350ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "errors" "fmt" "io" "net" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/integrationtests/tools/israce" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type versioner interface { GetVersion() protocol.VersionNumber } 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) } type versionNegotiationTracer struct { connTracer loggedVersions bool receivedVersionNegotiation bool chosen logging.VersionNumber clientVersions, serverVersions []logging.VersionNumber } func (t *versionNegotiationTracer) NegotiatedVersion(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) { if t.loggedVersions { Fail("only expected one call to NegotiatedVersions") } t.loggedVersions = true t.chosen = chosen t.clientVersions = clientVersions t.serverVersions = serverVersions } func (t *versionNegotiationTracer) ReceivedVersionNegotiationPacket(*logging.Header, []logging.VersionNumber) { t.receivedVersionNegotiation = true } 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 } } }() } if !israce.Enabled { Context("Version Negotiation", func() { var supportedVersions []protocol.VersionNumber BeforeEach(func() { supportedVersions = protocol.SupportedVersions protocol.SupportedVersions = append(protocol.SupportedVersions, []protocol.VersionNumber{7, 8, 9, 10}...) }) AfterEach(func() { protocol.SupportedVersions = supportedVersions }) 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.Versions = []protocol.VersionNumber{7, 8, protocol.SupportedVersions[0], 9} serverTracer := &versionNegotiationTracer{} serverConfig.Tracer = newTracer(func() logging.ConnectionTracer { return serverTracer }) runServer(getTLSConfig()) defer server.Close() clientTracer := &versionNegotiationTracer{} sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer })}), ) Expect(err).ToNot(HaveOccurred()) Expect(sess.(versioner).GetVersion()).To(Equal(expectedVersion)) Expect(sess.CloseWithError(0, "")).To(Succeed()) Expect(clientTracer.chosen).To(Equal(expectedVersion)) Expect(clientTracer.receivedVersionNegotiation).To(BeFalse()) Expect(clientTracer.clientVersions).To(Equal(protocol.SupportedVersions)) Expect(clientTracer.serverVersions).To(BeEmpty()) Expect(serverTracer.chosen).To(Equal(expectedVersion)) Expect(serverTracer.serverVersions).To(Equal(serverConfig.Versions)) Expect(serverTracer.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 serverConfig.Versions = supportedVersions serverTracer := &versionNegotiationTracer{} serverConfig.Tracer = newTracer(func() logging.ConnectionTracer { return serverTracer }) runServer(getTLSConfig()) defer server.Close() clientVersions := []protocol.VersionNumber{7, 8, 9, protocol.SupportedVersions[0], 10} clientTracer := &versionNegotiationTracer{} sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{ Versions: clientVersions, Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer }), }), ) Expect(err).ToNot(HaveOccurred()) Expect(sess.(versioner).GetVersion()).To(Equal(protocol.SupportedVersions[0])) Expect(sess.CloseWithError(0, "")).To(Succeed()) Expect(clientTracer.chosen).To(Equal(expectedVersion)) Expect(clientTracer.receivedVersionNegotiation).To(BeTrue()) Expect(clientTracer.clientVersions).To(Equal(clientVersions)) Expect(clientTracer.serverVersions).To(ContainElements(supportedVersions)) // may contain greased versions Expect(serverTracer.chosen).To(Equal(expectedVersion)) Expect(serverTracer.serverVersions).To(Equal(serverConfig.Versions)) Expect(serverTracer.clientVersions).To(BeEmpty()) }) }) } 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() { tlsConf := getTLSConfig() tlsConf.CipherSuites = []uint16{suiteID} ln, err := quic.ListenAddr("localhost:0", tlsConf, serverConfig) Expect(err).ToNot(HaveOccurred()) defer ln.Close() go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), nil, ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) Expect(sess.ConnectionState().TLS.CipherSuite).To(Equal(suiteID)) Expect(sess.CloseWithError(0, "")).To(Succeed()) }) } }) Context("Certificate validation", func() { for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("using %s", version), func() { var clientConfig *quic.Config BeforeEach(func() { serverConfig.Versions = []protocol.VersionNumber{version} clientConfig = getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}) }) It("accepts the certificate", func() { runServer(getTLSConfig()) _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), clientConfig, ) Expect(err).ToNot(HaveOccurred()) }) It("works with a long certificate chain", func() { runServer(getTLSConfigWithLongCertChain()) _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) }) It("errors if the server name doesn't match", func() { runServer(getTLSConfig()) conn, err := net.ListenUDP("udp", nil) Expect(err).ToNot(HaveOccurred()) _, err = quic.Dial( conn, server.Addr(), "foo.bar", getTLSClientConfig(), clientConfig, ) 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")) }) It("fails the handshake if the client fails to provide the requested client cert", func() { tlsConf := getTLSConfig() tlsConf.ClientAuth = tls.RequireAndVerifyClientCert runServer(tlsConf) sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), clientConfig, ) // 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 session is returned, so we might already get the error while dialing. if err == nil { errChan := make(chan error) go func() { defer GinkgoRecover() _, err := sess.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(ContainSubstring("tls: bad certificate")) }) It("uses the ServerName in the tls.Config", func() { runServer(getTLSConfig()) tlsConf := getTLSClientConfig() tlsConf.ServerName = "foo.bar" _, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, clientConfig, ) 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("rate limiting", func() { var ( server quic.Listener pconn net.PacketConn ) dial := func() (quic.Session, error) { remoteAddr := fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) raddr, err := net.ResolveUDPAddr("udp", remoteAddr) Expect(err).ToNot(HaveOccurred()) return quic.Dial( pconn, raddr, remoteAddr, getTLSClientConfig(), nil, ) } BeforeEach(func() { serverConfig.AcceptToken = func(addr net.Addr, token *quic.Token) bool { if token != nil { Expect(token.IsRetryToken).To(BeFalse()) } return true } 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()) }) AfterEach(func() { Expect(server.Close()).To(Succeed()) Expect(pconn.Close()).To(Succeed()) }) It("rejects new connection attempts if connections don't get accepted", func() { for i := 0; i < protocol.MaxAcceptQueueSize; i++ { sess, err := dial() Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") } time.Sleep(25 * time.Millisecond) // wait a bit for the sessions to be queued _, err := dial() Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) // now accept one session, freeing one spot in the queue _, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) // dial again, and expect that this dial succeeds sess, err := dial() Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") time.Sleep(25 * time.Millisecond) // wait a bit for the session to be queued _, err = dial() Expect(err).To(HaveOccurred()) Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) }) It("removes closed connections from the accept queue", func() { firstSess, err := dial() Expect(err).ToNot(HaveOccurred()) for i := 1; i < protocol.MaxAcceptQueueSize; i++ { sess, err := dial() Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") } time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the sessions to be queued _, err = dial() Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.ConnectionRefused)) // Now close the one of the session that are waiting to be accepted. // This should free one spot in the queue. Expect(firstSess.CloseWithError(0, "")) Eventually(firstSess.Context().Done()).Should(BeClosed()) time.Sleep(scaleDuration(20 * time.Millisecond)) // dial again, and expect that this dial succeeds _, err = dial() Expect(err).ToNot(HaveOccurred()) time.Sleep(scaleDuration(20 * time.Millisecond)) // wait a bit for the session to be queued _, err = dial() Expect(err).To(HaveOccurred()) 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() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) cs := sess.ConnectionState() Expect(cs.TLS.NegotiatedProtocol).To(Equal(alpn)) close(done) }() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), nil, ) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") cs := sess.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( 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() { tokenChan := make(chan *quic.Token, 100) serverConfig.AcceptToken = func(addr net.Addr, token *quic.Token) bool { if token != nil && !token.IsRetryToken { tokenChan <- token } return true } server, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) // dial the first session 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}) sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), quicConf, ) Expect(err).ToNot(HaveOccurred()) Expect(gets).To(Receive()) Eventually(puts).Should(Receive()) Expect(tokenChan).ToNot(Receive()) // received a token. Close this session. Expect(sess.CloseWithError(0, "")).To(Succeed()) // dial the second session 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()) }() sess, err = quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), quicConf, ) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") Expect(gets).To(Receive()) Expect(tokenChan).To(Receive()) Eventually(done).Should(BeClosed()) }) It("rejects invalid Retry token with the INVALID_TOKEN error", func() { tokenChan := make(chan *quic.Token, 10) serverConfig.AcceptToken = func(addr net.Addr, token *quic.Token) bool { tokenChan <- token return false } server, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) defer server.Close() _, err = quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), nil, ) Expect(err).To(HaveOccurred()) var transportErr *quic.TransportError Expect(errors.As(err, &transportErr)).To(BeTrue()) Expect(transportErr.ErrorCode).To(Equal(quic.InvalidToken)) // Receiving a Retry might lead the client to measure a very small RTT. // Then, it sometimes would retransmit the ClientHello before receiving the ServerHello. Expect(len(tokenChan)).To(BeNumerically(">=", 2)) token := <-tokenChan Expect(token).To(BeNil()) token = <-tokenChan Expect(token).ToNot(BeNil()) // If the ClientHello was retransmitted, make sure that it contained the same Retry token. for i := 2; i < len(tokenChan); i++ { Expect(<-tokenChan).To(Equal(token)) } Expect(token.IsRetryToken).To(BeTrue()) }) }) }) quic-go-0.25.0/integrationtests/self/http_test.go000066400000000000000000000231071417145451600220610ustar00rootroot00000000000000package self_test import ( "bufio" "bytes" "compress/gzip" "context" "crypto/tls" "errors" "fmt" "io" "net" "net/http" "strconv" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/http3" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/testdata" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) var _ = Describe("HTTP tests", func() { var ( mux *http.ServeMux client *http.Client server *http3.Server stoppedServing chan struct{} port string ) versions := protocol.SupportedVersions 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. }) server = &http3.Server{ Server: &http.Server{ Handler: mux, TLSConfig: testdata.GetTLSConfig(), }, QuicConfig: getQuicConfig(&quic.Config{Versions: versions}), } 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 = strconv.Itoa(conn.LocalAddr().(*net.UDPAddr).Port) stoppedServing = make(chan struct{}) go func() { defer GinkgoRecover() server.Serve(conn) close(stoppedServing) }() }) AfterEach(func() { Expect(server.Close()).NotTo(HaveOccurred()) Eventually(stoppedServing).Should(BeClosed()) }) for _, v := range versions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { BeforeEach(func() { client = &http.Client{ Transport: &http3.RoundTripper{ TLSClientConfig: &tls.Config{ RootCAs: testdata.GetRootCA(), }, DisableCompression: true, QuicConfig: getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, MaxIdleTimeout: 10 * time.Second, }), }, } }) It("downloads a hello", func() { resp, err := client.Get("https://localhost:" + port + "/hello") 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 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, "https://localhost:"+port+"/headers/request", 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("https://localhost:" + port + "/headers/response") 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("https://localhost:" + port + "/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("downloads a large file", func() { resp, err := client.Get("https://localhost:" + port + "/prdatalong") 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("https://localhost:" + port + "/hello") 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("https://localhost:" + port + "/prdata") 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( "https://localhost:"+port+"/echo", "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( "https://localhost:"+port+"/echo", "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("https://localhost:" + port + "/gzipped/hello") 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("cancels requests", func() { handlerCalled := make(chan struct{}) mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) { defer GinkgoRecover() defer close(handlerCalled) for { if _, err := w.Write([]byte("foobar")); err != nil { Expect(r.Context().Done()).To(BeClosed()) var strErr *quic.StreamError Expect(errors.As(err, &strErr)).To(BeTrue()) Expect(strErr.ErrorCode).To(Equal(quic.StreamErrorCode(0x10c))) return } } }) req, err := http.NewRequest(http.MethodGet, "https://localhost:"+port+"/cancel", 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}) Expect(err).To(HaveOccurred()) }) 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("PUT", "https://localhost:"+port+"/echoline", 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()) }) }) } }) quic-go-0.25.0/integrationtests/self/key_update_test.go000066400000000000000000000055351417145451600232410ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" quic "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var ( sentHeaders []*logging.ExtendedHeader receivedHeaders []*logging.ExtendedHeader ) func countKeyPhases() (sent, received int) { lastKeyPhase := protocol.KeyPhaseOne for _, hdr := range sentHeaders { if hdr.IsLongHeader { continue } if hdr.KeyPhase != lastKeyPhase { sent++ lastKeyPhase = hdr.KeyPhase } } lastKeyPhase = protocol.KeyPhaseOne for _, hdr := range receivedHeaders { if hdr.IsLongHeader { continue } if hdr.KeyPhase != lastKeyPhase { received++ lastKeyPhase = hdr.KeyPhase } } return } type keyUpdateConnTracer struct { connTracer } func (t *keyUpdateConnTracer) SentPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { sentHeaders = append(sentHeaders, hdr) } func (t *keyUpdateConnTracer) ReceivedPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, frames []logging.Frame) { receivedHeaders = append(receivedHeaders, hdr) } var _ = Describe("Key Update tests", func() { var server quic.Listener runServer := func() { var err error server, err = quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRDataLong) Expect(err).ToNot(HaveOccurred()) }() } It("downloads a large file", func() { origKeyUpdateInterval := handshake.KeyUpdateInterval defer func() { handshake.KeyUpdateInterval = origKeyUpdateInterval }() handshake.KeyUpdateInterval = 1 // update keys as frequently as possible runServer() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{Tracer: newTracer(func() logging.ConnectionTracer { return &keyUpdateConnTracer{} })}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRDataLong)) Expect(sess.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)) }) }) quic-go-0.25.0/integrationtests/self/mitm_test.go000066400000000000000000000403411417145451600220470ustar00rootroot00000000000000package self_test import ( "bytes" "context" "errors" "fmt" "io" "math" mrand "math/rand" "net" "sync/atomic" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/testutils" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("MITM test", func() { for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { const connIDLen = 6 // explicitly set the connection ID length, so the proxy can parse it var ( proxy *quicproxy.QuicProxy serverConn, clientConn *net.UDPConn serverSess quic.Session serverConfig *quic.Config ) startServerAndProxy := func(delayCb quicproxy.DelayCallback, dropCb quicproxy.DropCallback) { 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(), serverConfig) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() var err error serverSess, err = ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := serverSess.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()) } BeforeEach(func() { serverConfig = getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, ConnectionIDLength: connIDLen, }) addr, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) clientConn, err = net.ListenUDP("udp", addr) Expect(err).ToNot(HaveOccurred()) }) Context("unsuccessful attacks", func() { AfterEach(func() { Eventually(serverSess.Context().Done()).Should(BeClosed()) // Test shutdown is tricky due to the proxy. Just wait for a bit. time.Sleep(50 * time.Millisecond) Expect(clientConn.Close()).To(Succeed()) Expect(serverConn.Close()).To(Succeed()) Expect(proxy.Close()).To(Succeed()) }) Context("injecting invalid packets", func() { const rtt = 20 * time.Millisecond sendRandomPacketsOfSameType := func(conn net.PacketConn, remoteAddr net.Addr, raw []byte) { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw, connIDLen) Expect(err).ToNot(HaveOccurred()) replyHdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: hdr.IsLongHeader, DestConnectionID: hdr.DestConnectionID, SrcConnectionID: hdr.SrcConnectionID, Type: hdr.Type, Version: hdr.Version, }, PacketNumber: protocol.PacketNumber(mrand.Int31n(math.MaxInt32 / 4)), PacketNumberLen: protocol.PacketNumberLen(mrand.Int31n(4) + 1), } const numPackets = 10 ticker := time.NewTicker(rtt / numPackets) for i := 0; i < numPackets; i++ { payloadLen := mrand.Int31n(100) replyHdr.Length = protocol.ByteCount(mrand.Int31n(payloadLen + 1)) buf := &bytes.Buffer{} Expect(replyHdr.Write(buf, version)).To(Succeed()) b := make([]byte, payloadLen) mrand.Read(b) buf.Write(b) if _, err := conn.WriteTo(buf.Bytes(), remoteAddr); err != nil { return } <-ticker.C } } runTest := func(delayCb quicproxy.DelayCallback) { startServerAndProxy(delayCb, nil) raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxy.LocalPort())) Expect(err).ToNot(HaveOccurred()) sess, err := quic.Dial( clientConn, raddr, fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, ConnectionIDLength: connIDLen, }), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) Expect(sess.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(clientConn, serverConn.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(serverConn, clientConn.LocalAddr(), raw) } return rtt / 2 } runTest(delayCb) }) }) runTest := func(dropCb quicproxy.DropCallback) { startServerAndProxy(nil, dropCb) raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxy.LocalPort())) Expect(err).ToNot(HaveOccurred()) sess, err := quic.Dial( clientConn, raddr, fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, ConnectionIDLength: connIDLen, }), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) Expect(sess.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 := clientConn.WriteTo(raw, serverConn.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 := serverConn.WriteTo(raw, clientConn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) } return false } runTest(dropCb) }) }) Context("corrupting packets", func() { const idleTimeout = time.Second var numCorrupted, numPackets int32 BeforeEach(func() { numCorrupted = 0 numPackets = 0 serverConfig.MaxIdleTimeout = idleTimeout }) AfterEach(func() { num := atomic.LoadInt32(&numCorrupted) fmt.Fprintf(GinkgoWriter, "Corrupted %d of %d packets.", num, atomic.LoadInt32(&numPackets)) Expect(num).To(BeNumerically(">=", 1)) // If the packet containing the CONNECTION_CLOSE is corrupted, // we have to wait for the session to time out. Eventually(serverSess.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 { atomic.AddInt32(&numPackets, 1) if mrand.Intn(interval) == 0 { pos := mrand.Intn(len(raw)) raw[pos] = byte(mrand.Intn(256)) _, err := clientConn.WriteTo(raw, serverConn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) atomic.AddInt32(&numCorrupted, 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 { atomic.AddInt32(&numPackets, 1) if mrand.Intn(interval) == 0 { pos := mrand.Intn(len(raw)) raw[pos] = byte(mrand.Intn(256)) _, err := serverConn.WriteTo(raw, clientConn.LocalAddr()) Expect(err).ToNot(HaveOccurred()) atomic.AddInt32(&numCorrupted, 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 // AfterEach closes the proxy, but each function is responsible // for closing client and server connections AfterEach(func() { // Test shutdown is tricky due to the proxy. Just wait for a bit. time.Sleep(50 * time.Millisecond) Expect(proxy.Close()).To(Succeed()) }) // sendForgedVersionNegotiationPacket sends a fake VN packet with no supported versions // from serverConn to client's remoteAddr // expects hdr from an Initial packet intercepted from client sendForgedVersionNegotationPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) { // Create fake version negotiation packet with no supported versions versions := []protocol.VersionNumber{} packet, _ := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, versions) // Send the packet _, err := conn.WriteTo(packet, remoteAddr) Expect(err).ToNot(HaveOccurred()) } // sendForgedRetryPacket sends a fake Retry packet with a modified srcConnID // from serverConn to client's remoteAddr // expects hdr from an Initial packet intercepted from client sendForgedRetryPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) { var x byte = 0x12 fakeSrcConnID := protocol.ConnectionID{x, x, x, x, x, x, x, x} retryPacket := testutils.ComposeRetryPacket(fakeSrcConnID, hdr.SrcConnectionID, hdr.DestConnectionID, []byte("token"), hdr.Version) _, err := conn.WriteTo(retryPacket, remoteAddr) Expect(err).ToNot(HaveOccurred()) } // Send a forged Initial packet with no frames to client // expects hdr from an Initial packet intercepted from client sendForgedInitialPacket := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) { initialPacket := testutils.ComposeInitialPacket(hdr.DestConnectionID, hdr.SrcConnectionID, hdr.Version, hdr.DestConnectionID, nil) _, err := conn.WriteTo(initialPacket, remoteAddr) Expect(err).ToNot(HaveOccurred()) } // Send a forged Initial packet with ACK for random packet to client // expects hdr from an Initial packet intercepted from client sendForgedInitialPacketWithAck := func(conn net.PacketConn, remoteAddr net.Addr, hdr *wire.Header) { // 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.Version, hdr.DestConnectionID, []wire.Frame{ack}) _, err := conn.WriteTo(initialPacket, remoteAddr) Expect(err).ToNot(HaveOccurred()) } runTest := func(delayCb quicproxy.DelayCallback) error { startServerAndProxy(delayCb, nil) raddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("localhost:%d", proxy.LocalPort())) Expect(err).ToNot(HaveOccurred()) _, err = quic.Dial( clientConn, raddr, fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, ConnectionIDLength: connIDLen, HandshakeIdleTimeout: 2 * time.Second, }), ) return 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() { delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw, connIDLen) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial { return 0 } sendForgedVersionNegotationPacket(serverConn, clientConn.LocalAddr(), hdr) } return rtt / 2 } err := runTest(delayCb) Expect(err).To(HaveOccurred()) vnErr := &quic.VersionNegotiationError{} Expect(errors.As(err, &vnErr)).To(BeTrue()) }) // 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 srcConnID is sent to client", func() { var initialPacketIntercepted bool delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming && !initialPacketIntercepted { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw, connIDLen) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial { return 0 } initialPacketIntercepted = true sendForgedRetryPacket(serverConn, clientConn.LocalAddr(), hdr) } return rtt / 2 } err := runTest(delayCb) Expect(err).To(HaveOccurred()) Expect(err.(net.Error).Timeout()).To(BeTrue()) }) // 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() { delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { defer GinkgoRecover() hdr, _, _, err := wire.ParsePacket(raw, connIDLen) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial { return 0 } sendForgedInitialPacket(serverConn, clientConn.LocalAddr(), hdr) } return rtt } err := runTest(delayCb) Expect(err).To(HaveOccurred()) Expect(err.(net.Error).Timeout()).To(BeTrue()) }) // 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() { clientAddr := clientConn.LocalAddr() delayCb := func(dir quicproxy.Direction, raw []byte) time.Duration { if dir == quicproxy.DirectionIncoming { hdr, _, _, err := wire.ParsePacket(raw, connIDLen) Expect(err).ToNot(HaveOccurred()) if hdr.Type != protocol.PacketTypeInitial { return 0 } sendForgedInitialPacketWithAck(serverConn, clientAddr, hdr) } return rtt } err := runTest(delayCb) 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")) }) }) }) } }) quic-go-0.25.0/integrationtests/self/multiplex_test.go000066400000000000000000000135271417145451600231320ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "runtime" "time" quic "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Multiplexing", func() { for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { runServer := func(ln quic.Listener) { go func() { defer GinkgoRecover() for { sess, err := ln.Accept(context.Background()) if err != nil { return } go func() { defer GinkgoRecover() str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) defer str.Close() _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) }() } }() } dial := func(conn net.PacketConn, addr net.Addr) { sess, err := quic.Dial( conn, addr, fmt.Sprintf("localhost:%d", addr.(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") str, err := sess.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(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) 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() done1 := make(chan struct{}) done2 := make(chan struct{}) go func() { defer GinkgoRecover() dial(conn, server.Addr()) close(done1) }() go func() { defer GinkgoRecover() dial(conn, 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() done1 := make(chan struct{}) done2 := make(chan struct{}) go func() { defer GinkgoRecover() dial(conn, server1.Addr()) close(done1) }() go func() { defer GinkgoRecover() dial(conn, 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() server, err := quic.Listen( conn, getTLSConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) runServer(server) done := make(chan struct{}) go func() { defer GinkgoRecover() dial(conn, server.Addr()) close(done) }() timeout := 30 * time.Second if debugLog() { timeout = time.Minute } Eventually(done, timeout).Should(BeClosed()) }) It("runs a server and client on the same conn", func() { if runtime.GOOS == "linux" { Skip("This test would require setting of iptables rules, see https://stackoverflow.com/questions/23859164/linux-udp-socket-sendto-operation-not-permitted.") } addr1, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn1, err := net.ListenUDP("udp", addr1) Expect(err).ToNot(HaveOccurred()) defer conn1.Close() addr2, err := net.ResolveUDPAddr("udp", "localhost:0") Expect(err).ToNot(HaveOccurred()) conn2, err := net.ListenUDP("udp", addr2) Expect(err).ToNot(HaveOccurred()) defer conn2.Close() server1, err := quic.Listen( conn1, getTLSConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) runServer(server1) defer server1.Close() server2, err := quic.Listen( conn2, getTLSConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) runServer(server2) defer server2.Close() done1 := make(chan struct{}) done2 := make(chan struct{}) go func() { defer GinkgoRecover() dial(conn2, server1.Addr()) close(done1) }() go func() { defer GinkgoRecover() dial(conn1, server2.Addr()) close(done2) }() timeout := 30 * time.Second if debugLog() { timeout = time.Minute } Eventually(done1, timeout).Should(BeClosed()) Eventually(done2, timeout).Should(BeClosed()) }) }) }) } }) quic-go-0.25.0/integrationtests/self/packetization_test.go000066400000000000000000000071021417145451600237440ustar00rootroot00000000000000package self_test import ( "context" "fmt" "net" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "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 serverTracer := newPacketTracer() server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{ AcceptToken: func(net.Addr, *quic.Token) bool { return true }, DisablePathMTUDiscovery: true, Tracer: newTracer(func() logging.ConnectionTracer { return serverTracer }), }), ) Expect(err).ToNot(HaveOccurred()) serverAddr := fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) defer server.Close() 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() clientTracer := newPacketTracer() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ DisablePathMTUDiscovery: true, Tracer: newTracer(func() logging.ConnectionTracer { return clientTracer }), }), ) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.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 := sess.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(sess.CloseWithError(0, "")).To(Succeed()) countBundledPackets := func(packets []packet) (numBundled int) { for _, p := range packets { if p.hdr.IsLongHeader { continue } 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(clientTracer.getRcvdPackets()) numBundledOutgoing := countBundledPackets(serverTracer.getRcvdPackets()) 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), )) }) }) quic-go-0.25.0/integrationtests/self/resumption_test.go000066400000000000000000000065771417145451600233230ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "net" "sync" quic "github.com/lucas-clemente/quic-go" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type clientSessionCache struct { mutex sync.Mutex cache map[string]*tls.ClientSessionState gets chan<- string puts chan<- string } func newClientSessionCache(gets, puts chan<- string) *clientSessionCache { return &clientSessionCache{ cache: make(map[string]*tls.ClientSessionState), gets: gets, puts: puts, } } var _ tls.ClientSessionCache = &clientSessionCache{} func (c *clientSessionCache) Get(sessionKey string) (*tls.ClientSessionState, bool) { c.gets <- sessionKey c.mutex.Lock() session, ok := c.cache[sessionKey] c.mutex.Unlock() return session, ok } func (c *clientSessionCache) Put(sessionKey string, cs *tls.ClientSessionState) { c.puts <- sessionKey c.mutex.Lock() c.cache[sessionKey] = cs c.mutex.Unlock() } var _ = Describe("TLS session resumption", func() { It("uses session resumption", func() { server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) Expect(err).ToNot(HaveOccurred()) defer server.Close() gets := make(chan string, 100) puts := make(chan string, 100) cache := newClientSessionCache(gets, puts) tlsConf := getTLSClientConfig() tlsConf.ClientSessionCache = cache sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, nil, ) Expect(err).ToNot(HaveOccurred()) var sessionKey string Eventually(puts).Should(Receive(&sessionKey)) Expect(sess.ConnectionState().TLS.DidResume).To(BeFalse()) serverSess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverSess.ConnectionState().TLS.DidResume).To(BeFalse()) sess, err = quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, nil, ) Expect(err).ToNot(HaveOccurred()) Expect(gets).To(Receive(Equal(sessionKey))) Expect(sess.ConnectionState().TLS.DidResume).To(BeTrue()) serverSess, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverSess.ConnectionState().TLS.DidResume).To(BeTrue()) }) It("doesn't use session resumption, if the config disables it", func() { sConf := getTLSConfig() sConf.SessionTicketsDisabled = true server, err := quic.ListenAddr("localhost:0", sConf, nil) Expect(err).ToNot(HaveOccurred()) defer server.Close() gets := make(chan string, 100) puts := make(chan string, 100) cache := newClientSessionCache(gets, puts) tlsConf := getTLSClientConfig() tlsConf.ClientSessionCache = cache sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, nil, ) Expect(err).ToNot(HaveOccurred()) Consistently(puts).ShouldNot(Receive()) Expect(sess.ConnectionState().TLS.DidResume).To(BeFalse()) serverSess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverSess.ConnectionState().TLS.DidResume).To(BeFalse()) sess, err = quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), tlsConf, nil, ) Expect(err).ToNot(HaveOccurred()) Expect(sess.ConnectionState().TLS.DidResume).To(BeFalse()) serverSess, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(serverSess.ConnectionState().TLS.DidResume).To(BeFalse()) }) }) quic-go-0.25.0/integrationtests/self/rtt_test.go000066400000000000000000000063601417145451600217150ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "time" quic "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("non-zero RTT", func() { for _, v := range protocol.SupportedVersions { version := v runServer := func() quic.Listener { ln, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) str.Close() }() return ln } downloadFile := func(port int) { sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", port), getTLSClientConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) sess.CloseWithError(0, "") } Context(fmt.Sprintf("with QUIC version %s", version), func() { 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() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) sess.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()) }) } }) } }) quic-go-0.25.0/integrationtests/self/self_suite_test.go000066400000000000000000000300661417145451600232460ustar00rootroot00000000000000package self_test import ( "bufio" "bytes" "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "flag" "fmt" "io" "log" "math/big" mrand "math/rand" "net" "os" "strconv" "sync" "testing" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" "github.com/lucas-clemente/quic-go/qlog" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) const alpn = "quic-go integration tests" 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 enableQlog bool tlsConfig *tls.Config tlsConfigLongChain *tls.Config tlsClientConfig *tls.Config quicConfigTracer logging.Tracer ) // read the logfile command line flag // to set call ginkgo -- -logfile=log.txt func init() { flag.StringVar(&logFileName, "logfile", "", "log file") flag.BoolVar(&enableQlog, "qlog", false, "enable qlog") } var _ = BeforeSuite(func() { mrand.Seed(GinkgoRandomSeed()) ca, caPrivateKey, err := generateCA() if err != nil { panic(err) } leafCert, leafPrivateKey, err := 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 := generateTLSConfigWithLongCertChain(ca, caPrivateKey) if err != nil { panic(err) } tlsConfigLongChain = tlsConfLongChain root := x509.NewCertPool() root.AddCert(ca) tlsClientConfig = &tls.Config{ RootCAs: root, NextProtos: []string{alpn}, } if enableQlog { quicConfigTracer = qlog.NewTracer(func(p logging.Perspective, connectionID []byte) io.WriteCloser { role := "server" if p == logging.PerspectiveClient { role = "client" } filename := fmt.Sprintf("log_%x_%s.qlog", connectionID, role) fmt.Fprintf(GinkgoWriter, "Creating %s.\n", filename) f, err := os.Create(filename) Expect(err).ToNot(HaveOccurred()) bw := bufio.NewWriter(f) return utils.NewBufferedWriteCloser(bw, f) }) } }) func generateCA() (*x509.Certificate, *rsa.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, } caPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } caBytes, err := x509.CreateCertificate(rand.Reader, certTempl, certTempl, &caPrivateKey.PublicKey, caPrivateKey) if err != nil { return nil, nil, err } ca, err := x509.ParseCertificate(caBytes) if err != nil { return nil, nil, err } return ca, caPrivateKey, nil } func generateLeafCert(ca *x509.Certificate, caPrivateKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { certTempl := &x509.Certificate{ SerialNumber: big.NewInt(1), DNSNames: []string{"localhost"}, NotBefore: time.Now(), NotAfter: time.Now().Add(24 * time.Hour), ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, KeyUsage: x509.KeyUsageDigitalSignature, } privKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } certBytes, err := x509.CreateCertificate(rand.Reader, certTempl, ca, &privKey.PublicKey, caPrivateKey) if err != nil { return nil, nil, err } cert, err := x509.ParseCertificate(certBytes) if err != nil { return nil, nil, err } return cert, privKey, nil } // getTLSConfigWithLongCertChain 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 *rsa.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 } func getTLSConfig() *tls.Config { return tlsConfig.Clone() } func getTLSConfigWithLongCertChain() *tls.Config { return tlsConfigLongChain.Clone() } func getTLSClientConfig() *tls.Config { return tlsClientConfig.Clone() } func getQuicConfig(conf *quic.Config) *quic.Config { if conf == nil { conf = &quic.Config{} } else { conf = conf.Clone() } if conf.Tracer == nil { conf.Tracer = quicConfigTracer } else if quicConfigTracer != nil { conf.Tracer = logging.NewMultiplexedTracer(quicConfigTracer, conf.Tracer) } return conf } 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) } }) var _ = AfterEach(func() { 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 } type tracer struct { createNewConnTracer func() logging.ConnectionTracer } var _ logging.Tracer = &tracer{} func newTracer(c func() logging.ConnectionTracer) logging.Tracer { return &tracer{createNewConnTracer: c} } func (t *tracer) TracerForConnection(context.Context, logging.Perspective, logging.ConnectionID) logging.ConnectionTracer { return t.createNewConnTracer() } func (t *tracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {} func (t *tracer) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) { } type connTracer struct{} var _ logging.ConnectionTracer = &connTracer{} func (t *connTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { } func (t *connTracer) NegotiatedVersion(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) { } func (t *connTracer) ClosedConnection(error) {} func (t *connTracer) SentTransportParameters(*logging.TransportParameters) {} func (t *connTracer) ReceivedTransportParameters(*logging.TransportParameters) {} func (t *connTracer) RestoredTransportParameters(*logging.TransportParameters) {} func (t *connTracer) SentPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { } func (t *connTracer) ReceivedVersionNegotiationPacket(*logging.Header, []logging.VersionNumber) {} func (t *connTracer) ReceivedRetry(*logging.Header) {} func (t *connTracer) ReceivedPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, frames []logging.Frame) { } func (t *connTracer) BufferedPacket(logging.PacketType) {} func (t *connTracer) DroppedPacket(logging.PacketType, logging.ByteCount, logging.PacketDropReason) {} func (t *connTracer) UpdatedMetrics(rttStats *logging.RTTStats, cwnd, bytesInFlight logging.ByteCount, packetsInFlight int) { } func (t *connTracer) AcknowledgedPacket(logging.EncryptionLevel, logging.PacketNumber) {} func (t *connTracer) LostPacket(logging.EncryptionLevel, logging.PacketNumber, logging.PacketLossReason) { } func (t *connTracer) UpdatedCongestionState(logging.CongestionState) {} func (t *connTracer) UpdatedPTOCount(value uint32) {} func (t *connTracer) UpdatedKeyFromTLS(logging.EncryptionLevel, logging.Perspective) {} func (t *connTracer) UpdatedKey(generation logging.KeyPhase, remote bool) {} func (t *connTracer) DroppedEncryptionLevel(logging.EncryptionLevel) {} func (t *connTracer) DroppedKey(logging.KeyPhase) {} func (t *connTracer) SetLossTimer(logging.TimerType, logging.EncryptionLevel, time.Time) {} func (t *connTracer) LossTimerExpired(logging.TimerType, logging.EncryptionLevel) {} func (t *connTracer) LossTimerCanceled() {} func (t *connTracer) Debug(string, string) {} func (t *connTracer) Close() {} type packet struct { time time.Time hdr *logging.ExtendedHeader frames []logging.Frame } type packetTracer struct { connTracer closed chan struct{} sent, rcvd []packet } func newPacketTracer() *packetTracer { return &packetTracer{closed: make(chan struct{})} } func (t *packetTracer) ReceivedPacket(hdr *logging.ExtendedHeader, _ logging.ByteCount, frames []logging.Frame) { t.rcvd = append(t.rcvd, packet{time: time.Now(), hdr: hdr, frames: frames}) } func (t *packetTracer) SentPacket(hdr *logging.ExtendedHeader, _ logging.ByteCount, ack *wire.AckFrame, frames []logging.Frame) { if ack != nil { frames = append(frames, ack) } t.sent = append(t.sent, packet{time: time.Now(), hdr: hdr, frames: frames}) } func (t *packetTracer) Close() { close(t.closed) } func (t *packetTracer) getSentPackets() []packet { <-t.closed return t.sent } func (t *packetTracer) getRcvdPackets() []packet { <-t.closed return t.rcvd } func TestSelf(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Self integration tests") } quic-go-0.25.0/integrationtests/self/stateless_reset_test.go000066400000000000000000000060241417145451600243120ustar00rootroot00000000000000package self_test import ( "context" "errors" "fmt" "math/rand" "net" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "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() { statelessResetKey := make([]byte, 32) rand.Read(statelessResetKey) serverConfig := getQuicConfig(&quic.Config{StatelessResetKey: statelessResetKey}) ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), serverConfig) Expect(err).ToNot(HaveOccurred()) serverPort := ln.Addr().(*net.UDPAddr).Port closeServer := make(chan struct{}) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) <-closeServer ln.Close() }() drop := utils.AtomicBool{} proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DropPacket: func(quicproxy.Direction, []byte) bool { return drop.Get() }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ ConnectionIDLength: connIDLen, MaxIdleTimeout: 2 * time.Second, }), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.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.Set(true) close(closeServer) time.Sleep(100 * time.Millisecond) ln2, err := quic.ListenAddr( fmt.Sprintf("localhost:%d", serverPort), getTLSConfig(), serverConfig, ) Expect(err).ToNot(HaveOccurred()) drop.Set(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()) statelessResetErr := &quic.StatelessResetError{} Expect(errors.As(serr, &statelessResetErr)).To(BeTrue()) Expect(ln2.Close()).To(Succeed()) Eventually(acceptStopped).Should(BeClosed()) }) } }) quic-go-0.25.0/integrationtests/self/stream_test.go000066400000000000000000000077561417145451600224110ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "sync" quic "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Bidirectional streams", func() { const numStreams = 300 var ( server quic.Listener serverAddr string qconf *quic.Config ) for _, v := range []protocol.VersionNumber{protocol.VersionTLS} { version := v Context(fmt.Sprintf("with QUIC %s", version), func() { BeforeEach(func() { var err error qconf = &quic.Config{ Versions: []protocol.VersionNumber{version}, MaxIncomingStreams: 0, } server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(qconf)) Expect(err).ToNot(HaveOccurred()) serverAddr = fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port) }) AfterEach(func() { server.Close() }) runSendingPeer := func(sess quic.Session) { var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { str, err := sess.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(sess quic.Session) { var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { str, err := sess.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 sess quic.Session go func() { defer GinkgoRecover() var err error sess, err = server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(sess) }() client, err := quic.DialAddr( serverAddr, getTLSClientConfig(), getQuicConfig(qconf), ) Expect(err).ToNot(HaveOccurred()) runSendingPeer(client) }) It(fmt.Sprintf("server opening %d streams to a client", numStreams), func() { go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runSendingPeer(sess) sess.CloseWithError(0, "") }() client, err := quic.DialAddr( serverAddr, getTLSClientConfig(), getQuicConfig(qconf), ) 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() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() runReceivingPeer(sess) close(done) }() runSendingPeer(sess) <-done close(done1) }() client, err := quic.DialAddr( serverAddr, getTLSClientConfig(), getQuicConfig(qconf), ) Expect(err).ToNot(HaveOccurred()) done2 := make(chan struct{}) go func() { defer GinkgoRecover() runSendingPeer(client) close(done2) }() runReceivingPeer(client) <-done1 <-done2 }) }) } }) quic-go-0.25.0/integrationtests/self/timeout_test.go000066400000000000000000000362121417145451600225710ustar00rootroot00000000000000package self_test import ( "bytes" "context" "fmt" "io" mrand "math/rand" "net" "runtime/pprof" "strings" "sync/atomic" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type faultyConn struct { net.PacketConn MaxPackets int32 counter int32 } func (c *faultyConn) ReadFrom(p []byte) (int, net.Addr, error) { n, addr, err := c.PacketConn.ReadFrom(p) counter := atomic.AddInt32(&c.counter, 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 := atomic.AddInt32(&c.counter, 1) if counter <= c.MaxPackets { return c.PacketConn.WriteTo(p, addr) } return 0, io.ErrClosedPipe } func areHandshakesRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "RunHandshake") } 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( "localhost:12345", getTLSClientConfig(), getQuicConfig(&quic.Config{HandshakeIdleTimeout: 10 * 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.DialAddrContext( 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.DialAddrEarlyContext( 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 = 100 * 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() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) }() drop := utils.AtomicBool{} 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.Get() }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true, MaxIdleTimeout: idleTimeout}), ) Expect(err).ToNot(HaveOccurred()) strIn, err := sess.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) strOut, err := sess.OpenStream() Expect(err).ToNot(HaveOccurred()) _, err = strIn.Read(make([]byte, 6)) Expect(err).ToNot(HaveOccurred()) drop.Set(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 = sess.OpenStream() checkTimeoutError(err) _, err = sess.OpenUniStream() checkTimeoutError(err) _, err = sess.AcceptStream(context.Background()) checkTimeoutError(err) _, err = sess.AcceptUniStream(context.Background()) checkTimeoutError(err) }) Context("timing out at the right time", func() { var idleTimeout time.Duration BeforeEach(func() { idleTimeout = scaleDuration(100 * 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() serverSessionClosed := make(chan struct{}) go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) sess.AcceptStream(context.Background()) // blocks until the session is closed close(serverSessionClosed) }() tr := newPacketTracer() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: idleTimeout, Tracer: newTracer(func() logging.ConnectionTracer { return tr }), DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := sess.AcceptStream(context.Background()) checkTimeoutError(err) close(done) }() Eventually(done, 2*idleTimeout).Should(BeClosed()) var lastAckElicitingPacketSentAt time.Time for _, p := range tr.getSentPackets() { var hasAckElicitingFrame bool for _, f := range p.frames { if _, ok := f.(*logging.AckFrame); ok { continue } hasAckElicitingFrame = true break } if hasAckElicitingFrame { lastAckElicitingPacketSentAt = p.time } } rcvdPackets := tr.getRcvdPackets() 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(time.Since(utils.MaxTime(lastAckElicitingPacketSentAt, lastPacketRcvdAt))).To(And( BeNumerically(">=", idleTimeout), BeNumerically("<", idleTimeout*6/5), )) Consistently(serverSessionClosed).ShouldNot(BeClosed()) // make the go routine return Expect(server.Close()).To(Succeed()) Eventually(serverSessionClosed).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() drop := utils.AtomicBool{} 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.Get() } return false }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() serverSessionClosed := make(chan struct{}) go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) <-sess.Context().Done() // block until the session is closed close(serverSessionClosed) }() sess, err := quic.DialAddr( 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.Set(true) str, err := sess.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 := sess.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(serverSessionClosed).ShouldNot(BeClosed()) // make the go routine return Expect(server.Close()).To(Succeed()) Eventually(serverSessionClosed).Should(BeClosed()) }) }) It("does not time out if keepalive is set", func() { const idleTimeout = 100 * time.Millisecond server, err := quic.ListenAddr( "localhost:0", getTLSConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) Expect(err).ToNot(HaveOccurred()) defer server.Close() serverSessionClosed := make(chan struct{}) go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) sess.AcceptStream(context.Background()) // blocks until the session is closed close(serverSessionClosed) }() drop := utils.AtomicBool{} 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.Get() }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), getTLSClientConfig(), getQuicConfig(&quic.Config{ MaxIdleTimeout: idleTimeout, KeepAlive: true, DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) // wait longer than the idle timeout time.Sleep(3 * idleTimeout) str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Consistently(serverSessionClosed).ShouldNot(BeClosed()) // idle timeout will still kick in if pings are dropped drop.Set(true) time.Sleep(2 * idleTimeout) _, err = str.Write([]byte("foobar")) checkTimeoutError(err) Expect(server.Close()).To(Succeed()) Eventually(serverSessionClosed).Should(BeClosed()) }) Context("faulty packet conns", func() { const handshakeTimeout = time.Second / 2 BeforeEach(func() { Expect(areHandshakesRunning()).To(BeFalse()) }) AfterEach(func() { Expect(areHandshakesRunning()).To(BeFalse()) }) runServer := func(ln quic.Listener) error { sess, err := ln.Accept(context.Background()) if err != nil { return err } str, err := sess.OpenUniStream() if err != nil { return err } defer str.Close() _, err = str.Write(PRData) return err } runClient := func(sess quic.Session) error { str, err := sess.AcceptUniStream(context.Background()) if err != nil { return err } data, err := io.ReadAll(str) if err != nil { return err } Expect(data).To(Equal(PRData)) return sess.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() sess, err := quic.DialAddr( 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(sess) }() 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())) 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, KeepAlive: true, DisablePathMTUDiscovery: true, }), ) Expect(err).ToNot(HaveOccurred()) 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() sess, err := quic.Dial( &faultyConn{PacketConn: conn, MaxPackets: maxPackets}, ln.Addr(), "localhost", getTLSClientConfig(), getQuicConfig(&quic.Config{DisablePathMTUDiscovery: true}), ) if err != nil { clientErrChan <- err return } clientErrChan <- runClient(sess) }() 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()) } }) }) }) quic-go-0.25.0/integrationtests/self/tracer_test.go000066400000000000000000000134601417145451600223630ustar00rootroot00000000000000package self_test import ( "bufio" "bytes" "context" "fmt" "io" "io/ioutil" mrand "math/rand" "net" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" "github.com/lucas-clemente/quic-go/qlog" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type customTracer struct{} var _ logging.Tracer = &customTracer{} func (t *customTracer) TracerForConnection(context.Context, logging.Perspective, logging.ConnectionID) logging.ConnectionTracer { return &customConnTracer{} } func (t *customTracer) SentPacket(net.Addr, *logging.Header, logging.ByteCount, []logging.Frame) {} func (t *customTracer) DroppedPacket(net.Addr, logging.PacketType, logging.ByteCount, logging.PacketDropReason) { } type customConnTracer struct{} var _ logging.ConnectionTracer = &customConnTracer{} func (t *customConnTracer) StartedConnection(local, remote net.Addr, srcConnID, destConnID logging.ConnectionID) { } func (t *customConnTracer) NegotiatedVersion(chosen logging.VersionNumber, clientVersions, serverVersions []logging.VersionNumber) { } func (t *customConnTracer) ClosedConnection(error) {} func (t *customConnTracer) SentTransportParameters(*logging.TransportParameters) {} func (t *customConnTracer) ReceivedTransportParameters(*logging.TransportParameters) {} func (t *customConnTracer) RestoredTransportParameters(*logging.TransportParameters) {} func (t *customConnTracer) SentPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, ack *logging.AckFrame, frames []logging.Frame) { } func (t *customConnTracer) ReceivedVersionNegotiationPacket(*logging.Header, []logging.VersionNumber) { } func (t *customConnTracer) ReceivedRetry(*logging.Header) {} func (t *customConnTracer) ReceivedPacket(hdr *logging.ExtendedHeader, size logging.ByteCount, frames []logging.Frame) { } func (t *customConnTracer) BufferedPacket(logging.PacketType) {} func (t *customConnTracer) DroppedPacket(logging.PacketType, logging.ByteCount, logging.PacketDropReason) { } func (t *customConnTracer) UpdatedMetrics(rttStats *logging.RTTStats, cwnd, bytesInFlight logging.ByteCount, packetsInFlight int) { } func (t *customConnTracer) AcknowledgedPacket(logging.EncryptionLevel, logging.PacketNumber) {} func (t *customConnTracer) LostPacket(logging.EncryptionLevel, logging.PacketNumber, logging.PacketLossReason) { } func (t *customConnTracer) UpdatedCongestionState(logging.CongestionState) {} func (t *customConnTracer) UpdatedPTOCount(value uint32) {} func (t *customConnTracer) UpdatedKeyFromTLS(logging.EncryptionLevel, logging.Perspective) {} func (t *customConnTracer) UpdatedKey(generation logging.KeyPhase, remote bool) {} func (t *customConnTracer) DroppedEncryptionLevel(logging.EncryptionLevel) {} func (t *customConnTracer) DroppedKey(logging.KeyPhase) {} func (t *customConnTracer) SetLossTimer(logging.TimerType, logging.EncryptionLevel, time.Time) {} func (t *customConnTracer) LossTimerExpired(logging.TimerType, logging.EncryptionLevel) {} func (t *customConnTracer) LossTimerCanceled() {} func (t *customConnTracer) Debug(string, string) {} func (t *customConnTracer) Close() {} var _ = Describe("Handshake tests", func() { addTracers := func(pers protocol.Perspective, conf *quic.Config) *quic.Config { enableQlog := mrand.Int()%3 != 0 enableCustomTracer := mrand.Int()%3 != 0 fmt.Fprintf(GinkgoWriter, "%s using qlog: %t, custom: %t\n", pers, enableQlog, enableCustomTracer) var tracers []logging.Tracer if enableQlog { tracers = append(tracers, qlog.NewTracer(func(p logging.Perspective, connectionID []byte) io.WriteCloser { 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 %x\n", p, connectionID) return nil } fmt.Fprintf(GinkgoWriter, "%s qlog tracing connection %x\n", p, connectionID) return utils.NewBufferedWriteCloser(bufio.NewWriter(&bytes.Buffer{}), ioutil.NopCloser(nil)) })) } if enableCustomTracer { tracers = append(tracers, &customTracer{}) } c := conf.Clone() c.Tracer = logging.NewMultiplexedTracer(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) go func() { defer GinkgoRecover() ln, err := quic.ListenAddr("localhost:0", getTLSConfig(), quicServerConf) Expect(err).ToNot(HaveOccurred()) serverChan <- ln sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() ln := <-serverChan defer ln.Close() sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", ln.Addr().(*net.UDPAddr).Port), getTLSClientConfig(), quicClientConf, ) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(PRData)) }) } }) quic-go-0.25.0/integrationtests/self/uni_stream_test.go000066400000000000000000000063211417145451600232470ustar00rootroot00000000000000package self_test import ( "context" "fmt" "io" "net" "sync" quic "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Unidirectional Streams", func() { const numStreams = 500 var ( server quic.Listener serverAddr string qconf *quic.Config ) BeforeEach(func() { var err error qconf = &quic.Config{Versions: []protocol.VersionNumber{protocol.VersionTLS}} server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(qconf)) 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(sess quic.Session) { for i := 0; i < numStreams; i++ { str, err := sess.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(sess quic.Session) { var wg sync.WaitGroup wg.Add(numStreams) for i := 0; i < numStreams; i++ { str, err := sess.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() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(sess) sess.CloseWithError(0, "") }() client, err := quic.DialAddr( serverAddr, getTLSClientConfig(), getQuicConfig(qconf), ) Expect(err).ToNot(HaveOccurred()) runSendingPeer(client) <-client.Context().Done() }) It(fmt.Sprintf("server opening %d streams to a client", numStreams), func() { go func() { defer GinkgoRecover() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) runSendingPeer(sess) }() client, err := quic.DialAddr( serverAddr, getTLSClientConfig(), getQuicConfig(qconf), ) Expect(err).ToNot(HaveOccurred()) runReceivingPeer(client) }) 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() sess, err := server.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() runReceivingPeer(sess) close(done) }() runSendingPeer(sess) <-done close(done1) }() client, err := quic.DialAddr( serverAddr, getTLSClientConfig(), getQuicConfig(qconf), ) Expect(err).ToNot(HaveOccurred()) done2 := make(chan struct{}) go func() { defer GinkgoRecover() runSendingPeer(client) close(done2) }() runReceivingPeer(client) <-done1 <-done2 }) }) quic-go-0.25.0/integrationtests/self/zero_rtt_test.go000066400000000000000000000644051417145451600227600ustar00rootroot00000000000000package self_test import ( "context" "crypto/tls" "fmt" "io" mrand "math/rand" "net" "sort" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go" quicproxy "github.com/lucas-clemente/quic-go/integrationtests/tools/proxy" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" ) var _ = Describe("0-RTT", func() { rtt := scaleDuration(5 * time.Millisecond) for _, v := range protocol.SupportedVersions { version := v Context(fmt.Sprintf("with QUIC version %s", version), func() { runCountingProxy := func(serverPort int) (*quicproxy.QuicProxy, *uint32) { var num0RTTPackets uint32 // to be used as an atomic proxy, err := quicproxy.NewQuicProxy("localhost:0", &quicproxy.Opts{ RemoteAddr: fmt.Sprintf("localhost:%d", serverPort), DelayPacket: func(_ quicproxy.Direction, data []byte) time.Duration { for len(data) > 0 { hdr, _, rest, err := wire.ParsePacket(data, 0) Expect(err).ToNot(HaveOccurred()) if hdr.Type == protocol.PacketType0RTT { atomic.AddUint32(&num0RTTPackets, 1) break } data = rest } return rtt / 2 }, }) Expect(err).ToNot(HaveOccurred()) return proxy, &num0RTTPackets } dialAndReceiveSessionTicket := func(serverConf *quic.Config) (*tls.Config, *tls.Config) { tlsConf := getTLSConfig() if serverConf == nil { serverConf = getQuicConfig(&quic.Config{ AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, }) serverConf.Versions = []protocol.VersionNumber{version} } ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, 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 session in order to receive a session ticket done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) <-sess.Context().Done() }() clientConf := getTLSClientConfig() gets := make(chan string, 100) puts := make(chan string, 100) clientConf.ClientSessionCache = newClientSessionCache(gets, puts) sess, err := quic.DialAddr( fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) Eventually(puts).Should(Receive()) // received the session ticket. We're done here. Expect(sess.CloseWithError(0, "")).To(Succeed()) Eventually(done).Should(BeClosed()) return tlsConf, clientConf } transfer0RTTData := func( ln quic.EarlyListener, proxyPort int, clientTLSConf *tls.Config, clientConf *quic.Config, testdata []byte, // data to transfer ) { // now dial the second session, and use 0-RTT to send some data done := make(chan struct{}) go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(testdata)) Expect(sess.ConnectionState().TLS.Used0RTT).To(BeTrue()) Expect(sess.CloseWithError(0, "")).To(Succeed()) close(done) }() if clientConf == nil { clientConf = getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}) } sess, err := quic.DialAddrEarly( fmt.Sprintf("localhost:%d", proxyPort), clientTLSConf, clientConf, ) Expect(err).ToNot(HaveOccurred()) defer sess.CloseWithError(0, "") str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(testdata) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) Expect(sess.ConnectionState().TLS.Used0RTT).To(BeTrue()) Eventually(done).Should(BeClosed()) Eventually(sess.Context().Done()).Should(BeClosed()) } check0RTTRejected := func( ln quic.EarlyListener, proxyPort int, clientConf *tls.Config, ) { sess, err := quic.DialAddrEarly( fmt.Sprintf("localhost:%d", proxyPort), clientConf, getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(make([]byte, 3000)) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) Expect(sess.ConnectionState().TLS.Used0RTT).To(BeFalse()) // make sure the server doesn't process the data ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(50*time.Millisecond)) defer cancel() serverSess, err := ln.Accept(ctx) Expect(err).ToNot(HaveOccurred()) Expect(serverSess.ConnectionState().TLS.Used0RTT).To(BeFalse()) _, err = serverSess.AcceptUniStream(ctx) Expect(err).To(Equal(context.DeadlineExceeded)) Expect(serverSess.CloseWithError(0, "")).To(Succeed()) Eventually(sess.Context().Done()).Should(BeClosed()) } // can be used to extract 0-RTT from a packetTracer 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, clientTLSConf := dialAndReceiveSessionTicket(nil) tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Tracer: newTracer(func() logging.ConnectionTracer { return tracer }), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() transfer0RTTData( ln, proxy.LocalPort(), clientTLSConf, &quic.Config{ ConnectionIDLength: connIDLen, Versions: []protocol.VersionNumber{version}, }, PRData, ) var numNewConnIDs int for _, p := range tracer.getRcvdPackets() { 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 := atomic.LoadUint32(num0RTTPackets) fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) zeroRTTPackets := get0RTTPackets(tracer.getRcvdPackets()) Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10)) sort.Slice(zeroRTTPackets, func(i, j int) bool { return zeroRTTPackets[i] < zeroRTTPackets[j] }) Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0))) }) } // Test that data intended to be sent with 1-RTT protection is not sent in 0-RTT packets. It("waits until a session until the handshake is done", func() { tlsConf, clientConf := dialAndReceiveSessionTicket(nil) zeroRTTData := GeneratePRData(2 * 1100) // 2 packets oneRTTData := PRData tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Tracer: newTracer(func() logging.ConnectionTracer { return tracer }), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() // now dial the second session, and use 0-RTT to send some data go func() { defer GinkgoRecover() sess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) str, err := sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(zeroRTTData)) str, err = sess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err = io.ReadAll(str) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal(oneRTTData)) Expect(sess.CloseWithError(0, "")).To(Succeed()) }() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() sess, err := quic.DialAddrEarly( fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) sent0RTT := make(chan struct{}) go func() { defer GinkgoRecover() defer close(sent0RTT) str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(zeroRTTData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) }() Eventually(sent0RTT).Should(BeClosed()) // wait for the handshake to complete Eventually(sess.HandshakeComplete().Done()).Should(BeClosed()) str, err := sess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = str.Write(PRData) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) <-sess.Context().Done() num0RTT := atomic.LoadUint32(num0RTTPackets) fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).To(Or(BeEquivalentTo(2), BeEquivalentTo(3))) // the FIN might be sent in a separate packet Expect(get0RTTPackets(tracer.getRcvdPackets())).To(HaveLen(int(num0RTT))) }) It("transfers 0-RTT data, when 0-RTT packets are lost", func() { var ( num0RTTPackets uint32 // to be used as an atomic num0RTTDropped uint32 ) tlsConf, clientConf := dialAndReceiveSessionTicket(nil) tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, Tracer: newTracer(func() logging.ConnectionTracer { return 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 { hdr, _, _, err := wire.ParsePacket(data, 0) Expect(err).ToNot(HaveOccurred()) if hdr.Type == protocol.PacketType0RTT { atomic.AddUint32(&num0RTTPackets, 1) } return rtt / 2 }, DropPacket: func(_ quicproxy.Direction, data []byte) bool { hdr, _, _, err := wire.ParsePacket(data, 0) Expect(err).ToNot(HaveOccurred()) if hdr.Type == protocol.PacketType0RTT { // drop 25% of the 0-RTT packets drop := mrand.Intn(4) == 0 if drop { atomic.AddUint32(&num0RTTDropped, 1) } return drop } return false }, }) Expect(err).ToNot(HaveOccurred()) defer proxy.Close() transfer0RTTData(ln, proxy.LocalPort(), clientConf, nil, PRData) num0RTT := atomic.LoadUint32(&num0RTTPackets) numDropped := atomic.LoadUint32(&num0RTTDropped) fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets. Dropped %d of those.", num0RTT, numDropped) Expect(numDropped).ToNot(BeZero()) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(tracer.getRcvdPackets())).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, clientConf := dialAndReceiveSessionTicket(nil) countZeroRTTBytes := func(data []byte) (n protocol.ByteCount) { for len(data) > 0 { hdr, _, rest, err := wire.ParsePacket(data, 0) if err != nil { return } data = rest if hdr.Type == protocol.PacketType0RTT { n += hdr.Length - 16 /* AEAD tag */ } } return } tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, Tracer: newTracer(func() logging.ConnectionTracer { return 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.Equal(connID) { Expect(secondConnID).To(BeNil()) firstCounter += zeroRTTBytes } else if secondConnID == nil { secondConnID = connID secondCounter += zeroRTTBytes } else if secondConnID != nil && secondConnID.Equal(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(), 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(tracer.getRcvdPackets()) Expect(len(zeroRTTPackets)).To(BeNumerically(">=", 5)) Expect(zeroRTTPackets[0]).To(BeNumerically(">=", protocol.PacketNumber(5))) }) It("doesn't reject 0-RTT when the server's transport stream limit increased", func() { const maxStreams = 1 tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{ MaxIncomingUniStreams: maxStreams, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, })) tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, MaxIncomingUniStreams: maxStreams + 1, Tracer: newTracer(func() logging.ConnectionTracer { return tracer }), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, _ := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() sess, err := quic.DialAddrEarly( fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.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 = sess.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 = sess.OpenUniStreamSync(ctx) Expect(err).ToNot(HaveOccurred()) Expect(sess.ConnectionState().TLS.Used0RTT).To(BeTrue()) Expect(sess.CloseWithError(0, "")).To(Succeed()) }) It("rejects 0-RTT when the server's stream limit decreased", func() { const maxStreams = 42 tlsConf, clientConf := dialAndReceiveSessionTicket(getQuicConfig(&quic.Config{ MaxIncomingStreams: maxStreams, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, })) tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, MaxIncomingStreams: maxStreams - 1, Tracer: newTracer(func() logging.ConnectionTracer { return 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 := atomic.LoadUint32(num0RTTPackets) fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(tracer.getRcvdPackets())).To(BeEmpty()) }) It("rejects 0-RTT when the ALPN changed", func() { tlsConf, clientConf := dialAndReceiveSessionTicket(nil) // now close the listener and dial new connection with a different ALPN clientConf.NextProtos = []string{"new-alpn"} tlsConf.NextProtos = []string{"new-alpn"} tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Tracer: newTracer(func() logging.ConnectionTracer { return 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 := atomic.LoadUint32(num0RTTPackets) fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(tracer.getRcvdPackets())).To(BeEmpty()) }) DescribeTable("flow control limits", func(addFlowControlLimit func(*quic.Config, uint64)) { tracer := newPacketTracer() firstConf := getQuicConfig(&quic.Config{ AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Versions: []protocol.VersionNumber{version}, }) addFlowControlLimit(firstConf, 3) tlsConf, clientConf := dialAndReceiveSessionTicket(firstConf) secondConf := getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Tracer: newTracer(func() logging.ConnectionTracer { return 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() sess, err := quic.DialAddrEarly( fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) str, err := sess.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()) serverSess, err := ln.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) rstr, err := serverSess.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) data, err := io.ReadAll(rstr) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("foobar"))) Expect(serverSess.ConnectionState().TLS.Used0RTT).To(BeTrue()) Expect(serverSess.CloseWithError(0, "")).To(Succeed()) Eventually(sess.Context().Done()).Should(BeClosed()) var processedFirst bool for _, p := range tracer.getRcvdPackets() { 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 { // All other STREAM frames can only be sent after handshake completion. Expect(p.hdr.IsLongHeader).To(BeFalse()) Expect(sf.Offset).ToNot(BeZero()) } } } } }, 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 }), ) for _, l := range []int{0, 15} { connIDLen := l It(fmt.Sprintf("correctly deals with 0-RTT rejections, for %d byte connection IDs", connIDLen), func() { tlsConf, clientConf := dialAndReceiveSessionTicket(nil) // now dial new connection with different transport parameters tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, MaxIncomingUniStreams: 1, Tracer: newTracer(func() logging.ConnectionTracer { return tracer }), }), ) Expect(err).ToNot(HaveOccurred()) defer ln.Close() proxy, num0RTTPackets := runCountingProxy(ln.Addr().(*net.UDPAddr).Port) defer proxy.Close() sess, err := quic.DialAddrEarly( fmt.Sprintf("localhost:%d", proxy.LocalPort()), clientConf, getQuicConfig(&quic.Config{Versions: []protocol.VersionNumber{version}}), ) Expect(err).ToNot(HaveOccurred()) // The client remembers that it was allowed to open 2 uni-directional streams. firstStr, err := sess.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 := sess.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 = sess.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 = sess.OpenUniStream() Expect(err).To(MatchError(quic.Err0RTTRejected)) _, err = sess.AcceptStream(ctx) Expect(err).To(Equal(quic.Err0RTTRejected)) newSess := sess.NextSession() str, err := newSess.OpenUniStream() Expect(err).ToNot(HaveOccurred()) _, err = newSess.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(sess.CloseWithError(0, "")).To(Succeed()) // The client should send 0-RTT packets, but the server doesn't process them. num0RTT := atomic.LoadUint32(num0RTTPackets) fmt.Fprintf(GinkgoWriter, "Sent %d 0-RTT packets.", num0RTT) Expect(num0RTT).ToNot(BeZero()) Expect(get0RTTPackets(tracer.getRcvdPackets())).To(BeEmpty()) }) } It("queues 0-RTT packets, if the Initial is delayed", func() { tlsConf, clientConf := dialAndReceiveSessionTicket(nil) tracer := newPacketTracer() ln, err := quic.ListenAddrEarly( "localhost:0", tlsConf, getQuicConfig(&quic.Config{ Versions: []protocol.VersionNumber{version}, AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Tracer: newTracer(func() logging.ConnectionTracer { return 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 && data[0]&0x80 > 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(), clientConf, nil, PRData) Expect(tracer.getRcvdPackets()[0].hdr.Type).To(Equal(protocol.PacketTypeInitial)) zeroRTTPackets := get0RTTPackets(tracer.getRcvdPackets()) Expect(len(zeroRTTPackets)).To(BeNumerically(">", 10)) Expect(zeroRTTPackets[0]).To(Equal(protocol.PacketNumber(0))) }) }) } }) quic-go-0.25.0/integrationtests/tools/000077500000000000000000000000001417145451600177205ustar00rootroot00000000000000quic-go-0.25.0/integrationtests/tools/israce/000077500000000000000000000000001417145451600211665ustar00rootroot00000000000000quic-go-0.25.0/integrationtests/tools/israce/norace.go000066400000000000000000000001741417145451600227660ustar00rootroot00000000000000//go:build !race // +build !race package israce // Enabled reports if the race detector is enabled. const Enabled = false quic-go-0.25.0/integrationtests/tools/israce/race.go000066400000000000000000000001711417145451600224260ustar00rootroot00000000000000//go:build race // +build race package israce // Enabled reports if the race detector is enabled. const Enabled = true quic-go-0.25.0/integrationtests/tools/proxy/000077500000000000000000000000001417145451600211015ustar00rootroot00000000000000quic-go-0.25.0/integrationtests/tools/proxy/proxy.go000066400000000000000000000206711417145451600226170ustar00rootroot00000000000000package quicproxy import ( "net" "sort" "sync" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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 } 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) { srvudp, err := net.DialUDP("udp", nil, p.serverAddr) if err != nil { return nil, err } return &connection{ ClientAddr: cliAddr, ServerConn: srvudp, 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 } } } } quic-go-0.25.0/integrationtests/tools/proxy/proxy_suite_test.go000066400000000000000000000002731417145451600250630ustar00rootroot00000000000000package quicproxy import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestQuicGo(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "QUIC Proxy") } quic-go-0.25.0/integrationtests/tools/proxy/proxy_test.go000066400000000000000000000354571417145451600236660ustar00rootroot00000000000000package quicproxy import ( "bytes" "fmt" "net" "runtime/pprof" "strconv" "strings" "sync/atomic" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "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 { b := &bytes.Buffer{} hdr := wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Version: protocol.VersionTLS, Length: 4 + protocol.ByteCount(len(payload)), DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0, 0, 0x13, 0x37}, SrcConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0, 0, 0x13, 0x37}, }, PacketNumber: p, PacketNumberLen: protocol.PacketNumberLen4, } Expect(hdr.Write(b, protocol.VersionWhatever)).To(Succeed()) raw := b.Bytes() raw = append(raw, payload...) return raw } readPacketNumber := func(b []byte) protocol.PacketNumber { hdr, data, _, err := wire.ParsePacket(b, 0) ExpectWithOffset(1, err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial)) extHdr, err := hdr.ParseExtended(bytes.NewReader(data), protocol.VersionTLS) 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) 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 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) atomic.StoreInt32(&serverNumPacketsSent, 0) // setup 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 atomic.AddInt32(&serverNumPacketsSent, 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(atomic.LoadInt32(&serverNumPacketsSent)).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 int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), DropPacket: func(d Direction, _ []byte) bool { if d != DirectionIncoming { return false } return atomic.AddInt32(&counter, 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 int32 opts := &Opts{ RemoteAddr: serverConn.LocalAddr().String(), DropPacket: func(d Direction, _ []byte) bool { if d != DirectionOutgoing { return false } return atomic.AddInt32(&counter, 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 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 := atomic.AddInt32(&counter, 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 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 := atomic.AddInt32(&counter, 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 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 := atomic.AddInt32(&counter, 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))) }) }) }) }) quic-go-0.25.0/interface.go000066400000000000000000000365751417145451600154610ustar00rootroot00000000000000package quic import ( "context" "errors" "io" "net" "time" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/logging" ) // The StreamID is the ID of a QUIC stream. type StreamID = protocol.StreamID // A VersionNumber is a QUIC version number. type VersionNumber = protocol.VersionNumber const ( // VersionDraft29 is IETF QUIC draft-29 VersionDraft29 = protocol.VersionDraft29 // Version1 is RFC 9000 Version1 = protocol.Version1 ) // A Token can be used to verify the ownership of the client address. type Token struct { // IsRetryToken encodes how the client received the token. There are two ways: // * In a Retry packet sent when trying to establish a new connection. // * In a NEW_TOKEN frame on a previous connection. IsRetryToken bool RemoteAddr string SentTime time.Time } // 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") // SessionTracingKey can be used to associate a ConnectionTracer with a Session. // It is set on the Session.Context() context, // as well as on the context passed to logging.Tracer.NewConnectionTracer. var SessionTracingKey = sessionTracingCtxKey{} type sessionTracingCtxKey struct{} // Stream is the interface implemented by QUIC streams // In addition to the errors listed on the Session, // 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 implements the StreamError // interface, and Canceled() == true. // If the session 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 implements the StreamError // interface, and Canceled() == true. // If the session 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 or after closing the stream it is a no-op. 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. // Warning: This API should not be considered stable and might change soon. 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 Session is a QUIC connection between two peers. // Calls to the session (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 (this is a net.Error temporary error) // * VersionNegotiationError: returned by the client, when there's no version overlap between the peers type Session interface { // AcceptStream returns the next stream opened by the peer, blocking until one is available. // If the session 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 session 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. // If the error is non-nil, it satisfies the net.Error interface. // When reaching the peer's stream limit, err.Temporary() will be true. // If the session was closed due to a timeout, Timeout() will be true. OpenStream() (Stream, error) // OpenStreamSync opens a new bidirectional QUIC stream. // It blocks until a new stream can be opened. // If the error is non-nil, it satisfies the net.Error interface. // If the session was closed due to a timeout, Timeout() will be true. OpenStreamSync(context.Context) (Stream, error) // OpenUniStream opens a new outgoing unidirectional QUIC stream. // If the error is non-nil, it satisfies the net.Error interface. // When reaching the peer's stream limit, Temporary() will be true. // If the session was closed due to a timeout, Timeout() will be true. OpenUniStream() (SendStream, error) // OpenUniStreamSync opens a new outgoing unidirectional QUIC stream. // It blocks until a new stream can be opened. // If the error is non-nil, it satisfies the net.Error interface. // If the session was closed due to a timeout, Timeout() will be true. 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 // The context is cancelled when the session is closed. // Warning: This API should not be considered stable and might change soon. Context() context.Context // ConnectionState returns basic details about the QUIC connection. // It blocks until the handshake completes. // Warning: This API should not be considered stable and might change soon. ConnectionState() ConnectionState // SendMessage sends a message as a datagram. // See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. SendMessage([]byte) error // ReceiveMessage gets a message received in a datagram. // See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. ReceiveMessage() ([]byte, error) } // An EarlySession is a session 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 EarlySession interface { Session // HandshakeComplete blocks until the handshake completes (or fails). // Data sent before completion of the handshake is encrypted with 1-RTT keys. // Note that the client's identity hasn't been verified yet. HandshakeComplete() context.Context NextSession() Session } // Config contains all configuration data needed for a QUIC server or client. type Config struct { // The QUIC versions that can be negotiated. // If not set, it uses all versions available. // Warning: This API should not be considered stable and will change soon. Versions []VersionNumber // The length of the connection ID in bytes. // It can be 0, or any value between 4 and 18. // If not set, the interpretation depends on where the Config is used: // If used for dialing an address, a 0 byte connection ID will be used. // If used for a server, or dialing on a packet conn, a 4 byte connection ID will be used. // When dialing on a packet conn, the ConnectionIDLength value must be the same for every Dial call. ConnectionIDLength int // HandshakeIdleTimeout is the idle timeout before completion of the handshake. // Specifically, if we don't receive any packet from the peer within this time, the connection attempt is 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 // AcceptToken determines if a Token is accepted. // It is called with token = nil if the client didn't send a token. // If not set, a default verification function is used: // * it verifies that the address matches, and // * if the token is a retry token, that it was issued within the last 5 seconds // * else, that it was issued within the last 24 hours. // This option is only valid for the server. AcceptToken func(clientAddr net.Addr, token *Token) bool // 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. 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. 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. InitialConnectionReceiveWindow uint64 // MaxConnectionReceiveWindow is the connection-level flow control window for receiving data. // If this value is zero, it will default to 15 MB. 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 session or on streams // in this callback. AllowConnectionWindowIncrease func(sess Session, delta uint64) bool // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. // Values above 2^60 are invalid. // If not set, it will default to 100. // If set to a negative value, it doesn't allow any bidirectional streams. MaxIncomingStreams int64 // MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open. // Values above 2^60 are invalid. // If not set, it will default to 100. // If set to a negative value, it doesn't allow any unidirectional streams. MaxIncomingUniStreams int64 // The StatelessResetKey is used to generate stateless reset tokens. // If no key is configured, sending of stateless resets is disabled. StatelessResetKey []byte // KeepAlive defines whether this peer will periodically send a packet to keep the connection alive. KeepAlive bool // DisablePathMTUDiscovery disables Path MTU Discovery (RFC 8899). // Packets will then be at most 1252 (IPv4) / 1232 (IPv6) bytes in size. // Note that Path MTU discovery is always disabled on Windows, see https://github.com/lucas-clemente/quic-go/issues/3273. DisablePathMTUDiscovery bool // DisableVersionNegotiationPackets disables the sending of Version Negotiation packets. // This can be useful if version information is exchanged out-of-band. // It has no effect for a client. DisableVersionNegotiationPackets bool // See https://datatracker.ietf.org/doc/draft-ietf-quic-datagram/. // Datagrams will only be available when both peers enable datagram support. EnableDatagrams bool Tracer logging.Tracer } // ConnectionState records basic details about a QUIC connection type ConnectionState struct { TLS handshake.ConnectionState SupportsDatagrams bool } // A Listener for incoming QUIC connections type Listener interface { // Close the server. All active sessions will be closed. Close() error // Addr returns the local network addr that the server is listening on. Addr() net.Addr // Accept returns new sessions. It should be called in a loop. Accept(context.Context) (Session, error) } // An EarlyListener listens for incoming QUIC connections, // and returns them before the handshake completes. type EarlyListener interface { // Close the server. All active sessions will be closed. Close() error // Addr returns the local network addr that the server is listening on. Addr() net.Addr // Accept returns new early sessions. It should be called in a loop. Accept(context.Context) (EarlySession, error) } quic-go-0.25.0/internal/000077500000000000000000000000001417145451600147665ustar00rootroot00000000000000quic-go-0.25.0/internal/ackhandler/000077500000000000000000000000001417145451600170625ustar00rootroot00000000000000quic-go-0.25.0/internal/ackhandler/ack_eliciting.go000066400000000000000000000010351417145451600221750ustar00rootroot00000000000000package ackhandler import "github.com/lucas-clemente/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 } quic-go-0.25.0/internal/ackhandler/ack_eliciting_test.go000066400000000000000000000015171417145451600232410ustar00rootroot00000000000000package ackhandler import ( "reflect" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "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)) }) } }) quic-go-0.25.0/internal/ackhandler/ackhandler.go000066400000000000000000000013461417145451600215110ustar00rootroot00000000000000package ackhandler import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" ) // NewAckHandler creates a new SentPacketHandler and a new ReceivedPacketHandler func NewAckHandler( initialPacketNumber protocol.PacketNumber, initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, pers protocol.Perspective, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber, ) (SentPacketHandler, ReceivedPacketHandler) { sph := newSentPacketHandler(initialPacketNumber, initialMaxDatagramSize, rttStats, pers, tracer, logger) return sph, newReceivedPacketHandler(sph, rttStats, logger, version) } quic-go-0.25.0/internal/ackhandler/ackhandler_suite_test.go000066400000000000000000000007171417145451600237620ustar00rootroot00000000000000package ackhandler import ( "math/rand" "testing" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestCrypto(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "AckHandler Suite") } var mockCtrl *gomock.Controller var _ = BeforeSuite(func() { rand.Seed(GinkgoRandomSeed()) }) var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) }) var _ = AfterEach(func() { mockCtrl.Finish() }) quic-go-0.25.0/internal/ackhandler/frame.go000066400000000000000000000003561417145451600205070ustar00rootroot00000000000000package ackhandler import "github.com/lucas-clemente/quic-go/internal/wire" type Frame struct { wire.Frame // nil if the frame has already been acknowledged in another packet OnLost func(wire.Frame) OnAcked func(wire.Frame) } quic-go-0.25.0/internal/ackhandler/gen.go000066400000000000000000000002101417145451600201530ustar00rootroot00000000000000package ackhandler //go:generate genny -pkg ackhandler -in ../utils/linkedlist/linkedlist.go -out packet_linkedlist.go gen Item=Packet quic-go-0.25.0/internal/ackhandler/interfaces.go000066400000000000000000000046301417145451600215370ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) // A Packet is a packet type Packet struct { PacketNumber protocol.PacketNumber Frames []Frame LargestAcked protocol.PacketNumber // InvalidPacketNumber if the packet doesn't contain an ACK Length protocol.ByteCount EncryptionLevel protocol.EncryptionLevel SendTime time.Time IsPathMTUProbePacket bool // We don't report the loss of Path MTU probe packets to the congestion controller. includedInBytesInFlight bool declaredLost bool skippedPacket bool } // SentPacketHandler handles ACKs received for outgoing packets type SentPacketHandler interface { // SentPacket may modify the packet SentPacket(packet *Packet) ReceivedAck(ackFrame *wire.AckFrame, encLevel protocol.EncryptionLevel, recvTime time.Time) (bool /* 1-RTT packet acked */, error) ReceivedBytes(protocol.ByteCount) DropPackets(protocol.EncryptionLevel) ResetForRetry() error SetHandshakeConfirmed() // The SendMode determines if and what kind of packets can be sent. SendMode() SendMode // TimeUntilSend is the time when the next packet should be sent. // It is used for pacing packets. TimeUntilSend() time.Time // HasPacingBudget says if the pacer allows sending of a (full size) packet at this moment. HasPacingBudget() bool SetMaxDatagramSize(count protocol.ByteCount) // only to be called once the handshake is complete QueueProbePacket(protocol.EncryptionLevel) bool /* was a packet queued */ 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, shouldInstigateAck bool) error DropPackets(protocol.EncryptionLevel) GetAlarmTimeout() time.Time GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame } quic-go-0.25.0/internal/ackhandler/mock_sent_packet_tracker_test.go000066400000000000000000000042741417145451600255030ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: interfaces.go // Package ackhandler is a generated GoMock package. package ackhandler import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLowestPacketNotConfirmedAcked", reflect.TypeOf((*MockSentPacketTracker)(nil).GetLowestPacketNotConfirmedAcked)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedPacket", reflect.TypeOf((*MockSentPacketTracker)(nil).ReceivedPacket), arg0) } quic-go-0.25.0/internal/ackhandler/mockgen.go000066400000000000000000000002671417145451600210410ustar00rootroot00000000000000package ackhandler //go:generate sh -c "../../mockgen_private.sh ackhandler mock_sent_packet_tracker_test.go github.com/lucas-clemente/quic-go/internal/ackhandler sentPacketTracker" quic-go-0.25.0/internal/ackhandler/packet_linkedlist.go000066400000000000000000000145311417145451600231060ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package ackhandler // Linked list implementation from the Go standard library. // PacketElement is an element of a linked list. type PacketElement 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 *PacketElement // The list to which this element belongs. list *PacketList // The value stored with this element. Value Packet } // Next returns the next list element or nil. func (e *PacketElement) Next() *PacketElement { 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 *PacketElement) Prev() *PacketElement { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } // PacketList is a linked list of Packets. type PacketList struct { root PacketElement // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element } // Init initializes or clears list l. func (l *PacketList) Init() *PacketList { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // NewPacketList returns an initialized list. func NewPacketList() *PacketList { return new(PacketList).Init() } // Len returns the number of elements of list l. // The complexity is O(1). func (l *PacketList) Len() int { return l.len } // Front returns the first element of list l or nil if the list is empty. func (l *PacketList) Front() *PacketElement { 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 *PacketList) Back() *PacketElement { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List value. func (l *PacketList) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *PacketList) insert(e, at *PacketElement) *PacketElement { n := at.next at.next = e e.prev = at e.next = n n.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). func (l *PacketList) insertValue(v Packet, at *PacketElement) *PacketElement { return l.insert(&PacketElement{Value: v}, at) } // remove removes e from its list, decrements l.len, and returns e. func (l *PacketList) remove(e *PacketElement) *PacketElement { 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 l.len-- return 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 *PacketList) Remove(e *PacketElement) Packet { 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 e.Value } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *PacketList) PushFront(v Packet) *PacketElement { 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 *PacketList) PushBack(v Packet) *PacketElement { 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 *PacketList) InsertBefore(v Packet, mark *PacketElement) *PacketElement { 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 *PacketList) InsertAfter(v Packet, mark *PacketElement) *PacketElement { 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 *PacketList) MoveToFront(e *PacketElement) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *PacketList) MoveToBack(e *PacketElement) { if e.list != l || l.root.prev == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *PacketList) MoveBefore(e, mark *PacketElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(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 *PacketList) MoveAfter(e, mark *PacketElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(e), mark) } // PushBackList inserts a copy of an other list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *PacketList) PushBackList(other *PacketList) { 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 an other list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *PacketList) PushFrontList(other *PacketList) { l.lazyInit() for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } quic-go-0.25.0/internal/ackhandler/packet_number_generator.go000066400000000000000000000040751417145451600243040ustar00rootroot00000000000000package ackhandler import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) type packetNumberGenerator interface { Peek() protocol.PacketNumber Pop() 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() protocol.PacketNumber { next := p.next p.next++ return 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 { return p.next } func (p *skippingPacketNumberGenerator) Pop() protocol.PacketNumber { next := p.next p.next++ // generate a new packet number for the next packet if p.next == p.nextToSkip { p.next++ p.generateNewSkip() } return next } func (p *skippingPacketNumberGenerator) generateNewSkip() { // make sure that there are never two consecutive packet numbers that are skipped p.nextToSkip = p.next + 2 + protocol.PacketNumber(p.rng.Int31n(int32(2*p.period))) p.period = utils.MinPacketNumber(2*p.period, p.maxPeriod) } quic-go-0.25.0/internal/ackhandler/packet_number_generator_test.go000066400000000000000000000057231417145451600253440ustar00rootroot00000000000000package ackhandler import ( "fmt" "math" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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)) Expect(png.Pop()).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) Expect(png.Pop()).To(Equal(protocol.PacketNumber(12345))) }) It("allows peeking", func() { png := newSkippingPacketNumberGenerator(initialPN, initialPeriod, maxPeriod).(*skippingPacketNumberGenerator) png.nextToSkip = 1000 Expect(png.Peek()).To(Equal(initialPN)) Expect(png.Peek()).To(Equal(initialPN)) Expect(png.Pop()).To(Equal(initialPN)) Expect(png.Peek()).To(Equal(initialPN + 1)) Expect(png.Peek()).To(Equal(initialPN + 1)) }) It("skips a packet number", func() { png := newSkippingPacketNumberGenerator(initialPN, initialPeriod, maxPeriod) var last protocol.PacketNumber var skipped bool for i := 0; i < 1000; i++ { num := png.Pop() if num > last+1 { skipped = true break } 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) last := initialPN lastSkip := initialPN for len(periods[i]) < len(expectedPeriods) { next := png.Pop() if next > last+1 { skipped := next - 1 Expect(skipped).To(BeNumerically(">", lastSkip+1)) periods[i] = append(periods[i], skipped-lastSkip-1) lastSkip = skipped } last = next } } 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)) } }) }) quic-go-0.25.0/internal/ackhandler/received_packet_handler.go000066400000000000000000000106071417145451600242270ustar00rootroot00000000000000package ackhandler import ( "fmt" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type receivedPacketHandler struct { sentPackets sentPacketTracker initialPackets *receivedPacketTracker handshakePackets *receivedPacketTracker appDataPackets *receivedPacketTracker lowest1RTTPacket protocol.PacketNumber } var _ ReceivedPacketHandler = &receivedPacketHandler{} func newReceivedPacketHandler( sentPackets sentPacketTracker, rttStats *utils.RTTStats, logger utils.Logger, version protocol.VersionNumber, ) ReceivedPacketHandler { return &receivedPacketHandler{ sentPackets: sentPackets, initialPackets: newReceivedPacketTracker(rttStats, logger, version), handshakePackets: newReceivedPacketTracker(rttStats, logger, version), appDataPackets: newReceivedPacketTracker(rttStats, logger, version), lowest1RTTPacket: protocol.InvalidPacketNumber, } } func (h *receivedPacketHandler) ReceivedPacket( pn protocol.PacketNumber, ecn protocol.ECN, encLevel protocol.EncryptionLevel, rcvTime time.Time, shouldInstigateAck bool, ) error { h.sentPackets.ReceivedPacket(encLevel) switch encLevel { case protocol.EncryptionInitial: h.initialPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) case protocol.EncryptionHandshake: h.handshakePackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) 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) } h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) case protocol.Encryption1RTT: if h.lowest1RTTPacket == protocol.InvalidPacketNumber || pn < h.lowest1RTTPacket { h.lowest1RTTPacket = pn } h.appDataPackets.IgnoreBelow(h.sentPackets.GetLowestPacketNotConfirmedAcked()) h.appDataPackets.ReceivedPacket(pn, ecn, rcvTime, shouldInstigateAck) default: panic(fmt.Sprintf("received packet with unknown encryption level: %s", encLevel)) } return nil } 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 { var initialAlarm, handshakeAlarm time.Time if h.initialPackets != nil { initialAlarm = h.initialPackets.GetAlarmTimeout() } if h.handshakePackets != nil { handshakeAlarm = h.handshakePackets.GetAlarmTimeout() } oneRTTAlarm := h.appDataPackets.GetAlarmTimeout() return utils.MinNonZeroTime(utils.MinNonZeroTime(initialAlarm, handshakeAlarm), oneRTTAlarm) } func (h *receivedPacketHandler) GetAckFrame(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame { var ack *wire.AckFrame //nolint:exhaustive // 0-RTT packets can't contain ACK frames. switch encLevel { case protocol.EncryptionInitial: if h.initialPackets != nil { ack = h.initialPackets.GetAckFrame(onlyIfQueued) } case protocol.EncryptionHandshake: if h.handshakePackets != nil { ack = h.handshakePackets.GetAckFrame(onlyIfQueued) } case protocol.Encryption1RTT: // 0-RTT packets can't contain ACK frames return h.appDataPackets.GetAckFrame(onlyIfQueued) default: return nil } // For Initial and Handshake ACKs, the delay time is ignored by the receiver. // Set it to 0 in order to save bytes. if ack != nil { ack.DelayTime = 0 } return ack } 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") } quic-go-0.25.0/internal/ackhandler/received_packet_handler_test.go000066400000000000000000000215401417145451600252640ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Received Packet Handler", func() { var handler ReceivedPacketHandler var sentPackets *MockSentPacketTracker BeforeEach(func() { sentPackets = NewMockSentPacketTracker(mockCtrl) handler = newReceivedPacketHandler( sentPackets, &utils.RTTStats{}, utils.DefaultLogger, protocol.VersionWhatever, ) }) 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()) }) }) quic-go-0.25.0/internal/ackhandler/received_packet_history.go000066400000000000000000000074321417145451600243150ustar00rootroot00000000000000package ackhandler import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 *utils.PacketIntervalList deletedBelow protocol.PacketNumber } func newReceivedPacketHistory() *receivedPacketHistory { return &receivedPacketHistory{ ranges: utils.NewPacketIntervalList(), } } // 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) h.maybeDeleteOldRanges() return isNew } func (h *receivedPacketHistory) addToRanges(p protocol.PacketNumber) bool /* is a new packet (and not a duplicate / delayed packet) */ { if h.ranges.Len() == 0 { h.ranges.PushBack(utils.PacketInterval{Start: p, End: p}) return true } for el := h.ranges.Back(); el != nil; el = el.Prev() { // p already included in an existing range. Nothing to do here if p >= el.Value.Start && p <= el.Value.End { return false } if el.Value.End == p-1 { // extend a range at the end el.Value.End = p return true } if el.Value.Start == p+1 { // extend a range at the beginning el.Value.Start = p prev := el.Prev() if prev != nil && prev.Value.End+1 == el.Value.Start { // merge two ranges prev.Value.End = el.Value.End h.ranges.Remove(el) } return true } // create a new range at the end if p > el.Value.End { h.ranges.InsertAfter(utils.PacketInterval{Start: p, End: p}, el) return true } } // create a new range at the beginning h.ranges.InsertBefore(utils.PacketInterval{Start: p, End: p}, h.ranges.Front()) return true } // Delete old ranges, if we're tracking more than 500 of them. // This is a DoS defense against a peer that sends us too many gaps. func (h *receivedPacketHistory) maybeDeleteOldRanges() { for h.ranges.Len() > protocol.MaxNumAckRanges { h.ranges.Remove(h.ranges.Front()) } } // DeleteBelow deletes all entries below (but not including) p func (h *receivedPacketHistory) DeleteBelow(p protocol.PacketNumber) { if p < h.deletedBelow { return } h.deletedBelow = p nextEl := h.ranges.Front() for el := h.ranges.Front(); nextEl != nil; el = nextEl { nextEl = el.Next() if el.Value.End < p { // delete a whole range h.ranges.Remove(el) } else if p > el.Value.Start && p <= el.Value.End { el.Value.Start = p return } else { // no ranges affected. Nothing to do return } } } // GetAckRanges gets a slice of all AckRanges that can be used in an AckFrame func (h *receivedPacketHistory) GetAckRanges() []wire.AckRange { if h.ranges.Len() == 0 { return nil } ackRanges := make([]wire.AckRange, h.ranges.Len()) i := 0 for el := h.ranges.Back(); el != nil; el = el.Prev() { ackRanges[i] = wire.AckRange{Smallest: el.Value.Start, Largest: el.Value.End} i++ } return ackRanges } func (h *receivedPacketHistory) GetHighestAckRange() wire.AckRange { ackRange := wire.AckRange{} if h.ranges.Len() > 0 { r := h.ranges.Back().Value ackRange.Smallest = r.Start ackRange.Largest = r.End } return ackRange } func (h *receivedPacketHistory) IsPotentiallyDuplicate(p protocol.PacketNumber) bool { if p < h.deletedBelow { return true } for el := h.ranges.Back(); el != nil; el = el.Prev() { if p > el.Value.End { return false } if p <= el.Value.End && p >= el.Value.Start { return true } } return false } quic-go-0.25.0/internal/ackhandler/received_packet_history_test.go000066400000000000000000000332641417145451600253560ustar00rootroot00000000000000package ackhandler import ( "fmt" "math/rand" "sort" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(2)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(2)) Expect(hist.ReceivedPacket(7)).To(BeTrue()) Expect(hist.ranges.Len()).To(Equal(3)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4})) Expect(hist.ranges.Front().Next().Value).To(Equal(utils.PacketInterval{Start: 7, End: 7})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(2)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(2)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 5})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(2)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 4, End: 4})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 6, End: 7})) }) It("closes a range", func() { Expect(hist.ReceivedPacket(6)).To(BeTrue()) Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ranges.Len()).To(Equal(2)) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ranges.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(4)) Expect(hist.ReceivedPacket(5)).To(BeTrue()) Expect(hist.ranges.Len()).To(Equal(3)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 1, End: 1})) Expect(hist.ranges.Front().Next().Value).To(Equal(utils.PacketInterval{Start: 4, End: 6})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{Start: 10, End: 10})) }) }) Context("deleting", func() { It("does nothing when the history is empty", func() { hist.DeleteBelow(5) Expect(hist.ranges.Len()).To(BeZero()) }) 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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(2)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 5, End: 5})) Expect(hist.ranges.Back().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 5, End: 6})) Expect(hist.ReceivedPacket(2)).To(BeFalse()) Expect(hist.ranges.Len()).To(Equal(1)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{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.Len()).To(Equal(protocol.MaxNumAckRanges)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 0, End: 0})) hist.ReceivedPacket(2*protocol.MaxNumAckRanges + 1000) // check that the oldest ACK range was deleted Expect(hist.ranges.Len()).To(Equal(protocol.MaxNumAckRanges)) Expect(hist.ranges.Front().Value).To(Equal(utils.PacketInterval{Start: 2, End: 2})) }) }) Context("ACK range export", func() { It("returns nil if there are no ranges", func() { Expect(hist.GetAckRanges()).To(BeNil()) }) It("gets a single ACK range", func() { Expect(hist.ReceivedPacket(4)).To(BeTrue()) Expect(hist.ReceivedPacket(5)).To(BeTrue()) ackRanges := hist.GetAckRanges() Expect(ackRanges).To(HaveLen(1)) Expect(ackRanges[0]).To(Equal(wire.AckRange{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.GetAckRanges() Expect(ackRanges).To(HaveLen(3)) Expect(ackRanges[0]).To(Equal(wire.AckRange{Smallest: 10, Largest: 11})) Expect(ackRanges[1]).To(Equal(wire.AckRange{Smallest: 4, Largest: 6})) Expect(ackRanges[2]).To(Equal(wire.AckRange{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.GetAckRanges() 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)) }) }) }) quic-go-0.25.0/internal/ackhandler/received_packet_tracker.go000066400000000000000000000130061417145451600242410ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) // number of ack-eliciting packets received before sending an ack. const packetsBeforeAck = 2 type receivedPacketTracker struct { largestObserved protocol.PacketNumber ignoreBelow protocol.PacketNumber largestObservedReceivedTime time.Time ect0, ect1, ecnce uint64 packetHistory *receivedPacketHistory maxAckDelay time.Duration rttStats *utils.RTTStats hasNewAck bool // true as soon as we received an ack-eliciting new packet ackQueued bool // true once we received more than 2 (or later in the connection 10) ack-eliciting packets ackElicitingPacketsReceivedSinceLastAck int ackAlarm time.Time lastAck *wire.AckFrame logger utils.Logger version protocol.VersionNumber } func newReceivedPacketTracker( rttStats *utils.RTTStats, logger utils.Logger, version protocol.VersionNumber, ) *receivedPacketTracker { return &receivedPacketTracker{ packetHistory: newReceivedPacketHistory(), maxAckDelay: protocol.MaxAckDelay, rttStats: rttStats, logger: logger, version: version, } } func (h *receivedPacketTracker) ReceivedPacket(packetNumber protocol.PacketNumber, ecn protocol.ECN, rcvTime time.Time, shouldInstigateAck bool) { if packetNumber < h.ignoreBelow { return } isMissing := h.isMissing(packetNumber) if packetNumber >= h.largestObserved { h.largestObserved = packetNumber h.largestObservedReceivedTime = rcvTime } if isNew := h.packetHistory.ReceivedPacket(packetNumber); isNew && shouldInstigateAck { h.hasNewAck = true } if shouldInstigateAck { h.maybeQueueAck(packetNumber, rcvTime, isMissing) } switch ecn { case protocol.ECNNon: case protocol.ECT0: h.ect0++ case protocol.ECT1: h.ect1++ case protocol.ECNCE: h.ecnce++ } } // IgnoreBelow sets a lower limit for acknowledging packets. // Packets with packet numbers smaller than p will not be acked. func (h *receivedPacketTracker) IgnoreBelow(p protocol.PacketNumber) { if p <= h.ignoreBelow { return } h.ignoreBelow = p h.packetHistory.DeleteBelow(p) if h.logger.Debug() { h.logger.Debugf("\tIgnoring all packets below %d.", p) } } // isMissing says if a packet was reported missing in the last ACK. func (h *receivedPacketTracker) 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 *receivedPacketTracker) hasNewMissingPackets() bool { if h.lastAck == nil { return false } highestRange := h.packetHistory.GetHighestAckRange() return highestRange.Smallest > h.lastAck.LargestAcked()+1 && highestRange.Len() == 1 } // maybeQueueAck queues an ACK, if necessary. func (h *receivedPacketTracker) maybeQueueAck(pn protocol.PacketNumber, rcvTime time.Time, wasMissing bool) { // always acknowledge the first packet if h.lastAck == nil { if !h.ackQueued { h.logger.Debugf("\tQueueing ACK because the first packet should be acknowledged.") } h.ackQueued = true return } if h.ackQueued { return } h.ackElicitingPacketsReceivedSinceLastAck++ // 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) } h.ackQueued = 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) } h.ackQueued = true } else if h.ackAlarm.IsZero() { if h.logger.Debug() { h.logger.Debugf("\tSetting ACK timer to max ack delay: %s", h.maxAckDelay) } h.ackAlarm = rcvTime.Add(h.maxAckDelay) } // 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.") h.ackQueued = true } if h.ackQueued { // cancel the ack alarm h.ackAlarm = time.Time{} } } func (h *receivedPacketTracker) GetAckFrame(onlyIfQueued bool) *wire.AckFrame { if !h.hasNewAck { return nil } now := time.Now() if onlyIfQueued { if !h.ackQueued && (h.ackAlarm.IsZero() || h.ackAlarm.After(now)) { return nil } if h.logger.Debug() && !h.ackQueued && !h.ackAlarm.IsZero() { h.logger.Debugf("Sending ACK because the ACK timer expired.") } } ack := &wire.AckFrame{ AckRanges: h.packetHistory.GetAckRanges(), // Make sure that the DelayTime is always positive. // This is not guaranteed on systems that don't have a monotonic clock. DelayTime: utils.MaxDuration(0, now.Sub(h.largestObservedReceivedTime)), ECT0: h.ect0, ECT1: h.ect1, ECNCE: h.ecnce, } h.lastAck = ack h.ackAlarm = time.Time{} h.ackQueued = false h.hasNewAck = false h.ackElicitingPacketsReceivedSinceLastAck = 0 return ack } func (h *receivedPacketTracker) GetAlarmTimeout() time.Time { return h.ackAlarm } func (h *receivedPacketTracker) IsPotentiallyDuplicate(pn protocol.PacketNumber) bool { return h.packetHistory.IsPotentiallyDuplicate(pn) } quic-go-0.25.0/internal/ackhandler/received_packet_tracker_test.go000066400000000000000000000333441417145451600253070ustar00rootroot00000000000000package ackhandler import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Received Packet Tracker", func() { var ( tracker *receivedPacketTracker rttStats *utils.RTTStats ) BeforeEach(func() { rttStats = &utils.RTTStats{} tracker = newReceivedPacketTracker(rttStats, utils.DefaultLogger, protocol.VersionWhatever) }) Context("accepting packets", func() { It("saves the time when each packet arrived", func() { tracker.ReceivedPacket(protocol.PacketNumber(3), protocol.ECNNon, time.Now(), true) Expect(tracker.largestObservedReceivedTime).To(BeTemporally("~", time.Now(), 10*time.Millisecond)) }) It("updates the largestObserved and the largestObservedReceivedTime", func() { now := time.Now() tracker.largestObserved = 3 tracker.largestObservedReceivedTime = now.Add(-1 * time.Second) tracker.ReceivedPacket(5, protocol.ECNNon, now, true) Expect(tracker.largestObserved).To(Equal(protocol.PacketNumber(5))) Expect(tracker.largestObservedReceivedTime).To(Equal(now)) }) It("doesn't update the largestObserved and the largestObservedReceivedTime for a belated packet", func() { now := time.Now() timestamp := now.Add(-1 * time.Second) tracker.largestObserved = 5 tracker.largestObservedReceivedTime = timestamp tracker.ReceivedPacket(4, protocol.ECNNon, now, true) Expect(tracker.largestObserved).To(Equal(protocol.PacketNumber(5))) Expect(tracker.largestObservedReceivedTime).To(Equal(timestamp)) }) }) Context("ACKs", func() { Context("queueing ACKs", func() { receiveAndAck10Packets := func() { for i := 1; i <= 10; i++ { tracker.ReceivedPacket(protocol.PacketNumber(i), protocol.ECNNon, time.Time{}, true) } Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) Expect(tracker.ackQueued).To(BeFalse()) } It("always queues an ACK for the first packet", func() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(0, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(0, protocol.ECT0, time.Now(), true) pn := protocol.PacketNumber(1) for i := 0; i < 2; i++ { tracker.ReceivedPacket(pn, protocol.ECT1, time.Now(), true) pn++ } for i := 0; i < 3; i++ { tracker.ReceivedPacket(pn, protocol.ECNCE, time.Now(), true) 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++ { tracker.ReceivedPacket(p, protocol.ECNNon, time.Time{}, true) Expect(tracker.ackQueued).To(BeFalse()) p++ tracker.ReceivedPacket(p, protocol.ECNNon, time.Time{}, true) 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() tracker.ReceivedPacket(11, protocol.ECNNon, rcvTime, true) Expect(tracker.GetAckFrame(false)).ToNot(BeNil()) tracker.ReceivedPacket(12, protocol.ECNNon, rcvTime, true) Expect(tracker.GetAckFrame(true)).To(BeNil()) tracker.ReceivedPacket(13, protocol.ECNNon, rcvTime, true) Expect(tracker.GetAckFrame(false)).ToNot(BeNil()) }) It("only sets the timer when receiving a ack-eliciting packets", func() { receiveAndAck10Packets() tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), false) Expect(tracker.ackQueued).To(BeFalse()) Expect(tracker.GetAlarmTimeout()).To(BeZero()) rcvTime := time.Now().Add(10 * time.Millisecond) tracker.ReceivedPacket(12, protocol.ECNNon, rcvTime, true) Expect(tracker.ackQueued).To(BeFalse()) Expect(tracker.GetAlarmTimeout()).To(Equal(rcvTime.Add(protocol.MaxAckDelay))) }) It("queues an ACK if it was reported missing before", func() { receiveAndAck10Packets() tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(13, protocol.ECNNon, time.Now(), true) 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()) tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), true) Expect(tracker.ackQueued).To(BeTrue()) }) It("doesn't queue an ACK if it was reported missing before, but is below the threshold", func() { receiveAndAck10Packets() // 11 is missing tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(13, protocol.ECNNon, time.Now(), true) ack := tracker.GetAckFrame(true) // ACK: 1-10, 12-13 Expect(ack).ToNot(BeNil()) // now receive 11 tracker.IgnoreBelow(12) tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), false) ack = tracker.GetAckFrame(true) Expect(ack).To(BeNil()) }) 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) tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true) 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) tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), true) 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() tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true) Expect(tracker.GetAckFrame(true)).To(BeNil()) tracker.ReceivedPacket(13, protocol.ECNNon, time.Now(), false) // 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()) tracker.ReceivedPacket(12, protocol.ECNNon, time.Now(), false) Expect(tracker.GetAckFrame(true)).To(BeNil()) // 11 is received out-of-order, but this hasn't been reported in an ACK frame yet tracker.ReceivedPacket(11, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) // 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) // The first packet is always acknowledged. Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) tracker.ReceivedPacket(2, protocol.ECNNon, time.Now(), false) Expect(tracker.GetAckFrame(false)).To(BeNil()) tracker.ReceivedPacket(3, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(2, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(0, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(2, protocol.ECNNon, time.Now().Add(-1337*time.Millisecond), true) 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() { tracker.ReceivedPacket(0, protocol.ECNNon, time.Now().Add(time.Hour), true) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.DelayTime).To(BeZero()) }) It("saves the last sent ACK", func() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(tracker.lastAck).To(Equal(ack)) tracker.ReceivedPacket(2, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(4, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(0, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(3, protocol.ECNNon, time.Now(), true) 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("doesn't add delayed packets to the packetHistory", func() { tracker.IgnoreBelow(7) tracker.ReceivedPacket(4, protocol.ECNNon, time.Now(), true) tracker.ReceivedPacket(10, protocol.ECNNon, time.Now(), true) ack := tracker.GetAckFrame(true) Expect(ack).ToNot(BeNil()) Expect(ack.LargestAcked()).To(Equal(protocol.PacketNumber(10))) Expect(ack.LowestAcked()).To(Equal(protocol.PacketNumber(10))) }) It("deletes packets from the packetHistory when a lower limit is set", func() { for i := 1; i <= 12; i++ { tracker.ReceivedPacket(protocol.PacketNumber(i), protocol.ECNNon, time.Now(), true) } 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) 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() { tracker.ReceivedPacket(1, protocol.ECNNon, time.Now(), true) tracker.ackQueued = false tracker.ackAlarm = time.Now().Add(-time.Minute) Expect(tracker.GetAckFrame(true)).ToNot(BeNil()) }) }) }) }) }) quic-go-0.25.0/internal/ackhandler/send_mode.go000066400000000000000000000016571417145451600213570ustar00rootroot00000000000000package 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 // 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" default: return fmt.Sprintf("invalid send mode: %d", s) } } quic-go-0.25.0/internal/ackhandler/send_mode_test.go000066400000000000000000000011011417145451600223760ustar00rootroot00000000000000package ackhandler import ( . "github.com/onsi/ginkgo" . "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(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")) }) }) quic-go-0.25.0/internal/ackhandler/sent_packet_handler.go000066400000000000000000000671671417145451600234270ustar00rootroot00000000000000package ackhandler import ( "errors" "fmt" "time" "github.com/lucas-clemente/quic-go/internal/congestion" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/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 ) type packetNumberSpace struct { history *sentPacketHistory pns packetNumberGenerator lossTime time.Time lastAckElicitingPacketTime time.Time largestAcked protocol.PacketNumber largestSent protocol.PacketNumber } func newPacketNumberSpace(initialPN protocol.PacketNumber, skipPNs bool, rttStats *utils.RTTStats) *packetNumberSpace { var pns packetNumberGenerator if skipPNs { pns = newSkippingPacketNumberGenerator(initialPN, protocol.SkipPacketInitialPeriod, protocol.SkipPacketMaxPeriod) } else { pns = newSequentialPacketNumberGenerator(initialPN) } return &packetNumberSpace{ history: newSentPacketHistory(rttStats), 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 perspective protocol.Perspective tracer logging.ConnectionTracer logger utils.Logger } var ( _ SentPacketHandler = &sentPacketHandler{} _ sentPacketTracker = &sentPacketHandler{} ) func newSentPacketHandler( initialPN protocol.PacketNumber, initialMaxDatagramSize protocol.ByteCount, rttStats *utils.RTTStats, pers protocol.Perspective, tracer logging.ConnectionTracer, logger utils.Logger, ) *sentPacketHandler { congestion := congestion.NewCubicSender( congestion.DefaultClock{}, rttStats, initialMaxDatagramSize, true, // use Reno tracer, ) return &sentPacketHandler{ peerCompletedAddressValidation: pers == protocol.PerspectiveServer, peerAddressValidated: pers == protocol.PerspectiveClient, initialPackets: newPacketNumberSpace(initialPN, false, rttStats), handshakePackets: newPacketNumberSpace(0, false, rttStats), appDataPackets: newPacketNumberSpace(0, true, rttStats), rttStats: rttStats, congestion: congestion, perspective: pers, tracer: tracer, logger: logger, } } func (h *sentPacketHandler) DropPackets(encLevel protocol.EncryptionLevel) { if h.perspective == protocol.PerspectiveClient && encLevel == protocol.EncryptionInitial { // This function is called when the crypto setup seals a Handshake packet. // If this Handshake packet is coalesced behind an Initial packet, we would drop the Initial packet number space // before SentPacket() was called for that Initial packet. return } h.dropPackets(encLevel) } 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) 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 { 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.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(packet *Packet) { h.bytesSent += packet.Length // For the client, drop the Initial packet number space when the first Handshake packet is sent. if h.perspective == protocol.PerspectiveClient && packet.EncryptionLevel == protocol.EncryptionHandshake && h.initialPackets != nil { h.dropPackets(protocol.EncryptionInitial) } isAckEliciting := h.sentPacketImpl(packet) h.getPacketNumberSpace(packet.EncryptionLevel).history.SentPacket(packet, isAckEliciting) if h.tracer != nil && isAckEliciting { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } if isAckEliciting || !h.peerCompletedAddressValidation { 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) sentPacketImpl(packet *Packet) bool /* is ack-eliciting */ { pnSpace := h.getPacketNumberSpace(packet.EncryptionLevel) if h.logger.Debug() && pnSpace.history.HasOutstandingPackets() { for p := utils.MaxPacketNumber(0, pnSpace.largestSent+1); p < packet.PacketNumber; p++ { h.logger.Debugf("Skipping packet number %d", p) } } pnSpace.largestSent = packet.PacketNumber isAckEliciting := len(packet.Frames) > 0 if isAckEliciting { pnSpace.lastAckElicitingPacketTime = packet.SendTime packet.includedInBytesInFlight = true h.bytesInFlight += packet.Length if h.numProbesToSend > 0 { h.numProbesToSend-- } } h.congestion.OnPacketSent(packet.SendTime, h.bytesInFlight, packet.PacketNumber, packet.Length, isAckEliciting) return isAckEliciting } 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", } } pnSpace.largestAcked = utils.MaxPacketNumber(pnSpace.largestAcked, largestAcked) // 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 = utils.MinDuration(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() } } 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) } // 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.ptoCount != 0 { h.tracer.UpdatedPTOCount(0) } h.ptoCount = 0 } h.numProbesToSend = 0 if h.tracer != nil { h.tracer.UpdatedMetrics(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } pnSpace.history.DeleteOldPackets(rcvTime) 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 = utils.MaxPacketNumber(h.lowestNotConfirmedAcked, p.LargestAcked+1) } for _, f := range p.Frames { if f.OnAcked != nil { f.OnAcked(f.Frame) } } if err := pnSpace.history.Remove(p.PacketNumber); err != nil { return nil, err } if h.tracer != 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 } // 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.rttStats.PTO(false) << h.ptoCount) 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.rttStats.PTO(false) << h.ptoCount) } } if h.handshakePackets != nil && !h.handshakePackets.lastAckElicitingPacketTime.IsZero() { t := h.handshakePackets.lastAckElicitingPacketTime.Add(h.rttStats.PTO(false) << h.ptoCount) 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.rttStats.PTO(true) << h.ptoCount) 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.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() } } 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() } } 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() } } return } h.alarm = ptoTime if h.tracer != 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(utils.MaxDuration(h.rttStats.LatestRTT(), h.rttStats.SmoothedRTT())) lossDelay := time.Duration(timeThreshold * maxRTT) // Minimum time of granularity before packets are deemed lost. lossDelay = utils.MaxDuration(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 } if p.declaredLost || p.skippedPacket { return true, nil } var packetLost bool if p.SendTime.Before(lostSendTime) { packetLost = true if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (time threshold)", p.PacketNumber) } if h.tracer != nil { h.tracer.LostPacket(p.EncryptionLevel, p.PacketNumber, logging.PacketLossTimeThreshold) } } else if pnSpace.largestAcked >= p.PacketNumber+packetThreshold { packetLost = true if h.logger.Debug() { h.logger.Debugf("\tlost packet %d (reordering threshold)", p.PacketNumber) } if h.tracer != 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 { p.declaredLost = true // 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.OnPacketLost(p.PacketNumber, p.Length, priorInFlight) } } 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(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 { h.tracer.LossTimerExpired(logging.TimerTypePTO, encLevel) 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 _ = h.PopPacketNumber(protocol.Encryption1RTT) 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) PeekPacketNumber(encLevel protocol.EncryptionLevel) (protocol.PacketNumber, protocol.PacketNumberLen) { pnSpace := h.getPacketNumberSpace(encLevel) var lowestUnacked protocol.PacketNumber if p := pnSpace.history.FirstOutstanding(); p != nil { lowestUnacked = p.PacketNumber } else { lowestUnacked = pnSpace.largestAcked + 1 } pn := pnSpace.pns.Peek() return pn, protocol.GetPacketNumberLengthForHeader(pn, lowestUnacked) } func (h *sentPacketHandler) PopPacketNumber(encLevel protocol.EncryptionLevel) protocol.PacketNumber { return h.getPacketNumberSpace(encLevel).pns.Pop() } func (h *sentPacketHandler) SendMode() 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 } return SendAny } func (h *sentPacketHandler) TimeUntilSend() time.Time { return h.congestion.TimeUntilSend(h.bytesInFlight) } func (h *sentPacketHandler) HasPacingBudget() bool { return h.congestion.HasPacingBudget() } 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) p.declaredLost = true return true } func (h *sentPacketHandler) queueFramesForRetransmission(p *Packet) { if len(p.Frames) == 0 { panic("no frames") } for _, f := range p.Frames { f.OnLost(f.Frame) } p.Frames = nil } func (h *sentPacketHandler) ResetForRetry() 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. now := time.Now() h.rttStats.UpdateRTT(utils.MaxDuration(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(h.rttStats, h.congestion.GetCongestionWindow(), h.bytesInFlight, h.packetsInFlight()) } } h.initialPackets = newPacketNumberSpace(h.initialPackets.pns.Pop(), false, h.rttStats) h.appDataPackets = newPacketNumberSpace(h.appDataPackets.pns.Pop(), true, h.rttStats) oldAlarm := h.alarm h.alarm = time.Time{} if h.tracer != nil { h.tracer.UpdatedPTOCount(0) if !oldAlarm.IsZero() { h.tracer.LossTimerCanceled() } } h.ptoCount = 0 return nil } func (h *sentPacketHandler) SetHandshakeConfirmed() { 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() } quic-go-0.25.0/internal/ackhandler/sent_packet_handler_test.go000066400000000000000000001664331417145451600244620ustar00rootroot00000000000000package ackhandler import ( "fmt" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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.InitialPacketSizeIPv4, rttStats, perspective, nil, utils.DefaultLogger) streamFrame = wire.StreamFrame{ StreamID: 5, Data: []byte{0x13, 0x37}, } }) getPacket := func(pn protocol.PacketNumber, encLevel protocol.EncryptionLevel) *Packet { if el, ok := handler.getPacketNumberSpace(encLevel).history.packetMap[pn]; ok { return &el.Value } 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{}, 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 } 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, pnSpace.history.packetMap).To(HaveKey(p)) } } updateRTT := func(rtt time.Duration) { handler.rttStats.UpdateRTT(rtt, 0, time.Now()) ExpectWithOffset(1, handler.rttStats.SmoothedRTT()).To(Equal(rtt)) } Context("registering sent packets", func() { It("accepts two consecutive packets", func() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, EncryptionLevel: protocol.EncryptionHandshake})) handler.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() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, EncryptionLevel: protocol.Encryption0RTT})) handler.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() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 0, EncryptionLevel: protocol.Encryption1RTT})) Expect(handler.appDataPackets.largestSent).To(BeZero()) handler.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) handler.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) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: sendTime, EncryptionLevel: protocol.EncryptionInitial})) handler.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++ { handler.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() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 100, EncryptionLevel: protocol.Encryption0RTT})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 101, EncryptionLevel: protocol.Encryption0RTT})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 102, EncryptionLevel: protocol.Encryption1RTT})) acked1RTT, err := handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 100, Largest: 101}}}, protocol.Encryption1RTT, time.Now(), ) Expect(err).ToNot(HaveOccurred()) Expect(acked1RTT).To(BeFalse()) acked1RTT, err = handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 101, Largest: 102}}}, 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 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() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 100})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 102})) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 100, Largest: 102}}} _, 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: 101 (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 _, ok := pnSpace.history.packetMap[p]; ok { 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{} handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 13, Frames: []Frame{{ Frame: ping, OnAcked: func(f wire.Frame) { Expect(f).To(Equal(ping)) acked = true }, }}, })) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 13, Largest: 13}}} _, 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() { handler.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: 13, LargestAcked: 100, Frames: []Frame{{Frame: &streamFrame, OnLost: func(wire.Frame) {}}}, Length: 1, EncryptionLevel: protocol.Encryption1RTT, }, { PacketNumber: 14, LargestAcked: 200, Frames: []Frame{{Frame: &streamFrame, OnLost: func(wire.Frame) {}}}, Length: 1, EncryptionLevel: protocol.Encryption1RTT, }, { PacketNumber: 15, Frames: []Frame{{Frame: &streamFrame, OnLost: func(wire.Frame) {}}}, Length: 1, EncryptionLevel: protocol.Encryption1RTT, }, } for _, packet := range morePackets { handler.SentPacket(packet) } }) It("determines which ACK we have received an ACK for", func() { ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 13, Largest: 15}}} _, 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: 13, Largest: 13}}} ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 15, Largest: 15}}} _, 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: 14, Largest: 14}}} ack2 := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 13, Largest: 13}}} _, 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, ) handler.SentPacket(&Packet{ PacketNumber: 1, Length: 42, Frames: []Frame{{Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) {}}}, 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), ) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) handler.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) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) // lose packet 1 gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), cong.EXPECT().OnPacketLost(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 OnPacketLost 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 handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 1, SendTime: time.Now().Add(-time.Hour), IsPathMTUProbePacket: true, Frames: []Frame{{Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) { mtuPacketDeclaredLost = true }}}, })) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) // lose packet 1, but don't EXPECT any calls to OnPacketLost() 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 OnPacketLost with the right bytes_in_flight value", func() { cong.EXPECT().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(4) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2, SendTime: time.Now().Add(-30 * time.Minute)})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 3, SendTime: time.Now().Add(-30 * time.Minute)})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 4, SendTime: time.Now()})) // receive the first ACK gomock.InOrder( cong.EXPECT().MaybeExitSlowStart(), cong.EXPECT().OnPacketLost(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().OnPacketLost(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) handler.SentPacket(&Packet{ Length: 42, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) cong.EXPECT().CanSend(protocol.ByteCount(42)).Return(true) handler.SendMode() }) It("allows sending of ACKs when congestion limited", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) cong.EXPECT().CanSend(gomock.Any()).Return(true) Expect(handler.SendMode()).To(Equal(SendAny)) cong.EXPECT().CanSend(gomock.Any()).Return(false) Expect(handler.SendMode()).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().OnPacketSent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() for i := protocol.PacketNumber(0); i < protocol.MaxOutstandingSentPackets; i++ { Expect(handler.SendMode()).To(Equal(SendAny)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: i})) } Expect(handler.SendMode()).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()).To(Equal(SendPTOHandshake)) }) It("says if it has pacing budget", func() { cong.EXPECT().HasPacingBudget().Return(true) Expect(handler.HasPacingBudget()).To(BeTrue()) cong.EXPECT().HasPacingBudget().Return(false) Expect(handler.HasPacingBudget()).To(BeFalse()) }) 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) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 10})) handler.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()).To(Equal(SendAny)) }) Context("probe packets", func() { It("queues a probe packet", func() { handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 10})) handler.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 handler.SetHandshakeConfirmed() sendTime := time.Now().Add(-time.Hour) handler.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)) }) It("reset the PTO count when receiving an ACK", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) now := time.Now() handler.SetHandshakeConfirmed() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)})) handler.SentPacket(ackElicitingPacket(&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()).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) handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 1, EncryptionLevel: protocol.EncryptionHandshake, SendTime: sendTimeHandshake, })) handler.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()).To(Equal(SendPTOHandshake)) Expect(handler.GetLossDetectionTimeout()).To(Equal(sendTimeHandshake.Add(handler.rttStats.PTO(false) << 1))) handler.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()).ToNot(Equal(SendPTOHandshake)) Expect(handler.ptoCount).To(BeZero()) }) It("allows two 1-RTT PTOs", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SetHandshakeConfirmed() var lostPackets []protocol.PacketNumber handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 1, SendTime: time.Now().Add(-time.Hour), Frames: []Frame{ {Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) { lostPackets = append(lostPackets, 1) }}, }, })) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 3})) Expect(handler.SendMode()).ToNot(Equal(SendPTOAppData)) }) It("skips a packet number for 1-RTT PTOs", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SetHandshakeConfirmed() var lostPackets []protocol.PacketNumber pn := handler.PopPacketNumber(protocol.Encryption1RTT) handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: pn, SendTime: time.Now().Add(-time.Hour), Frames: []Frame{ {Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) { lostPackets = append(lostPackets, 1) }}, }, })) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) // The packet number generator might have introduced another skipped a packet number. Expect(handler.PopPacketNumber(protocol.Encryption1RTT)).To(BeNumerically(">=", pn+2)) }) It("only counts ack-eliciting packets as probe packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SetHandshakeConfirmed() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)})) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) for p := protocol.PacketNumber(3); p < 30; p++ { handler.SentPacket(nonAckElicitingPacket(&Packet{PacketNumber: p})) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) } handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 30})) Expect(handler.SendMode()).ToNot(Equal(SendPTOAppData)) }) It("gets two probe packets if PTO expires", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SetHandshakeConfirmed() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2})) 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()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 3})) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 4})) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) // PTO Expect(handler.ptoCount).To(BeEquivalentTo(2)) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 5})) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 6})) Expect(handler.SendMode()).To(Equal(SendAny)) }) It("gets two probe packets if PTO expires, for Handshake packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SentPacket(initialPacket(&Packet{PacketNumber: 1})) handler.SentPacket(initialPacket(&Packet{PacketNumber: 2})) updateRTT(time.Hour) Expect(handler.initialPackets.lossTime.IsZero()).To(BeTrue()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOInitial)) handler.SentPacket(initialPacket(&Packet{PacketNumber: 3})) Expect(handler.SendMode()).To(Equal(SendPTOInitial)) handler.SentPacket(initialPacket(&Packet{PacketNumber: 4})) Expect(handler.SendMode()).To(Equal(SendAny)) }) It("doesn't send 1-RTT probe packets before the handshake completes", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1})) updateRTT(time.Hour) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) Expect(handler.SendMode()).To(Equal(SendAny)) handler.SetHandshakeConfirmed() Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) }) It("resets the send mode when it receives an acknowledgement after queueing probe packets", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SetHandshakeConfirmed() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: time.Now().Add(-time.Hour)})) updateRTT(time.Second) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOAppData)) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}} _, err := handler.ReceivedAck(ack, protocol.Encryption1RTT, time.Now()) Expect(err).ToNot(HaveOccurred()) Expect(handler.SendMode()).To(Equal(SendAny)) }) It("handles ACKs for the original packet", func() { handler.ReceivedPacket(protocol.EncryptionHandshake) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 5, 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) handler.SetHandshakeConfirmed() updateRTT(time.Second) handler.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() { handler.ReceivedPacket(protocol.EncryptionInitial) // receiving an Initial packet doesn't validate the client's address handler.ReceivedBytes(200) handler.SentPacket(&Packet{ PacketNumber: 1, Length: 599, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) Expect(handler.SendMode()).To(Equal(SendAny)) handler.SentPacket(&Packet{ PacketNumber: 2, Length: 1, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{{Frame: &wire.PingFrame{}}}, SendTime: time.Now(), }) Expect(handler.SendMode()).To(Equal(SendNone)) }) It("cancels the loss detection timer when it is amplification limited, and resets it when becoming unblocked", func() { handler.ReceivedBytes(300) handler.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) handler.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) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2, SendTime: t})) handler.SentPacket(handshakePacket(&Packet{PacketNumber: 3, SendTime: t})) handler.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 client", func() { BeforeEach(func() { perspective = protocol.PerspectiveClient }) It("sends an Initial packet to unblock the server", func() { handler.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()).To(Equal(SendPTOInitial)) // send a single packet to unblock the server handler.SentPacket(initialPacket(&Packet{PacketNumber: 2})) Expect(handler.SendMode()).To(Equal(SendAny)) // Now receive an ACK for a Handshake packet. // This tells the client that the server completed address validation. handler.SentPacket(handshakePacket(&Packet{PacketNumber: 1})) _, 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() { handler.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()) handler.SentPacket(handshakePacketNonAckEliciting(&Packet{PacketNumber: 1})) // also drops Initial packets Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).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()) handler.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() { handler.SentPacket(handshakePacket(&Packet{PacketNumber: 1})) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).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() { handler.SentPacket(initialPacket(&Packet{PacketNumber: 1, SendTime: time.Now().Add(-42 * time.Second)})) _, err := handler.ReceivedAck( &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 1}}}, protocol.EncryptionInitial, time.Now(), ) Expect(err).ToNot(HaveOccurred()) handler.SentPacket(handshakePacketNonAckEliciting(&Packet{PacketNumber: 1, SendTime: time.Now()})) Expect(handler.initialPackets).To(BeNil()) pto := handler.rttStats.PTO(false) Expect(pto).ToNot(BeZero()) 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() handler.SentPacket(initialPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)})) handler.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()).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++ { handler.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() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-time.Hour)})) handler.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() handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second)})) handler.SentPacket(ackElicitingPacket(&Packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second)})) handler.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()).To(Equal(SendAny)) expectInPacketHistory([]protocol.PacketNumber{1, 3}, protocol.Encryption1RTT) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) expectInPacketHistory([]protocol.PacketNumber{3}, protocol.Encryption1RTT) Expect(handler.SendMode()).To(Equal(SendAny)) }) It("sets the early retransmit alarm for crypto packets", func() { handler.ReceivedBytes(1000) now := time.Now() handler.SentPacket(initialPacket(&Packet{PacketNumber: 1, SendTime: now.Add(-2 * time.Second)})) handler.SentPacket(initialPacket(&Packet{PacketNumber: 2, SendTime: now.Add(-2 * time.Second)})) handler.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()).To(Equal(SendAny)) expectInPacketHistory([]protocol.PacketNumber{1, 3}, protocol.EncryptionInitial) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) expectInPacketHistory([]protocol.PacketNumber{3}, protocol.EncryptionInitial) Expect(handler.SendMode()).To(Equal(SendAny)) }) It("sets the early retransmit alarm for Path MTU probe packets", func() { var mtuPacketDeclaredLost bool now := time.Now() handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 1, SendTime: now.Add(-3 * time.Second), IsPathMTUProbePacket: true, Frames: []Frame{{Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) { mtuPacketDeclaredLost = true }}}, })) handler.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() { handler.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++ { handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: i, EncryptionLevel: protocol.EncryptionInitial, })) } for i := protocol.PacketNumber(0); i < 10; i++ { handler.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()) }) Context("deleting Initials", func() { BeforeEach(func() { perspective = protocol.PerspectiveClient }) It("deletes Initials, as a client", func() { for i := protocol.PacketNumber(0); i < 6; i++ { handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: i, EncryptionLevel: protocol.EncryptionInitial, })) } Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(6))) handler.DropPackets(protocol.EncryptionInitial) // DropPackets should be ignored for clients and the Initial packet number space. // It has to be possible to send another Initial packets after this function was called. handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 10, EncryptionLevel: protocol.EncryptionInitial, })) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(7))) // Sending a Handshake packet triggers dropping of Initials. handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 1, EncryptionLevel: protocol.EncryptionHandshake, })) Expect(handler.bytesInFlight).To(Equal(protocol.ByteCount(1))) Expect(lostPackets).To(BeEmpty()) // frames must not be queued for retransmission 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++ { handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: i, EncryptionLevel: protocol.EncryptionHandshake, })) } for i := protocol.PacketNumber(0); i < 10; i++ { handler.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 { continue } handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: i, EncryptionLevel: protocol.Encryption0RTT, })) } for i := protocol.PacketNumber(6); i < 12; i++ { handler.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() handler.SentPacket(handshakePacket(&Packet{PacketNumber: 1, SendTime: now.Add(-time.Minute)})) handler.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()).To(Equal(SendPTOHandshake)) Expect(handler.ptoCount).To(BeEquivalentTo(1)) handler.DropPackets(protocol.EncryptionHandshake) Expect(handler.ptoCount).To(BeZero()) Expect(handler.SendMode()).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() { handler.SentPacket(initialPacket(&Packet{PacketNumber: 42})) Expect(handler.GetLossDetectionTimeout()).ToNot(BeZero()) Expect(handler.bytesInFlight).ToNot(BeZero()) Expect(handler.SendMode()).To(Equal(SendAny)) // now receive a Retry Expect(handler.ResetForRetry()).To(Succeed()) Expect(lostPackets).To(Equal([]protocol.PacketNumber{42})) Expect(handler.bytesInFlight).To(BeZero()) Expect(handler.GetLossDetectionTimeout()).To(BeZero()) Expect(handler.SendMode()).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 handler.SentPacket(&Packet{ PacketNumber: 13, EncryptionLevel: protocol.EncryptionInitial, Frames: []Frame{ {Frame: &wire.CryptoFrame{Data: []byte("foobar")}, OnLost: func(wire.Frame) { lostInitial = true }}, }, Length: 100, }) pn := handler.PopPacketNumber(protocol.Encryption0RTT) handler.SentPacket(&Packet{ PacketNumber: pn, EncryptionLevel: protocol.Encryption0RTT, Frames: []Frame{ {Frame: &wire.StreamFrame{Data: []byte("foobar")}, OnLost: func(wire.Frame) { lost0RTT = true }}, }, Length: 999, }) Expect(handler.bytesInFlight).ToNot(BeZero()) // now receive a Retry Expect(handler.ResetForRetry()).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() { handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 42, EncryptionLevel: protocol.EncryptionInitial, SendTime: time.Now().Add(-500 * time.Millisecond), })) handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 43, EncryptionLevel: protocol.EncryptionInitial, SendTime: time.Now().Add(-10 * time.Millisecond), })) Expect(handler.ResetForRetry()).To(Succeed()) Expect(handler.rttStats.SmoothedRTT()).To(BeNumerically("~", 500*time.Millisecond, 100*time.Millisecond)) }) It("uses a Retry for an RTT estimate, but doesn't set the RTT to a value lower than 5ms", func() { handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 42, EncryptionLevel: protocol.EncryptionInitial, SendTime: time.Now().Add(-500 * time.Microsecond), })) handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 43, EncryptionLevel: protocol.EncryptionInitial, SendTime: time.Now().Add(-10 * time.Microsecond), })) Expect(handler.ResetForRetry()).To(Succeed()) Expect(handler.rttStats.SmoothedRTT()).To(Equal(minRTTAfterRetry)) }) It("doesn't use a Retry for an RTT estimate, if it was not retransmitted", func() { handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 42, EncryptionLevel: protocol.EncryptionInitial, SendTime: time.Now().Add(-800 * time.Millisecond), })) Expect(handler.OnLossDetectionTimeout()).To(Succeed()) Expect(handler.SendMode()).To(Equal(SendPTOInitial)) handler.SentPacket(ackElicitingPacket(&Packet{ PacketNumber: 43, EncryptionLevel: protocol.EncryptionInitial, SendTime: time.Now().Add(-100 * time.Millisecond), })) Expect(handler.ResetForRetry()).To(Succeed()) Expect(handler.rttStats.SmoothedRTT()).To(BeZero()) }) }) }) quic-go-0.25.0/internal/ackhandler/sent_packet_history.go000066400000000000000000000051661417145451600235020ustar00rootroot00000000000000package ackhandler import ( "fmt" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) type sentPacketHistory struct { rttStats *utils.RTTStats packetList *PacketList packetMap map[protocol.PacketNumber]*PacketElement highestSent protocol.PacketNumber } func newSentPacketHistory(rttStats *utils.RTTStats) *sentPacketHistory { return &sentPacketHistory{ rttStats: rttStats, packetList: NewPacketList(), packetMap: make(map[protocol.PacketNumber]*PacketElement), highestSent: protocol.InvalidPacketNumber, } } func (h *sentPacketHistory) SentPacket(p *Packet, isAckEliciting bool) { if p.PacketNumber <= h.highestSent { panic("non-sequential packet number use") } // Skipped packet numbers. for pn := h.highestSent + 1; pn < p.PacketNumber; pn++ { el := h.packetList.PushBack(Packet{ PacketNumber: pn, EncryptionLevel: p.EncryptionLevel, SendTime: p.SendTime, skippedPacket: true, }) h.packetMap[pn] = el } h.highestSent = p.PacketNumber if isAckEliciting { el := h.packetList.PushBack(*p) h.packetMap[p.PacketNumber] = el } } // Iterate iterates through all packets. func (h *sentPacketHistory) Iterate(cb func(*Packet) (cont bool, err error)) error { cont := true var next *PacketElement for el := h.packetList.Front(); cont && el != nil; el = next { var err error next = el.Next() cont, err = cb(&el.Value) if err != nil { return err } } return nil } // FirstOutStanding returns the first outstanding packet. func (h *sentPacketHistory) FirstOutstanding() *Packet { for el := h.packetList.Front(); el != nil; el = el.Next() { p := &el.Value if !p.declaredLost && !p.skippedPacket && !p.IsPathMTUProbePacket { return p } } return nil } func (h *sentPacketHistory) Len() int { return len(h.packetMap) } func (h *sentPacketHistory) Remove(p protocol.PacketNumber) error { el, ok := h.packetMap[p] if !ok { return fmt.Errorf("packet %d not found in sent packet history", p) } h.packetList.Remove(el) delete(h.packetMap, p) return nil } func (h *sentPacketHistory) HasOutstandingPackets() bool { return h.FirstOutstanding() != nil } func (h *sentPacketHistory) DeleteOldPackets(now time.Time) { maxAge := 3 * h.rttStats.PTO(false) var nextEl *PacketElement for el := h.packetList.Front(); el != nil; el = nextEl { nextEl = el.Next() p := el.Value if p.SendTime.After(now.Add(-maxAge)) { break } if !p.skippedPacket && !p.declaredLost { // should only happen in the case of drastic RTT changes continue } delete(h.packetMap, p.PacketNumber) h.packetList.Remove(el) } } quic-go-0.25.0/internal/ackhandler/sent_packet_history_test.go000066400000000000000000000200551417145451600245330ustar00rootroot00000000000000package ackhandler import ( "errors" "time" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("SentPacketHistory", func() { var ( hist *sentPacketHistory rttStats *utils.RTTStats ) expectInHistory := func(packetNumbers []protocol.PacketNumber) { var mapLen int for _, el := range hist.packetMap { if !el.Value.skippedPacket { mapLen++ } } var listLen int for el := hist.packetList.Front(); el != nil; el = el.Next() { if !el.Value.skippedPacket { listLen++ } } ExpectWithOffset(1, mapLen).To(Equal(len(packetNumbers))) ExpectWithOffset(1, listLen).To(Equal(len(packetNumbers))) i := 0 err := hist.Iterate(func(p *Packet) (bool, error) { if p.skippedPacket { return true, nil } pn := packetNumbers[i] ExpectWithOffset(1, p.PacketNumber).To(Equal(pn)) ExpectWithOffset(1, hist.packetMap[pn].Value.PacketNumber).To(Equal(pn)) i++ return true, nil }) Expect(err).ToNot(HaveOccurred()) } BeforeEach(func() { rttStats = utils.NewRTTStats() hist = newSentPacketHistory(rttStats) }) It("saves sent packets", func() { hist.SentPacket(&Packet{PacketNumber: 1}, true) hist.SentPacket(&Packet{PacketNumber: 3}, true) hist.SentPacket(&Packet{PacketNumber: 4}, true) expectInHistory([]protocol.PacketNumber{1, 3, 4}) }) It("doesn't save non-ack-eliciting packets", func() { hist.SentPacket(&Packet{PacketNumber: 1}, true) hist.SentPacket(&Packet{PacketNumber: 3}, false) hist.SentPacket(&Packet{PacketNumber: 4}, true) expectInHistory([]protocol.PacketNumber{1, 4}) for el := hist.packetList.Front(); el != nil; el = el.Next() { Expect(el.Value.PacketNumber).ToNot(Equal(protocol.PacketNumber(3))) } }) It("gets the length", func() { hist.SentPacket(&Packet{PacketNumber: 0}, true) hist.SentPacket(&Packet{PacketNumber: 1}, true) hist.SentPacket(&Packet{PacketNumber: 2}, true) 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.SentPacket(&Packet{PacketNumber: 2}, true) hist.SentPacket(&Packet{PacketNumber: 3}, true) front := hist.FirstOutstanding() Expect(front).ToNot(BeNil()) Expect(front.PacketNumber).To(Equal(protocol.PacketNumber(2))) }) It("doesn't regard path MTU packets as outstanding", func() { hist.SentPacket(&Packet{PacketNumber: 2}, true) hist.SentPacket(&Packet{PacketNumber: 4, IsPathMTUProbePacket: true}, true) front := hist.FirstOutstanding() Expect(front).ToNot(BeNil()) Expect(front.PacketNumber).To(Equal(protocol.PacketNumber(2))) }) }) It("removes packets", func() { hist.SentPacket(&Packet{PacketNumber: 1}, true) hist.SentPacket(&Packet{PacketNumber: 4}, true) hist.SentPacket(&Packet{PacketNumber: 8}, true) err := hist.Remove(4) Expect(err).ToNot(HaveOccurred()) expectInHistory([]protocol.PacketNumber{1, 8}) }) It("errors when trying to remove a non existing packet", func() { hist.SentPacket(&Packet{PacketNumber: 1}, true) err := hist.Remove(2) Expect(err).To(MatchError("packet 2 not found in sent packet history")) }) Context("iterating", func() { BeforeEach(func() { hist.SentPacket(&Packet{PacketNumber: 1}, true) hist.SentPacket(&Packet{PacketNumber: 4}, true) hist.SentPacket(&Packet{PacketNumber: 8}, true) }) 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 []protocol.PacketNumber Expect(hist.Iterate(func(p *Packet) (bool, error) { if p.skippedPacket { skippedPackets = append(skippedPackets, p.PacketNumber) } else { packets = append(packets, 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})) }) 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("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.SentPacket(&Packet{EncryptionLevel: protocol.Encryption1RTT}, true) Expect(hist.HasOutstandingPackets()).To(BeTrue()) }) It("accounts for deleted packets", func() { hist.SentPacket(&Packet{ PacketNumber: 10, EncryptionLevel: protocol.Encryption1RTT, }, true) Expect(hist.HasOutstandingPackets()).To(BeTrue()) Expect(hist.Remove(10)).To(Succeed()) Expect(hist.HasOutstandingPackets()).To(BeFalse()) }) It("counts the number of packets", func() { hist.SentPacket(&Packet{ PacketNumber: 10, EncryptionLevel: protocol.Encryption1RTT, }, true) hist.SentPacket(&Packet{ PacketNumber: 11, EncryptionLevel: protocol.Encryption1RTT, }, true) Expect(hist.Remove(11)).To(Succeed()) Expect(hist.HasOutstandingPackets()).To(BeTrue()) Expect(hist.Remove(10)).To(Succeed()) Expect(hist.HasOutstandingPackets()).To(BeFalse()) }) }) Context("deleting old packets", func() { const pto = 3 * time.Second BeforeEach(func() { rttStats.UpdateRTT(time.Second, 0, time.Time{}) Expect(rttStats.PTO(false)).To(Equal(pto)) }) It("deletes old packets after 3 PTOs", func() { now := time.Now() hist.SentPacket(&Packet{PacketNumber: 10, SendTime: now.Add(-3 * pto), declaredLost: true}, true) expectInHistory([]protocol.PacketNumber{10}) hist.DeleteOldPackets(now.Add(-time.Nanosecond)) expectInHistory([]protocol.PacketNumber{10}) hist.DeleteOldPackets(now) expectInHistory([]protocol.PacketNumber{}) }) It("doesn't delete a packet if it hasn't been declared lost yet", func() { now := time.Now() hist.SentPacket(&Packet{PacketNumber: 10, SendTime: now.Add(-3 * pto), declaredLost: true}, true) hist.SentPacket(&Packet{PacketNumber: 11, SendTime: now.Add(-3 * pto), declaredLost: false}, true) expectInHistory([]protocol.PacketNumber{10, 11}) hist.DeleteOldPackets(now) expectInHistory([]protocol.PacketNumber{11}) }) It("deletes skipped packets", func() { now := time.Now() hist.SentPacket(&Packet{PacketNumber: 10, SendTime: now.Add(-3 * pto)}, true) expectInHistory([]protocol.PacketNumber{10}) Expect(hist.Len()).To(Equal(11)) hist.DeleteOldPackets(now) expectInHistory([]protocol.PacketNumber{10}) // the packet was not declared lost Expect(hist.Len()).To(Equal(1)) }) }) }) quic-go-0.25.0/internal/congestion/000077500000000000000000000000001417145451600171365ustar00rootroot00000000000000quic-go-0.25.0/internal/congestion/bandwidth.go000066400000000000000000000011471417145451600214340ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/lucas-clemente/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 } quic-go-0.25.0/internal/congestion/bandwidth_test.go000066400000000000000000000004131417145451600224660ustar00rootroot00000000000000package congestion import ( "time" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Bandwidth", func() { It("converts from time delta", func() { Expect(BandwidthFromDelta(1, time.Millisecond)).To(Equal(1000 * BytesPerSecond)) }) }) quic-go-0.25.0/internal/congestion/clock.go000066400000000000000000000005161417145451600205620ustar00rootroot00000000000000package 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() } quic-go-0.25.0/internal/congestion/congestion_suite_test.go000066400000000000000000000003061417145451600241040ustar00rootroot00000000000000package congestion import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestCongestion(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Congestion Suite") } quic-go-0.25.0/internal/congestion/cubic.go000066400000000000000000000206301417145451600205530ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) // 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 protocol.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize // TODO: when re-enabling cubic, make sure to use the actual packet size here maxDatagramSize = protocol.ByteCount(protocol.InitialPacketSizeIPv4) ) 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 = utils.MinByteCount(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 } quic-go-0.25.0/internal/congestion/cubic_sender.go000066400000000000000000000233301417145451600221130ustar00rootroot00000000000000package congestion import ( "fmt" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/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.InitialPacketSizeIPv4) 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.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() bool { return c.pacer.Budget(c.clock.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 = utils.MaxPacketNumber(ackedPacketNumber, c.largestAckedPacketNumber) if c.InRecovery() { return } c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime) if c.InSlowStart() { c.hybridSlowStart.OnPacketAcked(ackedPacketNumber) } } func (c *cubicSender) OnPacketLost(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 = utils.MinByteCount(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 || 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) } quic-go-0.25.0/internal/congestion/cubic_sender_test.go000066400000000000000000000456121417145451600231610ustar00rootroot00000000000000package congestion import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "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 protocol.ByteCount = 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.InitialPacketSizeIPv4, 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.OnPacketLost(ackedPacketNumber, packetLength, bytesInFlight) } bytesInFlight -= protocol.ByteCount(n) * packetLength } // Does not increment acked_packet_number_. LosePacket := func(number protocol.PacketNumber) { sender.OnPacketLost(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).ToNot(Equal(utils.InfDuration)) }) 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.InitialPacketSizeIPv4, 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.InitialPacketSizeIPv4, 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.InitialPacketSizeIPv4, 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.InitialPacketSizeIPv4, 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)) }) }) quic-go-0.25.0/internal/congestion/cubic_test.go000066400000000000000000000246421417145451600216210ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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)) }) }) quic-go-0.25.0/internal/congestion/hybrid_slow_start.go000066400000000000000000000077731417145451600232450ustar00rootroot00000000000000package congestion import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) // 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 = utils.MinInt64(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) minRTTincreaseThreshold := time.Duration(utils.MaxInt64(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 } quic-go-0.25.0/internal/congestion/hybrid_slow_start_test.go000066400000000000000000000043161417145451600242720ustar00rootroot00000000000000package congestion import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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()) }) }) quic-go-0.25.0/internal/congestion/interface.go000066400000000000000000000020201417145451600214170ustar00rootroot00000000000000package congestion import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" ) // A SendAlgorithm performs congestion control type SendAlgorithm interface { TimeUntilSend(bytesInFlight protocol.ByteCount) time.Time HasPacingBudget() 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) OnPacketLost(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 } quic-go-0.25.0/internal/congestion/pacer.go000066400000000000000000000046771417145451600205750ustar00rootroot00000000000000package congestion import ( "math" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) const maxBurstSizePackets = 10 // The pacer implements a token bucket pacing algorithm. type pacer struct { budgetAtLastSent protocol.ByteCount maxDatagramSize protocol.ByteCount lastSentTime time.Time getAdjustedBandwidth func() uint64 // in bytes/s } func newPacer(getBandwidth func() Bandwidth) *pacer { p := &pacer{ maxDatagramSize: initialMaxDatagramSize, getAdjustedBandwidth: 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.getAdjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 return utils.MinByteCount(p.maxBurstSize(), budget) } func (p *pacer) maxBurstSize() protocol.ByteCount { return utils.MaxByteCount( protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/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{} } return p.lastSentTime.Add(utils.MaxDuration( protocol.MinPacingDelay, time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond, )) } func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) { p.maxDatagramSize = s } quic-go-0.25.0/internal/congestion/pacer_test.go000066400000000000000000000103651417145451600216230ustar00rootroot00000000000000package congestion import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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("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)) }) }) quic-go-0.25.0/internal/flowcontrol/000077500000000000000000000000001417145451600173365ustar00rootroot00000000000000quic-go-0.25.0/internal/flowcontrol/base_flow_controller.go000066400000000000000000000076471417145451600241070ustar00rootroot00000000000000package flowcontrol import ( "sync" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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 be called after receiving a MAX_{STREAM_}DATA frame. func (c *baseFlowController) UpdateSendWindow(offset protocol.ByteCount) { if offset > c.sendWindow { c.sendWindow = offset } } 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 := utils.MinByteCount(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 } quic-go-0.25.0/internal/flowcontrol/base_flow_controller_test.go000066400000000000000000000217041417145451600251340ustar00rootroot00000000000000package flowcontrol import ( "os" "strconv" "time" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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() { controller.UpdateSendWindow(20) Expect(controller.sendWindowSize()).To(Equal(protocol.ByteCount(20))) controller.UpdateSendWindow(10) 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 }) }) }) }) quic-go-0.25.0/internal/flowcontrol/connection_flow_controller.go000066400000000000000000000071671417145451600253310ustar00rootroot00000000000000package flowcontrol import ( "errors" "fmt" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" ) type connectionFlowController struct { baseFlowController queueWindowUpdate func() } 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, queueWindowUpdate func(), 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, }, queueWindowUpdate: queueWindowUpdate, } } 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) shouldQueueWindowUpdate := c.hasWindowUpdate() c.mutex.Unlock() if shouldQueueWindowUpdate { c.queueWindowUpdate() } } func (c *connectionFlowController) GetWindowUpdate() protocol.ByteCount { c.mutex.Lock() oldWindowSize := c.receiveWindowSize offset := c.baseFlowController.getWindowUpdate() if 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 := utils.MinByteCount(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 } quic-go-0.25.0/internal/flowcontrol/connection_flow_controller_test.go000066400000000000000000000141721417145451600263620ustar00rootroot00000000000000package flowcontrol import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Connection Flow controller", func() { var ( controller *connectionFlowController queuedWindowUpdate bool ) // 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() { queuedWindowUpdate = false controller = &connectionFlowController{} controller.rttStats = &utils.RTTStats{} controller.logger = utils.DefaultLogger controller.queueWindowUpdate = func() { queuedWindowUpdate = true } 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, nil, 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(queuedWindowUpdate).To(BeFalse()) controller.AddBytesRead(29) Expect(queuedWindowUpdate).To(BeTrue()) Expect(controller.GetWindowUpdate()).ToNot(BeZero()) queuedWindowUpdate = false controller.AddBytesRead(1) Expect(queuedWindowUpdate).To(BeFalse()) }) 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)) }) }) }) quic-go-0.25.0/internal/flowcontrol/flowcontrol_suite_test.go000066400000000000000000000006111417145451600245030ustar00rootroot00000000000000package flowcontrol import ( "testing" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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() }) quic-go-0.25.0/internal/flowcontrol/interface.go000066400000000000000000000026701417145451600216320ustar00rootroot00000000000000package flowcontrol import "github.com/lucas-clemente/quic-go/internal/protocol" type flowController interface { // for sending SendWindowSize() protocol.ByteCount UpdateSendWindow(protocol.ByteCount) AddBytesSent(protocol.ByteCount) // for receiving AddBytesRead(protocol.ByteCount) GetWindowUpdate() protocol.ByteCount // returns 0 if no update is necessary IsNewlyBlocked() (bool, protocol.ByteCount) } // A StreamFlowController is a flow controller for a QUIC stream. type StreamFlowController interface { flowController // for receiving // UpdateHighestReceived should be 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 should be called when reading from the stream is aborted early, // and there won't be any further calls to AddBytesRead. Abandon() } // The ConnectionFlowController is the flow controller for the connection. type ConnectionFlowController interface { flowController Reset() error } 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 } quic-go-0.25.0/internal/flowcontrol/stream_flow_controller.go000066400000000000000000000113011417145451600244460ustar00rootroot00000000000000package flowcontrol import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" ) type streamFlowController struct { baseFlowController streamID protocol.StreamID queueWindowUpdate func() 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, queueWindowUpdate func(protocol.StreamID), rttStats *utils.RTTStats, logger utils.Logger, ) StreamFlowController { return &streamFlowController{ streamID: streamID, connection: cfc.(connectionFlowControllerI), queueWindowUpdate: func() { queueWindowUpdate(streamID) }, 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) { c.mutex.Lock() c.baseFlowController.addBytesRead(n) shouldQueueWindowUpdate := c.shouldQueueWindowUpdate() c.mutex.Unlock() if shouldQueueWindowUpdate { c.queueWindowUpdate() } c.connection.AddBytesRead(n) } func (c *streamFlowController) Abandon() { c.mutex.Lock() unread := c.highestReceived - c.bytesRead 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 utils.MinByteCount(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize()) } 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 } quic-go-0.25.0/internal/flowcontrol/stream_flow_controller_test.go000066400000000000000000000255251417145451600255220ustar00rootroot00000000000000package flowcontrol import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Stream Flow controller", func() { var ( controller *streamFlowController queuedWindowUpdate bool ) BeforeEach(func() { queuedWindowUpdate = false rttStats := &utils.RTTStats{} controller = &streamFlowController{ streamID: 10, connection: NewConnectionFlowController( 1000, 1000, func() {}, func(protocol.ByteCount) bool { return true }, rttStats, utils.DefaultLogger, ).(*connectionFlowController), } controller.maxReceiveWindowSize = 10000 controller.rttStats = rttStats controller.logger = utils.DefaultLogger controller.queueWindowUpdate = func() { queuedWindowUpdate = true } }) 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, nil, func(protocol.ByteCount) bool { return true }, nil, utils.DefaultLogger) fc := NewStreamFlowController(5, cc, receiveWindow, maxReceiveWindow, sendWindow, nil, 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 with the correct stream ID", func() { var queued bool queueWindowUpdate := func(id protocol.StreamID) { Expect(id).To(Equal(protocol.StreamID(5))) queued = true } cc := NewConnectionFlowController(receiveWindow, maxReceiveWindow, func() {}, func(protocol.ByteCount) bool { return true }, nil, utils.DefaultLogger) fc := NewStreamFlowController(5, cc, receiveWindow, maxReceiveWindow, sendWindow, queueWindowUpdate, rttStats, utils.DefaultLogger).(*streamFlowController) fc.AddBytesRead(receiveWindow) Expect(queued).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("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() { controller.AddBytesRead(1) Expect(queuedWindowUpdate).To(BeFalse()) controller.AddBytesRead(29) Expect(queuedWindowUpdate).To(BeTrue()) Expect(controller.GetWindowUpdate()).ToNot(BeZero()) queuedWindowUpdate = false controller.AddBytesRead(1) Expect(queuedWindowUpdate).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()) controller.AddBytesRead(30) Expect(queuedWindowUpdate).To(BeFalse()) offset := controller.GetWindowUpdate() Expect(offset).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()) }) }) }) quic-go-0.25.0/internal/handshake/000077500000000000000000000000001417145451600167145ustar00rootroot00000000000000quic-go-0.25.0/internal/handshake/aead.go000066400000000000000000000106531417145451600201420ustar00rootroot00000000000000package handshake import ( "crypto/cipher" "encoding/binary" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" "github.com/lucas-clemente/quic-go/internal/utils" ) func createAEAD(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) cipher.AEAD { key := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic key", suite.KeyLen) iv := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic iv", suite.IVLen()) return suite.AEAD(key, iv) } type longHeaderSealer struct { aead cipher.AEAD headerProtector headerProtector // use a single slice to avoid allocations nonceBuf []byte } var _ LongHeaderSealer = &longHeaderSealer{} func newLongHeaderSealer(aead cipher.AEAD, headerProtector headerProtector) LongHeaderSealer { return &longHeaderSealer{ aead: aead, headerProtector: headerProtector, nonceBuf: make([]byte, aead.NonceSize()), } } func (s *longHeaderSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { binary.BigEndian.PutUint64(s.nonceBuf[len(s.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 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 cipher.AEAD headerProtector headerProtector highestRcvdPN protocol.PacketNumber // highest packet number received (which could be successfully unprotected) // use a single slice to avoid allocations nonceBuf []byte } var _ LongHeaderOpener = &longHeaderOpener{} func newLongHeaderOpener(aead cipher.AEAD, headerProtector headerProtector) LongHeaderOpener { return &longHeaderOpener{ aead: aead, headerProtector: headerProtector, nonceBuf: make([]byte, aead.NonceSize()), } } 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[len(o.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. dec, err := o.aead.Open(dst, o.nonceBuf, src, ad) if err == nil { o.highestRcvdPN = utils.MaxPacketNumber(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) } type handshakeSealer struct { LongHeaderSealer dropInitialKeys func() dropped bool } func newHandshakeSealer( aead cipher.AEAD, headerProtector headerProtector, dropInitialKeys func(), perspective protocol.Perspective, ) LongHeaderSealer { sealer := newLongHeaderSealer(aead, headerProtector) // The client drops Initial keys when sending the first Handshake packet. if perspective == protocol.PerspectiveServer { return sealer } return &handshakeSealer{ LongHeaderSealer: sealer, dropInitialKeys: dropInitialKeys, } } func (s *handshakeSealer) Seal(dst, src []byte, pn protocol.PacketNumber, ad []byte) []byte { data := s.LongHeaderSealer.Seal(dst, src, pn, ad) if !s.dropped { s.dropInitialKeys() s.dropped = true } return data } type handshakeOpener struct { LongHeaderOpener dropInitialKeys func() dropped bool } func newHandshakeOpener( aead cipher.AEAD, headerProtector headerProtector, dropInitialKeys func(), perspective protocol.Perspective, ) LongHeaderOpener { opener := newLongHeaderOpener(aead, headerProtector) // The server drops Initial keys when first successfully processing a Handshake packet. if perspective == protocol.PerspectiveClient { return opener } return &handshakeOpener{ LongHeaderOpener: opener, dropInitialKeys: dropInitialKeys, } } func (o *handshakeOpener) Open(dst, src []byte, pn protocol.PacketNumber, ad []byte) ([]byte, error) { dec, err := o.LongHeaderOpener.Open(dst, src, pn, ad) if err == nil && !o.dropped { o.dropInitialKeys() o.dropped = true } return dec, err } quic-go-0.25.0/internal/handshake/aead_test.go000066400000000000000000000163611417145451600212030ustar00rootroot00000000000000package handshake import ( "bytes" "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/tls" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Long Header AEAD", 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(aead, newHeaderProtector(cs, hpKey, true)), newLongHeaderOpener(aead, newHeaderProtector(cs, hpKey, true)) } 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})) }) }) }) } }) var _ = Describe("Long Header AEAD", func() { var ( dropped chan struct{} // use a chan because closing it twice will panic aead cipher.AEAD hp headerProtector ) dropCb := func() { close(dropped) } msg := []byte("Lorem ipsum dolor sit amet.") ad := []byte("Donec in velit neque.") BeforeEach(func() { dropped = make(chan struct{}) 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()) hp = newHeaderProtector(cipherSuites[0], hpKey, true) }) Context("for the server", func() { It("drops keys when first successfully processing a Handshake packet", func() { serverOpener := newHandshakeOpener(aead, hp, dropCb, protocol.PerspectiveServer) // first try to open an invalid message _, err := serverOpener.Open(nil, []byte("invalid"), 0, []byte("invalid")) Expect(err).To(HaveOccurred()) Expect(dropped).ToNot(BeClosed()) // then open a valid message enc := newLongHeaderSealer(aead, hp).Seal(nil, msg, 10, ad) _, err = serverOpener.Open(nil, enc, 10, ad) Expect(err).ToNot(HaveOccurred()) Expect(dropped).To(BeClosed()) // now open the same message again to make sure the callback is only called once _, err = serverOpener.Open(nil, enc, 10, ad) Expect(err).ToNot(HaveOccurred()) }) It("doesn't drop keys when sealing a Handshake packet", func() { serverSealer := newHandshakeSealer(aead, hp, dropCb, protocol.PerspectiveServer) serverSealer.Seal(nil, msg, 1, ad) Expect(dropped).ToNot(BeClosed()) }) }) Context("for the client", func() { It("drops keys when first sealing a Handshake packet", func() { clientSealer := newHandshakeSealer(aead, hp, dropCb, protocol.PerspectiveClient) // seal the first message clientSealer.Seal(nil, msg, 1, ad) Expect(dropped).To(BeClosed()) // seal another message to make sure the callback is only called once clientSealer.Seal(nil, msg, 2, ad) }) It("doesn't drop keys when processing a Handshake packet", func() { enc := newLongHeaderSealer(aead, hp).Seal(nil, msg, 42, ad) clientOpener := newHandshakeOpener(aead, hp, dropCb, protocol.PerspectiveClient) _, err := clientOpener.Open(nil, enc, 42, ad) Expect(err).ToNot(HaveOccurred()) Expect(dropped).ToNot(BeClosed()) }) }) }) quic-go-0.25.0/internal/handshake/crypto_setup.go000066400000000000000000000552751417145451600220210ustar00rootroot00000000000000package handshake import ( "bytes" "crypto/tls" "errors" "fmt" "io" "net" "sync" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/qtls" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" "github.com/lucas-clemente/quic-go/quicvarint" ) // TLS unexpected_message alert const alertUnexpectedMessage uint8 = 10 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) } } const clientSessionStateRevision = 3 type conn struct { localAddr, remoteAddr net.Addr version protocol.VersionNumber } var _ ConnWithVersion = &conn{} func newConn(local, remote net.Addr, version protocol.VersionNumber) ConnWithVersion { return &conn{ localAddr: local, remoteAddr: remote, version: version, } } 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 } func (c *conn) GetQUICVersion() protocol.VersionNumber { return c.version } type cryptoSetup struct { tlsConf *tls.Config extraConf *qtls.ExtraConfig conn *qtls.Conn version protocol.VersionNumber messageChan chan []byte isReadingHandshakeMessage chan struct{} readFirstHandshakeMessage bool ourParams *wire.TransportParameters peerParams *wire.TransportParameters paramsChan <-chan []byte runner handshakeRunner alertChan chan uint8 // handshakeDone is closed as soon as the go routine running qtls.Handshake() returns handshakeDone chan struct{} // is closed when Close() is called closeChan chan struct{} zeroRTTParameters *wire.TransportParameters clientHelloWritten bool clientHelloWrittenChan chan *wire.TransportParameters rttStats *utils.RTTStats tracer logging.ConnectionTracer logger utils.Logger perspective protocol.Perspective mutex sync.Mutex // protects all members below handshakeCompleteTime time.Time readEncLevel protocol.EncryptionLevel writeEncLevel protocol.EncryptionLevel zeroRTTOpener LongHeaderOpener // only set for the server zeroRTTSealer LongHeaderSealer // only set for the client initialStream io.Writer initialOpener LongHeaderOpener initialSealer LongHeaderSealer handshakeStream io.Writer handshakeOpener LongHeaderOpener handshakeSealer LongHeaderSealer aead *updatableAEAD has1RTTSealer bool has1RTTOpener bool } var ( _ qtls.RecordLayer = &cryptoSetup{} _ CryptoSetup = &cryptoSetup{} ) // NewCryptoSetupClient creates a new crypto setup for the client func NewCryptoSetupClient( initialStream io.Writer, handshakeStream io.Writer, connID protocol.ConnectionID, localAddr net.Addr, remoteAddr net.Addr, tp *wire.TransportParameters, runner handshakeRunner, tlsConf *tls.Config, enable0RTT bool, rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber, ) (CryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) { cs, clientHelloWritten := newCryptoSetup( initialStream, handshakeStream, connID, tp, runner, tlsConf, enable0RTT, rttStats, tracer, logger, protocol.PerspectiveClient, version, ) cs.conn = qtls.Client(newConn(localAddr, remoteAddr, version), cs.tlsConf, cs.extraConf) return cs, clientHelloWritten } // NewCryptoSetupServer creates a new crypto setup for the server func NewCryptoSetupServer( initialStream io.Writer, handshakeStream io.Writer, connID protocol.ConnectionID, localAddr net.Addr, remoteAddr net.Addr, tp *wire.TransportParameters, runner handshakeRunner, tlsConf *tls.Config, enable0RTT bool, rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, version protocol.VersionNumber, ) CryptoSetup { cs, _ := newCryptoSetup( initialStream, handshakeStream, connID, tp, runner, tlsConf, enable0RTT, rttStats, tracer, logger, protocol.PerspectiveServer, version, ) cs.conn = qtls.Server(newConn(localAddr, remoteAddr, version), cs.tlsConf, cs.extraConf) return cs } func newCryptoSetup( initialStream io.Writer, handshakeStream io.Writer, connID protocol.ConnectionID, tp *wire.TransportParameters, runner handshakeRunner, tlsConf *tls.Config, enable0RTT bool, rttStats *utils.RTTStats, tracer logging.ConnectionTracer, logger utils.Logger, perspective protocol.Perspective, version protocol.VersionNumber, ) (*cryptoSetup, <-chan *wire.TransportParameters /* ClientHello written. Receive nil for non-0-RTT */) { initialSealer, initialOpener := NewInitialAEAD(connID, perspective, version) if tracer != nil { tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveClient) tracer.UpdatedKeyFromTLS(protocol.EncryptionInitial, protocol.PerspectiveServer) } extHandler := newExtensionHandler(tp.Marshal(perspective), perspective, version) cs := &cryptoSetup{ tlsConf: tlsConf, initialStream: initialStream, initialSealer: initialSealer, initialOpener: initialOpener, handshakeStream: handshakeStream, aead: newUpdatableAEAD(rttStats, tracer, logger), readEncLevel: protocol.EncryptionInitial, writeEncLevel: protocol.EncryptionInitial, runner: runner, ourParams: tp, paramsChan: extHandler.TransportParameters(), rttStats: rttStats, tracer: tracer, logger: logger, perspective: perspective, handshakeDone: make(chan struct{}), alertChan: make(chan uint8), clientHelloWrittenChan: make(chan *wire.TransportParameters, 1), messageChan: make(chan []byte, 100), isReadingHandshakeMessage: make(chan struct{}), closeChan: make(chan struct{}), version: version, } var maxEarlyData uint32 if enable0RTT { maxEarlyData = 0xffffffff } cs.extraConf = &qtls.ExtraConfig{ GetExtensions: extHandler.GetExtensions, ReceivedExtensions: extHandler.ReceivedExtensions, AlternativeRecordLayer: cs, EnforceNextProtoSelection: true, MaxEarlyData: maxEarlyData, Accept0RTT: cs.accept0RTT, Rejected0RTT: cs.rejected0RTT, Enable0RTT: enable0RTT, GetAppDataForSessionState: cs.marshalDataForSessionState, SetAppDataFromSessionState: cs.handleDataFromSessionState, } return cs, cs.clientHelloWrittenChan } 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(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) RunHandshake() { // Handle errors that might occur when HandleData() is called. handshakeComplete := make(chan struct{}) handshakeErrChan := make(chan error, 1) go func() { defer close(h.handshakeDone) if err := h.conn.Handshake(); err != nil { handshakeErrChan <- err return } close(handshakeComplete) }() select { case <-handshakeComplete: // return when the handshake is done h.mutex.Lock() h.handshakeCompleteTime = time.Now() h.mutex.Unlock() h.runner.OnHandshakeComplete() case <-h.closeChan: // wait until the Handshake() go routine has returned <-h.handshakeDone case alert := <-h.alertChan: handshakeErr := <-handshakeErrChan h.onError(alert, handshakeErr.Error()) } } func (h *cryptoSetup) onError(alert uint8, message string) { h.runner.OnError(qerr.NewCryptoError(alert, message)) } // Close closes the crypto setup. // It aborts the handshake, if it is still running. // It must only be called once. func (h *cryptoSetup) Close() error { close(h.closeChan) // wait until qtls.Handshake() actually returned <-h.handshakeDone return nil } // handleMessage handles a TLS handshake message. // It is called by the crypto streams when a new message is available. // It returns if it is done with messages on the same encryption level. func (h *cryptoSetup) HandleMessage(data []byte, encLevel protocol.EncryptionLevel) bool /* stream finished */ { msgType := messageType(data[0]) h.logger.Debugf("Received %s message (%d bytes, encryption level: %s)", msgType, len(data), encLevel) if err := h.checkEncryptionLevel(msgType, encLevel); err != nil { h.onError(alertUnexpectedMessage, err.Error()) return false } h.messageChan <- data if encLevel == protocol.Encryption1RTT { h.handlePostHandshakeMessage() return false } readLoop: for { select { case data := <-h.paramsChan: if data == nil { h.onError(0x6d, "missing quic_transport_parameters extension") } else { h.handleTransportParameters(data) } case <-h.isReadingHandshakeMessage: break readLoop case <-h.handshakeDone: break readLoop case <-h.closeChan: break readLoop } } // We're done with the Initial encryption level after processing a ClientHello / ServerHello, // but only if a handshake opener and sealer was created. // Otherwise, a HelloRetryRequest was performed. // We're done with the Handshake encryption level after processing the Finished message. return ((msgType == typeClientHello || msgType == typeServerHello) && h.handshakeOpener != nil && h.handshakeSealer != nil) || msgType == typeFinished } func (h *cryptoSetup) checkEncryptionLevel(msgType messageType, encLevel protocol.EncryptionLevel) error { var expected protocol.EncryptionLevel switch msgType { case typeClientHello, typeServerHello: expected = protocol.EncryptionInitial case typeEncryptedExtensions, typeCertificate, typeCertificateRequest, typeCertificateVerify, typeFinished: expected = protocol.EncryptionHandshake case typeNewSessionTicket: expected = protocol.Encryption1RTT default: return fmt.Errorf("unexpected handshake message: %d", msgType) } if encLevel != expected { return fmt.Errorf("expected handshake message %s to have encryption level %s, has %s", msgType, expected, encLevel) } return nil } func (h *cryptoSetup) handleTransportParameters(data []byte) { var tp wire.TransportParameters if err := tp.Unmarshal(data, h.perspective.Opposite()); err != nil { h.runner.OnError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), }) } h.peerParams = &tp h.runner.OnReceivedParams(h.peerParams) } // must be called after receiving the transport parameters func (h *cryptoSetup) marshalDataForSessionState() []byte { buf := &bytes.Buffer{} quicvarint.Write(buf, clientSessionStateRevision) quicvarint.Write(buf, uint64(h.rttStats.SmoothedRTT().Microseconds())) h.peerParams.MarshalForSessionTicket(buf) return buf.Bytes() } func (h *cryptoSetup) handleDataFromSessionState(data []byte) { tp, err := h.handleDataFromSessionStateImpl(data) if err != nil { h.logger.Debugf("Restoring of transport parameters from session ticket failed: %s", err.Error()) return } h.zeroRTTParameters = tp } func (h *cryptoSetup) handleDataFromSessionStateImpl(data []byte) (*wire.TransportParameters, error) { r := bytes.NewReader(data) ver, err := quicvarint.Read(r) if err != nil { return nil, err } if ver != clientSessionStateRevision { return nil, fmt.Errorf("mismatching version. Got %d, expected %d", ver, clientSessionStateRevision) } rtt, err := quicvarint.Read(r) if err != nil { return nil, err } h.rttStats.SetInitialRTT(time.Duration(rtt) * time.Microsecond) var tp wire.TransportParameters if err := tp.UnmarshalFromSessionTicket(r); err != nil { return nil, err } return &tp, nil } // only valid for the server func (h *cryptoSetup) GetSessionTicket() ([]byte, error) { var appData []byte // Save transport parameters to the session ticket if we're allowing 0-RTT. if h.extraConf.MaxEarlyData > 0 { appData = (&sessionTicket{ Parameters: h.ourParams, RTT: h.rttStats.SmoothedRTT(), }).Marshal() } return h.conn.GetSessionTicket(appData) } // accept0RTT is called for the server when receiving the client's session ticket. // It decides whether to accept 0-RTT. func (h *cryptoSetup) accept0RTT(sessionTicketData []byte) bool { var t sessionTicket if err := t.Unmarshal(sessionTicketData); err != nil { h.logger.Debugf("Unmarshalling transport parameters from session ticket failed: %s", err.Error()) return false } valid := h.ourParams.ValidFor0RTT(t.Parameters) if valid { h.logger.Debugf("Accepting 0-RTT. Restoring RTT from session ticket: %s", t.RTT) h.rttStats.SetInitialRTT(t.RTT) } else { h.logger.Debugf("Transport parameters changed. Rejecting 0-RTT.") } return valid } // 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.") h.mutex.Lock() had0RTTKeys := h.zeroRTTSealer != nil h.zeroRTTSealer = nil h.mutex.Unlock() if had0RTTKeys { h.runner.DropKeys(protocol.Encryption0RTT) } } func (h *cryptoSetup) handlePostHandshakeMessage() { // make sure the handshake has already completed <-h.handshakeDone done := make(chan struct{}) defer close(done) // h.alertChan is an unbuffered channel. // If an error occurs during conn.HandlePostHandshakeMessage, // it will be sent on this channel. // Read it from a go-routine so that HandlePostHandshakeMessage doesn't deadlock. alertChan := make(chan uint8, 1) go func() { <-h.isReadingHandshakeMessage select { case alert := <-h.alertChan: alertChan <- alert case <-done: } }() if err := h.conn.HandlePostHandshakeMessage(); err != nil { select { case <-h.closeChan: case alert := <-alertChan: h.onError(alert, err.Error()) } } } // ReadHandshakeMessage is called by TLS. // It blocks until a new handshake message is available. func (h *cryptoSetup) ReadHandshakeMessage() ([]byte, error) { if !h.readFirstHandshakeMessage { h.readFirstHandshakeMessage = true } else { select { case h.isReadingHandshakeMessage <- struct{}{}: case <-h.closeChan: return nil, errors.New("error while handling the handshake message") } } select { case msg := <-h.messageChan: return msg, nil case <-h.closeChan: return nil, errors.New("error while handling the handshake message") } } func (h *cryptoSetup) SetReadKey(encLevel qtls.EncryptionLevel, suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { h.mutex.Lock() switch encLevel { case qtls.Encryption0RTT: if h.perspective == protocol.PerspectiveClient { panic("Received 0-RTT read key for the client") } h.zeroRTTOpener = newLongHeaderOpener( createAEAD(suite, trafficSecret), newHeaderProtector(suite, trafficSecret, true), ) h.mutex.Unlock() h.logger.Debugf("Installed 0-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) if h.tracer != nil { h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective.Opposite()) } return case qtls.EncryptionHandshake: h.readEncLevel = protocol.EncryptionHandshake h.handshakeOpener = newHandshakeOpener( createAEAD(suite, trafficSecret), newHeaderProtector(suite, trafficSecret, true), h.dropInitialKeys, h.perspective, ) h.logger.Debugf("Installed Handshake Read keys (using %s)", tls.CipherSuiteName(suite.ID)) case qtls.EncryptionApplication: h.readEncLevel = protocol.Encryption1RTT h.aead.SetReadKey(suite, trafficSecret) h.has1RTTOpener = true h.logger.Debugf("Installed 1-RTT Read keys (using %s)", tls.CipherSuiteName(suite.ID)) default: panic("unexpected read encryption level") } h.mutex.Unlock() if h.tracer != nil { h.tracer.UpdatedKeyFromTLS(h.readEncLevel, h.perspective.Opposite()) } } func (h *cryptoSetup) SetWriteKey(encLevel qtls.EncryptionLevel, suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { h.mutex.Lock() switch encLevel { case qtls.Encryption0RTT: if h.perspective == protocol.PerspectiveServer { panic("Received 0-RTT write key for the server") } h.zeroRTTSealer = newLongHeaderSealer( createAEAD(suite, trafficSecret), newHeaderProtector(suite, trafficSecret, true), ) h.mutex.Unlock() h.logger.Debugf("Installed 0-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) if h.tracer != nil { h.tracer.UpdatedKeyFromTLS(protocol.Encryption0RTT, h.perspective) } return case qtls.EncryptionHandshake: h.writeEncLevel = protocol.EncryptionHandshake h.handshakeSealer = newHandshakeSealer( createAEAD(suite, trafficSecret), newHeaderProtector(suite, trafficSecret, true), h.dropInitialKeys, h.perspective, ) h.logger.Debugf("Installed Handshake Write keys (using %s)", tls.CipherSuiteName(suite.ID)) case qtls.EncryptionApplication: h.writeEncLevel = protocol.Encryption1RTT h.aead.SetWriteKey(suite, trafficSecret) h.has1RTTSealer = true h.logger.Debugf("Installed 1-RTT Write keys (using %s)", tls.CipherSuiteName(suite.ID)) if h.zeroRTTSealer != nil { h.zeroRTTSealer = nil h.logger.Debugf("Dropping 0-RTT keys.") if h.tracer != nil { h.tracer.DroppedEncryptionLevel(protocol.Encryption0RTT) } } default: panic("unexpected write encryption level") } h.mutex.Unlock() if h.tracer != nil { h.tracer.UpdatedKeyFromTLS(h.writeEncLevel, h.perspective) } } // WriteRecord is called when TLS writes data func (h *cryptoSetup) WriteRecord(p []byte) (int, error) { h.mutex.Lock() defer h.mutex.Unlock() //nolint:exhaustive // LS records can only be written for Initial and Handshake. switch h.writeEncLevel { case protocol.EncryptionInitial: // assume that the first WriteRecord call contains the ClientHello n, err := h.initialStream.Write(p) if !h.clientHelloWritten && h.perspective == protocol.PerspectiveClient { h.clientHelloWritten = true if h.zeroRTTSealer != nil && h.zeroRTTParameters != nil { h.logger.Debugf("Doing 0-RTT.") h.clientHelloWrittenChan <- h.zeroRTTParameters } else { h.logger.Debugf("Not doing 0-RTT.") h.clientHelloWrittenChan <- nil } } return n, err case protocol.EncryptionHandshake: return h.handshakeStream.Write(p) default: panic(fmt.Sprintf("unexpected write encryption level: %s", h.writeEncLevel)) } } func (h *cryptoSetup) SendAlert(alert uint8) { select { case h.alertChan <- alert: case <-h.closeChan: // no need to send an alert when we've already closed } } // used a callback in the handshakeSealer and handshakeOpener func (h *cryptoSetup) dropInitialKeys() { h.mutex.Lock() h.initialOpener = nil h.initialSealer = nil h.mutex.Unlock() h.runner.DropKeys(protocol.EncryptionInitial) h.logger.Debugf("Dropping Initial keys.") } func (h *cryptoSetup) SetHandshakeConfirmed() { h.aead.SetHandshakeConfirmed() // drop Handshake keys var dropped bool h.mutex.Lock() if h.handshakeOpener != nil { h.handshakeOpener = nil h.handshakeSealer = nil dropped = true } h.mutex.Unlock() if dropped { h.runner.DropKeys(protocol.EncryptionHandshake) h.logger.Debugf("Dropping Handshake keys.") } } func (h *cryptoSetup) GetInitialSealer() (LongHeaderSealer, error) { h.mutex.Lock() defer h.mutex.Unlock() if h.initialSealer == nil { return nil, ErrKeysDropped } return h.initialSealer, nil } func (h *cryptoSetup) Get0RTTSealer() (LongHeaderSealer, error) { h.mutex.Lock() defer h.mutex.Unlock() if h.zeroRTTSealer == nil { return nil, ErrKeysDropped } return h.zeroRTTSealer, nil } func (h *cryptoSetup) GetHandshakeSealer() (LongHeaderSealer, error) { h.mutex.Lock() defer h.mutex.Unlock() if h.handshakeSealer == nil { if h.initialSealer == nil { return nil, ErrKeysDropped } return nil, ErrKeysNotYetAvailable } return h.handshakeSealer, nil } func (h *cryptoSetup) Get1RTTSealer() (ShortHeaderSealer, error) { h.mutex.Lock() defer h.mutex.Unlock() if !h.has1RTTSealer { return nil, ErrKeysNotYetAvailable } return h.aead, nil } func (h *cryptoSetup) GetInitialOpener() (LongHeaderOpener, error) { h.mutex.Lock() defer h.mutex.Unlock() if h.initialOpener == nil { return nil, ErrKeysDropped } return h.initialOpener, nil } func (h *cryptoSetup) Get0RTTOpener() (LongHeaderOpener, error) { h.mutex.Lock() defer h.mutex.Unlock() 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) { h.mutex.Lock() defer h.mutex.Unlock() 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) { h.mutex.Lock() defer h.mutex.Unlock() 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(protocol.Encryption0RTT) } } if !h.has1RTTOpener { return nil, ErrKeysNotYetAvailable } return h.aead, nil } func (h *cryptoSetup) ConnectionState() ConnectionState { return qtls.GetConnectionState(h.conn) } quic-go-0.25.0/internal/handshake/crypto_setup_test.go000066400000000000000000000657701417145451600230610ustar00rootroot00000000000000package handshake import ( "bytes" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" "math/big" "time" mocktls "github.com/lucas-clemente/quic-go/internal/mocks/tls" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/testdata" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var helloRetryRequestRandom = []byte{ // See RFC 8446, Section 4.1.3. 0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C, 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB, 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C, } type chunk struct { data []byte encLevel protocol.EncryptionLevel } type stream struct { encLevel protocol.EncryptionLevel chunkChan chan<- chunk } func newStream(chunkChan chan<- chunk, encLevel protocol.EncryptionLevel) *stream { return &stream{ chunkChan: chunkChan, encLevel: encLevel, } } func (s *stream) Write(b []byte) (int, error) { data := make([]byte, len(b)) copy(data, b) select { case s.chunkChan <- chunk{data: data, encLevel: s.encLevel}: default: panic("chunkChan too small") } return len(b), nil } var _ = Describe("Crypto Setup TLS", func() { var clientConf, serverConf *tls.Config // unparam incorrectly complains that the first argument is never used. //nolint:unparam initStreams := func() (chan chunk, *stream /* initial */, *stream /* handshake */) { chunkChan := make(chan chunk, 100) initialStream := newStream(chunkChan, protocol.EncryptionInitial) handshakeStream := newStream(chunkChan, protocol.EncryptionHandshake) return chunkChan, initialStream, handshakeStream } BeforeEach(func() { serverConf = testdata.GetTLSConfig() serverConf.NextProtos = []string{"crypto-setup"} clientConf = &tls.Config{ ServerName: "localhost", RootCAs: testdata.GetRootCA(), NextProtos: []string{"crypto-setup"}, } }) It("returns Handshake() when an error occurs in qtls", func() { sErrChan := make(chan error, 1) runner := NewMockHandshakeRunner(mockCtrl) runner.EXPECT().OnError(gomock.Any()).Do(func(e error) { sErrChan <- e }) _, sInitialStream, sHandshakeStream := initStreams() var token protocol.StatelessResetToken server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{StatelessResetToken: &token}, runner, testdata.GetTLSConfig(), false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() server.RunHandshake() Expect(sErrChan).To(Receive(MatchError(&qerr.TransportError{ ErrorCode: 0x10a, ErrorMessage: "local error: tls: unexpected message", }))) close(done) }() fakeCH := append([]byte{byte(typeClientHello), 0, 0, 6}, []byte("foobar")...) handledMessage := make(chan struct{}) go func() { defer GinkgoRecover() server.HandleMessage(fakeCH, protocol.EncryptionInitial) close(handledMessage) }() Eventually(handledMessage).Should(BeClosed()) Eventually(done).Should(BeClosed()) }) It("errors when a message is received at the wrong encryption level", func() { sErrChan := make(chan error, 1) _, sInitialStream, sHandshakeStream := initStreams() runner := NewMockHandshakeRunner(mockCtrl) runner.EXPECT().OnError(gomock.Any()).Do(func(e error) { sErrChan <- e }) var token protocol.StatelessResetToken server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{StatelessResetToken: &token}, runner, testdata.GetTLSConfig(), false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() server.RunHandshake() close(done) }() fakeCH := append([]byte{byte(typeClientHello), 0, 0, 6}, []byte("foobar")...) server.HandleMessage(fakeCH, protocol.EncryptionHandshake) // wrong encryption level Expect(sErrChan).To(Receive(MatchError(&qerr.TransportError{ ErrorCode: 0x100 + qerr.TransportErrorCode(alertUnexpectedMessage), ErrorMessage: "expected handshake message ClientHello to have encryption level Initial, has Handshake", }))) // make the go routine return Expect(server.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("returns Handshake() when handling a message fails", func() { sErrChan := make(chan error, 1) _, sInitialStream, sHandshakeStream := initStreams() runner := NewMockHandshakeRunner(mockCtrl) runner.EXPECT().OnError(gomock.Any()).Do(func(e error) { sErrChan <- e }) var token protocol.StatelessResetToken server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{StatelessResetToken: &token}, runner, serverConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() server.RunHandshake() var err error Expect(sErrChan).To(Receive(&err)) Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode).To(BeEquivalentTo(0x100 + int(alertUnexpectedMessage))) close(done) }() fakeCH := append([]byte{byte(typeServerHello), 0, 0, 6}, []byte("foobar")...) server.HandleMessage(fakeCH, protocol.EncryptionInitial) // wrong encryption level Eventually(done).Should(BeClosed()) }) It("returns Handshake() when it is closed", func() { _, sInitialStream, sHandshakeStream := initStreams() var token protocol.StatelessResetToken server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{StatelessResetToken: &token}, NewMockHandshakeRunner(mockCtrl), serverConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() server.RunHandshake() close(done) }() Expect(server.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) Context("doing the handshake", 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}, } } 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 } handshake := func(client CryptoSetup, cChunkChan <-chan chunk, server CryptoSetup, sChunkChan <-chan chunk) { done := make(chan struct{}) go func() { defer GinkgoRecover() for { select { case c := <-cChunkChan: msgType := messageType(c.data[0]) finished := server.HandleMessage(c.data, c.encLevel) if msgType == typeFinished { Expect(finished).To(BeTrue()) } else if msgType == typeClientHello { // If this ClientHello didn't elicit a HelloRetryRequest, we're done with Initial keys. _, err := server.GetHandshakeOpener() Expect(finished).To(Equal(err == nil)) } else { Expect(finished).To(BeFalse()) } case c := <-sChunkChan: msgType := messageType(c.data[0]) finished := client.HandleMessage(c.data, c.encLevel) if msgType == typeFinished { Expect(finished).To(BeTrue()) } else if msgType == typeServerHello { Expect(finished).To(Equal(!bytes.Equal(c.data[6:6+32], helloRetryRequestRandom))) } else { Expect(finished).To(BeFalse()) } case <-done: // handshake complete return } } }() go func() { defer GinkgoRecover() defer close(done) server.RunHandshake() ticket, err := server.GetSessionTicket() Expect(err).ToNot(HaveOccurred()) if ticket != nil { client.HandleMessage(ticket, protocol.Encryption1RTT) } }() client.RunHandshake() Eventually(done).Should(BeClosed()) } handshakeWithTLSConf := func( clientConf, serverConf *tls.Config, clientRTTStats, serverRTTStats *utils.RTTStats, clientTransportParameters, serverTransportParameters *wire.TransportParameters, enable0RTT bool, ) (<-chan *wire.TransportParameters /* clientHelloWrittenChan */, CryptoSetup /* client */, error /* client error */, CryptoSetup /* server */, error /* server error */) { var cHandshakeComplete bool cChunkChan, cInitialStream, cHandshakeStream := initStreams() cErrChan := make(chan error, 1) cRunner := NewMockHandshakeRunner(mockCtrl) cRunner.EXPECT().OnReceivedParams(gomock.Any()) cRunner.EXPECT().OnError(gomock.Any()).Do(func(e error) { cErrChan <- e }).MaxTimes(1) cRunner.EXPECT().OnHandshakeComplete().Do(func() { cHandshakeComplete = true }).MaxTimes(1) cRunner.EXPECT().DropKeys(gomock.Any()).MaxTimes(1) client, clientHelloWrittenChan := NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, clientTransportParameters, cRunner, clientConf, enable0RTT, clientRTTStats, nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) var sHandshakeComplete bool sChunkChan, sInitialStream, sHandshakeStream := initStreams() sErrChan := make(chan error, 1) sRunner := NewMockHandshakeRunner(mockCtrl) sRunner.EXPECT().OnReceivedParams(gomock.Any()) sRunner.EXPECT().OnError(gomock.Any()).Do(func(e error) { sErrChan <- e }).MaxTimes(1) sRunner.EXPECT().OnHandshakeComplete().Do(func() { sHandshakeComplete = true }).MaxTimes(1) if serverTransportParameters.StatelessResetToken == nil { var token protocol.StatelessResetToken serverTransportParameters.StatelessResetToken = &token } server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, serverTransportParameters, sRunner, serverConf, enable0RTT, serverRTTStats, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) handshake(client, cChunkChan, server, sChunkChan) var cErr, sErr error select { case sErr = <-sErrChan: default: Expect(sHandshakeComplete).To(BeTrue()) } select { case cErr = <-cErrChan: default: Expect(cHandshakeComplete).To(BeTrue()) } return clientHelloWrittenChan, client, cErr, server, sErr } It("handshakes", func() { _, _, clientErr, _, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{}, 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{}, &wire.TransportParameters{}, 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{}, &wire.TransportParameters{}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) }) It("signals when it has written the ClientHello", func() { runner := NewMockHandshakeRunner(mockCtrl) cChunkChan, cInitialStream, cHandshakeStream := initStreams() client, chChan := NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{}, runner, &tls.Config{InsecureSkipVerify: true}, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() client.RunHandshake() close(done) }() var ch chunk Eventually(cChunkChan).Should(Receive(&ch)) Eventually(chChan).Should(Receive(BeNil())) // make sure the whole ClientHello was written Expect(len(ch.data)).To(BeNumerically(">=", 4)) Expect(messageType(ch.data[0])).To(Equal(typeClientHello)) length := int(ch.data[1])<<16 | int(ch.data[2])<<8 | int(ch.data[3]) Expect(len(ch.data) - 4).To(Equal(length)) // make the go routine return Expect(client.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("receives transport parameters", func() { var cTransportParametersRcvd, sTransportParametersRcvd *wire.TransportParameters cChunkChan, cInitialStream, cHandshakeStream := initStreams() cTransportParameters := &wire.TransportParameters{MaxIdleTimeout: 0x42 * time.Second} cRunner := NewMockHandshakeRunner(mockCtrl) cRunner.EXPECT().OnReceivedParams(gomock.Any()).Do(func(tp *wire.TransportParameters) { sTransportParametersRcvd = tp }) cRunner.EXPECT().OnHandshakeComplete() client, _ := NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, cTransportParameters, cRunner, clientConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) sChunkChan, sInitialStream, sHandshakeStream := initStreams() var token protocol.StatelessResetToken sRunner := NewMockHandshakeRunner(mockCtrl) sRunner.EXPECT().OnReceivedParams(gomock.Any()).Do(func(tp *wire.TransportParameters) { cTransportParametersRcvd = tp }) sRunner.EXPECT().OnHandshakeComplete() sTransportParameters := &wire.TransportParameters{ MaxIdleTimeout: 0x1337 * time.Second, StatelessResetToken: &token, } server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, sTransportParameters, sRunner, serverConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() handshake(client, cChunkChan, server, sChunkChan) close(done) }() Eventually(done).Should(BeClosed()) Expect(cTransportParametersRcvd.MaxIdleTimeout).To(Equal(cTransportParameters.MaxIdleTimeout)) Expect(sTransportParametersRcvd).ToNot(BeNil()) Expect(sTransportParametersRcvd.MaxIdleTimeout).To(Equal(sTransportParameters.MaxIdleTimeout)) }) Context("with session tickets", func() { It("errors when the NewSessionTicket is sent at the wrong encryption level", func() { cChunkChan, cInitialStream, cHandshakeStream := initStreams() cRunner := NewMockHandshakeRunner(mockCtrl) cRunner.EXPECT().OnReceivedParams(gomock.Any()) cRunner.EXPECT().OnHandshakeComplete() client, _ := NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{}, cRunner, clientConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) sChunkChan, sInitialStream, sHandshakeStream := initStreams() sRunner := NewMockHandshakeRunner(mockCtrl) sRunner.EXPECT().OnReceivedParams(gomock.Any()) sRunner.EXPECT().OnHandshakeComplete() var token protocol.StatelessResetToken server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{StatelessResetToken: &token}, sRunner, serverConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() handshake(client, cChunkChan, server, sChunkChan) close(done) }() Eventually(done).Should(BeClosed()) // inject an invalid session ticket cRunner.EXPECT().OnError(&qerr.TransportError{ ErrorCode: 0x100 + qerr.TransportErrorCode(alertUnexpectedMessage), ErrorMessage: "expected handshake message NewSessionTicket to have encryption level 1-RTT, has Handshake", }) b := append([]byte{uint8(typeNewSessionTicket), 0, 0, 6}, []byte("foobar")...) client.HandleMessage(b, protocol.EncryptionHandshake) }) It("errors when handling the NewSessionTicket fails", func() { cChunkChan, cInitialStream, cHandshakeStream := initStreams() cRunner := NewMockHandshakeRunner(mockCtrl) cRunner.EXPECT().OnReceivedParams(gomock.Any()) cRunner.EXPECT().OnHandshakeComplete() client, _ := NewCryptoSetupClient( cInitialStream, cHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{}, cRunner, clientConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("client"), protocol.VersionTLS, ) sChunkChan, sInitialStream, sHandshakeStream := initStreams() sRunner := NewMockHandshakeRunner(mockCtrl) sRunner.EXPECT().OnReceivedParams(gomock.Any()) sRunner.EXPECT().OnHandshakeComplete() var token protocol.StatelessResetToken server := NewCryptoSetupServer( sInitialStream, sHandshakeStream, protocol.ConnectionID{}, nil, nil, &wire.TransportParameters{StatelessResetToken: &token}, sRunner, serverConf, false, &utils.RTTStats{}, nil, utils.DefaultLogger.WithPrefix("server"), protocol.VersionTLS, ) done := make(chan struct{}) go func() { defer GinkgoRecover() handshake(client, cChunkChan, server, sChunkChan) close(done) }() Eventually(done).Should(BeClosed()) // inject an invalid session ticket cRunner.EXPECT().OnError(gomock.Any()).Do(func(err error) { Expect(err).To(BeAssignableToTypeOf(&qerr.TransportError{})) Expect(err.(*qerr.TransportError).ErrorCode.IsCryptoError()).To(BeTrue()) }) b := append([]byte{uint8(typeNewSessionTicket), 0, 0, 6}, []byte("foobar")...) client.HandleMessage(b, protocol.Encryption1RTT) }) It("uses session resumption", func() { csc := mocktls.NewMockClientSessionCache(mockCtrl) var state *tls.ClientSessionState receivedSessionTicket := make(chan struct{}) csc.EXPECT().Get(gomock.Any()) csc.EXPECT().Put(gomock.Any(), gomock.Any()).Do(func(_ string, css *tls.ClientSessionState) { state = css close(receivedSessionTicket) }) clientConf.ClientSessionCache = csc const clientRTT = 30 * time.Millisecond // RTT as measured by the client. Should be restored. clientOrigRTTStats := newRTTStatsWithRTT(clientRTT) clientHelloWrittenChan, client, clientErr, server, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientOrigRTTStats, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(receivedSessionTicket).Should(BeClosed()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) Expect(clientHelloWrittenChan).To(Receive(BeNil())) csc.EXPECT().Get(gomock.Any()).Return(state, true) csc.EXPECT().Put(gomock.Any(), gomock.Any()).MaxTimes(1) clientRTTStats := &utils.RTTStats{} clientHelloWrittenChan, client, clientErr, server, serverErr = handshakeWithTLSConf( clientConf, serverConf, clientRTTStats, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(receivedSessionTicket).Should(BeClosed()) Expect(server.ConnectionState().DidResume).To(BeTrue()) Expect(client.ConnectionState().DidResume).To(BeTrue()) Expect(clientRTTStats.SmoothedRTT()).To(Equal(clientRTT)) Expect(clientHelloWrittenChan).To(Receive(BeNil())) }) It("doesn't use session resumption if the server disabled it", func() { csc := mocktls.NewMockClientSessionCache(mockCtrl) var state *tls.ClientSessionState receivedSessionTicket := make(chan struct{}) csc.EXPECT().Get(gomock.Any()) csc.EXPECT().Put(gomock.Any(), gomock.Any()).Do(func(_ string, css *tls.ClientSessionState) { state = css close(receivedSessionTicket) }) clientConf.ClientSessionCache = csc _, client, clientErr, server, serverErr := handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(receivedSessionTicket).Should(BeClosed()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) serverConf.SessionTicketsDisabled = true csc.EXPECT().Get(gomock.Any()).Return(state, true) _, client, clientErr, server, serverErr = handshakeWithTLSConf( clientConf, serverConf, &utils.RTTStats{}, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{}, false, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(receivedSessionTicket).Should(BeClosed()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) }) It("uses 0-RTT", func() { csc := mocktls.NewMockClientSessionCache(mockCtrl) var state *tls.ClientSessionState receivedSessionTicket := make(chan struct{}) csc.EXPECT().Get(gomock.Any()) csc.EXPECT().Put(gomock.Any(), gomock.Any()).Do(func(_ string, css *tls.ClientSessionState) { state = css close(receivedSessionTicket) }) 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 clientHelloWrittenChan, client, clientErr, server, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientOrigRTTStats, serverOrigRTTStats, &wire.TransportParameters{}, &wire.TransportParameters{InitialMaxData: initialMaxData}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(receivedSessionTicket).Should(BeClosed()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) Expect(clientHelloWrittenChan).To(Receive(BeNil())) csc.EXPECT().Get(gomock.Any()).Return(state, true) csc.EXPECT().Put(gomock.Any(), nil) csc.EXPECT().Put(gomock.Any(), gomock.Any()).MaxTimes(1) clientRTTStats := &utils.RTTStats{} serverRTTStats := &utils.RTTStats{} clientHelloWrittenChan, client, clientErr, server, serverErr = handshakeWithTLSConf( clientConf, serverConf, clientRTTStats, serverRTTStats, &wire.TransportParameters{}, &wire.TransportParameters{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 Expect(clientHelloWrittenChan).To(Receive(&tp)) Expect(tp.InitialMaxData).To(Equal(initialMaxData)) 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 := mocktls.NewMockClientSessionCache(mockCtrl) var state *tls.ClientSessionState receivedSessionTicket := make(chan struct{}) csc.EXPECT().Get(gomock.Any()) csc.EXPECT().Put(gomock.Any(), gomock.Any()).Do(func(_ string, css *tls.ClientSessionState) { state = css close(receivedSessionTicket) }) 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 clientHelloWrittenChan, client, clientErr, server, serverErr := handshakeWithTLSConf( clientConf, serverConf, clientOrigRTTStats, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{InitialMaxData: initialMaxData}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Eventually(receivedSessionTicket).Should(BeClosed()) Expect(server.ConnectionState().DidResume).To(BeFalse()) Expect(client.ConnectionState().DidResume).To(BeFalse()) Expect(clientHelloWrittenChan).To(Receive(BeNil())) csc.EXPECT().Get(gomock.Any()).Return(state, true) csc.EXPECT().Put(gomock.Any(), nil) csc.EXPECT().Put(gomock.Any(), gomock.Any()).MaxTimes(1) clientRTTStats := &utils.RTTStats{} clientHelloWrittenChan, client, clientErr, server, serverErr = handshakeWithTLSConf( clientConf, serverConf, clientRTTStats, &utils.RTTStats{}, &wire.TransportParameters{}, &wire.TransportParameters{InitialMaxData: initialMaxData - 1}, true, ) Expect(clientErr).ToNot(HaveOccurred()) Expect(serverErr).ToNot(HaveOccurred()) Expect(clientRTTStats.SmoothedRTT()).To(Equal(clientRTT)) var tp *wire.TransportParameters Expect(clientHelloWrittenChan).To(Receive(&tp)) 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()) }) }) }) }) quic-go-0.25.0/internal/handshake/handshake_suite_test.go000066400000000000000000000017021417145451600234410ustar00rootroot00000000000000package handshake import ( "crypto/tls" "encoding/hex" "strings" "testing" "github.com/lucas-clemente/quic-go/internal/qtls" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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 = []*qtls.CipherSuiteTLS13{ qtls.CipherSuiteTLS13ByID(tls.TLS_AES_128_GCM_SHA256), qtls.CipherSuiteTLS13ByID(tls.TLS_AES_256_GCM_SHA384), qtls.CipherSuiteTLS13ByID(tls.TLS_CHACHA20_POLY1305_SHA256), } quic-go-0.25.0/internal/handshake/header_protector.go000066400000000000000000000065461417145451600226070ustar00rootroot00000000000000package handshake import ( "crypto/aes" "crypto/cipher" "crypto/tls" "encoding/binary" "fmt" "golang.org/x/crypto/chacha20" "github.com/lucas-clemente/quic-go/internal/qtls" ) type headerProtector interface { EncryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) DecryptHeader(sample []byte, firstByte *byte, hdrBytes []byte) } func newHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector { switch suite.ID { case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384: return newAESHeaderProtector(suite, trafficSecret, isLongHeader) case tls.TLS_CHACHA20_POLY1305_SHA256: return newChaChaHeaderProtector(suite, trafficSecret, isLongHeader) default: panic(fmt.Sprintf("Invalid cipher suite id: %d", suite.ID)) } } type aesHeaderProtector struct { mask []byte block cipher.Block isLongHeader bool } var _ headerProtector = &aesHeaderProtector{} func newAESHeaderProtector(suite *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector { hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic hp", suite.KeyLen) block, err := aes.NewCipher(hpKey) if err != nil { panic(fmt.Sprintf("error creating new AES cipher: %s", err)) } return &aesHeaderProtector{ block: block, mask: make([]byte, block.BlockSize()), 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 *qtls.CipherSuiteTLS13, trafficSecret []byte, isLongHeader bool) headerProtector { hpKey := hkdfExpandLabel(suite.Hash, trafficSecret, []byte{}, "quic hp", 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] } } quic-go-0.25.0/internal/handshake/hkdf.go000066400000000000000000000015341417145451600201620ustar00rootroot00000000000000package handshake import ( "crypto" "encoding/binary" "golang.org/x/crypto/hkdf" ) // hkdfExpandLabel HKDF expands a label. // 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 } quic-go-0.25.0/internal/handshake/hkdf_test.go000066400000000000000000000013451417145451600212210ustar00rootroot00000000000000package handshake import ( "crypto" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Initial AEAD using AES-GCM", func() { // Result generated by running in qtls: // cipherSuiteTLS13ByID(TLS_AES_128_GCM_SHA256).expandLabel([]byte("secret"), []byte("context"), "label", 42) It("gets the same results as qtls", func() { expanded := hkdfExpandLabel(crypto.SHA256, []byte("secret"), []byte("context"), "label", 42) Expect(expanded).To(Equal([]byte{0x78, 0x87, 0x6a, 0xb5, 0x84, 0xa2, 0x26, 0xb7, 0x8, 0x5a, 0x7b, 0x3a, 0x4c, 0xbb, 0x1e, 0xbc, 0x2f, 0x9b, 0x67, 0xd0, 0x6a, 0xa2, 0x24, 0xb4, 0x7d, 0x29, 0x3c, 0x7a, 0xce, 0xc7, 0xc3, 0x74, 0xcd, 0x59, 0x7a, 0xa8, 0x21, 0x5e, 0xe7, 0xca, 0x1, 0xda})) }) }) quic-go-0.25.0/internal/handshake/initial_aead.go000066400000000000000000000043031417145451600216460ustar00rootroot00000000000000package handshake import ( "crypto" "crypto/tls" "golang.org/x/crypto/hkdf" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" ) var ( quicSaltOld = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99} quicSalt = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a} ) func getSalt(v protocol.VersionNumber) []byte { if v == protocol.Version1 { return quicSalt } return quicSaltOld } var initialSuite = &qtls.CipherSuiteTLS13{ ID: tls.TLS_AES_128_GCM_SHA256, KeyLen: 16, AEAD: qtls.AEADAESGCMTLS13, Hash: crypto.SHA256, } // NewInitialAEAD creates a new AEAD for Initial encryption / decryption. func NewInitialAEAD(connID protocol.ConnectionID, pers protocol.Perspective, v protocol.VersionNumber) (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) otherKey, otherIV := computeInitialKeyAndIV(otherSecret) encrypter := qtls.AEADAESGCMTLS13(myKey, myIV) decrypter := qtls.AEADAESGCMTLS13(otherKey, otherIV) return newLongHeaderSealer(encrypter, newHeaderProtector(initialSuite, mySecret, true)), newLongHeaderOpener(decrypter, newAESHeaderProtector(initialSuite, otherSecret, true)) } func computeSecrets(connID protocol.ConnectionID, v protocol.VersionNumber) (clientSecret, serverSecret []byte) { initialSecret := hkdf.Extract(crypto.SHA256.New, connID, 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) (key, iv []byte) { key = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic key", 16) iv = hkdfExpandLabel(crypto.SHA256, secret, []byte{}, "quic iv", 12) return } quic-go-0.25.0/internal/handshake/initial_aead_test.go000066400000000000000000000363001417145451600227070ustar00rootroot00000000000000package handshake import ( "fmt" "math/rand" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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})) }) // values taken from the Appendix of the draft Context("using the test vector from the QUIC draft, for old draft version", func() { const version = protocol.VersionDraft29 var connID protocol.ConnectionID BeforeEach(func() { connID = protocol.ConnectionID(splitHexString("0x8394c8f03e515708")) }) It("computes the client key and IV", func() { clientSecret, _ := computeSecrets(connID, version) Expect(clientSecret).To(Equal(splitHexString("0088119288f1d866733ceeed15ff9d50 902cf82952eee27e9d4d4918ea371d87"))) key, iv := computeInitialKeyAndIV(clientSecret) Expect(key).To(Equal(splitHexString("175257a31eb09dea9366d8bb79ad80ba"))) Expect(iv).To(Equal(splitHexString("6b26114b9cba2b63a9e8dd4f"))) }) It("computes the server key and IV", func() { _, serverSecret := computeSecrets(connID, version) Expect(serverSecret).To(Equal(splitHexString("006f881359244dd9ad1acf85f595bad6 7c13f9f5586f5e64e1acae1d9ea8f616"))) key, iv := computeInitialKeyAndIV(serverSecret) Expect(key).To(Equal(splitHexString("149d0b1662ab871fbe63c49b5e655a5d"))) Expect(iv).To(Equal(splitHexString("bab2b12a4c76016ace47856d"))) }) It("encrypts the client's Initial", func() { sealer, _ := NewInitialAEAD(connID, protocol.PerspectiveClient, version) header := splitHexString("c3ff00001d088394c8f03e5157080000449e00000002") data := splitHexString("060040c4010000c003036660261ff947 cea49cce6cfad687f457cf1b14531ba1 4131a0e8f309a1d0b9c4000006130113 031302010000910000000b0009000006 736572766572ff01000100000a001400 12001d00170018001901000101010201 03010400230000003300260024001d00 204cfdfcd178b784bf328cae793b136f 2aedce005ff183d7bb14952072366470 37002b0003020304000d0020001e0403 05030603020308040805080604010501 060102010402050206020202002d0002 0101001c00024001") 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(splitHexString("fb66bc5f93032b7ddd89fe0ff15d9c4f"))) sealer.EncryptHeader(sample, &header[0], header[len(header)-4:]) Expect(header[0]).To(Equal(byte(0xc5))) Expect(header[len(header)-4:]).To(Equal(splitHexString("4a95245b"))) packet := append(header, sealed...) Expect(packet).To(Equal(splitHexString("c5ff00001d088394c8f03e5157080000 449e4a95245bfb66bc5f93032b7ddd89 fe0ff15d9c4f7050fccdb71c1cd80512 d4431643a53aafa1b0b518b44968b18b 8d3e7a4d04c30b3ed9410325b2abb2da fb1c12f8b70479eb8df98abcaf95dd8f 3d1c78660fbc719f88b23c8aef6771f3 d50e10fdfb4c9d92386d44481b6c52d5 9e5538d3d3942de9f13a7f8b702dc317 24180da9df22714d01003fc5e3d165c9 50e630b8540fbd81c9df0ee63f949970 26c4f2e1887a2def79050ac2d86ba318 e0b3adc4c5aa18bcf63c7cf8e85f5692 49813a2236a7e72269447cd1c755e451 f5e77470eb3de64c8849d29282069802 9cfa18e5d66176fe6e5ba4ed18026f90 900a5b4980e2f58e39151d5cd685b109 29636d4f02e7fad2a5a458249f5c0298 a6d53acbe41a7fc83fa7cc01973f7a74 d1237a51974e097636b6203997f921d0 7bc1940a6f2d0de9f5a11432946159ed 6cc21df65c4ddd1115f86427259a196c 7148b25b6478b0dc7766e1c4d1b1f515 9f90eabc61636226244642ee148b464c 9e619ee50a5e3ddc836227cad938987c 4ea3c1fa7c75bbf88d89e9ada642b2b8 8fe8107b7ea375b1b64889a4e9e5c38a 1c896ce275a5658d250e2d76e1ed3a34 ce7e3a3f383d0c996d0bed106c2899ca 6fc263ef0455e74bb6ac1640ea7bfedc 59f03fee0e1725ea150ff4d69a7660c5 542119c71de270ae7c3ecfd1af2c4ce5 51986949cc34a66b3e216bfe18b347e6 c05fd050f85912db303a8f054ec23e38 f44d1c725ab641ae929fecc8e3cefa56 19df4231f5b4c009fa0c0bbc60bc75f7 6d06ef154fc8577077d9d6a1d2bd9bf0 81dc783ece60111bea7da9e5a9748069 d078b2bef48de04cabe3755b197d52b3 2046949ecaa310274b4aac0d008b1948 c1082cdfe2083e386d4fd84c0ed0666d 3ee26c4515c4fee73433ac703b690a9f 7bf278a77486ace44c489a0c7ac8dfe4 d1a58fb3a730b993ff0f0d61b4d89557 831eb4c752ffd39c10f6b9f46d8db278 da624fd800e4af85548a294c1518893a 8778c4f6d6d73c93df200960104e062b 388ea97dcf4016bced7f62b4f062cb6c 04c20693d9a0e3b74ba8fe74cc012378 84f40d765ae56a51688d985cf0ceaef4 3045ed8c3f0c33bced08537f6882613a cd3b08d665fce9dd8aa73171e2d3771a 61dba2790e491d413d93d987e2745af2 9418e428be34941485c93447520ffe23 1da2304d6a0fd5d07d08372202369661 59bef3cf904d722324dd852513df39ae 030d8173908da6364786d3c1bfcb19ea 77a63b25f1e7fc661def480c5d00d444 56269ebd84efd8e3a8b2c257eec76060 682848cbf5194bc99e49ee75e4d0d254 bad4bfd74970c30e44b65511d4ad0e6e c7398e08e01307eeeea14e46ccd87cf3 6b285221254d8fc6a6765c524ded0085 dca5bd688ddf722e2c0faf9d0fb2ce7a 0c3f2cee19ca0ffba461ca8dc5d2c817 8b0762cf67135558494d2a96f1a139f0 edb42d2af89a9c9122b07acbc29e5e72 2df8615c343702491098478a389c9872 a10b0c9875125e257c7bfdf27eef4060 bd3d00f4c14fd3e3496c38d3c5d1a566 8c39350effbc2d16ca17be4ce29f02ed 969504dda2a8c6b9ff919e693ee79e09 089316e7d1d89ec099db3b2b268725d8 88536a4b8bf9aee8fb43e82a4d919d48 43b1ca70a2d8d3f725ead1391377dcc0"))) }) It("encrypt the server's Initial", func() { sealer, _ := NewInitialAEAD(connID, protocol.PerspectiveServer, version) header := splitHexString("c1ff00001d0008f067a5502a4262b50040740001") data := splitHexString("0d0000000018410a020000560303eefc e7f7b37ba1d1632e96677825ddf73988 cfc79825df566dc5430b9a045a120013 0100002e00330024001d00209d3c940d 89690b84d08a60993c144eca684d1081 287c834d5311bcf32bb9da1a002b0002 0304") sealed := sealer.Seal(nil, data, 1, header) sample := sealed[2 : 2+16] Expect(sample).To(Equal(splitHexString("823a5d3a1207c86ee49132824f046524"))) sealer.EncryptHeader(sample, &header[0], header[len(header)-2:]) Expect(header).To(Equal(splitHexString("caff00001d0008f067a5502a4262b5004074aaf2"))) packet := append(header, sealed...) Expect(packet).To(Equal(splitHexString("caff00001d0008f067a5502a4262b500 4074aaf2f007823a5d3a1207c86ee491 32824f0465243d082d868b107a38092b c80528664cbf9456ebf27673fb5fa506 1ab573c9f001b81da028a00d52ab00b1 5bebaa70640e106cf2acd043e9c6b441 1c0a79637134d8993701fe779e58c2fe 753d14b0564021565ea92e57bc6faf56 dfc7a40870e6"))) }) }) // values taken from the Appendix of the draft Context("using the test vector from the QUIC draft, for QUIC v1", func() { const version = protocol.Version1 var connID protocol.ConnectionID BeforeEach(func() { connID = protocol.ConnectionID(splitHexString("0x8394c8f03e515708")) }) It("computes the client key and IV", func() { clientSecret, _ := computeSecrets(connID, version) Expect(clientSecret).To(Equal(splitHexString("c00cf151ca5be075ed0ebfb5c80323c4 2d6b7db67881289af4008f1f6c357aea"))) key, iv := computeInitialKeyAndIV(clientSecret) Expect(key).To(Equal(splitHexString("1f369613dd76d5467730efcbe3b1a22d"))) Expect(iv).To(Equal(splitHexString("fa044b2f42a3fd3b46fb255c"))) }) It("computes the server key and IV", func() { _, serverSecret := computeSecrets(connID, version) Expect(serverSecret).To(Equal(splitHexString("3c199828fd139efd216c155ad844cc81 fb82fa8d7446fa7d78be803acdda951b"))) key, iv := computeInitialKeyAndIV(serverSecret) Expect(key).To(Equal(splitHexString("cf3a5331653c364c88f0f379b6067e37"))) Expect(iv).To(Equal(splitHexString("0ac1493ca1905853b0bba03e"))) }) It("encrypts the client's Initial", func() { sealer, _ := NewInitialAEAD(connID, protocol.PerspectiveClient, version) header := splitHexString("c300000001088394c8f03e5157080000449e00000002") data := splitHexString("060040f1010000ed0303ebf8fa56f129 39b9584a3896472ec40bb863cfd3e868 04fe3a47f06a2b69484c000004130113 02010000c000000010000e00000b6578 616d706c652e636f6dff01000100000a 00080006001d00170018001000070005 04616c706e0005000501000000000033 00260024001d00209370b2c9caa47fba baf4559fedba753de171fa71f50f1ce1 5d43e994ec74d748002b000302030400 0d0010000e0403050306030203080408 050806002d00020101001c0002400100 3900320408ffffffffffffffff050480 00ffff07048000ffff08011001048000 75300901100f088394c8f03e51570806 048000ffff") 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(splitHexString("d1b1c98dd7689fb8ec11d242b123dc9b"))) sealer.EncryptHeader(sample, &header[0], header[len(header)-4:]) Expect(header[0]).To(Equal(byte(0xc0))) Expect(header[len(header)-4:]).To(Equal(splitHexString("7b9aec34"))) packet := append(header, sealed...) Expect(packet).To(Equal(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"))) }) It("encrypt the server's Initial", func() { sealer, _ := NewInitialAEAD(connID, protocol.PerspectiveServer, version) header := splitHexString("c1000000010008f067a5502a4262b50040750001") data := splitHexString("02000000000600405a020000560303ee fce7f7b37ba1d1632e96677825ddf739 88cfc79825df566dc5430b9a045a1200 130100002e00330024001d00209d3c94 0d89690b84d08a60993c144eca684d10 81287c834d5311bcf32bb9da1a002b00 020304") sealed := sealer.Seal(nil, data, 1, header) sample := sealed[2 : 2+16] Expect(sample).To(Equal(splitHexString("2cd0991cd25b0aac406a5816b6394100"))) sealer.EncryptHeader(sample, &header[0], header[len(header)-2:]) Expect(header).To(Equal(splitHexString("cf000000010008f067a5502a4262b5004075c0d9"))) packet := append(header, sealed...) Expect(packet).To(Equal(splitHexString("cf000000010008f067a5502a4262b500 4075c0d95a482cd0991cd25b0aac406a 5816b6394100f37a1c69797554780bb3 8cc5a99f5ede4cf73c3ec2493a1839b3 dbcba3f6ea46c5b7684df3548e7ddeb9 c3bf9c73cc3f3bded74b562bfb19fb84 022f8ef4cdd93795d77d06edbb7aaf2f 58891850abbdca3d20398c276456cbc4 2158407dd074ee"))) }) }) for _, ver := range []protocol.VersionNumber{protocol.VersionDraft29, protocol.Version1} { v := ver Context(fmt.Sprintf("using version %s", v), func() { It("seals and opens", func() { connectionID := protocol.ConnectionID{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.ConnectionID{0, 0, 0, 0, 0, 0, 0, 1} c2 := protocol.ConnectionID{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.ConnectionID{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})) }) }) } }) quic-go-0.25.0/internal/handshake/interface.go000066400000000000000000000066701417145451600212140ustar00rootroot00000000000000package handshake import ( "errors" "io" "net" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" "github.com/lucas-clemente/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") ) // ConnectionState contains information about the state of the connection. type ConnectionState = qtls.ConnectionState 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 } // A tlsExtensionHandler sends and received the QUIC TLS extension. type tlsExtensionHandler interface { GetExtensions(msgType uint8) []qtls.Extension ReceivedExtensions(msgType uint8, exts []qtls.Extension) TransportParameters() <-chan []byte } type handshakeRunner interface { OnReceivedParams(*wire.TransportParameters) OnHandshakeComplete() OnError(error) DropKeys(protocol.EncryptionLevel) } // CryptoSetup handles the handshake and protecting / unprotecting packets type CryptoSetup interface { RunHandshake() io.Closer ChangeConnectionID(protocol.ConnectionID) GetSessionTicket() ([]byte, error) HandleMessage([]byte, protocol.EncryptionLevel) bool SetLargest1RTTAcked(protocol.PacketNumber) error 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) } // ConnWithVersion is the connection used in the ClientHelloInfo. // It can be used to determine the QUIC version in use. type ConnWithVersion interface { net.Conn GetQUICVersion() protocol.VersionNumber } quic-go-0.25.0/internal/handshake/mock_handshake_runner_test.go000066400000000000000000000056061417145451600246410ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: interface.go // Package handshake is a generated GoMock package. package handshake import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // MockHandshakeRunner is a mock of HandshakeRunner interface. type MockHandshakeRunner struct { ctrl *gomock.Controller recorder *MockHandshakeRunnerMockRecorder } // MockHandshakeRunnerMockRecorder is the mock recorder for MockHandshakeRunner. type MockHandshakeRunnerMockRecorder struct { mock *MockHandshakeRunner } // NewMockHandshakeRunner creates a new mock instance. func NewMockHandshakeRunner(ctrl *gomock.Controller) *MockHandshakeRunner { mock := &MockHandshakeRunner{ctrl: ctrl} mock.recorder = &MockHandshakeRunnerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockHandshakeRunner) EXPECT() *MockHandshakeRunnerMockRecorder { return m.recorder } // DropKeys mocks base method. func (m *MockHandshakeRunner) DropKeys(arg0 protocol.EncryptionLevel) { m.ctrl.T.Helper() m.ctrl.Call(m, "DropKeys", arg0) } // DropKeys indicates an expected call of DropKeys. func (mr *MockHandshakeRunnerMockRecorder) DropKeys(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropKeys", reflect.TypeOf((*MockHandshakeRunner)(nil).DropKeys), arg0) } // OnError mocks base method. func (m *MockHandshakeRunner) OnError(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnError", arg0) } // OnError indicates an expected call of OnError. func (mr *MockHandshakeRunnerMockRecorder) OnError(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnError", reflect.TypeOf((*MockHandshakeRunner)(nil).OnError), arg0) } // OnHandshakeComplete mocks base method. func (m *MockHandshakeRunner) OnHandshakeComplete() { m.ctrl.T.Helper() m.ctrl.Call(m, "OnHandshakeComplete") } // OnHandshakeComplete indicates an expected call of OnHandshakeComplete. func (mr *MockHandshakeRunnerMockRecorder) OnHandshakeComplete() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnHandshakeComplete", reflect.TypeOf((*MockHandshakeRunner)(nil).OnHandshakeComplete)) } // OnReceivedParams mocks base method. func (m *MockHandshakeRunner) OnReceivedParams(arg0 *wire.TransportParameters) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnReceivedParams", arg0) } // OnReceivedParams indicates an expected call of OnReceivedParams. func (mr *MockHandshakeRunnerMockRecorder) OnReceivedParams(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnReceivedParams", reflect.TypeOf((*MockHandshakeRunner)(nil).OnReceivedParams), arg0) } quic-go-0.25.0/internal/handshake/mockgen.go000066400000000000000000000002571417145451600206720ustar00rootroot00000000000000package handshake //go:generate sh -c "../../mockgen_private.sh handshake mock_handshake_runner_test.go github.com/lucas-clemente/quic-go/internal/handshake handshakeRunner" quic-go-0.25.0/internal/handshake/retry.go000066400000000000000000000032671417145451600204200ustar00rootroot00000000000000package handshake import ( "bytes" "crypto/aes" "crypto/cipher" "fmt" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" ) var ( oldRetryAEAD cipher.AEAD // used for QUIC draft versions up to 34 retryAEAD cipher.AEAD // used for QUIC draft-34 ) func init() { oldRetryAEAD = initAEAD([16]byte{0xcc, 0xce, 0x18, 0x7e, 0xd0, 0x9a, 0x09, 0xd0, 0x57, 0x28, 0x15, 0x5a, 0x6c, 0xb9, 0x6b, 0xe1}) retryAEAD = initAEAD([16]byte{0xbe, 0x0c, 0x69, 0x0b, 0x9f, 0x66, 0x57, 0x5a, 0x1d, 0x76, 0x6b, 0x54, 0xe3, 0x68, 0xc8, 0x4e}) } 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 oldRetryNonce = [12]byte{0xe5, 0x49, 0x30, 0xf9, 0x7f, 0x21, 0x36, 0xf0, 0x53, 0x0a, 0x8c, 0x1c} retryNonce = [12]byte{0x46, 0x15, 0x99, 0xd3, 0x5d, 0x63, 0x2b, 0xf2, 0x23, 0x98, 0x25, 0xbb} ) // GetRetryIntegrityTag calculates the integrity tag on a Retry packet func GetRetryIntegrityTag(retry []byte, origDestConnID protocol.ConnectionID, version protocol.VersionNumber) *[16]byte { retryMutex.Lock() retryBuf.WriteByte(uint8(origDestConnID.Len())) retryBuf.Write(origDestConnID.Bytes()) retryBuf.Write(retry) var tag [16]byte var sealed []byte if version != protocol.Version1 { sealed = oldRetryAEAD.Seal(tag[:0], oldRetryNonce[:], nil, retryBuf.Bytes()) } else { sealed = retryAEAD.Seal(tag[:0], retryNonce[:], nil, retryBuf.Bytes()) } if len(sealed) != 16 { panic(fmt.Sprintf("unexpected Retry integrity tag length: %d", len(sealed))) } retryBuf.Reset() retryMutex.Unlock() return &tag } quic-go-0.25.0/internal/handshake/retry_test.go000066400000000000000000000031221417145451600214450ustar00rootroot00000000000000package handshake import ( "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Retry Integrity Check", func() { It("calculates retry integrity tags", func() { fooTag := GetRetryIntegrityTag([]byte("foo"), protocol.ConnectionID{1, 2, 3, 4}, protocol.VersionDraft29) barTag := GetRetryIntegrityTag([]byte("bar"), protocol.ConnectionID{1, 2, 3, 4}, protocol.VersionDraft29) Expect(fooTag).ToNot(BeNil()) Expect(barTag).ToNot(BeNil()) Expect(*fooTag).ToNot(Equal(*barTag)) }) It("includes the original connection ID in the tag calculation", func() { t1 := GetRetryIntegrityTag([]byte("foobar"), protocol.ConnectionID{1, 2, 3, 4}, protocol.Version1) t2 := GetRetryIntegrityTag([]byte("foobar"), protocol.ConnectionID{4, 3, 2, 1}, protocol.Version1) Expect(*t1).ToNot(Equal(*t2)) }) It("uses the test vector from the draft, for old draft versions", func() { connID := protocol.ConnectionID(splitHexString("0x8394c8f03e515708")) data := splitHexString("ffff00001d0008f067a5502a4262b574 6f6b656ed16926d81f6f9ca2953a8aa4 575e1e49") Expect(GetRetryIntegrityTag(data[:len(data)-16], connID, protocol.VersionDraft29)[:]).To(Equal(data[len(data)-16:])) }) It("uses the test vector from the draft, for version 1", func() { connID := protocol.ConnectionID(splitHexString("0x8394c8f03e515708")) data := splitHexString("ff000000010008f067a5502a4262b574 6f6b656e04a265ba2eff4d829058fb3f 0f2496ba") Expect(GetRetryIntegrityTag(data[:len(data)-16], connID, protocol.Version1)[:]).To(Equal(data[len(data)-16:])) }) }) quic-go-0.25.0/internal/handshake/session_ticket.go000066400000000000000000000022621417145451600222730ustar00rootroot00000000000000package handshake import ( "bytes" "errors" "fmt" "time" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/quicvarint" ) const sessionTicketRevision = 2 type sessionTicket struct { Parameters *wire.TransportParameters RTT time.Duration // to be encoded in mus } func (t *sessionTicket) Marshal() []byte { b := &bytes.Buffer{} quicvarint.Write(b, sessionTicketRevision) quicvarint.Write(b, uint64(t.RTT.Microseconds())) t.Parameters.MarshalForSessionTicket(b) return b.Bytes() } func (t *sessionTicket) Unmarshal(b []byte) error { r := bytes.NewReader(b) rev, err := quicvarint.Read(r) if err != nil { return errors.New("failed to read session ticket revision") } if rev != sessionTicketRevision { return fmt.Errorf("unknown session ticket revision: %d", rev) } rtt, err := quicvarint.Read(r) if err != nil { return errors.New("failed to read RTT") } var tp wire.TransportParameters if err := tp.UnmarshalFromSessionTicket(r); err != nil { return fmt.Errorf("unmarshaling transport parameters from session ticket failed: %s", err.Error()) } t.Parameters = &tp t.RTT = time.Duration(rtt) * time.Microsecond return nil } quic-go-0.25.0/internal/handshake/session_ticket_test.go000066400000000000000000000034101417145451600233260ustar00rootroot00000000000000package handshake import ( "bytes" "time" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Session Ticket", func() { It("marshals and unmarshals a session ticket", func() { ticket := &sessionTicket{ Parameters: &wire.TransportParameters{ InitialMaxStreamDataBidiLocal: 1, InitialMaxStreamDataBidiRemote: 2, }, RTT: 1337 * time.Microsecond, } var t sessionTicket Expect(t.Unmarshal(ticket.Marshal())).To(Succeed()) Expect(t.Parameters.InitialMaxStreamDataBidiLocal).To(BeEquivalentTo(1)) Expect(t.Parameters.InitialMaxStreamDataBidiRemote).To(BeEquivalentTo(2)) Expect(t.RTT).To(Equal(1337 * time.Microsecond)) }) It("refuses to unmarshal if the ticket is too short for the revision", func() { Expect((&sessionTicket{}).Unmarshal([]byte{})).To(MatchError("failed to read session ticket revision")) }) It("refuses to unmarshal if the revision doesn't match", func() { b := &bytes.Buffer{} quicvarint.Write(b, 1337) Expect((&sessionTicket{}).Unmarshal(b.Bytes())).To(MatchError("unknown session ticket revision: 1337")) }) It("refuses to unmarshal if the RTT cannot be read", func() { b := &bytes.Buffer{} quicvarint.Write(b, sessionTicketRevision) Expect((&sessionTicket{}).Unmarshal(b.Bytes())).To(MatchError("failed to read RTT")) }) It("refuses to unmarshal if unmarshaling the transport parameters fails", func() { b := &bytes.Buffer{} quicvarint.Write(b, sessionTicketRevision) b.Write([]byte("foobar")) err := (&sessionTicket{}).Unmarshal(b.Bytes()) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("unmarshaling transport parameters from session ticket failed")) }) }) quic-go-0.25.0/internal/handshake/tls_extension_handler.go000066400000000000000000000032641417145451600236430ustar00rootroot00000000000000package handshake import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" ) const ( quicTLSExtensionTypeOldDrafts = 0xffa5 quicTLSExtensionType = 0x39 ) type extensionHandler struct { ourParams []byte paramsChan chan []byte extensionType uint16 perspective protocol.Perspective } var _ tlsExtensionHandler = &extensionHandler{} // newExtensionHandler creates a new extension handler func newExtensionHandler(params []byte, pers protocol.Perspective, v protocol.VersionNumber) tlsExtensionHandler { et := uint16(quicTLSExtensionType) if v != protocol.Version1 { et = quicTLSExtensionTypeOldDrafts } return &extensionHandler{ ourParams: params, paramsChan: make(chan []byte), perspective: pers, extensionType: et, } } func (h *extensionHandler) GetExtensions(msgType uint8) []qtls.Extension { if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeClientHello) || (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeEncryptedExtensions) { return nil } return []qtls.Extension{{ Type: h.extensionType, Data: h.ourParams, }} } func (h *extensionHandler) ReceivedExtensions(msgType uint8, exts []qtls.Extension) { if (h.perspective == protocol.PerspectiveClient && messageType(msgType) != typeEncryptedExtensions) || (h.perspective == protocol.PerspectiveServer && messageType(msgType) != typeClientHello) { return } var data []byte for _, ext := range exts { if ext.Type == h.extensionType { data = ext.Data break } } h.paramsChan <- data } func (h *extensionHandler) TransportParameters() <-chan []byte { return h.paramsChan } quic-go-0.25.0/internal/handshake/tls_extension_handler_test.go000066400000000000000000000135471417145451600247070ustar00rootroot00000000000000package handshake import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qtls" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("TLS Extension Handler, for the server", func() { var ( handlerServer tlsExtensionHandler handlerClient tlsExtensionHandler version protocol.VersionNumber ) BeforeEach(func() { version = protocol.VersionDraft29 }) JustBeforeEach(func() { handlerServer = newExtensionHandler( []byte("foobar"), protocol.PerspectiveServer, version, ) handlerClient = newExtensionHandler( []byte("raboof"), protocol.PerspectiveClient, version, ) }) Context("for the server", func() { for _, ver := range []protocol.VersionNumber{protocol.VersionDraft29, protocol.Version1} { v := ver Context(fmt.Sprintf("sending, for version %s", v), func() { var extensionType uint16 BeforeEach(func() { version = v if v == protocol.VersionDraft29 { extensionType = quicTLSExtensionTypeOldDrafts } else { extensionType = quicTLSExtensionType } }) It("only adds TransportParameters for the Encrypted Extensions", func() { // test 2 other handshake types Expect(handlerServer.GetExtensions(uint8(typeCertificate))).To(BeEmpty()) Expect(handlerServer.GetExtensions(uint8(typeFinished))).To(BeEmpty()) }) It("adds TransportParameters to the EncryptedExtensions message", func() { exts := handlerServer.GetExtensions(uint8(typeEncryptedExtensions)) Expect(exts).To(HaveLen(1)) Expect(exts[0].Type).To(BeEquivalentTo(extensionType)) Expect(exts[0].Data).To(Equal([]byte("foobar"))) }) }) } Context("receiving", func() { var chExts []qtls.Extension JustBeforeEach(func() { chExts = handlerClient.GetExtensions(uint8(typeClientHello)) Expect(chExts).To(HaveLen(1)) }) It("sends the extension on the channel", func() { go func() { defer GinkgoRecover() handlerServer.ReceivedExtensions(uint8(typeClientHello), chExts) }() var data []byte Eventually(handlerServer.TransportParameters()).Should(Receive(&data)) Expect(data).To(Equal([]byte("raboof"))) }) It("sends nil on the channel if the extension is missing", func() { go func() { defer GinkgoRecover() handlerServer.ReceivedExtensions(uint8(typeClientHello), nil) }() var data []byte Eventually(handlerServer.TransportParameters()).Should(Receive(&data)) Expect(data).To(BeEmpty()) }) It("ignores extensions with different code points", func() { go func() { defer GinkgoRecover() exts := []qtls.Extension{{Type: 0x1337, Data: []byte("invalid")}} handlerServer.ReceivedExtensions(uint8(typeClientHello), exts) }() var data []byte Eventually(handlerServer.TransportParameters()).Should(Receive()) Expect(data).To(BeEmpty()) }) It("ignores extensions that are not sent with the ClientHello", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() handlerServer.ReceivedExtensions(uint8(typeFinished), chExts) close(done) }() Consistently(handlerServer.TransportParameters()).ShouldNot(Receive()) Eventually(done).Should(BeClosed()) }) }) }) Context("for the client", func() { for _, ver := range []protocol.VersionNumber{protocol.VersionDraft29, protocol.Version1} { v := ver Context(fmt.Sprintf("sending, for version %s", v), func() { var extensionType uint16 BeforeEach(func() { version = v if v == protocol.VersionDraft29 { extensionType = quicTLSExtensionTypeOldDrafts } else { extensionType = quicTLSExtensionType } }) It("only adds TransportParameters for the Encrypted Extensions", func() { // test 2 other handshake types Expect(handlerClient.GetExtensions(uint8(typeCertificate))).To(BeEmpty()) Expect(handlerClient.GetExtensions(uint8(typeFinished))).To(BeEmpty()) }) It("adds TransportParameters to the ClientHello message", func() { exts := handlerClient.GetExtensions(uint8(typeClientHello)) Expect(exts).To(HaveLen(1)) Expect(exts[0].Type).To(BeEquivalentTo(extensionType)) Expect(exts[0].Data).To(Equal([]byte("raboof"))) }) }) } Context("receiving", func() { var chExts []qtls.Extension JustBeforeEach(func() { chExts = handlerServer.GetExtensions(uint8(typeEncryptedExtensions)) Expect(chExts).To(HaveLen(1)) }) It("sends the extension on the channel", func() { go func() { defer GinkgoRecover() handlerClient.ReceivedExtensions(uint8(typeEncryptedExtensions), chExts) }() var data []byte Eventually(handlerClient.TransportParameters()).Should(Receive(&data)) Expect(data).To(Equal([]byte("foobar"))) }) It("sends nil on the channel if the extension is missing", func() { go func() { defer GinkgoRecover() handlerClient.ReceivedExtensions(uint8(typeEncryptedExtensions), nil) }() var data []byte Eventually(handlerClient.TransportParameters()).Should(Receive(&data)) Expect(data).To(BeEmpty()) }) It("ignores extensions with different code points", func() { go func() { defer GinkgoRecover() exts := []qtls.Extension{{Type: 0x1337, Data: []byte("invalid")}} handlerClient.ReceivedExtensions(uint8(typeEncryptedExtensions), exts) }() var data []byte Eventually(handlerClient.TransportParameters()).Should(Receive()) Expect(data).To(BeEmpty()) }) It("ignores extensions that are not sent with the EncryptedExtensions", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() handlerClient.ReceivedExtensions(uint8(typeFinished), chExts) close(done) }() Consistently(handlerClient.TransportParameters()).ShouldNot(Receive()) Eventually(done).Should(BeClosed()) }) }) }) }) quic-go-0.25.0/internal/handshake/token_generator.go000066400000000000000000000067711417145451600224440ustar00rootroot00000000000000package handshake import ( "encoding/asn1" "fmt" "io" "net" "time" "github.com/lucas-clemente/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 RemoteAddr string SentTime time.Time // only set for retry tokens OriginalDestConnectionID protocol.ConnectionID RetrySrcConnectionID protocol.ConnectionID } // 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 TookenGenerator func NewTokenGenerator(rand io.Reader) (*TokenGenerator, error) { tokenProtector, err := newTokenProtector(rand) if err != nil { return nil, err } return &TokenGenerator{ tokenProtector: tokenProtector, }, nil } // 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, RetrySrcConnectionID: retrySrcConnID, 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, RemoteAddr: decodeRemoteAddr(t.RemoteAddr), SentTime: time.Unix(0, t.Timestamp), } if t.IsRetryToken { token.OriginalDestConnectionID = protocol.ConnectionID(t.OriginalDestConnectionID) token.RetrySrcConnectionID = protocol.ConnectionID(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())...) } // decodeRemoteAddr decodes the remote address saved in the token func decodeRemoteAddr(data []byte) string { // data will never be empty for a token that we generated. // Check it to be on the safe side if len(data) == 0 { return "" } if data[0] == tokenPrefixIP { return net.IP(data[1:]).String() } return string(data[1:]) } quic-go-0.25.0/internal/handshake/token_generator_test.go000066400000000000000000000100671417145451600234740ustar00rootroot00000000000000package handshake import ( "crypto/rand" "encoding/asn1" "net" "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Token Generator", func() { var tokenGen *TokenGenerator BeforeEach(func() { var err error tokenGen, err = NewTokenGenerator(rand.Reader) Expect(err).ToNot(HaveOccurred()) }) It("generates a token", func() { ip := net.IPv4(127, 0, 0, 1) token, err := tokenGen.NewRetryToken(&net.UDPAddr{IP: ip, Port: 1337}, nil, nil) 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() { ip := net.IPv4(192, 168, 0, 1) tokenEnc, err := tokenGen.NewRetryToken( &net.UDPAddr{IP: ip, Port: 1337}, nil, nil, ) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.RemoteAddr).To(Equal("192.168.0.1")) 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() { tokenEnc, err := tokenGen.NewRetryToken( &net.UDPAddr{}, protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}, ) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.OriginalDestConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef})) Expect(token.RetrySrcConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde})) }) 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, nil, nil) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.RemoteAddr).To(Equal(ip.String())) 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, nil, nil) Expect(err).ToNot(HaveOccurred()) token, err := tokenGen.DecodeToken(tokenEnc) Expect(err).ToNot(HaveOccurred()) Expect(token.RemoteAddr).To(Equal("192.168.13.37:1337")) Expect(token.SentTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond)) }) }) quic-go-0.25.0/internal/handshake/token_protector.go000066400000000000000000000041741417145451600224720ustar00rootroot00000000000000package handshake import ( "crypto/aes" "crypto/cipher" "crypto/sha256" "fmt" "io" "golang.org/x/crypto/hkdf" ) // TokenProtector is used to create and verify a token type tokenProtector interface { // NewToken creates a new token NewToken([]byte) ([]byte, error) // DecodeToken decodes a token DecodeToken([]byte) ([]byte, error) } const ( tokenSecretSize = 32 tokenNonceSize = 32 ) // tokenProtector is used to create and verify a token type tokenProtectorImpl struct { rand io.Reader secret []byte } // newTokenProtector creates a source for source address tokens func newTokenProtector(rand io.Reader) (tokenProtector, error) { secret := make([]byte, tokenSecretSize) if _, err := rand.Read(secret); err != nil { return nil, err } return &tokenProtectorImpl{ rand: rand, secret: secret, }, nil } // NewToken encodes data into a new token. func (s *tokenProtectorImpl) NewToken(data []byte) ([]byte, error) { nonce := make([]byte, tokenNonceSize) if _, err := s.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 *tokenProtectorImpl) 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 *tokenProtectorImpl) createAEAD(nonce []byte) (cipher.AEAD, []byte, error) { h := hkdf.New(sha256.New, s.secret, 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 } quic-go-0.25.0/internal/handshake/token_protector_test.go000066400000000000000000000033721417145451600235300ustar00rootroot00000000000000package handshake import ( "crypto/rand" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type zeroReader struct{} func (r *zeroReader) Read(b []byte) (int, error) { for i := range b { b[i] = 0 } return len(b), nil } var _ = Describe("Token Protector", func() { var tp tokenProtector BeforeEach(func() { var err error tp, err = newTokenProtector(rand.Reader) Expect(err).ToNot(HaveOccurred()) }) It("uses the random source", func() { tp1, err := newTokenProtector(&zeroReader{}) Expect(err).ToNot(HaveOccurred()) tp2, err := newTokenProtector(&zeroReader{}) Expect(err).ToNot(HaveOccurred()) t1, err := tp1.NewToken([]byte("foo")) Expect(err).ToNot(HaveOccurred()) t2, err := tp2.NewToken([]byte("foo")) Expect(err).ToNot(HaveOccurred()) Expect(t1).To(Equal(t2)) tp3, err := newTokenProtector(rand.Reader) Expect(err).ToNot(HaveOccurred()) t3, err := tp3.NewToken([]byte("foo")) Expect(err).ToNot(HaveOccurred()) Expect(t3).ToNot(Equal(t1)) }) 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("fails deconding invalid tokens", func() { token, err := tp.NewToken([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) token = token[1:] // remove the first byte _, err = tp.DecodeToken(token) 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")) }) }) quic-go-0.25.0/internal/handshake/updatable_aead.go000066400000000000000000000257241417145451600221700ustar00rootroot00000000000000package handshake import ( "crypto" "crypto/cipher" "crypto/tls" "encoding/binary" "fmt" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/qtls" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/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 type updatableAEAD struct { suite *qtls.CipherSuiteTLS13 keyPhase protocol.KeyPhase largestAcked protocol.PacketNumber firstPacketNumber protocol.PacketNumber handshakeConfirmed bool keyUpdateInterval uint64 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 // 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) *updatableAEAD { return &updatableAEAD{ firstPacketNumber: protocol.InvalidPacketNumber, largestAcked: protocol.InvalidPacketNumber, firstRcvdWithCurrentKey: protocol.InvalidPacketNumber, firstSentWithCurrentKey: protocol.InvalidPacketNumber, keyUpdateInterval: KeyUpdateInterval, rttStats: rttStats, tracer: tracer, logger: logger, } } 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(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.nextSendAEAD = createAEAD(a.suite, a.nextSendTrafficSecret) } 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()) } // For the client, this function is called before SetWriteKey. // For the server, this function is called after SetWriteKey. func (a *updatableAEAD) SetReadKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { a.rcvAEAD = createAEAD(suite, trafficSecret) a.headerDecrypter = newHeaderProtector(suite, trafficSecret, false) if a.suite == nil { a.setAEADParameters(a.rcvAEAD, suite) } a.nextRcvTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) a.nextRcvAEAD = createAEAD(suite, a.nextRcvTrafficSecret) } // For the client, this function is called after SetReadKey. // For the server, this function is called before SetWriteKey. func (a *updatableAEAD) SetWriteKey(suite *qtls.CipherSuiteTLS13, trafficSecret []byte) { a.sendAEAD = createAEAD(suite, trafficSecret) a.headerEncrypter = newHeaderProtector(suite, trafficSecret, false) if a.suite == nil { a.setAEADParameters(a.sendAEAD, suite) } a.nextSendTrafficSecret = a.getNextTrafficSecret(suite.Hash, trafficSecret) a.nextSendAEAD = createAEAD(suite, a.nextSendTrafficSecret) } func (a *updatableAEAD) setAEADParameters(aead cipher.AEAD, suite *qtls.CipherSuiteTLS13) { 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 = utils.MaxPacketNumber(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(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(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 } if a.numRcvdWithCurrentKey >= a.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 >= a.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(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 } quic-go-0.25.0/internal/handshake/updatable_aead_test.go000066400000000000000000000567271417145451600232360ustar00rootroot00000000000000package handshake import ( "crypto/rand" "crypto/tls" "fmt" "time" "github.com/golang/mock/gomock" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Updatable AEAD", func() { It("ChaCha test vector from the draft", func() { secret := splitHexString("9ac312a7f877468ebe69422748ad00a1 5443f18203a07d6060f688f30f21632b") aead := newUpdatableAEAD(&utils.RTTStats{}, nil, nil) chacha := cipherSuites[2] Expect(chacha.ID).To(Equal(tls.TLS_CHACHA20_POLY1305_SHA256)) aead.SetWriteKey(chacha, secret) header := splitHexString("4200bff4") const pnOffset = 1 payloadOffset := len(header) plaintext := splitHexString("01") payload := aead.Seal(nil, plaintext, 654360564, header) Expect(payload).To(Equal(splitHexString("655e5cd55c41f69080575d7999c25a5bfb"))) packet := append(header, payload...) aead.EncryptHeader(packet[pnOffset+4:pnOffset+4+16], &packet[0], packet[pnOffset:payloadOffset]) Expect(packet).To(Equal(splitHexString("4cfe4189655e5cd55c41f69080575d7999c25a5bfb"))) }) 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() { 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) server = newUpdatableAEAD(rttStats, serverTracer, utils.DefaultLogger) 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 keyUpdateInterval = 20 BeforeEach(func() { Expect(server.keyUpdateInterval).To(BeEquivalentTo(protocol.KeyUpdateInterval)) server.keyUpdateInterval = keyUpdateInterval server.SetHandshakeConfirmed() }) It("initiates a key update after sealing the maximum number of packets, for the first update", func() { for i := 0; i < keyUpdateInterval; 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 < keyUpdateInterval; 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 = keyUpdateInterval + 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 < keyUpdateInterval; 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 < keyUpdateInterval; 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 < keyUpdateInterval; 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 < keyUpdateInterval; 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 < keyUpdateInterval; 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 < keyUpdateInterval; 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()) }) }) }) }) }) } }) quic-go-0.25.0/internal/logutils/000077500000000000000000000000001417145451600166305ustar00rootroot00000000000000quic-go-0.25.0/internal/logutils/frame.go000066400000000000000000000016001417145451600202460ustar00rootroot00000000000000package logutils import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/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 ConvertFrame(frame wire.Frame) logging.Frame { switch f := frame.(type) { 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) } } quic-go-0.25.0/internal/logutils/frame_test.go000066400000000000000000000031111417145451600213040ustar00rootroot00000000000000package logutils import ( "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("CRYPTO frame", func() { It("converts CRYPTO frames", func() { f := ConvertFrame(&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 := ConvertFrame(&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 := ConvertFrame(&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 := ConvertFrame(&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))) }) }) quic-go-0.25.0/internal/logutils/logutils_suite_test.go000066400000000000000000000003001417145451600232620ustar00rootroot00000000000000package logutils import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestLogutils(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Logutils Suite") } quic-go-0.25.0/internal/mocks/000077500000000000000000000000001417145451600161025ustar00rootroot00000000000000quic-go-0.25.0/internal/mocks/ackhandler/000077500000000000000000000000001417145451600201765ustar00rootroot00000000000000quic-go-0.25.0/internal/mocks/ackhandler/received_packet_handler.go000066400000000000000000000102641417145451600253420ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/ackhandler (interfaces: ReceivedPacketHandler) // Package mockackhandler is a generated GoMock package. package mockackhandler import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropPackets", reflect.TypeOf((*MockReceivedPacketHandler)(nil).DropPackets), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckFrame", reflect.TypeOf((*MockReceivedPacketHandler)(nil).GetAckFrame), arg0, arg1) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAlarmTimeout", reflect.TypeOf((*MockReceivedPacketHandler)(nil).GetAlarmTimeout)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsPotentiallyDuplicate", reflect.TypeOf((*MockReceivedPacketHandler)(nil).IsPotentiallyDuplicate), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedPacket", reflect.TypeOf((*MockReceivedPacketHandler)(nil).ReceivedPacket), arg0, arg1, arg2, arg3, arg4) } quic-go-0.25.0/internal/mocks/ackhandler/sent_packet_handler.go000066400000000000000000000222401417145451600245220ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/ackhandler (interfaces: SentPacketHandler) // Package mockackhandler is a generated GoMock package. package mockackhandler import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" ackhandler "github.com/lucas-clemente/quic-go/internal/ackhandler" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DropPackets", reflect.TypeOf((*MockSentPacketHandler)(nil).DropPackets), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLossDetectionTimeout", reflect.TypeOf((*MockSentPacketHandler)(nil).GetLossDetectionTimeout)) } // HasPacingBudget mocks base method. func (m *MockSentPacketHandler) HasPacingBudget() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasPacingBudget") ret0, _ := ret[0].(bool) return ret0 } // HasPacingBudget indicates an expected call of HasPacingBudget. func (mr *MockSentPacketHandlerMockRecorder) HasPacingBudget() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPacingBudget", reflect.TypeOf((*MockSentPacketHandler)(nil).HasPacingBudget)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnLossDetectionTimeout", reflect.TypeOf((*MockSentPacketHandler)(nil).OnLossDetectionTimeout)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeekPacketNumber", reflect.TypeOf((*MockSentPacketHandler)(nil).PeekPacketNumber), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopPacketNumber", reflect.TypeOf((*MockSentPacketHandler)(nil).PopPacketNumber), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "QueueProbePacket", reflect.TypeOf((*MockSentPacketHandler)(nil).QueueProbePacket), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedAck", reflect.TypeOf((*MockSentPacketHandler)(nil).ReceivedAck), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedBytes", reflect.TypeOf((*MockSentPacketHandler)(nil).ReceivedBytes), arg0) } // ResetForRetry mocks base method. func (m *MockSentPacketHandler) ResetForRetry() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ResetForRetry") ret0, _ := ret[0].(error) return ret0 } // ResetForRetry indicates an expected call of ResetForRetry. func (mr *MockSentPacketHandlerMockRecorder) ResetForRetry() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetForRetry", reflect.TypeOf((*MockSentPacketHandler)(nil).ResetForRetry)) } // SendMode mocks base method. func (m *MockSentPacketHandler) SendMode() ackhandler.SendMode { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendMode") ret0, _ := ret[0].(ackhandler.SendMode) return ret0 } // SendMode indicates an expected call of SendMode. func (mr *MockSentPacketHandlerMockRecorder) SendMode() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMode", reflect.TypeOf((*MockSentPacketHandler)(nil).SendMode)) } // SentPacket mocks base method. func (m *MockSentPacketHandler) SentPacket(arg0 *ackhandler.Packet) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentPacket", arg0) } // SentPacket indicates an expected call of SentPacket. func (mr *MockSentPacketHandlerMockRecorder) SentPacket(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockSentPacketHandler)(nil).SentPacket), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHandshakeConfirmed", reflect.TypeOf((*MockSentPacketHandler)(nil).SetHandshakeConfirmed)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxDatagramSize", reflect.TypeOf((*MockSentPacketHandler)(nil).SetMaxDatagramSize), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeUntilSend", reflect.TypeOf((*MockSentPacketHandler)(nil).TimeUntilSend)) } quic-go-0.25.0/internal/mocks/congestion.go000066400000000000000000000175761417145451600206210ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/congestion (interfaces: SendAlgorithmWithDebugInfos) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanSend", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).CanSend), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCongestionWindow", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).GetCongestionWindow)) } // HasPacingBudget mocks base method. func (m *MockSendAlgorithmWithDebugInfos) HasPacingBudget() bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HasPacingBudget") ret0, _ := ret[0].(bool) return ret0 } // HasPacingBudget indicates an expected call of HasPacingBudget. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) HasPacingBudget() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasPacingBudget", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).HasPacingBudget)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InRecovery", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).InRecovery)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InSlowStart", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).InSlowStart)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybeExitSlowStart", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).MaybeExitSlowStart)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPacketAcked", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnPacketAcked), arg0, arg1, arg2, arg3) } // OnPacketLost mocks base method. func (m *MockSendAlgorithmWithDebugInfos) OnPacketLost(arg0 protocol.PacketNumber, arg1, arg2 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "OnPacketLost", arg0, arg1, arg2) } // OnPacketLost indicates an expected call of OnPacketLost. func (mr *MockSendAlgorithmWithDebugInfosMockRecorder) OnPacketLost(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPacketLost", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnPacketLost), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnPacketSent", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnPacketSent), arg0, arg1, arg2, arg3, arg4) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OnRetransmissionTimeout", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).OnRetransmissionTimeout), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxDatagramSize", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).SetMaxDatagramSize), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TimeUntilSend", reflect.TypeOf((*MockSendAlgorithmWithDebugInfos)(nil).TimeUntilSend), arg0) } quic-go-0.25.0/internal/mocks/connection_flow_controller.go000066400000000000000000000114631417145451600240670ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/flowcontrol (interfaces: ConnectionFlowController) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesRead", reflect.TypeOf((*MockConnectionFlowController)(nil).AddBytesRead), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesSent", reflect.TypeOf((*MockConnectionFlowController)(nil).AddBytesSent), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWindowUpdate", reflect.TypeOf((*MockConnectionFlowController)(nil).GetWindowUpdate)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNewlyBlocked", reflect.TypeOf((*MockConnectionFlowController)(nil).IsNewlyBlocked)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockConnectionFlowController)(nil).Reset)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWindowSize", reflect.TypeOf((*MockConnectionFlowController)(nil).SendWindowSize)) } // UpdateSendWindow mocks base method. func (m *MockConnectionFlowController) UpdateSendWindow(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateSendWindow", arg0) } // UpdateSendWindow indicates an expected call of UpdateSendWindow. func (mr *MockConnectionFlowControllerMockRecorder) UpdateSendWindow(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSendWindow", reflect.TypeOf((*MockConnectionFlowController)(nil).UpdateSendWindow), arg0) } quic-go-0.25.0/internal/mocks/crypto_setup.go000066400000000000000000000230561417145451600211770ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/handshake (interfaces: CryptoSetup) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" handshake "github.com/lucas-clemente/quic-go/internal/handshake" protocol "github.com/lucas-clemente/quic-go/internal/protocol" qtls "github.com/lucas-clemente/quic-go/internal/qtls" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChangeConnectionID", reflect.TypeOf((*MockCryptoSetup)(nil).ChangeConnectionID), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockCryptoSetup)(nil).Close)) } // ConnectionState mocks base method. func (m *MockCryptoSetup) ConnectionState() qtls.ConnectionState { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ConnectionState") ret0, _ := ret[0].(qtls.ConnectionState) return ret0 } // ConnectionState indicates an expected call of ConnectionState. func (mr *MockCryptoSetupMockRecorder) ConnectionState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockCryptoSetup)(nil).ConnectionState)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get0RTTOpener", reflect.TypeOf((*MockCryptoSetup)(nil).Get0RTTOpener)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get0RTTSealer", reflect.TypeOf((*MockCryptoSetup)(nil).Get0RTTSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get1RTTOpener", reflect.TypeOf((*MockCryptoSetup)(nil).Get1RTTOpener)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get1RTTSealer", reflect.TypeOf((*MockCryptoSetup)(nil).Get1RTTSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandshakeOpener", reflect.TypeOf((*MockCryptoSetup)(nil).GetHandshakeOpener)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandshakeSealer", reflect.TypeOf((*MockCryptoSetup)(nil).GetHandshakeSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitialOpener", reflect.TypeOf((*MockCryptoSetup)(nil).GetInitialOpener)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitialSealer", reflect.TypeOf((*MockCryptoSetup)(nil).GetInitialSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSessionTicket", reflect.TypeOf((*MockCryptoSetup)(nil).GetSessionTicket)) } // HandleMessage mocks base method. func (m *MockCryptoSetup) HandleMessage(arg0 []byte, arg1 protocol.EncryptionLevel) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleMessage", arg0, arg1) ret0, _ := ret[0].(bool) return ret0 } // HandleMessage indicates an expected call of HandleMessage. func (mr *MockCryptoSetupMockRecorder) HandleMessage(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockCryptoSetup)(nil).HandleMessage), arg0, arg1) } // RunHandshake mocks base method. func (m *MockCryptoSetup) RunHandshake() { m.ctrl.T.Helper() m.ctrl.Call(m, "RunHandshake") } // RunHandshake indicates an expected call of RunHandshake. func (mr *MockCryptoSetupMockRecorder) RunHandshake() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunHandshake", reflect.TypeOf((*MockCryptoSetup)(nil).RunHandshake)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetHandshakeConfirmed", reflect.TypeOf((*MockCryptoSetup)(nil).SetHandshakeConfirmed)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLargest1RTTAcked", reflect.TypeOf((*MockCryptoSetup)(nil).SetLargest1RTTAcked), arg0) } quic-go-0.25.0/internal/mocks/logging/000077500000000000000000000000001417145451600175305ustar00rootroot00000000000000quic-go-0.25.0/internal/mocks/logging/connection_tracer.go000066400000000000000000000361601417145451600235640ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/logging (interfaces: ConnectionTracer) // Package mocklogging is a generated GoMock package. package mocklogging import ( net "net" reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" utils "github.com/lucas-clemente/quic-go/internal/utils" wire "github.com/lucas-clemente/quic-go/internal/wire" logging "github.com/lucas-clemente/quic-go/logging" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcknowledgedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).AcknowledgedPacket), arg0, arg1) } // BufferedPacket mocks base method. func (m *MockConnectionTracer) BufferedPacket(arg0 logging.PacketType) { m.ctrl.T.Helper() m.ctrl.Call(m, "BufferedPacket", arg0) } // BufferedPacket indicates an expected call of BufferedPacket. func (mr *MockConnectionTracerMockRecorder) BufferedPacket(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BufferedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).BufferedPacket), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConnectionTracer)(nil).Close)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClosedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).ClosedConnection), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockConnectionTracer)(nil).Debug), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedEncryptionLevel", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedEncryptionLevel), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedKey", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedKey), arg0) } // DroppedPacket mocks base method. func (m *MockConnectionTracer) DroppedPacket(arg0 logging.PacketType, arg1 protocol.ByteCount, arg2 logging.PacketDropReason) { m.ctrl.T.Helper() m.ctrl.Call(m, "DroppedPacket", arg0, arg1, arg2) } // DroppedPacket indicates an expected call of DroppedPacket. func (mr *MockConnectionTracerMockRecorder) DroppedPacket(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedPacket), arg0, arg1, arg2) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LossTimerCanceled", reflect.TypeOf((*MockConnectionTracer)(nil).LossTimerCanceled)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LossTimerExpired", reflect.TypeOf((*MockConnectionTracer)(nil).LossTimerExpired), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LostPacket", reflect.TypeOf((*MockConnectionTracer)(nil).LostPacket), arg0, arg1, arg2) } // NegotiatedVersion mocks base method. func (m *MockConnectionTracer) NegotiatedVersion(arg0 protocol.VersionNumber, arg1, arg2 []protocol.VersionNumber) { 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NegotiatedVersion", reflect.TypeOf((*MockConnectionTracer)(nil).NegotiatedVersion), arg0, arg1, arg2) } // ReceivedPacket mocks base method. func (m *MockConnectionTracer) ReceivedPacket(arg0 *wire.ExtendedHeader, arg1 protocol.ByteCount, arg2 []logging.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedPacket", arg0, arg1, arg2) } // ReceivedPacket indicates an expected call of ReceivedPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedPacket(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedPacket), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedRetry", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedRetry), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedTransportParameters), arg0) } // ReceivedVersionNegotiationPacket mocks base method. func (m *MockConnectionTracer) ReceivedVersionNegotiationPacket(arg0 *wire.Header, arg1 []protocol.VersionNumber) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedVersionNegotiationPacket", arg0, arg1) } // ReceivedVersionNegotiationPacket indicates an expected call of ReceivedVersionNegotiationPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedVersionNegotiationPacket(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedVersionNegotiationPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedVersionNegotiationPacket), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoredTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).RestoredTransportParameters), arg0) } // SentPacket mocks base method. func (m *MockConnectionTracer) SentPacket(arg0 *wire.ExtendedHeader, arg1 protocol.ByteCount, arg2 *wire.AckFrame, 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 *MockConnectionTracerMockRecorder) SentPacket(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockConnectionTracer)(nil).SentPacket), arg0, arg1, arg2, arg3) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).SentTransportParameters), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLossTimer", reflect.TypeOf((*MockConnectionTracer)(nil).SetLossTimer), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).StartedConnection), arg0, arg1, arg2, arg3) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedCongestionState", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedCongestionState), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedKey", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedKey), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedKeyFromTLS", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedKeyFromTLS), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedMetrics", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedMetrics), arg0, arg1, arg2, arg3) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedPTOCount", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedPTOCount), arg0) } quic-go-0.25.0/internal/mocks/logging/tracer.go000066400000000000000000000055131417145451600213430ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/logging (interfaces: Tracer) // Package mocklogging is a generated GoMock package. package mocklogging import ( context "context" net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" logging "github.com/lucas-clemente/quic-go/logging" ) // 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 } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedPacket", reflect.TypeOf((*MockTracer)(nil).DroppedPacket), arg0, arg1, arg2, arg3) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockTracer)(nil).SentPacket), arg0, arg1, arg2, arg3) } // TracerForConnection mocks base method. func (m *MockTracer) TracerForConnection(arg0 context.Context, arg1 protocol.Perspective, arg2 protocol.ConnectionID) logging.ConnectionTracer { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TracerForConnection", arg0, arg1, arg2) ret0, _ := ret[0].(logging.ConnectionTracer) return ret0 } // TracerForConnection indicates an expected call of TracerForConnection. func (mr *MockTracerMockRecorder) TracerForConnection(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TracerForConnection", reflect.TypeOf((*MockTracer)(nil).TracerForConnection), arg0, arg1, arg2) } quic-go-0.25.0/internal/mocks/long_header_opener.go000066400000000000000000000055271417145451600222610ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/handshake (interfaces: LongHeaderOpener) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodePacketNumber", reflect.TypeOf((*MockLongHeaderOpener)(nil).DecodePacketNumber), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptHeader", reflect.TypeOf((*MockLongHeaderOpener)(nil).DecryptHeader), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockLongHeaderOpener)(nil).Open), arg0, arg1, arg2, arg3) } quic-go-0.25.0/internal/mocks/mockgen.go000066400000000000000000000054311417145451600200570ustar00rootroot00000000000000package mocks //go:generate sh -c "mockgen -package mockquic -destination quic/stream.go github.com/lucas-clemente/quic-go Stream" //go:generate sh -c "mockgen -package mockquic -destination quic/early_session_tmp.go github.com/lucas-clemente/quic-go EarlySession && sed 's/qtls.ConnectionState/quic.ConnectionState/g' quic/early_session_tmp.go > quic/early_session.go && rm quic/early_session_tmp.go && goimports -w quic/early_session.go" //go:generate sh -c "mockgen -package mockquic -destination quic/early_listener.go github.com/lucas-clemente/quic-go EarlyListener" //go:generate sh -c "mockgen -package mocklogging -destination logging/tracer.go github.com/lucas-clemente/quic-go/logging Tracer" //go:generate sh -c "mockgen -package mocklogging -destination logging/connection_tracer.go github.com/lucas-clemente/quic-go/logging ConnectionTracer" //go:generate sh -c "mockgen -package mocks -destination short_header_sealer.go github.com/lucas-clemente/quic-go/internal/handshake ShortHeaderSealer" //go:generate sh -c "mockgen -package mocks -destination short_header_opener.go github.com/lucas-clemente/quic-go/internal/handshake ShortHeaderOpener" //go:generate sh -c "mockgen -package mocks -destination long_header_opener.go github.com/lucas-clemente/quic-go/internal/handshake LongHeaderOpener" //go:generate sh -c "mockgen -package mocks -destination crypto_setup_tmp.go github.com/lucas-clemente/quic-go/internal/handshake CryptoSetup && sed -E 's~github.com/marten-seemann/qtls[[:alnum:]_-]*~github.com/lucas-clemente/quic-go/internal/qtls~g; s~qtls.ConnectionStateWith0RTT~qtls.ConnectionState~g' crypto_setup_tmp.go > crypto_setup.go && rm crypto_setup_tmp.go && goimports -w crypto_setup.go" //go:generate sh -c "mockgen -package mocks -destination stream_flow_controller.go github.com/lucas-clemente/quic-go/internal/flowcontrol StreamFlowController" //go:generate sh -c "mockgen -package mocks -destination congestion.go github.com/lucas-clemente/quic-go/internal/congestion SendAlgorithmWithDebugInfos" //go:generate sh -c "mockgen -package mocks -destination connection_flow_controller.go github.com/lucas-clemente/quic-go/internal/flowcontrol ConnectionFlowController" //go:generate sh -c "mockgen -package mockackhandler -destination ackhandler/sent_packet_handler.go github.com/lucas-clemente/quic-go/internal/ackhandler SentPacketHandler" //go:generate sh -c "mockgen -package mockackhandler -destination ackhandler/received_packet_handler.go github.com/lucas-clemente/quic-go/internal/ackhandler ReceivedPacketHandler" // The following command produces a warning message on OSX, however, it still generates the correct mock file. // See https://github.com/golang/mock/issues/339 for details. //go:generate sh -c "mockgen -package mocktls -destination tls/client_session_cache.go crypto/tls ClientSessionCache" quic-go-0.25.0/internal/mocks/quic/000077500000000000000000000000001417145451600170435ustar00rootroot00000000000000quic-go-0.25.0/internal/mocks/quic/early_listener.go000066400000000000000000000045611417145451600224210ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go (interfaces: EarlyListener) // Package mockquic is a generated GoMock package. package mockquic import ( context "context" net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" quic "github.com/lucas-clemente/quic-go" ) // MockEarlyListener is a mock of EarlyListener interface. type MockEarlyListener struct { ctrl *gomock.Controller recorder *MockEarlyListenerMockRecorder } // MockEarlyListenerMockRecorder is the mock recorder for MockEarlyListener. type MockEarlyListenerMockRecorder struct { mock *MockEarlyListener } // NewMockEarlyListener creates a new mock instance. func NewMockEarlyListener(ctrl *gomock.Controller) *MockEarlyListener { mock := &MockEarlyListener{ctrl: ctrl} mock.recorder = &MockEarlyListenerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEarlyListener) EXPECT() *MockEarlyListenerMockRecorder { return m.recorder } // Accept mocks base method. func (m *MockEarlyListener) Accept(arg0 context.Context) (quic.EarlySession, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Accept", arg0) ret0, _ := ret[0].(quic.EarlySession) ret1, _ := ret[1].(error) return ret0, ret1 } // Accept indicates an expected call of Accept. func (mr *MockEarlyListenerMockRecorder) Accept(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Accept", reflect.TypeOf((*MockEarlyListener)(nil).Accept), arg0) } // Addr mocks base method. func (m *MockEarlyListener) 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 *MockEarlyListenerMockRecorder) Addr() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Addr", reflect.TypeOf((*MockEarlyListener)(nil).Addr)) } // Close mocks base method. func (m *MockEarlyListener) 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 *MockEarlyListenerMockRecorder) Close() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockEarlyListener)(nil).Close)) } quic-go-0.25.0/internal/mocks/quic/early_session.go000066400000000000000000000214761417145451600222630ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go (interfaces: EarlySession) // Package mockquic is a generated GoMock package. package mockquic import ( context "context" net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" quic "github.com/lucas-clemente/quic-go" qerr "github.com/lucas-clemente/quic-go/internal/qerr" ) // MockEarlySession is a mock of EarlySession interface. type MockEarlySession struct { ctrl *gomock.Controller recorder *MockEarlySessionMockRecorder } // MockEarlySessionMockRecorder is the mock recorder for MockEarlySession. type MockEarlySessionMockRecorder struct { mock *MockEarlySession } // NewMockEarlySession creates a new mock instance. func NewMockEarlySession(ctrl *gomock.Controller) *MockEarlySession { mock := &MockEarlySession{ctrl: ctrl} mock.recorder = &MockEarlySessionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockEarlySession) EXPECT() *MockEarlySessionMockRecorder { return m.recorder } // AcceptStream mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) AcceptStream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptStream", reflect.TypeOf((*MockEarlySession)(nil).AcceptStream), arg0) } // AcceptUniStream mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) AcceptUniStream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUniStream", reflect.TypeOf((*MockEarlySession)(nil).AcceptUniStream), arg0) } // CloseWithError mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) CloseWithError(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseWithError", reflect.TypeOf((*MockEarlySession)(nil).CloseWithError), arg0, arg1) } // ConnectionState mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) ConnectionState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockEarlySession)(nil).ConnectionState)) } // Context mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) Context() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockEarlySession)(nil).Context)) } // HandshakeComplete mocks base method. func (m *MockEarlySession) HandshakeComplete() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandshakeComplete") ret0, _ := ret[0].(context.Context) return ret0 } // HandshakeComplete indicates an expected call of HandshakeComplete. func (mr *MockEarlySessionMockRecorder) HandshakeComplete() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandshakeComplete", reflect.TypeOf((*MockEarlySession)(nil).HandshakeComplete)) } // LocalAddr mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) LocalAddr() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockEarlySession)(nil).LocalAddr)) } // NextSession mocks base method. func (m *MockEarlySession) NextSession() quic.Session { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NextSession") ret0, _ := ret[0].(quic.Session) return ret0 } // NextSession indicates an expected call of NextSession. func (mr *MockEarlySessionMockRecorder) NextSession() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextSession", reflect.TypeOf((*MockEarlySession)(nil).NextSession)) } // OpenStream mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) OpenStream() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockEarlySession)(nil).OpenStream)) } // OpenStreamSync mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) OpenStreamSync(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStreamSync", reflect.TypeOf((*MockEarlySession)(nil).OpenStreamSync), arg0) } // OpenUniStream mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) OpenUniStream() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStream", reflect.TypeOf((*MockEarlySession)(nil).OpenUniStream)) } // OpenUniStreamSync mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) OpenUniStreamSync(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStreamSync", reflect.TypeOf((*MockEarlySession)(nil).OpenUniStreamSync), arg0) } // ReceiveMessage mocks base method. func (m *MockEarlySession) ReceiveMessage() ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReceiveMessage") ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // ReceiveMessage indicates an expected call of ReceiveMessage. func (mr *MockEarlySessionMockRecorder) ReceiveMessage() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveMessage", reflect.TypeOf((*MockEarlySession)(nil).ReceiveMessage)) } // RemoteAddr mocks base method. func (m *MockEarlySession) 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 *MockEarlySessionMockRecorder) RemoteAddr() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockEarlySession)(nil).RemoteAddr)) } // SendMessage mocks base method. func (m *MockEarlySession) SendMessage(arg0 []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendMessage", arg0) ret0, _ := ret[0].(error) return ret0 } // SendMessage indicates an expected call of SendMessage. func (mr *MockEarlySessionMockRecorder) SendMessage(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockEarlySession)(nil).SendMessage), arg0) } quic-go-0.25.0/internal/mocks/quic/stream.go000066400000000000000000000130021417145451600206610ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go (interfaces: Stream) // Package mockquic is a generated GoMock package. package mockquic import ( context "context" reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" qerr "github.com/lucas-clemente/quic-go/internal/qerr" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRead", reflect.TypeOf((*MockStream)(nil).CancelRead), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelWrite", reflect.TypeOf((*MockStream)(nil).CancelWrite), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStream)(nil).Close)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockStream)(nil).Context)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockStream)(nil).Read), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockStream)(nil).SetDeadline), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockStream)(nil).SetReadDeadline), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockStream)(nil).SetWriteDeadline), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockStream)(nil).StreamID)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockStream)(nil).Write), arg0) } quic-go-0.25.0/internal/mocks/short_header_opener.go000066400000000000000000000057141417145451600224570ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/handshake (interfaces: ShortHeaderOpener) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecodePacketNumber", reflect.TypeOf((*MockShortHeaderOpener)(nil).DecodePacketNumber), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecryptHeader", reflect.TypeOf((*MockShortHeaderOpener)(nil).DecryptHeader), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Open", reflect.TypeOf((*MockShortHeaderOpener)(nil).Open), arg0, arg1, arg2, arg3, arg4, arg5) } quic-go-0.25.0/internal/mocks/short_header_sealer.go000066400000000000000000000061161417145451600224370ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/handshake (interfaces: ShortHeaderSealer) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EncryptHeader", reflect.TypeOf((*MockShortHeaderSealer)(nil).EncryptHeader), arg0, arg1, arg2) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "KeyPhase", reflect.TypeOf((*MockShortHeaderSealer)(nil).KeyPhase)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Overhead", reflect.TypeOf((*MockShortHeaderSealer)(nil).Overhead)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Seal", reflect.TypeOf((*MockShortHeaderSealer)(nil).Seal), arg0, arg1, arg2, arg3) } quic-go-0.25.0/internal/mocks/stream_flow_controller.go000066400000000000000000000124001417145451600232130ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/internal/flowcontrol (interfaces: StreamFlowController) // Package mocks is a generated GoMock package. package mocks import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Abandon", reflect.TypeOf((*MockStreamFlowController)(nil).Abandon)) } // AddBytesRead mocks base method. func (m *MockStreamFlowController) AddBytesRead(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "AddBytesRead", arg0) } // AddBytesRead indicates an expected call of AddBytesRead. func (mr *MockStreamFlowControllerMockRecorder) AddBytesRead(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesRead", reflect.TypeOf((*MockStreamFlowController)(nil).AddBytesRead), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddBytesSent", reflect.TypeOf((*MockStreamFlowController)(nil).AddBytesSent), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWindowUpdate", reflect.TypeOf((*MockStreamFlowController)(nil).GetWindowUpdate)) } // IsNewlyBlocked mocks base method. func (m *MockStreamFlowController) 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 *MockStreamFlowControllerMockRecorder) IsNewlyBlocked() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNewlyBlocked", reflect.TypeOf((*MockStreamFlowController)(nil).IsNewlyBlocked)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendWindowSize", reflect.TypeOf((*MockStreamFlowController)(nil).SendWindowSize)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateHighestReceived", reflect.TypeOf((*MockStreamFlowController)(nil).UpdateHighestReceived), arg0, arg1) } // UpdateSendWindow mocks base method. func (m *MockStreamFlowController) UpdateSendWindow(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdateSendWindow", arg0) } // UpdateSendWindow indicates an expected call of UpdateSendWindow. func (mr *MockStreamFlowControllerMockRecorder) UpdateSendWindow(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateSendWindow", reflect.TypeOf((*MockStreamFlowController)(nil).UpdateSendWindow), arg0) } quic-go-0.25.0/internal/mocks/tls/000077500000000000000000000000001417145451600167045ustar00rootroot00000000000000quic-go-0.25.0/internal/mocks/tls/client_session_cache.go000066400000000000000000000037541417145451600234100ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: crypto/tls (interfaces: ClientSessionCache) // Package mocktls is a generated GoMock package. package mocktls import ( tls "crypto/tls" reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockClientSessionCache is a mock of ClientSessionCache interface. type MockClientSessionCache struct { ctrl *gomock.Controller recorder *MockClientSessionCacheMockRecorder } // MockClientSessionCacheMockRecorder is the mock recorder for MockClientSessionCache. type MockClientSessionCacheMockRecorder struct { mock *MockClientSessionCache } // NewMockClientSessionCache creates a new mock instance. func NewMockClientSessionCache(ctrl *gomock.Controller) *MockClientSessionCache { mock := &MockClientSessionCache{ctrl: ctrl} mock.recorder = &MockClientSessionCacheMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockClientSessionCache) EXPECT() *MockClientSessionCacheMockRecorder { return m.recorder } // Get mocks base method. func (m *MockClientSessionCache) Get(arg0 string) (*tls.ClientSessionState, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Get", arg0) ret0, _ := ret[0].(*tls.ClientSessionState) ret1, _ := ret[1].(bool) return ret0, ret1 } // Get indicates an expected call of Get. func (mr *MockClientSessionCacheMockRecorder) Get(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClientSessionCache)(nil).Get), arg0) } // Put mocks base method. func (m *MockClientSessionCache) Put(arg0 string, arg1 *tls.ClientSessionState) { m.ctrl.T.Helper() m.ctrl.Call(m, "Put", arg0, arg1) } // Put indicates an expected call of Put. func (mr *MockClientSessionCacheMockRecorder) Put(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockClientSessionCache)(nil).Put), arg0, arg1) } quic-go-0.25.0/internal/protocol/000077500000000000000000000000001417145451600166275ustar00rootroot00000000000000quic-go-0.25.0/internal/protocol/connection_id.go000066400000000000000000000032051417145451600217710ustar00rootroot00000000000000package protocol import ( "bytes" "crypto/rand" "fmt" "io" ) // A ConnectionID in QUIC type ConnectionID []byte const maxConnectionIDLen = 20 // GenerateConnectionID generates a connection ID using cryptographic random func GenerateConnectionID(len int) (ConnectionID, error) { b := make([]byte, len) if _, err := rand.Read(b); err != nil { return nil, err } return ConnectionID(b), nil } // 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 nil, err } len := MinConnectionIDLenInitial + int(r[0])%(maxConnectionIDLen-MinConnectionIDLenInitial+1) return GenerateConnectionID(len) } // 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, len int) (ConnectionID, error) { if len == 0 { return nil, nil } c := make(ConnectionID, len) _, err := io.ReadFull(r, c) if err == io.ErrUnexpectedEOF { return nil, io.EOF } return c, err } // Equal says if two connection IDs are equal func (c ConnectionID) Equal(other ConnectionID) bool { return bytes.Equal(c, other) } // Len returns the length of the connection ID in bytes func (c ConnectionID) Len() int { return len(c) } // Bytes returns the byte representation func (c ConnectionID) Bytes() []byte { return []byte(c) } func (c ConnectionID) String() string { if c.Len() == 0 { return "(empty)" } return fmt.Sprintf("%x", c.Bytes()) } quic-go-0.25.0/internal/protocol/connection_id_test.go000066400000000000000000000056271417145451600230420ustar00rootroot00000000000000package protocol import ( "bytes" "io" . "github.com/onsi/ginkgo" . "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("says if connection IDs are equal", func() { c1 := ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} c2 := ConnectionID{8, 7, 6, 5, 4, 3, 2, 1} Expect(c1.Equal(c1)).To(BeTrue()) Expect(c2.Equal(c2)).To(BeTrue()) Expect(c1.Equal(c2)).To(BeFalse()) Expect(c2.Equal(c1)).To(BeFalse()) }) 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 nil for 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).To(BeNil()) }) It("returns the length", func() { c := ConnectionID{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 := ConnectionID([]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(BeNil()) }) It("has a string representation", func() { c := ConnectionID([]byte{0xde, 0xad, 0xbe, 0xef, 0x42}) Expect(c.String()).To(Equal("deadbeef42")) }) It("has a long string representation", func() { c := ConnectionID{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)")) }) }) quic-go-0.25.0/internal/protocol/encryption_level.go000066400000000000000000000012611417145451600225370ustar00rootroot00000000000000package 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" } quic-go-0.25.0/internal/protocol/encryption_level_test.go000066400000000000000000000011221417145451600235720ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "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")) }) }) quic-go-0.25.0/internal/protocol/key_phase.go000066400000000000000000000011621417145451600211260ustar00rootroot00000000000000package 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" } } quic-go-0.25.0/internal/protocol/key_phase_test.go000066400000000000000000000014031417145451600221630ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "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)) }) }) quic-go-0.25.0/internal/protocol/packet_number.go000066400000000000000000000044321417145451600220000ustar00rootroot00000000000000package 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 } quic-go-0.25.0/internal/protocol/packet_number_test.go000066400000000000000000000154421417145451600230420ustar00rootroot00000000000000package protocol import ( "fmt" . "github.com/onsi/ginkgo" . "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)) } }) }) }) }) } }) quic-go-0.25.0/internal/protocol/params.go000066400000000000000000000230521417145451600204430ustar00rootroot00000000000000package protocol import "time" // DesiredReceiveBufferSize is the kernel UDP receive buffer size that we'd like to use. const DesiredReceiveBufferSize = (1 << 20) * 2 // 2 MB // InitialPacketSizeIPv4 is the maximum packet size that we use for sending IPv4 packets. const InitialPacketSizeIPv4 = 1252 // InitialPacketSizeIPv6 is the maximum packet size that we use for sending IPv6 packets. const InitialPacketSizeIPv6 = 1232 // MaxCongestionWindowPackets is the maximum congestion window in packet. const MaxCongestionWindowPackets = 10000 // MaxUndecryptablePackets limits the number of undecryptable packets that are queued in the session. 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 // MaxSessionUnprocessedPackets is the max number of packets stored in each session that are not yet processed. const MaxSessionUnprocessedPackets = 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 sessions 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 // RetryTokenValidity is the duration that a retry token is considered valid const RetryTokenValidity = 10 * time.Second // 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 // DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds. const DefaultHandshakeTimeout = 10 * 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 sessions 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 // MaxDatagramFrameSize is the maximum size of a DATAGRAM frame as defined in // https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. // The size is chosen such that a DATAGRAM frame fits into a QUIC packet. const MaxDatagramFrameSize ByteCount = 1220 // DatagramRcvQueueLen is the length of the receive queue for DATAGRAM frames. // See https://datatracker.ietf.org/doc/draft-pauly-quic-datagram/. const DatagramRcvQueueLen = 128 // 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 session is created, all buffered packets are passed to the session immediately. // To avoid blocking, this value has to be smaller than MaxSessionUnprocessedPackets. // To avoid packets being dropped as undecryptable by the session, this value has to be smaller than MaxUndecryptablePackets. const Max0RTTQueueLen = 31 quic-go-0.25.0/internal/protocol/params_test.go000066400000000000000000000005541417145451600215040ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Parameters", func() { It("can queue more packets in the session than in the 0-RTT queue", func() { Expect(MaxSessionUnprocessedPackets).To(BeNumerically(">", Max0RTTQueueLen)) Expect(MaxUndecryptablePackets).To(BeNumerically(">", Max0RTTQueueLen)) }) }) quic-go-0.25.0/internal/protocol/perspective.go000066400000000000000000000007651417145451600215170ustar00rootroot00000000000000package 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" } } quic-go-0.25.0/internal/protocol/perspective_test.go000066400000000000000000000010311417145451600225410ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "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)) }) }) quic-go-0.25.0/internal/protocol/protocol.go000066400000000000000000000057661417145451600210350ustar00rootroot00000000000000package 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 ( ECNNon ECN = iota // 00 ECT1 // 01 ECT0 // 10 ECNCE // 11 ) // 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 ByteCount = 1452 // 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 // 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 quic-go-0.25.0/internal/protocol/protocol_suite_test.go000066400000000000000000000003001417145451600232600ustar00rootroot00000000000000package protocol import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestProtocol(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Protocol Suite") } quic-go-0.25.0/internal/protocol/protocol_test.go000066400000000000000000000014341417145451600220600ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "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(ECN(0)).To(Equal(ECNNon)) Expect(ECN(0b00000010)).To(Equal(ECT0)) Expect(ECN(0b00000001)).To(Equal(ECT1)) Expect(ECN(0b00000011)).To(Equal(ECNCE)) }) }) quic-go-0.25.0/internal/protocol/stream.go000066400000000000000000000034101417145451600204470ustar00rootroot00000000000000package 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 } quic-go-0.25.0/internal/protocol/stream_test.go000066400000000000000000000057541417145451600215230ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "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)) } } }) }) }) quic-go-0.25.0/internal/protocol/version.go000066400000000000000000000065631417145451600206550ustar00rootroot00000000000000package protocol import ( "crypto/rand" "encoding/binary" "fmt" "math" ) // VersionNumber is a version number as int type VersionNumber 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 ( VersionTLS VersionNumber = 0x1 VersionWhatever VersionNumber = math.MaxUint32 - 1 // for when the version doesn't matter VersionUnknown VersionNumber = math.MaxUint32 VersionDraft29 VersionNumber = 0xff00001d Version1 VersionNumber = 0x1 ) // SupportedVersions lists the versions that the server supports // must be in sorted descending order var SupportedVersions = []VersionNumber{Version1, VersionDraft29} // IsValidVersion says if the version is known to quic-go func IsValidVersion(v VersionNumber) bool { return v == VersionTLS || IsSupportedVersion(SupportedVersions, v) } func (vn VersionNumber) String() string { // For releases, VersionTLS will be set to a draft version. // A switch statement can't contain duplicate cases. if vn == VersionTLS && VersionTLS != VersionDraft29 && VersionTLS != Version1 { return "TLS dev version (WIP)" } //nolint:exhaustive switch vn { case VersionWhatever: return "whatever" case VersionUnknown: return "unknown" case VersionDraft29: return "draft-29" case Version1: return "v1" default: if vn.isGQUIC() { return fmt.Sprintf("gQUIC %d", vn.toGQUICVersion()) } return fmt.Sprintf("%#x", uint32(vn)) } } func (vn VersionNumber) isGQUIC() bool { return vn > gquicVersion0 && vn <= maxGquicVersion } func (vn VersionNumber) toGQUICVersion() int { return int(10*(vn-gquicVersion0)/0x100) + int(vn%0x10) } // IsSupportedVersion returns true if the server supports this version func IsSupportedVersion(supported []VersionNumber, v VersionNumber) 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 []VersionNumber) (VersionNumber, bool) { for _, ourVer := range ours { for _, theirVer := range theirs { if ourVer == theirVer { return ourVer, true } } } return 0, false } // generateReservedVersion generates a reserved version number (v & 0x0f0f0f0f == 0x0a0a0a0a) func generateReservedVersion() VersionNumber { b := make([]byte, 4) _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything return VersionNumber((binary.BigEndian.Uint32(b) | 0x0a0a0a0a) & 0xfafafafa) } // GetGreasedVersions adds one reserved version number to a slice of version numbers, at a random position func GetGreasedVersions(supported []VersionNumber) []VersionNumber { b := make([]byte, 1) _, _ = rand.Read(b) // ignore the error here. Failure to read random data doesn't break anything randPos := int(b[0]) % (len(supported) + 1) greased := make([]VersionNumber, len(supported)+1) copy(greased, supported[:randPos]) greased[randPos] = generateReservedVersion() copy(greased[randPos+1:], supported[randPos:]) return greased } quic-go-0.25.0/internal/protocol/version_test.go000066400000000000000000000103671417145451600217110ustar00rootroot00000000000000package protocol import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Version", func() { isReservedVersion := func(v VersionNumber) bool { return v&0x0f0f0f0f == 0x0a0a0a0a } It("says if a version is valid", func() { Expect(IsValidVersion(VersionTLS)).To(BeTrue()) Expect(IsValidVersion(VersionWhatever)).To(BeFalse()) Expect(IsValidVersion(VersionUnknown)).To(BeFalse()) Expect(IsValidVersion(VersionDraft29)).To(BeTrue()) Expect(IsValidVersion(Version1)).To(BeTrue()) Expect(IsValidVersion(1234)).To(BeFalse()) }) It("versions don't have reserved version numbers", func() { Expect(isReservedVersion(VersionTLS)).To(BeFalse()) }) It("has the right string representation", func() { Expect(VersionWhatever.String()).To(Equal("whatever")) Expect(VersionUnknown.String()).To(Equal("unknown")) Expect(VersionDraft29.String()).To(Equal("draft-29")) Expect(Version1.String()).To(Equal("v1")) // check with unsupported version numbers from the wiki Expect(VersionNumber(0x51303039).String()).To(Equal("gQUIC 9")) Expect(VersionNumber(0x51303133).String()).To(Equal("gQUIC 13")) Expect(VersionNumber(0x51303235).String()).To(Equal("gQUIC 25")) Expect(VersionNumber(0x51303438).String()).To(Equal("gQUIC 48")) Expect(VersionNumber(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()) }) It("has supported versions in sorted order", func() { Expect(SupportedVersions[0]).To(Equal(Version1)) for i := 1; i < len(SupportedVersions)-1; i++ { Expect(SupportedVersions[i]).To(BeNumerically(">", SupportedVersions[i+1])) } }) Context("highest supported version", func() { It("finds the supported version", func() { supportedVersions := []VersionNumber{1, 2, 3} other := []VersionNumber{6, 5, 4, 3} ver, ok := ChooseSupportedVersion(supportedVersions, other) Expect(ok).To(BeTrue()) Expect(ver).To(Equal(VersionNumber(3))) }) It("picks the preferred version", func() { supportedVersions := []VersionNumber{2, 1, 3} other := []VersionNumber{3, 6, 1, 8, 2, 10} ver, ok := ChooseSupportedVersion(supportedVersions, other) Expect(ok).To(BeTrue()) Expect(ver).To(Equal(VersionNumber(2))) }) It("says when no matching version was found", func() { _, ok := ChooseSupportedVersion([]VersionNumber{1}, []VersionNumber{2}) Expect(ok).To(BeFalse()) }) It("handles empty inputs", func() { _, ok := ChooseSupportedVersion([]VersionNumber{102, 101}, []VersionNumber{}) Expect(ok).To(BeFalse()) _, ok = ChooseSupportedVersion([]VersionNumber{}, []VersionNumber{1, 2}) Expect(ok).To(BeFalse()) _, ok = ChooseSupportedVersion([]VersionNumber{}, []VersionNumber{}) Expect(ok).To(BeFalse()) }) }) Context("reserved versions", func() { It("adds a greased version if passed an empty slice", func() { greased := GetGreasedVersions([]VersionNumber{}) Expect(greased).To(HaveLen(1)) Expect(isReservedVersion(greased[0])).To(BeTrue()) }) It("creates greased lists of version numbers", func() { supported := []VersionNumber{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()) }) }) }) quic-go-0.25.0/internal/qerr/000077500000000000000000000000001417145451600157375ustar00rootroot00000000000000quic-go-0.25.0/internal/qerr/error_codes.go000066400000000000000000000047631417145451600206060ustar00rootroot00000000000000package qerr import ( "fmt" "github.com/lucas-clemente/quic-go/internal/qtls" ) // 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 qtls.Alert(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)) } } quic-go-0.25.0/internal/qerr/errorcodes_test.go000066400000000000000000000031311417145451600214720ustar00rootroot00000000000000package qerr import ( "go/ast" "go/parser" "go/token" "path" "runtime" "strconv" . "github.com/onsi/ginkgo" . "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()) } }) }) quic-go-0.25.0/internal/qerr/errors.go000066400000000000000000000065301417145451600176060ustar00rootroot00000000000000package qerr import ( "fmt" "net" "github.com/lucas-clemente/quic-go/internal/protocol" ) var ( ErrHandshakeTimeout = &HandshakeTimeoutError{} ErrIdleTimeout = &IdleTimeoutError{} ) type TransportError struct { Remote bool FrameType uint64 ErrorCode TransportErrorCode ErrorMessage string } var _ error = &TransportError{} // NewCryptoError create a new TransportError instance for a crypto error func NewCryptoError(tlsAlert uint8, errorMessage string) *TransportError { return &TransportError{ ErrorCode: 0x100 + TransportErrorCode(tlsAlert), ErrorMessage: errorMessage, } } func (e *TransportError) Error() string { str := e.ErrorCode.String() if e.FrameType != 0 { str += fmt.Sprintf(" (frame type: %#x)", e.FrameType) } msg := e.ErrorMessage 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 } // 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", e.ErrorCode) } return fmt.Sprintf("Application error %#x: %s", e.ErrorCode, 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.VersionNumber Theirs []protocol.VersionNumber } 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 } quic-go-0.25.0/internal/qerr/errors_suite_test.go000066400000000000000000000002741417145451600220550ustar00rootroot00000000000000package qerr import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestErrorcodes(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Errors Suite") } quic-go-0.25.0/internal/qerr/errors_test.go000066400000000000000000000102121417145451600206350ustar00rootroot00000000000000package qerr import ( "errors" "net" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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: foobar")) }) It("has a string representation for empty error phrases", func() { Expect((&TransportError{ErrorCode: FlowControlError}).Error()).To(Equal("FLOW_CONTROL_ERROR")) }) It("includes the frame type, for errors without a message", func() { Expect((&TransportError{ ErrorCode: FlowControlError, FrameType: 0x1337, }).Error()).To(Equal("FLOW_CONTROL_ERROR (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 (frame type: 0x1337): foobar")) }) Context("crypto errors", func() { It("has a string representation for errors with a message", func() { err := NewCryptoError(0x42, "foobar") Expect(err.Error()).To(Equal("CRYPTO_ERROR (0x142): foobar")) }) It("has a string representation for errors without a message", func() { err := NewCryptoError(0x2a, "") Expect(err.Error()).To(Equal("CRYPTO_ERROR (0x12a): 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: foobar")) }) It("has a string representation for errors without a message", func() { Expect((&ApplicationError{ ErrorCode: 0x42, }).Error()).To(Equal("Application error 0x42")) }) }) 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(nerr.Temporary()).To(BeFalse()) 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(nerr.Temporary()).To(BeFalse()) 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.VersionNumber{2, 3}, Theirs: []protocol.VersionNumber{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()) Expect(nerr.Temporary()).To(BeTrue()) }) }) 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()) }) }) quic-go-0.25.0/internal/qtls/000077500000000000000000000000001417145451600157515ustar00rootroot00000000000000quic-go-0.25.0/internal/qtls/go116.go000066400000000000000000000057651417145451600171520ustar00rootroot00000000000000//go:build go1.16 && !go1.17 // +build go1.16,!go1.17 package qtls import ( "crypto" "crypto/cipher" "crypto/tls" "net" "unsafe" "github.com/marten-seemann/qtls-go1-16" ) type ( // Alert is a TLS alert Alert = qtls.Alert // A Certificate is qtls.Certificate. Certificate = qtls.Certificate // CertificateRequestInfo contains inforamtion about a certificate request. CertificateRequestInfo = qtls.CertificateRequestInfo // A CipherSuiteTLS13 is a cipher suite for TLS 1.3 CipherSuiteTLS13 = qtls.CipherSuiteTLS13 // ClientHelloInfo contains information about a ClientHello. ClientHelloInfo = qtls.ClientHelloInfo // ClientSessionCache is a cache used for session resumption. ClientSessionCache = qtls.ClientSessionCache // ClientSessionState is a state needed for session resumption. ClientSessionState = qtls.ClientSessionState // A Config is a qtls.Config. Config = qtls.Config // A Conn is a qtls.Conn. Conn = qtls.Conn // ConnectionState contains information about the state of the connection. ConnectionState = qtls.ConnectionStateWith0RTT // EncryptionLevel is the encryption level of a message. EncryptionLevel = qtls.EncryptionLevel // Extension is a TLS extension Extension = qtls.Extension // ExtraConfig is the qtls.ExtraConfig ExtraConfig = qtls.ExtraConfig // RecordLayer is a qtls RecordLayer. RecordLayer = qtls.RecordLayer ) const ( // EncryptionHandshake is the Handshake encryption level EncryptionHandshake = qtls.EncryptionHandshake // Encryption0RTT is the 0-RTT encryption level Encryption0RTT = qtls.Encryption0RTT // EncryptionApplication is the application data encryption level EncryptionApplication = qtls.EncryptionApplication ) // AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { return qtls.AEADAESGCMTLS13(key, fixedNonce) } // Client returns a new TLS client side connection. func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { return qtls.Client(conn, config, extraConfig) } // Server returns a new TLS server side connection. func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { return qtls.Server(conn, config, extraConfig) } func GetConnectionState(conn *Conn) ConnectionState { return conn.ConnectionStateWith0RTT() } // ToTLSConnectionState extracts the tls.ConnectionState func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState { return cs.ConnectionState } type cipherSuiteTLS13 struct { ID uint16 KeyLen int AEAD func(key, fixedNonce []byte) cipher.AEAD Hash crypto.Hash } //go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-16.cipherSuiteTLS13ByID func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 // CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite. func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 { val := cipherSuiteTLS13ByID(id) cs := (*cipherSuiteTLS13)(unsafe.Pointer(val)) return &qtls.CipherSuiteTLS13{ ID: cs.ID, KeyLen: cs.KeyLen, AEAD: cs.AEAD, Hash: cs.Hash, } } quic-go-0.25.0/internal/qtls/go117.go000066400000000000000000000057651417145451600171530ustar00rootroot00000000000000//go:build go1.17 && !go1.18 // +build go1.17,!go1.18 package qtls import ( "crypto" "crypto/cipher" "crypto/tls" "net" "unsafe" "github.com/marten-seemann/qtls-go1-17" ) type ( // Alert is a TLS alert Alert = qtls.Alert // A Certificate is qtls.Certificate. Certificate = qtls.Certificate // CertificateRequestInfo contains inforamtion about a certificate request. CertificateRequestInfo = qtls.CertificateRequestInfo // A CipherSuiteTLS13 is a cipher suite for TLS 1.3 CipherSuiteTLS13 = qtls.CipherSuiteTLS13 // ClientHelloInfo contains information about a ClientHello. ClientHelloInfo = qtls.ClientHelloInfo // ClientSessionCache is a cache used for session resumption. ClientSessionCache = qtls.ClientSessionCache // ClientSessionState is a state needed for session resumption. ClientSessionState = qtls.ClientSessionState // A Config is a qtls.Config. Config = qtls.Config // A Conn is a qtls.Conn. Conn = qtls.Conn // ConnectionState contains information about the state of the connection. ConnectionState = qtls.ConnectionStateWith0RTT // EncryptionLevel is the encryption level of a message. EncryptionLevel = qtls.EncryptionLevel // Extension is a TLS extension Extension = qtls.Extension // ExtraConfig is the qtls.ExtraConfig ExtraConfig = qtls.ExtraConfig // RecordLayer is a qtls RecordLayer. RecordLayer = qtls.RecordLayer ) const ( // EncryptionHandshake is the Handshake encryption level EncryptionHandshake = qtls.EncryptionHandshake // Encryption0RTT is the 0-RTT encryption level Encryption0RTT = qtls.Encryption0RTT // EncryptionApplication is the application data encryption level EncryptionApplication = qtls.EncryptionApplication ) // AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { return qtls.AEADAESGCMTLS13(key, fixedNonce) } // Client returns a new TLS client side connection. func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { return qtls.Client(conn, config, extraConfig) } // Server returns a new TLS server side connection. func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { return qtls.Server(conn, config, extraConfig) } func GetConnectionState(conn *Conn) ConnectionState { return conn.ConnectionStateWith0RTT() } // ToTLSConnectionState extracts the tls.ConnectionState func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState { return cs.ConnectionState } type cipherSuiteTLS13 struct { ID uint16 KeyLen int AEAD func(key, fixedNonce []byte) cipher.AEAD Hash crypto.Hash } //go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-17.cipherSuiteTLS13ByID func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 // CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite. func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 { val := cipherSuiteTLS13ByID(id) cs := (*cipherSuiteTLS13)(unsafe.Pointer(val)) return &qtls.CipherSuiteTLS13{ ID: cs.ID, KeyLen: cs.KeyLen, AEAD: cs.AEAD, Hash: cs.Hash, } } quic-go-0.25.0/internal/qtls/go118.go000066400000000000000000000057421417145451600171470ustar00rootroot00000000000000//go:build go1.18 // +build go1.18 package qtls import ( "crypto" "crypto/cipher" "crypto/tls" "net" "unsafe" "github.com/marten-seemann/qtls-go1-18" ) type ( // Alert is a TLS alert Alert = qtls.Alert // A Certificate is qtls.Certificate. Certificate = qtls.Certificate // CertificateRequestInfo contains inforamtion about a certificate request. CertificateRequestInfo = qtls.CertificateRequestInfo // A CipherSuiteTLS13 is a cipher suite for TLS 1.3 CipherSuiteTLS13 = qtls.CipherSuiteTLS13 // ClientHelloInfo contains information about a ClientHello. ClientHelloInfo = qtls.ClientHelloInfo // ClientSessionCache is a cache used for session resumption. ClientSessionCache = qtls.ClientSessionCache // ClientSessionState is a state needed for session resumption. ClientSessionState = qtls.ClientSessionState // A Config is a qtls.Config. Config = qtls.Config // A Conn is a qtls.Conn. Conn = qtls.Conn // ConnectionState contains information about the state of the connection. ConnectionState = qtls.ConnectionStateWith0RTT // EncryptionLevel is the encryption level of a message. EncryptionLevel = qtls.EncryptionLevel // Extension is a TLS extension Extension = qtls.Extension // ExtraConfig is the qtls.ExtraConfig ExtraConfig = qtls.ExtraConfig // RecordLayer is a qtls RecordLayer. RecordLayer = qtls.RecordLayer ) const ( // EncryptionHandshake is the Handshake encryption level EncryptionHandshake = qtls.EncryptionHandshake // Encryption0RTT is the 0-RTT encryption level Encryption0RTT = qtls.Encryption0RTT // EncryptionApplication is the application data encryption level EncryptionApplication = qtls.EncryptionApplication ) // AEADAESGCMTLS13 creates a new AES-GCM AEAD for TLS 1.3 func AEADAESGCMTLS13(key, fixedNonce []byte) cipher.AEAD { return qtls.AEADAESGCMTLS13(key, fixedNonce) } // Client returns a new TLS client side connection. func Client(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { return qtls.Client(conn, config, extraConfig) } // Server returns a new TLS server side connection. func Server(conn net.Conn, config *Config, extraConfig *ExtraConfig) *Conn { return qtls.Server(conn, config, extraConfig) } func GetConnectionState(conn *Conn) ConnectionState { return conn.ConnectionStateWith0RTT() } // ToTLSConnectionState extracts the tls.ConnectionState func ToTLSConnectionState(cs ConnectionState) tls.ConnectionState { return cs.ConnectionState } type cipherSuiteTLS13 struct { ID uint16 KeyLen int AEAD func(key, fixedNonce []byte) cipher.AEAD Hash crypto.Hash } //go:linkname cipherSuiteTLS13ByID github.com/marten-seemann/qtls-go1-18.cipherSuiteTLS13ByID func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 // CipherSuiteTLS13ByID gets a TLS 1.3 cipher suite. func CipherSuiteTLS13ByID(id uint16) *CipherSuiteTLS13 { val := cipherSuiteTLS13ByID(id) cs := (*cipherSuiteTLS13)(unsafe.Pointer(val)) return &qtls.CipherSuiteTLS13{ ID: cs.ID, KeyLen: cs.KeyLen, AEAD: cs.AEAD, Hash: cs.Hash, } } quic-go-0.25.0/internal/qtls/go119.go000066400000000000000000000001461417145451600171410ustar00rootroot00000000000000//go:build go1.19 // +build go1.19 package qtls var _ int = "quic-go doesn't build on Go 1.19 yet." quic-go-0.25.0/internal/qtls/qtls_suite_test.go000066400000000000000000000005741417145451600215410ustar00rootroot00000000000000package qtls import ( "testing" gomock "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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() }) quic-go-0.25.0/internal/qtls/qtls_test.go000066400000000000000000000005641417145451600203270ustar00rootroot00000000000000package qtls import ( "crypto/tls" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("qtls wrapper", func() { It("gets cipher suites", func() { for _, id := range []uint16{tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384, tls.TLS_CHACHA20_POLY1305_SHA256} { cs := CipherSuiteTLS13ByID(id) Expect(cs.ID).To(Equal(id)) } }) }) quic-go-0.25.0/internal/testdata/000077500000000000000000000000001417145451600165775ustar00rootroot00000000000000quic-go-0.25.0/internal/testdata/ca.pem000066400000000000000000000020051417145451600176620ustar00rootroot00000000000000-----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----- quic-go-0.25.0/internal/testdata/cert.go000066400000000000000000000022551417145451600200670ustar00rootroot00000000000000package testdata import ( "crypto/tls" "crypto/x509" "io/ioutil" "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{ 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 := ioutil.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 } quic-go-0.25.0/internal/testdata/cert.pem000066400000000000000000000020221417145451600202330ustar00rootroot00000000000000-----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----- quic-go-0.25.0/internal/testdata/cert_test.go000066400000000000000000000013541417145451600211250ustar00rootroot00000000000000package testdata import ( "crypto/tls" "io/ioutil" . "github.com/onsi/ginkgo" . "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 := ioutil.ReadAll(conn) Expect(err).ToNot(HaveOccurred()) Expect(string(data)).To(Equal("foobar")) }) }) quic-go-0.25.0/internal/testdata/generate_key.sh000077500000000000000000000012441417145451600216010ustar00rootroot00000000000000#!/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 quic-go-0.25.0/internal/testdata/priv.key000066400000000000000000000032501417145451600202710ustar00rootroot00000000000000-----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----- quic-go-0.25.0/internal/testdata/testdata_suite_test.go000066400000000000000000000003001417145451600232000ustar00rootroot00000000000000package testdata import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestTestdata(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Testdata Suite") } quic-go-0.25.0/internal/testutils/000077500000000000000000000000001417145451600170265ustar00rootroot00000000000000quic-go-0.25.0/internal/testutils/testutils.go000066400000000000000000000061051417145451600214170ustar00rootroot00000000000000package testutils import ( "bytes" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) // Utilities for simulating packet injection and man-in-the-middle (MITM) attacker tests. // Do not use for non-testing purposes. // writePacket returns a new raw packet with the specified header and payload func writePacket(hdr *wire.ExtendedHeader, data []byte) []byte { buf := &bytes.Buffer{} hdr.Write(buf, protocol.VersionTLS) return append(buf.Bytes(), data...) } // packRawPayload returns a new raw payload containing given frames func packRawPayload(version protocol.VersionNumber, frames []wire.Frame) []byte { buf := new(bytes.Buffer) for _, cf := range frames { cf.Write(buf, version) } return buf.Bytes() } // ComposeInitialPacket returns an Initial packet encrypted under key // (the original destination connection ID) containing specified frames func ComposeInitialPacket(srcConnID protocol.ConnectionID, destConnID protocol.ConnectionID, version protocol.VersionNumber, key protocol.ConnectionID, frames []wire.Frame) []byte { sealer, _ := handshake.NewInitialAEAD(key, protocol.PerspectiveServer, version) // compose payload var payload []byte if len(frames) == 0 { payload = make([]byte, protocol.MinInitialPacketSize) } else { payload = packRawPayload(version, frames) } // compose Initial header payloadSize := len(payload) pnLength := protocol.PacketNumberLen4 length := payloadSize + int(pnLength) + sealer.Overhead() hdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: srcConnID, DestConnectionID: destConnID, Length: protocol.ByteCount(length), Version: version, }, PacketNumberLen: pnLength, PacketNumber: 0x0, } raw := writePacket(hdr, payload) // encrypt payload and header payloadOffset := len(raw) - payloadSize var encrypted []byte encrypted = sealer.Seal(encrypted, payload, hdr.PacketNumber, raw[:payloadOffset]) hdrBytes := raw[0:payloadOffset] encrypted = append(hdrBytes, encrypted...) pnOffset := payloadOffset - int(pnLength) // packet number offset sealer.EncryptHeader( encrypted[payloadOffset:payloadOffset+16], // first 16 bytes of payload (sample) &encrypted[0], // first byte of header encrypted[pnOffset:payloadOffset], // packet number bytes ) return encrypted } // ComposeRetryPacket returns a new raw Retry Packet func ComposeRetryPacket( srcConnID protocol.ConnectionID, destConnID protocol.ConnectionID, origDestConnID protocol.ConnectionID, token []byte, version protocol.VersionNumber, ) []byte { hdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeRetry, SrcConnectionID: srcConnID, DestConnectionID: destConnID, Token: token, Version: version, }, } data := writePacket(hdr, nil) return append(data, handshake.GetRetryIntegrityTag(data, origDestConnID, version)[:]...) } quic-go-0.25.0/internal/utils/000077500000000000000000000000001417145451600161265ustar00rootroot00000000000000quic-go-0.25.0/internal/utils/atomic_bool.go000066400000000000000000000005131417145451600207430ustar00rootroot00000000000000package utils import "sync/atomic" // An AtomicBool is an atomic bool type AtomicBool struct { v int32 } // Set sets the value func (a *AtomicBool) Set(value bool) { var n int32 if value { n = 1 } atomic.StoreInt32(&a.v, n) } // Get gets the value func (a *AtomicBool) Get() bool { return atomic.LoadInt32(&a.v) != 0 } quic-go-0.25.0/internal/utils/atomic_bool_test.go000066400000000000000000000007241417145451600220060ustar00rootroot00000000000000package utils import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Atomic Bool", func() { var a *AtomicBool BeforeEach(func() { a = &AtomicBool{} }) It("has the right default value", func() { Expect(a.Get()).To(BeFalse()) }) It("sets the value to true", func() { a.Set(true) Expect(a.Get()).To(BeTrue()) }) It("sets the value to false", func() { a.Set(true) a.Set(false) Expect(a.Get()).To(BeFalse()) }) }) quic-go-0.25.0/internal/utils/buffered_write_closer.go000066400000000000000000000007411417145451600230220ustar00rootroot00000000000000package 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() } quic-go-0.25.0/internal/utils/buffered_write_closer_test.go000066400000000000000000000010031417145451600240510ustar00rootroot00000000000000package utils import ( "bufio" "bytes" . "github.com/onsi/ginkgo" . "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")) }) }) quic-go-0.25.0/internal/utils/byteinterval_linkedlist.go000066400000000000000000000153401417145451600234120ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package utils // Linked list implementation from the Go standard library. // ByteIntervalElement is an element of a linked list. type ByteIntervalElement 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 *ByteIntervalElement // The list to which this element belongs. list *ByteIntervalList // The value stored with this element. Value ByteInterval } // Next returns the next list element or nil. func (e *ByteIntervalElement) Next() *ByteIntervalElement { 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 *ByteIntervalElement) Prev() *ByteIntervalElement { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } // ByteIntervalList is a linked list of ByteIntervals. type ByteIntervalList struct { root ByteIntervalElement // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element } // Init initializes or clears list l. func (l *ByteIntervalList) Init() *ByteIntervalList { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // NewByteIntervalList returns an initialized list. func NewByteIntervalList() *ByteIntervalList { return new(ByteIntervalList).Init() } // Len returns the number of elements of list l. // The complexity is O(1). func (l *ByteIntervalList) Len() int { return l.len } // Front returns the first element of list l or nil if the list is empty. func (l *ByteIntervalList) Front() *ByteIntervalElement { 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 *ByteIntervalList) Back() *ByteIntervalElement { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List value. func (l *ByteIntervalList) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *ByteIntervalList) insert(e, at *ByteIntervalElement) *ByteIntervalElement { n := at.next at.next = e e.prev = at e.next = n n.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). func (l *ByteIntervalList) insertValue(v ByteInterval, at *ByteIntervalElement) *ByteIntervalElement { return l.insert(&ByteIntervalElement{Value: v}, at) } // remove removes e from its list, decrements l.len, and returns e. func (l *ByteIntervalList) remove(e *ByteIntervalElement) *ByteIntervalElement { 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 l.len-- return 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 *ByteIntervalList) Remove(e *ByteIntervalElement) ByteInterval { 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 e.Value } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *ByteIntervalList) PushFront(v ByteInterval) *ByteIntervalElement { 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 *ByteIntervalList) PushBack(v ByteInterval) *ByteIntervalElement { 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 *ByteIntervalList) InsertBefore(v ByteInterval, mark *ByteIntervalElement) *ByteIntervalElement { 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 *ByteIntervalList) InsertAfter(v ByteInterval, mark *ByteIntervalElement) *ByteIntervalElement { 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 *ByteIntervalList) MoveToFront(e *ByteIntervalElement) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *ByteIntervalList) MoveToBack(e *ByteIntervalElement) { if e.list != l || l.root.prev == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *ByteIntervalList) MoveBefore(e, mark *ByteIntervalElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(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 *ByteIntervalList) MoveAfter(e, mark *ByteIntervalElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(e), mark) } // PushBackList inserts a copy of an other list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *ByteIntervalList) PushBackList(other *ByteIntervalList) { 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 an other list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *ByteIntervalList) PushFrontList(other *ByteIntervalList) { l.lazyInit() for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } quic-go-0.25.0/internal/utils/byteoder_big_endian_test.go000066400000000000000000000052021417145451600234670ustar00rootroot00000000000000package utils import ( "bytes" "io" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Big Endian encoding / decoding", func() { Context("ReadUint16", func() { It("reads a big endian", func() { b := []byte{0x13, 0xEF} val, err := BigEndian.ReadUint16(bytes.NewReader(b)) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint16(0x13EF))) }) It("throws an error if less than 2 bytes are passed", func() { b := []byte{0x13, 0xEF} for i := 0; i < len(b); i++ { _, err := BigEndian.ReadUint16(bytes.NewReader(b[:i])) Expect(err).To(MatchError(io.EOF)) } }) }) Context("ReadUint24", func() { It("reads a big endian", func() { b := []byte{0x13, 0xbe, 0xef} val, err := BigEndian.ReadUint24(bytes.NewReader(b)) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint32(0x13beef))) }) It("throws an error if less than 3 bytes are passed", func() { b := []byte{0x13, 0xbe, 0xef} for i := 0; i < len(b); i++ { _, err := BigEndian.ReadUint24(bytes.NewReader(b[:i])) Expect(err).To(MatchError(io.EOF)) } }) }) Context("ReadUint32", func() { It("reads a big endian", func() { b := []byte{0x12, 0x35, 0xAB, 0xFF} val, err := BigEndian.ReadUint32(bytes.NewReader(b)) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint32(0x1235ABFF))) }) It("throws an error if less than 4 bytes are passed", func() { b := []byte{0x12, 0x35, 0xAB, 0xFF} for i := 0; i < len(b); i++ { _, err := BigEndian.ReadUint32(bytes.NewReader(b[:i])) Expect(err).To(MatchError(io.EOF)) } }) }) Context("WriteUint16", func() { It("outputs 2 bytes", func() { b := &bytes.Buffer{} BigEndian.WriteUint16(b, uint16(1)) Expect(b.Len()).To(Equal(2)) }) It("outputs a big endian", func() { num := uint16(0xFF11) b := &bytes.Buffer{} BigEndian.WriteUint16(b, num) Expect(b.Bytes()).To(Equal([]byte{0xFF, 0x11})) }) }) Context("WriteUint24", func() { It("outputs 3 bytes", func() { b := &bytes.Buffer{} BigEndian.WriteUint24(b, uint32(1)) Expect(b.Len()).To(Equal(3)) }) It("outputs a big endian", func() { num := uint32(0xff11aa) b := &bytes.Buffer{} BigEndian.WriteUint24(b, num) Expect(b.Bytes()).To(Equal([]byte{0xff, 0x11, 0xaa})) }) }) Context("WriteUint32", func() { It("outputs 4 bytes", func() { b := &bytes.Buffer{} BigEndian.WriteUint32(b, uint32(1)) Expect(b.Len()).To(Equal(4)) }) It("outputs a big endian", func() { num := uint32(0xEFAC3512) b := &bytes.Buffer{} BigEndian.WriteUint32(b, num) Expect(b.Bytes()).To(Equal([]byte{0xEF, 0xAC, 0x35, 0x12})) }) }) }) quic-go-0.25.0/internal/utils/byteorder.go000066400000000000000000000006301417145451600204530ustar00rootroot00000000000000package utils import ( "bytes" "io" ) // A ByteOrder specifies how to convert byte sequences into 16-, 32-, or 64-bit unsigned integers. type ByteOrder interface { ReadUint32(io.ByteReader) (uint32, error) ReadUint24(io.ByteReader) (uint32, error) ReadUint16(io.ByteReader) (uint16, error) WriteUint32(*bytes.Buffer, uint32) WriteUint24(*bytes.Buffer, uint32) WriteUint16(*bytes.Buffer, uint16) } quic-go-0.25.0/internal/utils/byteorder_big_endian.go000066400000000000000000000037751417145451600226270ustar00rootroot00000000000000package utils import ( "bytes" "io" ) // BigEndian is the big-endian implementation of ByteOrder. var BigEndian ByteOrder = bigEndian{} type bigEndian struct{} var _ ByteOrder = &bigEndian{} // ReadUintN reads N bytes func (bigEndian) ReadUintN(b io.ByteReader, length uint8) (uint64, error) { var res uint64 for i := uint8(0); i < length; i++ { bt, err := b.ReadByte() if err != nil { return 0, err } res ^= uint64(bt) << ((length - 1 - i) * 8) } return res, nil } // ReadUint32 reads a uint32 func (bigEndian) ReadUint32(b io.ByteReader) (uint32, error) { var b1, b2, b3, b4 uint8 var err error if b4, err = b.ReadByte(); err != nil { return 0, err } if b3, err = b.ReadByte(); err != nil { return 0, err } if b2, err = b.ReadByte(); err != nil { return 0, err } if b1, err = b.ReadByte(); err != nil { return 0, err } return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16 + uint32(b4)<<24, nil } // ReadUint24 reads a uint24 func (bigEndian) ReadUint24(b io.ByteReader) (uint32, error) { var b1, b2, b3 uint8 var err error if b3, err = b.ReadByte(); err != nil { return 0, err } if b2, err = b.ReadByte(); err != nil { return 0, err } if b1, err = b.ReadByte(); err != nil { return 0, err } return uint32(b1) + uint32(b2)<<8 + uint32(b3)<<16, nil } // ReadUint16 reads a uint16 func (bigEndian) ReadUint16(b io.ByteReader) (uint16, error) { var b1, b2 uint8 var err error if b2, err = b.ReadByte(); err != nil { return 0, err } if b1, err = b.ReadByte(); err != nil { return 0, err } return uint16(b1) + uint16(b2)<<8, nil } // WriteUint32 writes a uint32 func (bigEndian) WriteUint32(b *bytes.Buffer, i uint32) { b.Write([]byte{uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i)}) } // WriteUint24 writes a uint24 func (bigEndian) WriteUint24(b *bytes.Buffer, i uint32) { b.Write([]byte{uint8(i >> 16), uint8(i >> 8), uint8(i)}) } // WriteUint16 writes a uint16 func (bigEndian) WriteUint16(b *bytes.Buffer, i uint16) { b.Write([]byte{uint8(i >> 8), uint8(i)}) } quic-go-0.25.0/internal/utils/gen.go000066400000000000000000000005571417145451600172350ustar00rootroot00000000000000package utils //go:generate genny -pkg utils -in linkedlist/linkedlist.go -out byteinterval_linkedlist.go gen Item=ByteInterval //go:generate genny -pkg utils -in linkedlist/linkedlist.go -out packetinterval_linkedlist.go gen Item=PacketInterval //go:generate genny -pkg utils -in linkedlist/linkedlist.go -out newconnectionid_linkedlist.go gen Item=NewConnectionID quic-go-0.25.0/internal/utils/ip.go000066400000000000000000000004461417145451600170710ustar00rootroot00000000000000package utils import "net" func IsIPv4(ip net.IP) bool { // If ip is not an IPv4 address, To4 returns nil. // Note that there might be some corner cases, where this is not correct. // See https://stackoverflow.com/questions/22751035/golang-distinguish-ipv4-ipv6. return ip.To4() != nil } quic-go-0.25.0/internal/utils/ip_test.go000066400000000000000000000005661417145451600201330ustar00rootroot00000000000000package utils import ( "net" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("IP", func() { It("tells IPv4 and IPv6 addresses apart", func() { Expect(IsIPv4(net.IPv4(127, 0, 0, 1))).To(BeTrue()) Expect(IsIPv4(net.IPv4zero)).To(BeTrue()) Expect(IsIPv4(net.IPv6zero)).To(BeFalse()) Expect(IsIPv4(net.IPv6loopback)).To(BeFalse()) }) }) quic-go-0.25.0/internal/utils/linkedlist/000077500000000000000000000000001417145451600202705ustar00rootroot00000000000000quic-go-0.25.0/internal/utils/linkedlist/README.md000066400000000000000000000005041417145451600215460ustar00rootroot00000000000000# Usage This is the Go standard library implementation of a linked list (https://golang.org/src/container/list/list.go), modified such that genny (https://github.com/cheekybits/genny) can be used to generate a typed linked list. To generate, run ``` genny -pkg $PACKAGE -in linkedlist.go -out $OUTFILE gen Item=$TYPE ``` quic-go-0.25.0/internal/utils/linkedlist/linkedlist.go000066400000000000000000000142361417145451600227670ustar00rootroot00000000000000package linkedlist import "github.com/cheekybits/genny/generic" // Linked list implementation from the Go standard library. // Item is a generic type. type Item generic.Type // ItemElement is an element of a linked list. type ItemElement 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 *ItemElement // The list to which this element belongs. list *ItemList // The value stored with this element. Value Item } // Next returns the next list element or nil. func (e *ItemElement) Next() *ItemElement { 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 *ItemElement) Prev() *ItemElement { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } // ItemList is a linked list of Items. type ItemList struct { root ItemElement // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element } // Init initializes or clears list l. func (l *ItemList) Init() *ItemList { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // NewItemList returns an initialized list. func NewItemList() *ItemList { return new(ItemList).Init() } // Len returns the number of elements of list l. // The complexity is O(1). func (l *ItemList) Len() int { return l.len } // Front returns the first element of list l or nil if the list is empty. func (l *ItemList) Front() *ItemElement { 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 *ItemList) Back() *ItemElement { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List value. func (l *ItemList) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *ItemList) insert(e, at *ItemElement) *ItemElement { n := at.next at.next = e e.prev = at e.next = n n.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). func (l *ItemList) insertValue(v Item, at *ItemElement) *ItemElement { return l.insert(&ItemElement{Value: v}, at) } // remove removes e from its list, decrements l.len, and returns e. func (l *ItemList) remove(e *ItemElement) *ItemElement { 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 l.len-- return 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 *ItemList) Remove(e *ItemElement) Item { 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 e.Value } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *ItemList) PushFront(v Item) *ItemElement { 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 *ItemList) PushBack(v Item) *ItemElement { 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 *ItemList) InsertBefore(v Item, mark *ItemElement) *ItemElement { 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 *ItemList) InsertAfter(v Item, mark *ItemElement) *ItemElement { 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 *ItemList) MoveToFront(e *ItemElement) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *ItemList) MoveToBack(e *ItemElement) { if e.list != l || l.root.prev == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *ItemList) MoveBefore(e, mark *ItemElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(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 *ItemList) MoveAfter(e, mark *ItemElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(e), mark) } // PushBackList inserts a copy of an other list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *ItemList) PushBackList(other *ItemList) { 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 an other list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *ItemList) PushFrontList(other *ItemList) { l.lazyInit() for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } quic-go-0.25.0/internal/utils/log.go000066400000000000000000000054321417145451600172420ustar00rootroot00000000000000package 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/lucas-clemente/quic-go/wiki/Logging") return LogLevelNothing } } quic-go-0.25.0/internal/utils/log_test.go000066400000000000000000000076371417145451600203120ustar00rootroot00000000000000package utils import ( "bytes" "log" "os" "time" . "github.com/onsi/ginkgo" . "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)) }) }) }) quic-go-0.25.0/internal/utils/minmax.go000066400000000000000000000055211417145451600177510ustar00rootroot00000000000000package utils import ( "math" "time" "github.com/lucas-clemente/quic-go/internal/protocol" ) // InfDuration is a duration of infinite length const InfDuration = time.Duration(math.MaxInt64) // Max returns the maximum of two Ints func Max(a, b int) int { if a < b { return b } return a } // MaxUint32 returns the maximum of two uint32 func MaxUint32(a, b uint32) uint32 { if a < b { return b } return a } // MaxUint64 returns the maximum of two uint64 func MaxUint64(a, b uint64) uint64 { if a < b { return b } return a } // MinUint64 returns the maximum of two uint64 func MinUint64(a, b uint64) uint64 { if a < b { return a } return b } // Min returns the minimum of two Ints func Min(a, b int) int { if a < b { return a } return b } // MinUint32 returns the maximum of two uint32 func MinUint32(a, b uint32) uint32 { if a < b { return a } return b } // MinInt64 returns the minimum of two int64 func MinInt64(a, b int64) int64 { if a < b { return a } return b } // MaxInt64 returns the minimum of two int64 func MaxInt64(a, b int64) int64 { if a > b { return a } return b } // MinByteCount returns the minimum of two ByteCounts func MinByteCount(a, b protocol.ByteCount) protocol.ByteCount { if a < b { return a } return b } // MaxByteCount returns the maximum of two ByteCounts func MaxByteCount(a, b protocol.ByteCount) protocol.ByteCount { if a < b { return b } return a } // MaxDuration returns the max duration func MaxDuration(a, b time.Duration) time.Duration { if a > b { return a } return b } // MinDuration returns the minimum duration func MinDuration(a, b time.Duration) time.Duration { if a > b { return b } return a } // MinNonZeroDuration return the minimum duration that's not zero. func MinNonZeroDuration(a, b time.Duration) time.Duration { if a == 0 { return b } if b == 0 { return a } return MinDuration(a, b) } // AbsDuration returns the absolute value of a time duration func AbsDuration(d time.Duration) time.Duration { if d >= 0 { return d } return -d } // MinTime returns the earlier time func MinTime(a, b time.Time) time.Time { if a.After(b) { return b } return a } // MinNonZeroTime returns the earlist time that is not time.Time{} // If both a and b are time.Time{}, it returns time.Time{} func MinNonZeroTime(a, b time.Time) time.Time { if a.IsZero() { return b } if b.IsZero() { return a } return MinTime(a, b) } // MaxTime returns the later time func MaxTime(a, b time.Time) time.Time { if a.After(b) { return a } return b } // MaxPacketNumber returns the max packet number func MaxPacketNumber(a, b protocol.PacketNumber) protocol.PacketNumber { if a > b { return a } return b } // MinPacketNumber returns the min packet number func MinPacketNumber(a, b protocol.PacketNumber) protocol.PacketNumber { if a < b { return a } return b } quic-go-0.25.0/internal/utils/minmax_test.go000066400000000000000000000072421417145451600210120ustar00rootroot00000000000000package utils import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Min / Max", func() { Context("Max", func() { It("returns the maximum", func() { Expect(Max(5, 7)).To(Equal(7)) Expect(Max(7, 5)).To(Equal(7)) }) It("returns the maximum uint32", func() { Expect(MaxUint32(5, 7)).To(Equal(uint32(7))) Expect(MaxUint32(7, 5)).To(Equal(uint32(7))) }) It("returns the maximum uint64", func() { Expect(MaxUint64(5, 7)).To(Equal(uint64(7))) Expect(MaxUint64(7, 5)).To(Equal(uint64(7))) }) It("returns the minimum uint64", func() { Expect(MinUint64(5, 7)).To(Equal(uint64(5))) Expect(MinUint64(7, 5)).To(Equal(uint64(5))) }) It("returns the maximum int64", func() { Expect(MaxInt64(5, 7)).To(Equal(int64(7))) Expect(MaxInt64(7, 5)).To(Equal(int64(7))) }) It("returns the maximum ByteCount", func() { Expect(MaxByteCount(7, 5)).To(Equal(protocol.ByteCount(7))) Expect(MaxByteCount(5, 7)).To(Equal(protocol.ByteCount(7))) }) It("returns the maximum duration", func() { Expect(MaxDuration(time.Microsecond, time.Nanosecond)).To(Equal(time.Microsecond)) Expect(MaxDuration(time.Nanosecond, time.Microsecond)).To(Equal(time.Microsecond)) }) It("returns the minimum duration", func() { Expect(MinDuration(time.Microsecond, time.Nanosecond)).To(Equal(time.Nanosecond)) Expect(MinDuration(time.Nanosecond, time.Microsecond)).To(Equal(time.Nanosecond)) }) It("returns packet number max", func() { Expect(MaxPacketNumber(1, 2)).To(Equal(protocol.PacketNumber(2))) Expect(MaxPacketNumber(2, 1)).To(Equal(protocol.PacketNumber(2))) }) It("returns the maximum time", func() { a := time.Now() b := a.Add(time.Second) Expect(MaxTime(a, b)).To(Equal(b)) Expect(MaxTime(b, a)).To(Equal(b)) }) }) Context("Min", func() { It("returns the minimum", func() { Expect(Min(5, 7)).To(Equal(5)) Expect(Min(7, 5)).To(Equal(5)) }) It("returns the minimum uint32", func() { Expect(MinUint32(7, 5)).To(Equal(uint32(5))) Expect(MinUint32(5, 7)).To(Equal(uint32(5))) }) It("returns the minimum int64", func() { Expect(MinInt64(7, 5)).To(Equal(int64(5))) Expect(MinInt64(5, 7)).To(Equal(int64(5))) }) It("returns the minimum ByteCount", func() { Expect(MinByteCount(7, 5)).To(Equal(protocol.ByteCount(5))) Expect(MinByteCount(5, 7)).To(Equal(protocol.ByteCount(5))) }) It("returns packet number min", func() { Expect(MinPacketNumber(1, 2)).To(Equal(protocol.PacketNumber(1))) Expect(MinPacketNumber(2, 1)).To(Equal(protocol.PacketNumber(1))) }) It("returns the minimum duration", func() { a := time.Now() b := a.Add(time.Second) Expect(MinTime(a, b)).To(Equal(a)) Expect(MinTime(b, a)).To(Equal(a)) }) It("returns the minium non-zero duration", func() { var a time.Duration b := time.Second Expect(MinNonZeroDuration(0, 0)).To(BeZero()) Expect(MinNonZeroDuration(a, b)).To(Equal(b)) Expect(MinNonZeroDuration(b, a)).To(Equal(b)) Expect(MinNonZeroDuration(time.Minute, time.Hour)).To(Equal(time.Minute)) }) It("returns the minium non-zero time", func() { a := time.Time{} b := time.Now() Expect(MinNonZeroTime(time.Time{}, time.Time{})).To(Equal(time.Time{})) Expect(MinNonZeroTime(a, b)).To(Equal(b)) Expect(MinNonZeroTime(b, a)).To(Equal(b)) Expect(MinNonZeroTime(b, b.Add(time.Second))).To(Equal(b)) Expect(MinNonZeroTime(b.Add(time.Second), b)).To(Equal(b)) }) }) It("returns the abs time", func() { Expect(AbsDuration(time.Microsecond)).To(Equal(time.Microsecond)) Expect(AbsDuration(-time.Microsecond)).To(Equal(time.Microsecond)) }) }) quic-go-0.25.0/internal/utils/new_connection_id.go000066400000000000000000000004251417145451600221420ustar00rootroot00000000000000package utils import ( "github.com/lucas-clemente/quic-go/internal/protocol" ) // NewConnectionID is a new connection ID type NewConnectionID struct { SequenceNumber uint64 ConnectionID protocol.ConnectionID StatelessResetToken protocol.StatelessResetToken } quic-go-0.25.0/internal/utils/newconnectionid_linkedlist.go000066400000000000000000000156461417145451600241010ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package utils // Linked list implementation from the Go standard library. // NewConnectionIDElement is an element of a linked list. type NewConnectionIDElement 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 *NewConnectionIDElement // The list to which this element belongs. list *NewConnectionIDList // The value stored with this element. Value NewConnectionID } // Next returns the next list element or nil. func (e *NewConnectionIDElement) Next() *NewConnectionIDElement { 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 *NewConnectionIDElement) Prev() *NewConnectionIDElement { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } // NewConnectionIDList is a linked list of NewConnectionIDs. type NewConnectionIDList struct { root NewConnectionIDElement // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element } // Init initializes or clears list l. func (l *NewConnectionIDList) Init() *NewConnectionIDList { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // NewNewConnectionIDList returns an initialized list. func NewNewConnectionIDList() *NewConnectionIDList { return new(NewConnectionIDList).Init() } // Len returns the number of elements of list l. // The complexity is O(1). func (l *NewConnectionIDList) Len() int { return l.len } // Front returns the first element of list l or nil if the list is empty. func (l *NewConnectionIDList) Front() *NewConnectionIDElement { 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 *NewConnectionIDList) Back() *NewConnectionIDElement { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List value. func (l *NewConnectionIDList) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *NewConnectionIDList) insert(e, at *NewConnectionIDElement) *NewConnectionIDElement { n := at.next at.next = e e.prev = at e.next = n n.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). func (l *NewConnectionIDList) insertValue(v NewConnectionID, at *NewConnectionIDElement) *NewConnectionIDElement { return l.insert(&NewConnectionIDElement{Value: v}, at) } // remove removes e from its list, decrements l.len, and returns e. func (l *NewConnectionIDList) remove(e *NewConnectionIDElement) *NewConnectionIDElement { 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 l.len-- return 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 *NewConnectionIDList) Remove(e *NewConnectionIDElement) NewConnectionID { 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 e.Value } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *NewConnectionIDList) PushFront(v NewConnectionID) *NewConnectionIDElement { 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 *NewConnectionIDList) PushBack(v NewConnectionID) *NewConnectionIDElement { 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 *NewConnectionIDList) InsertBefore(v NewConnectionID, mark *NewConnectionIDElement) *NewConnectionIDElement { 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 *NewConnectionIDList) InsertAfter(v NewConnectionID, mark *NewConnectionIDElement) *NewConnectionIDElement { 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 *NewConnectionIDList) MoveToFront(e *NewConnectionIDElement) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *NewConnectionIDList) MoveToBack(e *NewConnectionIDElement) { if e.list != l || l.root.prev == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *NewConnectionIDList) MoveBefore(e, mark *NewConnectionIDElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(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 *NewConnectionIDList) MoveAfter(e, mark *NewConnectionIDElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(e), mark) } // PushBackList inserts a copy of an other list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *NewConnectionIDList) PushBackList(other *NewConnectionIDList) { 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 an other list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *NewConnectionIDList) PushFrontList(other *NewConnectionIDList) { l.lazyInit() for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } quic-go-0.25.0/internal/utils/packet_interval.go000066400000000000000000000003521417145451600216300ustar00rootroot00000000000000package utils import "github.com/lucas-clemente/quic-go/internal/protocol" // PacketInterval is an interval from one PacketNumber to the other type PacketInterval struct { Start protocol.PacketNumber End protocol.PacketNumber } quic-go-0.25.0/internal/utils/packetinterval_linkedlist.go000066400000000000000000000155441417145451600237240ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package utils // Linked list implementation from the Go standard library. // PacketIntervalElement is an element of a linked list. type PacketIntervalElement 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 *PacketIntervalElement // The list to which this element belongs. list *PacketIntervalList // The value stored with this element. Value PacketInterval } // Next returns the next list element or nil. func (e *PacketIntervalElement) Next() *PacketIntervalElement { 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 *PacketIntervalElement) Prev() *PacketIntervalElement { if p := e.prev; e.list != nil && p != &e.list.root { return p } return nil } // PacketIntervalList is a linked list of PacketIntervals. type PacketIntervalList struct { root PacketIntervalElement // sentinel list element, only &root, root.prev, and root.next are used len int // current list length excluding (this) sentinel element } // Init initializes or clears list l. func (l *PacketIntervalList) Init() *PacketIntervalList { l.root.next = &l.root l.root.prev = &l.root l.len = 0 return l } // NewPacketIntervalList returns an initialized list. func NewPacketIntervalList() *PacketIntervalList { return new(PacketIntervalList).Init() } // Len returns the number of elements of list l. // The complexity is O(1). func (l *PacketIntervalList) Len() int { return l.len } // Front returns the first element of list l or nil if the list is empty. func (l *PacketIntervalList) Front() *PacketIntervalElement { 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 *PacketIntervalList) Back() *PacketIntervalElement { if l.len == 0 { return nil } return l.root.prev } // lazyInit lazily initializes a zero List value. func (l *PacketIntervalList) lazyInit() { if l.root.next == nil { l.Init() } } // insert inserts e after at, increments l.len, and returns e. func (l *PacketIntervalList) insert(e, at *PacketIntervalElement) *PacketIntervalElement { n := at.next at.next = e e.prev = at e.next = n n.prev = e e.list = l l.len++ return e } // insertValue is a convenience wrapper for insert(&Element{Value: v}, at). func (l *PacketIntervalList) insertValue(v PacketInterval, at *PacketIntervalElement) *PacketIntervalElement { return l.insert(&PacketIntervalElement{Value: v}, at) } // remove removes e from its list, decrements l.len, and returns e. func (l *PacketIntervalList) remove(e *PacketIntervalElement) *PacketIntervalElement { 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 l.len-- return 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 *PacketIntervalList) Remove(e *PacketIntervalElement) PacketInterval { 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 e.Value } // PushFront inserts a new element e with value v at the front of list l and returns e. func (l *PacketIntervalList) PushFront(v PacketInterval) *PacketIntervalElement { 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 *PacketIntervalList) PushBack(v PacketInterval) *PacketIntervalElement { 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 *PacketIntervalList) InsertBefore(v PacketInterval, mark *PacketIntervalElement) *PacketIntervalElement { 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 *PacketIntervalList) InsertAfter(v PacketInterval, mark *PacketIntervalElement) *PacketIntervalElement { 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 *PacketIntervalList) MoveToFront(e *PacketIntervalElement) { if e.list != l || l.root.next == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *PacketIntervalList) MoveToBack(e *PacketIntervalElement) { if e.list != l || l.root.prev == e { return } // see comment in List.Remove about initialization of l l.insert(l.remove(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 *PacketIntervalList) MoveBefore(e, mark *PacketIntervalElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(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 *PacketIntervalList) MoveAfter(e, mark *PacketIntervalElement) { if e.list != l || e == mark || mark.list != l { return } l.insert(l.remove(e), mark) } // PushBackList inserts a copy of an other list at the back of list l. // The lists l and other may be the same. They must not be nil. func (l *PacketIntervalList) PushBackList(other *PacketIntervalList) { 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 an other list at the front of list l. // The lists l and other may be the same. They must not be nil. func (l *PacketIntervalList) PushFrontList(other *PacketIntervalList) { l.lazyInit() for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { l.insertValue(e.Value, &l.root) } } quic-go-0.25.0/internal/utils/rand.go000066400000000000000000000011571417145451600174050ustar00rootroot00000000000000package 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 } quic-go-0.25.0/internal/utils/rand_test.go000066400000000000000000000010351417145451600204370ustar00rootroot00000000000000package utils import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Rand", func() { It("generates random numbers", func() { const ( num = 1000 max = 123456 ) 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)) }) }) quic-go-0.25.0/internal/utils/rtt_stats.go000066400000000000000000000077021417145451600205120ustar00rootroot00000000000000package utils import ( "time" "github.com/lucas-clemente/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() + MaxDuration(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 == InfDuration || 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(AbsDuration(r.smoothedRTT-sample)/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) { if r.hasMeasurement { panic("initial RTT set after first measurement") } 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 = MaxDuration(r.meanDeviation, AbsDuration(r.smoothedRTT-r.latestRTT)) r.smoothedRTT = MaxDuration(r.smoothedRTT, r.latestRTT) } quic-go-0.25.0/internal/utils/rtt_stats_test.go000066400000000000000000000144701417145451600215510ustar00rootroot00000000000000package utils import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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() { // Make sure we ignore bad RTTs. // base::test::MockLog log; 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, InfDuration, -1000 * time.Microsecond, } // log.StartCapturingLogs(); for _, badSendDelta := range badSendDeltas { // SCOPED_TRACE(Message() << "bad_send_delta = " // << bad_send_delta.ToMicroseconds()); // EXPECT_CALL(log, Log(LOG_WARNING, _, _, _, HasSubstr("Ignoring"))); 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)) }) }) quic-go-0.25.0/internal/utils/streamframe_interval.go000066400000000000000000000003351417145451600226700ustar00rootroot00000000000000package utils import "github.com/lucas-clemente/quic-go/internal/protocol" // ByteInterval is an interval from one ByteCount to the other type ByteInterval struct { Start protocol.ByteCount End protocol.ByteCount } quic-go-0.25.0/internal/utils/timer.go000066400000000000000000000021111417145451600175700ustar00rootroot00000000000000package 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 } // Stop stops the timer func (t *Timer) Stop() { t.t.Stop() } quic-go-0.25.0/internal/utils/timer_test.go000066400000000000000000000040211417145451600206310ustar00rootroot00000000000000package utils import ( "time" . "github.com/onsi/ginkgo" . "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("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()) }) }) quic-go-0.25.0/internal/utils/utils_suite_test.go000066400000000000000000000002701417145451600220640ustar00rootroot00000000000000package utils import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestCrypto(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Utils Suite") } quic-go-0.25.0/internal/wire/000077500000000000000000000000001417145451600157345ustar00rootroot00000000000000quic-go-0.25.0/internal/wire/ack_frame.go000066400000000000000000000147271417145451600202060ustar00rootroot00000000000000package wire import ( "bytes" "errors" "sort" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/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(r *bytes.Reader, ackDelayExponent uint8, _ protocol.VersionNumber) (*AckFrame, error) { typeByte, err := r.ReadByte() if err != nil { return nil, err } ecn := typeByte&0x1 > 0 frame := &AckFrame{} la, err := quicvarint.Read(r) if err != nil { return nil, err } largestAcked := protocol.PacketNumber(la) delay, err := quicvarint.Read(r) if err != nil { return nil, err } delayTime := time.Duration(delay*1< largestAcked { return nil, errors.New("invalid first ACK range") } smallest := largestAcked - ackBlock // read all the other ACK ranges frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largestAcked}) for i := uint64(0); i < numBlocks; i++ { g, err := quicvarint.Read(r) if err != nil { return nil, err } gap := protocol.PacketNumber(g) if smallest < gap+2 { return nil, errInvalidAckRanges } largest := smallest - gap - 2 ab, err := quicvarint.Read(r) if err != nil { return nil, err } ackBlock := protocol.PacketNumber(ab) if ackBlock > largest { return nil, errInvalidAckRanges } smallest = largest - ackBlock frame.AckRanges = append(frame.AckRanges, AckRange{Smallest: smallest, Largest: largest}) } if !frame.validateAckRanges() { return nil, errInvalidAckRanges } // parse (and skip) the ECN section if ecn { for i := 0; i < 3; i++ { if _, err := quicvarint.Read(r); err != nil { return nil, err } } } return frame, nil } // Write writes an ACK frame. func (f *AckFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { hasECN := f.ECT0 > 0 || f.ECT1 > 0 || f.ECNCE > 0 if hasECN { b.WriteByte(0x3) } else { b.WriteByte(0x2) } quicvarint.Write(b, uint64(f.LargestAcked())) quicvarint.Write(b, encodeAckDelay(f.DelayTime)) numRanges := f.numEncodableAckRanges() quicvarint.Write(b, uint64(numRanges-1)) // write the first range _, firstRange := f.encodeAckRange(0) quicvarint.Write(b, firstRange) // write all the other range for i := 1; i < numRanges; i++ { gap, len := f.encodeAckRange(i) quicvarint.Write(b, gap) quicvarint.Write(b, len) } if hasECN { quicvarint.Write(b, f.ECT0) quicvarint.Write(b, f.ECT1) quicvarint.Write(b, f.ECNCE) } return nil } // Length of a written frame func (f *AckFrame) Length(version protocol.VersionNumber) 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 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 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 encodeAckDelay(delay time.Duration) uint64 { return uint64(delay.Nanoseconds() / (1000 * (1 << protocol.AckDelayExponent))) } quic-go-0.25.0/internal/wire/ack_frame_test.go000066400000000000000000000412541417145451600212400ustar00rootroot00000000000000package wire import ( "bytes" "io" "math" "time" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("ACK Frame (for IETF QUIC)", func() { Context("parsing", func() { It("parses an ACK frame without any ranges", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(100))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(90))) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(b.Len()).To(BeZero()) }) It("parses an ACK frame that only acks a single packet", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(55))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(55))) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(b.Len()).To(BeZero()) }) It("accepts an ACK frame that acks all packets from 0 to largest", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(20))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(0))) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(b.Len()).To(BeZero()) }) It("rejects an ACK frame that has a first ACK block which is larger than LargestAcked", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) _, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).To(MatchError("invalid first ACK range")) }) It("parses an ACK frame that has a single block", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) 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}, })) Expect(b.Len()).To(BeZero()) }) It("parses an ACK frame that has a multiple blocks", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) 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}, })) Expect(b.Len()).To(BeZero()) }) It("uses the ack delay exponent", func() { const delayTime = 1 << 10 * time.Millisecond buf := &bytes.Buffer{} f := &AckFrame{ AckRanges: []AckRange{{Smallest: 1, Largest: 1}}, DelayTime: delayTime, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) for i := uint8(0); i < 8; i++ { b := bytes.NewReader(buf.Bytes()) frame, err := parseAckFrame(b, protocol.AckDelayExponent+i, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.DelayTime).To(Equal(delayTime * (1 << i))) } }) It("gracefully handles overflows of the delay time", func() { data := []byte{0x2} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) 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 := []byte{0x2} data = append(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 _, err := parseAckFrame(bytes.NewReader(data), protocol.AckDelayExponent, versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseAckFrame(bytes.NewReader(data[0:i]), protocol.AckDelayExponent, versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) Context("ACK_ECN", func() { It("parses", func() { data := []byte{0x3} data = append(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 b := bytes.NewReader(data) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.LargestAcked()).To(Equal(protocol.PacketNumber(100))) Expect(frame.LowestAcked()).To(Equal(protocol.PacketNumber(90))) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(b.Len()).To(BeZero()) }) It("errors on EOF", func() { data := []byte{0x3} data = append(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 _, err := parseAckFrame(bytes.NewReader(data), protocol.AckDelayExponent, versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseAckFrame(bytes.NewReader(data[0:i]), protocol.AckDelayExponent, versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) }) }) Context("when writing", func() { It("writes a simple frame", func() { buf := &bytes.Buffer{} f := &AckFrame{ AckRanges: []AckRange{{Smallest: 100, Largest: 1337}}, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) expected := []byte{0x2} 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(buf.Bytes()).To(Equal(expected)) }) It("writes an ACK-ECN frame", func() { buf := &bytes.Buffer{} f := &AckFrame{ AckRanges: []AckRange{{Smallest: 10, Largest: 2000}}, ECT0: 13, ECT1: 37, ECNCE: 12345, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) Expect(f.Length(versionIETFFrames)).To(BeEquivalentTo(buf.Len())) expected := []byte{0x3} 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(buf.Bytes()).To(Equal(expected)) }) It("writes a frame that acks a single packet", func() { buf := &bytes.Buffer{} f := &AckFrame{ AckRanges: []AckRange{{Smallest: 0x2eadbeef, Largest: 0x2eadbeef}}, DelayTime: 18 * time.Millisecond, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) Expect(f.Length(versionIETFFrames)).To(BeEquivalentTo(buf.Len())) b := bytes.NewReader(buf.Bytes()) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(frame.DelayTime).To(Equal(f.DelayTime)) Expect(b.Len()).To(BeZero()) }) It("writes a frame that acks many packets", func() { buf := &bytes.Buffer{} f := &AckFrame{ AckRanges: []AckRange{{Smallest: 0x1337, Largest: 0x2eadbeef}}, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) Expect(f.Length(versionIETFFrames)).To(BeEquivalentTo(buf.Len())) b := bytes.NewReader(buf.Bytes()) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeFalse()) Expect(b.Len()).To(BeZero()) }) It("writes a frame with a a single gap", func() { buf := &bytes.Buffer{} f := &AckFrame{ AckRanges: []AckRange{ {Smallest: 400, Largest: 1000}, {Smallest: 100, Largest: 200}, }, } Expect(f.validateAckRanges()).To(BeTrue()) err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(f.Length(versionIETFFrames)).To(BeEquivalentTo(buf.Len())) b := bytes.NewReader(buf.Bytes()) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeTrue()) Expect(b.Len()).To(BeZero()) }) It("writes a frame with multiple ranges", func() { buf := &bytes.Buffer{} 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()) Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) Expect(f.Length(versionIETFFrames)).To(BeEquivalentTo(buf.Len())) b := bytes.NewReader(buf.Bytes()) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) Expect(frame.HasMissingRanges()).To(BeTrue()) Expect(b.Len()).To(BeZero()) }) It("limits the maximum size of the ACK frame", func() { buf := &bytes.Buffer{} 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()) Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) Expect(f.Length(versionIETFFrames)).To(BeEquivalentTo(buf.Len())) // make sure the ACK frame is *a little bit* smaller than the MaxAckFrameSize Expect(buf.Len()).To(BeNumerically(">", protocol.MaxAckFrameSize-5)) Expect(buf.Len()).To(BeNumerically("<=", protocol.MaxAckFrameSize)) b := bytes.NewReader(buf.Bytes()) frame, err := parseAckFrame(b, protocol.AckDelayExponent, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.HasMissingRanges()).To(BeTrue()) Expect(b.Len()).To(BeZero()) 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()) }) }) }) quic-go-0.25.0/internal/wire/ack_range.go000066400000000000000000000005301417145451600201730ustar00rootroot00000000000000package wire import "github.com/lucas-clemente/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 } quic-go-0.25.0/internal/wire/ack_range_test.go000066400000000000000000000004671417145451600212430ustar00rootroot00000000000000package wire import ( . "github.com/onsi/ginkgo" . "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)) }) }) quic-go-0.25.0/internal/wire/connection_close_frame.go000066400000000000000000000042731417145451600227670ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A ConnectionCloseFrame is a CONNECTION_CLOSE frame type ConnectionCloseFrame struct { IsApplicationError bool ErrorCode uint64 FrameType uint64 ReasonPhrase string } func parseConnectionCloseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*ConnectionCloseFrame, error) { typeByte, err := r.ReadByte() if err != nil { return nil, err } f := &ConnectionCloseFrame{IsApplicationError: typeByte == 0x1d} ec, err := quicvarint.Read(r) if err != nil { return nil, err } f.ErrorCode = ec // read the Frame Type, if this is not an application error if !f.IsApplicationError { ft, err := quicvarint.Read(r) if err != nil { return nil, err } f.FrameType = ft } var reasonPhraseLen uint64 reasonPhraseLen, err = quicvarint.Read(r) if err != nil { return nil, err } // shortcut to prevent the unnecessary allocation of dataLen bytes // if the dataLen is larger than the remaining length of the packet // reading the whole reason phrase would result in EOF when attempting to READ if int(reasonPhraseLen) > r.Len() { return nil, io.EOF } reasonPhrase := make([]byte, reasonPhraseLen) if _, err := io.ReadFull(r, reasonPhrase); err != nil { // this should never happen, since we already checked the reasonPhraseLen earlier return nil, err } f.ReasonPhrase = string(reasonPhrase) return f, nil } // Length of a written frame func (f *ConnectionCloseFrame) Length(protocol.VersionNumber) protocol.ByteCount { length := 1 + quicvarint.Len(f.ErrorCode) + quicvarint.Len(uint64(len(f.ReasonPhrase))) + protocol.ByteCount(len(f.ReasonPhrase)) if !f.IsApplicationError { length += quicvarint.Len(f.FrameType) // for the frame type } return length } func (f *ConnectionCloseFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { if f.IsApplicationError { b.WriteByte(0x1d) } else { b.WriteByte(0x1c) } quicvarint.Write(b, f.ErrorCode) if !f.IsApplicationError { quicvarint.Write(b, f.FrameType) } quicvarint.Write(b, uint64(len(f.ReasonPhrase))) b.WriteString(f.ReasonPhrase) return nil } quic-go-0.25.0/internal/wire/connection_close_frame_test.go000066400000000000000000000125761417145451600240330ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "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 := []byte{0x1c} data = append(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)...) b := bytes.NewReader(data) frame, err := parseConnectionCloseFrame(b, versionIETFFrames) 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(b.Len()).To(BeZero()) }) It("accepts sample frame containing an application error code", func() { reason := "The application messed things up." data := []byte{0x1d} data = append(data, encodeVarInt(0xcafe)...) data = append(data, encodeVarInt(uint64(len(reason)))...) // reason phrase length data = append(data, reason...) b := bytes.NewReader(data) frame, err := parseConnectionCloseFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.IsApplicationError).To(BeTrue()) Expect(frame.ErrorCode).To(BeEquivalentTo(0xcafe)) Expect(frame.ReasonPhrase).To(Equal(reason)) Expect(b.Len()).To(BeZero()) }) It("rejects long reason phrases", func() { data := []byte{0x1c} data = append(data, encodeVarInt(0xcafe)...) data = append(data, encodeVarInt(0x42)...) // frame type data = append(data, encodeVarInt(0xffff)...) // reason phrase length b := bytes.NewReader(data) _, err := parseConnectionCloseFrame(b, versionIETFFrames) Expect(err).To(MatchError(io.EOF)) }) It("errors on EOFs", func() { reason := "No recent network activity." data := []byte{0x1c} data = append(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)...) _, err := parseConnectionCloseFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseConnectionCloseFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) It("parses a frame without a reason phrase", func() { data := []byte{0x1c} data = append(data, encodeVarInt(0xcafe)...) data = append(data, encodeVarInt(0x42)...) // frame type data = append(data, encodeVarInt(0)...) b := bytes.NewReader(data) frame, err := parseConnectionCloseFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.ReasonPhrase).To(BeEmpty()) Expect(b.Len()).To(BeZero()) }) }) Context("when writing", func() { It("writes a frame without a reason phrase", func() { b := &bytes.Buffer{} frame := &ConnectionCloseFrame{ ErrorCode: 0xbeef, FrameType: 0x12345, } Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) expected := []byte{0x1c} expected = append(expected, encodeVarInt(0xbeef)...) expected = append(expected, encodeVarInt(0x12345)...) // frame type expected = append(expected, encodeVarInt(0)...) // reason phrase length Expect(b.Bytes()).To(Equal(expected)) }) It("writes a frame with a reason phrase", func() { b := &bytes.Buffer{} frame := &ConnectionCloseFrame{ ErrorCode: 0xdead, ReasonPhrase: "foobar", } Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) expected := []byte{0x1c} 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.Bytes()).To(Equal(expected)) }) It("writes a frame with an application error code", func() { b := &bytes.Buffer{} frame := &ConnectionCloseFrame{ IsApplicationError: true, ErrorCode: 0xdead, ReasonPhrase: "foobar", } Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) expected := []byte{0x1d} expected = append(expected, encodeVarInt(0xdead)...) expected = append(expected, encodeVarInt(6)...) // reason phrase length expected = append(expected, []byte("foobar")...) Expect(b.Bytes()).To(Equal(expected)) }) It("has proper min length, for a frame containing a QUIC error code", func() { b := &bytes.Buffer{} f := &ConnectionCloseFrame{ ErrorCode: 0xcafe, FrameType: 0xdeadbeef, ReasonPhrase: "foobar", } Expect(f.Write(b, versionIETFFrames)).To(Succeed()) Expect(f.Length(versionIETFFrames)).To(Equal(protocol.ByteCount(b.Len()))) }) It("has proper min length, for a frame containing an application error code", func() { b := &bytes.Buffer{} f := &ConnectionCloseFrame{ IsApplicationError: true, ErrorCode: 0xcafe, ReasonPhrase: "foobar", } err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(f.Length(versionIETFFrames)).To(Equal(protocol.ByteCount(b.Len()))) }) }) }) quic-go-0.25.0/internal/wire/crypto_frame.go000066400000000000000000000052421417145451600207600ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A CryptoFrame is a CRYPTO frame type CryptoFrame struct { Offset protocol.ByteCount Data []byte } func parseCryptoFrame(r *bytes.Reader, _ protocol.VersionNumber) (*CryptoFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } frame := &CryptoFrame{} offset, err := quicvarint.Read(r) if err != nil { return nil, err } frame.Offset = protocol.ByteCount(offset) dataLen, err := quicvarint.Read(r) if err != nil { return nil, err } if dataLen > uint64(r.Len()) { return nil, io.EOF } if dataLen != 0 { frame.Data = make([]byte, dataLen) if _, err := io.ReadFull(r, frame.Data); err != nil { // this should never happen, since we already checked the dataLen earlier return nil, err } } return frame, nil } func (f *CryptoFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x6) quicvarint.Write(b, uint64(f.Offset)) quicvarint.Write(b, uint64(len(f.Data))) b.Write(f.Data) return nil } // Length of a written frame func (f *CryptoFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.Offset)) + quicvarint.Len(uint64(len(f.Data))) + protocol.ByteCount(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 := 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.VersionNumber) (*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 } quic-go-0.25.0/internal/wire/crypto_frame_test.go000066400000000000000000000111171417145451600220150ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("CRYPTO frame", func() { Context("when parsing", func() { It("parses", func() { data := []byte{0x6} data = append(data, encodeVarInt(0xdecafbad)...) // offset data = append(data, encodeVarInt(6)...) // length data = append(data, []byte("foobar")...) r := bytes.NewReader(data) frame, err := parseCryptoFrame(r, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.Offset).To(Equal(protocol.ByteCount(0xdecafbad))) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(r.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x6} data = append(data, encodeVarInt(0xdecafbad)...) // offset data = append(data, encodeVarInt(6)...) // data length data = append(data, []byte("foobar")...) _, err := parseCryptoFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseCryptoFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("when writing", func() { It("writes a frame", func() { f := &CryptoFrame{ Offset: 0x123456, Data: []byte("foobar"), } b := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x6} expected = append(expected, encodeVarInt(0x123456)...) // offset expected = append(expected, encodeVarInt(6)...) // length expected = append(expected, []byte("foobar")...) Expect(b.Bytes()).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, } b := &bytes.Buffer{} var frameOneByteTooSmallCounter int for i := 1; i < maxSize; i++ { b.Reset() 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} err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] err := f.Write(b, versionIETFFrames) 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 b.Len() == i-1 { frameOneByteTooSmallCounter++ continue } Expect(b.Len()).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(versionIETFFrames)).To(Equal(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(versionIETFFrames) - 6 new, needsSplit := f.MaybeSplitOffFrame(hdrLen+3, versionIETFFrames) 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(versionIETFFrames), versionIETFFrames) 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(versionIETFFrames) - 6 for i := protocol.ByteCount(0); i <= length; i++ { f, needsSplit := f.MaybeSplitOffFrame(i, versionIETFFrames) Expect(needsSplit).To(BeTrue()) Expect(f).To(BeNil()) } f, needsSplit := f.MaybeSplitOffFrame(length+1, versionIETFFrames) Expect(needsSplit).To(BeTrue()) Expect(f).ToNot(BeNil()) }) }) }) quic-go-0.25.0/internal/wire/data_blocked_frame.go000066400000000000000000000016431417145451600220350ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A DataBlockedFrame is a DATA_BLOCKED frame type DataBlockedFrame struct { MaximumData protocol.ByteCount } func parseDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DataBlockedFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } offset, err := quicvarint.Read(r) if err != nil { return nil, err } return &DataBlockedFrame{ MaximumData: protocol.ByteCount(offset), }, nil } func (f *DataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { typeByte := uint8(0x14) b.WriteByte(typeByte) quicvarint.Write(b, uint64(f.MaximumData)) return nil } // Length of a written frame func (f *DataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.MaximumData)) } quic-go-0.25.0/internal/wire/data_blocked_frame_test.go000066400000000000000000000030551417145451600230730ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("DATA_BLOCKED frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { data := []byte{0x14} data = append(data, encodeVarInt(0x12345678)...) b := bytes.NewReader(data) frame, err := parseDataBlockedFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.MaximumData).To(Equal(protocol.ByteCount(0x12345678))) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x14} data = append(data, encodeVarInt(0x12345678)...) _, err := parseDataBlockedFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).ToNot(HaveOccurred()) for i := range data { _, err := parseDataBlockedFrame(bytes.NewReader(data[:i]), versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { b := &bytes.Buffer{} frame := DataBlockedFrame{MaximumData: 0xdeadbeef} err := frame.Write(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x14} expected = append(expected, encodeVarInt(0xdeadbeef)...) Expect(b.Bytes()).To(Equal(expected)) }) It("has the correct min length", func() { frame := DataBlockedFrame{MaximumData: 0x12345} Expect(frame.Length(versionIETFFrames)).To(Equal(1 + quicvarint.Len(0x12345))) }) }) }) quic-go-0.25.0/internal/wire/datagram_frame.go000066400000000000000000000036401417145451600212200ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A DatagramFrame is a DATAGRAM frame type DatagramFrame struct { DataLenPresent bool Data []byte } func parseDatagramFrame(r *bytes.Reader, _ protocol.VersionNumber) (*DatagramFrame, error) { typeByte, err := r.ReadByte() if err != nil { return nil, err } f := &DatagramFrame{} f.DataLenPresent = typeByte&0x1 > 0 var length uint64 if f.DataLenPresent { var err error len, err := quicvarint.Read(r) if err != nil { return nil, err } if len > uint64(r.Len()) { return nil, io.EOF } length = len } else { length = uint64(r.Len()) } f.Data = make([]byte, length) if _, err := io.ReadFull(r, f.Data); err != nil { return nil, err } return f, nil } func (f *DatagramFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { typeByte := uint8(0x30) if f.DataLenPresent { typeByte ^= 0x1 } b.WriteByte(typeByte) if f.DataLenPresent { quicvarint.Write(b, uint64(len(f.Data))) } b.Write(f.Data) return nil } // MaxDataLen returns the maximum data length func (f *DatagramFrame) MaxDataLen(maxSize protocol.ByteCount, version protocol.VersionNumber) 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.VersionNumber) protocol.ByteCount { length := 1 + protocol.ByteCount(len(f.Data)) if f.DataLenPresent { length += quicvarint.Len(uint64(len(f.Data))) } return length } quic-go-0.25.0/internal/wire/datagram_frame_test.go000066400000000000000000000121631417145451600222570ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("STREAM frame", func() { Context("when parsing", func() { It("parses a frame containing a length", func() { data := []byte{0x30 ^ 0x1} data = append(data, encodeVarInt(0x6)...) // length data = append(data, []byte("foobar")...) r := bytes.NewReader(data) frame, err := parseDatagramFrame(r, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.Data).To(Equal([]byte("foobar"))) Expect(frame.DataLenPresent).To(BeTrue()) Expect(r.Len()).To(BeZero()) }) It("parses a frame without length", func() { data := []byte{0x30} data = append(data, []byte("Lorem ipsum dolor sit amet")...) r := bytes.NewReader(data) frame, err := parseDatagramFrame(r, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.Data).To(Equal([]byte("Lorem ipsum dolor sit amet"))) Expect(frame.DataLenPresent).To(BeFalse()) Expect(r.Len()).To(BeZero()) }) It("errors when the length is longer than the rest of the frame", func() { data := []byte{0x30 ^ 0x1} data = append(data, encodeVarInt(0x6)...) // length data = append(data, []byte("fooba")...) r := bytes.NewReader(data) _, err := parseDatagramFrame(r, versionIETFFrames) Expect(err).To(MatchError(io.EOF)) }) It("errors on EOFs", func() { data := []byte{0x30 ^ 0x1} data = append(data, encodeVarInt(6)...) // length data = append(data, []byte("foobar")...) _, err := parseDatagramFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseDatagramFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a frame with length", func() { f := &DatagramFrame{ DataLenPresent: true, Data: []byte("foobar"), } buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) expected := []byte{0x30 ^ 0x1} expected = append(expected, encodeVarInt(0x6)...) expected = append(expected, []byte("foobar")...) Expect(buf.Bytes()).To(Equal(expected)) }) It("writes a frame without length", func() { f := &DatagramFrame{Data: []byte("Lorem ipsum")} buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) expected := []byte{0x30} expected = append(expected, []byte("Lorem ipsum")...) Expect(buf.Bytes()).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(versionIETFFrames)).To(Equal(1 + quicvarint.Len(6) + 6)) }) It("has the right length for a frame without length", func() { f := &DatagramFrame{Data: []byte("foobar")} Expect(f.Length(versionIETFFrames)).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), versionIETFFrames) 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} Expect(f.Write(b, versionIETFFrames)).To(Succeed()) Expect(b.Len()).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] Expect(f.Write(b, versionIETFFrames)).To(Succeed()) Expect(b.Len()).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 := &DatagramFrame{DataLenPresent: true} b := &bytes.Buffer{} var frameOneByteTooSmallCounter int for i := 1; i < 3000; i++ { b.Reset() f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), versionIETFFrames) 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} Expect(f.Write(b, versionIETFFrames)).To(Succeed()) Expect(b.Len()).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] Expect(f.Write(b, versionIETFFrames)).To(Succeed()) // 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 b.Len() == i-1 { frameOneByteTooSmallCounter++ continue } Expect(b.Len()).To(Equal(i)) } Expect(frameOneByteTooSmallCounter).To(Equal(1)) }) }) }) quic-go-0.25.0/internal/wire/extended_header.go000066400000000000000000000161201417145451600213730ustar00rootroot00000000000000package wire import ( "bytes" "errors" "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/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(b *bytes.Reader, v protocol.VersionNumber) (bool /* reserved bits valid */, error) { startLen := b.Len() // read the (now unencrypted) first byte var err error h.typeByte, err = b.ReadByte() if err != nil { return false, err } if _, err := b.Seek(int64(h.Header.ParsedLen())-1, io.SeekCurrent); err != nil { return false, err } var reservedBitsValid bool if h.IsLongHeader { reservedBitsValid, err = h.parseLongHeader(b, v) } else { reservedBitsValid, err = h.parseShortHeader(b, v) } if err != nil { return false, err } h.parsedLen = protocol.ByteCount(startLen - b.Len()) return reservedBitsValid, err } func (h *ExtendedHeader) parseLongHeader(b *bytes.Reader, _ protocol.VersionNumber) (bool /* reserved bits valid */, error) { if err := h.readPacketNumber(b); err != nil { return false, err } if h.typeByte&0xc != 0 { return false, nil } return true, nil } func (h *ExtendedHeader) parseShortHeader(b *bytes.Reader, _ protocol.VersionNumber) (bool /* reserved bits valid */, error) { h.KeyPhase = protocol.KeyPhaseZero if h.typeByte&0x4 > 0 { h.KeyPhase = protocol.KeyPhaseOne } if err := h.readPacketNumber(b); err != nil { return false, err } if h.typeByte&0x18 != 0 { return false, nil } return true, nil } func (h *ExtendedHeader) readPacketNumber(b *bytes.Reader) error { h.PacketNumberLen = protocol.PacketNumberLen(h.typeByte&0x3) + 1 switch h.PacketNumberLen { case protocol.PacketNumberLen1: n, err := b.ReadByte() if err != nil { return err } h.PacketNumber = protocol.PacketNumber(n) case protocol.PacketNumberLen2: n, err := utils.BigEndian.ReadUint16(b) if err != nil { return err } h.PacketNumber = protocol.PacketNumber(n) case protocol.PacketNumberLen3: n, err := utils.BigEndian.ReadUint24(b) if err != nil { return err } h.PacketNumber = protocol.PacketNumber(n) case protocol.PacketNumberLen4: n, err := utils.BigEndian.ReadUint32(b) if err != nil { return err } h.PacketNumber = protocol.PacketNumber(n) default: return fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen) } return nil } // Write writes the Header. func (h *ExtendedHeader) Write(b *bytes.Buffer, ver protocol.VersionNumber) error { if h.DestConnectionID.Len() > protocol.MaxConnIDLen { return fmt.Errorf("invalid connection ID length: %d bytes", h.DestConnectionID.Len()) } if h.SrcConnectionID.Len() > protocol.MaxConnIDLen { return fmt.Errorf("invalid connection ID length: %d bytes", h.SrcConnectionID.Len()) } if h.IsLongHeader { return h.writeLongHeader(b, ver) } return h.writeShortHeader(b, ver) } func (h *ExtendedHeader) writeLongHeader(b *bytes.Buffer, _ protocol.VersionNumber) error { var packetType uint8 //nolint:exhaustive switch h.Type { case protocol.PacketTypeInitial: packetType = 0x0 case protocol.PacketType0RTT: packetType = 0x1 case protocol.PacketTypeHandshake: packetType = 0x2 case protocol.PacketTypeRetry: packetType = 0x3 } firstByte := 0xc0 | packetType<<4 if h.Type != protocol.PacketTypeRetry { // Retry packets don't have a packet number firstByte |= uint8(h.PacketNumberLen - 1) } b.WriteByte(firstByte) utils.BigEndian.WriteUint32(b, uint32(h.Version)) b.WriteByte(uint8(h.DestConnectionID.Len())) b.Write(h.DestConnectionID.Bytes()) b.WriteByte(uint8(h.SrcConnectionID.Len())) b.Write(h.SrcConnectionID.Bytes()) //nolint:exhaustive switch h.Type { case protocol.PacketTypeRetry: b.Write(h.Token) return nil case protocol.PacketTypeInitial: quicvarint.Write(b, uint64(len(h.Token))) b.Write(h.Token) } quicvarint.WriteWithLen(b, uint64(h.Length), 2) return h.writePacketNumber(b) } func (h *ExtendedHeader) writeShortHeader(b *bytes.Buffer, _ protocol.VersionNumber) error { typeByte := 0x40 | uint8(h.PacketNumberLen-1) if h.KeyPhase == protocol.KeyPhaseOne { typeByte |= byte(1 << 2) } b.WriteByte(typeByte) b.Write(h.DestConnectionID.Bytes()) return h.writePacketNumber(b) } func (h *ExtendedHeader) writePacketNumber(b *bytes.Buffer) error { switch h.PacketNumberLen { case protocol.PacketNumberLen1: b.WriteByte(uint8(h.PacketNumber)) case protocol.PacketNumberLen2: utils.BigEndian.WriteUint16(b, uint16(h.PacketNumber)) case protocol.PacketNumberLen3: utils.BigEndian.WriteUint24(b, uint32(h.PacketNumber)) case protocol.PacketNumberLen4: utils.BigEndian.WriteUint32(b, uint32(h.PacketNumber)) default: return fmt.Errorf("invalid packet number length: %d", h.PacketNumberLen) } return nil } // 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(v protocol.VersionNumber) protocol.ByteCount { if h.IsLongHeader { 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 += quicvarint.Len(uint64(len(h.Token))) + protocol.ByteCount(len(h.Token)) } return length } length := protocol.ByteCount(1 /* type byte */ + h.DestConnectionID.Len()) length += protocol.ByteCount(h.PacketNumberLen) return length } // Log logs the Header func (h *ExtendedHeader) Log(logger utils.Logger) { if h.IsLongHeader { 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) } else { logger.Debugf("\tShort Header{DestConnectionID: %s, PacketNumber: %d, PacketNumberLen: %d, KeyPhase: %s}", h.DestConnectionID, h.PacketNumber, h.PacketNumberLen, h.KeyPhase) } } quic-go-0.25.0/internal/wire/extended_header_test.go000066400000000000000000000400571417145451600224400ustar00rootroot00000000000000package wire import ( "bytes" "log" "os" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Header", func() { const versionIETFHeader = protocol.VersionTLS // a QUIC version that uses the IETF Header format Context("Writing", func() { var buf *bytes.Buffer BeforeEach(func() { buf = &bytes.Buffer{} }) Context("Long Header", func() { srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} It("writes", func() { Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe}, SrcConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad, 0x0, 0x0, 0x13, 0x37}, Version: 0x1020304, Length: protocol.InitialPacketSizeIPv4, }, PacketNumber: 0xdecaf, PacketNumberLen: protocol.PacketNumberLen3, }).Write(buf, versionIETFHeader)).To(Succeed()) 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(protocol.InitialPacketSizeIPv4)...) // length expected = append(expected, []byte{0xd, 0xec, 0xaf}...) // packet number Expect(buf.Bytes()).To(Equal(expected)) }) It("refuses to write a header with a too long connection ID", func() { err := (&ExtendedHeader{ Header: Header{ IsLongHeader: true, SrcConnectionID: srcConnID, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}, // connection IDs must be at most 20 bytes long Version: 0x1020304, Type: 0x5, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Write(buf, versionIETFHeader) Expect(err).To(MatchError("invalid connection ID length: 21 bytes")) }) It("writes a header with a 20 byte connection ID", func() { err := (&ExtendedHeader{ Header: Header{ IsLongHeader: true, SrcConnectionID: srcConnID, DestConnectionID: protocol.ConnectionID{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, }).Write(buf, versionIETFHeader) Expect(err).ToNot(HaveOccurred()) Expect(buf.Bytes()).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.") Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Version: 0x1020304, Type: protocol.PacketTypeInitial, Token: token, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Write(buf, versionIETFHeader)).To(Succeed()) expectedSubstring := append(encodeVarInt(uint64(len(token))), token...) Expect(buf.Bytes()).To(ContainSubstring(string(expectedSubstring))) }) It("uses a 2-byte encoding for the length on Initial packets", func() { Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Version: 0x1020304, Type: protocol.PacketTypeInitial, Length: 37, }, PacketNumber: 0xdecafbad, PacketNumberLen: protocol.PacketNumberLen4, }).Write(buf, versionIETFHeader)).To(Succeed()) b := &bytes.Buffer{} quicvarint.WriteWithLen(b, 37, 2) Expect(buf.Bytes()[buf.Len()-6 : buf.Len()-4]).To(Equal(b.Bytes())) }) 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.") Expect((&ExtendedHeader{Header: Header{ IsLongHeader: true, Version: 0x1020304, Type: protocol.PacketTypeRetry, Token: token, }}).Write(buf, versionIETFHeader)).To(Succeed()) expected := []byte{ 0xc0 | 0x3<<4, 0x1, 0x2, 0x3, 0x4, // version number 0x0, // dest connection ID length 0x0, // src connection ID length } expected = append(expected, token...) Expect(buf.Bytes()).To(Equal(expected)) }) }) Context("short header", func() { It("writes a header with connection ID", func() { Expect((&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 0x42, }).Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Bytes()).To(Equal([]byte{ 0x40, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37, // connection ID 0x42, // packet number })) }) It("writes a header without connection ID", func() { Expect((&ExtendedHeader{ PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 0x42, }).Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Bytes()).To(Equal([]byte{ 0x40, 0x42, // packet number })) }) It("writes a header with a 2 byte packet number", func() { Expect((&ExtendedHeader{ PacketNumberLen: protocol.PacketNumberLen2, PacketNumber: 0x765, }).Write(buf, versionIETFHeader)).To(Succeed()) expected := []byte{0x40 | 0x1} expected = append(expected, []byte{0x7, 0x65}...) // packet number Expect(buf.Bytes()).To(Equal(expected)) }) It("writes a header with a 4 byte packet number", func() { Expect((&ExtendedHeader{ PacketNumberLen: protocol.PacketNumberLen4, PacketNumber: 0x12345678, }).Write(buf, versionIETFHeader)).To(Succeed()) expected := []byte{0x40 | 0x3} expected = append(expected, []byte{0x12, 0x34, 0x56, 0x78}...) Expect(buf.Bytes()).To(Equal(expected)) }) It("errors when given an invalid packet number length", func() { err := (&ExtendedHeader{ PacketNumberLen: 5, PacketNumber: 0xdecafbad, }).Write(buf, versionIETFHeader) Expect(err).To(MatchError("invalid packet number length: 5")) }) It("writes the Key Phase Bit", func() { Expect((&ExtendedHeader{ KeyPhase: protocol.KeyPhaseOne, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 0x42, }).Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Bytes()).To(Equal([]byte{ 0x40 | 0x4, 0x42, // packet number })) }) }) }) Context("getting the length", func() { var buf *bytes.Buffer BeforeEach(func() { buf = &bytes.Buffer{} }) It("has the right length for the Long Header, for a short length", func() { h := &ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{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(versionIETFHeader)).To(BeEquivalentTo(expectedLen)) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(expectedLen)) }) It("has the right length for the Long Header, for a long length", func() { h := &ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{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(versionIETFHeader)).To(BeEquivalentTo(expectedLen)) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(expectedLen)) }) It("has the right length for an Initial that has a short length", func() { h := &ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{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(versionIETFHeader)).To(BeEquivalentTo(expectedLen)) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(expectedLen)) }) It("has the right length for an Initial not containing a Token", func() { h := &ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{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(versionIETFHeader)).To(BeEquivalentTo(expectedLen)) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(expectedLen)) }) It("has the right length for an Initial containing a Token", func() { h := &ExtendedHeader{ Header: Header{ IsLongHeader: true, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{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(versionIETFHeader)).To(BeEquivalentTo(expectedLen)) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(expectedLen)) }) It("has the right length for a Short Header containing a connection ID", func() { h := &ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, }, PacketNumberLen: protocol.PacketNumberLen1, } Expect(h.GetLength(versionIETFHeader)).To(Equal(protocol.ByteCount(1 + 8 + 1))) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(10)) }) It("has the right length for a short header without a connection ID", func() { h := &ExtendedHeader{PacketNumberLen: protocol.PacketNumberLen1} Expect(h.GetLength(versionIETFHeader)).To(Equal(protocol.ByteCount(1 + 1))) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(2)) }) It("has the right length for a short header with a 2 byte packet number", func() { h := &ExtendedHeader{PacketNumberLen: protocol.PacketNumberLen2} Expect(h.GetLength(versionIETFHeader)).To(Equal(protocol.ByteCount(1 + 2))) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(3)) }) It("has the right length for a short header with a 5 byte packet number", func() { h := &ExtendedHeader{PacketNumberLen: protocol.PacketNumberLen4} Expect(h.GetLength(versionIETFHeader)).To(Equal(protocol.ByteCount(1 + 4))) Expect(h.Write(buf, versionIETFHeader)).To(Succeed()) Expect(buf.Len()).To(Equal(5)) }) }) 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{ IsLongHeader: true, DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}, SrcConnectionID: protocol.ConnectionID{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{ IsLongHeader: true, DestConnectionID: protocol.ConnectionID{0xca, 0xfe, 0x13, 0x37}, SrcConnectionID: protocol.ConnectionID{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{ IsLongHeader: true, DestConnectionID: protocol.ConnectionID{0xca, 0xfe, 0x13, 0x37}, SrcConnectionID: protocol.ConnectionID{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{ IsLongHeader: true, DestConnectionID: protocol.ConnectionID{0xca, 0xfe, 0x13, 0x37}, SrcConnectionID: protocol.ConnectionID{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}")) }) It("logs Short Headers containing a connection ID", func() { (&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}, }, KeyPhase: protocol.KeyPhaseOne, PacketNumber: 1337, PacketNumberLen: 4, }).Log(logger) Expect(buf.String()).To(ContainSubstring("Short Header{DestConnectionID: deadbeefcafe1337, PacketNumber: 1337, PacketNumberLen: 4, KeyPhase: 1}")) }) }) }) quic-go-0.25.0/internal/wire/frame_parser.go000066400000000000000000000072741417145451600207430ustar00rootroot00000000000000package wire import ( "bytes" "errors" "fmt" "reflect" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" ) type frameParser struct { ackDelayExponent uint8 supportsDatagrams bool version protocol.VersionNumber } // NewFrameParser creates a new frame parser. func NewFrameParser(supportsDatagrams bool, v protocol.VersionNumber) FrameParser { return &frameParser{ supportsDatagrams: supportsDatagrams, version: v, } } // ParseNext parses the next frame. // It skips PADDING frames. func (p *frameParser) ParseNext(r *bytes.Reader, encLevel protocol.EncryptionLevel) (Frame, error) { for r.Len() != 0 { typeByte, _ := r.ReadByte() if typeByte == 0x0 { // PADDING frame continue } r.UnreadByte() f, err := p.parseFrame(r, typeByte, encLevel) if err != nil { return nil, &qerr.TransportError{ FrameType: uint64(typeByte), ErrorCode: qerr.FrameEncodingError, ErrorMessage: err.Error(), } } return f, nil } return nil, nil } func (p *frameParser) parseFrame(r *bytes.Reader, typeByte byte, encLevel protocol.EncryptionLevel) (Frame, error) { var frame Frame var err error if typeByte&0xf8 == 0x8 { frame, err = parseStreamFrame(r, p.version) } else { switch typeByte { case 0x1: frame, err = parsePingFrame(r, p.version) case 0x2, 0x3: ackDelayExponent := p.ackDelayExponent if encLevel != protocol.Encryption1RTT { ackDelayExponent = protocol.DefaultAckDelayExponent } frame, err = parseAckFrame(r, ackDelayExponent, p.version) case 0x4: frame, err = parseResetStreamFrame(r, p.version) case 0x5: frame, err = parseStopSendingFrame(r, p.version) case 0x6: frame, err = parseCryptoFrame(r, p.version) case 0x7: frame, err = parseNewTokenFrame(r, p.version) case 0x10: frame, err = parseMaxDataFrame(r, p.version) case 0x11: frame, err = parseMaxStreamDataFrame(r, p.version) case 0x12, 0x13: frame, err = parseMaxStreamsFrame(r, p.version) case 0x14: frame, err = parseDataBlockedFrame(r, p.version) case 0x15: frame, err = parseStreamDataBlockedFrame(r, p.version) case 0x16, 0x17: frame, err = parseStreamsBlockedFrame(r, p.version) case 0x18: frame, err = parseNewConnectionIDFrame(r, p.version) case 0x19: frame, err = parseRetireConnectionIDFrame(r, p.version) case 0x1a: frame, err = parsePathChallengeFrame(r, p.version) case 0x1b: frame, err = parsePathResponseFrame(r, p.version) case 0x1c, 0x1d: frame, err = parseConnectionCloseFrame(r, p.version) case 0x1e: frame, err = parseHandshakeDoneFrame(r, p.version) case 0x30, 0x31: if p.supportsDatagrams { frame, err = parseDatagramFrame(r, p.version) break } fallthrough default: err = errors.New("unknown frame type") } } if err != nil { return nil, err } if !p.isAllowedAtEncLevel(frame, encLevel) { return nil, fmt.Errorf("%s not allowed at encryption level %s", reflect.TypeOf(frame).Elem().Name(), encLevel) } return frame, 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") } } func (p *frameParser) SetAckDelayExponent(exp uint8) { p.ackDelayExponent = exp } quic-go-0.25.0/internal/wire/frame_parser_test.go000066400000000000000000000335711417145451600220010ustar00rootroot00000000000000package wire import ( "bytes" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Frame parsing", func() { var ( buf *bytes.Buffer parser FrameParser ) BeforeEach(func() { buf = &bytes.Buffer{} parser = NewFrameParser(true, versionIETFFrames) }) It("returns nil if there's nothing more to read", func() { f, err := parser.ParseNext(bytes.NewReader(nil), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeNil()) }) It("skips PADDING frames", func() { buf.Write([]byte{0}) // PADDING frame (&PingFrame{}).Write(buf, versionIETFFrames) f, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(f).To(Equal(&PingFrame{})) }) It("handles PADDING at the end", func() { r := bytes.NewReader([]byte{0, 0, 0}) f, err := parser.ParseNext(r, protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeNil()) Expect(r.Len()).To(BeZero()) }) It("unpacks ACK frames", func() { f := &AckFrame{AckRanges: []AckRange{{Smallest: 1, Largest: 0x13}}} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(BeAssignableToTypeOf(f)) Expect(frame.(*AckFrame).LargestAcked()).To(Equal(protocol.PacketNumber(0x13))) }) 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, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) 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, } Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.EncryptionHandshake) 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, } err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks STOP_SENDING frames", func() { f := &StopSendingFrame{StreamID: 0x42} buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks CRYPTO frames", func() { f := &CryptoFrame{ Offset: 0x1337, Data: []byte("lorem ipsum"), } err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(Equal(f)) }) It("unpacks NEW_TOKEN frames", func() { f := &NewTokenFrame{Token: []byte("foobar")} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(Equal(f)) }) It("unpacks STREAM frames", func() { f := &StreamFrame{ StreamID: 0x42, Offset: 0x1337, Fin: true, Data: []byte("foobar"), } err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).ToNot(BeNil()) Expect(frame).To(Equal(f)) }) It("unpacks MAX_DATA frames", func() { f := &MaxDataFrame{ MaximumData: 0xcafe, } buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks MAX_STREAM_DATA frames", func() { f := &MaxStreamDataFrame{ StreamID: 0xdeadbeef, MaximumStreamData: 0xdecafbad, } buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks MAX_STREAMS frames", func() { f := &MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: 0x1337, } buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks DATA_BLOCKED frames", func() { f := &DataBlockedFrame{MaximumData: 0x1234} buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks STREAM_DATA_BLOCKED frames", func() { f := &StreamDataBlockedFrame{ StreamID: 0xdeadbeef, MaximumStreamData: 0xdead, } err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks STREAMS_BLOCKED frames", func() { f := &StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: 0x1234567, } buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks NEW_CONNECTION_ID frames", func() { f := &NewConnectionIDFrame{ SequenceNumber: 0x1337, ConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, StatelessResetToken: protocol.StatelessResetToken{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, } buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks RETIRE_CONNECTION_ID frames", func() { f := &RetireConnectionIDFrame{SequenceNumber: 0x1337} buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks PATH_CHALLENGE frames", func() { f := &PathChallengeFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) 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})) }) It("unpacks PATH_RESPONSE frames", func() { f := &PathResponseFrame{Data: [8]byte{1, 2, 3, 4, 5, 6, 7, 8}} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) 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})) }) It("unpacks CONNECTION_CLOSE frames", func() { f := &ConnectionCloseFrame{ IsApplicationError: true, ReasonPhrase: "foobar", } buf := &bytes.Buffer{} err := f.Write(buf, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks HANDSHAKE_DONE frames", func() { f := &HandshakeDoneFrame{} buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("unpacks DATAGRAM frames", func() { f := &DatagramFrame{Data: []byte("foobar")} buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) frame, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("errors when DATAGRAM frames are not supported", func() { parser = NewFrameParser(false, versionIETFFrames) f := &DatagramFrame{Data: []byte("foobar")} buf := &bytes.Buffer{} Expect(f.Write(buf, versionIETFFrames)).To(Succeed()) _, err := parser.ParseNext(bytes.NewReader(buf.Bytes()), protocol.Encryption1RTT) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.FrameEncodingError, FrameType: 0x30, ErrorMessage: "unknown frame type", })) }) It("errors on invalid type", func() { _, err := parser.ParseNext(bytes.NewReader([]byte{0x42}), protocol.Encryption1RTT) 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 := &bytes.Buffer{} f.Write(b, versionIETFFrames) _, err := parser.ParseNext(bytes.NewReader(b.Bytes()[:b.Len()-2]), protocol.Encryption1RTT) 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.ConnectionID{0xde, 0xad, 0xbe, 0xef}}, &RetireConnectionIDFrame{}, &PathChallengeFrame{}, &PathResponseFrame{}, &ConnectionCloseFrame{}, &HandshakeDoneFrame{}, &DatagramFrame{}, } var framesSerialized [][]byte BeforeEach(func() { framesSerialized = nil for _, frame := range frames { buf := &bytes.Buffer{} Expect(frame.Write(buf, versionIETFFrames)).To(Succeed()) framesSerialized = append(framesSerialized, buf.Bytes()) } }) It("rejects all frames but ACK, CRYPTO, PING and CONNECTION_CLOSE in Initial packets", func() { for i, b := range framesSerialized { _, err := parser.ParseNext(bytes.NewReader(b), protocol.EncryptionInitial) 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(bytes.NewReader(b), protocol.EncryptionHandshake) 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(bytes.NewReader(b), protocol.Encryption0RTT) 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(bytes.NewReader(b), protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) } }) }) }) quic-go-0.25.0/internal/wire/handshake_done_frame.go000066400000000000000000000012461417145451600223730ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" ) // A HandshakeDoneFrame is a HANDSHAKE_DONE frame type HandshakeDoneFrame struct{} // ParseHandshakeDoneFrame parses a HandshakeDone frame func parseHandshakeDoneFrame(r *bytes.Reader, _ protocol.VersionNumber) (*HandshakeDoneFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } return &HandshakeDoneFrame{}, nil } func (f *HandshakeDoneFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x1e) return nil } // Length of a written frame func (f *HandshakeDoneFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { return 1 } quic-go-0.25.0/internal/wire/header.go000066400000000000000000000153611417145451600175210ustar00rootroot00000000000000package wire import ( "bytes" "encoding/binary" "errors" "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/quicvarint" ) // ParseConnectionID parses the destination connection ID of a packet. // It uses the data slice for the connection ID. // That means that the connection ID must not be used after the packet buffer is released. func ParseConnectionID(data []byte, shortHeaderConnIDLen int) (protocol.ConnectionID, error) { if len(data) == 0 { return nil, io.EOF } isLongHeader := data[0]&0x80 > 0 if !isLongHeader { if len(data) < shortHeaderConnIDLen+1 { return nil, io.EOF } return protocol.ConnectionID(data[1 : 1+shortHeaderConnIDLen]), nil } if len(data) < 6 { return nil, io.EOF } destConnIDLen := int(data[5]) if len(data) < 6+destConnIDLen { return nil, io.EOF } return protocol.ConnectionID(data[6 : 6+destConnIDLen]), nil } // IsVersionNegotiationPacket says if this is a version negotiation packet func IsVersionNegotiationPacket(b []byte) bool { if len(b) < 5 { return false } return b[0]&0x80 > 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 b[0]&0x80 == 0 { return false } if !protocol.IsSupportedVersion(protocol.SupportedVersions, protocol.VersionNumber(binary.BigEndian.Uint32(b[1:5]))) { return false } return b[0]&0x30>>4 == 0x1 } var ErrUnsupportedVersion = errors.New("unsupported version") // The Header is the version independent part of the header type Header struct { IsLongHeader bool typeByte byte Type protocol.PacketType Version protocol.VersionNumber 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 packet. // If the packet has a long header, the packet is cut according to the length field. // If we understand the version, the packet is header up unto the packet number. // Otherwise, only the invariant part of the header is parsed. func ParsePacket(data []byte, shortHeaderConnIDLen int) (*Header, []byte /* packet data */, []byte /* rest */, error) { hdr, err := parseHeader(bytes.NewReader(data), shortHeaderConnIDLen) if err != nil { if err == ErrUnsupportedVersion { return hdr, nil, nil, ErrUnsupportedVersion } return nil, nil, nil, err } var rest []byte if hdr.IsLongHeader { 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) rest = data[packetLen:] data = data[:packetLen] } return hdr, data, rest, nil } // ParseHeader parses the header. // For short header packets: up to the packet number. // For long header packets: // * if we understand the version: up to the packet number // * if not, only the invariant part of the header func parseHeader(b *bytes.Reader, shortHeaderConnIDLen int) (*Header, error) { startLen := b.Len() h, err := parseHeaderImpl(b, shortHeaderConnIDLen) if err != nil { return h, err } h.parsedLen = protocol.ByteCount(startLen - b.Len()) return h, err } func parseHeaderImpl(b *bytes.Reader, shortHeaderConnIDLen int) (*Header, error) { typeByte, err := b.ReadByte() if err != nil { return nil, err } h := &Header{ typeByte: typeByte, IsLongHeader: typeByte&0x80 > 0, } if !h.IsLongHeader { if h.typeByte&0x40 == 0 { return nil, errors.New("not a QUIC packet") } if err := h.parseShortHeader(b, shortHeaderConnIDLen); err != nil { return nil, err } return h, nil } return h, h.parseLongHeader(b) } func (h *Header) parseShortHeader(b *bytes.Reader, shortHeaderConnIDLen int) error { var err error h.DestConnectionID, err = protocol.ReadConnectionID(b, shortHeaderConnIDLen) return err } func (h *Header) parseLongHeader(b *bytes.Reader) error { v, err := utils.BigEndian.ReadUint32(b) if err != nil { return err } h.Version = protocol.VersionNumber(v) if h.Version != 0 && h.typeByte&0x40 == 0 { return errors.New("not a QUIC packet") } destConnIDLen, err := b.ReadByte() if err != nil { return err } h.DestConnectionID, err = protocol.ReadConnectionID(b, int(destConnIDLen)) if err != nil { return err } srcConnIDLen, err := b.ReadByte() if err != nil { return err } h.SrcConnectionID, err = protocol.ReadConnectionID(b, int(srcConnIDLen)) if err != nil { return err } if h.Version == 0 { // version negotiation packet return 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 ErrUnsupportedVersion } switch (h.typeByte & 0x30) >> 4 { case 0x0: h.Type = protocol.PacketTypeInitial case 0x1: h.Type = protocol.PacketType0RTT case 0x2: h.Type = protocol.PacketTypeHandshake case 0x3: h.Type = protocol.PacketTypeRetry } if h.Type == protocol.PacketTypeRetry { tokenLen := b.Len() - 16 if tokenLen <= 0 { return io.EOF } h.Token = make([]byte, tokenLen) if _, err := io.ReadFull(b, h.Token); err != nil { return err } _, err := b.Seek(16, io.SeekCurrent) return err } if h.Type == protocol.PacketTypeInitial { tokenLen, err := quicvarint.Read(b) if err != nil { return err } if tokenLen > uint64(b.Len()) { return io.EOF } h.Token = make([]byte, tokenLen) if _, err := io.ReadFull(b, h.Token); err != nil { return err } } pl, err := quicvarint.Read(b) if err != nil { return err } h.Length = protocol.ByteCount(pl) return 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(b *bytes.Reader, ver protocol.VersionNumber) (*ExtendedHeader, error) { extHdr := h.toExtendedHeader() reservedBitsValid, err := extHdr.parse(b, ver) 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 { if h.IsLongHeader { return h.Type.String() } return "1-RTT" } quic-go-0.25.0/internal/wire/header_test.go000066400000000000000000000545271417145451600205670ustar00rootroot00000000000000package wire import ( "bytes" "encoding/binary" "io" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Header Parsing", func() { appendVersion := func(data []byte, v protocol.VersionNumber) []byte { offset := len(data) data = append(data, []byte{0, 0, 0, 0}...) binary.BigEndian.PutUint32(data[offset:], uint32(v)) return data } Context("Parsing the Connection ID", func() { It("parses the connection ID of a long header packet", func() { buf := &bytes.Buffer{} Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, SrcConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6}, Version: versionIETFFrames, }, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) connID, err := ParseConnectionID(buf.Bytes(), 8) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(Equal(protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad})) }) It("parses the connection ID of a short header packet", func() { buf := &bytes.Buffer{} Expect((&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, }, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) buf.Write([]byte("foobar")) connID, err := ParseConnectionID(buf.Bytes(), 4) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(Equal(protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad})) }) It("errors on EOF, for short header packets", func() { buf := &bytes.Buffer{} Expect((&ExtendedHeader{ Header: Header{ DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, }, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) data := buf.Bytes()[:buf.Len()-2] // cut the packet number _, err := ParseConnectionID(data, 8) Expect(err).ToNot(HaveOccurred()) for i := 0; i < len(data); i++ { b := make([]byte, i) copy(b, data[:i]) _, err := ParseConnectionID(b, 8) Expect(err).To(MatchError(io.EOF)) } }) It("errors on EOF, for long header packets", func() { buf := &bytes.Buffer{} Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad, 0x13, 0x37}, SrcConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 8, 9}, Version: versionIETFFrames, }, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) data := buf.Bytes()[:buf.Len()-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)) } }) }) Context("identifying 0-RTT packets", func() { var zeroRTTHeader []byte BeforeEach(func() { zeroRTTHeader = make([]byte, 5) zeroRTTHeader[0] = 0x80 | 0x1<<4 binary.BigEndian.PutUint32(zeroRTTHeader[1:], uint32(versionIETFFrames)) }) It("recognizes 0-RTT packets", func() { 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(zeroRTTHeader)).To(BeTrue()) Expect(Is0RTTPacket(append(zeroRTTHeader, []byte("foobar")...))).To(BeTrue()) }) }) 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.ConnectionID{9, 8, 7, 6, 5, 4, 3, 2, 1} srcConnID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} data := []byte{0xc0 ^ 0x3} data = appendVersion(data, versionIETFFrames) data = append(data, 0x9) // dest conn id length data = append(data, destConnID...) data = append(data, 0x4) // src conn id length data = append(data, srcConnID...) 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, 0) Expect(err).ToNot(HaveOccurred()) Expect(pdata).To(Equal(data)) Expect(hdr.IsLongHeader).To(BeTrue()) 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(versionIETFFrames)) Expect(rest).To(BeEmpty()) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0xbeef))) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen4)) Expect(b.Len()).To(Equal(6)) // foobar 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, 0) 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, 0) Expect(err).To(MatchError(ErrUnsupportedVersion)) Expect(hdr.IsLongHeader).To(BeTrue()) Expect(hdr.Version).To(Equal(protocol.VersionNumber(0xdeadbeef))) Expect(hdr.DestConnectionID).To(Equal(protocol.ConnectionID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8})) Expect(hdr.SrcConnectionID).To(Equal(protocol.ConnectionID{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, versionIETFFrames) data = append(data, 0x0) // dest conn ID len data = append(data, 0x4) // 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, 0) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketType0RTT)) Expect(hdr.SrcConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef})) Expect(hdr.DestConnectionID).To(BeEmpty()) }) It("parses a Long Header without a source connection ID", func() { data := []byte{0xc0 ^ 0x2<<4} data = appendVersion(data, versionIETFFrames) data = append(data, 0xa) // dest conn ID len data = append(data, []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}...) // 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}...) hdr, _, _, err := ParsePacket(data, 0) Expect(err).ToNot(HaveOccurred()) Expect(hdr.SrcConnectionID).To(BeEmpty()) Expect(hdr.DestConnectionID).To(Equal(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})) }) It("parses a Long Header with a 2 byte packet number", func() { data := []byte{0xc0 ^ 0x1} data = appendVersion(data, versionIETFFrames) // 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, 0) Expect(err).ToNot(HaveOccurred()) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x123))) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen2)) Expect(b.Len()).To(BeZero()) }) It("parses a Retry packet", func() { data := []byte{0xc0 | 0x3<<4 | (10 - 3) /* connection ID length */} data = appendVersion(data, versionIETFFrames) 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, 0) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeRetry)) Expect(hdr.DestConnectionID).To(Equal(protocol.ConnectionID{6, 5, 4, 3, 2, 1})) Expect(hdr.SrcConnectionID).To(Equal(protocol.ConnectionID{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, versionIETFFrames) 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, 0) Expect(err).To(MatchError(io.EOF)) }) It("errors if the token length is too large", func() { data := []byte{0xc0 ^ 0x1} data = appendVersion(data, versionIETFFrames) 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, 0) 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, versionIETFFrames) 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, 0) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeHandshake)) extHdr, err := hdr.ParseExtended(bytes.NewReader(data), versionIETFFrames) 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, versionIETFFrames) 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 := 0; i < len(data); i++ { _, _, _, err := ParsePacket(data[:i], 0) 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, versionIETFFrames) 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++ { data = data[:i] hdr, _, _, err := ParsePacket(data, 0) Expect(err).ToNot(HaveOccurred()) b := bytes.NewReader(data) _, err = hdr.ParseExtended(b, versionIETFFrames) Expect(err).To(Equal(io.EOF)) } }) It("errors on EOF, for a Retry packet", func() { data := []byte{0xc0 ^ 0x3<<4} data = appendVersion(data, versionIETFFrames) 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, 0) Expect(err).ToNot(HaveOccurred()) b := bytes.NewReader(data) _, err = hdr.ParseExtended(b, versionIETFFrames) Expect(err).To(Equal(io.EOF)) } }) Context("coalesced packets", func() { It("cuts packets", func() { buf := &bytes.Buffer{} hdr := Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4}, Length: 2 + 6, Version: versionIETFFrames, } Expect((&ExtendedHeader{ Header: hdr, PacketNumber: 0x1337, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) hdrRaw := append([]byte{}, buf.Bytes()...) buf.Write([]byte("foobar")) // payload of the first packet buf.Write([]byte("raboof")) // second packet parsedHdr, data, rest, err := ParsePacket(buf.Bytes(), 4) 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() { buf := &bytes.Buffer{} Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4}, Length: 3, Version: versionIETFFrames, }, PacketNumber: 0x1337, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) _, _, _, err := ParsePacket(buf.Bytes(), 4) 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() { buf := &bytes.Buffer{} Expect((&ExtendedHeader{ Header: Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4}, Length: 1000, Version: versionIETFFrames, }, PacketNumber: 0x1337, PacketNumberLen: 2, }).Write(buf, versionIETFFrames)).To(Succeed()) buf.Write(make([]byte, 500-2 /* for packet number length */)) _, _, _, err := ParsePacket(buf.Bytes(), 4) Expect(err).To(MatchError("packet length (500 bytes) is smaller than the expected length (1000 bytes)")) }) }) }) Context("Short Headers", func() { It("reads a Short Header with a 8 byte connection ID", func() { connID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37} data := append([]byte{0x40}, connID...) data = append(data, 0x42) // packet number Expect(IsVersionNegotiationPacket(data)).To(BeFalse()) hdr, pdata, rest, err := ParsePacket(data, 8) Expect(err).ToNot(HaveOccurred()) Expect(hdr.IsLongHeader).To(BeFalse()) Expect(hdr.DestConnectionID).To(Equal(connID)) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.KeyPhase).To(Equal(protocol.KeyPhaseZero)) Expect(extHdr.DestConnectionID).To(Equal(connID)) Expect(extHdr.SrcConnectionID).To(BeEmpty()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x42))) Expect(hdr.ParsedLen()).To(BeEquivalentTo(len(data) - 1)) Expect(extHdr.ParsedLen()).To(Equal(hdr.ParsedLen() + 1)) Expect(pdata).To(Equal(data)) Expect(rest).To(BeEmpty()) }) It("errors if 0x40 is not set", func() { connID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37} data := append([]byte{0x0}, connID...) _, _, _, err := ParsePacket(data, 8) Expect(err).To(MatchError("not a QUIC packet")) }) It("errors if the 4th or 5th bit are set", func() { connID := protocol.ConnectionID{1, 2, 3, 4, 5} data := append([]byte{0x40 | 0x10 /* set the 4th bit */}, connID...) data = append(data, 0x42) // packet number hdr, _, _, err := ParsePacket(data, 5) Expect(err).ToNot(HaveOccurred()) Expect(hdr.IsLongHeader).To(BeFalse()) extHdr, err := hdr.ParseExtended(bytes.NewReader(data), versionIETFFrames) Expect(err).To(MatchError(ErrInvalidReservedBits)) Expect(extHdr).ToNot(BeNil()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x42))) }) It("reads a Short Header with a 5 byte connection ID", func() { connID := protocol.ConnectionID{1, 2, 3, 4, 5} data := append([]byte{0x40}, connID...) data = append(data, 0x42) // packet number hdr, pdata, rest, err := ParsePacket(data, 5) Expect(err).ToNot(HaveOccurred()) Expect(pdata).To(HaveLen(len(data))) Expect(hdr.IsLongHeader).To(BeFalse()) Expect(hdr.DestConnectionID).To(Equal(connID)) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.KeyPhase).To(Equal(protocol.KeyPhaseZero)) Expect(extHdr.DestConnectionID).To(Equal(connID)) Expect(extHdr.SrcConnectionID).To(BeEmpty()) Expect(rest).To(BeEmpty()) }) It("reads the Key Phase Bit", func() { data := []byte{ 0x40 ^ 0x4, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, // connection ID } data = append(data, 11) // packet number hdr, _, _, err := ParsePacket(data, 6) Expect(err).ToNot(HaveOccurred()) Expect(hdr.IsLongHeader).To(BeFalse()) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.KeyPhase).To(Equal(protocol.KeyPhaseOne)) Expect(b.Len()).To(BeZero()) }) It("reads a header with a 2 byte packet number", func() { data := []byte{ 0x40 | 0x1, 0xde, 0xad, 0xbe, 0xef, // connection ID } data = append(data, []byte{0x13, 0x37}...) // packet number hdr, _, _, err := ParsePacket(data, 4) Expect(err).ToNot(HaveOccurred()) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.IsLongHeader).To(BeFalse()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x1337))) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen2)) Expect(b.Len()).To(BeZero()) }) It("reads a header with a 3 byte packet number", func() { data := []byte{ 0x40 | 0x2, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x1, 0x2, 0x3, 0x4, // connection ID } data = append(data, []byte{0x99, 0xbe, 0xef}...) // packet number hdr, _, _, err := ParsePacket(data, 10) Expect(err).ToNot(HaveOccurred()) b := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.IsLongHeader).To(BeFalse()) Expect(extHdr.PacketNumber).To(Equal(protocol.PacketNumber(0x99beef))) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen3)) Expect(b.Len()).To(BeZero()) }) It("errors on EOF, when parsing the header", func() { data := []byte{ 0x40 ^ 0x2, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37, // connection ID } for i := 0; i < len(data); i++ { data = data[:i] _, _, _, err := ParsePacket(data, 8) Expect(err).To(Equal(io.EOF)) } }) It("errors on EOF, when parsing the extended header", func() { data := []byte{ 0x40 ^ 0x3, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, // connection ID } hdrLen := len(data) data = append(data, []byte{0xde, 0xad, 0xbe, 0xef}...) // packet number for i := hdrLen; i < len(data); i++ { data = data[:i] hdr, _, _, err := ParsePacket(data, 6) Expect(err).ToNot(HaveOccurred()) _, err = hdr.ParseExtended(bytes.NewReader(data), versionIETFFrames) Expect(err).To(Equal(io.EOF)) } }) }) It("tells its packet type for logging", func() { Expect((&Header{IsLongHeader: true, Type: protocol.PacketTypeHandshake}).PacketType()).To(Equal("Handshake")) Expect((&Header{}).PacketType()).To(Equal("1-RTT")) }) }) quic-go-0.25.0/internal/wire/interface.go000066400000000000000000000006601417145451600202250ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" ) // A Frame in QUIC type Frame interface { Write(b *bytes.Buffer, version protocol.VersionNumber) error Length(version protocol.VersionNumber) protocol.ByteCount } // A FrameParser parses QUIC frames, one by one. type FrameParser interface { ParseNext(*bytes.Reader, protocol.EncryptionLevel) (Frame, error) SetAckDelayExponent(uint8) } quic-go-0.25.0/internal/wire/log.go000066400000000000000000000061661417145451600170550ustar00rootroot00000000000000package wire import ( "fmt" "strings" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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, ConnectionID: %s, StatelessResetToken: %#x}", dir, f.SequenceNumber, f.ConnectionID, f.StatelessResetToken) case *NewTokenFrame: logger.Debugf("\t%s &wire.NewTokenFrame{Token: %#x}", dir, f.Token) default: logger.Debugf("\t%s %#v", dir, frame) } } quic-go-0.25.0/internal/wire/log_test.go000066400000000000000000000121651417145451600201100ustar00rootroot00000000000000package wire import ( "bytes" "log" "os" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "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, ConnectionID: protocol.ConnectionID{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, ConnectionID: deadbeef, StatelessResetToken: 0x0102030405060708090a0b0c0d0e0f10}")) }) 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")) }) }) quic-go-0.25.0/internal/wire/max_data_frame.go000066400000000000000000000017671417145451600212260ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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(r *bytes.Reader, _ protocol.VersionNumber) (*MaxDataFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } frame := &MaxDataFrame{} byteOffset, err := quicvarint.Read(r) if err != nil { return nil, err } frame.MaximumData = protocol.ByteCount(byteOffset) return frame, nil } // Write writes a MAX_STREAM_DATA frame func (f *MaxDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { b.WriteByte(0x10) quicvarint.Write(b, uint64(f.MaximumData)) return nil } // Length of a written frame func (f *MaxDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.MaximumData)) } quic-go-0.25.0/internal/wire/max_data_frame_test.go000066400000000000000000000031021417145451600222460ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("MAX_DATA frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { data := []byte{0x10} data = append(data, encodeVarInt(0xdecafbad123456)...) // byte offset b := bytes.NewReader(data) frame, err := parseMaxDataFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.MaximumData).To(Equal(protocol.ByteCount(0xdecafbad123456))) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x10} data = append(data, encodeVarInt(0xdecafbad1234567)...) // byte offset _, err := parseMaxDataFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseMaxDataFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("writing", func() { It("has proper min length", func() { f := &MaxDataFrame{ MaximumData: 0xdeadbeef, } Expect(f.Length(versionIETFFrames)).To(Equal(1 + quicvarint.Len(0xdeadbeef))) }) It("writes a MAX_DATA frame", func() { b := &bytes.Buffer{} f := &MaxDataFrame{ MaximumData: 0xdeadbeefcafe, } err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x10} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) Expect(b.Bytes()).To(Equal(expected)) }) }) }) quic-go-0.25.0/internal/wire/max_stream_data_frame.go000066400000000000000000000022271417145451600225710ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A MaxStreamDataFrame is a MAX_STREAM_DATA frame type MaxStreamDataFrame struct { StreamID protocol.StreamID MaximumStreamData protocol.ByteCount } func parseMaxStreamDataFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamDataFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } sid, err := quicvarint.Read(r) if err != nil { return nil, err } offset, err := quicvarint.Read(r) if err != nil { return nil, err } return &MaxStreamDataFrame{ StreamID: protocol.StreamID(sid), MaximumStreamData: protocol.ByteCount(offset), }, nil } func (f *MaxStreamDataFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { b.WriteByte(0x11) quicvarint.Write(b, uint64(f.StreamID)) quicvarint.Write(b, uint64(f.MaximumStreamData)) return nil } // Length of a written frame func (f *MaxStreamDataFrame) Length(version protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData)) } quic-go-0.25.0/internal/wire/max_stream_data_frame_test.go000066400000000000000000000037311417145451600236310ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("MAX_STREAM_DATA frame", func() { Context("parsing", func() { It("accepts sample frame", func() { data := []byte{0x11} data = append(data, encodeVarInt(0xdeadbeef)...) // Stream ID data = append(data, encodeVarInt(0x12345678)...) // Offset b := bytes.NewReader(data) frame, err := parseMaxStreamDataFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(frame.MaximumStreamData).To(Equal(protocol.ByteCount(0x12345678))) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x11} data = append(data, encodeVarInt(0xdeadbeef)...) // Stream ID data = append(data, encodeVarInt(0x12345678)...) // Offset _, err := parseMaxStreamDataFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseMaxStreamDataFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("writing", func() { It("has proper min length", func() { f := &MaxStreamDataFrame{ StreamID: 0x1337, MaximumStreamData: 0xdeadbeef, } Expect(f.Length(protocol.VersionWhatever)).To(Equal(1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData)))) }) It("writes a sample frame", func() { b := &bytes.Buffer{} f := &MaxStreamDataFrame{ StreamID: 0xdecafbad, MaximumStreamData: 0xdeadbeefcafe42, } expected := []byte{0x11} expected = append(expected, encodeVarInt(0xdecafbad)...) expected = append(expected, encodeVarInt(0xdeadbeefcafe42)...) err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal(expected)) }) }) }) quic-go-0.25.0/internal/wire/max_streams_frame.go000066400000000000000000000024131417145451600217600ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A MaxStreamsFrame is a MAX_STREAMS frame type MaxStreamsFrame struct { Type protocol.StreamType MaxStreamNum protocol.StreamNum } func parseMaxStreamsFrame(r *bytes.Reader, _ protocol.VersionNumber) (*MaxStreamsFrame, error) { typeByte, err := r.ReadByte() if err != nil { return nil, err } f := &MaxStreamsFrame{} switch typeByte { case 0x12: f.Type = protocol.StreamTypeBidi case 0x13: f.Type = protocol.StreamTypeUni } streamID, err := quicvarint.Read(r) if err != nil { return nil, err } f.MaxStreamNum = protocol.StreamNum(streamID) if f.MaxStreamNum > protocol.MaxStreamCount { return nil, fmt.Errorf("%d exceeds the maximum stream count", f.MaxStreamNum) } return f, nil } func (f *MaxStreamsFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { switch f.Type { case protocol.StreamTypeBidi: b.WriteByte(0x12) case protocol.StreamTypeUni: b.WriteByte(0x13) } quicvarint.Write(b, uint64(f.MaxStreamNum)) return nil } // Length of a written frame func (f *MaxStreamsFrame) Length(protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.MaxStreamNum)) } quic-go-0.25.0/internal/wire/max_streams_frame_test.go000066400000000000000000000066241417145451600230270ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("MAX_STREAMS frame", func() { Context("parsing", func() { It("accepts a frame for a bidirectional stream", func() { data := []byte{0x12} data = append(data, encodeVarInt(0xdecaf)...) b := bytes.NewReader(data) f, err := parseMaxStreamsFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeBidi)) Expect(f.MaxStreamNum).To(BeEquivalentTo(0xdecaf)) Expect(b.Len()).To(BeZero()) }) It("accepts a frame for a bidirectional stream", func() { data := []byte{0x13} data = append(data, encodeVarInt(0xdecaf)...) b := bytes.NewReader(data) f, err := parseMaxStreamsFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeUni)) Expect(f.MaxStreamNum).To(BeEquivalentTo(0xdecaf)) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x1d} data = append(data, encodeVarInt(0xdeadbeefcafe13)...) _, err := parseMaxStreamsFrame(bytes.NewReader(data), protocol.VersionWhatever) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseMaxStreamsFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever) Expect(err).To(HaveOccurred()) } }) 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 := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) frame, err := parseMaxStreamsFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever) 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 := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) _, err := parseMaxStreamsFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever) 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 := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) expected := []byte{0x12} expected = append(expected, encodeVarInt(0xdeadbeef)...) Expect(b.Bytes()).To(Equal(expected)) }) It("for a unidirectional stream", func() { f := &MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: 0xdecafbad, } b := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) expected := []byte{0x13} expected = append(expected, encodeVarInt(0xdecafbad)...) Expect(b.Bytes()).To(Equal(expected)) }) It("has the correct min length", func() { frame := MaxStreamsFrame{MaxStreamNum: 0x1337} Expect(frame.Length(protocol.VersionWhatever)).To(Equal(1 + quicvarint.Len(0x1337))) }) }) }) quic-go-0.25.0/internal/wire/new_connection_id_frame.go000066400000000000000000000041171417145451600231240ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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(r *bytes.Reader, _ protocol.VersionNumber) (*NewConnectionIDFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } seq, err := quicvarint.Read(r) if err != nil { return nil, err } ret, err := quicvarint.Read(r) if err != nil { return nil, err } if ret > seq { //nolint:stylecheck return nil, fmt.Errorf("Retire Prior To value (%d) larger than Sequence Number (%d)", ret, seq) } connIDLen, err := r.ReadByte() if err != nil { return nil, err } if connIDLen > protocol.MaxConnIDLen { return nil, fmt.Errorf("invalid connection ID length: %d", connIDLen) } connID, err := protocol.ReadConnectionID(r, int(connIDLen)) if err != nil { return nil, err } frame := &NewConnectionIDFrame{ SequenceNumber: seq, RetirePriorTo: ret, ConnectionID: connID, } if _, err := io.ReadFull(r, frame.StatelessResetToken[:]); err != nil { if err == io.ErrUnexpectedEOF { return nil, io.EOF } return nil, err } return frame, nil } func (f *NewConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x18) quicvarint.Write(b, f.SequenceNumber) quicvarint.Write(b, f.RetirePriorTo) connIDLen := f.ConnectionID.Len() if connIDLen > protocol.MaxConnIDLen { return fmt.Errorf("invalid connection ID length: %d", connIDLen) } b.WriteByte(uint8(connIDLen)) b.Write(f.ConnectionID.Bytes()) b.Write(f.StatelessResetToken[:]) return nil } // Length of a written frame func (f *NewConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(f.SequenceNumber) + quicvarint.Len(f.RetirePriorTo) + 1 /* connection ID length */ + protocol.ByteCount(f.ConnectionID.Len()) + 16 } quic-go-0.25.0/internal/wire/new_connection_id_frame_test.go000066400000000000000000000111621417145451600241610ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("NEW_CONNECTION_ID frame", func() { Context("when parsing", func() { It("accepts a sample frame", func() { data := []byte{0x18} data = append(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 b := bytes.NewReader(data) frame, err := parseNewConnectionIDFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.SequenceNumber).To(Equal(uint64(0xdeadbeef))) Expect(frame.RetirePriorTo).To(Equal(uint64(0xcafe))) Expect(frame.ConnectionID).To(Equal(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10})) Expect(string(frame.StatelessResetToken[:])).To(Equal("deadbeefdecafbad")) }) It("errors when the Retire Prior To value is larger than the Sequence Number", func() { data := []byte{0x18} data = append(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 b := bytes.NewReader(data) _, err := parseNewConnectionIDFrame(b, versionIETFFrames) Expect(err).To(MatchError("Retire Prior To value (1001) larger than Sequence Number (1000)")) }) It("errors when the connection ID has an invalid length", func() { data := []byte{0x18} data = append(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 b := bytes.NewReader(data) _, err := parseNewConnectionIDFrame(b, versionIETFFrames) Expect(err).To(MatchError("invalid connection ID length: 21")) }) It("errors on EOFs", func() { data := []byte{0x18} data = append(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 _, err := parseNewConnectionIDFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseNewConnectionIDFrame(bytes.NewReader(data[0:i]), versionIETFFrames) 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.ConnectionID{1, 2, 3, 4, 5, 6}, StatelessResetToken: token, } b := &bytes.Buffer{} Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) expected := []byte{0x18} 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.Bytes()).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.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, StatelessResetToken: token, } b := &bytes.Buffer{} Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) Expect(frame.Length(versionIETFFrames)).To(BeEquivalentTo(b.Len())) }) }) }) quic-go-0.25.0/internal/wire/new_token_frame.go000066400000000000000000000021421417145451600214250ustar00rootroot00000000000000package wire import ( "bytes" "errors" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A NewTokenFrame is a NEW_TOKEN frame type NewTokenFrame struct { Token []byte } func parseNewTokenFrame(r *bytes.Reader, _ protocol.VersionNumber) (*NewTokenFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } tokenLen, err := quicvarint.Read(r) if err != nil { return nil, err } if uint64(r.Len()) < tokenLen { return nil, io.EOF } if tokenLen == 0 { return nil, errors.New("token must not be empty") } token := make([]byte, int(tokenLen)) if _, err := io.ReadFull(r, token); err != nil { return nil, err } return &NewTokenFrame{Token: token}, nil } func (f *NewTokenFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x7) quicvarint.Write(b, uint64(len(f.Token))) b.Write(f.Token) return nil } // Length of a written frame func (f *NewTokenFrame) Length(protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(len(f.Token))) + protocol.ByteCount(len(f.Token)) } quic-go-0.25.0/internal/wire/new_token_frame_test.go000066400000000000000000000044711417145451600224730ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "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 := []byte{0x7} data = append(data, encodeVarInt(uint64(len(token)))...) data = append(data, token...) b := bytes.NewReader(data) f, err := parseNewTokenFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(string(f.Token)).To(Equal(token)) Expect(b.Len()).To(BeZero()) }) It("rejects empty tokens", func() { data := []byte{0x7} data = append(data, encodeVarInt(uint64(0))...) b := bytes.NewReader(data) _, err := parseNewTokenFrame(b, protocol.VersionWhatever) Expect(err).To(MatchError("token must not be empty")) }) It("errors on EOFs", func() { token := "Lorem ipsum dolor sit amet, consectetur adipiscing elit" data := []byte{0x7} data = append(data, encodeVarInt(uint64(len(token)))...) data = append(data, token...) _, err := parseNewTokenFrame(bytes.NewReader(data), protocol.VersionWhatever) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseNewTokenFrame(bytes.NewReader(data[0:i]), protocol.VersionWhatever) Expect(err).To(HaveOccurred()) } }) }) 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 := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) expected := []byte{0x7} expected = append(expected, encodeVarInt(uint64(len(token)))...) expected = append(expected, token...) Expect(b.Bytes()).To(Equal(expected)) }) It("has the correct min length", func() { frame := &NewTokenFrame{Token: []byte("foobar")} Expect(frame.Length(protocol.VersionWhatever)).To(Equal(1 + quicvarint.Len(6) + 6)) }) }) }) quic-go-0.25.0/internal/wire/path_challenge_frame.go000066400000000000000000000014671417145451600224030ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" ) // A PathChallengeFrame is a PATH_CHALLENGE frame type PathChallengeFrame struct { Data [8]byte } func parsePathChallengeFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PathChallengeFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } frame := &PathChallengeFrame{} if _, err := io.ReadFull(r, frame.Data[:]); err != nil { if err == io.ErrUnexpectedEOF { return nil, io.EOF } return nil, err } return frame, nil } func (f *PathChallengeFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x1a) b.Write(f.Data[:]) return nil } // Length of a written frame func (f *PathChallengeFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { return 1 + 8 } quic-go-0.25.0/internal/wire/path_challenge_frame_test.go000066400000000000000000000026521417145451600234370ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("PATH_CHALLENGE frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { b := bytes.NewReader([]byte{0x1a, 1, 2, 3, 4, 5, 6, 7, 8}) f, err := parsePathChallengeFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeZero()) Expect(f.Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) }) It("errors on EOFs", func() { data := []byte{0x1a, 1, 2, 3, 4, 5, 6, 7, 8} _, err := parsePathChallengeFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parsePathChallengeFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { b := &bytes.Buffer{} frame := PathChallengeFrame{Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}} err := frame.Write(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal([]byte{0x1a, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37})) }) It("has the correct min length", func() { frame := PathChallengeFrame{} Expect(frame.Length(protocol.VersionWhatever)).To(Equal(protocol.ByteCount(9))) }) }) }) quic-go-0.25.0/internal/wire/path_response_frame.go000066400000000000000000000014571417145451600223160ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" ) // A PathResponseFrame is a PATH_RESPONSE frame type PathResponseFrame struct { Data [8]byte } func parsePathResponseFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PathResponseFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } frame := &PathResponseFrame{} if _, err := io.ReadFull(r, frame.Data[:]); err != nil { if err == io.ErrUnexpectedEOF { return nil, io.EOF } return nil, err } return frame, nil } func (f *PathResponseFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x1b) b.Write(f.Data[:]) return nil } // Length of a written frame func (f *PathResponseFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { return 1 + 8 } quic-go-0.25.0/internal/wire/path_response_frame_test.go000066400000000000000000000026431417145451600233530ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("PATH_RESPONSE frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { b := bytes.NewReader([]byte{0x1b, 1, 2, 3, 4, 5, 6, 7, 8}) f, err := parsePathResponseFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeZero()) Expect(f.Data).To(Equal([8]byte{1, 2, 3, 4, 5, 6, 7, 8})) }) It("errors on EOFs", func() { data := []byte{0x1b, 1, 2, 3, 4, 5, 6, 7, 8} _, err := parsePathResponseFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parsePathResponseFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { b := &bytes.Buffer{} frame := PathResponseFrame{Data: [8]byte{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37}} err := frame.Write(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(b.Bytes()).To(Equal([]byte{0x1b, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37})) }) It("has the correct min length", func() { frame := PathResponseFrame{} Expect(frame.Length(protocol.VersionWhatever)).To(Equal(protocol.ByteCount(9))) }) }) }) quic-go-0.25.0/internal/wire/ping_frame.go000066400000000000000000000010601417145451600203670ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" ) // A PingFrame is a PING frame type PingFrame struct{} func parsePingFrame(r *bytes.Reader, _ protocol.VersionNumber) (*PingFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } return &PingFrame{}, nil } func (f *PingFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { b.WriteByte(0x1) return nil } // Length of a written frame func (f *PingFrame) Length(version protocol.VersionNumber) protocol.ByteCount { return 1 } quic-go-0.25.0/internal/wire/ping_frame_test.go000066400000000000000000000016461417145451600214400ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("PingFrame", func() { Context("when parsing", func() { It("accepts sample frame", func() { b := bytes.NewReader([]byte{0x1}) _, err := parsePingFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { _, err := parsePingFrame(bytes.NewReader(nil), protocol.VersionWhatever) Expect(err).To(HaveOccurred()) }) }) Context("when writing", func() { It("writes a sample frame", func() { b := &bytes.Buffer{} frame := PingFrame{} frame.Write(b, protocol.VersionWhatever) Expect(b.Bytes()).To(Equal([]byte{0x1})) }) It("has the correct min length", func() { frame := PingFrame{} Expect(frame.Length(0)).To(Equal(protocol.ByteCount(1))) }) }) }) quic-go-0.25.0/internal/wire/pool.go000066400000000000000000000010741417145451600172360ustar00rootroot00000000000000package wire import ( "sync" "github.com/lucas-clemente/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) } quic-go-0.25.0/internal/wire/pool_test.go000066400000000000000000000010311417145451600202660ustar00rootroot00000000000000package wire import ( . "github.com/onsi/ginkgo" . "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) }) }) quic-go-0.25.0/internal/wire/reset_stream_frame.go000066400000000000000000000027621417145451600221410ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/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(r *bytes.Reader, _ protocol.VersionNumber) (*ResetStreamFrame, error) { if _, err := r.ReadByte(); err != nil { // read the TypeByte return nil, err } var streamID protocol.StreamID var byteOffset protocol.ByteCount sid, err := quicvarint.Read(r) if err != nil { return nil, err } streamID = protocol.StreamID(sid) errorCode, err := quicvarint.Read(r) if err != nil { return nil, err } bo, err := quicvarint.Read(r) if err != nil { return nil, err } byteOffset = protocol.ByteCount(bo) return &ResetStreamFrame{ StreamID: streamID, ErrorCode: qerr.StreamErrorCode(errorCode), FinalSize: byteOffset, }, nil } func (f *ResetStreamFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x4) quicvarint.Write(b, uint64(f.StreamID)) quicvarint.Write(b, uint64(f.ErrorCode)) quicvarint.Write(b, uint64(f.FinalSize)) return nil } // Length of a written frame func (f *ResetStreamFrame) Length(version protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode)) + quicvarint.Len(uint64(f.FinalSize)) } quic-go-0.25.0/internal/wire/reset_stream_frame_test.go000066400000000000000000000044011417145451600231700ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("RESET_STREAM frame", func() { Context("when parsing", func() { It("accepts sample frame", func() { data := []byte{0x4} data = append(data, encodeVarInt(0xdeadbeef)...) // stream ID data = append(data, encodeVarInt(0x1337)...) // error code data = append(data, encodeVarInt(0x987654321)...) // byte offset b := bytes.NewReader(data) frame, err := parseResetStreamFrame(b, versionIETFFrames) 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))) }) It("errors on EOFs", func() { data := []byte{0x4} data = append(data, encodeVarInt(0xdeadbeef)...) // stream ID data = append(data, encodeVarInt(0x1337)...) // error code data = append(data, encodeVarInt(0x987654321)...) // byte offset _, err := parseResetStreamFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseResetStreamFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := ResetStreamFrame{ StreamID: 0x1337, FinalSize: 0x11223344decafbad, ErrorCode: 0xcafe, } b := &bytes.Buffer{} err := frame.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x4} expected = append(expected, encodeVarInt(0x1337)...) expected = append(expected, encodeVarInt(0xcafe)...) expected = append(expected, encodeVarInt(0x11223344decafbad)...) Expect(b.Bytes()).To(Equal(expected)) }) It("has the correct min length", func() { rst := ResetStreamFrame{ StreamID: 0x1337, FinalSize: 0x1234567, ErrorCode: 0xde, } expectedLen := 1 + quicvarint.Len(0x1337) + quicvarint.Len(0x1234567) + 2 Expect(rst.Length(versionIETFFrames)).To(Equal(expectedLen)) }) }) }) quic-go-0.25.0/internal/wire/retire_connection_id_frame.go000066400000000000000000000016021417145451600236210ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A RetireConnectionIDFrame is a RETIRE_CONNECTION_ID frame type RetireConnectionIDFrame struct { SequenceNumber uint64 } func parseRetireConnectionIDFrame(r *bytes.Reader, _ protocol.VersionNumber) (*RetireConnectionIDFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } seq, err := quicvarint.Read(r) if err != nil { return nil, err } return &RetireConnectionIDFrame{SequenceNumber: seq}, nil } func (f *RetireConnectionIDFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x19) quicvarint.Write(b, f.SequenceNumber) return nil } // Length of a written frame func (f *RetireConnectionIDFrame) Length(protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(f.SequenceNumber) } quic-go-0.25.0/internal/wire/retire_connection_id_frame_test.go000066400000000000000000000030271417145451600246630ustar00rootroot00000000000000package wire import ( "bytes" "io" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("NEW_CONNECTION_ID frame", func() { Context("when parsing", func() { It("accepts a sample frame", func() { data := []byte{0x19} data = append(data, encodeVarInt(0xdeadbeef)...) // sequence number b := bytes.NewReader(data) frame, err := parseRetireConnectionIDFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.SequenceNumber).To(Equal(uint64(0xdeadbeef))) }) It("errors on EOFs", func() { data := []byte{0x18} data = append(data, encodeVarInt(0xdeadbeef)...) // sequence number _, err := parseRetireConnectionIDFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseRetireConnectionIDFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(MatchError(io.EOF)) } }) }) Context("when writing", func() { It("writes a sample frame", func() { frame := &RetireConnectionIDFrame{SequenceNumber: 0x1337} b := &bytes.Buffer{} Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) expected := []byte{0x19} expected = append(expected, encodeVarInt(0x1337)...) Expect(b.Bytes()).To(Equal(expected)) }) It("has the correct length", func() { frame := &RetireConnectionIDFrame{SequenceNumber: 0xdecafbad} b := &bytes.Buffer{} Expect(frame.Write(b, versionIETFFrames)).To(Succeed()) Expect(frame.Length(versionIETFFrames)).To(BeEquivalentTo(b.Len())) }) }) }) quic-go-0.25.0/internal/wire/stop_sending_frame.go000066400000000000000000000023051417145451600221310ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/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(r *bytes.Reader, _ protocol.VersionNumber) (*StopSendingFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } streamID, err := quicvarint.Read(r) if err != nil { return nil, err } errorCode, err := quicvarint.Read(r) if err != nil { return nil, err } return &StopSendingFrame{ StreamID: protocol.StreamID(streamID), ErrorCode: qerr.StreamErrorCode(errorCode), }, nil } // Length of a written frame func (f *StopSendingFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.ErrorCode)) } func (f *StopSendingFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { b.WriteByte(0x5) quicvarint.Write(b, uint64(f.StreamID)) quicvarint.Write(b, uint64(f.ErrorCode)) return nil } quic-go-0.25.0/internal/wire/stop_sending_frame_test.go000066400000000000000000000036701417145451600231760ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("STOP_SENDING frame", func() { Context("when parsing", func() { It("parses a sample frame", func() { data := []byte{0x5} data = append(data, encodeVarInt(0xdecafbad)...) // stream ID data = append(data, encodeVarInt(0x1337)...) // error code b := bytes.NewReader(data) frame, err := parseStopSendingFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdecafbad))) Expect(frame.ErrorCode).To(Equal(qerr.StreamErrorCode(0x1337))) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x5} data = append(data, encodeVarInt(0xdecafbad)...) // stream ID data = append(data, encodeVarInt(0x123456)...) // error code _, err := parseStopSendingFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseStopSendingFrame(bytes.NewReader(data[:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("when writing", func() { It("writes", func() { frame := &StopSendingFrame{ StreamID: 0xdeadbeefcafe, ErrorCode: 0xdecafbad, } buf := &bytes.Buffer{} Expect(frame.Write(buf, versionIETFFrames)).To(Succeed()) expected := []byte{0x5} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) expected = append(expected, encodeVarInt(0xdecafbad)...) Expect(buf.Bytes()).To(Equal(expected)) }) It("has the correct min length", func() { frame := &StopSendingFrame{ StreamID: 0xdeadbeef, ErrorCode: 0x1234567, } Expect(frame.Length(versionIETFFrames)).To(Equal(1 + quicvarint.Len(0xdeadbeef) + quicvarint.Len(0x1234567))) }) }) }) quic-go-0.25.0/internal/wire/stream_data_blocked_frame.go000066400000000000000000000022671417145451600234130ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A StreamDataBlockedFrame is a STREAM_DATA_BLOCKED frame type StreamDataBlockedFrame struct { StreamID protocol.StreamID MaximumStreamData protocol.ByteCount } func parseStreamDataBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamDataBlockedFrame, error) { if _, err := r.ReadByte(); err != nil { return nil, err } sid, err := quicvarint.Read(r) if err != nil { return nil, err } offset, err := quicvarint.Read(r) if err != nil { return nil, err } return &StreamDataBlockedFrame{ StreamID: protocol.StreamID(sid), MaximumStreamData: protocol.ByteCount(offset), }, nil } func (f *StreamDataBlockedFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { b.WriteByte(0x15) quicvarint.Write(b, uint64(f.StreamID)) quicvarint.Write(b, uint64(f.MaximumStreamData)) return nil } // Length of a written frame func (f *StreamDataBlockedFrame) Length(version protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.StreamID)) + quicvarint.Len(uint64(f.MaximumStreamData)) } quic-go-0.25.0/internal/wire/stream_data_blocked_frame_test.go000066400000000000000000000036561417145451600244550ustar00rootroot00000000000000package wire import ( "bytes" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("STREAM_DATA_BLOCKED frame", func() { Context("parsing", func() { It("accepts sample frame", func() { data := []byte{0x15} data = append(data, encodeVarInt(0xdeadbeef)...) // stream ID data = append(data, encodeVarInt(0xdecafbad)...) // offset b := bytes.NewReader(data) frame, err := parseStreamDataBlockedFrame(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(frame.StreamID).To(Equal(protocol.StreamID(0xdeadbeef))) Expect(frame.MaximumStreamData).To(Equal(protocol.ByteCount(0xdecafbad))) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x15} data = append(data, encodeVarInt(0xdeadbeef)...) data = append(data, encodeVarInt(0xc0010ff)...) _, err := parseStreamDataBlockedFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseStreamDataBlockedFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("writing", func() { It("has proper min length", func() { f := &StreamDataBlockedFrame{ StreamID: 0x1337, MaximumStreamData: 0xdeadbeef, } Expect(f.Length(0)).To(Equal(1 + quicvarint.Len(0x1337) + quicvarint.Len(0xdeadbeef))) }) It("writes a sample frame", func() { b := &bytes.Buffer{} f := &StreamDataBlockedFrame{ StreamID: 0xdecafbad, MaximumStreamData: 0x1337, } err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x15} expected = append(expected, encodeVarInt(uint64(f.StreamID))...) expected = append(expected, encodeVarInt(uint64(f.MaximumStreamData))...) Expect(b.Bytes()).To(Equal(expected)) }) }) }) quic-go-0.25.0/internal/wire/stream_frame.go000066400000000000000000000112401417145451600207260ustar00rootroot00000000000000package wire import ( "bytes" "errors" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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(r *bytes.Reader, _ protocol.VersionNumber) (*StreamFrame, error) { typeByte, err := r.ReadByte() if err != nil { return nil, err } hasOffset := typeByte&0x4 > 0 fin := typeByte&0x1 > 0 hasDataLen := typeByte&0x2 > 0 streamID, err := quicvarint.Read(r) if err != nil { return nil, err } var offset uint64 if hasOffset { offset, err = quicvarint.Read(r) if err != nil { return nil, err } } var dataLen uint64 if hasDataLen { var err error dataLen, err = quicvarint.Read(r) if err != nil { return nil, err } } else { // The rest of the packet is data dataLen = uint64(r.Len()) } 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, 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 { if _, err := io.ReadFull(r, frame.Data); err != nil { return nil, err } } if frame.Offset+frame.DataLen() > protocol.MaxByteCount { return nil, errors.New("stream data overflows maximum offset") } return frame, nil } // Write writes a STREAM frame func (f *StreamFrame) Write(b *bytes.Buffer, version protocol.VersionNumber) error { if len(f.Data) == 0 && !f.Fin { return errors.New("StreamFrame: attempting to write empty frame without FIN") } typeByte := byte(0x8) if f.Fin { typeByte ^= 0x1 } hasOffset := f.Offset != 0 if f.DataLenPresent { typeByte ^= 0x2 } if hasOffset { typeByte ^= 0x4 } b.WriteByte(typeByte) quicvarint.Write(b, uint64(f.StreamID)) if hasOffset { quicvarint.Write(b, uint64(f.Offset)) } if f.DataLenPresent { quicvarint.Write(b, uint64(f.DataLen())) } b.Write(f.Data) return nil } // Length returns the total length of the STREAM frame func (f *StreamFrame) Length(version protocol.VersionNumber) 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 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, version protocol.VersionNumber) protocol.ByteCount { headerLen := 1 + quicvarint.Len(uint64(f.StreamID)) if f.Offset != 0 { headerLen += quicvarint.Len(uint64(f.Offset)) } 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 } // 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.VersionNumber) (*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) } quic-go-0.25.0/internal/wire/stream_frame_test.go000066400000000000000000000363471417145451600220040ustar00rootroot00000000000000package wire import ( "bytes" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("STREAM frame", func() { Context("when parsing", func() { It("parses a frame with OFF bit", func() { data := []byte{0x8 ^ 0x4} data = append(data, encodeVarInt(0x12345)...) // stream ID data = append(data, encodeVarInt(0xdecafbad)...) // offset data = append(data, []byte("foobar")...) r := bytes.NewReader(data) frame, err := parseStreamFrame(r, versionIETFFrames) 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(r.Len()).To(BeZero()) }) It("respects the LEN when parsing the frame", func() { data := []byte{0x8 ^ 0x2} data = append(data, encodeVarInt(0x12345)...) // stream ID data = append(data, encodeVarInt(4)...) // data length data = append(data, []byte("foobar")...) r := bytes.NewReader(data) frame, err := parseStreamFrame(r, versionIETFFrames) 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(r.Len()).To(Equal(2)) }) It("parses a frame with FIN bit", func() { data := []byte{0x8 ^ 0x1} data = append(data, encodeVarInt(9)...) // stream ID data = append(data, []byte("foobar")...) r := bytes.NewReader(data) frame, err := parseStreamFrame(r, versionIETFFrames) 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(r.Len()).To(BeZero()) }) It("allows empty frames", func() { data := []byte{0x8 ^ 0x4} data = append(data, encodeVarInt(0x1337)...) // stream ID data = append(data, encodeVarInt(0x12345)...) // offset r := bytes.NewReader(data) f, err := parseStreamFrame(r, versionIETFFrames) 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()) }) It("rejects frames that overflow the maximum offset", func() { data := []byte{0x8 ^ 0x4} data = append(data, encodeVarInt(0x12345)...) // stream ID data = append(data, encodeVarInt(uint64(protocol.MaxByteCount-5))...) // offset data = append(data, []byte("foobar")...) r := bytes.NewReader(data) _, err := parseStreamFrame(r, versionIETFFrames) Expect(err).To(MatchError("stream data overflows maximum offset")) }) It("rejects frames that claim to be longer than the packet size", func() { data := []byte{0x8 ^ 0x2} data = append(data, encodeVarInt(0x12345)...) // stream ID data = append(data, encodeVarInt(uint64(protocol.MaxPacketBufferSize)+1)...) // data length data = append(data, make([]byte, protocol.MaxPacketBufferSize+1)...) r := bytes.NewReader(data) _, err := parseStreamFrame(r, versionIETFFrames) Expect(err).To(Equal(io.EOF)) }) It("errors on EOFs", func() { data := []byte{0x8 ^ 0x4 ^ 0x2} data = append(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(bytes.NewReader(data), versionIETFFrames) Expect(err).NotTo(HaveOccurred()) for i := range data { _, err := parseStreamFrame(bytes.NewReader(data[0:i]), versionIETFFrames) Expect(err).To(HaveOccurred()) } }) }) Context("using the buffer", func() { It("uses the buffer for long STREAM frames", func() { data := []byte{0x8} data = append(data, encodeVarInt(0x12345)...) // stream ID data = append(data, bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize)...) r := bytes.NewReader(data) frame, err := parseStreamFrame(r, versionIETFFrames) 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(r.Len()).To(BeZero()) Expect(frame.PutBack).ToNot(Panic()) }) It("doesn't use the buffer for short STREAM frames", func() { data := []byte{0x8} data = append(data, encodeVarInt(0x12345)...) // stream ID data = append(data, bytes.Repeat([]byte{'f'}, protocol.MinStreamFrameBufferSize-1)...) r := bytes.NewReader(data) frame, err := parseStreamFrame(r, versionIETFFrames) 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(r.Len()).To(BeZero()) Expect(frame.PutBack).ToNot(Panic()) }) }) Context("when writing", func() { It("writes a frame without offset", func() { f := &StreamFrame{ StreamID: 0x1337, Data: []byte("foobar"), } b := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, []byte("foobar")...) Expect(b.Bytes()).To(Equal(expected)) }) It("writes a frame with offset", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x123456, Data: []byte("foobar"), } b := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) 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.Bytes()).To(Equal(expected)) }) It("writes a frame with FIN bit", func() { f := &StreamFrame{ StreamID: 0x1337, Offset: 0x123456, Fin: true, } b := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) expected := []byte{0x8 ^ 0x4 ^ 0x1} expected = append(expected, encodeVarInt(0x1337)...) // stream ID expected = append(expected, encodeVarInt(0x123456)...) // offset Expect(b.Bytes()).To(Equal(expected)) }) It("writes a frame with data length", func() { f := &StreamFrame{ StreamID: 0x1337, Data: []byte("foobar"), DataLenPresent: true, } b := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) 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.Bytes()).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 := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) 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.Bytes()).To(Equal(expected)) }) It("refuses to write an empty frame without FIN", func() { f := &StreamFrame{ StreamID: 0x42, Offset: 0x1337, } b := &bytes.Buffer{} err := f.Write(b, versionIETFFrames) 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(versionIETFFrames)).To(Equal(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(versionIETFFrames)).To(Equal(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(versionIETFFrames)).To(Equal(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, } b := &bytes.Buffer{} for i := 1; i < 3000; i++ { b.Reset() f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), versionIETFFrames) 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} err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).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, } b := &bytes.Buffer{} var frameOneByteTooSmallCounter int for i := 1; i < 3000; i++ { b.Reset() f.Data = nil maxDataLen := f.MaxDataLen(protocol.ByteCount(i), versionIETFFrames) 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} err := f.Write(b, versionIETFFrames) Expect(err).ToNot(HaveOccurred()) Expect(b.Len()).To(BeNumerically(">", i)) continue } f.Data = data[:int(maxDataLen)] err := f.Write(b, versionIETFFrames) 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 b.Len() == i-1 { frameOneByteTooSmallCounter++ continue } Expect(b.Len()).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(versionIETFFrames), versionIETFFrames) Expect(needsSplit).To(BeFalse()) Expect(frame).To(BeNil()) Expect(f.DataLen()).To(BeEquivalentTo(100)) frame, needsSplit = f.MaybeSplitOffFrame(f.Length(versionIETFFrames)-1, versionIETFFrames) 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, versionIETFFrames) 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(versionIETFFrames)-3, versionIETFFrames) 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, versionIETFFrames) 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(versionIETFFrames) for i := protocol.ByteCount(0); i < minFrameSize; i++ { f, needsSplit := f.MaybeSplitOffFrame(i, versionIETFFrames) 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, versionIETFFrames) Expect(needsSplit).To(BeTrue()) Expect(f.Length(versionIETFFrames)).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(versionIETFFrames) for i := protocol.ByteCount(0); i < minFrameSize; i++ { f, needsSplit := f.MaybeSplitOffFrame(i, versionIETFFrames) 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, versionIETFFrames) 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(versionIETFFrames) == i-1 { frameOneByteTooSmallCounter++ continue } Expect(newFrame.Length(versionIETFFrames)).To(Equal(i)) } Expect(frameOneByteTooSmallCounter).To(Equal(1)) }) }) }) quic-go-0.25.0/internal/wire/streams_blocked_frame.go000066400000000000000000000024541417145451600226030ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" ) // A StreamsBlockedFrame is a STREAMS_BLOCKED frame type StreamsBlockedFrame struct { Type protocol.StreamType StreamLimit protocol.StreamNum } func parseStreamsBlockedFrame(r *bytes.Reader, _ protocol.VersionNumber) (*StreamsBlockedFrame, error) { typeByte, err := r.ReadByte() if err != nil { return nil, err } f := &StreamsBlockedFrame{} switch typeByte { case 0x16: f.Type = protocol.StreamTypeBidi case 0x17: f.Type = protocol.StreamTypeUni } streamLimit, err := quicvarint.Read(r) if err != nil { return nil, err } f.StreamLimit = protocol.StreamNum(streamLimit) if f.StreamLimit > protocol.MaxStreamCount { return nil, fmt.Errorf("%d exceeds the maximum stream count", f.StreamLimit) } return f, nil } func (f *StreamsBlockedFrame) Write(b *bytes.Buffer, _ protocol.VersionNumber) error { switch f.Type { case protocol.StreamTypeBidi: b.WriteByte(0x16) case protocol.StreamTypeUni: b.WriteByte(0x17) } quicvarint.Write(b, uint64(f.StreamLimit)) return nil } // Length of a written frame func (f *StreamsBlockedFrame) Length(_ protocol.VersionNumber) protocol.ByteCount { return 1 + quicvarint.Len(uint64(f.StreamLimit)) } quic-go-0.25.0/internal/wire/streams_blocked_frame_test.go000066400000000000000000000067641417145451600236520ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("STREAMS_BLOCKED frame", func() { Context("parsing", func() { It("accepts a frame for bidirectional streams", func() { expected := []byte{0x16} expected = append(expected, encodeVarInt(0x1337)...) b := bytes.NewReader(expected) f, err := parseStreamsBlockedFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeBidi)) Expect(f.StreamLimit).To(BeEquivalentTo(0x1337)) Expect(b.Len()).To(BeZero()) }) It("accepts a frame for unidirectional streams", func() { expected := []byte{0x17} expected = append(expected, encodeVarInt(0x7331)...) b := bytes.NewReader(expected) f, err := parseStreamsBlockedFrame(b, protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(f.Type).To(Equal(protocol.StreamTypeUni)) Expect(f.StreamLimit).To(BeEquivalentTo(0x7331)) Expect(b.Len()).To(BeZero()) }) It("errors on EOFs", func() { data := []byte{0x16} data = append(data, encodeVarInt(0x12345678)...) _, err := parseStreamsBlockedFrame(bytes.NewReader(data), versionIETFFrames) Expect(err).ToNot(HaveOccurred()) for i := range data { _, err := parseStreamsBlockedFrame(bytes.NewReader(data[:i]), versionIETFFrames) 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 := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) frame, err := parseStreamsBlockedFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(Equal(f)) }) It("errors when receiving a too large stream count", func() { f := &StreamsBlockedFrame{ Type: streamType, StreamLimit: protocol.MaxStreamCount + 1, } b := &bytes.Buffer{} Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) _, err := parseStreamsBlockedFrame(bytes.NewReader(b.Bytes()), protocol.VersionWhatever) 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() { b := &bytes.Buffer{} f := StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: 0xdeadbeefcafe, } Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) expected := []byte{0x16} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) Expect(b.Bytes()).To(Equal(expected)) }) It("writes a frame for unidirectional streams", func() { b := &bytes.Buffer{} f := StreamsBlockedFrame{ Type: protocol.StreamTypeUni, StreamLimit: 0xdeadbeefcafe, } Expect(f.Write(b, protocol.VersionWhatever)).To(Succeed()) expected := []byte{0x17} expected = append(expected, encodeVarInt(0xdeadbeefcafe)...) Expect(b.Bytes()).To(Equal(expected)) }) It("has the correct min length", func() { frame := StreamsBlockedFrame{StreamLimit: 0x123456} Expect(frame.Length(0)).To(Equal(protocol.ByteCount(1) + quicvarint.Len(0x123456))) }) }) }) quic-go-0.25.0/internal/wire/transport_parameter_test.go000066400000000000000000000651071417145451600234270ustar00rootroot00000000000000package wire import ( "bytes" "fmt" "math" "math/rand" "net" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Transport Parameters", func() { getRandomValueUpTo := func(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)) } getRandomValue := func() uint64 { return getRandomValueUpTo(math.MaxInt64) } BeforeEach(func() { rand.Seed(GinkgoRandomSeed()) }) addInitialSourceConnectionID := func(b *bytes.Buffer) { quicvarint.Write(b, uint64(initialSourceConnectionIDParameterID)) quicvarint.Write(b, 6) b.Write([]byte("foobar")) } It("has a string representation", func() { p := &TransportParameters{ InitialMaxStreamDataBidiLocal: 1234, InitialMaxStreamDataBidiRemote: 2345, InitialMaxStreamDataUni: 3456, InitialMaxData: 4567, MaxBidiStreamNum: 1337, MaxUniStreamNum: 7331, MaxIdleTimeout: 42 * time.Second, OriginalDestinationConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, InitialSourceConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, RetrySourceConnectionID: &protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}, 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.ConnectionID{0xde, 0xad, 0xbe, 0xef}, InitialSourceConnectionID: protocol.ConnectionID{}, 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[:]) 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.ConnectionID{0xde, 0xad, 0xbe, 0xef}, InitialSourceConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, RetrySourceConnectionID: &protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}, AckDelayExponent: 13, MaxAckDelay: 42 * time.Millisecond, ActiveConnectionIDLimit: getRandomValue(), 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.ConnectionID{0xde, 0xad, 0xbe, 0xef})) Expect(p.InitialSourceConnectionID).To(Equal(protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad})) Expect(p.RetrySourceConnectionID).To(Equal(&protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde})) Expect(p.AckDelayExponent).To(Equal(uint8(13))) Expect(p.MaxAckDelay).To(Equal(42 * time.Millisecond)) Expect(p.ActiveConnectionIDLimit).To(Equal(params.ActiveConnectionIDLimit)) Expect(p.MaxDatagramFrameSize).To(Equal(params.MaxDatagramFrameSize)) }) It("doesn't marshal a retry_source_connection_id, if no Retry was performed", func() { data := (&TransportParameters{ StatelessResetToken: &protocol.StatelessResetToken{}, }).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() { data := (&TransportParameters{ RetrySourceConnectionID: &protocol.ConnectionID{}, StatelessResetToken: &protocol.StatelessResetToken{}, }).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 := &bytes.Buffer{} quicvarint.Write(b, uint64(statelessResetTokenParameterID)) quicvarint.Write(b, 15) b.Write(make([]byte, 15)) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "wrong length for stateless_reset_token: 15 (expected 16)", })) }) It("errors when the max_packet_size is too small", func() { b := &bytes.Buffer{} quicvarint.Write(b, uint64(maxUDPPayloadSizeParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(1199))) quicvarint.Write(b, 1199) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), protocol.PerspectiveServer)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for max_packet_size: 1199 (minimum 1200)", })) }) It("errors when disable_active_migration has content", func() { b := &bytes.Buffer{} quicvarint.Write(b, uint64(disableActiveMigrationParameterID)) quicvarint.Write(b, 6) b.Write([]byte("foobar")) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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 := &bytes.Buffer{} quicvarint.Write(b, uint64(statelessResetTokenParameterID)) quicvarint.Write(b, 16) b.Write(make([]byte, 16)) addInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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 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, when no value was sent", func() { data := (&TransportParameters{ AckDelayExponent: protocol.DefaultAckDelayExponent, StatelessResetToken: &protocol.StatelessResetToken{}, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.AckDelayExponent).To(BeEquivalentTo(protocol.DefaultAckDelayExponent)) }) It("errors when the varint value has the wrong length", func() { b := &bytes.Buffer{} quicvarint.Write(b, uint64(initialMaxStreamDataBidiLocalParameterID)) quicvarint.Write(b, 2) val := uint64(0xdeadbeef) Expect(quicvarint.Len(val)).ToNot(BeEquivalentTo(2)) quicvarint.Write(b, val) addInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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 := &bytes.Buffer{} quicvarint.Write(b, uint64(initialMaxStreamsBidiParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1)))) quicvarint.Write(b, uint64(protocol.MaxStreamCount+1)) addInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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 := &bytes.Buffer{} quicvarint.Write(b, uint64(initialMaxStreamsUniParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(uint64(protocol.MaxStreamCount+1)))) quicvarint.Write(b, uint64(protocol.MaxStreamCount+1)) addInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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() { b := &bytes.Buffer{} val := uint64(math.MaxUint64) / 5 quicvarint.Write(b, uint64(maxAckDelayParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(val))) quicvarint.Write(b, val) addInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), protocol.PerspectiveClient)).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "invalid value for max_ack_delay: 3689348814741910323ms (maximum 16383ms)", })) }) It("skips unknown parameters", func() { b := &bytes.Buffer{} // write a known parameter quicvarint.Write(b, uint64(initialMaxStreamDataBidiLocalParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(0x1337))) quicvarint.Write(b, 0x1337) // write an unknown parameter quicvarint.Write(b, 0x42) quicvarint.Write(b, 6) b.Write([]byte("foobar")) // write a known parameter quicvarint.Write(b, uint64(initialMaxStreamDataBidiRemoteParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(0x42))) quicvarint.Write(b, 0x42) addInitialSourceConnectionID(b) p := &TransportParameters{} Expect(p.Unmarshal(b.Bytes(), 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() { b := &bytes.Buffer{} // write first parameter quicvarint.Write(b, uint64(initialMaxStreamDataBidiLocalParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(0x1337))) quicvarint.Write(b, 0x1337) // write a second parameter quicvarint.Write(b, uint64(initialMaxStreamDataBidiRemoteParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(0x42))) quicvarint.Write(b, 0x42) // write first parameter again quicvarint.Write(b, uint64(initialMaxStreamDataBidiLocalParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(0x1337))) quicvarint.Write(b, 0x1337) addInitialSourceConnectionID(b) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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 := &bytes.Buffer{} quicvarint.Write(b, 0x42) quicvarint.Write(b, 7) b.Write([]byte("foobar")) p := &TransportParameters{} Expect(p.Unmarshal(b.Bytes(), 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 := &bytes.Buffer{} quicvarint.Write(b, uint64(statelessResetTokenParameterID)) quicvarint.Write(b, uint64(quicvarint.Len(16))) b.Write(make([]byte, 16)) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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 := &bytes.Buffer{} quicvarint.Write(b, uint64(originalDestinationConnectionIDParameterID)) quicvarint.Write(b, 6) b.Write([]byte("foobar")) Expect((&TransportParameters{}).Unmarshal(b.Bytes(), 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: net.IPv4(127, 0, 0, 1), IPv4Port: 42, IPv6: net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, IPv6Port: 13, ConnectionID: protocol.ConnectionID{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{}, }).Marshal(protocol.PerspectiveServer) p := &TransportParameters{} Expect(p.Unmarshal(data, protocol.PerspectiveServer)).To(Succeed()) Expect(p.PreferredAddress.IPv4.String()).To(Equal(pa.IPv4.String())) Expect(p.PreferredAddress.IPv4Port).To(Equal(pa.IPv4Port)) Expect(p.PreferredAddress.IPv6.String()).To(Equal(pa.IPv6.String())) Expect(p.PreferredAddress.IPv6Port).To(Equal(pa.IPv6Port)) 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 := &bytes.Buffer{} quicvarint.Write(b, uint64(preferredAddressParameterID)) quicvarint.Write(b, 6) b.Write([]byte("foobar")) p := &TransportParameters{} Expect(p.Unmarshal(b.Bytes(), 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.ConnectionID{} 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 too long connection IDs", func() { pa.ConnectionID = protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21} Expect(pa.ConnectionID.Len()).To(BeNumerically(">", protocol.MaxConnIDLen)) 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: 21", })) }) 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++ { buf := &bytes.Buffer{} quicvarint.Write(buf, uint64(preferredAddressParameterID)) buf.Write(raw[:i]) p := &TransportParameters{} Expect(p.Unmarshal(buf.Bytes(), 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: getRandomValue(), } Expect(params.ValidFor0RTT(params)).To(BeTrue()) b := &bytes.Buffer{} params.MarshalForSessionTicket(b) var tp TransportParameters Expect(tp.UnmarshalFromSessionTicket(bytes.NewReader(b.Bytes()))).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)) }) It("rejects the parameters if it can't parse them", func() { var p TransportParameters Expect(p.UnmarshalFromSessionTicket(bytes.NewReader([]byte("foobar")))).ToNot(Succeed()) }) It("rejects the parameters if the version changed", func() { var p TransportParameters buf := &bytes.Buffer{} p.MarshalForSessionTicket(buf) data := buf.Bytes() b := &bytes.Buffer{} quicvarint.Write(b, transportParameterMarshalingVersion+1) b.Write(data[quicvarint.Len(transportParameterMarshalingVersion):]) Expect(p.UnmarshalFromSessionTicket(bytes.NewReader(b.Bytes()))).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, } 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()) }) }) }) }) quic-go-0.25.0/internal/wire/transport_parameters.go000066400000000000000000000451251417145451600225510ustar00rootroot00000000000000package wire import ( "bytes" "errors" "fmt" "io" "math/rand" "net" "sort" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/quicvarint" ) const transportParameterMarshalingVersion = 1 func init() { rand.Seed(time.Now().UTC().UnixNano()) } 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 // https://datatracker.ietf.org/doc/draft-ietf-quic-datagram/ maxDatagramFrameSizeParameterID transportParameterID = 0x20 ) // PreferredAddress is the value encoding in the preferred_address transport parameter type PreferredAddress struct { IPv4 net.IP IPv4Port uint16 IPv6 net.IP IPv6Port uint16 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(bytes.NewReader(data), sentBy, false); err != nil { return &qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), } } return nil } func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspective, fromSessionTicket bool) error { // needed to check that every parameter is only sent at most once var parameterIDs []transportParameterID var ( readOriginalDestinationConnectionID bool readInitialSourceConnectionID bool ) p.AckDelayExponent = protocol.DefaultAckDelayExponent p.MaxAckDelay = protocol.DefaultMaxAckDelay p.MaxDatagramFrameSize = protocol.InvalidByteCount for r.Len() > 0 { paramIDInt, err := quicvarint.Read(r) if err != nil { return err } paramID := transportParameterID(paramIDInt) paramLen, err := quicvarint.Read(r) if err != nil { return err } if uint64(r.Len()) < paramLen { return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen) } parameterIDs = append(parameterIDs, paramID) switch paramID { case maxIdleTimeoutParameterID, maxUDPPayloadSizeParameterID, initialMaxDataParameterID, initialMaxStreamDataBidiLocalParameterID, initialMaxStreamDataBidiRemoteParameterID, initialMaxStreamDataUniParameterID, initialMaxStreamsBidiParameterID, initialMaxStreamsUniParameterID, maxAckDelayParameterID, activeConnectionIDLimitParameterID, maxDatagramFrameSizeParameterID, ackDelayExponentParameterID: if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil { return err } case preferredAddressParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent a preferred_address") } if err := p.readPreferredAddress(r, int(paramLen)); err != nil { return err } 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 r.Read(token[:]) p.StatelessResetToken = &token case originalDestinationConnectionIDParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent an original_destination_connection_id") } p.OriginalDestinationConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) readOriginalDestinationConnectionID = true case initialSourceConnectionIDParameterID: p.InitialSourceConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) readInitialSourceConnectionID = true case retrySourceConnectionIDParameterID: if sentBy == protocol.PerspectiveClient { return errors.New("client sent a retry_source_connection_id") } connID, _ := protocol.ReadConnectionID(r, int(paramLen)) p.RetrySourceConnectionID = &connID default: r.Seek(int64(paramLen), io.SeekCurrent) } } 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 sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] }) 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(r *bytes.Reader, expectedLen int) error { remainingLen := r.Len() pa := &PreferredAddress{} ipv4 := make([]byte, 4) if _, err := io.ReadFull(r, ipv4); err != nil { return err } pa.IPv4 = net.IP(ipv4) port, err := utils.BigEndian.ReadUint16(r) if err != nil { return err } pa.IPv4Port = port ipv6 := make([]byte, 16) if _, err := io.ReadFull(r, ipv6); err != nil { return err } pa.IPv6 = net.IP(ipv6) port, err = utils.BigEndian.ReadUint16(r) if err != nil { return err } pa.IPv6Port = port connIDLen, err := r.ReadByte() if err != nil { return err } if connIDLen == 0 || connIDLen > protocol.MaxConnIDLen { return fmt.Errorf("invalid connection ID length: %d", connIDLen) } connID, err := protocol.ReadConnectionID(r, int(connIDLen)) if err != nil { return err } pa.ConnectionID = connID if _, err := io.ReadFull(r, pa.StatelessResetToken[:]); err != nil { return err } if bytesRead := remainingLen - r.Len(); 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( r *bytes.Reader, paramID transportParameterID, expectedLen int, ) error { remainingLen := r.Len() val, err := quicvarint.Read(r) if err != nil { return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err) } if remainingLen-r.Len() != 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 = utils.MaxDuration(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond) case maxUDPPayloadSizeParameterID: if val < 1200 { return fmt.Errorf("invalid value for max_packet_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: 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 { b := &bytes.Buffer{} // add a greased value quicvarint.Write(b, uint64(27+31*rand.Intn(100))) length := rand.Intn(16) randomData := make([]byte, length) rand.Read(randomData) quicvarint.Write(b, uint64(length)) b.Write(randomData) // initial_max_stream_data_bidi_local p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) // initial_max_stream_data_bidi_remote p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) // initial_max_stream_data_uni p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) // initial_max_data p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) // initial_max_bidi_streams p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) // initial_max_uni_streams p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) // idle_timeout p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond)) // max_packet_size p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize)) // max_ack_delay // Only send it if is different from the default value. if p.MaxAckDelay != protocol.DefaultMaxAckDelay { 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 { p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent)) } // disable_active_migration if p.DisableActiveMigration { quicvarint.Write(b, uint64(disableActiveMigrationParameterID)) quicvarint.Write(b, 0) } if pers == protocol.PerspectiveServer { // stateless_reset_token if p.StatelessResetToken != nil { quicvarint.Write(b, uint64(statelessResetTokenParameterID)) quicvarint.Write(b, 16) b.Write(p.StatelessResetToken[:]) } // original_destination_connection_id quicvarint.Write(b, uint64(originalDestinationConnectionIDParameterID)) quicvarint.Write(b, uint64(p.OriginalDestinationConnectionID.Len())) b.Write(p.OriginalDestinationConnectionID.Bytes()) // preferred_address if p.PreferredAddress != nil { quicvarint.Write(b, uint64(preferredAddressParameterID)) quicvarint.Write(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16) ipv4 := p.PreferredAddress.IPv4 b.Write(ipv4[len(ipv4)-4:]) utils.BigEndian.WriteUint16(b, p.PreferredAddress.IPv4Port) b.Write(p.PreferredAddress.IPv6) utils.BigEndian.WriteUint16(b, p.PreferredAddress.IPv6Port) b.WriteByte(uint8(p.PreferredAddress.ConnectionID.Len())) b.Write(p.PreferredAddress.ConnectionID.Bytes()) b.Write(p.PreferredAddress.StatelessResetToken[:]) } } // active_connection_id_limit p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) // initial_source_connection_id quicvarint.Write(b, uint64(initialSourceConnectionIDParameterID)) quicvarint.Write(b, uint64(p.InitialSourceConnectionID.Len())) b.Write(p.InitialSourceConnectionID.Bytes()) // retry_source_connection_id if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil { quicvarint.Write(b, uint64(retrySourceConnectionIDParameterID)) quicvarint.Write(b, uint64(p.RetrySourceConnectionID.Len())) b.Write(p.RetrySourceConnectionID.Bytes()) } if p.MaxDatagramFrameSize != protocol.InvalidByteCount { p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) } return b.Bytes() } func (p *TransportParameters) marshalVarintParam(b *bytes.Buffer, id transportParameterID, val uint64) { quicvarint.Write(b, uint64(id)) quicvarint.Write(b, uint64(quicvarint.Len(val))) quicvarint.Write(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 *bytes.Buffer) { quicvarint.Write(b, transportParameterMarshalingVersion) // initial_max_stream_data_bidi_local p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) // initial_max_stream_data_bidi_remote p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) // initial_max_stream_data_uni p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) // initial_max_data p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) // initial_max_bidi_streams p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) // initial_max_uni_streams p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) // active_connection_id_limit p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) } // UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket. func (p *TransportParameters) UnmarshalFromSessionTicket(r *bytes.Reader) error { version, err := quicvarint.Read(r) if err != nil { return err } if version != transportParameterMarshalingVersion { return fmt.Errorf("unknown transport parameter marshaling version: %d", version) } return p.unmarshal(r, protocol.PerspectiveServer, true) } // ValidFor0RTT checks if the transport parameters match those saved in the session ticket. func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { 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 } // 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...) } quic-go-0.25.0/internal/wire/version_negotiation.go000066400000000000000000000035161417145451600223550ustar00rootroot00000000000000package wire import ( "bytes" "crypto/rand" "errors" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" ) // ParseVersionNegotiationPacket parses a Version Negotiation packet. func ParseVersionNegotiationPacket(b *bytes.Reader) (*Header, []protocol.VersionNumber, error) { hdr, err := parseHeader(b, 0) if err != nil { return nil, nil, err } if b.Len() == 0 { //nolint:stylecheck return nil, nil, errors.New("Version Negotiation packet has empty version list") } if b.Len()%4 != 0 { //nolint:stylecheck return nil, nil, errors.New("Version Negotiation packet has a version list with an invalid length") } versions := make([]protocol.VersionNumber, b.Len()/4) for i := 0; b.Len() > 0; i++ { v, err := utils.BigEndian.ReadUint32(b) if err != nil { return nil, nil, err } versions[i] = protocol.VersionNumber(v) } return hdr, versions, nil } // ComposeVersionNegotiation composes a Version Negotiation func ComposeVersionNegotiation(destConnID, srcConnID protocol.ConnectionID, versions []protocol.VersionNumber) ([]byte, error) { 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 := bytes.NewBuffer(make([]byte, 0, expectedLen)) r := make([]byte, 1) _, _ = rand.Read(r) // ignore the error here. It is not critical to have perfect random here. buf.WriteByte(r[0] | 0x80) utils.BigEndian.WriteUint32(buf, 0) // version 0 buf.WriteByte(uint8(destConnID.Len())) buf.Write(destConnID) buf.WriteByte(uint8(srcConnID.Len())) buf.Write(srcConnID) for _, v := range greasedVersions { utils.BigEndian.WriteUint32(buf, uint32(v)) } return buf.Bytes(), nil } quic-go-0.25.0/internal/wire/version_negotiation_test.go000066400000000000000000000066531417145451600234210ustar00rootroot00000000000000package wire import ( "bytes" "encoding/binary" "github.com/lucas-clemente/quic-go/internal/protocol" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Version Negotiation Packets", func() { It("parses a Version Negotiation packet", func() { srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} destConnID := protocol.ConnectionID{9, 8, 7, 6, 5, 4, 3, 2, 1} versions := []protocol.VersionNumber{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()) hdr, supportedVersions, err := ParseVersionNegotiationPacket(bytes.NewReader(data)) Expect(err).ToNot(HaveOccurred()) Expect(hdr.DestConnectionID).To(Equal(destConnID)) Expect(hdr.SrcConnectionID).To(Equal(srcConnID)) Expect(hdr.IsLongHeader).To(BeTrue()) Expect(hdr.Version).To(BeZero()) Expect(supportedVersions).To(Equal(versions)) }) It("errors if it contains versions of the wrong length", func() { connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} versions := []protocol.VersionNumber{0x22334455, 0x33445566} data, err := ComposeVersionNegotiation(connID, connID, versions) Expect(err).ToNot(HaveOccurred()) _, _, err = ParseVersionNegotiationPacket(bytes.NewReader(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.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} versions := []protocol.VersionNumber{0x22334455} data, err := ComposeVersionNegotiation(connID, connID, versions) Expect(err).ToNot(HaveOccurred()) // remove 8 bytes (two versions), since ComposeVersionNegotiation also added a reserved version number data = data[:len(data)-8] _, _, err = ParseVersionNegotiationPacket(bytes.NewReader(data)) Expect(err).To(MatchError("Version Negotiation packet has empty version list")) }) It("adds a reserved version", func() { srcConnID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0x13, 0x37} destConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} versions := []protocol.VersionNumber{1001, 1003} data, err := ComposeVersionNegotiation(destConnID, srcConnID, versions) Expect(err).ToNot(HaveOccurred()) Expect(data[0] & 0x80).ToNot(BeZero()) hdr, supportedVersions, err := ParseVersionNegotiationPacket(bytes.NewReader(data)) Expect(err).ToNot(HaveOccurred()) Expect(hdr.DestConnectionID).To(Equal(destConnID)) Expect(hdr.SrcConnectionID).To(Equal(srcConnID)) Expect(hdr.Version).To(BeZero()) // 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.VersionNumber 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 }) }) quic-go-0.25.0/internal/wire/wire_suite_test.go000066400000000000000000000007631417145451600215070ustar00rootroot00000000000000package wire import ( "bytes" "testing" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/quicvarint" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestWire(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Wire Suite") } const ( // a QUIC version that uses the IETF frame types versionIETFFrames = protocol.VersionTLS ) func encodeVarInt(i uint64) []byte { b := &bytes.Buffer{} quicvarint.Write(b, i) return b.Bytes() } quic-go-0.25.0/interop/000077500000000000000000000000001417145451600146325ustar00rootroot00000000000000quic-go-0.25.0/interop/Dockerfile000066400000000000000000000021371417145451600166270ustar00rootroot00000000000000FROM martenseemann/quic-network-simulator-endpoint:latest AS builder RUN apt-get update && apt-get install -y wget tar git RUN wget https://dl.google.com/go/go1.17.linux-amd64.tar.gz && \ tar xfz go1.17.linux-amd64.tar.gz && \ rm go1.17.linux-amd64.tar.gz ENV PATH="/go/bin:${PATH}" # build with --build-arg CACHEBUST=$(date +%s) ARG CACHEBUST=1 RUN git clone https://github.com/lucas-clemente/quic-go && \ cd quic-go && \ git fetch origin interop && git checkout -t origin/interop && \ go get ./... WORKDIR /quic-go RUN git rev-parse HEAD > commit.txt RUN go build -o server -ldflags="-X github.com/lucas-clemente/quic-go/qlog.quicGoVersion=$(git describe --always --long --dirty)" interop/server/main.go RUN go build -o client -ldflags="-X github.com/lucas-clemente/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" ] quic-go-0.25.0/interop/client/000077500000000000000000000000001417145451600161105ustar00rootroot00000000000000quic-go-0.25.0/interop/client/main.go000066400000000000000000000115121417145451600173630ustar00rootroot00000000000000package main import ( "crypto/tls" "errors" "flag" "fmt" "io" "log" "net/http" "os" "strings" "time" "golang.org/x/sync/errgroup" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/http3" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/interop/http09" "github.com/lucas-clemente/quic-go/interop/utils" "github.com/lucas-clemente/quic-go/qlog" ) 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() getLogWriter, err := utils.GetQLOGWriter() if err != nil { return err } quicConf := &quic.Config{Tracer: qlog.NewTracer(getLogWriter)} 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.KeyUpdateInterval = 100 case "chacha20": tlsConf.CipherSuites = []uint16{tls.TLS_CHACHA20_POLY1305_SHA256} 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.VersionNumber{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 } quic-go-0.25.0/interop/http09/000077500000000000000000000000001417145451600157625ustar00rootroot00000000000000quic-go-0.25.0/interop/http09/client.go000066400000000000000000000065731417145451600176020ustar00rootroot00000000000000package http09 import ( "context" "crypto/tls" "errors" "io/ioutil" "log" "net" "net/http" "strings" "sync" "golang.org/x/net/idna" "github.com/lucas-clemente/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 sess quic.EarlySession dialErr error } func (c *client) RoundTrip(req *http.Request) (*http.Response, error) { c.once.Do(func() { c.sess, c.dialErr = quic.DialAddrEarly(c.hostname, c.tlsConf, c.quicConf) }) if c.dialErr != nil { return nil, c.dialErr } if req.Method != MethodGet0RTT { <-c.sess.HandshakeComplete().Done() } return c.doRequest(req) } func (c *client) doRequest(req *http.Request) (*http.Response, error) { str, err := c.sess.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: ioutil.NopCloser(str), } return rsp, nil } func (c *client) Close() error { if c.sess == nil { return nil } return c.sess.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) } quic-go-0.25.0/interop/http09/http09_suite_test.go000066400000000000000000000002741417145451600217140ustar00rootroot00000000000000package http09 import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestHttp09(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "HTTP/0.9 Suite") } quic-go-0.25.0/interop/http09/http_test.go000066400000000000000000000041511417145451600203300ustar00rootroot00000000000000package http09 import ( "crypto/tls" "fmt" "io/ioutil" "net" "net/http" "net/http/httptest" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/testdata" . "github.com/onsi/ginkgo" . "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 }).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 := ioutil.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 := ioutil.ReadAll(rsp.Body) Expect(err).ToNot(HaveOccurred()) Expect(data).To(Equal([]byte("done"))) }) }) quic-go-0.25.0/interop/http09/server.go000066400000000000000000000056401417145451600176240ustar00rootroot00000000000000package http09 import ( "context" "errors" "io" "io/ioutil" "log" "net" "net/http" "net/url" "runtime" "strings" "sync" "github.com/lucas-clemente/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 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} ln, err := quic.ListenEarly(conn, tlsConf, s.QuicConfig) if err != nil { return err } s.mutex.Lock() s.listener = ln s.mutex.Unlock() for { sess, err := ln.Accept(context.Background()) if err != nil { return err } go s.handleConn(sess) } } func (s *Server) handleConn(sess quic.Session) { for { str, err := sess.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 := ioutil.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() } quic-go-0.25.0/interop/run_endpoint.sh000066400000000000000000000007441417145451600176770ustar00rootroot00000000000000#!/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 quic-go-0.25.0/interop/server/000077500000000000000000000000001417145451600161405ustar00rootroot00000000000000quic-go-0.25.0/interop/server/main.go000066400000000000000000000046251417145451600174220ustar00rootroot00000000000000package main import ( "crypto/tls" "fmt" "log" "net" "net/http" "os" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/http3" "github.com/lucas-clemente/quic-go/interop/http09" "github.com/lucas-clemente/quic-go/interop/utils" "github.com/lucas-clemente/quic-go/qlog" ) 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") getLogWriter, err := utils.GetQLOGWriter() if err != nil { fmt.Println(err.Error()) os.Exit(1) } // a quic.Config that doesn't do a Retry quicConf := &quic.Config{ AcceptToken: func(_ net.Addr, _ *quic.Token) bool { return true }, Tracer: qlog.NewTracer(getLogWriter), } 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", "transfer", "resumption", "zerortt", "multiconnect": err = runHTTP09Server(quicConf) case "chacha20": tlsConf.CipherSuites = []uint16{tls.TLS_CHACHA20_POLY1305_SHA256} err = runHTTP09Server(quicConf) case "retry": // By default, quic-go performs a Retry on every incoming connection. quicConf.AcceptToken = nil err = runHTTP09Server(quicConf) 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) error { server := http09.Server{ Server: &http.Server{ Addr: ":443", TLSConfig: tlsConf, }, QuicConfig: quicConf, } http.DefaultServeMux.Handle("/", http.FileServer(http.Dir("/www"))) return server.ListenAndServe() } func runHTTP3Server(quicConf *quic.Config) error { server := http3.Server{ Server: &http.Server{ Addr: ":443", TLSConfig: tlsConf, }, QuicConfig: quicConf, } http.DefaultServeMux.Handle("/", http.FileServer(http.Dir("/www"))) return server.ListenAndServe() } quic-go-0.25.0/interop/utils/000077500000000000000000000000001417145451600157725ustar00rootroot00000000000000quic-go-0.25.0/interop/utils/logging.go000066400000000000000000000024111417145451600177450ustar00rootroot00000000000000package utils import ( "bufio" "fmt" "io" "log" "os" "strings" "github.com/lucas-clemente/quic-go/logging" "github.com/lucas-clemente/quic-go/internal/utils" ) // 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 } // GetQLOGWriter creates the QLOGDIR and returns the GetLogWriter callback func GetQLOGWriter() (func(perspective logging.Perspective, connID []byte) io.WriteCloser, error) { qlogDir := os.Getenv("QLOGDIR") if len(qlogDir) == 0 { return nil, nil } if _, err := os.Stat(qlogDir); os.IsNotExist(err) { if err := os.MkdirAll(qlogDir, 0o666); err != nil { return nil, fmt.Errorf("failed to create qlog dir %s: %s", qlogDir, err.Error()) } } return func(_ logging.Perspective, connID []byte) io.WriteCloser { path := fmt.Sprintf("%s/%x.qlog", 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 utils.NewBufferedWriteCloser(bufio.NewWriter(f), f) }, nil } quic-go-0.25.0/logging/000077500000000000000000000000001417145451600146005ustar00rootroot00000000000000quic-go-0.25.0/logging/frame.go000066400000000000000000000041711417145451600162240ustar00rootroot00000000000000package logging import "github.com/lucas-clemente/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 } quic-go-0.25.0/logging/interface.go000066400000000000000000000122301417145451600170650ustar00rootroot00000000000000// Package logging defines a logging interface for quic-go. // This package should not be considered stable package logging import ( "context" "net" "time" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" ) type ( // A ByteCount is used to count bytes. ByteCount = protocol.ByteCount // A ConnectionID is a QUIC Connection ID. ConnectionID = protocol.ConnectionID // 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. VersionNumber = protocol.VersionNumber // The Header is the QUIC packet header, before removing header protection. Header = wire.Header // The ExtendedHeader is the QUIC 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 ( // KeyPhaseZero is key phase bit 0 KeyPhaseZero KeyPhaseBit = protocol.KeyPhaseZero // KeyPhaseOne is key phase bit 1 KeyPhaseOne KeyPhaseBit = protocol.KeyPhaseOne ) const ( // PerspectiveServer is used for a QUIC server PerspectiveServer Perspective = protocol.PerspectiveServer // PerspectiveClient is used for a QUIC client PerspectiveClient Perspective = protocol.PerspectiveClient ) const ( // EncryptionInitial is the Initial encryption level EncryptionInitial EncryptionLevel = protocol.EncryptionInitial // EncryptionHandshake is the Handshake encryption level EncryptionHandshake EncryptionLevel = protocol.EncryptionHandshake // Encryption1RTT is the 1-RTT encryption level Encryption1RTT EncryptionLevel = protocol.Encryption1RTT // Encryption0RTT is the 0-RTT encryption level Encryption0RTT EncryptionLevel = protocol.Encryption0RTT ) const ( // StreamTypeUni is a unidirectional stream StreamTypeUni = protocol.StreamTypeUni // StreamTypeBidi is a bidirectional stream StreamTypeBidi = protocol.StreamTypeBidi ) // A Tracer traces events. type Tracer interface { // TracerForConnection requests a new tracer for a connection. // The ODCID is the original destination connection ID: // The destination connection ID that the client used on the first Initial packet it sent on this connection. // If nil is returned, tracing will be disabled for this connection. TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer SentPacket(net.Addr, *Header, ByteCount, []Frame) DroppedPacket(net.Addr, PacketType, ByteCount, PacketDropReason) } // A ConnectionTracer records events. type ConnectionTracer interface { StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) ClosedConnection(error) SentTransportParameters(*TransportParameters) ReceivedTransportParameters(*TransportParameters) RestoredTransportParameters(parameters *TransportParameters) // for 0-RTT SentPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame) ReceivedVersionNegotiationPacket(*Header, []VersionNumber) ReceivedRetry(*Header) ReceivedPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) BufferedPacket(PacketType) DroppedPacket(PacketType, ByteCount, PacketDropReason) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFlight ByteCount, packetsInFlight int) AcknowledgedPacket(EncryptionLevel, PacketNumber) LostPacket(EncryptionLevel, PacketNumber, PacketLossReason) UpdatedCongestionState(CongestionState) UpdatedPTOCount(value uint32) UpdatedKeyFromTLS(EncryptionLevel, Perspective) UpdatedKey(generation KeyPhase, remote bool) DroppedEncryptionLevel(EncryptionLevel) DroppedKey(generation KeyPhase) SetLossTimer(TimerType, EncryptionLevel, time.Time) LossTimerExpired(TimerType, EncryptionLevel) LossTimerCanceled() // Close is called when the connection is closed. Close() Debug(name, msg string) } quic-go-0.25.0/logging/logging_suite_test.go000066400000000000000000000005761417145451600210350ustar00rootroot00000000000000package logging import ( "testing" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) 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() }) quic-go-0.25.0/logging/mock_connection_tracer_test.go000066400000000000000000000357531417145451600227130ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/logging (interfaces: ConnectionTracer) // Package logging is a generated GoMock package. package logging import ( net "net" reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" utils "github.com/lucas-clemente/quic-go/internal/utils" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcknowledgedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).AcknowledgedPacket), arg0, arg1) } // BufferedPacket mocks base method. func (m *MockConnectionTracer) BufferedPacket(arg0 PacketType) { m.ctrl.T.Helper() m.ctrl.Call(m, "BufferedPacket", arg0) } // BufferedPacket indicates an expected call of BufferedPacket. func (mr *MockConnectionTracerMockRecorder) BufferedPacket(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BufferedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).BufferedPacket), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConnectionTracer)(nil).Close)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClosedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).ClosedConnection), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Debug", reflect.TypeOf((*MockConnectionTracer)(nil).Debug), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedEncryptionLevel", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedEncryptionLevel), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedKey", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedKey), arg0) } // DroppedPacket mocks base method. func (m *MockConnectionTracer) DroppedPacket(arg0 PacketType, arg1 protocol.ByteCount, arg2 PacketDropReason) { m.ctrl.T.Helper() m.ctrl.Call(m, "DroppedPacket", arg0, arg1, arg2) } // DroppedPacket indicates an expected call of DroppedPacket. func (mr *MockConnectionTracerMockRecorder) DroppedPacket(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).DroppedPacket), arg0, arg1, arg2) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LossTimerCanceled", reflect.TypeOf((*MockConnectionTracer)(nil).LossTimerCanceled)) } // LossTimerExpired mocks base method. func (m *MockConnectionTracer) LossTimerExpired(arg0 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LossTimerExpired", reflect.TypeOf((*MockConnectionTracer)(nil).LossTimerExpired), arg0, arg1) } // LostPacket mocks base method. func (m *MockConnectionTracer) LostPacket(arg0 protocol.EncryptionLevel, arg1 protocol.PacketNumber, arg2 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LostPacket", reflect.TypeOf((*MockConnectionTracer)(nil).LostPacket), arg0, arg1, arg2) } // NegotiatedVersion mocks base method. func (m *MockConnectionTracer) NegotiatedVersion(arg0 protocol.VersionNumber, arg1, arg2 []protocol.VersionNumber) { 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NegotiatedVersion", reflect.TypeOf((*MockConnectionTracer)(nil).NegotiatedVersion), arg0, arg1, arg2) } // ReceivedPacket mocks base method. func (m *MockConnectionTracer) ReceivedPacket(arg0 *wire.ExtendedHeader, arg1 protocol.ByteCount, arg2 []Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedPacket", arg0, arg1, arg2) } // ReceivedPacket indicates an expected call of ReceivedPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedPacket(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedPacket), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedRetry", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedRetry), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedTransportParameters), arg0) } // ReceivedVersionNegotiationPacket mocks base method. func (m *MockConnectionTracer) ReceivedVersionNegotiationPacket(arg0 *wire.Header, arg1 []protocol.VersionNumber) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReceivedVersionNegotiationPacket", arg0, arg1) } // ReceivedVersionNegotiationPacket indicates an expected call of ReceivedVersionNegotiationPacket. func (mr *MockConnectionTracerMockRecorder) ReceivedVersionNegotiationPacket(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceivedVersionNegotiationPacket", reflect.TypeOf((*MockConnectionTracer)(nil).ReceivedVersionNegotiationPacket), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RestoredTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).RestoredTransportParameters), arg0) } // SentPacket mocks base method. func (m *MockConnectionTracer) SentPacket(arg0 *wire.ExtendedHeader, arg1 protocol.ByteCount, arg2 *wire.AckFrame, arg3 []Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "SentPacket", arg0, arg1, arg2, arg3) } // SentPacket indicates an expected call of SentPacket. func (mr *MockConnectionTracerMockRecorder) SentPacket(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockConnectionTracer)(nil).SentPacket), arg0, arg1, arg2, arg3) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentTransportParameters", reflect.TypeOf((*MockConnectionTracer)(nil).SentTransportParameters), arg0) } // SetLossTimer mocks base method. func (m *MockConnectionTracer) SetLossTimer(arg0 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLossTimer", reflect.TypeOf((*MockConnectionTracer)(nil).SetLossTimer), arg0, arg1, arg2) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StartedConnection", reflect.TypeOf((*MockConnectionTracer)(nil).StartedConnection), arg0, arg1, arg2, arg3) } // UpdatedCongestionState mocks base method. func (m *MockConnectionTracer) UpdatedCongestionState(arg0 CongestionState) { m.ctrl.T.Helper() m.ctrl.Call(m, "UpdatedCongestionState", arg0) } // UpdatedCongestionState indicates an expected call of UpdatedCongestionState. func (mr *MockConnectionTracerMockRecorder) UpdatedCongestionState(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedCongestionState", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedCongestionState), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedKey", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedKey), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedKeyFromTLS", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedKeyFromTLS), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedMetrics", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedMetrics), arg0, arg1, arg2, arg3) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatedPTOCount", reflect.TypeOf((*MockConnectionTracer)(nil).UpdatedPTOCount), arg0) } quic-go-0.25.0/logging/mock_tracer_test.go000066400000000000000000000053461417145451600204670ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go/logging (interfaces: Tracer) // Package logging is a generated GoMock package. package logging import ( context "context" net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 } // DroppedPacket mocks base method. func (m *MockTracer) DroppedPacket(arg0 net.Addr, arg1 PacketType, arg2 protocol.ByteCount, arg3 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DroppedPacket", reflect.TypeOf((*MockTracer)(nil).DroppedPacket), arg0, arg1, arg2, arg3) } // SentPacket mocks base method. func (m *MockTracer) SentPacket(arg0 net.Addr, arg1 *wire.Header, arg2 protocol.ByteCount, arg3 []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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SentPacket", reflect.TypeOf((*MockTracer)(nil).SentPacket), arg0, arg1, arg2, arg3) } // TracerForConnection mocks base method. func (m *MockTracer) TracerForConnection(arg0 context.Context, arg1 protocol.Perspective, arg2 protocol.ConnectionID) ConnectionTracer { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "TracerForConnection", arg0, arg1, arg2) ret0, _ := ret[0].(ConnectionTracer) return ret0 } // TracerForConnection indicates an expected call of TracerForConnection. func (mr *MockTracerMockRecorder) TracerForConnection(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TracerForConnection", reflect.TypeOf((*MockTracer)(nil).TracerForConnection), arg0, arg1, arg2) } quic-go-0.25.0/logging/mockgen.go000066400000000000000000000006301417145451600165510ustar00rootroot00000000000000package logging //go:generate sh -c "mockgen -package logging -self_package github.com/lucas-clemente/quic-go/logging -destination mock_connection_tracer_test.go github.com/lucas-clemente/quic-go/logging ConnectionTracer" //go:generate sh -c "mockgen -package logging -self_package github.com/lucas-clemente/quic-go/logging -destination mock_tracer_test.go github.com/lucas-clemente/quic-go/logging Tracer" quic-go-0.25.0/logging/multiplex.go000066400000000000000000000132031417145451600171510ustar00rootroot00000000000000package logging import ( "context" "net" "time" ) type tracerMultiplexer struct { tracers []Tracer } var _ Tracer = &tracerMultiplexer{} // 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 &tracerMultiplexer{tracers} } func (m *tracerMultiplexer) TracerForConnection(ctx context.Context, p Perspective, odcid ConnectionID) ConnectionTracer { var connTracers []ConnectionTracer for _, t := range m.tracers { if ct := t.TracerForConnection(ctx, p, odcid); ct != nil { connTracers = append(connTracers, ct) } } return NewMultiplexedConnectionTracer(connTracers...) } func (m *tracerMultiplexer) SentPacket(remote net.Addr, hdr *Header, size ByteCount, frames []Frame) { for _, t := range m.tracers { t.SentPacket(remote, hdr, size, frames) } } func (m *tracerMultiplexer) DroppedPacket(remote net.Addr, typ PacketType, size ByteCount, reason PacketDropReason) { for _, t := range m.tracers { t.DroppedPacket(remote, typ, size, reason) } } type connTracerMultiplexer struct { tracers []ConnectionTracer } var _ ConnectionTracer = &connTracerMultiplexer{} // 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 &connTracerMultiplexer{tracers: tracers} } func (m *connTracerMultiplexer) StartedConnection(local, remote net.Addr, srcConnID, destConnID ConnectionID) { for _, t := range m.tracers { t.StartedConnection(local, remote, srcConnID, destConnID) } } func (m *connTracerMultiplexer) NegotiatedVersion(chosen VersionNumber, clientVersions, serverVersions []VersionNumber) { for _, t := range m.tracers { t.NegotiatedVersion(chosen, clientVersions, serverVersions) } } func (m *connTracerMultiplexer) ClosedConnection(e error) { for _, t := range m.tracers { t.ClosedConnection(e) } } func (m *connTracerMultiplexer) SentTransportParameters(tp *TransportParameters) { for _, t := range m.tracers { t.SentTransportParameters(tp) } } func (m *connTracerMultiplexer) ReceivedTransportParameters(tp *TransportParameters) { for _, t := range m.tracers { t.ReceivedTransportParameters(tp) } } func (m *connTracerMultiplexer) RestoredTransportParameters(tp *TransportParameters) { for _, t := range m.tracers { t.RestoredTransportParameters(tp) } } func (m *connTracerMultiplexer) SentPacket(hdr *ExtendedHeader, size ByteCount, ack *AckFrame, frames []Frame) { for _, t := range m.tracers { t.SentPacket(hdr, size, ack, frames) } } func (m *connTracerMultiplexer) ReceivedVersionNegotiationPacket(hdr *Header, versions []VersionNumber) { for _, t := range m.tracers { t.ReceivedVersionNegotiationPacket(hdr, versions) } } func (m *connTracerMultiplexer) ReceivedRetry(hdr *Header) { for _, t := range m.tracers { t.ReceivedRetry(hdr) } } func (m *connTracerMultiplexer) ReceivedPacket(hdr *ExtendedHeader, size ByteCount, frames []Frame) { for _, t := range m.tracers { t.ReceivedPacket(hdr, size, frames) } } func (m *connTracerMultiplexer) BufferedPacket(typ PacketType) { for _, t := range m.tracers { t.BufferedPacket(typ) } } func (m *connTracerMultiplexer) DroppedPacket(typ PacketType, size ByteCount, reason PacketDropReason) { for _, t := range m.tracers { t.DroppedPacket(typ, size, reason) } } func (m *connTracerMultiplexer) UpdatedCongestionState(state CongestionState) { for _, t := range m.tracers { t.UpdatedCongestionState(state) } } func (m *connTracerMultiplexer) UpdatedMetrics(rttStats *RTTStats, cwnd, bytesInFLight ByteCount, packetsInFlight int) { for _, t := range m.tracers { t.UpdatedMetrics(rttStats, cwnd, bytesInFLight, packetsInFlight) } } func (m *connTracerMultiplexer) AcknowledgedPacket(encLevel EncryptionLevel, pn PacketNumber) { for _, t := range m.tracers { t.AcknowledgedPacket(encLevel, pn) } } func (m *connTracerMultiplexer) LostPacket(encLevel EncryptionLevel, pn PacketNumber, reason PacketLossReason) { for _, t := range m.tracers { t.LostPacket(encLevel, pn, reason) } } func (m *connTracerMultiplexer) UpdatedPTOCount(value uint32) { for _, t := range m.tracers { t.UpdatedPTOCount(value) } } func (m *connTracerMultiplexer) UpdatedKeyFromTLS(encLevel EncryptionLevel, perspective Perspective) { for _, t := range m.tracers { t.UpdatedKeyFromTLS(encLevel, perspective) } } func (m *connTracerMultiplexer) UpdatedKey(generation KeyPhase, remote bool) { for _, t := range m.tracers { t.UpdatedKey(generation, remote) } } func (m *connTracerMultiplexer) DroppedEncryptionLevel(encLevel EncryptionLevel) { for _, t := range m.tracers { t.DroppedEncryptionLevel(encLevel) } } func (m *connTracerMultiplexer) DroppedKey(generation KeyPhase) { for _, t := range m.tracers { t.DroppedKey(generation) } } func (m *connTracerMultiplexer) SetLossTimer(typ TimerType, encLevel EncryptionLevel, exp time.Time) { for _, t := range m.tracers { t.SetLossTimer(typ, encLevel, exp) } } func (m *connTracerMultiplexer) LossTimerExpired(typ TimerType, encLevel EncryptionLevel) { for _, t := range m.tracers { t.LossTimerExpired(typ, encLevel) } } func (m *connTracerMultiplexer) LossTimerCanceled() { for _, t := range m.tracers { t.LossTimerCanceled() } } func (m *connTracerMultiplexer) Debug(name, msg string) { for _, t := range m.tracers { t.Debug(name, msg) } } func (m *connTracerMultiplexer) Close() { for _, t := range m.tracers { t.Close() } } quic-go-0.25.0/logging/multiplex_test.go000066400000000000000000000233461417145451600202210ustar00rootroot00000000000000package logging import ( "context" "errors" "net" "time" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "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 := NewMockTracer(mockCtrl) tracer := NewMultiplexedTracer(tr) Expect(tracer).To(BeAssignableToTypeOf(&MockTracer{})) }) Context("tracing events", func() { var ( tracer Tracer tr1, tr2 *MockTracer ) BeforeEach(func() { tr1 = NewMockTracer(mockCtrl) tr2 = NewMockTracer(mockCtrl) tracer = NewMultiplexedTracer(tr1, tr2) }) It("multiplexes the TracerForConnection call", func() { ctx := context.Background() tr1.EXPECT().TracerForConnection(ctx, PerspectiveClient, ConnectionID{1, 2, 3}) tr2.EXPECT().TracerForConnection(ctx, PerspectiveClient, ConnectionID{1, 2, 3}) tracer.TracerForConnection(ctx, PerspectiveClient, ConnectionID{1, 2, 3}) }) It("uses multiple connection tracers", func() { ctx := context.Background() ctr1 := NewMockConnectionTracer(mockCtrl) ctr2 := NewMockConnectionTracer(mockCtrl) tr1.EXPECT().TracerForConnection(ctx, PerspectiveServer, ConnectionID{1, 2, 3}).Return(ctr1) tr2.EXPECT().TracerForConnection(ctx, PerspectiveServer, ConnectionID{1, 2, 3}).Return(ctr2) tr := tracer.TracerForConnection(ctx, PerspectiveServer, ConnectionID{1, 2, 3}) ctr1.EXPECT().LossTimerCanceled() ctr2.EXPECT().LossTimerCanceled() tr.LossTimerCanceled() }) It("handles tracers that return a nil ConnectionTracer", func() { ctx := context.Background() ctr1 := NewMockConnectionTracer(mockCtrl) tr1.EXPECT().TracerForConnection(ctx, PerspectiveServer, ConnectionID{1, 2, 3}).Return(ctr1) tr2.EXPECT().TracerForConnection(ctx, PerspectiveServer, ConnectionID{1, 2, 3}) tr := tracer.TracerForConnection(ctx, PerspectiveServer, ConnectionID{1, 2, 3}) ctr1.EXPECT().LossTimerCanceled() tr.LossTimerCanceled() }) It("returns nil when all tracers return a nil ConnectionTracer", func() { ctx := context.Background() tr1.EXPECT().TracerForConnection(ctx, PerspectiveClient, ConnectionID{1, 2, 3}) tr2.EXPECT().TracerForConnection(ctx, PerspectiveClient, ConnectionID{1, 2, 3}) Expect(tracer.TracerForConnection(ctx, PerspectiveClient, ConnectionID{1, 2, 3})).To(BeNil()) }) It("traces the PacketSent event", func() { remote := &net.UDPAddr{IP: net.IPv4(4, 3, 2, 1)} hdr := &Header{DestConnectionID: ConnectionID{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 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) }) }) }) Context("Connection Tracer", func() { var ( tracer ConnectionTracer tr1 *MockConnectionTracer tr2 *MockConnectionTracer ) BeforeEach(func() { tr1 = NewMockConnectionTracer(mockCtrl) tr2 = NewMockConnectionTracer(mockCtrl) tracer = NewMultiplexedConnectionTracer(tr1, tr2) }) It("trace the ConnectionStarted event", func() { local := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4)} remote := &net.UDPAddr{IP: net.IPv4(4, 3, 2, 1)} tr1.EXPECT().StartedConnection(local, remote, ConnectionID{1, 2, 3, 4}, ConnectionID{4, 3, 2, 1}) tr2.EXPECT().StartedConnection(local, remote, ConnectionID{1, 2, 3, 4}, ConnectionID{4, 3, 2, 1}) tracer.StartedConnection(local, remote, ConnectionID{1, 2, 3, 4}, ConnectionID{4, 3, 2, 1}) }) 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 SentPacket event", func() { hdr := &ExtendedHeader{Header: Header{DestConnectionID: ConnectionID{1, 2, 3}}} ack := &AckFrame{AckRanges: []AckRange{{Smallest: 1, Largest: 10}}} ping := &PingFrame{} tr1.EXPECT().SentPacket(hdr, ByteCount(1337), ack, []Frame{ping}) tr2.EXPECT().SentPacket(hdr, ByteCount(1337), ack, []Frame{ping}) tracer.SentPacket(hdr, 1337, ack, []Frame{ping}) }) It("traces the ReceivedVersionNegotiationPacket event", func() { hdr := &Header{DestConnectionID: ConnectionID{1, 2, 3}} tr1.EXPECT().ReceivedVersionNegotiationPacket(hdr, []VersionNumber{1337}) tr2.EXPECT().ReceivedVersionNegotiationPacket(hdr, []VersionNumber{1337}) tracer.ReceivedVersionNegotiationPacket(hdr, []VersionNumber{1337}) }) It("traces the ReceivedRetry event", func() { hdr := &Header{DestConnectionID: ConnectionID{1, 2, 3}} tr1.EXPECT().ReceivedRetry(hdr) tr2.EXPECT().ReceivedRetry(hdr) tracer.ReceivedRetry(hdr) }) It("traces the ReceivedPacket event", func() { hdr := &ExtendedHeader{Header: Header{DestConnectionID: ConnectionID{1, 2, 3}}} ping := &PingFrame{} tr1.EXPECT().ReceivedPacket(hdr, ByteCount(1337), []Frame{ping}) tr2.EXPECT().ReceivedPacket(hdr, ByteCount(1337), []Frame{ping}) tracer.ReceivedPacket(hdr, 1337, []Frame{ping}) }) It("traces the BufferedPacket event", func() { tr1.EXPECT().BufferedPacket(PacketTypeHandshake) tr2.EXPECT().BufferedPacket(PacketTypeHandshake) tracer.BufferedPacket(PacketTypeHandshake) }) It("traces the DroppedPacket event", func() { tr1.EXPECT().DroppedPacket(PacketTypeInitial, ByteCount(1337), PacketDropHeaderParseError) tr2.EXPECT().DroppedPacket(PacketTypeInitial, ByteCount(1337), PacketDropHeaderParseError) tracer.DroppedPacket(PacketTypeInitial, 1337, PacketDropHeaderParseError) }) 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() }) }) }) quic-go-0.25.0/logging/packet_header.go000066400000000000000000000011641417145451600177100ustar00rootroot00000000000000package logging import ( "github.com/lucas-clemente/quic-go/internal/protocol" ) // PacketTypeFromHeader determines the packet type from a *wire.Header. func PacketTypeFromHeader(hdr *Header) PacketType { if !hdr.IsLongHeader { return PacketType1RTT } 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 } } quic-go-0.25.0/logging/packet_header_test.go000066400000000000000000000033331417145451600207470ustar00rootroot00000000000000package logging import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "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{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Version: protocol.VersionTLS, })).To(Equal(PacketTypeInitial)) }) It("recognizes Handshake packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Version: protocol.VersionTLS, })).To(Equal(PacketTypeHandshake)) }) It("recognizes Retry packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeRetry, Version: protocol.VersionTLS, })).To(Equal(PacketTypeRetry)) }) It("recognizes 0-RTT packets", func() { Expect(PacketTypeFromHeader(&wire.Header{ IsLongHeader: true, Type: protocol.PacketType0RTT, Version: protocol.VersionTLS, })).To(Equal(PacketType0RTT)) }) It("recognizes Version Negotiation packets", func() { Expect(PacketTypeFromHeader(&wire.Header{IsLongHeader: true})).To(Equal(PacketTypeVersionNegotiation)) }) It("recognizes 1-RTT packets", func() { Expect(PacketTypeFromHeader(&wire.Header{})).To(Equal(PacketType1RTT)) }) It("handles unrecognized packet types", func() { Expect(PacketTypeFromHeader(&wire.Header{ IsLongHeader: true, Version: protocol.VersionTLS, })).To(Equal(PacketTypeNotDetermined)) }) }) }) quic-go-0.25.0/logging/types.go000066400000000000000000000072321417145451600162770ustar00rootroot00000000000000package 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 session is closed type TimeoutReason uint8 const ( // TimeoutReasonHandshake is used when the session 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 session 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 // CongestionStateCongestionAvoidance is the recovery phase of Reno / Cubic CongestionStateRecovery // CongestionStateApplicationLimited means that the congestion controller is application limited CongestionStateApplicationLimited ) quic-go-0.25.0/mock_ack_frame_source_test.go000066400000000000000000000032141417145451600210410ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: packet_packer.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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(encLevel protocol.EncryptionLevel, onlyIfQueued bool) *wire.AckFrame { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAckFrame", encLevel, onlyIfQueued) ret0, _ := ret[0].(*wire.AckFrame) return ret0 } // GetAckFrame indicates an expected call of GetAckFrame. func (mr *MockAckFrameSourceMockRecorder) GetAckFrame(encLevel, onlyIfQueued interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAckFrame", reflect.TypeOf((*MockAckFrameSource)(nil).GetAckFrame), encLevel, onlyIfQueued) } quic-go-0.25.0/mock_batch_conn_test.go000066400000000000000000000026301417145451600176500ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: conn_oob.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadBatch", reflect.TypeOf((*MockBatchConn)(nil).ReadBatch), ms, flags) } quic-go-0.25.0/mock_crypto_data_handler_test.go000066400000000000000000000031401417145451600215550ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: crypto_stream_manager.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // MockCryptoDataHandler is a mock of CryptoDataHandler interface. type MockCryptoDataHandler struct { ctrl *gomock.Controller recorder *MockCryptoDataHandlerMockRecorder } // MockCryptoDataHandlerMockRecorder is the mock recorder for MockCryptoDataHandler. type MockCryptoDataHandlerMockRecorder struct { mock *MockCryptoDataHandler } // NewMockCryptoDataHandler creates a new mock instance. func NewMockCryptoDataHandler(ctrl *gomock.Controller) *MockCryptoDataHandler { mock := &MockCryptoDataHandler{ctrl: ctrl} mock.recorder = &MockCryptoDataHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCryptoDataHandler) EXPECT() *MockCryptoDataHandlerMockRecorder { return m.recorder } // HandleMessage mocks base method. func (m *MockCryptoDataHandler) HandleMessage(arg0 []byte, arg1 protocol.EncryptionLevel) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleMessage", arg0, arg1) ret0, _ := ret[0].(bool) return ret0 } // HandleMessage indicates an expected call of HandleMessage. func (mr *MockCryptoDataHandlerMockRecorder) HandleMessage(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockCryptoDataHandler)(nil).HandleMessage), arg0, arg1) } quic-go-0.25.0/mock_crypto_stream_test.go000066400000000000000000000075641417145451600204600ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: crypto_stream.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // MockCryptoStream is a mock of CryptoStream interface. type MockCryptoStream struct { ctrl *gomock.Controller recorder *MockCryptoStreamMockRecorder } // MockCryptoStreamMockRecorder is the mock recorder for MockCryptoStream. type MockCryptoStreamMockRecorder struct { mock *MockCryptoStream } // NewMockCryptoStream creates a new mock instance. func NewMockCryptoStream(ctrl *gomock.Controller) *MockCryptoStream { mock := &MockCryptoStream{ctrl: ctrl} mock.recorder = &MockCryptoStreamMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockCryptoStream) EXPECT() *MockCryptoStreamMockRecorder { return m.recorder } // Finish mocks base method. func (m *MockCryptoStream) Finish() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Finish") ret0, _ := ret[0].(error) return ret0 } // Finish indicates an expected call of Finish. func (mr *MockCryptoStreamMockRecorder) Finish() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Finish", reflect.TypeOf((*MockCryptoStream)(nil).Finish)) } // GetCryptoData mocks base method. func (m *MockCryptoStream) GetCryptoData() []byte { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetCryptoData") ret0, _ := ret[0].([]byte) return ret0 } // GetCryptoData indicates an expected call of GetCryptoData. func (mr *MockCryptoStreamMockRecorder) GetCryptoData() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCryptoData", reflect.TypeOf((*MockCryptoStream)(nil).GetCryptoData)) } // HandleCryptoFrame mocks base method. func (m *MockCryptoStream) HandleCryptoFrame(arg0 *wire.CryptoFrame) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandleCryptoFrame", arg0) ret0, _ := ret[0].(error) return ret0 } // HandleCryptoFrame indicates an expected call of HandleCryptoFrame. func (mr *MockCryptoStreamMockRecorder) HandleCryptoFrame(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleCryptoFrame", reflect.TypeOf((*MockCryptoStream)(nil).HandleCryptoFrame), arg0) } // HasData mocks base method. func (m *MockCryptoStream) 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 *MockCryptoStreamMockRecorder) HasData() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasData", reflect.TypeOf((*MockCryptoStream)(nil).HasData)) } // PopCryptoFrame mocks base method. func (m *MockCryptoStream) PopCryptoFrame(arg0 protocol.ByteCount) *wire.CryptoFrame { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PopCryptoFrame", arg0) ret0, _ := ret[0].(*wire.CryptoFrame) return ret0 } // PopCryptoFrame indicates an expected call of PopCryptoFrame. func (mr *MockCryptoStreamMockRecorder) PopCryptoFrame(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PopCryptoFrame", reflect.TypeOf((*MockCryptoStream)(nil).PopCryptoFrame), arg0) } // Write mocks base method. func (m *MockCryptoStream) Write(p []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", p) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *MockCryptoStreamMockRecorder) Write(p interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockCryptoStream)(nil).Write), p) } quic-go-0.25.0/mock_frame_source_test.go000066400000000000000000000054561417145451600202350ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: packet_packer.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" ackhandler "github.com/lucas-clemente/quic-go/internal/ackhandler" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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) ([]ackhandler.Frame, protocol.ByteCount) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendControlFrames", arg0, arg1) 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendControlFrames", reflect.TypeOf((*MockFrameSource)(nil).AppendControlFrames), arg0, arg1) } // AppendStreamFrames mocks base method. func (m *MockFrameSource) AppendStreamFrames(arg0 []ackhandler.Frame, arg1 protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AppendStreamFrames", arg0, arg1) ret0, _ := ret[0].([]ackhandler.Frame) ret1, _ := ret[1].(protocol.ByteCount) return ret0, ret1 } // AppendStreamFrames indicates an expected call of AppendStreamFrames. func (mr *MockFrameSourceMockRecorder) AppendStreamFrames(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppendStreamFrames", reflect.TypeOf((*MockFrameSource)(nil).AppendStreamFrames), arg0, arg1) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HasData", reflect.TypeOf((*MockFrameSource)(nil).HasData)) } quic-go-0.25.0/mock_mtu_discoverer_test.go000066400000000000000000000050641417145451600206100ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: mtu_discoverer.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" ackhandler "github.com/lucas-clemente/quic-go/internal/ackhandler" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPing", reflect.TypeOf((*MockMtuDiscoverer)(nil).GetPing)) } // NextProbeTime mocks base method. func (m *MockMtuDiscoverer) NextProbeTime() time.Time { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NextProbeTime") ret0, _ := ret[0].(time.Time) return ret0 } // NextProbeTime indicates an expected call of NextProbeTime. func (mr *MockMtuDiscovererMockRecorder) NextProbeTime() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextProbeTime", reflect.TypeOf((*MockMtuDiscoverer)(nil).NextProbeTime)) } // ShouldSendProbe mocks base method. func (m *MockMtuDiscoverer) ShouldSendProbe(now time.Time) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ShouldSendProbe", now) ret0, _ := ret[0].(bool) return ret0 } // ShouldSendProbe indicates an expected call of ShouldSendProbe. func (mr *MockMtuDiscovererMockRecorder) ShouldSendProbe(now interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ShouldSendProbe", reflect.TypeOf((*MockMtuDiscoverer)(nil).ShouldSendProbe), now) } quic-go-0.25.0/mock_multiplexer_test.go000066400000000000000000000041621417145451600201260ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: multiplexer.go // Package quic is a generated GoMock package. package quic import ( net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" logging "github.com/lucas-clemente/quic-go/logging" ) // MockMultiplexer is a mock of Multiplexer interface. type MockMultiplexer struct { ctrl *gomock.Controller recorder *MockMultiplexerMockRecorder } // MockMultiplexerMockRecorder is the mock recorder for MockMultiplexer. type MockMultiplexerMockRecorder struct { mock *MockMultiplexer } // NewMockMultiplexer creates a new mock instance. func NewMockMultiplexer(ctrl *gomock.Controller) *MockMultiplexer { mock := &MockMultiplexer{ctrl: ctrl} mock.recorder = &MockMultiplexerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockMultiplexer) EXPECT() *MockMultiplexerMockRecorder { return m.recorder } // AddConn mocks base method. func (m *MockMultiplexer) AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer) (packetHandlerManager, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "AddConn", c, connIDLen, statelessResetKey, tracer) ret0, _ := ret[0].(packetHandlerManager) ret1, _ := ret[1].(error) return ret0, ret1 } // AddConn indicates an expected call of AddConn. func (mr *MockMultiplexerMockRecorder) AddConn(c, connIDLen, statelessResetKey, tracer interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddConn", reflect.TypeOf((*MockMultiplexer)(nil).AddConn), c, connIDLen, statelessResetKey, tracer) } // RemoveConn mocks base method. func (m *MockMultiplexer) RemoveConn(arg0 indexableConn) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RemoveConn", arg0) ret0, _ := ret[0].(error) return ret0 } // RemoveConn indicates an expected call of RemoveConn. func (mr *MockMultiplexerMockRecorder) RemoveConn(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveConn", reflect.TypeOf((*MockMultiplexer)(nil).RemoveConn), arg0) } quic-go-0.25.0/mock_packer_test.go000066400000000000000000000151571417145451600170270ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: packet_packer.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" ackhandler "github.com/lucas-clemente/quic-go/internal/ackhandler" protocol "github.com/lucas-clemente/quic-go/internal/protocol" qerr "github.com/lucas-clemente/quic-go/internal/qerr" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 } // HandleTransportParameters mocks base method. func (m *MockPacker) HandleTransportParameters(arg0 *wire.TransportParameters) { m.ctrl.T.Helper() m.ctrl.Call(m, "HandleTransportParameters", arg0) } // HandleTransportParameters indicates an expected call of HandleTransportParameters. func (mr *MockPackerMockRecorder) HandleTransportParameters(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleTransportParameters", reflect.TypeOf((*MockPacker)(nil).HandleTransportParameters), arg0) } // MaybePackAckPacket mocks base method. func (m *MockPacker) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaybePackAckPacket", handshakeConfirmed) ret0, _ := ret[0].(*packedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // MaybePackAckPacket indicates an expected call of MaybePackAckPacket. func (mr *MockPackerMockRecorder) MaybePackAckPacket(handshakeConfirmed interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybePackAckPacket", reflect.TypeOf((*MockPacker)(nil).MaybePackAckPacket), handshakeConfirmed) } // MaybePackProbePacket mocks base method. func (m *MockPacker) MaybePackProbePacket(arg0 protocol.EncryptionLevel) (*packedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "MaybePackProbePacket", arg0) ret0, _ := ret[0].(*packedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // MaybePackProbePacket indicates an expected call of MaybePackProbePacket. func (mr *MockPackerMockRecorder) MaybePackProbePacket(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybePackProbePacket", reflect.TypeOf((*MockPacker)(nil).MaybePackProbePacket), arg0) } // PackApplicationClose mocks base method. func (m *MockPacker) PackApplicationClose(arg0 *qerr.ApplicationError) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackApplicationClose", arg0) ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackApplicationClose indicates an expected call of PackApplicationClose. func (mr *MockPackerMockRecorder) PackApplicationClose(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackApplicationClose", reflect.TypeOf((*MockPacker)(nil).PackApplicationClose), arg0) } // PackCoalescedPacket mocks base method. func (m *MockPacker) PackCoalescedPacket() (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackCoalescedPacket") ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackCoalescedPacket indicates an expected call of PackCoalescedPacket. func (mr *MockPackerMockRecorder) PackCoalescedPacket() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackCoalescedPacket", reflect.TypeOf((*MockPacker)(nil).PackCoalescedPacket)) } // PackConnectionClose mocks base method. func (m *MockPacker) PackConnectionClose(arg0 *qerr.TransportError) (*coalescedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackConnectionClose", arg0) ret0, _ := ret[0].(*coalescedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackConnectionClose indicates an expected call of PackConnectionClose. func (mr *MockPackerMockRecorder) PackConnectionClose(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackConnectionClose", reflect.TypeOf((*MockPacker)(nil).PackConnectionClose), arg0) } // PackMTUProbePacket mocks base method. func (m *MockPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount) (*packedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackMTUProbePacket", ping, size) ret0, _ := ret[0].(*packedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackMTUProbePacket indicates an expected call of PackMTUProbePacket. func (mr *MockPackerMockRecorder) PackMTUProbePacket(ping, size interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackMTUProbePacket", reflect.TypeOf((*MockPacker)(nil).PackMTUProbePacket), ping, size) } // PackPacket mocks base method. func (m *MockPacker) PackPacket() (*packedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "PackPacket") ret0, _ := ret[0].(*packedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // PackPacket indicates an expected call of PackPacket. func (mr *MockPackerMockRecorder) PackPacket() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PackPacket", reflect.TypeOf((*MockPacker)(nil).PackPacket)) } // SetMaxPacketSize mocks base method. func (m *MockPacker) SetMaxPacketSize(arg0 protocol.ByteCount) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetMaxPacketSize", arg0) } // SetMaxPacketSize indicates an expected call of SetMaxPacketSize. func (mr *MockPackerMockRecorder) SetMaxPacketSize(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetMaxPacketSize", reflect.TypeOf((*MockPacker)(nil).SetMaxPacketSize), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetToken", reflect.TypeOf((*MockPacker)(nil).SetToken), arg0) } quic-go-0.25.0/mock_packet_handler_manager_test.go000066400000000000000000000152161417145451600222140ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: server.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockPacketHandlerManager)(nil).Add), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).AddResetToken), arg0, arg1) } // AddWithConnID mocks base method. func (m *MockPacketHandlerManager) AddWithConnID(arg0, arg1 protocol.ConnectionID, arg2 func() 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddWithConnID", reflect.TypeOf((*MockPacketHandlerManager)(nil).AddWithConnID), arg0, arg1, arg2) } // CloseServer mocks base method. func (m *MockPacketHandlerManager) CloseServer() { m.ctrl.T.Helper() m.ctrl.Call(m, "CloseServer") } // CloseServer indicates an expected call of CloseServer. func (mr *MockPacketHandlerManagerMockRecorder) CloseServer() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseServer", reflect.TypeOf((*MockPacketHandlerManager)(nil).CloseServer)) } // Destroy mocks base method. func (m *MockPacketHandlerManager) Destroy() error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Destroy") ret0, _ := ret[0].(error) return ret0 } // Destroy indicates an expected call of Destroy. func (mr *MockPacketHandlerManagerMockRecorder) Destroy() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Destroy", reflect.TypeOf((*MockPacketHandlerManager)(nil).Destroy)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).GetStatelessResetToken), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockPacketHandlerManager)(nil).Remove), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResetToken", reflect.TypeOf((*MockPacketHandlerManager)(nil).RemoveResetToken), arg0) } // ReplaceWithClosed mocks base method. func (m *MockPacketHandlerManager) ReplaceWithClosed(arg0 protocol.ConnectionID, arg1 packetHandler) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReplaceWithClosed", arg0, arg1) } // ReplaceWithClosed indicates an expected call of ReplaceWithClosed. func (mr *MockPacketHandlerManagerMockRecorder) ReplaceWithClosed(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceWithClosed", reflect.TypeOf((*MockPacketHandlerManager)(nil).ReplaceWithClosed), arg0, arg1) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Retire", reflect.TypeOf((*MockPacketHandlerManager)(nil).Retire), arg0) } // SetServer mocks base method. func (m *MockPacketHandlerManager) SetServer(arg0 unknownPacketHandler) { m.ctrl.T.Helper() m.ctrl.Call(m, "SetServer", arg0) } // SetServer indicates an expected call of SetServer. func (mr *MockPacketHandlerManagerMockRecorder) SetServer(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetServer", reflect.TypeOf((*MockPacketHandlerManager)(nil).SetServer), arg0) } quic-go-0.25.0/mock_packet_handler_test.go000066400000000000000000000053231417145451600205200ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: server.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // 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 } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "destroy", reflect.TypeOf((*MockPacketHandler)(nil).destroy), arg0) } // getPerspective mocks base method. func (m *MockPacketHandler) getPerspective() protocol.Perspective { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getPerspective") ret0, _ := ret[0].(protocol.Perspective) return ret0 } // getPerspective indicates an expected call of getPerspective. func (mr *MockPacketHandlerMockRecorder) getPerspective() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getPerspective", reflect.TypeOf((*MockPacketHandler)(nil).getPerspective)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handlePacket", reflect.TypeOf((*MockPacketHandler)(nil).handlePacket), arg0) } // shutdown mocks base method. func (m *MockPacketHandler) shutdown() { m.ctrl.T.Helper() m.ctrl.Call(m, "shutdown") } // shutdown indicates an expected call of shutdown. func (mr *MockPacketHandlerMockRecorder) shutdown() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "shutdown", reflect.TypeOf((*MockPacketHandler)(nil).shutdown)) } quic-go-0.25.0/mock_packetconn_test.go000066400000000000000000000105051417145451600176770ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: net (interfaces: PacketConn) // Package quic is a generated GoMock package. package quic import ( net "net" reflect "reflect" time "time" gomock "github.com/golang/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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockPacketConn)(nil).Close)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockPacketConn)(nil).LocalAddr)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReadFrom", reflect.TypeOf((*MockPacketConn)(nil).ReadFrom), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockPacketConn)(nil).SetDeadline), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockPacketConn)(nil).SetReadDeadline), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockPacketConn)(nil).SetWriteDeadline), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteTo", reflect.TypeOf((*MockPacketConn)(nil).WriteTo), arg0, arg1) } quic-go-0.25.0/mock_quic_session_test.go000066400000000000000000000272701417145451600202650ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: server.go // Package quic is a generated GoMock package. package quic import ( context "context" net "net" reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // MockQuicSession is a mock of QuicSession interface. type MockQuicSession struct { ctrl *gomock.Controller recorder *MockQuicSessionMockRecorder } // MockQuicSessionMockRecorder is the mock recorder for MockQuicSession. type MockQuicSessionMockRecorder struct { mock *MockQuicSession } // NewMockQuicSession creates a new mock instance. func NewMockQuicSession(ctrl *gomock.Controller) *MockQuicSession { mock := &MockQuicSession{ctrl: ctrl} mock.recorder = &MockQuicSessionMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockQuicSession) EXPECT() *MockQuicSessionMockRecorder { return m.recorder } // AcceptStream mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) AcceptStream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptStream", reflect.TypeOf((*MockQuicSession)(nil).AcceptStream), arg0) } // AcceptUniStream mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) AcceptUniStream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUniStream", reflect.TypeOf((*MockQuicSession)(nil).AcceptUniStream), arg0) } // CloseWithError mocks base method. func (m *MockQuicSession) CloseWithError(arg0 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 *MockQuicSessionMockRecorder) CloseWithError(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseWithError", reflect.TypeOf((*MockQuicSession)(nil).CloseWithError), arg0, arg1) } // ConnectionState mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) ConnectionState() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectionState", reflect.TypeOf((*MockQuicSession)(nil).ConnectionState)) } // Context mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) Context() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockQuicSession)(nil).Context)) } // GetVersion mocks base method. func (m *MockQuicSession) GetVersion() protocol.VersionNumber { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetVersion") ret0, _ := ret[0].(protocol.VersionNumber) return ret0 } // GetVersion indicates an expected call of GetVersion. func (mr *MockQuicSessionMockRecorder) GetVersion() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetVersion", reflect.TypeOf((*MockQuicSession)(nil).GetVersion)) } // HandshakeComplete mocks base method. func (m *MockQuicSession) HandshakeComplete() context.Context { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "HandshakeComplete") ret0, _ := ret[0].(context.Context) return ret0 } // HandshakeComplete indicates an expected call of HandshakeComplete. func (mr *MockQuicSessionMockRecorder) HandshakeComplete() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandshakeComplete", reflect.TypeOf((*MockQuicSession)(nil).HandshakeComplete)) } // LocalAddr mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) LocalAddr() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockQuicSession)(nil).LocalAddr)) } // NextSession mocks base method. func (m *MockQuicSession) NextSession() Session { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NextSession") ret0, _ := ret[0].(Session) return ret0 } // NextSession indicates an expected call of NextSession. func (mr *MockQuicSessionMockRecorder) NextSession() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextSession", reflect.TypeOf((*MockQuicSession)(nil).NextSession)) } // OpenStream mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) OpenStream() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockQuicSession)(nil).OpenStream)) } // OpenStreamSync mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) OpenStreamSync(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStreamSync", reflect.TypeOf((*MockQuicSession)(nil).OpenStreamSync), arg0) } // OpenUniStream mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) OpenUniStream() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStream", reflect.TypeOf((*MockQuicSession)(nil).OpenUniStream)) } // OpenUniStreamSync mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) OpenUniStreamSync(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStreamSync", reflect.TypeOf((*MockQuicSession)(nil).OpenUniStreamSync), arg0) } // ReceiveMessage mocks base method. func (m *MockQuicSession) ReceiveMessage() ([]byte, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "ReceiveMessage") ret0, _ := ret[0].([]byte) ret1, _ := ret[1].(error) return ret0, ret1 } // ReceiveMessage indicates an expected call of ReceiveMessage. func (mr *MockQuicSessionMockRecorder) ReceiveMessage() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReceiveMessage", reflect.TypeOf((*MockQuicSession)(nil).ReceiveMessage)) } // RemoteAddr mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) RemoteAddr() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockQuicSession)(nil).RemoteAddr)) } // SendMessage mocks base method. func (m *MockQuicSession) SendMessage(arg0 []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SendMessage", arg0) ret0, _ := ret[0].(error) return ret0 } // SendMessage indicates an expected call of SendMessage. func (mr *MockQuicSessionMockRecorder) SendMessage(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMessage", reflect.TypeOf((*MockQuicSession)(nil).SendMessage), arg0) } // destroy mocks base method. func (m *MockQuicSession) destroy(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "destroy", arg0) } // destroy indicates an expected call of destroy. func (mr *MockQuicSessionMockRecorder) destroy(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "destroy", reflect.TypeOf((*MockQuicSession)(nil).destroy), arg0) } // earlySessionReady mocks base method. func (m *MockQuicSession) earlySessionReady() <-chan struct{} { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "earlySessionReady") ret0, _ := ret[0].(<-chan struct{}) return ret0 } // earlySessionReady indicates an expected call of earlySessionReady. func (mr *MockQuicSessionMockRecorder) earlySessionReady() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "earlySessionReady", reflect.TypeOf((*MockQuicSession)(nil).earlySessionReady)) } // getPerspective mocks base method. func (m *MockQuicSession) getPerspective() protocol.Perspective { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "getPerspective") ret0, _ := ret[0].(protocol.Perspective) return ret0 } // getPerspective indicates an expected call of getPerspective. func (mr *MockQuicSessionMockRecorder) getPerspective() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getPerspective", reflect.TypeOf((*MockQuicSession)(nil).getPerspective)) } // handlePacket mocks base method. func (m *MockQuicSession) handlePacket(arg0 *receivedPacket) { m.ctrl.T.Helper() m.ctrl.Call(m, "handlePacket", arg0) } // handlePacket indicates an expected call of handlePacket. func (mr *MockQuicSessionMockRecorder) handlePacket(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handlePacket", reflect.TypeOf((*MockQuicSession)(nil).handlePacket), arg0) } // run mocks base method. func (m *MockQuicSession) 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 *MockQuicSessionMockRecorder) run() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "run", reflect.TypeOf((*MockQuicSession)(nil).run)) } // shutdown mocks base method. func (m *MockQuicSession) shutdown() { m.ctrl.T.Helper() m.ctrl.Call(m, "shutdown") } // shutdown indicates an expected call of shutdown. func (mr *MockQuicSessionMockRecorder) shutdown() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "shutdown", reflect.TypeOf((*MockQuicSession)(nil).shutdown)) } quic-go-0.25.0/mock_receive_stream_internal_test.go000066400000000000000000000120721417145451600224440ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: receive_stream.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelRead", arg0) } // CancelRead indicates an expected call of CancelRead. func (mr *MockReceiveStreamIMockRecorder) CancelRead(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRead", reflect.TypeOf((*MockReceiveStreamI)(nil).CancelRead), arg0) } // Read mocks base method. func (m *MockReceiveStreamI) Read(p []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", p) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Read indicates an expected call of Read. func (mr *MockReceiveStreamIMockRecorder) Read(p interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockReceiveStreamI)(nil).Read), p) } // SetReadDeadline mocks base method. func (m *MockReceiveStreamI) SetReadDeadline(t time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", t) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockReceiveStreamIMockRecorder) SetReadDeadline(t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockReceiveStreamI)(nil).SetReadDeadline), t) } // StreamID mocks base method. func (m *MockReceiveStreamI) StreamID() StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockReceiveStreamIMockRecorder) StreamID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockReceiveStreamI)(nil).StreamID)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeForShutdown", reflect.TypeOf((*MockReceiveStreamI)(nil).closeForShutdown), arg0) } // getWindowUpdate mocks base method. func (m *MockReceiveStreamI) 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 *MockReceiveStreamIMockRecorder) getWindowUpdate() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getWindowUpdate", reflect.TypeOf((*MockReceiveStreamI)(nil).getWindowUpdate)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleResetStreamFrame", reflect.TypeOf((*MockReceiveStreamI)(nil).handleResetStreamFrame), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStreamFrame", reflect.TypeOf((*MockReceiveStreamI)(nil).handleStreamFrame), arg0) } quic-go-0.25.0/mock_sealing_manager_test.go000066400000000000000000000064141417145451600206720ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: packet_packer.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" handshake "github.com/lucas-clemente/quic-go/internal/handshake" ) // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get0RTTSealer", reflect.TypeOf((*MockSealingManager)(nil).Get0RTTSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get1RTTSealer", reflect.TypeOf((*MockSealingManager)(nil).Get1RTTSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHandshakeSealer", reflect.TypeOf((*MockSealingManager)(nil).GetHandshakeSealer)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInitialSealer", reflect.TypeOf((*MockSealingManager)(nil).GetInitialSealer)) } quic-go-0.25.0/mock_send_conn_test.go000066400000000000000000000050301417145451600175150ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: send_conn.go // Package quic is a generated GoMock package. package quic import ( net "net" reflect "reflect" gomock "github.com/golang/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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSendConn)(nil).Close)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockSendConn)(nil).LocalAddr)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockSendConn)(nil).RemoteAddr)) } // Write mocks base method. func (m *MockSendConn) Write(arg0 []byte) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", arg0) ret0, _ := ret[0].(error) return ret0 } // Write indicates an expected call of Write. func (mr *MockSendConnMockRecorder) Write(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockSendConn)(nil).Write), arg0) } quic-go-0.25.0/mock_send_stream_internal_test.go000066400000000000000000000144441417145451600217600ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: send_stream.go // Package quic is a generated GoMock package. package quic import ( context "context" reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" ackhandler "github.com/lucas-clemente/quic-go/internal/ackhandler" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelWrite", arg0) } // CancelWrite indicates an expected call of CancelWrite. func (mr *MockSendStreamIMockRecorder) CancelWrite(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelWrite", reflect.TypeOf((*MockSendStreamI)(nil).CancelWrite), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSendStreamI)(nil).Close)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockSendStreamI)(nil).Context)) } // SetWriteDeadline mocks base method. func (m *MockSendStreamI) SetWriteDeadline(t time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetWriteDeadline", t) ret0, _ := ret[0].(error) return ret0 } // SetWriteDeadline indicates an expected call of SetWriteDeadline. func (mr *MockSendStreamIMockRecorder) SetWriteDeadline(t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockSendStreamI)(nil).SetWriteDeadline), t) } // StreamID mocks base method. func (m *MockSendStreamI) StreamID() StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockSendStreamIMockRecorder) StreamID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockSendStreamI)(nil).StreamID)) } // Write mocks base method. func (m *MockSendStreamI) Write(p []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", p) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *MockSendStreamIMockRecorder) Write(p interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockSendStreamI)(nil).Write), p) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeForShutdown", reflect.TypeOf((*MockSendStreamI)(nil).closeForShutdown), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStopSendingFrame", reflect.TypeOf((*MockSendStreamI)(nil).handleStopSendingFrame), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "hasData", reflect.TypeOf((*MockSendStreamI)(nil).hasData)) } // popStreamFrame mocks base method. func (m *MockSendStreamI) popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "popStreamFrame", maxBytes) ret0, _ := ret[0].(*ackhandler.Frame) ret1, _ := ret[1].(bool) return ret0, ret1 } // popStreamFrame indicates an expected call of popStreamFrame. func (mr *MockSendStreamIMockRecorder) popStreamFrame(maxBytes interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "popStreamFrame", reflect.TypeOf((*MockSendStreamI)(nil).popStreamFrame), maxBytes) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "updateSendWindow", reflect.TypeOf((*MockSendStreamI)(nil).updateSendWindow), arg0) } quic-go-0.25.0/mock_sender_test.go000066400000000000000000000053431417145451600170360ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: send_queue.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Available", reflect.TypeOf((*MockSender)(nil).Available)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockSender)(nil).Close)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockSender)(nil).Run)) } // Send mocks base method. func (m *MockSender) Send(p *packetBuffer) { m.ctrl.T.Helper() m.ctrl.Call(m, "Send", p) } // Send indicates an expected call of Send. func (mr *MockSenderMockRecorder) Send(p interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockSender)(nil).Send), p) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WouldBlock", reflect.TypeOf((*MockSender)(nil).WouldBlock)) } quic-go-0.25.0/mock_session_runner_test.go000066400000000000000000000107271417145451600206340ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: session.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // MockSessionRunner is a mock of SessionRunner interface. type MockSessionRunner struct { ctrl *gomock.Controller recorder *MockSessionRunnerMockRecorder } // MockSessionRunnerMockRecorder is the mock recorder for MockSessionRunner. type MockSessionRunnerMockRecorder struct { mock *MockSessionRunner } // NewMockSessionRunner creates a new mock instance. func NewMockSessionRunner(ctrl *gomock.Controller) *MockSessionRunner { mock := &MockSessionRunner{ctrl: ctrl} mock.recorder = &MockSessionRunnerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockSessionRunner) EXPECT() *MockSessionRunnerMockRecorder { return m.recorder } // Add mocks base method. func (m *MockSessionRunner) 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 *MockSessionRunnerMockRecorder) Add(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockSessionRunner)(nil).Add), arg0, arg1) } // AddResetToken mocks base method. func (m *MockSessionRunner) 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 *MockSessionRunnerMockRecorder) AddResetToken(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddResetToken", reflect.TypeOf((*MockSessionRunner)(nil).AddResetToken), arg0, arg1) } // GetStatelessResetToken mocks base method. func (m *MockSessionRunner) 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 *MockSessionRunnerMockRecorder) GetStatelessResetToken(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStatelessResetToken", reflect.TypeOf((*MockSessionRunner)(nil).GetStatelessResetToken), arg0) } // Remove mocks base method. func (m *MockSessionRunner) Remove(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "Remove", arg0) } // Remove indicates an expected call of Remove. func (mr *MockSessionRunnerMockRecorder) Remove(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockSessionRunner)(nil).Remove), arg0) } // RemoveResetToken mocks base method. func (m *MockSessionRunner) RemoveResetToken(arg0 protocol.StatelessResetToken) { m.ctrl.T.Helper() m.ctrl.Call(m, "RemoveResetToken", arg0) } // RemoveResetToken indicates an expected call of RemoveResetToken. func (mr *MockSessionRunnerMockRecorder) RemoveResetToken(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveResetToken", reflect.TypeOf((*MockSessionRunner)(nil).RemoveResetToken), arg0) } // ReplaceWithClosed mocks base method. func (m *MockSessionRunner) ReplaceWithClosed(arg0 protocol.ConnectionID, arg1 packetHandler) { m.ctrl.T.Helper() m.ctrl.Call(m, "ReplaceWithClosed", arg0, arg1) } // ReplaceWithClosed indicates an expected call of ReplaceWithClosed. func (mr *MockSessionRunnerMockRecorder) ReplaceWithClosed(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReplaceWithClosed", reflect.TypeOf((*MockSessionRunner)(nil).ReplaceWithClosed), arg0, arg1) } // Retire mocks base method. func (m *MockSessionRunner) Retire(arg0 protocol.ConnectionID) { m.ctrl.T.Helper() m.ctrl.Call(m, "Retire", arg0) } // Retire indicates an expected call of Retire. func (mr *MockSessionRunnerMockRecorder) Retire(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Retire", reflect.TypeOf((*MockSessionRunner)(nil).Retire), arg0) } quic-go-0.25.0/mock_stream_getter_test.go000066400000000000000000000043161417145451600204220ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: session.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) // MockStreamGetter is a mock of StreamGetter interface. type MockStreamGetter struct { ctrl *gomock.Controller recorder *MockStreamGetterMockRecorder } // MockStreamGetterMockRecorder is the mock recorder for MockStreamGetter. type MockStreamGetterMockRecorder struct { mock *MockStreamGetter } // NewMockStreamGetter creates a new mock instance. func NewMockStreamGetter(ctrl *gomock.Controller) *MockStreamGetter { mock := &MockStreamGetter{ctrl: ctrl} mock.recorder = &MockStreamGetterMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockStreamGetter) EXPECT() *MockStreamGetterMockRecorder { return m.recorder } // GetOrOpenReceiveStream mocks base method. func (m *MockStreamGetter) 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 *MockStreamGetterMockRecorder) GetOrOpenReceiveStream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrOpenReceiveStream", reflect.TypeOf((*MockStreamGetter)(nil).GetOrOpenReceiveStream), arg0) } // GetOrOpenSendStream mocks base method. func (m *MockStreamGetter) 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 *MockStreamGetterMockRecorder) GetOrOpenSendStream(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrOpenSendStream", reflect.TypeOf((*MockStreamGetter)(nil).GetOrOpenSendStream), arg0) } quic-go-0.25.0/mock_stream_internal_test.go000066400000000000000000000230331417145451600207410ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: stream.go // Package quic is a generated GoMock package. package quic import ( context "context" reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" ackhandler "github.com/lucas-clemente/quic-go/internal/ackhandler" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelRead", arg0) } // CancelRead indicates an expected call of CancelRead. func (mr *MockStreamIMockRecorder) CancelRead(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelRead", reflect.TypeOf((*MockStreamI)(nil).CancelRead), arg0) } // CancelWrite mocks base method. func (m *MockStreamI) CancelWrite(arg0 StreamErrorCode) { m.ctrl.T.Helper() m.ctrl.Call(m, "CancelWrite", arg0) } // CancelWrite indicates an expected call of CancelWrite. func (mr *MockStreamIMockRecorder) CancelWrite(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CancelWrite", reflect.TypeOf((*MockStreamI)(nil).CancelWrite), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockStreamI)(nil).Close)) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Context", reflect.TypeOf((*MockStreamI)(nil).Context)) } // Read mocks base method. func (m *MockStreamI) Read(p []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Read", p) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Read indicates an expected call of Read. func (mr *MockStreamIMockRecorder) Read(p interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockStreamI)(nil).Read), p) } // SetDeadline mocks base method. func (m *MockStreamI) SetDeadline(t time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetDeadline", t) ret0, _ := ret[0].(error) return ret0 } // SetDeadline indicates an expected call of SetDeadline. func (mr *MockStreamIMockRecorder) SetDeadline(t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockStreamI)(nil).SetDeadline), t) } // SetReadDeadline mocks base method. func (m *MockStreamI) SetReadDeadline(t time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetReadDeadline", t) ret0, _ := ret[0].(error) return ret0 } // SetReadDeadline indicates an expected call of SetReadDeadline. func (mr *MockStreamIMockRecorder) SetReadDeadline(t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockStreamI)(nil).SetReadDeadline), t) } // SetWriteDeadline mocks base method. func (m *MockStreamI) SetWriteDeadline(t time.Time) error { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "SetWriteDeadline", t) ret0, _ := ret[0].(error) return ret0 } // SetWriteDeadline indicates an expected call of SetWriteDeadline. func (mr *MockStreamIMockRecorder) SetWriteDeadline(t interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockStreamI)(nil).SetWriteDeadline), t) } // StreamID mocks base method. func (m *MockStreamI) StreamID() StreamID { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StreamID") ret0, _ := ret[0].(StreamID) return ret0 } // StreamID indicates an expected call of StreamID. func (mr *MockStreamIMockRecorder) StreamID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamID", reflect.TypeOf((*MockStreamI)(nil).StreamID)) } // Write mocks base method. func (m *MockStreamI) Write(p []byte) (int, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Write", p) ret0, _ := ret[0].(int) ret1, _ := ret[1].(error) return ret0, ret1 } // Write indicates an expected call of Write. func (mr *MockStreamIMockRecorder) Write(p interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockStreamI)(nil).Write), p) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "closeForShutdown", reflect.TypeOf((*MockStreamI)(nil).closeForShutdown), arg0) } // getWindowUpdate mocks base method. func (m *MockStreamI) 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 *MockStreamIMockRecorder) getWindowUpdate() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "getWindowUpdate", reflect.TypeOf((*MockStreamI)(nil).getWindowUpdate)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleResetStreamFrame", reflect.TypeOf((*MockStreamI)(nil).handleResetStreamFrame), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStopSendingFrame", reflect.TypeOf((*MockStreamI)(nil).handleStopSendingFrame), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handleStreamFrame", reflect.TypeOf((*MockStreamI)(nil).handleStreamFrame), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "hasData", reflect.TypeOf((*MockStreamI)(nil).hasData)) } // popStreamFrame mocks base method. func (m *MockStreamI) popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "popStreamFrame", maxBytes) ret0, _ := ret[0].(*ackhandler.Frame) ret1, _ := ret[1].(bool) return ret0, ret1 } // popStreamFrame indicates an expected call of popStreamFrame. func (mr *MockStreamIMockRecorder) popStreamFrame(maxBytes interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "popStreamFrame", reflect.TypeOf((*MockStreamI)(nil).popStreamFrame), maxBytes) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "updateSendWindow", reflect.TypeOf((*MockStreamI)(nil).updateSendWindow), arg0) } quic-go-0.25.0/mock_stream_manager_test.go000066400000000000000000000206371417145451600205460ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: session.go // Package quic is a generated GoMock package. package quic import ( context "context" reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptStream", reflect.TypeOf((*MockStreamManager)(nil).AcceptStream), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AcceptUniStream", reflect.TypeOf((*MockStreamManager)(nil).AcceptUniStream), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CloseWithError", reflect.TypeOf((*MockStreamManager)(nil).CloseWithError), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStream", reflect.TypeOf((*MockStreamManager)(nil).DeleteStream), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrOpenReceiveStream", reflect.TypeOf((*MockStreamManager)(nil).GetOrOpenReceiveStream), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOrOpenSendStream", reflect.TypeOf((*MockStreamManager)(nil).GetOrOpenSendStream), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMaxStreamsFrame", reflect.TypeOf((*MockStreamManager)(nil).HandleMaxStreamsFrame), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockStreamManager)(nil).OpenStream)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStreamSync", reflect.TypeOf((*MockStreamManager)(nil).OpenStreamSync), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStream", reflect.TypeOf((*MockStreamManager)(nil).OpenUniStream)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenUniStreamSync", reflect.TypeOf((*MockStreamManager)(nil).OpenUniStreamSync), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResetFor0RTT", reflect.TypeOf((*MockStreamManager)(nil).ResetFor0RTT)) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLimits", reflect.TypeOf((*MockStreamManager)(nil).UpdateLimits), arg0) } // 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() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UseResetMaps", reflect.TypeOf((*MockStreamManager)(nil).UseResetMaps)) } quic-go-0.25.0/mock_stream_sender_test.go000066400000000000000000000047251417145451600204140ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: stream.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" protocol "github.com/lucas-clemente/quic-go/internal/protocol" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 } // onHasStreamData mocks base method. func (m *MockStreamSender) onHasStreamData(arg0 protocol.StreamID) { m.ctrl.T.Helper() m.ctrl.Call(m, "onHasStreamData", arg0) } // onHasStreamData indicates an expected call of onHasStreamData. func (mr *MockStreamSenderMockRecorder) onHasStreamData(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "onHasStreamData", reflect.TypeOf((*MockStreamSender)(nil).onHasStreamData), arg0) } // 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 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "onStreamCompleted", reflect.TypeOf((*MockStreamSender)(nil).onStreamCompleted), arg0) } // queueControlFrame mocks base method. func (m *MockStreamSender) queueControlFrame(arg0 wire.Frame) { m.ctrl.T.Helper() m.ctrl.Call(m, "queueControlFrame", arg0) } // queueControlFrame indicates an expected call of queueControlFrame. func (mr *MockStreamSenderMockRecorder) queueControlFrame(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "queueControlFrame", reflect.TypeOf((*MockStreamSender)(nil).queueControlFrame), arg0) } quic-go-0.25.0/mock_token_store_test.go000066400000000000000000000033621417145451600201110ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: github.com/lucas-clemente/quic-go (interfaces: TokenStore) // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockTokenStore is a mock of TokenStore interface. type MockTokenStore struct { ctrl *gomock.Controller recorder *MockTokenStoreMockRecorder } // MockTokenStoreMockRecorder is the mock recorder for MockTokenStore. type MockTokenStoreMockRecorder struct { mock *MockTokenStore } // NewMockTokenStore creates a new mock instance. func NewMockTokenStore(ctrl *gomock.Controller) *MockTokenStore { mock := &MockTokenStore{ctrl: ctrl} mock.recorder = &MockTokenStoreMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockTokenStore) EXPECT() *MockTokenStoreMockRecorder { return m.recorder } // Pop mocks base method. func (m *MockTokenStore) Pop(arg0 string) *ClientToken { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Pop", arg0) ret0, _ := ret[0].(*ClientToken) return ret0 } // Pop indicates an expected call of Pop. func (mr *MockTokenStoreMockRecorder) Pop(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pop", reflect.TypeOf((*MockTokenStore)(nil).Pop), arg0) } // Put mocks base method. func (m *MockTokenStore) Put(arg0 string, arg1 *ClientToken) { m.ctrl.T.Helper() m.ctrl.Call(m, "Put", arg0, arg1) } // Put indicates an expected call of Put. func (mr *MockTokenStoreMockRecorder) Put(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Put", reflect.TypeOf((*MockTokenStore)(nil).Put), arg0, arg1) } quic-go-0.25.0/mock_unknown_packet_handler_test.go000066400000000000000000000037131417145451600223000ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: server.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" gomock "github.com/golang/mock/gomock" ) // MockUnknownPacketHandler is a mock of UnknownPacketHandler interface. type MockUnknownPacketHandler struct { ctrl *gomock.Controller recorder *MockUnknownPacketHandlerMockRecorder } // MockUnknownPacketHandlerMockRecorder is the mock recorder for MockUnknownPacketHandler. type MockUnknownPacketHandlerMockRecorder struct { mock *MockUnknownPacketHandler } // NewMockUnknownPacketHandler creates a new mock instance. func NewMockUnknownPacketHandler(ctrl *gomock.Controller) *MockUnknownPacketHandler { mock := &MockUnknownPacketHandler{ctrl: ctrl} mock.recorder = &MockUnknownPacketHandlerMockRecorder{mock} return mock } // EXPECT returns an object that allows the caller to indicate expected use. func (m *MockUnknownPacketHandler) EXPECT() *MockUnknownPacketHandlerMockRecorder { return m.recorder } // handlePacket mocks base method. func (m *MockUnknownPacketHandler) handlePacket(arg0 *receivedPacket) { m.ctrl.T.Helper() m.ctrl.Call(m, "handlePacket", arg0) } // handlePacket indicates an expected call of handlePacket. func (mr *MockUnknownPacketHandlerMockRecorder) handlePacket(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "handlePacket", reflect.TypeOf((*MockUnknownPacketHandler)(nil).handlePacket), arg0) } // setCloseError mocks base method. func (m *MockUnknownPacketHandler) setCloseError(arg0 error) { m.ctrl.T.Helper() m.ctrl.Call(m, "setCloseError", arg0) } // setCloseError indicates an expected call of setCloseError. func (mr *MockUnknownPacketHandlerMockRecorder) setCloseError(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "setCloseError", reflect.TypeOf((*MockUnknownPacketHandler)(nil).setCloseError), arg0) } quic-go-0.25.0/mock_unpacker_test.go000066400000000000000000000027331417145451600173660ustar00rootroot00000000000000// Code generated by MockGen. DO NOT EDIT. // Source: session.go // Package quic is a generated GoMock package. package quic import ( reflect "reflect" time "time" gomock "github.com/golang/mock/gomock" wire "github.com/lucas-clemente/quic-go/internal/wire" ) // 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 } // Unpack mocks base method. func (m *MockUnpacker) Unpack(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "Unpack", hdr, rcvTime, data) ret0, _ := ret[0].(*unpackedPacket) ret1, _ := ret[1].(error) return ret0, ret1 } // Unpack indicates an expected call of Unpack. func (mr *MockUnpackerMockRecorder) Unpack(hdr, rcvTime, data interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unpack", reflect.TypeOf((*MockUnpacker)(nil).Unpack), hdr, rcvTime, data) } quic-go-0.25.0/mockgen.go000066400000000000000000000061301417145451600151240ustar00rootroot00000000000000package quic //go:generate sh -c "./mockgen_private.sh quic mock_send_conn_test.go github.com/lucas-clemente/quic-go sendConn" //go:generate sh -c "./mockgen_private.sh quic mock_sender_test.go github.com/lucas-clemente/quic-go sender" //go:generate sh -c "./mockgen_private.sh quic mock_stream_internal_test.go github.com/lucas-clemente/quic-go streamI" //go:generate sh -c "./mockgen_private.sh quic mock_crypto_stream_test.go github.com/lucas-clemente/quic-go cryptoStream" //go:generate sh -c "./mockgen_private.sh quic mock_receive_stream_internal_test.go github.com/lucas-clemente/quic-go receiveStreamI" //go:generate sh -c "./mockgen_private.sh quic mock_send_stream_internal_test.go github.com/lucas-clemente/quic-go sendStreamI" //go:generate sh -c "./mockgen_private.sh quic mock_stream_sender_test.go github.com/lucas-clemente/quic-go streamSender" //go:generate sh -c "./mockgen_private.sh quic mock_stream_getter_test.go github.com/lucas-clemente/quic-go streamGetter" //go:generate sh -c "./mockgen_private.sh quic mock_crypto_data_handler_test.go github.com/lucas-clemente/quic-go cryptoDataHandler" //go:generate sh -c "./mockgen_private.sh quic mock_frame_source_test.go github.com/lucas-clemente/quic-go frameSource" //go:generate sh -c "./mockgen_private.sh quic mock_ack_frame_source_test.go github.com/lucas-clemente/quic-go ackFrameSource" //go:generate sh -c "./mockgen_private.sh quic mock_stream_manager_test.go github.com/lucas-clemente/quic-go streamManager" //go:generate sh -c "./mockgen_private.sh quic mock_sealing_manager_test.go github.com/lucas-clemente/quic-go sealingManager" //go:generate sh -c "./mockgen_private.sh quic mock_unpacker_test.go github.com/lucas-clemente/quic-go unpacker" //go:generate sh -c "./mockgen_private.sh quic mock_packer_test.go github.com/lucas-clemente/quic-go packer" //go:generate sh -c "./mockgen_private.sh quic mock_mtu_discoverer_test.go github.com/lucas-clemente/quic-go mtuDiscoverer" //go:generate sh -c "./mockgen_private.sh quic mock_session_runner_test.go github.com/lucas-clemente/quic-go sessionRunner" //go:generate sh -c "./mockgen_private.sh quic mock_quic_session_test.go github.com/lucas-clemente/quic-go quicSession" //go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_test.go github.com/lucas-clemente/quic-go packetHandler" //go:generate sh -c "./mockgen_private.sh quic mock_unknown_packet_handler_test.go github.com/lucas-clemente/quic-go unknownPacketHandler" //go:generate sh -c "./mockgen_private.sh quic mock_packet_handler_manager_test.go github.com/lucas-clemente/quic-go packetHandlerManager" //go:generate sh -c "./mockgen_private.sh quic mock_multiplexer_test.go github.com/lucas-clemente/quic-go multiplexer" //go:generate sh -c "./mockgen_private.sh quic mock_batch_conn_test.go github.com/lucas-clemente/quic-go batchConn" //go:generate sh -c "mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_token_store_test.go github.com/lucas-clemente/quic-go TokenStore" //go:generate sh -c "mockgen -package quic -self_package github.com/lucas-clemente/quic-go -destination mock_packetconn_test.go net PacketConn" quic-go-0.25.0/mockgen_private.sh000077500000000000000000000025531417145451600166730ustar00rootroot00000000000000#!/bin/bash DEST=$2 PACKAGE=$3 TMPFILE="mockgen_tmp.go" # uppercase the name of the interface ORIG_INTERFACE_NAME=$4 INTERFACE_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${ORIG_INTERFACE_NAME:0:1})${ORIG_INTERFACE_NAME:1}" # Gather all files that contain interface definitions. # These interfaces might be used as embedded interfaces, # so we need to pass them to mockgen as aux_files. AUX=() for f in *.go; do if [[ -z ${f##*_test.go} ]]; then # skip test files continue; fi if $(egrep -qe "type (.*) interface" $f); then AUX+=("github.com/lucas-clemente/quic-go=$f") fi done # Find the file that defines the interface we're mocking. for f in *.go; do if [[ -z ${f##*_test.go} ]]; then # skip test files continue; fi INTERFACE=$(sed -n "/^type $ORIG_INTERFACE_NAME interface/,/^}/p" $f) if [[ -n "$INTERFACE" ]]; then SRC=$f break fi done if [[ -z "$INTERFACE" ]]; then echo "Interface $ORIG_INTERFACE_NAME not found." exit 1 fi AUX_FILES=$(IFS=, ; echo "${AUX[*]}") ## create a public alias for the interface, so that mockgen can process it echo -e "package $1\n" > $TMPFILE echo "$INTERFACE" | sed "s/$ORIG_INTERFACE_NAME/$INTERFACE_NAME/" >> $TMPFILE mockgen -package $1 -self_package $3 -destination $DEST -source=$TMPFILE -aux_files $AUX_FILES sed "s/$TMPFILE/$SRC/" "$DEST" > "$DEST.new" && mv "$DEST.new" "$DEST" rm "$TMPFILE" quic-go-0.25.0/mtu_discoverer.go000066400000000000000000000043421417145451600165360ustar00rootroot00000000000000package quic import ( "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type mtuDiscoverer interface { ShouldSendProbe(now time.Time) bool NextProbeTime() time.Time 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 ) type mtuFinder struct { lastProbeTime time.Time probeInFlight bool mtuIncreased func(protocol.ByteCount) rttStats *utils.RTTStats current protocol.ByteCount max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer) } var _ mtuDiscoverer = &mtuFinder{} func newMTUDiscoverer(rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) mtuDiscoverer { return &mtuFinder{ current: start, rttStats: rttStats, lastProbeTime: time.Now(), // to make sure the first probe packet is not sent immediately mtuIncreased: mtuIncreased, max: max, } } func (f *mtuFinder) done() bool { return f.max-f.current <= maxMTUDiff+1 } func (f *mtuFinder) ShouldSendProbe(now time.Time) bool { if f.probeInFlight || f.done() { return false } return !now.Before(f.NextProbeTime()) } // NextProbeTime returns the time when the next probe packet should be sent. // It returns the zero value if no probe packet should be sent. func (f *mtuFinder) NextProbeTime() time.Time { if f.probeInFlight || f.done() { return time.Time{} } return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT()) } func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) { size := (f.max + f.current) / 2 f.lastProbeTime = time.Now() f.probeInFlight = true return ackhandler.Frame{ Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) { f.probeInFlight = false f.max = size }, OnAcked: func(wire.Frame) { f.probeInFlight = false f.current = size f.mtuIncreased(size) }, }, size } quic-go-0.25.0/mtu_discoverer_test.go000066400000000000000000000065631417145451600176040ustar00rootroot00000000000000package quic import ( "math/rand" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("MTU Discoverer", func() { const ( rtt = 100 * time.Millisecond startMTU protocol.ByteCount = 1000 maxMTU protocol.ByteCount = 2000 ) var ( d mtuDiscoverer rttStats *utils.RTTStats now time.Time discoveredMTU protocol.ByteCount ) 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 }) now = time.Now() _ = discoveredMTU }) 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.NextProbeTime()).To(BeTemporally("~", now.Add(5*rtt), scaleDuration(20*time.Millisecond))) 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()) Expect(d.NextProbeTime()).To(BeZero()) ping.OnLost(ping.Frame) Expect(d.ShouldSendProbe(now.Add(10 * rtt))).To(BeTrue()) Expect(d.NextProbeTime()).ToNot(BeZero()) }) It("tries a lower size when a probe is lost", func() { ping, size := d.GetPing() Expect(size).To(Equal(protocol.ByteCount(1500))) ping.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.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() ping.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()) Expect(d.NextProbeTime()).To(BeZero()) }) It("finds the MTU", func() { const rep = 3000 var maxDiff protocol.ByteCount for i := 0; i < rep; i++ { max := protocol.ByteCount(rand.Intn(int(3000-startMTU))) + startMTU + 1 currentMTU := startMTU d := newMTUDiscoverer(rttStats, startMTU, max, func(s protocol.ByteCount) { currentMTU = s }) now := time.Now() realMTU := protocol.ByteCount(rand.Intn(int(max-startMTU))) + startMTU t := now.Add(mtuProbeDelay * rtt) var count int for d.ShouldSendProbe(t) { if count > 25 { Fail("too many iterations") } count++ ping, size := d.GetPing() if size <= realMTU { ping.OnAcked(ping.Frame) } else { ping.OnLost(ping.Frame) } t = t.Add(mtuProbeDelay * rtt) } diff := realMTU - currentMTU Expect(diff).To(BeNumerically(">=", 0)) maxDiff = utils.MaxByteCount(maxDiff, diff) } Expect(maxDiff).To(BeEquivalentTo(maxMTUDiff)) }) }) quic-go-0.25.0/multiplexer.go000066400000000000000000000054451417145451600160630ustar00rootroot00000000000000package quic import ( "bytes" "fmt" "net" "sync" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" ) var ( connMuxerOnce sync.Once connMuxer multiplexer ) type indexableConn interface { LocalAddr() net.Addr } type multiplexer interface { AddConn(c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer) (packetHandlerManager, error) RemoveConn(indexableConn) error } type connManager struct { connIDLen int statelessResetKey []byte tracer logging.Tracer manager packetHandlerManager } // The connMultiplexer listens on multiple net.PacketConns and dispatches // incoming packets to the session handler. type connMultiplexer struct { mutex sync.Mutex conns map[string] /* LocalAddr().String() */ connManager newPacketHandlerManager func(net.PacketConn, int, []byte, logging.Tracer, utils.Logger) (packetHandlerManager, error) // so it can be replaced in the tests logger utils.Logger } var _ multiplexer = &connMultiplexer{} func getMultiplexer() multiplexer { connMuxerOnce.Do(func() { connMuxer = &connMultiplexer{ conns: make(map[string]connManager), logger: utils.DefaultLogger.WithPrefix("muxer"), newPacketHandlerManager: newPacketHandlerMap, } }) return connMuxer } func (m *connMultiplexer) AddConn( c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer, ) (packetHandlerManager, error) { m.mutex.Lock() defer m.mutex.Unlock() addr := c.LocalAddr() connIndex := addr.Network() + " " + addr.String() p, ok := m.conns[connIndex] if !ok { manager, err := m.newPacketHandlerManager(c, connIDLen, statelessResetKey, tracer, m.logger) if err != nil { return nil, err } p = connManager{ connIDLen: connIDLen, statelessResetKey: statelessResetKey, manager: manager, tracer: tracer, } m.conns[connIndex] = p } else { if p.connIDLen != connIDLen { return nil, fmt.Errorf("cannot use %d byte connection IDs on a connection that is already using %d byte connction IDs", connIDLen, p.connIDLen) } if statelessResetKey != nil && !bytes.Equal(p.statelessResetKey, statelessResetKey) { return nil, fmt.Errorf("cannot use different stateless reset keys on the same packet conn") } if tracer != p.tracer { return nil, fmt.Errorf("cannot use different tracers on the same packet conn") } } return p.manager, nil } func (m *connMultiplexer) RemoveConn(c indexableConn) error { m.mutex.Lock() defer m.mutex.Unlock() connIndex := c.LocalAddr().Network() + " " + c.LocalAddr().String() if _, ok := m.conns[connIndex]; !ok { return fmt.Errorf("cannote remove connection, connection is unknown") } delete(m.conns, connIndex) return nil } quic-go-0.25.0/multiplexer_test.go000066400000000000000000000061021417145451600171110ustar00rootroot00000000000000package quic import ( "net" "github.com/golang/mock/gomock" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type testConn struct { counter int net.PacketConn } var _ = Describe("Multiplexer", func() { It("adds a new packet conn ", func() { conn := NewMockPacketConn(mockCtrl) conn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}) _, err := getMultiplexer().AddConn(conn, 8, nil, nil) Expect(err).ToNot(HaveOccurred()) }) It("recognizes when the same connection is added twice", func() { pconn := NewMockPacketConn(mockCtrl) pconn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 4321}).Times(2) pconn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1) conn := testConn{PacketConn: pconn} tracer := mocklogging.NewMockTracer(mockCtrl) _, err := getMultiplexer().AddConn(conn, 8, []byte("foobar"), tracer) Expect(err).ToNot(HaveOccurred()) conn.counter++ _, err = getMultiplexer().AddConn(conn, 8, []byte("foobar"), tracer) Expect(err).ToNot(HaveOccurred()) Expect(getMultiplexer().(*connMultiplexer).conns).To(HaveLen(1)) }) It("errors when adding an existing conn with a different connection ID length", func() { conn := NewMockPacketConn(mockCtrl) conn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}).Times(2) _, err := getMultiplexer().AddConn(conn, 5, nil, nil) Expect(err).ToNot(HaveOccurred()) _, err = getMultiplexer().AddConn(conn, 6, nil, nil) Expect(err).To(MatchError("cannot use 6 byte connection IDs on a connection that is already using 5 byte connction IDs")) }) It("errors when adding an existing conn with a different stateless rest key", func() { conn := NewMockPacketConn(mockCtrl) conn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}).Times(2) _, err := getMultiplexer().AddConn(conn, 7, []byte("foobar"), nil) Expect(err).ToNot(HaveOccurred()) _, err = getMultiplexer().AddConn(conn, 7, []byte("raboof"), nil) Expect(err).To(MatchError("cannot use different stateless reset keys on the same packet conn")) }) It("errors when adding an existing conn with different tracers", func() { conn := NewMockPacketConn(mockCtrl) conn.EXPECT().ReadFrom(gomock.Any()).Do(func([]byte) { <-(make(chan struct{})) }).MaxTimes(1) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 1234}).Times(2) _, err := getMultiplexer().AddConn(conn, 7, nil, mocklogging.NewMockTracer(mockCtrl)) Expect(err).ToNot(HaveOccurred()) _, err = getMultiplexer().AddConn(conn, 7, nil, mocklogging.NewMockTracer(mockCtrl)) Expect(err).To(MatchError("cannot use different tracers on the same packet conn")) }) }) quic-go-0.25.0/packet_handler_map.go000066400000000000000000000324421417145451600173070ustar00rootroot00000000000000package quic import ( "crypto/hmac" "crypto/rand" "crypto/sha256" "errors" "fmt" "hash" "log" "net" "sync" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" ) type zeroRTTQueue struct { queue []*receivedPacket retireTimer *time.Timer } var _ packetHandler = &zeroRTTQueue{} func (h *zeroRTTQueue) handlePacket(p *receivedPacket) { if len(h.queue) < protocol.Max0RTTQueueLen { h.queue = append(h.queue, p) } } func (h *zeroRTTQueue) shutdown() {} func (h *zeroRTTQueue) destroy(error) {} func (h *zeroRTTQueue) getPerspective() protocol.Perspective { return protocol.PerspectiveClient } func (h *zeroRTTQueue) EnqueueAll(sess packetHandler) { for _, p := range h.queue { sess.handlePacket(p) } } func (h *zeroRTTQueue) Clear() { for _, p := range h.queue { p.buffer.Release() } } type packetHandlerMapEntry struct { packetHandler packetHandler is0RTTQueue bool } // The packetHandlerMap stores packetHandlers, identified by connection ID. // It is used: // * by the server to store sessions // * when multiplexing outgoing connections to store clients type packetHandlerMap struct { mutex sync.Mutex conn connection connIDLen int handlers map[string] /* string(ConnectionID)*/ packetHandlerMapEntry resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler server unknownPacketHandler numZeroRTTEntries int listening chan struct{} // is closed when listen returns closed bool deleteRetiredSessionsAfter time.Duration zeroRTTQueueDuration time.Duration statelessResetEnabled bool statelessResetMutex sync.Mutex statelessResetHasher hash.Hash tracer logging.Tracer logger utils.Logger } var _ packetHandlerManager = &packetHandlerMap{} func setReceiveBuffer(c net.PacketConn, logger utils.Logger) error { conn, ok := c.(interface{ SetReadBuffer(int) error }) if !ok { return errors.New("connection doesn't allow setting of receive buffer size. Not a *net.UDPConn?") } size, err := inspectReadBuffer(c) if err != nil { return fmt.Errorf("failed to determine receive buffer size: %w", err) } if size >= protocol.DesiredReceiveBufferSize { logger.Debugf("Conn has receive buffer of %d kiB (wanted: at least %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024) } if err := conn.SetReadBuffer(protocol.DesiredReceiveBufferSize); err != nil { return fmt.Errorf("failed to increase receive buffer size: %w", err) } newSize, err := inspectReadBuffer(c) if err != nil { return fmt.Errorf("failed to determine receive buffer size: %w", err) } if newSize == size { return fmt.Errorf("failed to increase receive buffer size (wanted: %d kiB, got %d kiB)", protocol.DesiredReceiveBufferSize/1024, newSize/1024) } if newSize < protocol.DesiredReceiveBufferSize { return fmt.Errorf("failed to sufficiently increase receive buffer size (was: %d kiB, wanted: %d kiB, got: %d kiB)", size/1024, protocol.DesiredReceiveBufferSize/1024, newSize/1024) } logger.Debugf("Increased receive buffer size to %d kiB", newSize/1024) return nil } // only print warnings about the UPD receive buffer size once var receiveBufferWarningOnce sync.Once func newPacketHandlerMap( c net.PacketConn, connIDLen int, statelessResetKey []byte, tracer logging.Tracer, logger utils.Logger, ) (packetHandlerManager, error) { if err := setReceiveBuffer(c, logger); err != nil { receiveBufferWarningOnce.Do(func() { log.Printf("%s. See https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size for details.", err) }) } conn, err := wrapConn(c) if err != nil { return nil, err } m := &packetHandlerMap{ conn: conn, connIDLen: connIDLen, listening: make(chan struct{}), handlers: make(map[string]packetHandlerMapEntry), resetTokens: make(map[protocol.StatelessResetToken]packetHandler), deleteRetiredSessionsAfter: protocol.RetiredConnectionIDDeleteTimeout, zeroRTTQueueDuration: protocol.Max0RTTQueueingDuration, statelessResetEnabled: len(statelessResetKey) > 0, statelessResetHasher: hmac.New(sha256.New, statelessResetKey), tracer: tracer, logger: logger, } go m.listen() if logger.Debug() { go m.logUsage() } return m, nil } func (h *packetHandlerMap) logUsage() { ticker := time.NewTicker(2 * time.Second) var printedZero bool for { select { case <-h.listening: 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) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { h.mutex.Lock() defer h.mutex.Unlock() if _, ok := h.handlers[string(id)]; ok { h.logger.Debugf("Not adding connection ID %s, as it already exists.", id) return false } h.handlers[string(id)] = packetHandlerMapEntry{packetHandler: handler} h.logger.Debugf("Adding connection ID %s.", id) return true } func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, fn func() packetHandler) bool { h.mutex.Lock() defer h.mutex.Unlock() var q *zeroRTTQueue if entry, ok := h.handlers[string(clientDestConnID)]; ok { if !entry.is0RTTQueue { h.logger.Debugf("Not adding connection ID %s for a new session, as it already exists.", clientDestConnID) return false } q = entry.packetHandler.(*zeroRTTQueue) q.retireTimer.Stop() h.numZeroRTTEntries-- if h.numZeroRTTEntries < 0 { panic("number of 0-RTT queues < 0") } } sess := fn() if q != nil { q.EnqueueAll(sess) } h.handlers[string(clientDestConnID)] = packetHandlerMapEntry{packetHandler: sess} h.handlers[string(newConnID)] = packetHandlerMapEntry{packetHandler: sess} h.logger.Debugf("Adding connection IDs %s and %s for a new session.", clientDestConnID, newConnID) return true } func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { h.mutex.Lock() delete(h.handlers, string(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.deleteRetiredSessionsAfter) time.AfterFunc(h.deleteRetiredSessionsAfter, func() { h.mutex.Lock() delete(h.handlers, string(id)) h.mutex.Unlock() h.logger.Debugf("Removing connection ID %s after it has been retired.", id) }) } func (h *packetHandlerMap) ReplaceWithClosed(id protocol.ConnectionID, handler packetHandler) { h.mutex.Lock() h.handlers[string(id)] = packetHandlerMapEntry{packetHandler: handler} h.mutex.Unlock() h.logger.Debugf("Replacing session for connection ID %s with a closed session.", id) time.AfterFunc(h.deleteRetiredSessionsAfter, func() { h.mutex.Lock() handler.shutdown() delete(h.handlers, string(id)) h.mutex.Unlock() h.logger.Debugf("Removing connection ID %s for a closed session after it has been retired.", id) }) } 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) SetServer(s unknownPacketHandler) { h.mutex.Lock() h.server = s h.mutex.Unlock() } func (h *packetHandlerMap) CloseServer() { h.mutex.Lock() if h.server == nil { h.mutex.Unlock() return } h.server = nil var wg sync.WaitGroup for _, entry := range h.handlers { if entry.packetHandler.getPerspective() == protocol.PerspectiveServer { wg.Add(1) go func(handler packetHandler) { // blocks until the CONNECTION_CLOSE has been sent and the run-loop has stopped handler.shutdown() wg.Done() }(entry.packetHandler) } } h.mutex.Unlock() wg.Wait() } // Destroy closes the underlying connection and waits until listen() has returned. // It does not close active sessions. func (h *packetHandlerMap) Destroy() error { if err := h.conn.Close(); err != nil { return err } <-h.listening // wait until listening returns return nil } func (h *packetHandlerMap) close(e error) error { h.mutex.Lock() if h.closed { h.mutex.Unlock() return nil } var wg sync.WaitGroup for _, entry := range h.handlers { wg.Add(1) go func(handler packetHandler) { handler.destroy(e) wg.Done() }(entry.packetHandler) } if h.server != nil { h.server.setCloseError(e) } h.closed = true h.mutex.Unlock() wg.Wait() return getMultiplexer().RemoveConn(h.conn) } func (h *packetHandlerMap) listen() { defer close(h.listening) for { p, err := h.conn.ReadPacket() if nerr, ok := err.(net.Error); ok && nerr.Temporary() { h.logger.Debugf("Temporary error reading from conn: %w", err) continue } if err != nil { h.close(err) return } h.handlePacket(p) } } func (h *packetHandlerMap) handlePacket(p *receivedPacket) { connID, err := wire.ParseConnectionID(p.data, h.connIDLen) if err != nil { h.logger.Debugf("error parsing connection ID on packet from %s: %s", p.remoteAddr, err) if h.tracer != nil { h.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) } p.buffer.MaybeRelease() return } h.mutex.Lock() defer h.mutex.Unlock() if isStatelessReset := h.maybeHandleStatelessReset(p.data); isStatelessReset { return } if entry, ok := h.handlers[string(connID)]; ok { if entry.is0RTTQueue { // only enqueue 0-RTT packets in the 0-RTT queue if wire.Is0RTTPacket(p.data) { entry.packetHandler.handlePacket(p) return } } else { // existing session entry.packetHandler.handlePacket(p) return } } if p.data[0]&0x80 == 0 { go h.maybeSendStatelessReset(p, connID) return } if h.server == nil { // no server set h.logger.Debugf("received a packet with an unexpected connection ID %s", connID) return } if wire.Is0RTTPacket(p.data) { if h.numZeroRTTEntries >= protocol.Max0RTTQueues { return } h.numZeroRTTEntries++ queue := &zeroRTTQueue{queue: make([]*receivedPacket, 0, 8)} h.handlers[string(connID)] = packetHandlerMapEntry{ packetHandler: queue, is0RTTQueue: true, } queue.retireTimer = time.AfterFunc(h.zeroRTTQueueDuration, func() { h.mutex.Lock() defer h.mutex.Unlock() // The entry might have been replaced by an actual session. // Only delete it if it's still a 0-RTT queue. if entry, ok := h.handlers[string(connID)]; ok && entry.is0RTTQueue { delete(h.handlers, string(connID)) h.numZeroRTTEntries-- if h.numZeroRTTEntries < 0 { panic("number of 0-RTT queues < 0") } entry.packetHandler.(*zeroRTTQueue).Clear() if h.logger.Debug() { h.logger.Debugf("Removing 0-RTT queue for %s.", connID) } } }) queue.handlePacket(p) return } h.server.handlePacket(p) } func (h *packetHandlerMap) maybeHandleStatelessReset(data []byte) bool { // stateless resets are always short header packets if data[0]&0x80 != 0 { return false } if len(data) < 17 /* type byte + 16 bytes for the reset token */ { return false } var token protocol.StatelessResetToken copy(token[:], data[len(data)-16:]) if sess, ok := h.resetTokens[token]; ok { h.logger.Debugf("Received a stateless reset with token %#x. Closing session.", token) go sess.destroy(&StatelessResetError{Token: token}) return true } return false } func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken { var token protocol.StatelessResetToken if !h.statelessResetEnabled { // 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 } func (h *packetHandlerMap) maybeSendStatelessReset(p *receivedPacket, connID protocol.ConnectionID) { defer p.buffer.Release() if !h.statelessResetEnabled { return } // Don't send a stateless reset in response to very small packets. // This includes packets that could be stateless resets. if len(p.data) <= protocol.MinStatelessResetSize { return } token := h.GetStatelessResetToken(connID) h.logger.Debugf("Sending stateless reset to %s (connection ID: %s). Token: %#x", p.remoteAddr, connID, token) data := make([]byte, protocol.MinStatelessResetSize-16, protocol.MinStatelessResetSize) rand.Read(data) data[0] = (data[0] & 0x7f) | 0x40 data = append(data, token[:]...) if _, err := h.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil { h.logger.Debugf("Error sending Stateless Reset: %s", err) } } quic-go-0.25.0/packet_handler_map_test.go000066400000000000000000000446601417145451600203530ustar00rootroot00000000000000package quic import ( "bytes" "crypto/rand" "errors" "net" "time" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Packet Handler Map", func() { type packetToRead struct { addr net.Addr data []byte err error } var ( handler *packetHandlerMap conn *MockPacketConn tracer *mocklogging.MockTracer packetChan chan packetToRead connIDLen int statelessResetKey []byte ) getPacketWithPacketType := func(connID protocol.ConnectionID, t protocol.PacketType, length protocol.ByteCount) []byte { buf := &bytes.Buffer{} Expect((&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: t, DestConnectionID: connID, Length: length, Version: protocol.VersionTLS, }, PacketNumberLen: protocol.PacketNumberLen2, }).Write(buf, protocol.VersionWhatever)).To(Succeed()) return buf.Bytes() } getPacket := func(connID protocol.ConnectionID) []byte { return getPacketWithPacketType(connID, protocol.PacketTypeHandshake, 2) } BeforeEach(func() { statelessResetKey = nil connIDLen = 0 tracer = mocklogging.NewMockTracer(mockCtrl) packetChan = make(chan packetToRead, 10) }) JustBeforeEach(func() { conn = NewMockPacketConn(mockCtrl) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes() conn.EXPECT().ReadFrom(gomock.Any()).DoAndReturn(func(b []byte) (int, net.Addr, error) { p, ok := <-packetChan if !ok { return 0, nil, errors.New("closed") } return copy(b, p.data), p.addr, p.err }).AnyTimes() phm, err := newPacketHandlerMap(conn, connIDLen, statelessResetKey, tracer, utils.DefaultLogger) Expect(err).ToNot(HaveOccurred()) handler = phm.(*packetHandlerMap) }) It("closes", func() { getMultiplexer() // make the sync.Once execute // replace the clientMuxer. getClientMultiplexer will now return the MockMultiplexer mockMultiplexer := NewMockMultiplexer(mockCtrl) origMultiplexer := connMuxer connMuxer = mockMultiplexer defer func() { connMuxer = origMultiplexer }() testErr := errors.New("test error ") sess1 := NewMockPacketHandler(mockCtrl) sess1.EXPECT().destroy(testErr) sess2 := NewMockPacketHandler(mockCtrl) sess2.EXPECT().destroy(testErr) handler.Add(protocol.ConnectionID{1, 1, 1, 1}, sess1) handler.Add(protocol.ConnectionID{2, 2, 2, 2}, sess2) mockMultiplexer.EXPECT().RemoveConn(gomock.Any()) handler.close(testErr) close(packetChan) Eventually(handler.listening).Should(BeClosed()) }) Context("other operations", func() { AfterEach(func() { // delete sessions and the server before closing // They might be mock implementations, and we'd have to register the expected calls before otherwise. handler.mutex.Lock() for connID := range handler.handlers { delete(handler.handlers, connID) } handler.server = nil handler.mutex.Unlock() conn.EXPECT().Close().MaxTimes(1) close(packetChan) handler.Destroy() Eventually(handler.listening).Should(BeClosed()) }) Context("handling packets", func() { BeforeEach(func() { connIDLen = 5 }) It("handles packets for different packet handlers on the same packet conn", func() { connID1 := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} connID2 := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1} packetHandler1 := NewMockPacketHandler(mockCtrl) packetHandler2 := NewMockPacketHandler(mockCtrl) handledPacket1 := make(chan struct{}) handledPacket2 := make(chan struct{}) packetHandler1.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) { connID, err := wire.ParseConnectionID(p.data, 0) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(Equal(connID1)) close(handledPacket1) }) packetHandler2.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) { connID, err := wire.ParseConnectionID(p.data, 0) Expect(err).ToNot(HaveOccurred()) Expect(connID).To(Equal(connID2)) close(handledPacket2) }) handler.Add(connID1, packetHandler1) handler.Add(connID2, packetHandler2) packetChan <- packetToRead{data: getPacket(connID1)} packetChan <- packetToRead{data: getPacket(connID2)} Eventually(handledPacket1).Should(BeClosed()) Eventually(handledPacket2).Should(BeClosed()) }) It("drops unparseable packets", func() { addr := &net.UDPAddr{IP: net.IPv4(9, 8, 7, 6), Port: 1234} tracer.EXPECT().DroppedPacket(addr, logging.PacketTypeNotDetermined, protocol.ByteCount(4), logging.PacketDropHeaderParseError) handler.handlePacket(&receivedPacket{ buffer: getPacketBuffer(), remoteAddr: addr, data: []byte{0, 1, 2, 3}, }) }) It("deletes removed sessions immediately", func() { handler.deleteRetiredSessionsAfter = time.Hour connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} handler.Add(connID, NewMockPacketHandler(mockCtrl)) handler.Remove(connID) handler.handlePacket(&receivedPacket{data: getPacket(connID)}) // don't EXPECT any calls to handlePacket of the MockPacketHandler }) It("deletes retired session entries after a wait time", func() { handler.deleteRetiredSessionsAfter = scaleDuration(10 * time.Millisecond) connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} sess := NewMockPacketHandler(mockCtrl) handler.Add(connID, sess) handler.Retire(connID) time.Sleep(scaleDuration(30 * time.Millisecond)) handler.handlePacket(&receivedPacket{data: getPacket(connID)}) // don't EXPECT any calls to handlePacket of the MockPacketHandler }) It("passes packets arriving late for closed sessions to that session", func() { handler.deleteRetiredSessionsAfter = time.Hour connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} packetHandler := NewMockPacketHandler(mockCtrl) handled := make(chan struct{}) packetHandler.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) { close(handled) }) handler.Add(connID, packetHandler) handler.Retire(connID) handler.handlePacket(&receivedPacket{data: getPacket(connID)}) Eventually(handled).Should(BeClosed()) }) It("drops packets for unknown receivers", func() { connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} handler.handlePacket(&receivedPacket{data: getPacket(connID)}) }) It("closes the packet handlers when reading from the conn fails", func() { done := make(chan struct{}) packetHandler := NewMockPacketHandler(mockCtrl) packetHandler.EXPECT().destroy(gomock.Any()).Do(func(e error) { Expect(e).To(HaveOccurred()) close(done) }) handler.Add(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, packetHandler) packetChan <- packetToRead{err: errors.New("read failed")} Eventually(done).Should(BeClosed()) }) It("continues listening for temporary errors", func() { packetHandler := NewMockPacketHandler(mockCtrl) handler.Add(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, packetHandler) err := deadlineError{} Expect(err.Temporary()).To(BeTrue()) packetChan <- packetToRead{err: err} // don't EXPECT any calls to packetHandler.destroy time.Sleep(50 * time.Millisecond) }) It("says if a connection ID is already taken", func() { connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} Expect(handler.Add(connID, NewMockPacketHandler(mockCtrl))).To(BeTrue()) Expect(handler.Add(connID, NewMockPacketHandler(mockCtrl))).To(BeFalse()) }) It("says if a connection ID is already taken, for AddWithConnID", func() { clientDestConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} newConnID1 := protocol.ConnectionID{1, 2, 3, 4} newConnID2 := protocol.ConnectionID{4, 3, 2, 1} Expect(handler.AddWithConnID(clientDestConnID, newConnID1, func() packetHandler { return NewMockPacketHandler(mockCtrl) })).To(BeTrue()) Expect(handler.AddWithConnID(clientDestConnID, newConnID2, func() packetHandler { return NewMockPacketHandler(mockCtrl) })).To(BeFalse()) }) }) Context("running a server", func() { It("adds a server", func() { connID := protocol.ConnectionID{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} p := getPacket(connID) server := NewMockUnknownPacketHandler(mockCtrl) server.EXPECT().handlePacket(gomock.Any()).Do(func(p *receivedPacket) { cid, err := wire.ParseConnectionID(p.data, 0) Expect(err).ToNot(HaveOccurred()) Expect(cid).To(Equal(connID)) }) handler.SetServer(server) handler.handlePacket(&receivedPacket{data: p}) }) It("closes all server sessions", func() { handler.SetServer(NewMockUnknownPacketHandler(mockCtrl)) clientSess := NewMockPacketHandler(mockCtrl) clientSess.EXPECT().getPerspective().Return(protocol.PerspectiveClient) serverSess := NewMockPacketHandler(mockCtrl) serverSess.EXPECT().getPerspective().Return(protocol.PerspectiveServer) serverSess.EXPECT().shutdown() handler.Add(protocol.ConnectionID{1, 1, 1, 1}, clientSess) handler.Add(protocol.ConnectionID{2, 2, 2, 2}, serverSess) handler.CloseServer() }) It("stops handling packets with unknown connection IDs after the server is closed", func() { connID := protocol.ConnectionID{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} p := getPacket(connID) server := NewMockUnknownPacketHandler(mockCtrl) // don't EXPECT any calls to server.handlePacket handler.SetServer(server) handler.CloseServer() handler.handlePacket(&receivedPacket{data: p}) }) }) Context("0-RTT", func() { JustBeforeEach(func() { handler.zeroRTTQueueDuration = time.Hour server := NewMockUnknownPacketHandler(mockCtrl) // we don't expect any calls to server.handlePacket handler.SetServer(server) }) It("queues 0-RTT packets", func() { server := NewMockUnknownPacketHandler(mockCtrl) // don't EXPECT any calls to server.handlePacket handler.SetServer(server) connID := protocol.ConnectionID{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} p1 := &receivedPacket{data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 1)} p2 := &receivedPacket{data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 2)} p3 := &receivedPacket{data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 3)} handler.handlePacket(p1) handler.handlePacket(p2) handler.handlePacket(p3) sess := NewMockPacketHandler(mockCtrl) done := make(chan struct{}) gomock.InOrder( sess.EXPECT().handlePacket(p1), sess.EXPECT().handlePacket(p2), sess.EXPECT().handlePacket(p3).Do(func(packet *receivedPacket) { close(done) }), ) handler.AddWithConnID(connID, protocol.ConnectionID{1, 2, 3, 4}, func() packetHandler { return sess }) Eventually(done).Should(BeClosed()) }) It("directs 0-RTT packets to existing sessions", func() { connID := protocol.ConnectionID{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} sess := NewMockPacketHandler(mockCtrl) handler.AddWithConnID(connID, protocol.ConnectionID{1, 2, 3, 4}, func() packetHandler { return sess }) p1 := &receivedPacket{data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 1)} sess.EXPECT().handlePacket(p1) handler.handlePacket(p1) }) It("limits the number of 0-RTT queues", func() { for i := 0; i < protocol.Max0RTTQueues; i++ { connID := make(protocol.ConnectionID, 8) rand.Read(connID) p := &receivedPacket{data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 1)} handler.handlePacket(p) } // We're already storing the maximum number of queues. This packet will be dropped. connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9} handler.handlePacket(&receivedPacket{data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 1)}) // Don't EXPECT any handlePacket() calls. sess := NewMockPacketHandler(mockCtrl) handler.AddWithConnID(connID, protocol.ConnectionID{1, 2, 3, 4}, func() packetHandler { return sess }) time.Sleep(20 * time.Millisecond) }) It("deletes queues if no session is created for this connection ID", func() { queueDuration := scaleDuration(10 * time.Millisecond) handler.zeroRTTQueueDuration = queueDuration server := NewMockUnknownPacketHandler(mockCtrl) // don't EXPECT any calls to server.handlePacket handler.SetServer(server) connID := protocol.ConnectionID{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88} p1 := &receivedPacket{ data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 1), buffer: getPacketBuffer(), } p2 := &receivedPacket{ data: getPacketWithPacketType(connID, protocol.PacketType0RTT, 2), buffer: getPacketBuffer(), } handler.handlePacket(p1) handler.handlePacket(p2) // wait a bit. The queue should now already be deleted. time.Sleep(queueDuration * 3) // Don't EXPECT any handlePacket() calls. sess := NewMockPacketHandler(mockCtrl) handler.AddWithConnID(connID, protocol.ConnectionID{1, 2, 3, 4}, func() packetHandler { return sess }) time.Sleep(20 * time.Millisecond) }) }) Context("stateless resets", func() { BeforeEach(func() { connIDLen = 5 }) Context("handling", func() { It("handles stateless resets", func() { packetHandler := NewMockPacketHandler(mockCtrl) token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} handler.AddResetToken(token, packetHandler) destroyed := make(chan struct{}) packet := append([]byte{0x40} /* short header packet */, make([]byte, 50)...) packet = append(packet, token[:]...) packetHandler.EXPECT().destroy(gomock.Any()).Do(func(err error) { defer GinkgoRecover() defer close(destroyed) Expect(err).To(HaveOccurred()) var resetErr *StatelessResetError Expect(errors.As(err, &resetErr)).To(BeTrue()) Expect(err.Error()).To(ContainSubstring("received a stateless reset")) Expect(resetErr.Token).To(Equal(token)) }) packetChan <- packetToRead{data: packet} Eventually(destroyed).Should(BeClosed()) }) It("handles stateless resets for 0-length connection IDs", func() { handler.connIDLen = 0 packetHandler := NewMockPacketHandler(mockCtrl) token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} handler.AddResetToken(token, packetHandler) destroyed := make(chan struct{}) packet := append([]byte{0x40} /* short header packet */, make([]byte, 50)...) packet = append(packet, token[:]...) packetHandler.EXPECT().destroy(gomock.Any()).Do(func(err error) { defer GinkgoRecover() Expect(err).To(HaveOccurred()) var resetErr *StatelessResetError Expect(errors.As(err, &resetErr)).To(BeTrue()) Expect(err.Error()).To(ContainSubstring("received a stateless reset")) Expect(resetErr.Token).To(Equal(token)) close(destroyed) }) packetChan <- packetToRead{data: packet} Eventually(destroyed).Should(BeClosed()) }) It("removes reset tokens", func() { connID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef, 0x42} packetHandler := NewMockPacketHandler(mockCtrl) handler.Add(connID, packetHandler) token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} handler.AddResetToken(token, NewMockPacketHandler(mockCtrl)) handler.RemoveResetToken(token) // don't EXPECT any call to packetHandler.destroy() packetHandler.EXPECT().handlePacket(gomock.Any()) p := append([]byte{0x40} /* short header packet */, connID.Bytes()...) p = append(p, make([]byte, 50)...) p = append(p, token[:]...) handler.handlePacket(&receivedPacket{data: p}) }) It("ignores packets too small to contain a stateless reset", func() { handler.connIDLen = 0 packetHandler := NewMockPacketHandler(mockCtrl) token := protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} handler.AddResetToken(token, packetHandler) done := make(chan struct{}) // don't EXPECT any calls here, but register the closing of the done channel packetHandler.EXPECT().destroy(gomock.Any()).Do(func(error) { close(done) }).AnyTimes() packetChan <- packetToRead{data: append([]byte{0x40} /* short header packet */, token[:15]...)} Consistently(done).ShouldNot(BeClosed()) }) }) Context("generating", func() { BeforeEach(func() { key := make([]byte, 32) rand.Read(key) statelessResetKey = key }) It("generates stateless reset tokens", func() { connID1 := []byte{0xde, 0xad, 0xbe, 0xef} connID2 := []byte{0xde, 0xca, 0xfb, 0xad} Expect(handler.GetStatelessResetToken(connID1)).ToNot(Equal(handler.GetStatelessResetToken(connID2))) }) It("sends stateless resets", func() { addr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} p := append([]byte{40}, make([]byte, 100)...) done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), addr).Do(func(b []byte, _ net.Addr) { defer close(done) Expect(b[0] & 0x80).To(BeZero()) // short header packet Expect(b).To(HaveLen(protocol.MinStatelessResetSize)) }) handler.handlePacket(&receivedPacket{ buffer: getPacketBuffer(), remoteAddr: addr, data: p, }) Eventually(done).Should(BeClosed()) }) It("doesn't send stateless resets for small packets", func() { addr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} p := append([]byte{40}, make([]byte, protocol.MinStatelessResetSize-2)...) handler.handlePacket(&receivedPacket{ buffer: getPacketBuffer(), remoteAddr: addr, data: p, }) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) }) }) Context("if no key is configured", func() { It("doesn't send stateless resets", func() { addr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} p := append([]byte{40}, make([]byte, 100)...) handler.handlePacket(&receivedPacket{ buffer: getPacketBuffer(), remoteAddr: addr, data: p, }) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) }) }) }) }) }) quic-go-0.25.0/packet_packer.go000066400000000000000000000670611417145451600163070ustar00rootroot00000000000000package quic import ( "bytes" "errors" "fmt" "net" "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type packer interface { PackCoalescedPacket() (*coalescedPacket, error) PackPacket() (*packedPacket, error) MaybePackProbePacket(protocol.EncryptionLevel) (*packedPacket, error) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) PackConnectionClose(*qerr.TransportError) (*coalescedPacket, error) PackApplicationClose(*qerr.ApplicationError) (*coalescedPacket, error) SetMaxPacketSize(protocol.ByteCount) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount) (*packedPacket, error) HandleTransportParameters(*wire.TransportParameters) SetToken([]byte) } type sealer interface { handshake.LongHeaderSealer } type payload struct { frames []ackhandler.Frame ack *wire.AckFrame length protocol.ByteCount } type packedPacket struct { buffer *packetBuffer *packetContents } type packetContents struct { header *wire.ExtendedHeader ack *wire.AckFrame frames []ackhandler.Frame length protocol.ByteCount isMTUProbePacket bool } type coalescedPacket struct { buffer *packetBuffer packets []*packetContents } func (p *packetContents) EncryptionLevel() protocol.EncryptionLevel { if !p.header.IsLongHeader { return protocol.Encryption1RTT } //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 *packetContents) IsAckEliciting() bool { return ackhandler.HasAckElicitingFrames(p.frames) } func (p *packetContents) ToAckHandlerPacket(now time.Time, q *retransmissionQueue) *ackhandler.Packet { largestAcked := protocol.InvalidPacketNumber if p.ack != nil { largestAcked = p.ack.LargestAcked() } encLevel := p.EncryptionLevel() for i := range p.frames { if p.frames[i].OnLost != nil { continue } switch encLevel { case protocol.EncryptionInitial: p.frames[i].OnLost = q.AddInitial case protocol.EncryptionHandshake: p.frames[i].OnLost = q.AddHandshake case protocol.Encryption0RTT, protocol.Encryption1RTT: p.frames[i].OnLost = q.AddAppData } } return &ackhandler.Packet{ PacketNumber: p.header.PacketNumber, LargestAcked: largestAcked, Frames: p.frames, Length: p.length, EncryptionLevel: encLevel, SendTime: now, IsPathMTUProbePacket: p.isMTUProbePacket, } } func getMaxPacketSize(addr net.Addr) protocol.ByteCount { maxSize := protocol.ByteCount(protocol.MinInitialPacketSize) // If this is not a UDP address, we don't know anything about the MTU. // Use the minimum size of an Initial packet as the max packet size. if udpAddr, ok := addr.(*net.UDPAddr); ok { if utils.IsIPv4(udpAddr.IP) { maxSize = protocol.InitialPacketSizeIPv4 } else { maxSize = protocol.InitialPacketSizeIPv6 } } return maxSize } 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.Frame, protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) AppendControlFrames([]ackhandler.Frame, protocol.ByteCount) ([]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 version protocol.VersionNumber cryptoSetup sealingManager initialStream cryptoStream handshakeStream cryptoStream token []byte pnManager packetNumberManager framer frameSource acks ackFrameSource datagramQueue *datagramQueue retransmissionQueue *retransmissionQueue maxPacketSize protocol.ByteCount numNonAckElicitingAcks int } var _ packer = &packetPacker{} func newPacketPacker( srcConnID protocol.ConnectionID, getDestConnID func() protocol.ConnectionID, initialStream cryptoStream, handshakeStream cryptoStream, packetNumberManager packetNumberManager, retransmissionQueue *retransmissionQueue, remoteAddr net.Addr, // only used for determining the max packet size cryptoSetup sealingManager, framer frameSource, acks ackFrameSource, datagramQueue *datagramQueue, perspective protocol.Perspective, version protocol.VersionNumber, ) *packetPacker { return &packetPacker{ cryptoSetup: cryptoSetup, getDestConnID: getDestConnID, srcConnID: srcConnID, initialStream: initialStream, handshakeStream: handshakeStream, retransmissionQueue: retransmissionQueue, datagramQueue: datagramQueue, perspective: perspective, version: version, framer: framer, acks: acks, pnManager: packetNumberManager, maxPacketSize: getMaxPacketSize(remoteAddr), } } // PackConnectionClose packs a packet that closes the connection with a transport error. func (p *packetPacker) PackConnectionClose(e *qerr.TransportError) (*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) } // PackApplicationClose packs a packet that closes the connection with an application error. func (p *packetPacker) PackApplicationClose(e *qerr.ApplicationError) (*coalescedPacket, error) { return p.packConnectionClose(true, uint64(e.ErrorCode), 0, e.ErrorMessage) } func (p *packetPacker) packConnectionClose( isApplicationError bool, errorCode uint64, frameType uint64, reason string, ) (*coalescedPacket, error) { var sealers [4]sealer var hdrs [4]*wire.ExtendedHeader var payloads [4]*payload var size protocol.ByteCount var numPackets 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 = "" } payload := &payload{ frames: []ackhandler.Frame{{Frame: ccf}}, length: ccf.Length(p.version), } var sealer sealer var err error var keyPhase protocol.KeyPhaseBit // only set for 1-RTT 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 { hdr = p.getShortHeader(keyPhase) } else { hdr = p.getLongHeader(encLevel) } hdrs[i] = hdr payloads[i] = payload size += p.packetLength(hdr, payload) + protocol.ByteCount(sealer.Overhead()) numPackets++ } contents := make([]*packetContents, 0, numPackets) buffer := getPacketBuffer() 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) } c, err := p.appendPacket(buffer, hdrs[i], payloads[i], paddingLen, encLevel, sealers[i], false) if err != nil { return nil, err } contents = append(contents, c) } return &coalescedPacket{buffer: buffer, packets: contents}, nil } // packetLength calculates the length of the serialized 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) packetLength(hdr *wire.ExtendedHeader, payload *payload) protocol.ByteCount { var paddingLen protocol.ByteCount pnLen := protocol.ByteCount(hdr.PacketNumberLen) if payload.length < 4-pnLen { paddingLen = 4 - pnLen - payload.length } return hdr.GetLength(p.version) + payload.length + paddingLen } func (p *packetPacker) MaybePackAckPacket(handshakeConfirmed bool) (*packedPacket, error) { var encLevel protocol.EncryptionLevel var ack *wire.AckFrame if !handshakeConfirmed { ack = p.acks.GetAckFrame(protocol.EncryptionInitial, true) if ack != nil { encLevel = protocol.EncryptionInitial } else { ack = p.acks.GetAckFrame(protocol.EncryptionHandshake, true) if ack != nil { encLevel = protocol.EncryptionHandshake } } } if ack == nil { ack = p.acks.GetAckFrame(protocol.Encryption1RTT, true) if ack == nil { return nil, nil } encLevel = protocol.Encryption1RTT } payload := &payload{ ack: ack, length: ack.Length(p.version), } sealer, hdr, err := p.getSealerAndHeader(encLevel) if err != nil { return nil, err } return p.writeSinglePacket(hdr, payload, encLevel, sealer) } // size is the expected size of the packet, if no padding was applied. func (p *packetPacker) initialPaddingLen(frames []ackhandler.Frame, size 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 size >= p.maxPacketSize { return 0 } return p.maxPacketSize - size } // 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() (*coalescedPacket, error) { maxPacketSize := p.maxPacketSize if p.perspective == protocol.PerspectiveClient { maxPacketSize = protocol.MinInitialPacketSize } var initialHdr, handshakeHdr, appDataHdr *wire.ExtendedHeader var initialPayload, handshakePayload, appDataPayload *payload var numPackets int // 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()), size, protocol.EncryptionInitial) if initialPayload != nil { size += p.packetLength(initialHdr, initialPayload) + protocol.ByteCount(initialSealer.Overhead()) numPackets++ } } // Add a Handshake packet. var handshakeSealer sealer if 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()), size, protocol.EncryptionHandshake) if handshakePayload != nil { s := p.packetLength(handshakeHdr, handshakePayload) + protocol.ByteCount(handshakeSealer.Overhead()) size += s numPackets++ } } } // Add a 0-RTT / 1-RTT packet. var appDataSealer sealer appDataEncLevel := protocol.Encryption1RTT if size < maxPacketSize-protocol.MinCoalescedPacketSize { var err error appDataSealer, appDataHdr, appDataPayload = p.maybeGetAppDataPacket(maxPacketSize-size, size) if err != nil { return nil, err } if appDataHdr != nil { if appDataHdr.IsLongHeader { appDataEncLevel = protocol.Encryption0RTT } if appDataPayload != nil { size += p.packetLength(appDataHdr, appDataPayload) + protocol.ByteCount(appDataSealer.Overhead()) numPackets++ } } } if numPackets == 0 { return nil, nil } buffer := getPacketBuffer() packet := &coalescedPacket{ buffer: buffer, packets: make([]*packetContents, 0, numPackets), } if initialPayload != nil { padding := p.initialPaddingLen(initialPayload.frames, size) cont, err := p.appendPacket(buffer, initialHdr, initialPayload, padding, protocol.EncryptionInitial, initialSealer, false) if err != nil { return nil, err } packet.packets = append(packet.packets, cont) } if handshakePayload != nil { cont, err := p.appendPacket(buffer, handshakeHdr, handshakePayload, 0, protocol.EncryptionHandshake, handshakeSealer, false) if err != nil { return nil, err } packet.packets = append(packet.packets, cont) } if appDataPayload != nil { cont, err := p.appendPacket(buffer, appDataHdr, appDataPayload, 0, appDataEncLevel, appDataSealer, false) if err != nil { return nil, err } packet.packets = append(packet.packets, cont) } return packet, nil } // PackPacket packs a packet in the application data packet number space. // It should be called after the handshake is confirmed. func (p *packetPacker) PackPacket() (*packedPacket, error) { sealer, hdr, payload := p.maybeGetAppDataPacket(p.maxPacketSize, 0) if payload == nil { return nil, nil } buffer := getPacketBuffer() encLevel := protocol.Encryption1RTT if hdr.IsLongHeader { encLevel = protocol.Encryption0RTT } cont, err := p.appendPacket(buffer, hdr, payload, 0, encLevel, sealer, false) if err != nil { return nil, err } return &packedPacket{ buffer: buffer, packetContents: cont, }, nil } func (p *packetPacker) maybeGetCryptoPacket(maxPacketSize, currentSize protocol.ByteCount, encLevel protocol.EncryptionLevel) (*wire.ExtendedHeader, *payload) { var s cryptoStream var hasRetransmission bool //nolint:exhaustive // Initial and Handshake are the only two encryption levels here. switch encLevel { case protocol.EncryptionInitial: s = p.initialStream hasRetransmission = p.retransmissionQueue.HasInitialData() case protocol.EncryptionHandshake: s = p.handshakeStream hasRetransmission = p.retransmissionQueue.HasHandshakeData() } hasData := s.HasData() var ack *wire.AckFrame if encLevel == protocol.EncryptionInitial || currentSize == 0 { ack = p.acks.GetAckFrame(encLevel, !hasRetransmission && !hasData) } if !hasData && !hasRetransmission && ack == nil { // nothing to send return nil, nil } var payload payload if ack != nil { payload.ack = ack payload.length = ack.Length(p.version) maxPacketSize -= payload.length } hdr := p.getLongHeader(encLevel) maxPacketSize -= hdr.GetLength(p.version) if hasRetransmission { for { var f wire.Frame //nolint:exhaustive // 0-RTT packets can't contain any retransmission.s switch encLevel { case protocol.EncryptionInitial: f = p.retransmissionQueue.GetInitialFrame(maxPacketSize) case protocol.EncryptionHandshake: f = p.retransmissionQueue.GetHandshakeFrame(maxPacketSize) } if f == nil { break } payload.frames = append(payload.frames, ackhandler.Frame{Frame: f}) frameLen := f.Length(p.version) payload.length += frameLen maxPacketSize -= frameLen } } else if s.HasData() { cf := s.PopCryptoFrame(maxPacketSize) payload.frames = []ackhandler.Frame{{Frame: cf}} payload.length += cf.Length(p.version) } return hdr, &payload } func (p *packetPacker) maybeGetAppDataPacket(maxPacketSize, currentSize protocol.ByteCount) (sealer, *wire.ExtendedHeader, *payload) { var sealer sealer var encLevel protocol.EncryptionLevel var hdr *wire.ExtendedHeader oneRTTSealer, err := p.cryptoSetup.Get1RTTSealer() if err == nil { encLevel = protocol.Encryption1RTT sealer = oneRTTSealer hdr = p.getShortHeader(oneRTTSealer.KeyPhase()) } else { // 1-RTT sealer not yet available if p.perspective != protocol.PerspectiveClient { return nil, nil, nil } sealer, err = p.cryptoSetup.Get0RTTSealer() if sealer == nil || err != nil { return nil, nil, nil } encLevel = protocol.Encryption0RTT hdr = p.getLongHeader(protocol.Encryption0RTT) } maxPayloadSize := maxPacketSize - hdr.GetLength(p.version) - protocol.ByteCount(sealer.Overhead()) payload := p.maybeGetAppDataPacketWithEncLevel(maxPayloadSize, encLevel == protocol.Encryption1RTT && currentSize == 0) return sealer, hdr, payload } func (p *packetPacker) maybeGetAppDataPacketWithEncLevel(maxPayloadSize protocol.ByteCount, ackAllowed bool) *payload { payload := p.composeNextPacket(maxPayloadSize, ackAllowed) // check if we have anything to send if len(payload.frames) == 0 { if payload.ack == nil { return nil } // the packet only contains an ACK if p.numNonAckElicitingAcks >= protocol.MaxNonAckElicitingAcks { ping := &wire.PingFrame{} // don't retransmit the PING frame when it is lost payload.frames = append(payload.frames, ackhandler.Frame{Frame: ping, OnLost: func(wire.Frame) {}}) payload.length += ping.Length(p.version) p.numNonAckElicitingAcks = 0 } else { p.numNonAckElicitingAcks++ } } else { p.numNonAckElicitingAcks = 0 } return payload } func (p *packetPacker) composeNextPacket(maxFrameSize protocol.ByteCount, ackAllowed bool) *payload { payload := &payload{frames: make([]ackhandler.Frame, 0, 1)} var hasDatagram bool if p.datagramQueue != nil { if datagram := p.datagramQueue.Get(); datagram != nil { payload.frames = append(payload.frames, ackhandler.Frame{ Frame: datagram, // set it to a no-op. Then we won't set the default callback, which would retransmit the frame. OnLost: func(wire.Frame) {}, }) payload.length += datagram.Length(p.version) hasDatagram = true } } var ack *wire.AckFrame hasData := p.framer.HasData() hasRetransmission := p.retransmissionQueue.HasAppData() // TODO: make sure ACKs are sent when a lot of DATAGRAMs are queued if !hasDatagram && ackAllowed { ack = p.acks.GetAckFrame(protocol.Encryption1RTT, !hasRetransmission && !hasData) if ack != nil { payload.ack = ack payload.length += ack.Length(p.version) } } if ack == nil && !hasData && !hasRetransmission { return payload } if hasRetransmission { for { remainingLen := maxFrameSize - payload.length if remainingLen < protocol.MinStreamFrameSize { break } f := p.retransmissionQueue.GetAppDataFrame(remainingLen) if f == nil { break } payload.frames = append(payload.frames, ackhandler.Frame{Frame: f}) payload.length += f.Length(p.version) } } if hasData { var lengthAdded protocol.ByteCount payload.frames, lengthAdded = p.framer.AppendControlFrames(payload.frames, maxFrameSize-payload.length) payload.length += lengthAdded payload.frames, lengthAdded = p.framer.AppendStreamFrames(payload.frames, maxFrameSize-payload.length) payload.length += lengthAdded } return payload } func (p *packetPacker) MaybePackProbePacket(encLevel protocol.EncryptionLevel) (*packedPacket, error) { var hdr *wire.ExtendedHeader var payload *payload var sealer sealer //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, payload = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), 0, protocol.EncryptionInitial) case protocol.EncryptionHandshake: var err error sealer, err = p.cryptoSetup.GetHandshakeSealer() if err != nil { return nil, err } hdr, payload = p.maybeGetCryptoPacket(p.maxPacketSize-protocol.ByteCount(sealer.Overhead()), 0, protocol.EncryptionHandshake) case protocol.Encryption1RTT: oneRTTSealer, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return nil, err } sealer = oneRTTSealer hdr = p.getShortHeader(oneRTTSealer.KeyPhase()) payload = p.maybeGetAppDataPacketWithEncLevel(p.maxPacketSize-protocol.ByteCount(sealer.Overhead())-hdr.GetLength(p.version), true) default: panic("unknown encryption level") } if payload == nil { return nil, nil } size := p.packetLength(hdr, payload) + protocol.ByteCount(sealer.Overhead()) var padding protocol.ByteCount if encLevel == protocol.EncryptionInitial { padding = p.initialPaddingLen(payload.frames, size) } buffer := getPacketBuffer() cont, err := p.appendPacket(buffer, hdr, payload, padding, encLevel, sealer, false) if err != nil { return nil, err } return &packedPacket{ buffer: buffer, packetContents: cont, }, nil } func (p *packetPacker) PackMTUProbePacket(ping ackhandler.Frame, size protocol.ByteCount) (*packedPacket, error) { payload := &payload{ frames: []ackhandler.Frame{ping}, length: ping.Length(p.version), } buffer := getPacketBuffer() sealer, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return nil, err } hdr := p.getShortHeader(sealer.KeyPhase()) padding := size - p.packetLength(hdr, payload) - protocol.ByteCount(sealer.Overhead()) contents, err := p.appendPacket(buffer, hdr, payload, padding, protocol.Encryption1RTT, sealer, true) if err != nil { return nil, err } contents.isMTUProbePacket = true return &packedPacket{ buffer: buffer, packetContents: contents, }, nil } func (p *packetPacker) getSealerAndHeader(encLevel protocol.EncryptionLevel) (sealer, *wire.ExtendedHeader, error) { switch encLevel { case protocol.EncryptionInitial: sealer, err := p.cryptoSetup.GetInitialSealer() if err != nil { return nil, nil, err } hdr := p.getLongHeader(protocol.EncryptionInitial) return sealer, hdr, nil case protocol.Encryption0RTT: sealer, err := p.cryptoSetup.Get0RTTSealer() if err != nil { return nil, nil, err } hdr := p.getLongHeader(protocol.Encryption0RTT) return sealer, hdr, nil case protocol.EncryptionHandshake: sealer, err := p.cryptoSetup.GetHandshakeSealer() if err != nil { return nil, nil, err } hdr := p.getLongHeader(protocol.EncryptionHandshake) return sealer, hdr, nil case protocol.Encryption1RTT: sealer, err := p.cryptoSetup.Get1RTTSealer() if err != nil { return nil, nil, err } hdr := p.getShortHeader(sealer.KeyPhase()) return sealer, hdr, nil default: return nil, nil, fmt.Errorf("unexpected encryption level: %s", encLevel) } } func (p *packetPacker) getShortHeader(kp protocol.KeyPhaseBit) *wire.ExtendedHeader { pn, pnLen := p.pnManager.PeekPacketNumber(protocol.Encryption1RTT) hdr := &wire.ExtendedHeader{} hdr.PacketNumber = pn hdr.PacketNumberLen = pnLen hdr.DestConnectionID = p.getDestConnID() hdr.KeyPhase = kp return hdr } func (p *packetPacker) getLongHeader(encLevel protocol.EncryptionLevel) *wire.ExtendedHeader { pn, pnLen := p.pnManager.PeekPacketNumber(encLevel) hdr := &wire.ExtendedHeader{ PacketNumber: pn, PacketNumberLen: pnLen, } hdr.IsLongHeader = true hdr.Version = p.version 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 } // writeSinglePacket packs a single packet. func (p *packetPacker) writeSinglePacket( hdr *wire.ExtendedHeader, payload *payload, encLevel protocol.EncryptionLevel, sealer sealer, ) (*packedPacket, error) { buffer := getPacketBuffer() var paddingLen protocol.ByteCount if encLevel == protocol.EncryptionInitial { paddingLen = p.initialPaddingLen(payload.frames, hdr.GetLength(p.version)+payload.length+protocol.ByteCount(sealer.Overhead())) } contents, err := p.appendPacket(buffer, hdr, payload, paddingLen, encLevel, sealer, false) if err != nil { return nil, err } return &packedPacket{ buffer: buffer, packetContents: contents, }, nil } func (p *packetPacker) appendPacket(buffer *packetBuffer, header *wire.ExtendedHeader, payload *payload, padding protocol.ByteCount, encLevel protocol.EncryptionLevel, sealer sealer, isMTUProbePacket bool) (*packetContents, error) { var paddingLen protocol.ByteCount pnLen := protocol.ByteCount(header.PacketNumberLen) if payload.length < 4-pnLen { paddingLen = 4 - pnLen - payload.length } paddingLen += padding if header.IsLongHeader { header.Length = pnLen + protocol.ByteCount(sealer.Overhead()) + payload.length + paddingLen } hdrOffset := buffer.Len() buf := bytes.NewBuffer(buffer.Data) if err := header.Write(buf, p.version); err != nil { return nil, err } payloadOffset := buf.Len() if payload.ack != nil { if err := payload.ack.Write(buf, p.version); err != nil { return nil, err } } if paddingLen > 0 { buf.Write(make([]byte, paddingLen)) } for _, frame := range payload.frames { if err := frame.Write(buf, p.version); err != nil { return nil, err } } if payloadSize := protocol.ByteCount(buf.Len()-payloadOffset) - paddingLen; payloadSize != payload.length { return nil, fmt.Errorf("PacketPacker BUG: payload size inconsistent (expected %d, got %d bytes)", payload.length, payloadSize) } if !isMTUProbePacket { if size := protocol.ByteCount(buf.Len() + sealer.Overhead()); size > p.maxPacketSize { return nil, fmt.Errorf("PacketPacker BUG: packet too large (%d bytes, allowed %d bytes)", size, p.maxPacketSize) } } raw := buffer.Data // encrypt the packet raw = raw[:buf.Len()] _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], header.PacketNumber, raw[hdrOffset:payloadOffset]) raw = raw[0 : buf.Len()+sealer.Overhead()] // apply header protection pnOffset := payloadOffset - int(header.PacketNumberLen) sealer.EncryptHeader(raw[pnOffset+4:pnOffset+4+16], &raw[hdrOffset], raw[pnOffset:payloadOffset]) buffer.Data = raw num := p.pnManager.PopPacketNumber(encLevel) if num != header.PacketNumber { return nil, errors.New("packetPacker BUG: Peeked and Popped packet numbers do not match") } return &packetContents{ header: header, ack: payload.ack, frames: payload.frames, length: buffer.Len() - hdrOffset, }, nil } func (p *packetPacker) SetToken(token []byte) { p.token = token } // When a higher MTU is discovered, use it. func (p *packetPacker) SetMaxPacketSize(s protocol.ByteCount) { p.maxPacketSize = s } // If the peer sets a max_packet_size that's smaller than the size we're currently using, // we need to reduce the size of packets we send. func (p *packetPacker) HandleTransportParameters(params *wire.TransportParameters) { if params.MaxUDPPayloadSize != 0 { p.maxPacketSize = utils.MinByteCount(p.maxPacketSize, params.MaxUDPPayloadSize) } } quic-go-0.25.0/packet_packer_test.go000066400000000000000000002411751417145451600173460ustar00rootroot00000000000000package quic import ( "bytes" "fmt" "math/rand" "net" "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/mocks" mockackhandler "github.com/lucas-clemente/quic-go/internal/mocks/ackhandler" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo/extensions/table" . "github.com/onsi/gomega" ) var _ = Describe("Packet packer", func() { const maxPacketSize protocol.ByteCount = 1357 const version = protocol.VersionTLS var ( packer *packetPacker retransmissionQueue *retransmissionQueue datagramQueue *datagramQueue framer *MockFrameSource ackFramer *MockAckFrameSource initialStream *MockCryptoStream handshakeStream *MockCryptoStream sealingManager *MockSealingManager pnManager *mockackhandler.MockSentPacketHandler ) connID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} parsePacket := func(data []byte) []*wire.ExtendedHeader { var hdrs []*wire.ExtendedHeader for len(data) > 0 { hdr, payload, rest, err := wire.ParsePacket(data, connID.Len()) Expect(err).ToNot(HaveOccurred()) r := bytes.NewReader(data) extHdr, err := hdr.ParseExtended(r, version) Expect(err).ToNot(HaveOccurred()) if extHdr.IsLongHeader { ExpectWithOffset(1, extHdr.Length).To(BeEquivalentTo(r.Len() - len(rest) + int(extHdr.PacketNumberLen))) ExpectWithOffset(1, extHdr.Length+protocol.ByteCount(extHdr.PacketNumberLen)).To(BeNumerically(">=", 4)) } else { ExpectWithOffset(1, len(payload)+int(extHdr.PacketNumberLen)).To(BeNumerically(">=", 4)) } data = rest hdrs = append(hdrs, extHdr) } return hdrs } appendFrames := func(fs, frames []ackhandler.Frame) ([]ackhandler.Frame, protocol.ByteCount) { var length protocol.ByteCount for _, f := range frames { length += f.Frame.Length(packer.version) } return append(fs, frames...), length } expectAppendStreamFrames := func(frames ...ackhandler.Frame) { framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any()).DoAndReturn(func(fs []ackhandler.Frame, _ protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { return appendFrames(fs, frames) }) } expectAppendControlFrames := func(frames ...ackhandler.Frame) { framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).DoAndReturn(func(fs []ackhandler.Frame, _ protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { return appendFrames(fs, frames) }) } BeforeEach(func() { rand.Seed(GinkgoRandomSeed()) retransmissionQueue = newRetransmissionQueue(version) mockSender := NewMockStreamSender(mockCtrl) mockSender.EXPECT().onHasStreamData(gomock.Any()).AnyTimes() initialStream = NewMockCryptoStream(mockCtrl) handshakeStream = NewMockCryptoStream(mockCtrl) framer = NewMockFrameSource(mockCtrl) ackFramer = NewMockAckFrameSource(mockCtrl) sealingManager = NewMockSealingManager(mockCtrl) pnManager = mockackhandler.NewMockSentPacketHandler(mockCtrl) datagramQueue = newDatagramQueue(func() {}, utils.DefaultLogger) packer = newPacketPacker( protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, func() protocol.ConnectionID { return connID }, initialStream, handshakeStream, pnManager, retransmissionQueue, &net.TCPAddr{}, sealingManager, framer, ackFramer, datagramQueue, protocol.PerspectiveServer, version, ) packer.version = version packer.maxPacketSize = maxPacketSize }) Context("determining the maximum packet size", func() { It("uses the minimum initial size, if it can't determine if the remote address is IPv4 or IPv6", func() { Expect(getMaxPacketSize(&net.TCPAddr{})).To(BeEquivalentTo(protocol.MinInitialPacketSize)) }) It("uses the maximum IPv4 packet size, if the remote address is IPv4", func() { addr := &net.UDPAddr{IP: net.IPv4(11, 12, 13, 14), Port: 1337} Expect(getMaxPacketSize(addr)).To(BeEquivalentTo(protocol.InitialPacketSizeIPv4)) }) It("uses the maximum IPv6 packet size, if the remote address is IPv6", func() { ip := net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") addr := &net.UDPAddr{IP: ip, Port: 1337} Expect(getMaxPacketSize(addr)).To(BeEquivalentTo(protocol.InitialPacketSizeIPv6)) }) }) 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) Expect(h.IsLongHeader).To(BeTrue()) Expect(h.PacketNumber).To(Equal(protocol.PacketNumber(0x42))) Expect(h.PacketNumberLen).To(Equal(protocol.PacketNumberLen3)) Expect(h.Version).To(Equal(packer.version)) }) It("sets source and destination connection ID", func() { pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} destConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1} packer.srcConnID = srcConnID packer.getDestConnID = func() protocol.ConnectionID { return destConnID } h := packer.getLongHeader(protocol.EncryptionHandshake) Expect(h.SrcConnectionID).To(Equal(srcConnID)) Expect(h.DestConnectionID).To(Equal(destConnID)) }) It("gets a short header", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x1337), protocol.PacketNumberLen4) h := packer.getShortHeader(protocol.KeyPhaseOne) Expect(h.IsLongHeader).To(BeFalse()) Expect(h.PacketNumber).To(Equal(protocol.PacketNumber(0x1337))) Expect(h.PacketNumberLen).To(Equal(protocol.PacketNumberLen4)) Expect(h.KeyPhase).To(Equal(protocol.KeyPhaseOne)) }) }) 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.Frame{Frame: f}) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].frames).To(Equal([]ackhandler.Frame{{Frame: 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() { ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true) ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, true) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true) p, err := packer.MaybePackAckPacket(false) 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.MaybePackAckPacket(false) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.ack).To(Equal(ack)) Expect(p.buffer.Len()).To(BeEquivalentTo(packer.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.MaybePackAckPacket(false) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.ack).To(Equal(ack)) parsePacket(p.buffer.Data) }) It("packs 1-RTT ACK-only 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) ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 10}}} ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, true).Return(ack) p, err := packer.MaybePackAckPacket(true) Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) Expect(p.ack).To(Equal(ack)) parsePacket(p.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() initialStream.EXPECT().HasData().AnyTimes() ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true).AnyTimes() handshakeStream.EXPECT().HasData().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) framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).DoAndReturn(func(frames []ackhandler.Frame, _ protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { Expect(frames).To(BeEmpty()) return append(frames, cf), cf.Length(packer.version) }) // TODO: check sizes framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any()).DoAndReturn(func(frames []ackhandler.Frame, _ protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { return frames, 0 }) p, err := packer.PackCoalescedPacket() Expect(p).ToNot(BeNil()) Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].header.Type).To(Equal(protocol.PacketType0RTT)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.Encryption0RTT)) Expect(p.packets[0].frames).To(Equal([]ackhandler.Frame{cf})) }) }) 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.NewCryptoError(0x42, "crypto error") quicErr.FrameType = 0x1234 p, err := packer.PackConnectionClose(quicErr) Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeHandshake)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.packets[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", }) Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].header.IsLongHeader).To(BeFalse()) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.packets[0].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", }) Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(3)) Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeInitial)) Expect(p.packets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.packets[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.packets[1].header.Type).To(Equal(protocol.PacketTypeHandshake)) Expect(p.packets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.packets[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.packets[2].header.IsLongHeader).To(BeFalse()) Expect(p.packets[2].header.PacketNumber).To(Equal(protocol.PacketNumber(3))) Expect(p.packets[2].frames).To(HaveLen(1)) Expect(p.packets[2].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.packets[2].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", }) Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(2)) Expect(p.buffer.Len()).To(BeNumerically("<", protocol.MinInitialPacketSize)) Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeHandshake)) Expect(p.packets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.packets[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.packets[1].header.IsLongHeader).To(BeFalse()) Expect(p.packets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.packets[1].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", }) Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(2)) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.packets[0].header.Type).To(Equal(protocol.PacketTypeInitial)) Expect(p.packets[0].header.PacketNumber).To(Equal(protocol.PacketNumber(1))) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := p.packets[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.packets[1].header.Type).To(Equal(protocol.PacketType0RTT)) Expect(p.packets[1].header.PacketNumber).To(Equal(protocol.PacketNumber(2))) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf = p.packets[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 := 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)) }) }) 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() p, err := packer.PackPacket() Expect(p).To(BeNil()) Expect(err).ToNot(HaveOccurred()) }) It("packs 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.Frame{Frame: f}) p, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) b := &bytes.Buffer{} f.Write(b, packer.version) Expect(p.frames).To(Equal([]ackhandler.Frame{{Frame: f}})) Expect(p.buffer.Data).To(ContainSubstring(b.String())) }) It("stores the encryption level a packet was sealed with", 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() expectAppendStreamFrames(ackhandler.Frame{Frame: &wire.StreamFrame{ StreamID: 5, Data: []byte("foobar"), }}) p, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) }) 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.PackPacket() Expect(err).NotTo(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.ack).To(Equal(ack)) }) It("packs control 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.ResetStreamFrame{}}, {Frame: &wire.MaxDataFrame{}}, } expectAppendControlFrames(frames...) expectAppendStreamFrames() p, err := packer.PackPacket() Expect(p).ToNot(BeNil()) Expect(err).ToNot(HaveOccurred()) Expect(p.frames).To(Equal(frames)) Expect(p.buffer.Len()).ToNot(BeZero()) }) It("packs DATAGRAM 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) f := &wire.DatagramFrame{ DataLenPresent: true, Data: []byte("foobar"), } done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) datagramQueue.AddAndWait(f) }() // make sure the DATAGRAM has actually been queued time.Sleep(scaleDuration(20 * time.Millisecond)) framer.EXPECT().HasData() p, err := packer.PackPacket() Expect(p).ToNot(BeNil()) Expect(err).ToNot(HaveOccurred()) Expect(p.frames).To(HaveLen(1)) Expect(p.frames[0].Frame).To(Equal(f)) Expect(p.buffer.Data).ToNot(BeEmpty()) 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()).DoAndReturn(func(fs []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { maxSize = maxLen return fs, 444 }), framer.EXPECT().AppendStreamFrames(gomock.Any(), gomock.Any()).Do(func(fs []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { Expect(maxLen).To(Equal(maxSize - 444)) return fs, 0 }), ) _, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) }) 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{}) handshakeStream.EXPECT().HasData() ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) packet, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.packets).To(HaveLen(1)) // 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, len(packer.getDestConnID())) Expect(err).ToNot(HaveOccurred()) r := bytes.NewReader(packet.buffer.Data) extHdr, err := hdr.ParseExtended(r, packer.version) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen1)) Expect(r.Len()).To(Equal(4 - 1 /* packet number length */ + sealer.Overhead())) // the first bytes of the payload should be a 2 PADDING frames... firstPayloadByte, err := r.ReadByte() Expect(err).ToNot(HaveOccurred()) Expect(firstPayloadByte).To(Equal(byte(0))) secondPayloadByte, err := r.ReadByte() Expect(err).ToNot(HaveOccurred()) Expect(secondPayloadByte).To(Equal(byte(0))) // ... followed by the PING frameParser := wire.NewFrameParser(false, packer.version) frame, err := frameParser.ParseNext(r, protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(r.Len()).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(packer.version)).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.Frame{Frame: f}) packet, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) // 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, len(packer.getDestConnID())) Expect(err).ToNot(HaveOccurred()) r := bytes.NewReader(packet.buffer.Data) extHdr, err := hdr.ParseExtended(r, packer.version) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumberLen).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, packer.version) frame, err := frameParser.ParseNext(r, protocol.Encryption1RTT) 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()).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.Frame{Frame: f1}, ackhandler.Frame{Frame: f2}, ackhandler.Frame{Frame: f3}) p, err := packer.PackPacket() Expect(p).ToNot(BeNil()) Expect(err).ToNot(HaveOccurred()) Expect(p.frames).To(HaveLen(3)) Expect(p.frames[0].Frame.(*wire.StreamFrame).Data).To(Equal([]byte("frame 1"))) Expect(p.frames[1].Frame.(*wire.StreamFrame).Data).To(Equal([]byte("frame 2"))) Expect(p.frames[2].Frame.(*wire.StreamFrame).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.PackPacket() Expect(p).ToNot(BeNil()) 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.PackPacket() Expect(p).ToNot(BeNil()) Expect(err).ToNot(HaveOccurred()) var hasPing bool for _, f := range p.frames { if _, ok := f.Frame.(*wire.PingFrame); ok { hasPing = true Expect(f.OnLost).ToNot(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.PackPacket() Expect(p).ToNot(BeNil()) 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) p, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).To(BeNil()) // 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.PackPacket() 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.OnLost).ToNot(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.PackPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) Expect(p.frames).ToNot(ContainElement(&wire.PingFrame{})) }) }) Context("handling transport parameters", func() { It("lowers the maximum packet size", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2).Times(2) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil).Times(2) framer.EXPECT().HasData().Return(true).Times(2) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Times(2) var initialMaxPacketSize protocol.ByteCount framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).Do(func(_ []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { initialMaxPacketSize = maxLen return nil, 0 }) expectAppendStreamFrames() _, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) // now reduce the maxPacketSize packer.HandleTransportParameters(&wire.TransportParameters{ MaxUDPPayloadSize: maxPacketSize - 10, }) framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).Do(func(_ []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { Expect(maxLen).To(Equal(initialMaxPacketSize - 10)) return nil, 0 }) expectAppendStreamFrames() _, err = packer.PackPacket() Expect(err).ToNot(HaveOccurred()) }) It("doesn't increase the max packet size", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2).Times(2) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil).Times(2) framer.EXPECT().HasData().Return(true).Times(2) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Times(2) var initialMaxPacketSize protocol.ByteCount framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).Do(func(_ []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { initialMaxPacketSize = maxLen return nil, 0 }) expectAppendStreamFrames() _, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) // now try to increase the maxPacketSize packer.HandleTransportParameters(&wire.TransportParameters{ MaxUDPPayloadSize: maxPacketSize + 10, }) framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).Do(func(_ []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { Expect(maxLen).To(Equal(initialMaxPacketSize)) return nil, 0 }) expectAppendStreamFrames() _, err = packer.PackPacket() Expect(err).ToNot(HaveOccurred()) }) }) Context("max packet size", func() { It("increases the max packet size", func() { pnManager.EXPECT().PeekPacketNumber(protocol.Encryption1RTT).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2).Times(2) sealingManager.EXPECT().Get1RTTSealer().Return(getSealer(), nil).Times(2) framer.EXPECT().HasData().Return(true).Times(2) ackFramer.EXPECT().GetAckFrame(protocol.Encryption1RTT, false).Times(2) var initialMaxPacketSize protocol.ByteCount framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).Do(func(_ []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { initialMaxPacketSize = maxLen return nil, 0 }) expectAppendStreamFrames() _, err := packer.PackPacket() Expect(err).ToNot(HaveOccurred()) // now reduce the maxPacketSize const packetSizeIncrease = 50 packer.SetMaxPacketSize(maxPacketSize + packetSizeIncrease) framer.EXPECT().AppendControlFrames(gomock.Any(), gomock.Any()).Do(func(_ []ackhandler.Frame, maxLen protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { Expect(maxLen).To(Equal(initialMaxPacketSize + packetSizeIncrease)) return nil, 0 }) expectAppendStreamFrames() _, err = packer.PackPacket() Expect(err).ToNot(HaveOccurred()) }) }) }) 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)) f := &wire.CryptoFrame{ Offset: 0x1337, Data: []byte("foobar"), } ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) handshakeStream.EXPECT().HasData().Return(true).AnyTimes() handshakeStream.EXPECT().PopCryptoFrame(gomock.Any()).Return(f) sealingManager.EXPECT().GetInitialSealer().Return(nil, handshake.ErrKeysDropped) sealingManager.EXPECT().GetHandshakeSealer().Return(getSealer(), nil) sealingManager.EXPECT().Get1RTTSealer().Return(nil, handshake.ErrKeysNotYetAvailable) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p).ToNot(BeNil()) 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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x42, Data: []byte("initial")} }) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) hdrs := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(1)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) }) It("packs a maximum size Handshake packet", func() { var f *wire.CryptoFrame 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) initialStream.EXPECT().HasData() handshakeStream.EXPECT().HasData().Return(true).Times(2) handshakeStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { f = &wire.CryptoFrame{Offset: 0x1337} f.Data = bytes.Repeat([]byte{'f'}, int(size-f.Length(packer.version)-1)) Expect(f.Length(packer.version)).To(Equal(size)) return f }) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].header.IsLongHeader).To(BeTrue()) Expect(p.buffer.Len()).To(BeEquivalentTo(packer.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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x42, Data: []byte("initial")} }) handshakeStream.EXPECT().HasData().Return(true).Times(2) handshakeStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x1337, Data: []byte("handshake")} }) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(packer.maxPacketSize)) Expect(p.packets).To(HaveLen(2)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.packets[1].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("handshake"))) hdrs := 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)) }) 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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x42, Data: []byte("initial")} }) handshakeStream.EXPECT().HasData() packer.retransmissionQueue.AddHandshake(&wire.PingFrame{}) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(packer.maxPacketSize)) Expect(p.packets).To(HaveLen(2)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.packets[1].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) hdrs := 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)) }) 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()) initialStream.EXPECT().HasData() handshakeStream.EXPECT().HasData() packer.retransmissionQueue.AddInitial(&wire.PingFrame{}) packer.retransmissionQueue.AddHandshake(&wire.PingFrame{}) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(packer.maxPacketSize)) Expect(p.packets).To(HaveLen(2)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(p.packets[1].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) hdrs := 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)) }) 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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x42, Data: []byte("initial")} }) expectAppendControlFrames() expectAppendStreamFrames() framer.EXPECT().HasData().Return(true) packer.retransmissionQueue.AddAppData(&wire.PingFrame{}) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeEquivalentTo(packer.maxPacketSize)) Expect(p.packets).To(HaveLen(2)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.packets[1].EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) hdrs := parsePacket(p.buffer.Data) Expect(hdrs).To(HaveLen(2)) Expect(hdrs[0].Type).To(Equal(protocol.PacketTypeInitial)) Expect(hdrs[1].IsLongHeader).To(BeFalse()) }) 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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x42, Data: []byte("initial")} }) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.Frame{Frame: &wire.StreamFrame{Data: []byte("foobar")}}) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.packets).To(HaveLen(2)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("initial"))) Expect(p.packets[1].EncryptionLevel()).To(Equal(protocol.Encryption0RTT)) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar"))) hdrs := 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)) }) 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.EXPECT().HasData().Return(true).Times(2) handshakeStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { return &wire.CryptoFrame{Offset: 0x1337, Data: []byte("handshake")} }) expectAppendControlFrames() expectAppendStreamFrames(ackhandler.Frame{Frame: &wire.StreamFrame{Data: []byte("foobar")}}) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically("<", 100)) Expect(p.packets).To(HaveLen(2)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(p.packets[0].frames).To(HaveLen(1)) Expect(p.packets[0].frames[0].Frame.(*wire.CryptoFrame).Data).To(Equal([]byte("handshake"))) Expect(p.packets[1].EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) Expect(p.packets[1].frames).To(HaveLen(1)) Expect(p.packets[1].frames[0].Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar"))) hdr, _, rest, err := wire.ParsePacket(p.buffer.Data, 0) Expect(err).ToNot(HaveOccurred()) Expect(hdr.Type).To(Equal(protocol.PacketTypeHandshake)) hdr, _, rest, err = wire.ParsePacket(rest, 0) Expect(err).ToNot(HaveOccurred()) Expect(hdr.IsLongHeader).To(BeFalse()) Expect(rest).To(BeEmpty()) }) 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.EXPECT().HasData().Return(true).Times(2) handshakeStream.EXPECT().PopCryptoFrame(gomock.Any()).DoAndReturn(func(size protocol.ByteCount) *wire.CryptoFrame { s := size - protocol.MinCoalescedPacketSize f := &wire.CryptoFrame{Offset: 0x1337} f.Data = bytes.Repeat([]byte{'f'}, int(s-f.Length(packer.version)-1)) Expect(f.Length(packer.version)).To(Equal(s)) return f }) p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(len(p.buffer.Data)).To(BeEquivalentTo(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{}) handshakeStream.EXPECT().HasData() ackFramer.EXPECT().GetAckFrame(protocol.EncryptionHandshake, false) packet, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.packets).To(HaveLen(1)) // 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, len(packer.getDestConnID())) Expect(err).ToNot(HaveOccurred()) r := bytes.NewReader(packet.buffer.Data) extHdr, err := hdr.ParseExtended(r, packer.version) Expect(err).ToNot(HaveOccurred()) Expect(extHdr.PacketNumberLen).To(Equal(protocol.PacketNumberLen1)) Expect(r.Len()).To(Equal(4 - 1 /* packet number length */ + sealer.Overhead())) // the first bytes of the payload should be a 2 PADDING frames... firstPayloadByte, err := r.ReadByte() Expect(err).ToNot(HaveOccurred()) Expect(firstPayloadByte).To(Equal(byte(0))) secondPayloadByte, err := r.ReadByte() Expect(err).ToNot(HaveOccurred()) Expect(secondPayloadByte).To(Equal(byte(0))) // ... followed by the PING frameParser := wire.NewFrameParser(false, packer.version) frame, err := frameParser.ParseNext(r, protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(r.Len()).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) initialStream.EXPECT().HasData() p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(p.packets[0].frames).To(Equal([]ackhandler.Frame{{Frame: f}})) Expect(p.packets[0].header.IsLongHeader).To(BeTrue()) }) 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) initialStream.EXPECT().HasData().Times(2) 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() Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[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) initialStream.EXPECT().HasData() ackFramer.EXPECT().GetAckFrame(protocol.EncryptionInitial, true) p, err := packer.PackCoalescedPacket() 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) initialStream.EXPECT().HasData() handshakeStream.EXPECT().HasData().Times(2) 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() Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[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) f := &wire.CryptoFrame{Data: []byte("foobar")} 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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).Return(f) packer.perspective = protocol.PerspectiveClient p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(p.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].header.Token).To(Equal(token)) Expect(p.packets[0].frames).To(HaveLen(1)) cf := p.packets[0].frames[0].Frame.(*wire.CryptoFrame) Expect(cf.Data).To(Equal([]byte("foobar"))) }) } It("adds an ACK frame", func() { f := &wire.CryptoFrame{Data: []byte("foobar")} 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.EXPECT().HasData().Return(true).Times(2) initialStream.EXPECT().PopCryptoFrame(gomock.Any()).Return(f) packer.version = protocol.VersionTLS packer.perspective = protocol.PerspectiveClient p, err := packer.PackCoalescedPacket() Expect(err).ToNot(HaveOccurred()) Expect(p.packets).To(HaveLen(1)) Expect(p.packets[0].ack).To(Equal(ack)) Expect(p.packets[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) initialStream.EXPECT().HasData() pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) packet, err := packer.MaybePackProbePacket(protocol.EncryptionInitial) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(packet.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(packet.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(Equal(f)) parsePacket(packet.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) initialStream.EXPECT().HasData() pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen1) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(0x42)) packet, err := packer.MaybePackProbePacket(protocol.EncryptionInitial) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionInitial)) Expect(packet.buffer.Len()).To(BeNumerically(">=", protocol.MinInitialPacketSize)) Expect(packet.buffer.Len()).To(BeEquivalentTo(maxPacketSize)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(BeAssignableToTypeOf(&wire.PingFrame{})) parsePacket(packet.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) handshakeStream.EXPECT().HasData() pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) packet, err := packer.MaybePackProbePacket(protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.EncryptionLevel()).To(Equal(protocol.EncryptionHandshake)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(Equal(f)) parsePacket(packet.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) handshakeStream.EXPECT().HasData() pnManager.EXPECT().PeekPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42), protocol.PacketNumberLen2) pnManager.EXPECT().PopPacketNumber(protocol.EncryptionHandshake).Return(protocol.PacketNumber(0x42)) packet, err := packer.MaybePackProbePacket(protocol.EncryptionHandshake) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) 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(packet.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.Frame{Frame: f}) packet, err := packer.MaybePackProbePacket(protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[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()).DoAndReturn(func(fs []ackhandler.Frame, maxSize protocol.ByteCount) ([]ackhandler.Frame, protocol.ByteCount) { sf, split := f.MaybeSplitOffFrame(maxSize, packer.version) Expect(split).To(BeTrue()) return append(fs, ackhandler.Frame{Frame: sf}), sf.Length(packer.version) }) packet, err := packer.MaybePackProbePacket(protocol.Encryption1RTT) Expect(err).ToNot(HaveOccurred()) Expect(packet).ToNot(BeNil()) Expect(packet.EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) Expect(packet.frames).To(HaveLen(1)) Expect(packet.frames[0].Frame).To(BeAssignableToTypeOf(&wire.StreamFrame{})) 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) 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, err := packer.PackMTUProbePacket(ping, probePacketSize) Expect(err).ToNot(HaveOccurred()) Expect(p.length).To(BeEquivalentTo(probePacketSize)) Expect(p.header.IsLongHeader).To(BeFalse()) Expect(p.header.PacketNumber).To(Equal(protocol.PacketNumber(0x43))) Expect(p.EncryptionLevel()).To(Equal(protocol.Encryption1RTT)) Expect(p.buffer.Data).To(HaveLen(int(probePacketSize))) Expect(p.packetContents.isMTUProbePacket).To(BeTrue()) }) }) }) }) var _ = Describe("Converting to AckHandler packets", func() { It("convert a packet", func() { packet := &packetContents{ header: &wire.ExtendedHeader{Header: wire.Header{}}, frames: []ackhandler.Frame{{Frame: &wire.MaxDataFrame{}}, {Frame: &wire.PingFrame{}}}, ack: &wire.AckFrame{AckRanges: []wire.AckRange{{Largest: 100, Smallest: 80}}}, length: 42, } t := time.Now() p := packet.ToAckHandlerPacket(t, nil) Expect(p.Length).To(Equal(protocol.ByteCount(42))) Expect(p.Frames).To(Equal(packet.frames)) Expect(p.LargestAcked).To(Equal(protocol.PacketNumber(100))) Expect(p.SendTime).To(Equal(t)) }) It("sets the LargestAcked to invalid, if the packet doesn't have an ACK frame", func() { packet := &packetContents{ header: &wire.ExtendedHeader{Header: wire.Header{}}, frames: []ackhandler.Frame{{Frame: &wire.MaxDataFrame{}}, {Frame: &wire.PingFrame{}}}, } p := packet.ToAckHandlerPacket(time.Now(), nil) Expect(p.LargestAcked).To(Equal(protocol.InvalidPacketNumber)) }) It("marks MTU probe packets", func() { packet := &packetContents{ header: &wire.ExtendedHeader{Header: wire.Header{}}, isMTUProbePacket: true, } Expect(packet.ToAckHandlerPacket(time.Now(), nil).IsPathMTUProbePacket).To(BeTrue()) }) DescribeTable( "doesn't overwrite the OnLost callback, if it is set", func(hdr wire.Header) { var pingLost bool packet := &packetContents{ header: &wire.ExtendedHeader{Header: hdr}, frames: []ackhandler.Frame{ {Frame: &wire.MaxDataFrame{}}, {Frame: &wire.PingFrame{}, OnLost: func(wire.Frame) { pingLost = true }}, }, } p := packet.ToAckHandlerPacket(time.Now(), newRetransmissionQueue(protocol.VersionTLS)) Expect(p.Frames).To(HaveLen(2)) Expect(p.Frames[0].OnLost).ToNot(BeNil()) p.Frames[1].OnLost(nil) Expect(pingLost).To(BeTrue()) }, Entry(protocol.EncryptionInitial.String(), wire.Header{IsLongHeader: true, Type: protocol.PacketTypeInitial}), Entry(protocol.EncryptionHandshake.String(), wire.Header{IsLongHeader: true, Type: protocol.PacketTypeHandshake}), Entry(protocol.Encryption0RTT.String(), wire.Header{IsLongHeader: true, Type: protocol.PacketType0RTT}), Entry(protocol.Encryption1RTT.String(), wire.Header{}), ) }) quic-go-0.25.0/packet_unpacker.go000066400000000000000000000143241417145451600166440ustar00rootroot00000000000000package quic import ( "bytes" "fmt" "time" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/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 { packetNumber protocol.PacketNumber // the decoded packet number hdr *wire.ExtendedHeader encryptionLevel protocol.EncryptionLevel data []byte } // The packetUnpacker unpacks QUIC packets. type packetUnpacker struct { cs handshake.CryptoSetup version protocol.VersionNumber } var _ unpacker = &packetUnpacker{} func newPacketUnpacker(cs handshake.CryptoSetup, version protocol.VersionNumber) unpacker { return &packetUnpacker{ cs: cs, version: version, } } // 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) Unpack(hdr *wire.Header, rcvTime time.Time, 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: if hdr.IsLongHeader { return nil, fmt.Errorf("unknown packet type: %s", hdr.Type) } encLevel = protocol.Encryption1RTT opener, err := u.cs.Get1RTTOpener() if err != nil { return nil, err } extHdr, decrypted, err = u.unpackShortHeaderPacket(opener, hdr, rcvTime, data) if err != nil { return nil, err } } return &unpackedPacket{ hdr: extHdr, packetNumber: extHdr.PacketNumber, encryptionLevel: encLevel, data: decrypted, }, nil } func (u *packetUnpacker) unpackLongHeaderPacket(opener handshake.LongHeaderOpener, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, []byte, error) { extHdr, parseErr := u.unpackHeader(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, hdr *wire.Header, rcvTime time.Time, data []byte, ) (*wire.ExtendedHeader, []byte, error) { extHdr, parseErr := u.unpackHeader(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 } extHdr.PacketNumber = opener.DecodePacketNumber(extHdr.PacketNumber, extHdr.PacketNumberLen) extHdrLen := extHdr.ParsedLen() decrypted, err := opener.Open(data[extHdrLen:extHdrLen], data[extHdrLen:], rcvTime, extHdr.PacketNumber, extHdr.KeyPhase, data[:extHdrLen]) if err != nil { return nil, nil, err } if parseErr != nil { return nil, nil, parseErr } return extHdr, decrypted, nil } // The error is either nil, a wire.ErrInvalidReservedBits or of type headerParseError. func (u *packetUnpacker) unpackHeader(hd headerDecryptor, hdr *wire.Header, data []byte) (*wire.ExtendedHeader, error) { extHdr, err := unpackHeader(hd, hdr, data, u.version) if err != nil && err != wire.ErrInvalidReservedBits { return nil, &headerParseError{err: err} } return extHdr, err } func unpackHeader(hd headerDecryptor, hdr *wire.Header, data []byte, version protocol.VersionNumber) (*wire.ExtendedHeader, error) { r := bytes.NewReader(data) 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(r, version) 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 } quic-go-0.25.0/packet_unpacker_test.go000066400000000000000000000273371417145451600177130ustar00rootroot00000000000000package quic import ( "bytes" "errors" "time" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Packet Unpacker", func() { const version = protocol.VersionTLS var ( unpacker *packetUnpacker cs *mocks.MockCryptoSetup connID = protocol.ConnectionID{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.") ) getHeader := func(extHdr *wire.ExtendedHeader) (*wire.Header, []byte) { buf := &bytes.Buffer{} ExpectWithOffset(1, extHdr.Write(buf, version)).To(Succeed()) hdrLen := buf.Len() if extHdr.Length > protocol.ByteCount(extHdr.PacketNumberLen) { buf.Write(make([]byte, int(extHdr.Length)-int(extHdr.PacketNumberLen))) } hdr, _, _, err := wire.ParsePacket(buf.Bytes(), connID.Len()) ExpectWithOffset(1, err).ToNot(HaveOccurred()) return hdr, buf.Bytes()[:hdrLen] } BeforeEach(func() { cs = mocks.NewMockCryptoSetup(mockCtrl) unpacker = newPacketUnpacker(cs, version).(*packetUnpacker) }) It("errors when the packet is too small to obtain the header decryption sample, for long headers", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: connID, Version: version, }, PacketNumber: 1337, PacketNumberLen: protocol.PacketNumberLen2, } hdr, hdrRaw := getHeader(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.Unpack(hdr, time.Now(), 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() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: connID}, PacketNumber: 1337, PacketNumberLen: protocol.PacketNumberLen2, } hdr, hdrRaw := getHeader(extHdr) data := append(hdrRaw, make([]byte, 2 /* fill up packet number */ +15 /* need 16 bytes */)...) opener := mocks.NewMockShortHeaderOpener(mockCtrl) cs.EXPECT().Get1RTTOpener().Return(opener, nil) _, err := unpacker.Unpack(hdr, 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{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Length: 3 + 6, // packet number len + payload DestConnectionID: connID, Version: version, }, PacketNumber: 2, PacketNumberLen: 3, } hdr, hdrRaw := getHeader(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.Unpack(hdr, time.Now(), 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{ IsLongHeader: true, Type: protocol.PacketType0RTT, Length: 3 + 6, // packet number len + payload DestConnectionID: connID, Version: version, }, PacketNumber: 20, PacketNumberLen: 2, } hdr, hdrRaw := getHeader(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.Unpack(hdr, time.Now(), 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() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: connID}, KeyPhase: protocol.KeyPhaseOne, PacketNumber: 99, PacketNumberLen: protocol.PacketNumberLen4, } hdr, hdrRaw := getHeader(extHdr) 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), ) packet, err := unpacker.Unpack(hdr, now, append(hdrRaw, payload...)) Expect(err).ToNot(HaveOccurred()) Expect(packet.encryptionLevel).To(Equal(protocol.Encryption1RTT)) Expect(packet.data).To(Equal([]byte("decrypted"))) }) It("returns the error when getting the sealer fails", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: connID}, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getHeader(extHdr) cs.EXPECT().Get1RTTOpener().Return(nil, handshake.ErrKeysNotYetAvailable) _, err := unpacker.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) Expect(err).To(MatchError(handshake.ErrKeysNotYetAvailable)) }) It("returns the error when unpacking fails", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Length: 3, // packet number len DestConnectionID: connID, Version: version, }, PacketNumber: 2, PacketNumberLen: 3, } hdr, hdrRaw := getHeader(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.Unpack(hdr, time.Now(), 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{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: connID, Version: version, }, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getHeader(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.Unpack(hdr, time.Now(), 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() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: connID}, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getHeader(extHdr) 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.Unpack(hdr, 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", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: connID}, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getHeader(extHdr) 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.Unpack(hdr, time.Now(), append(hdrRaw, payload...)) Expect(err).To(MatchError(handshake.ErrDecryptionFailed)) }) It("decrypts the header", func() { extHdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Length: 3, // packet number len DestConnectionID: connID, Version: version, }, PacketNumber: 0x1337, PacketNumberLen: 2, } hdr, hdrRaw := getHeader(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.Unpack(hdr, time.Now(), data) Expect(err).ToNot(HaveOccurred()) Expect(packet.packetNumber).To(Equal(protocol.PacketNumber(0x7331))) }) }) quic-go-0.25.0/qlog/000077500000000000000000000000001417145451600141145ustar00rootroot00000000000000quic-go-0.25.0/qlog/event.go000066400000000000000000000440361417145451600155730ustar00rootroot00000000000000package qlog import ( "errors" "fmt" "net" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/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 []versionNumber 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 utils.IsIPv4(e.SrcAddr.IP) { 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", connectionID(e.SrcConnectionID).String()) enc.StringKey("dst_cid", connectionID(e.DestConnectionID).String()) } type eventVersionNegotiated struct { clientVersions, serverVersions []versionNumber chosenVersion versionNumber } 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("owner", ownerRemote.String()) enc.StringKey("trigger", "version_negotiation") } } type eventPacketSent struct { Header packetHeader Length logging.ByteCount PayloadLength logging.ByteCount Frames frames IsCoalesced bool 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) enc.StringKeyOmitEmpty("trigger", e.Trigger) } type eventPacketReceived struct { Header packetHeader Length logging.ByteCount PayloadLength logging.ByteCount Frames frames 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) 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 packetHeader SupportedVersions []versionNumber } 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 eventPacketBuffered struct { PacketType logging.PacketType } 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}) enc.StringKey("trigger", "keys_unavailable") } type eventPacketDropped struct { PacketType logging.PacketType PacketSize protocol.ByteCount 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}) 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 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 Generation 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("generation", uint64(e.Generation)) } } type eventKeyRetired struct { KeyType keyType Generation protocol.KeyPhase } func (e eventKeyRetired) Category() category { return categorySecurity } func (e eventKeyRetired) Name() string { return "key_retired" } func (e eventKeyRetired) IsNil() bool { return false } func (e eventKeyRetired) 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("generation", uint64(e.Generation)) } } 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", connectionID(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", connectionID(*e.RetrySourceConnectionID).String()) } } enc.StringKey("initial_source_connection_id", connectionID(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 net.IP PortV4, PortV6 uint16 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.String()) enc.Uint16Key("port_v4", a.PortV4) enc.StringKey("ip_v6", a.IPv6.String()) enc.Uint16Key("port_v6", a.PortV6) enc.StringKey("connection_id", connectionID(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 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) } quic-go-0.25.0/qlog/event_test.go000066400000000000000000000023321417145451600166230ustar00rootroot00000000000000package qlog import ( "bytes" "encoding/json" "time" "github.com/francoispqt/gojay" . "github.com/onsi/ginkgo" . "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")) }) }) quic-go-0.25.0/qlog/frame.go000066400000000000000000000161121417145451600155360ustar00rootroot00000000000000package qlog import ( "fmt" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/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", connectionID(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)) } quic-go-0.25.0/qlog/frame_test.go000066400000000000000000000177021417145451600166030ustar00rootroot00000000000000package qlog import ( "bytes" "encoding/json" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/logging" "github.com/francoispqt/gojay" . "github.com/onsi/ginkgo" . "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.ConnectionID{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, }, ) }) }) quic-go-0.25.0/qlog/packet_header.go000066400000000000000000000065141417145451600172300ustar00rootroot00000000000000package qlog import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/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. type packetHeader struct { PacketType logging.PacketType KeyPhaseBit logging.KeyPhaseBit PacketNumber logging.PacketNumber Version logging.VersionNumber SrcConnectionID logging.ConnectionID DestConnectionID logging.ConnectionID Token *token } func transformHeader(hdr *wire.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 transformExtendedHeader(hdr *wire.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 && h.PacketType != logging.PacketTypeVersionNegotiation { enc.Int64Key("packet_number", int64(h.PacketNumber)) } if h.Version != 0 { enc.StringKey("version", versionNumber(h.Version).String()) } if h.PacketType != logging.PacketType1RTT { enc.IntKey("scil", h.SrcConnectionID.Len()) if h.SrcConnectionID.Len() > 0 { enc.StringKey("scid", connectionID(h.SrcConnectionID).String()) } } enc.IntKey("dcil", h.DestConnectionID.Len()) if h.DestConnectionID.Len() > 0 { enc.StringKey("dcid", connectionID(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) } } // a minimal header that only outputs the packet type type packetHeaderWithType struct { PacketType logging.PacketType } func (h packetHeaderWithType) IsNil() bool { return false } func (h packetHeaderWithType) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("packet_type", packetType(h.PacketType).String()) } // 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)) } quic-go-0.25.0/qlog/packet_header_test.go000066400000000000000000000115141417145451600202630ustar00rootroot00000000000000package qlog import ( "bytes" "encoding/json" "github.com/francoispqt/gojay" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "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(transformExtendedHeader(hdr))).To(Succeed()) data := buf.Bytes() ExpectWithOffset(1, json.Valid(data)).To(BeTrue()) checkEncoding(data, expected) } It("marshals a header for a 1-RTT packet", func() { check( &wire.ExtendedHeader{ PacketNumber: 42, KeyPhase: protocol.KeyPhaseZero, }, map[string]interface{}{ "packet_type": "1RTT", "packet_number": 42, "dcil": 0, "key_phase_bit": "0", }, ) }) It("marshals a header with a payload length", func() { check( &wire.ExtendedHeader{ PacketNumber: 42, Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Length: 123, Version: protocol.VersionNumber(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{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Length: 123, Version: protocol.VersionNumber(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{ IsLongHeader: true, Type: protocol.PacketTypeRetry, SrcConnectionID: protocol.ConnectionID{0x11, 0x22, 0x33, 0x44}, Version: protocol.VersionNumber(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{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Version: protocol.VersionNumber(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{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, SrcConnectionID: protocol.ConnectionID{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}, Version: protocol.VersionNumber(0xdecafbad), }, }, map[string]interface{}{ "packet_type": "handshake", "packet_number": 42, "dcil": 0, "scil": 16, "scid": "00112233445566778899aabbccddeeff", "version": "decafbad", }, ) }) It("marshals a 1-RTT header with a destination connection ID", func() { check( &wire.ExtendedHeader{ PacketNumber: 42, Header: wire.Header{DestConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}}, KeyPhase: protocol.KeyPhaseOne, }, map[string]interface{}{ "packet_type": "1RTT", "packet_number": 42, "dcil": 4, "dcid": "deadbeef", "key_phase_bit": "1", }, ) }) }) }) quic-go-0.25.0/qlog/qlog.go000066400000000000000000000332251417145451600154120ustar00rootroot00000000000000package qlog import ( "bytes" "context" "fmt" "io" "log" "net" "runtime/debug" "sync" "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" "github.com/francoispqt/gojay" ) // Setting of this only works when quic-go is used as a library. // When building a binary from this repository, the version can be set using the following go build flag: // -ldflags="-X github.com/lucas-clemente/quic-go/qlog.quicGoVersion=foobar" var quicGoVersion = "(devel)" func init() { if quicGoVersion != "(devel)" { // variable set by ldflags return } info, ok := debug.ReadBuildInfo() if !ok { // no build info available. This happens when quic-go is not used as a library. return } for _, d := range info.Deps { if d.Path == "github.com/lucas-clemente/quic-go" { quicGoVersion = d.Version if d.Replace != nil { if len(d.Replace.Version) > 0 { quicGoVersion = d.Version } else { quicGoVersion += " (replaced)" } } break } } } const eventChanSize = 50 type tracer struct { getLogWriter func(p logging.Perspective, connectionID []byte) io.WriteCloser } var _ logging.Tracer = &tracer{} // NewTracer creates a new qlog tracer. func NewTracer(getLogWriter func(p logging.Perspective, connectionID []byte) io.WriteCloser) logging.Tracer { return &tracer{getLogWriter: getLogWriter} } func (t *tracer) TracerForConnection(_ context.Context, p logging.Perspective, odcid protocol.ConnectionID) logging.ConnectionTracer { if w := t.getLogWriter(p, odcid.Bytes()); w != nil { return NewConnectionTracer(w, p, odcid) } return nil } func (t *tracer) SentPacket(net.Addr, *logging.Header, protocol.ByteCount, []logging.Frame) {} func (t *tracer) DroppedPacket(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { } type connectionTracer struct { mutex sync.Mutex w io.WriteCloser odcid protocol.ConnectionID perspective protocol.Perspective referenceTime time.Time events chan event encodeErr error runStopped chan struct{} lastMetrics *metrics } var _ logging.ConnectionTracer = &connectionTracer{} // NewConnectionTracer creates a new tracer to record a qlog for a connection. func NewConnectionTracer(w io.WriteCloser, p protocol.Perspective, odcid protocol.ConnectionID) logging.ConnectionTracer { t := &connectionTracer{ w: w, perspective: p, odcid: odcid, runStopped: make(chan struct{}), events: make(chan event, eventChanSize), referenceTime: time.Now(), } go t.run() return t } func (t *connectionTracer) run() { defer close(t.runStopped) buf := &bytes.Buffer{} enc := gojay.NewEncoder(buf) tl := &topLevel{ trace: trace{ VantagePoint: vantagePoint{Type: t.perspective}, CommonFields: commonFields{ ODCID: connectionID(t.odcid), GroupID: connectionID(t.odcid), ReferenceTime: t.referenceTime, }, }, } if err := enc.Encode(tl); err != nil { panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) } if err := buf.WriteByte('\n'); err != nil { panic(fmt.Sprintf("qlog encoding into a bytes.Buffer failed: %s", err)) } if _, err := t.w.Write(buf.Bytes()); err != nil { t.encodeErr = err } enc = gojay.NewEncoder(t.w) for ev := range t.events { if t.encodeErr != nil { // if encoding failed, just continue draining the event channel continue } if err := enc.Encode(ev); err != nil { t.encodeErr = err continue } if _, err := t.w.Write([]byte{'\n'}); err != nil { t.encodeErr = err } } } func (t *connectionTracer) Close() { if err := t.export(); err != nil { log.Printf("exporting qlog failed: %s\n", err) } } // export writes a qlog. func (t *connectionTracer) export() error { close(t.events) <-t.runStopped if t.encodeErr != nil { return t.encodeErr } return t.w.Close() } func (t *connectionTracer) recordEvent(eventTime time.Time, details eventDetails) { t.events <- event{ RelativeTime: eventTime.Sub(t.referenceTime), eventDetails: details, } } 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.mutex.Lock() t.recordEvent(time.Now(), &eventConnectionStarted{ SrcAddr: localAddr, DestAddr: remoteAddr, SrcConnectionID: srcConnID, DestConnectionID: destConnID, }) t.mutex.Unlock() } func (t *connectionTracer) NegotiatedVersion(chosen logging.VersionNumber, client, server []logging.VersionNumber) { var clientVersions, serverVersions []versionNumber if len(client) > 0 { clientVersions = make([]versionNumber, len(client)) for i, v := range client { clientVersions[i] = versionNumber(v) } } if len(server) > 0 { serverVersions = make([]versionNumber, len(server)) for i, v := range server { serverVersions[i] = versionNumber(v) } } t.mutex.Lock() t.recordEvent(time.Now(), &eventVersionNegotiated{ clientVersions: clientVersions, serverVersions: serverVersions, chosenVersion: versionNumber(chosen), }) t.mutex.Unlock() } func (t *connectionTracer) ClosedConnection(e error) { t.mutex.Lock() t.recordEvent(time.Now(), &eventConnectionClosed{e: e}) t.mutex.Unlock() } 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.mutex.Lock() t.recordEvent(time.Now(), ev) t.mutex.Unlock() } 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.mutex.Lock() t.recordEvent(time.Now(), ev) t.mutex.Unlock() } func (t *connectionTracer) toTransportParameters(tp *wire.TransportParameters) *eventTransportParameters { var pa *preferredAddress if tp.PreferredAddress != nil { pa = &preferredAddress{ IPv4: tp.PreferredAddress.IPv4, PortV4: tp.PreferredAddress.IPv4Port, IPv6: tp.PreferredAddress.IPv6, PortV6: tp.PreferredAddress.IPv6Port, 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) SentPacket(hdr *wire.ExtendedHeader, packetSize logging.ByteCount, 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}) } header := *transformExtendedHeader(hdr) t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketSent{ Header: header, Length: packetSize, PayloadLength: hdr.Length, Frames: fs, }) t.mutex.Unlock() } func (t *connectionTracer) ReceivedPacket(hdr *wire.ExtendedHeader, packetSize logging.ByteCount, frames []logging.Frame) { fs := make([]frame, len(frames)) for i, f := range frames { fs[i] = frame{Frame: f} } header := *transformExtendedHeader(hdr) t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketReceived{ Header: header, Length: packetSize, PayloadLength: hdr.Length, Frames: fs, }) t.mutex.Unlock() } func (t *connectionTracer) ReceivedRetry(hdr *wire.Header) { t.mutex.Lock() t.recordEvent(time.Now(), &eventRetryReceived{ Header: *transformHeader(hdr), }) t.mutex.Unlock() } func (t *connectionTracer) ReceivedVersionNegotiationPacket(hdr *wire.Header, versions []logging.VersionNumber) { ver := make([]versionNumber, len(versions)) for i, v := range versions { ver[i] = versionNumber(v) } t.mutex.Lock() t.recordEvent(time.Now(), &eventVersionNegotiationReceived{ Header: *transformHeader(hdr), SupportedVersions: ver, }) t.mutex.Unlock() } func (t *connectionTracer) BufferedPacket(pt logging.PacketType) { t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketBuffered{PacketType: pt}) t.mutex.Unlock() } func (t *connectionTracer) DroppedPacket(pt logging.PacketType, size protocol.ByteCount, reason logging.PacketDropReason) { t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketDropped{ PacketType: pt, PacketSize: size, Trigger: packetDropReason(reason), }) t.mutex.Unlock() } 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.mutex.Lock() t.recordEvent(time.Now(), &eventMetricsUpdated{ Last: t.lastMetrics, Current: m, }) t.lastMetrics = m t.mutex.Unlock() } func (t *connectionTracer) AcknowledgedPacket(protocol.EncryptionLevel, protocol.PacketNumber) {} func (t *connectionTracer) LostPacket(encLevel protocol.EncryptionLevel, pn protocol.PacketNumber, lossReason logging.PacketLossReason) { t.mutex.Lock() t.recordEvent(time.Now(), &eventPacketLost{ PacketType: getPacketTypeFromEncryptionLevel(encLevel), PacketNumber: pn, Trigger: packetLossReason(lossReason), }) t.mutex.Unlock() } func (t *connectionTracer) UpdatedCongestionState(state logging.CongestionState) { t.mutex.Lock() t.recordEvent(time.Now(), &eventCongestionStateUpdated{state: congestionState(state)}) t.mutex.Unlock() } func (t *connectionTracer) UpdatedPTOCount(value uint32) { t.mutex.Lock() t.recordEvent(time.Now(), &eventUpdatedPTO{Value: value}) t.mutex.Unlock() } func (t *connectionTracer) UpdatedKeyFromTLS(encLevel protocol.EncryptionLevel, pers protocol.Perspective) { t.mutex.Lock() t.recordEvent(time.Now(), &eventKeyUpdated{ Trigger: keyUpdateTLS, KeyType: encLevelToKeyType(encLevel, pers), }) t.mutex.Unlock() } func (t *connectionTracer) UpdatedKey(generation protocol.KeyPhase, remote bool) { trigger := keyUpdateLocal if remote { trigger = keyUpdateRemote } t.mutex.Lock() now := time.Now() t.recordEvent(now, &eventKeyUpdated{ Trigger: trigger, KeyType: keyTypeClient1RTT, Generation: generation, }) t.recordEvent(now, &eventKeyUpdated{ Trigger: trigger, KeyType: keyTypeServer1RTT, Generation: generation, }) t.mutex.Unlock() } func (t *connectionTracer) DroppedEncryptionLevel(encLevel protocol.EncryptionLevel) { t.mutex.Lock() now := time.Now() if encLevel == protocol.Encryption0RTT { t.recordEvent(now, &eventKeyRetired{KeyType: encLevelToKeyType(encLevel, t.perspective)}) } else { t.recordEvent(now, &eventKeyRetired{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveServer)}) t.recordEvent(now, &eventKeyRetired{KeyType: encLevelToKeyType(encLevel, protocol.PerspectiveClient)}) } t.mutex.Unlock() } func (t *connectionTracer) DroppedKey(generation protocol.KeyPhase) { t.mutex.Lock() now := time.Now() t.recordEvent(now, &eventKeyRetired{ KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveServer), Generation: generation, }) t.recordEvent(now, &eventKeyRetired{ KeyType: encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveClient), Generation: generation, }) t.mutex.Unlock() } func (t *connectionTracer) SetLossTimer(tt logging.TimerType, encLevel protocol.EncryptionLevel, timeout time.Time) { t.mutex.Lock() now := time.Now() t.recordEvent(now, &eventLossTimerSet{ TimerType: timerType(tt), EncLevel: encLevel, Delta: timeout.Sub(now), }) t.mutex.Unlock() } func (t *connectionTracer) LossTimerExpired(tt logging.TimerType, encLevel protocol.EncryptionLevel) { t.mutex.Lock() t.recordEvent(time.Now(), &eventLossTimerExpired{ TimerType: timerType(tt), EncLevel: encLevel, }) t.mutex.Unlock() } func (t *connectionTracer) LossTimerCanceled() { t.mutex.Lock() t.recordEvent(time.Now(), &eventLossTimerCanceled{}) t.mutex.Unlock() } func (t *connectionTracer) Debug(name, msg string) { t.mutex.Lock() t.recordEvent(time.Now(), &eventGeneric{ name: name, msg: msg, }) t.mutex.Unlock() } quic-go-0.25.0/qlog/qlog_suite_test.go000066400000000000000000000024421417145451600176570ustar00rootroot00000000000000package qlog import ( "encoding/json" "os" "strconv" "testing" "time" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestQlog(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "qlog Suite") } //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 } func checkEncoding(data []byte, expected map[string]interface{}) { // unmarshal the data m := make(map[string]interface{}) ExpectWithOffset(1, json.Unmarshal(data, &m)).To(Succeed()) ExpectWithOffset(1, m).To(HaveLen(len(expected))) for key, value := range expected { switch v := value.(type) { case bool, string, map[string]interface{}: ExpectWithOffset(1, m).To(HaveKeyWithValue(key, v)) case int: ExpectWithOffset(1, m).To(HaveKeyWithValue(key, float64(v))) case [][]float64: // used in the ACK frame ExpectWithOffset(1, m).To(HaveKey(key)) for i, l := range v { for j, s := range l { ExpectWithOffset(1, m[key].([]interface{})[i].([]interface{})[j].(float64)).To(Equal(s)) } } default: Fail("unexpected type") } } } quic-go-0.25.0/qlog/qlog_test.go000066400000000000000000001071041417145451600164470ustar00rootroot00000000000000package qlog import ( "bytes" "context" "encoding/json" "errors" "io" "log" "net" "os" "time" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "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 limitedWriter struct { io.WriteCloser N int written int } func (w *limitedWriter) Write(p []byte) (int, error) { if w.written+len(p) > w.N { return 0, errors.New("writer full") } n, err := w.WriteCloser.Write(p) w.written += n return n, err } type entry struct { Time time.Time Name string Event map[string]interface{} } var _ = Describe("Tracing", func() { Context("tracer", func() { It("returns nil when there's no io.WriteCloser", func() { t := NewTracer(func(logging.Perspective, []byte) io.WriteCloser { return nil }) Expect(t.TracerForConnection(context.Background(), logging.PerspectiveClient, logging.ConnectionID{1, 2, 3, 4})).To(BeNil()) }) }) It("stops writing when encountering an error", func() { buf := &bytes.Buffer{} t := NewConnectionTracer( &limitedWriter{WriteCloser: nopWriteCloser(buf), N: 250}, protocol.PerspectiveServer, protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, ) for i := uint32(0); i < 1000; i++ { t.UpdatedPTOCount(i) } b := &bytes.Buffer{} log.SetOutput(b) defer log.SetOutput(os.Stdout) t.Close() Expect(b.String()).To(ContainSubstring("writer full")) }) Context("connection tracer", func() { var ( tracer logging.ConnectionTracer buf *bytes.Buffer ) BeforeEach(func() { buf = &bytes.Buffer{} t := NewTracer(func(logging.Perspective, []byte) io.WriteCloser { return nopWriteCloser(buf) }) tracer = t.TracerForConnection(context.Background(), logging.PerspectiveServer, logging.ConnectionID{0xde, 0xad, 0xbe, 0xef}) }) It("exports a trace that has the right metadata", func() { tracer.Close() m := make(map[string]interface{}) Expect(json.Unmarshal(buf.Bytes(), &m)).To(Succeed()) Expect(m).To(HaveKeyWithValue("qlog_version", "draft-02")) 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() { exportAndParse := func() []entry { tracer.Close() m := make(map[string]interface{}) line, err := buf.ReadBytes('\n') Expect(err).ToNot(HaveOccurred()) Expect(json.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(json.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 } exportAndParseSingle := func() entry { entries := exportAndParse() Expect(entries).To(HaveLen(1)) return entries[0] } 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.ConnectionID{1, 2, 3, 4}, protocol.ConnectionID{5, 6, 7, 8}, ) entry := exportAndParseSingle() 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) entry := exportAndParseSingle() 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.VersionNumber{1, 2, 3}, []logging.VersionNumber{4, 5, 6}) entry := exportAndParseSingle() 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{}) entry := exportAndParseSingle() 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{}) entry := exportAndParseSingle() 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}, }) entry := exportAndParseSingle() 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{}) entry := exportAndParseSingle() 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", "remote")) Expect(ev).To(HaveKeyWithValue("trigger", "version_negotiation")) }) It("records application errors", func() { tracer.ClosedConnection(&quic.ApplicationError{ Remote: true, ErrorCode: 1337, ErrorMessage: "foobar", }) entry := exportAndParseSingle() 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", }) entry := exportAndParseSingle() 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() { 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.ConnectionID{0xde, 0xad, 0xc0, 0xde}, InitialSourceConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, RetrySourceConnectionID: &protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, ActiveConnectionIDLimit: 7, MaxDatagramFrameSize: protocol.InvalidByteCount, }) entry := exportAndParseSingle() 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.ConnectionID{0xde, 0xad, 0xc0, 0xde}, ActiveConnectionIDLimit: 7, }) entry := exportAndParseSingle() 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}, }) entry := exportAndParseSingle() 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: net.IPv4(12, 34, 56, 78), IPv4Port: 123, IPv6: net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, IPv6Port: 456, ConnectionID: protocol.ConnectionID{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}, }, }) entry := exportAndParseSingle() 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, }) entry := exportAndParseSingle() 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{}) entry := exportAndParseSingle() 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, }) entry := exportAndParseSingle() 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 packet, without an ACK", func() { tracer.SentPacket( &logging.ExtendedHeader{ Header: logging.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{4, 3, 2, 1}, Length: 1337, Version: protocol.VersionTLS, }, PacketNumber: 1337, }, 987, nil, []logging.Frame{ &logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}, &logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}, }, ) entry := exportAndParseSingle() 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")) 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 packet, without an ACK", func() { tracer.SentPacket( &logging.ExtendedHeader{ Header: logging.Header{DestConnectionID: protocol.ConnectionID{1, 2, 3, 4}}, PacketNumber: 1337, }, 123, &logging.AckFrame{AckRanges: []logging.AckRange{{Smallest: 1, Largest: 10}}}, []logging.Frame{&logging.MaxDataFrame{MaximumData: 987}}, ) entry := exportAndParseSingle() 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")) 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 packet", func() { tracer.ReceivedPacket( &logging.ExtendedHeader{ Header: logging.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{4, 3, 2, 1}, Token: []byte{0xde, 0xad, 0xbe, 0xef}, Length: 1234, Version: protocol.VersionTLS, }, PacketNumber: 1337, }, 789, []logging.Frame{ &logging.MaxStreamDataFrame{StreamID: 42, MaximumStreamData: 987}, &logging.StreamFrame{StreamID: 123, Offset: 1234, Length: 6, Fin: true}, }, ) entry := exportAndParseSingle() 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(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 Retry packet", func() { tracer.ReceivedRetry( &logging.Header{ IsLongHeader: true, Type: protocol.PacketTypeRetry, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{4, 3, 2, 1}, Token: []byte{0xde, 0xad, 0xbe, 0xef}, Version: protocol.VersionTLS, }, ) entry := exportAndParseSingle() 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( &logging.Header{ IsLongHeader: true, Type: protocol.PacketTypeRetry, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, SrcConnectionID: protocol.ConnectionID{4, 3, 2, 1}, }, []protocol.VersionNumber{0xdeadbeef, 0xdecafbad}, ) entry := exportAndParseSingle() 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(HaveKey("dcid")) Expect(header).To(HaveKey("scid")) }) It("records buffered packets", func() { tracer.BufferedPacket(logging.PacketTypeHandshake) entry := exportAndParseSingle() 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(HaveKeyWithValue("trigger", "keys_unavailable")) }) It("records dropped packets", func() { tracer.DroppedPacket(logging.PacketTypeHandshake, 1337, logging.PacketDropPayloadDecryptError) entry := exportAndParseSingle() 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", "handshake")) Expect(ev).To(HaveKeyWithValue("trigger", "payload_decrypt_error")) }) 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, ) entry := exportAndParseSingle() 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, ) entries := exportAndParse() 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) entry := exportAndParseSingle() 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 congestion state updates", func() { tracer.UpdatedCongestionState(logging.CongestionStateCongestionAvoidance) entry := exportAndParseSingle() 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) entry := exportAndParseSingle() 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) entry := exportAndParseSingle() 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("generation")) 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) entry := exportAndParseSingle() 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("generation", float64(0))) Expect(ev).ToNot(HaveKey("old")) Expect(ev).ToNot(HaveKey("new")) }) It("records QUIC key updates", func() { tracer.UpdatedKey(1337, true) entries := exportAndParse() 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("generation", 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) entries := exportAndParse() 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_retired")) 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) entries := exportAndParse() 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_retired")) 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) entries := exportAndParse() 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_retired")) ev := entry.Event Expect(ev).To(HaveKeyWithValue("generation", 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) entry := exportAndParseSingle() 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) entry := exportAndParseSingle() 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() entry := exportAndParseSingle() 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 a generic event", func() { tracer.Debug("foo", "bar") entry := exportAndParseSingle() 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")) }) }) }) }) quic-go-0.25.0/qlog/trace.go000066400000000000000000000031601417145451600155410ustar00rootroot00000000000000package qlog import ( "time" "github.com/francoispqt/gojay" "github.com/lucas-clemente/quic-go/internal/protocol" ) type topLevel struct { trace trace } func (topLevel) IsNil() bool { return false } func (l topLevel) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("qlog_format", "NDJSON") enc.StringKey("qlog_version", "draft-02") enc.StringKeyOmitEmpty("title", "quic-go qlog") enc.StringKey("code_version", quicGoVersion) enc.ObjectKey("trace", l.trace) } type vantagePoint struct { Name string Type protocol.Perspective } func (p vantagePoint) IsNil() bool { return false } func (p vantagePoint) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKeyOmitEmpty("name", p.Name) switch p.Type { case protocol.PerspectiveClient: enc.StringKey("type", "client") case protocol.PerspectiveServer: enc.StringKey("type", "server") } } type commonFields struct { ODCID connectionID GroupID connectionID ProtocolType string ReferenceTime time.Time } func (f commonFields) MarshalJSONObject(enc *gojay.Encoder) { enc.StringKey("ODCID", f.ODCID.String()) enc.StringKey("group_id", f.ODCID.String()) enc.StringKeyOmitEmpty("protocol_type", f.ProtocolType) enc.Float64Key("reference_time", float64(f.ReferenceTime.UnixNano())/1e6) enc.StringKey("time_format", "relative") } func (f commonFields) IsNil() bool { return false } type trace struct { VantagePoint vantagePoint CommonFields commonFields } func (trace) IsNil() bool { return false } func (t trace) MarshalJSONObject(enc *gojay.Encoder) { enc.ObjectKey("vantage_point", t.VantagePoint) enc.ObjectKey("common_fields", t.CommonFields) } quic-go-0.25.0/qlog/types.go000066400000000000000000000163021417145451600156110ustar00rootroot00000000000000package qlog import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/logging" ) type owner uint8 const ( ownerLocal owner = iota ownerRemote ) func (o owner) String() string { switch o { case ownerLocal: return "local" case ownerRemote: return "remote" default: return "unknown owner" } } type streamType protocol.StreamType func (s streamType) String() string { switch protocol.StreamType(s) { case protocol.StreamTypeUni: return "unidirectional" case protocol.StreamTypeBidi: return "bidirectional" default: return "unknown stream type" } } type connectionID protocol.ConnectionID func (c connectionID) String() string { return fmt.Sprintf("%x", []byte(c)) } // category is the qlog event category. type category uint8 const ( categoryConnectivity category = iota categoryTransport categorySecurity categoryRecovery ) func (c category) String() string { switch c { case categoryConnectivity: return "connectivity" case categoryTransport: return "transport" case categorySecurity: return "security" case categoryRecovery: return "recovery" default: return "unknown category" } } type versionNumber protocol.VersionNumber func (v versionNumber) String() string { return fmt.Sprintf("%x", uint32(v)) } func (packetHeader) IsNil() bool { return false } func encLevelToPacketNumberSpace(encLevel protocol.EncryptionLevel) string { switch encLevel { case protocol.EncryptionInitial: return "initial" case protocol.EncryptionHandshake: return "handshake" case protocol.Encryption0RTT, protocol.Encryption1RTT: return "application_data" default: return "unknown encryption level" } } type keyType uint8 const ( keyTypeServerInitial keyType = 1 + iota keyTypeClientInitial keyTypeServerHandshake keyTypeClientHandshake keyTypeServer0RTT keyTypeClient0RTT keyTypeServer1RTT keyTypeClient1RTT ) func encLevelToKeyType(encLevel protocol.EncryptionLevel, pers protocol.Perspective) keyType { if pers == protocol.PerspectiveServer { switch encLevel { case protocol.EncryptionInitial: return keyTypeServerInitial case protocol.EncryptionHandshake: return keyTypeServerHandshake case protocol.Encryption0RTT: return keyTypeServer0RTT case protocol.Encryption1RTT: return keyTypeServer1RTT default: return 0 } } switch encLevel { case protocol.EncryptionInitial: return keyTypeClientInitial case protocol.EncryptionHandshake: return keyTypeClientHandshake case protocol.Encryption0RTT: return keyTypeClient0RTT case protocol.Encryption1RTT: return keyTypeClient1RTT default: return 0 } } func (t keyType) String() string { switch t { case keyTypeServerInitial: return "server_initial_secret" case keyTypeClientInitial: return "client_initial_secret" case keyTypeServerHandshake: return "server_handshake_secret" case keyTypeClientHandshake: return "client_handshake_secret" case keyTypeServer0RTT: return "server_0rtt_secret" case keyTypeClient0RTT: return "client_0rtt_secret" case keyTypeServer1RTT: return "server_1rtt_secret" case keyTypeClient1RTT: return "client_1rtt_secret" default: return "unknown key type" } } type keyUpdateTrigger uint8 const ( keyUpdateTLS keyUpdateTrigger = iota keyUpdateRemote keyUpdateLocal ) func (t keyUpdateTrigger) String() string { switch t { case keyUpdateTLS: return "tls" case keyUpdateRemote: return "remote_update" case keyUpdateLocal: return "local_update" default: return "unknown key update trigger" } } type transportError uint64 func (e transportError) String() string { switch qerr.TransportErrorCode(e) { case qerr.NoError: return "no_error" case qerr.InternalError: return "internal_error" case qerr.ConnectionRefused: return "connection_refused" case qerr.FlowControlError: return "flow_control_error" case qerr.StreamLimitError: return "stream_limit_error" case qerr.StreamStateError: return "stream_state_error" case qerr.FinalSizeError: return "final_size_error" case qerr.FrameEncodingError: return "frame_encoding_error" case qerr.TransportParameterError: return "transport_parameter_error" case qerr.ConnectionIDLimitError: return "connection_id_limit_error" case qerr.ProtocolViolation: return "protocol_violation" case qerr.InvalidToken: return "invalid_token" case qerr.ApplicationErrorErrorCode: return "application_error" case qerr.CryptoBufferExceeded: return "crypto_buffer_exceeded" case qerr.KeyUpdateError: return "key_update_error" case qerr.AEADLimitReached: return "aead_limit_reached" case qerr.NoViablePathError: return "no_viable_path" default: return "" } } type packetType logging.PacketType func (t packetType) String() string { switch logging.PacketType(t) { case logging.PacketTypeInitial: return "initial" case logging.PacketTypeHandshake: return "handshake" case logging.PacketTypeRetry: return "retry" case logging.PacketType0RTT: return "0RTT" case logging.PacketTypeVersionNegotiation: return "version_negotiation" case logging.PacketTypeStatelessReset: return "stateless_reset" case logging.PacketType1RTT: return "1RTT" case logging.PacketTypeNotDetermined: return "" default: return "unknown packet type" } } type packetLossReason logging.PacketLossReason func (r packetLossReason) String() string { switch logging.PacketLossReason(r) { case logging.PacketLossReorderingThreshold: return "reordering_threshold" case logging.PacketLossTimeThreshold: return "time_threshold" default: return "unknown loss reason" } } type packetDropReason logging.PacketDropReason func (r packetDropReason) String() string { switch logging.PacketDropReason(r) { case logging.PacketDropKeyUnavailable: return "key_unavailable" case logging.PacketDropUnknownConnectionID: return "unknown_connection_id" case logging.PacketDropHeaderParseError: return "header_parse_error" case logging.PacketDropPayloadDecryptError: return "payload_decrypt_error" case logging.PacketDropProtocolViolation: return "protocol_violation" case logging.PacketDropDOSPrevention: return "dos_prevention" case logging.PacketDropUnsupportedVersion: return "unsupported_version" case logging.PacketDropUnexpectedPacket: return "unexpected_packet" case logging.PacketDropUnexpectedSourceConnectionID: return "unexpected_source_connection_id" case logging.PacketDropUnexpectedVersion: return "unexpected_version" case logging.PacketDropDuplicate: return "duplicate" default: return "unknown packet drop reason" } } type timerType logging.TimerType func (t timerType) String() string { switch logging.TimerType(t) { case logging.TimerTypeACK: return "ack" case logging.TimerTypePTO: return "pto" default: return "unknown timer type" } } type congestionState logging.CongestionState func (s congestionState) String() string { switch logging.CongestionState(s) { case logging.CongestionStateSlowStart: return "slow_start" case logging.CongestionStateCongestionAvoidance: return "congestion_avoidance" case logging.CongestionStateRecovery: return "recovery" case logging.CongestionStateApplicationLimited: return "application_limited" default: return "unknown congestion state" } } quic-go-0.25.0/qlog/types_test.go000066400000000000000000000162541417145451600166560ustar00rootroot00000000000000package qlog import ( "go/ast" "go/parser" gotoken "go/token" "path" "runtime" "strconv" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/logging" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Types", func() { It("has a string representation for the owner", func() { Expect(ownerLocal.String()).To(Equal("local")) Expect(ownerRemote.String()).To(Equal("remote")) }) It("has a string representation for the category", func() { Expect(categoryConnectivity.String()).To(Equal("connectivity")) Expect(categoryTransport.String()).To(Equal("transport")) Expect(categoryRecovery.String()).To(Equal("recovery")) Expect(categorySecurity.String()).To(Equal("security")) }) It("has a string representation for the packet type", func() { Expect(packetType(logging.PacketTypeInitial).String()).To(Equal("initial")) Expect(packetType(logging.PacketTypeHandshake).String()).To(Equal("handshake")) Expect(packetType(logging.PacketType0RTT).String()).To(Equal("0RTT")) Expect(packetType(logging.PacketType1RTT).String()).To(Equal("1RTT")) Expect(packetType(logging.PacketTypeStatelessReset).String()).To(Equal("stateless_reset")) Expect(packetType(logging.PacketTypeRetry).String()).To(Equal("retry")) Expect(packetType(logging.PacketTypeVersionNegotiation).String()).To(Equal("version_negotiation")) Expect(packetType(logging.PacketTypeNotDetermined).String()).To(BeEmpty()) }) It("has a string representation for the packet drop reason", func() { Expect(packetDropReason(logging.PacketDropKeyUnavailable).String()).To(Equal("key_unavailable")) Expect(packetDropReason(logging.PacketDropUnknownConnectionID).String()).To(Equal("unknown_connection_id")) Expect(packetDropReason(logging.PacketDropHeaderParseError).String()).To(Equal("header_parse_error")) Expect(packetDropReason(logging.PacketDropPayloadDecryptError).String()).To(Equal("payload_decrypt_error")) Expect(packetDropReason(logging.PacketDropProtocolViolation).String()).To(Equal("protocol_violation")) Expect(packetDropReason(logging.PacketDropDOSPrevention).String()).To(Equal("dos_prevention")) Expect(packetDropReason(logging.PacketDropUnsupportedVersion).String()).To(Equal("unsupported_version")) Expect(packetDropReason(logging.PacketDropUnexpectedPacket).String()).To(Equal("unexpected_packet")) Expect(packetDropReason(logging.PacketDropUnexpectedSourceConnectionID).String()).To(Equal("unexpected_source_connection_id")) Expect(packetDropReason(logging.PacketDropUnexpectedVersion).String()).To(Equal("unexpected_version")) }) It("has a string representation for the timer type", func() { Expect(timerType(logging.TimerTypeACK).String()).To(Equal("ack")) Expect(timerType(logging.TimerTypePTO).String()).To(Equal("pto")) }) It("has a string representation for the key type", func() { Expect(encLevelToKeyType(protocol.EncryptionInitial, protocol.PerspectiveClient).String()).To(Equal("client_initial_secret")) Expect(encLevelToKeyType(protocol.EncryptionInitial, protocol.PerspectiveServer).String()).To(Equal("server_initial_secret")) Expect(encLevelToKeyType(protocol.EncryptionHandshake, protocol.PerspectiveClient).String()).To(Equal("client_handshake_secret")) Expect(encLevelToKeyType(protocol.EncryptionHandshake, protocol.PerspectiveServer).String()).To(Equal("server_handshake_secret")) Expect(encLevelToKeyType(protocol.Encryption0RTT, protocol.PerspectiveClient).String()).To(Equal("client_0rtt_secret")) Expect(encLevelToKeyType(protocol.Encryption0RTT, protocol.PerspectiveServer).String()).To(Equal("server_0rtt_secret")) Expect(encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveClient).String()).To(Equal("client_1rtt_secret")) Expect(encLevelToKeyType(protocol.Encryption1RTT, protocol.PerspectiveServer).String()).To(Equal("server_1rtt_secret")) }) It("has a string representation for the key update trigger", func() { Expect(keyUpdateTLS.String()).To(Equal("tls")) Expect(keyUpdateRemote.String()).To(Equal("remote_update")) Expect(keyUpdateLocal.String()).To(Equal("local_update")) }) It("tells the packet number space from the encryption level", func() { Expect(encLevelToPacketNumberSpace(protocol.EncryptionInitial)).To(Equal("initial")) Expect(encLevelToPacketNumberSpace(protocol.EncryptionHandshake)).To(Equal("handshake")) Expect(encLevelToPacketNumberSpace(protocol.Encryption0RTT)).To(Equal("application_data")) Expect(encLevelToPacketNumberSpace(protocol.Encryption1RTT)).To(Equal("application_data")) }) Context("transport errors", 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), "../internal/qerr/error_codes.go") fileAst, err := parser.ParseFile(gotoken.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(transportError(val).String()).ToNot(BeEmpty()) } }) It("has a string representation for transport errors", func() { Expect(transportError(qerr.NoError).String()).To(Equal("no_error")) Expect(transportError(qerr.InternalError).String()).To(Equal("internal_error")) Expect(transportError(qerr.ConnectionRefused).String()).To(Equal("connection_refused")) Expect(transportError(qerr.FlowControlError).String()).To(Equal("flow_control_error")) Expect(transportError(qerr.StreamLimitError).String()).To(Equal("stream_limit_error")) Expect(transportError(qerr.StreamStateError).String()).To(Equal("stream_state_error")) Expect(transportError(qerr.FrameEncodingError).String()).To(Equal("frame_encoding_error")) Expect(transportError(qerr.ConnectionIDLimitError).String()).To(Equal("connection_id_limit_error")) Expect(transportError(qerr.ProtocolViolation).String()).To(Equal("protocol_violation")) Expect(transportError(qerr.InvalidToken).String()).To(Equal("invalid_token")) Expect(transportError(qerr.ApplicationErrorErrorCode).String()).To(Equal("application_error")) Expect(transportError(qerr.CryptoBufferExceeded).String()).To(Equal("crypto_buffer_exceeded")) Expect(transportError(qerr.NoViablePathError).String()).To(Equal("no_viable_path")) Expect(transportError(1337).String()).To(BeEmpty()) }) }) It("has a string representation for congestion state updates", func() { Expect(congestionState(logging.CongestionStateSlowStart).String()).To(Equal("slow_start")) Expect(congestionState(logging.CongestionStateCongestionAvoidance).String()).To(Equal("congestion_avoidance")) Expect(congestionState(logging.CongestionStateApplicationLimited).String()).To(Equal("application_limited")) Expect(congestionState(logging.CongestionStateRecovery).String()).To(Equal("recovery")) }) }) quic-go-0.25.0/quic_suite_test.go000066400000000000000000000010141417145451600167060ustar00rootroot00000000000000package quic import ( "io/ioutil" "log" "sync" "testing" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestQuicGo(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "QUIC Suite") } var mockCtrl *gomock.Controller var _ = BeforeEach(func() { mockCtrl = gomock.NewController(GinkgoT()) // reset the sync.Once connMuxerOnce = *new(sync.Once) }) var _ = BeforeSuite(func() { log.SetOutput(ioutil.Discard) }) var _ = AfterEach(func() { mockCtrl.Finish() }) quic-go-0.25.0/quicvarint/000077500000000000000000000000001417145451600153375ustar00rootroot00000000000000quic-go-0.25.0/quicvarint/io.go000066400000000000000000000023741417145451600163030ustar00rootroot00000000000000package quicvarint import ( "bytes" "io" ) // Reader implements both the io.ByteReader and io.Reader interfaces. type Reader interface { io.ByteReader io.Reader } var _ Reader = &bytes.Reader{} type byteReader struct { io.Reader } var _ Reader = &byteReader{} // NewReader returns a Reader for r. // If r already implements both io.ByteReader and io.Reader, NewReader returns r. // Otherwise, r is wrapped to add the missing interfaces. func NewReader(r io.Reader) Reader { if r, ok := r.(Reader); ok { return r } return &byteReader{r} } func (r *byteReader) ReadByte() (byte, error) { var b [1]byte _, err := r.Reader.Read(b[:]) return b[0], err } // Writer implements both the io.ByteWriter and io.Writer interfaces. type Writer interface { io.ByteWriter io.Writer } var _ Writer = &bytes.Buffer{} type byteWriter struct { io.Writer } var _ Writer = &byteWriter{} // NewWriter returns a Writer for w. // If r already implements both io.ByteWriter and io.Writer, NewWriter returns w. // Otherwise, w is wrapped to add the missing interfaces. func NewWriter(w io.Writer) Writer { if w, ok := w.(Writer); ok { return w } return &byteWriter{w} } func (w *byteWriter) WriteByte(c byte) error { _, err := w.Writer.Write([]byte{c}) return err } quic-go-0.25.0/quicvarint/io_test.go000066400000000000000000000027251417145451600173420ustar00rootroot00000000000000package quicvarint import ( "bytes" "io" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type nopReader struct{} func (r *nopReader) Read(_ []byte) (int, error) { return 0, io.ErrUnexpectedEOF } var _ io.Reader = &nopReader{} type nopWriter struct{} func (r *nopWriter) Write(_ []byte) (int, error) { return 0, io.ErrShortBuffer } var _ io.Writer = &nopWriter{} var _ = Describe("Varint I/O", func() { Context("Reader", func() { Context("NewReader", func() { It("passes through a Reader unchanged", func() { b := bytes.NewReader([]byte{0}) r := NewReader(b) Expect(r).To(Equal(b)) }) It("wraps an io.Reader", func() { n := &nopReader{} r := NewReader(n) Expect(r).ToNot(Equal(n)) }) }) It("returns an error when reading from an underlying io.Reader fails", func() { r := NewReader(&nopReader{}) val, err := r.ReadByte() Expect(err).To(Equal(io.ErrUnexpectedEOF)) Expect(val).To(Equal(byte(0))) }) }) Context("Writer", func() { Context("NewWriter", func() { It("passes through a Writer unchanged", func() { b := &bytes.Buffer{} w := NewWriter(b) Expect(w).To(Equal(b)) }) It("wraps an io.Writer", func() { n := &nopWriter{} w := NewWriter(n) Expect(w).ToNot(Equal(n)) }) }) It("returns an error when writing to an underlying io.Writer fails", func() { w := NewWriter(&nopWriter{}) err := w.WriteByte(0) Expect(err).To(Equal(io.ErrShortBuffer)) }) }) }) quic-go-0.25.0/quicvarint/quicvarint_suite_test.go000066400000000000000000000003141417145451600223210ustar00rootroot00000000000000package quicvarint_test import ( "testing" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func TestQuicVarint(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "QUIC Varint Suite") } quic-go-0.25.0/quicvarint/varint.go000066400000000000000000000062131417145451600171730ustar00rootroot00000000000000package quicvarint import ( "fmt" "io" "github.com/lucas-clemente/quic-go/internal/protocol" ) // taken from the QUIC draft const ( // Min is the minimum value allowed for a QUIC varint. Min = 0 // Max is the maximum allowed value for a QUIC varint (2^62-1). Max = maxVarInt8 maxVarInt1 = 63 maxVarInt2 = 16383 maxVarInt4 = 1073741823 maxVarInt8 = 4611686018427387903 ) // Read reads a number in the QUIC varint format from r. func Read(r io.ByteReader) (uint64, error) { firstByte, err := r.ReadByte() if err != nil { return 0, err } // the first two bits of the first byte encode the length len := 1 << ((firstByte & 0xc0) >> 6) b1 := firstByte & (0xff - 0xc0) if len == 1 { return uint64(b1), nil } b2, err := r.ReadByte() if err != nil { return 0, err } if len == 2 { return uint64(b2) + uint64(b1)<<8, nil } b3, err := r.ReadByte() if err != nil { return 0, err } b4, err := r.ReadByte() if err != nil { return 0, err } if len == 4 { return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil } b5, err := r.ReadByte() if err != nil { return 0, err } b6, err := r.ReadByte() if err != nil { return 0, err } b7, err := r.ReadByte() if err != nil { return 0, err } b8, err := r.ReadByte() if err != nil { return 0, err } return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil } // Write writes i in the QUIC varint format to w. func Write(w Writer, i uint64) { if i <= maxVarInt1 { w.WriteByte(uint8(i)) } else if i <= maxVarInt2 { w.Write([]byte{uint8(i>>8) | 0x40, uint8(i)}) } else if i <= maxVarInt4 { w.Write([]byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}) } else if i <= maxVarInt8 { w.Write([]byte{ uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32), uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i), }) } else { panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i)) } } // WriteWithLen writes i in the QUIC varint format with the desired length to w. func WriteWithLen(w Writer, i uint64, length protocol.ByteCount) { if length != 1 && length != 2 && length != 4 && length != 8 { panic("invalid varint length") } l := Len(i) if l == length { Write(w, i) return } if l > length { panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length)) } if length == 2 { w.WriteByte(0b01000000) } else if length == 4 { w.WriteByte(0b10000000) } else if length == 8 { w.WriteByte(0b11000000) } for j := protocol.ByteCount(1); j < length-l; j++ { w.WriteByte(0) } for j := protocol.ByteCount(0); j < l; j++ { w.WriteByte(uint8(i >> (8 * (l - 1 - j)))) } } // Len determines the number of bytes that will be needed to write the number i. func Len(i uint64) protocol.ByteCount { if i <= maxVarInt1 { return 1 } if i <= maxVarInt2 { return 2 } if i <= maxVarInt4 { return 4 } if i <= maxVarInt8 { return 8 } // Don't use a fmt.Sprintf here to format the error message. // The function would then exceed the inlining budget. panic(struct { message string num uint64 }{"value doesn't fit into 62 bits: ", i}) } quic-go-0.25.0/quicvarint/varint_test.go000066400000000000000000000146441417145451600202410ustar00rootroot00000000000000package quicvarint import ( "bytes" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Varint encoding / decoding", func() { Context("limits", func() { Specify("Min == 0", func() { Expect(Min).To(Equal(0)) }) Specify("Max == 2^62-1", func() { Expect(uint64(Max)).To(Equal(uint64(1<<62 - 1))) }) }) Context("decoding", func() { It("reads a 1 byte number", func() { b := bytes.NewReader([]byte{0b00011001}) val, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(25))) Expect(b.Len()).To(BeZero()) }) It("reads a number that is encoded too long", func() { b := bytes.NewReader([]byte{0b01000000, 0x25}) val, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(37))) Expect(b.Len()).To(BeZero()) }) It("reads a 2 byte number", func() { b := bytes.NewReader([]byte{0b01111011, 0xbd}) val, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(15293))) Expect(b.Len()).To(BeZero()) }) It("reads a 4 byte number", func() { b := bytes.NewReader([]byte{0b10011101, 0x7f, 0x3e, 0x7d}) val, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(494878333))) Expect(b.Len()).To(BeZero()) }) It("reads an 8 byte number", func() { b := bytes.NewReader([]byte{0b11000010, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c}) val, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(val).To(Equal(uint64(151288809941952652))) Expect(b.Len()).To(BeZero()) }) }) Context("encoding", func() { Context("with minimal length", func() { It("writes a 1 byte number", func() { b := &bytes.Buffer{} Write(b, 37) Expect(b.Bytes()).To(Equal([]byte{0x25})) }) It("writes the maximum 1 byte number in 1 byte", func() { b := &bytes.Buffer{} Write(b, maxVarInt1) Expect(b.Bytes()).To(Equal([]byte{0b00111111})) }) It("writes the minimum 2 byte number in 2 bytes", func() { b := &bytes.Buffer{} Write(b, maxVarInt1+1) Expect(b.Bytes()).To(Equal([]byte{0x40, maxVarInt1 + 1})) }) It("writes a 2 byte number", func() { b := &bytes.Buffer{} Write(b, 15293) Expect(b.Bytes()).To(Equal([]byte{0b01000000 ^ 0x3b, 0xbd})) }) It("writes the maximum 2 byte number in 2 bytes", func() { b := &bytes.Buffer{} Write(b, maxVarInt2) Expect(b.Bytes()).To(Equal([]byte{0b01111111, 0xff})) }) It("writes the minimum 4 byte number in 4 bytes", func() { b := &bytes.Buffer{} Write(b, maxVarInt2+1) Expect(b.Len()).To(Equal(4)) num, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(num).To(Equal(uint64(maxVarInt2 + 1))) }) It("writes a 4 byte number", func() { b := &bytes.Buffer{} Write(b, 494878333) Expect(b.Bytes()).To(Equal([]byte{0b10000000 ^ 0x1d, 0x7f, 0x3e, 0x7d})) }) It("writes the maximum 4 byte number in 4 bytes", func() { b := &bytes.Buffer{} Write(b, maxVarInt4) Expect(b.Bytes()).To(Equal([]byte{0b10111111, 0xff, 0xff, 0xff})) }) It("writes the minimum 8 byte number in 8 bytes", func() { b := &bytes.Buffer{} Write(b, maxVarInt4+1) Expect(b.Len()).To(Equal(8)) num, err := Read(b) Expect(err).ToNot(HaveOccurred()) Expect(num).To(Equal(uint64(maxVarInt4 + 1))) }) It("writes an 8 byte number", func() { b := &bytes.Buffer{} Write(b, 151288809941952652) Expect(b.Bytes()).To(Equal([]byte{0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c})) }) It("writes the maximum 8 byte number in 8 bytes", func() { b := &bytes.Buffer{} Write(b, maxVarInt8) Expect(b.Bytes()).To(Equal([]byte{0xff /* 11111111 */, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})) }) It("panics when given a too large number (> 62 bit)", func() { Expect(func() { Write(&bytes.Buffer{}, maxVarInt8+1) }).Should(Panic()) }) }) Context("with fixed length", func() { It("panics when given an invalid length", func() { Expect(func() { WriteWithLen(&bytes.Buffer{}, 25, 3) }).Should(Panic()) }) It("panics when given a too short length", func() { Expect(func() { WriteWithLen(&bytes.Buffer{}, maxVarInt1+1, 1) }).Should(Panic()) Expect(func() { WriteWithLen(&bytes.Buffer{}, maxVarInt2+1, 2) }).Should(Panic()) Expect(func() { WriteWithLen(&bytes.Buffer{}, maxVarInt4+1, 4) }).Should(Panic()) }) It("writes a 1-byte number in minimal encoding", func() { b := &bytes.Buffer{} WriteWithLen(b, 37, 1) Expect(b.Bytes()).To(Equal([]byte{0x25})) }) It("writes a 1-byte number in 2 bytes", func() { b := &bytes.Buffer{} WriteWithLen(b, 37, 2) Expect(b.Bytes()).To(Equal([]byte{0b01000000, 0x25})) Expect(Read(b)).To(BeEquivalentTo(37)) }) It("writes a 1-byte number in 4 bytes", func() { b := &bytes.Buffer{} WriteWithLen(b, 37, 4) Expect(b.Bytes()).To(Equal([]byte{0b10000000, 0, 0, 0x25})) Expect(Read(b)).To(BeEquivalentTo(37)) }) It("writes a 1-byte number in 8 bytes", func() { b := &bytes.Buffer{} WriteWithLen(b, 37, 8) Expect(b.Bytes()).To(Equal([]byte{0b11000000, 0, 0, 0, 0, 0, 0, 0x25})) Expect(Read(b)).To(BeEquivalentTo(37)) }) It("writes a 2-byte number in 4 bytes", func() { b := &bytes.Buffer{} WriteWithLen(b, 15293, 4) Expect(b.Bytes()).To(Equal([]byte{0b10000000, 0, 0x3b, 0xbd})) Expect(Read(b)).To(BeEquivalentTo(15293)) }) It("write a 4-byte number in 8 bytes", func() { b := &bytes.Buffer{} WriteWithLen(b, 494878333, 8) Expect(b.Bytes()).To(Equal([]byte{0b11000000, 0, 0, 0, 0x1d, 0x7f, 0x3e, 0x7d})) Expect(Read(b)).To(BeEquivalentTo(494878333)) }) }) }) Context("determining the length needed for encoding", func() { It("for numbers that need 1 byte", func() { Expect(Len(0)).To(BeEquivalentTo(1)) Expect(Len(maxVarInt1)).To(BeEquivalentTo(1)) }) It("for numbers that need 2 bytes", func() { Expect(Len(maxVarInt1 + 1)).To(BeEquivalentTo(2)) Expect(Len(maxVarInt2)).To(BeEquivalentTo(2)) }) It("for numbers that need 4 bytes", func() { Expect(Len(maxVarInt2 + 1)).To(BeEquivalentTo(4)) Expect(Len(maxVarInt4)).To(BeEquivalentTo(4)) }) It("for numbers that need 8 bytes", func() { Expect(Len(maxVarInt4 + 1)).To(BeEquivalentTo(8)) Expect(Len(maxVarInt8)).To(BeEquivalentTo(8)) }) It("panics when given a too large number (> 62 bit)", func() { Expect(func() { Len(maxVarInt8 + 1) }).Should(Panic()) }) }) }) quic-go-0.25.0/receive_stream.go000066400000000000000000000202321417145451600164750ustar00rootroot00000000000000package quic import ( "fmt" "io" "sync" "time" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type receiveStreamI interface { ReceiveStream handleStreamFrame(*wire.StreamFrame) error handleResetStreamFrame(*wire.ResetStreamFrame) error closeForShutdown(error) getWindowUpdate() protocol.ByteCount } type receiveStream struct { mutex sync.Mutex streamID protocol.StreamID sender streamSender frameQueue *frameSorter finalOffset protocol.ByteCount currentFrame []byte currentFrameDone func() currentFrameIsLast bool // is the currentFrame the last frame on this stream readPosInFrame int closeForShutdownErr error cancelReadErr error resetRemotelyErr *StreamError closedForShutdown bool // set when CloseForShutdown() is called finRead bool // set once we read a frame with a Fin canceledRead bool // set when CancelRead() is called resetRemotely bool // set when HandleResetStreamFrame() is called readChan chan struct{} deadline time.Time flowController flowcontrol.StreamFlowController version protocol.VersionNumber } var ( _ ReceiveStream = &receiveStream{} _ receiveStreamI = &receiveStream{} ) func newReceiveStream( streamID protocol.StreamID, sender streamSender, flowController flowcontrol.StreamFlowController, version protocol.VersionNumber, ) *receiveStream { return &receiveStream{ streamID: streamID, sender: sender, flowController: flowController, frameQueue: newFrameSorter(), readChan: make(chan struct{}, 1), finalOffset: protocol.MaxByteCount, version: version, } } func (s *receiveStream) StreamID() protocol.StreamID { return s.streamID } // Read implements io.Reader. It is not thread safe! func (s *receiveStream) Read(p []byte) (int, error) { s.mutex.Lock() completed, n, err := s.readImpl(p) s.mutex.Unlock() if completed { s.sender.onStreamCompleted(s.streamID) } return n, err } func (s *receiveStream) readImpl(p []byte) (bool /*stream completed */, int, error) { if s.finRead { return false, 0, io.EOF } if s.canceledRead { return false, 0, s.cancelReadErr } if s.resetRemotely { return false, 0, s.resetRemotelyErr } if s.closedForShutdown { return false, 0, s.closeForShutdownErr } bytesRead := 0 var deadlineTimer *utils.Timer for bytesRead < len(p) { if s.currentFrame == nil || s.readPosInFrame >= len(s.currentFrame) { s.dequeueNextFrame() } if s.currentFrame == nil && bytesRead > 0 { return false, bytesRead, s.closeForShutdownErr } for { // Stop waiting on errors if s.closedForShutdown { return false, bytesRead, s.closeForShutdownErr } if s.canceledRead { return false, bytesRead, s.cancelReadErr } if s.resetRemotely { return false, bytesRead, s.resetRemotelyErr } deadline := s.deadline if !deadline.IsZero() { if !time.Now().Before(deadline) { return false, bytesRead, errDeadline } if deadlineTimer == nil { deadlineTimer = utils.NewTimer() defer deadlineTimer.Stop() } deadlineTimer.Reset(deadline) } if s.currentFrame != nil || s.currentFrameIsLast { break } s.mutex.Unlock() if deadline.IsZero() { <-s.readChan } else { select { case <-s.readChan: case <-deadlineTimer.Chan(): deadlineTimer.SetRead() } } s.mutex.Lock() if s.currentFrame == nil { s.dequeueNextFrame() } } if bytesRead > len(p) { return false, bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p)) } if s.readPosInFrame > len(s.currentFrame) { return false, bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, len(s.currentFrame)) } m := copy(p[bytesRead:], s.currentFrame[s.readPosInFrame:]) s.readPosInFrame += m bytesRead += m // when a RESET_STREAM was received, the was already informed about the final byteOffset for this stream if !s.resetRemotely { s.flowController.AddBytesRead(protocol.ByteCount(m)) } if s.readPosInFrame >= len(s.currentFrame) && s.currentFrameIsLast { s.finRead = true return true, bytesRead, io.EOF } } return false, bytesRead, nil } func (s *receiveStream) dequeueNextFrame() { var offset protocol.ByteCount // We're done with the last frame. Release the buffer. if s.currentFrameDone != nil { s.currentFrameDone() } offset, s.currentFrame, s.currentFrameDone = s.frameQueue.Pop() s.currentFrameIsLast = offset+protocol.ByteCount(len(s.currentFrame)) >= s.finalOffset s.readPosInFrame = 0 } func (s *receiveStream) CancelRead(errorCode StreamErrorCode) { s.mutex.Lock() completed := s.cancelReadImpl(errorCode) s.mutex.Unlock() if completed { s.flowController.Abandon() s.sender.onStreamCompleted(s.streamID) } } func (s *receiveStream) cancelReadImpl(errorCode qerr.StreamErrorCode) bool /* completed */ { if s.finRead || s.canceledRead || s.resetRemotely { return false } s.canceledRead = true s.cancelReadErr = fmt.Errorf("Read on stream %d canceled with error code %d", s.streamID, errorCode) s.signalRead() s.sender.queueControlFrame(&wire.StopSendingFrame{ StreamID: s.streamID, ErrorCode: errorCode, }) // We're done with this stream if the final offset was already received. return s.finalOffset != protocol.MaxByteCount } func (s *receiveStream) handleStreamFrame(frame *wire.StreamFrame) error { s.mutex.Lock() completed, err := s.handleStreamFrameImpl(frame) s.mutex.Unlock() if completed { s.flowController.Abandon() s.sender.onStreamCompleted(s.streamID) } return err } func (s *receiveStream) handleStreamFrameImpl(frame *wire.StreamFrame) (bool /* completed */, error) { maxOffset := frame.Offset + frame.DataLen() if err := s.flowController.UpdateHighestReceived(maxOffset, frame.Fin); err != nil { return false, err } var newlyRcvdFinalOffset bool if frame.Fin { newlyRcvdFinalOffset = s.finalOffset == protocol.MaxByteCount s.finalOffset = maxOffset } if s.canceledRead { return newlyRcvdFinalOffset, nil } if err := s.frameQueue.Push(frame.Data, frame.Offset, frame.PutBack); err != nil { return false, err } s.signalRead() return false, nil } func (s *receiveStream) handleResetStreamFrame(frame *wire.ResetStreamFrame) error { s.mutex.Lock() completed, err := s.handleResetStreamFrameImpl(frame) s.mutex.Unlock() if completed { s.flowController.Abandon() s.sender.onStreamCompleted(s.streamID) } return err } func (s *receiveStream) handleResetStreamFrameImpl(frame *wire.ResetStreamFrame) (bool /*completed */, error) { if s.closedForShutdown { return false, nil } if err := s.flowController.UpdateHighestReceived(frame.FinalSize, true); err != nil { return false, err } newlyRcvdFinalOffset := s.finalOffset == protocol.MaxByteCount s.finalOffset = frame.FinalSize // ignore duplicate RESET_STREAM frames for this stream (after checking their final offset) if s.resetRemotely { return false, nil } s.resetRemotely = true s.resetRemotelyErr = &StreamError{ StreamID: s.streamID, ErrorCode: frame.ErrorCode, } s.signalRead() return newlyRcvdFinalOffset, nil } func (s *receiveStream) CloseRemote(offset protocol.ByteCount) { s.handleStreamFrame(&wire.StreamFrame{Fin: true, Offset: offset}) } func (s *receiveStream) SetReadDeadline(t time.Time) error { s.mutex.Lock() s.deadline = t s.mutex.Unlock() s.signalRead() return nil } // CloseForShutdown closes a stream abruptly. // It makes Read unblock (and return the error) immediately. // The peer will NOT be informed about this: the stream is closed without sending a FIN or RESET. func (s *receiveStream) closeForShutdown(err error) { s.mutex.Lock() s.closedForShutdown = true s.closeForShutdownErr = err s.mutex.Unlock() s.signalRead() } func (s *receiveStream) getWindowUpdate() protocol.ByteCount { return s.flowController.GetWindowUpdate() } // signalRead performs a non-blocking send on the readChan func (s *receiveStream) signalRead() { select { case s.readChan <- struct{}{}: default: } } quic-go-0.25.0/receive_stream_test.go000066400000000000000000000531251417145451600175430ustar00rootroot00000000000000package quic import ( "errors" "io" "runtime" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) var _ = Describe("Receive Stream", func() { const streamID protocol.StreamID = 1337 var ( str *receiveStream strWithTimeout io.Reader // str wrapped with gbytes.TimeoutReader mockFC *mocks.MockStreamFlowController mockSender *MockStreamSender ) BeforeEach(func() { mockSender = NewMockStreamSender(mockCtrl) mockFC = mocks.NewMockStreamFlowController(mockCtrl) str = newReceiveStream(streamID, mockSender, mockFC, protocol.VersionWhatever) timeout := scaleDuration(250 * time.Millisecond) strWithTimeout = gbytes.TimeoutReader(str, timeout) }) It("gets stream id", func() { Expect(str.StreamID()).To(Equal(protocol.StreamID(1337))) }) Context("reading", func() { It("reads a single STREAM frame", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4)) frame := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD, 0xBE, 0xEF}, } err := str.handleStreamFrame(&frame) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) }) It("reads a single STREAM frame in multiple goes", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)) frame := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD, 0xBE, 0xEF}, } err := str.handleStreamFrame(&frame) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 2) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(2)) Expect(b).To(Equal([]byte{0xDE, 0xAD})) n, err = strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(2)) Expect(b).To(Equal([]byte{0xBE, 0xEF})) }) It("reads all data available", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2) frame1 := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD}, } frame2 := wire.StreamFrame{ Offset: 2, Data: []byte{0xBE, 0xEF}, } err := str.handleStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame2) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 6) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00})) }) It("assembles multiple STREAM frames", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2) frame1 := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD}, } frame2 := wire.StreamFrame{ Offset: 2, Data: []byte{0xBE, 0xEF}, } err := str.handleStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame2) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) }) It("waits until data is available", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)) go func() { defer GinkgoRecover() frame := wire.StreamFrame{Data: []byte{0xDE, 0xAD}} time.Sleep(10 * time.Millisecond) err := str.handleStreamFrame(&frame) Expect(err).ToNot(HaveOccurred()) }() b := make([]byte, 2) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(2)) }) It("handles STREAM frames in wrong order", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2) frame1 := wire.StreamFrame{ Offset: 2, Data: []byte{0xBE, 0xEF}, } frame2 := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD}, } err := str.handleStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame2) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) }) It("ignores duplicate STREAM frames", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2) frame1 := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD}, } frame2 := wire.StreamFrame{ Offset: 0, Data: []byte{0x13, 0x37}, } frame3 := wire.StreamFrame{ Offset: 2, Data: []byte{0xBE, 0xEF}, } err := str.handleStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame2) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame3) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) }) It("doesn't rejects a STREAM frames with an overlapping data range", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4)) frame1 := wire.StreamFrame{ Offset: 0, Data: []byte("foob"), } frame2 := wire.StreamFrame{ Offset: 2, Data: []byte("obar"), } err := str.handleStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame2) Expect(err).ToNot(HaveOccurred()) b := make([]byte, 6) n, err := strWithTimeout.Read(b) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) Expect(b).To(Equal([]byte("foobar"))) }) Context("deadlines", func() { It("the deadline error has the right net.Error properties", func() { Expect(errDeadline.Temporary()).To(BeTrue()) Expect(errDeadline.Timeout()).To(BeTrue()) Expect(errDeadline).To(MatchError("deadline exceeded")) }) It("returns an error when Read is called after the deadline", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false).AnyTimes() f := &wire.StreamFrame{Data: []byte("foobar")} err := str.handleStreamFrame(f) Expect(err).ToNot(HaveOccurred()) str.SetReadDeadline(time.Now().Add(-time.Second)) b := make([]byte, 6) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) }) It("unblocks when the deadline is changed to the past", func() { str.SetReadDeadline(time.Now().Add(time.Hour)) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := str.Read(make([]byte, 6)) Expect(err).To(MatchError(errDeadline)) close(done) }() Consistently(done).ShouldNot(BeClosed()) str.SetReadDeadline(time.Now().Add(-time.Hour)) Eventually(done).Should(BeClosed()) }) It("unblocks after the deadline", func() { deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) str.SetReadDeadline(deadline) b := make([]byte, 6) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond))) }) It("doesn't unblock if the deadline is changed before the first one expires", func() { deadline1 := time.Now().Add(scaleDuration(50 * time.Millisecond)) deadline2 := time.Now().Add(scaleDuration(100 * time.Millisecond)) str.SetReadDeadline(deadline1) go func() { defer GinkgoRecover() time.Sleep(scaleDuration(20 * time.Millisecond)) str.SetReadDeadline(deadline2) // make sure that this was actually execute before the deadline expires Expect(time.Now()).To(BeTemporally("<", deadline1)) }() runtime.Gosched() b := make([]byte, 10) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond))) }) It("unblocks earlier, when a new deadline is set", func() { deadline1 := time.Now().Add(scaleDuration(200 * time.Millisecond)) deadline2 := time.Now().Add(scaleDuration(50 * time.Millisecond)) go func() { defer GinkgoRecover() time.Sleep(scaleDuration(10 * time.Millisecond)) str.SetReadDeadline(deadline2) // make sure that this was actually execute before the deadline expires Expect(time.Now()).To(BeTemporally("<", deadline2)) }() str.SetReadDeadline(deadline1) runtime.Gosched() b := make([]byte, 10) _, err := strWithTimeout.Read(b) Expect(err).To(MatchError(errDeadline)) Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(25*time.Millisecond))) }) It("doesn't unblock if the deadline is removed", func() { deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) str.SetReadDeadline(deadline) deadlineUnset := make(chan struct{}) go func() { defer GinkgoRecover() time.Sleep(scaleDuration(20 * time.Millisecond)) str.SetReadDeadline(time.Time{}) // make sure that this was actually execute before the deadline expires Expect(time.Now()).To(BeTemporally("<", deadline)) close(deadlineUnset) }() done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Read(make([]byte, 1)) Expect(err).To(MatchError("test done")) close(done) }() runtime.Gosched() Eventually(deadlineUnset).Should(BeClosed()) Consistently(done, scaleDuration(100*time.Millisecond)).ShouldNot(BeClosed()) // make the go routine return str.closeForShutdown(errors.New("test done")) Eventually(done).Should(BeClosed()) }) }) Context("closing", func() { Context("with FIN bit", func() { It("returns EOFs", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4)) str.handleStreamFrame(&wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD, 0xBE, 0xEF}, Fin: true, }) mockSender.EXPECT().onStreamCompleted(streamID) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(io.EOF)) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) n, err = strWithTimeout.Read(b) Expect(n).To(BeZero()) Expect(err).To(MatchError(io.EOF)) }) It("handles out-of-order frames", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false) mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2) frame1 := wire.StreamFrame{ Offset: 2, Data: []byte{0xBE, 0xEF}, Fin: true, } frame2 := wire.StreamFrame{ Offset: 0, Data: []byte{0xDE, 0xAD}, } err := str.handleStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.handleStreamFrame(&frame2) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().onStreamCompleted(streamID) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(io.EOF)) Expect(n).To(Equal(4)) Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) n, err = strWithTimeout.Read(b) Expect(n).To(BeZero()) Expect(err).To(MatchError(io.EOF)) }) It("returns EOFs with partial read", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), true) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)) err := str.handleStreamFrame(&wire.StreamFrame{ Offset: 0, Data: []byte{0xde, 0xad}, Fin: true, }) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().onStreamCompleted(streamID) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(io.EOF)) Expect(n).To(Equal(2)) Expect(b[:n]).To(Equal([]byte{0xde, 0xad})) }) It("handles immediate FINs", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0)) err := str.handleStreamFrame(&wire.StreamFrame{ Offset: 0, Fin: true, }) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().onStreamCompleted(streamID) b := make([]byte, 4) n, err := strWithTimeout.Read(b) Expect(n).To(BeZero()) Expect(err).To(MatchError(io.EOF)) }) }) It("closes when CloseRemote is called", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0)) str.CloseRemote(0) mockSender.EXPECT().onStreamCompleted(streamID) b := make([]byte, 8) n, err := strWithTimeout.Read(b) Expect(n).To(BeZero()) Expect(err).To(MatchError(io.EOF)) }) }) Context("closing for shutdown", func() { testErr := errors.New("test error") It("immediately returns all reads", func() { done := make(chan struct{}) b := make([]byte, 4) go func() { defer GinkgoRecover() n, err := strWithTimeout.Read(b) Expect(n).To(BeZero()) Expect(err).To(MatchError(testErr)) close(done) }() Consistently(done).ShouldNot(BeClosed()) str.closeForShutdown(testErr) Eventually(done).Should(BeClosed()) }) It("errors for all following reads", func() { str.closeForShutdown(testErr) b := make([]byte, 1) n, err := strWithTimeout.Read(b) Expect(n).To(BeZero()) Expect(err).To(MatchError(testErr)) }) }) }) Context("stream cancelations", func() { Context("canceling read", func() { It("unblocks Read", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Read([]byte{0}) Expect(err).To(MatchError("Read on stream 1337 canceled with error code 1234")) close(done) }() Consistently(done).ShouldNot(BeClosed()) str.CancelRead(1234) Eventually(done).Should(BeClosed()) }) It("doesn't allow further calls to Read", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) str.CancelRead(1234) _, err := strWithTimeout.Read([]byte{0}) Expect(err).To(MatchError("Read on stream 1337 canceled with error code 1234")) }) It("does nothing when CancelRead is called twice", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) str.CancelRead(1234) str.CancelRead(1234) _, err := strWithTimeout.Read([]byte{0}) Expect(err).To(MatchError("Read on stream 1337 canceled with error code 1234")) }) It("queues a STOP_SENDING frame", func() { mockSender.EXPECT().queueControlFrame(&wire.StopSendingFrame{ StreamID: streamID, ErrorCode: 1234, }) str.CancelRead(1234) }) It("doesn't send a STOP_SENDING frame, if the FIN was already read", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), true) mockFC.EXPECT().AddBytesRead(protocol.ByteCount(6)) // no calls to mockSender.queueControlFrame Expect(str.handleStreamFrame(&wire.StreamFrame{ StreamID: streamID, Data: []byte("foobar"), Fin: true, })).To(Succeed()) mockSender.EXPECT().onStreamCompleted(streamID) _, err := strWithTimeout.Read(make([]byte, 100)) Expect(err).To(MatchError(io.EOF)) str.CancelRead(1234) }) It("doesn't send a STOP_SENDING frame, if the stream was already reset", func() { gomock.InOrder( mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true), mockFC.EXPECT().Abandon(), ) mockSender.EXPECT().onStreamCompleted(streamID) Expect(str.handleResetStreamFrame(&wire.ResetStreamFrame{ StreamID: streamID, FinalSize: 42, })).To(Succeed()) str.CancelRead(1234) }) It("sends a STOP_SENDING and completes the stream after receiving the final offset", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(1000), true) Expect(str.handleStreamFrame(&wire.StreamFrame{ Offset: 1000, Fin: true, })).To(Succeed()) mockFC.EXPECT().Abandon() mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onStreamCompleted(streamID) str.CancelRead(1234) }) It("completes the stream when receiving the Fin after the stream was canceled", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) str.CancelRead(1234) gomock.InOrder( mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(1000), true), mockFC.EXPECT().Abandon(), ) mockSender.EXPECT().onStreamCompleted(streamID) Expect(str.handleStreamFrame(&wire.StreamFrame{ Offset: 1000, Fin: true, })).To(Succeed()) }) It("handles duplicate FinBits after the stream was canceled", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) str.CancelRead(1234) gomock.InOrder( mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(1000), true), mockFC.EXPECT().Abandon(), mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(1000), true), ) mockSender.EXPECT().onStreamCompleted(streamID) Expect(str.handleStreamFrame(&wire.StreamFrame{ Offset: 1000, Fin: true, })).To(Succeed()) Expect(str.handleStreamFrame(&wire.StreamFrame{ Offset: 1000, Fin: true, })).To(Succeed()) }) }) Context("receiving RESET_STREAM frames", func() { rst := &wire.ResetStreamFrame{ StreamID: streamID, FinalSize: 42, ErrorCode: 1234, } It("unblocks Read", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Read([]byte{0}) Expect(err).To(MatchError(&StreamError{ StreamID: streamID, ErrorCode: 1234, })) close(done) }() Consistently(done).ShouldNot(BeClosed()) mockSender.EXPECT().onStreamCompleted(streamID) gomock.InOrder( mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true), mockFC.EXPECT().Abandon(), ) Expect(str.handleResetStreamFrame(rst)).To(Succeed()) Eventually(done).Should(BeClosed()) }) It("doesn't allow further calls to Read", func() { mockSender.EXPECT().onStreamCompleted(streamID) gomock.InOrder( mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true), mockFC.EXPECT().Abandon(), ) Expect(str.handleResetStreamFrame(rst)).To(Succeed()) _, err := strWithTimeout.Read([]byte{0}) Expect(err).To(MatchError(&StreamError{ StreamID: streamID, ErrorCode: 1234, })) }) It("errors when receiving a RESET_STREAM with an inconsistent offset", func() { testErr := errors.New("already received a different final offset before") mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true).Return(testErr) err := str.handleResetStreamFrame(rst) Expect(err).To(MatchError(testErr)) }) It("ignores duplicate RESET_STREAM frames", func() { mockSender.EXPECT().onStreamCompleted(streamID) mockFC.EXPECT().Abandon() mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true).Times(2) Expect(str.handleResetStreamFrame(rst)).To(Succeed()) Expect(str.handleResetStreamFrame(rst)).To(Succeed()) }) It("doesn't call onStreamCompleted again when the final offset was already received via Fin", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) str.CancelRead(1234) mockSender.EXPECT().onStreamCompleted(streamID) mockFC.EXPECT().Abandon() mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true).Times(2) Expect(str.handleStreamFrame(&wire.StreamFrame{ StreamID: streamID, Offset: rst.FinalSize, Fin: true, })).To(Succeed()) Expect(str.handleResetStreamFrame(rst)).To(Succeed()) }) It("doesn't do anyting when it was closed for shutdown", func() { str.closeForShutdown(nil) err := str.handleResetStreamFrame(rst) Expect(err).ToNot(HaveOccurred()) }) }) }) Context("flow control", func() { It("errors when a STREAM frame causes a flow control violation", func() { testErr := errors.New("flow control violation") mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(8), false).Return(testErr) frame := wire.StreamFrame{ Offset: 2, Data: []byte("foobar"), } err := str.handleStreamFrame(&frame) Expect(err).To(MatchError(testErr)) }) It("gets a window update", func() { mockFC.EXPECT().GetWindowUpdate().Return(protocol.ByteCount(0x100)) Expect(str.getWindowUpdate()).To(Equal(protocol.ByteCount(0x100))) }) }) }) quic-go-0.25.0/retransmission_queue.go000066400000000000000000000064111417145451600177670ustar00rootroot00000000000000package quic import ( "fmt" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) type retransmissionQueue struct { initial []wire.Frame initialCryptoData []*wire.CryptoFrame handshake []wire.Frame handshakeCryptoData []*wire.CryptoFrame appData []wire.Frame version protocol.VersionNumber } func newRetransmissionQueue(ver protocol.VersionNumber) *retransmissionQueue { return &retransmissionQueue{version: ver} } func (q *retransmissionQueue) AddInitial(f wire.Frame) { if cf, ok := f.(*wire.CryptoFrame); ok { q.initialCryptoData = append(q.initialCryptoData, cf) return } q.initial = append(q.initial, f) } func (q *retransmissionQueue) AddHandshake(f wire.Frame) { if cf, ok := f.(*wire.CryptoFrame); ok { q.handshakeCryptoData = append(q.handshakeCryptoData, cf) return } q.handshake = append(q.handshake, f) } func (q *retransmissionQueue) HasInitialData() bool { return len(q.initialCryptoData) > 0 || len(q.initial) > 0 } func (q *retransmissionQueue) HasHandshakeData() bool { return len(q.handshakeCryptoData) > 0 || len(q.handshake) > 0 } func (q *retransmissionQueue) HasAppData() bool { return len(q.appData) > 0 } func (q *retransmissionQueue) AddAppData(f wire.Frame) { if _, ok := f.(*wire.StreamFrame); ok { panic("STREAM frames are handled with their respective streams.") } q.appData = append(q.appData, f) } func (q *retransmissionQueue) GetInitialFrame(maxLen protocol.ByteCount) wire.Frame { if len(q.initialCryptoData) > 0 { f := q.initialCryptoData[0] newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, q.version) if newFrame == nil && !needsSplit { // the whole frame fits q.initialCryptoData = q.initialCryptoData[1:] return f } if newFrame != nil { // frame was split. Leave the original frame in the queue. return newFrame } } if len(q.initial) == 0 { return nil } f := q.initial[0] if f.Length(q.version) > maxLen { return nil } q.initial = q.initial[1:] return f } func (q *retransmissionQueue) GetHandshakeFrame(maxLen protocol.ByteCount) wire.Frame { if len(q.handshakeCryptoData) > 0 { f := q.handshakeCryptoData[0] newFrame, needsSplit := f.MaybeSplitOffFrame(maxLen, q.version) if newFrame == nil && !needsSplit { // the whole frame fits q.handshakeCryptoData = q.handshakeCryptoData[1:] return f } if newFrame != nil { // frame was split. Leave the original frame in the queue. return newFrame } } if len(q.handshake) == 0 { return nil } f := q.handshake[0] if f.Length(q.version) > maxLen { return nil } q.handshake = q.handshake[1:] return f } func (q *retransmissionQueue) GetAppDataFrame(maxLen protocol.ByteCount) wire.Frame { if len(q.appData) == 0 { return nil } f := q.appData[0] if f.Length(q.version) > maxLen { return nil } q.appData = q.appData[1:] return f } func (q *retransmissionQueue) DropPackets(encLevel protocol.EncryptionLevel) { //nolint:exhaustive // Can only drop Initial and Handshake packet number space. switch encLevel { case protocol.EncryptionInitial: q.initial = nil q.initialCryptoData = nil case protocol.EncryptionHandshake: q.handshake = nil q.handshakeCryptoData = nil default: panic(fmt.Sprintf("unexpected encryption level: %s", encLevel)) } } quic-go-0.25.0/retransmission_queue_test.go000066400000000000000000000150351417145451600210300ustar00rootroot00000000000000package quic import ( "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Retransmission queue", func() { const version = protocol.VersionTLS var q *retransmissionQueue BeforeEach(func() { q = newRetransmissionQueue(version) }) Context("Initial data", func() { It("doesn't dequeue anything when it's empty", func() { Expect(q.HasInitialData()).To(BeFalse()) Expect(q.GetInitialFrame(protocol.MaxByteCount)).To(BeNil()) }) It("queues and retrieves a control frame", func() { f := &wire.MaxDataFrame{MaximumData: 0x42} q.AddInitial(f) Expect(q.HasInitialData()).To(BeTrue()) Expect(q.GetInitialFrame(f.Length(version) - 1)).To(BeNil()) Expect(q.GetInitialFrame(f.Length(version))).To(Equal(f)) Expect(q.HasInitialData()).To(BeFalse()) }) It("queues and retrieves a CRYPTO frame", func() { f := &wire.CryptoFrame{Data: []byte("foobar")} q.AddInitial(f) Expect(q.HasInitialData()).To(BeTrue()) Expect(q.GetInitialFrame(f.Length(version))).To(Equal(f)) Expect(q.HasInitialData()).To(BeFalse()) }) It("returns split CRYPTO frames", func() { f := &wire.CryptoFrame{ Offset: 100, Data: []byte("foobar"), } q.AddInitial(f) Expect(q.HasInitialData()).To(BeTrue()) f1 := q.GetInitialFrame(f.Length(version) - 3) Expect(f1).ToNot(BeNil()) Expect(f1).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) Expect(f1.(*wire.CryptoFrame).Data).To(Equal([]byte("foo"))) Expect(f1.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(100))) Expect(q.HasInitialData()).To(BeTrue()) f2 := q.GetInitialFrame(protocol.MaxByteCount) Expect(f2).ToNot(BeNil()) Expect(f2).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) Expect(f2.(*wire.CryptoFrame).Data).To(Equal([]byte("bar"))) Expect(f2.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(103))) Expect(q.HasInitialData()).To(BeFalse()) }) It("returns other frames when a CRYPTO frame wouldn't fit", func() { f := &wire.CryptoFrame{Data: []byte("foobar")} q.AddInitial(f) q.AddInitial(&wire.PingFrame{}) f1 := q.GetInitialFrame(2) // too small for a CRYPTO frame Expect(f1).ToNot(BeNil()) Expect(f1).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(q.HasInitialData()).To(BeTrue()) f2 := q.GetInitialFrame(protocol.MaxByteCount) Expect(f2).To(Equal(f)) }) It("retrieves both a CRYPTO frame and a control frame", func() { cf := &wire.MaxDataFrame{MaximumData: 0x42} f := &wire.CryptoFrame{Data: []byte("foobar")} q.AddInitial(f) q.AddInitial(cf) Expect(q.HasInitialData()).To(BeTrue()) Expect(q.GetInitialFrame(protocol.MaxByteCount)).To(Equal(f)) Expect(q.GetInitialFrame(protocol.MaxByteCount)).To(Equal(cf)) Expect(q.HasInitialData()).To(BeFalse()) }) It("drops all Initial frames", func() { q.AddInitial(&wire.CryptoFrame{Data: []byte("foobar")}) q.AddInitial(&wire.MaxDataFrame{MaximumData: 0x42}) q.DropPackets(protocol.EncryptionInitial) Expect(q.HasInitialData()).To(BeFalse()) Expect(q.GetInitialFrame(protocol.MaxByteCount)).To(BeNil()) }) }) Context("Handshake data", func() { It("doesn't dequeue anything when it's empty", func() { Expect(q.HasHandshakeData()).To(BeFalse()) Expect(q.GetHandshakeFrame(protocol.MaxByteCount)).To(BeNil()) }) It("queues and retrieves a control frame", func() { f := &wire.MaxDataFrame{MaximumData: 0x42} q.AddHandshake(f) Expect(q.HasHandshakeData()).To(BeTrue()) Expect(q.GetHandshakeFrame(f.Length(version) - 1)).To(BeNil()) Expect(q.GetHandshakeFrame(f.Length(version))).To(Equal(f)) Expect(q.HasHandshakeData()).To(BeFalse()) }) It("queues and retrieves a CRYPTO frame", func() { f := &wire.CryptoFrame{Data: []byte("foobar")} q.AddHandshake(f) Expect(q.HasHandshakeData()).To(BeTrue()) Expect(q.GetHandshakeFrame(f.Length(version))).To(Equal(f)) Expect(q.HasHandshakeData()).To(BeFalse()) }) It("returns split CRYPTO frames", func() { f := &wire.CryptoFrame{ Offset: 100, Data: []byte("foobar"), } q.AddHandshake(f) Expect(q.HasHandshakeData()).To(BeTrue()) f1 := q.GetHandshakeFrame(f.Length(version) - 3) Expect(f1).ToNot(BeNil()) Expect(f1).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) Expect(f1.(*wire.CryptoFrame).Data).To(Equal([]byte("foo"))) Expect(f1.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(100))) Expect(q.HasHandshakeData()).To(BeTrue()) f2 := q.GetHandshakeFrame(protocol.MaxByteCount) Expect(f2).ToNot(BeNil()) Expect(f2).To(BeAssignableToTypeOf(&wire.CryptoFrame{})) Expect(f2.(*wire.CryptoFrame).Data).To(Equal([]byte("bar"))) Expect(f2.(*wire.CryptoFrame).Offset).To(Equal(protocol.ByteCount(103))) Expect(q.HasHandshakeData()).To(BeFalse()) }) It("returns other frames when a CRYPTO frame wouldn't fit", func() { f := &wire.CryptoFrame{Data: []byte("foobar")} q.AddHandshake(f) q.AddHandshake(&wire.PingFrame{}) f1 := q.GetHandshakeFrame(2) // too small for a CRYPTO frame Expect(f1).ToNot(BeNil()) Expect(f1).To(BeAssignableToTypeOf(&wire.PingFrame{})) Expect(q.HasHandshakeData()).To(BeTrue()) f2 := q.GetHandshakeFrame(protocol.MaxByteCount) Expect(f2).To(Equal(f)) }) It("retrieves both a CRYPTO frame and a control frame", func() { cf := &wire.MaxDataFrame{MaximumData: 0x42} f := &wire.CryptoFrame{Data: []byte("foobar")} q.AddHandshake(f) q.AddHandshake(cf) Expect(q.HasHandshakeData()).To(BeTrue()) Expect(q.GetHandshakeFrame(protocol.MaxByteCount)).To(Equal(f)) Expect(q.GetHandshakeFrame(protocol.MaxByteCount)).To(Equal(cf)) Expect(q.HasHandshakeData()).To(BeFalse()) }) It("drops all Handshake frames", func() { q.AddHandshake(&wire.CryptoFrame{Data: []byte("foobar")}) q.AddHandshake(&wire.MaxDataFrame{MaximumData: 0x42}) q.DropPackets(protocol.EncryptionHandshake) Expect(q.HasHandshakeData()).To(BeFalse()) Expect(q.GetHandshakeFrame(protocol.MaxByteCount)).To(BeNil()) }) }) Context("Application data", func() { It("doesn't dequeue anything when it's empty", func() { Expect(q.GetAppDataFrame(protocol.MaxByteCount)).To(BeNil()) }) It("queues and retrieves a control frame", func() { f := &wire.MaxDataFrame{MaximumData: 0x42} Expect(q.HasAppData()).To(BeFalse()) q.AddAppData(f) Expect(q.HasAppData()).To(BeTrue()) Expect(q.GetAppDataFrame(f.Length(version) - 1)).To(BeNil()) Expect(q.GetAppDataFrame(f.Length(version))).To(Equal(f)) Expect(q.HasAppData()).To(BeFalse()) }) }) }) quic-go-0.25.0/send_conn.go000066400000000000000000000024331417145451600154510ustar00rootroot00000000000000package quic import ( "net" ) // A sendConn allows sending using a simple Write() on a non-connected packet conn. type sendConn interface { Write([]byte) error Close() error LocalAddr() net.Addr RemoteAddr() net.Addr } type sconn struct { connection remoteAddr net.Addr info *packetInfo oob []byte } var _ sendConn = &sconn{} func newSendConn(c connection, remote net.Addr, info *packetInfo) sendConn { return &sconn{ connection: c, remoteAddr: remote, info: info, oob: info.OOB(), } } func (c *sconn) Write(p []byte) error { _, err := c.WritePacket(p, c.remoteAddr, c.oob) return err } func (c *sconn) RemoteAddr() net.Addr { return c.remoteAddr } func (c *sconn) LocalAddr() net.Addr { addr := c.connection.LocalAddr() if c.info != nil { if udpAddr, ok := addr.(*net.UDPAddr); ok { addrCopy := *udpAddr addrCopy.IP = c.info.addr addr = &addrCopy } } return addr } type spconn struct { net.PacketConn remoteAddr net.Addr } var _ sendConn = &spconn{} func newSendPconn(c net.PacketConn, remote net.Addr) sendConn { return &spconn{PacketConn: c, remoteAddr: remote} } func (c *spconn) Write(p []byte) error { _, err := c.WriteTo(p, c.remoteAddr) return err } func (c *spconn) RemoteAddr() net.Addr { return c.remoteAddr } quic-go-0.25.0/send_conn_test.go000066400000000000000000000016761417145451600165200ustar00rootroot00000000000000package quic import ( "net" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Connection (for sending packets)", func() { var ( c sendConn packetConn *MockPacketConn addr net.Addr ) BeforeEach(func() { addr = &net.UDPAddr{IP: net.IPv4(192, 168, 100, 200), Port: 1337} packetConn = NewMockPacketConn(mockCtrl) c = newSendPconn(packetConn, addr) }) It("writes", func() { packetConn.EXPECT().WriteTo([]byte("foobar"), addr) Expect(c.Write([]byte("foobar"))).To(Succeed()) }) It("gets the remote address", func() { Expect(c.RemoteAddr().String()).To(Equal("192.168.100.200:1337")) }) It("gets the local address", func() { addr := &net.UDPAddr{ IP: net.IPv4(192, 168, 0, 1), Port: 1234, } packetConn.EXPECT().LocalAddr().Return(addr) Expect(c.LocalAddr()).To(Equal(addr)) }) It("closes", func() { packetConn.EXPECT().Close() Expect(c.Close()).To(Succeed()) }) }) quic-go-0.25.0/send_queue.go000066400000000000000000000034301417145451600156360ustar00rootroot00000000000000package quic type sender interface { Send(p *packetBuffer) Run() error WouldBlock() bool Available() <-chan struct{} Close() } type sendQueue struct { queue chan *packetBuffer closeCalled chan struct{} // runStopped when Close() is called runStopped chan struct{} // runStopped when the run loop returns available chan struct{} conn sendConn } var _ sender = &sendQueue{} const sendQueueCapacity = 8 func newSendQueue(conn sendConn) sender { return &sendQueue{ conn: conn, runStopped: make(chan struct{}), closeCalled: make(chan struct{}), available: make(chan struct{}, 1), queue: make(chan *packetBuffer, sendQueueCapacity), } } // Send sends out a packet. It's guaranteed to not block. // Callers need to make sure that there's actually space in the send queue by calling WouldBlock. // Otherwise Send will panic. func (h *sendQueue) Send(p *packetBuffer) { select { case h.queue <- p: case <-h.runStopped: default: panic("sendQueue.Send would have blocked") } } func (h *sendQueue) WouldBlock() bool { return len(h.queue) == sendQueueCapacity } func (h *sendQueue) Available() <-chan struct{} { return h.available } func (h *sendQueue) Run() error { defer close(h.runStopped) var shouldClose bool for { if shouldClose && len(h.queue) == 0 { return nil } select { case <-h.closeCalled: h.closeCalled = nil // prevent this case from being selected again // make sure that all queued packets are actually sent out shouldClose = true case p := <-h.queue: if err := h.conn.Write(p.Data); err != nil { return err } p.Release() select { case h.available <- struct{}{}: default: } } } } func (h *sendQueue) Close() { close(h.closeCalled) // wait until the run loop returned <-h.runStopped } quic-go-0.25.0/send_queue_test.go000066400000000000000000000056231417145451600167030ustar00rootroot00000000000000package quic import ( "errors" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Send Queue", func() { var q sender var c *MockSendConn BeforeEach(func() { c = NewMockSendConn(mockCtrl) q = newSendQueue(c) }) getPacket := func(b []byte) *packetBuffer { buf := getPacketBuffer() buf.Data = buf.Data[:len(b)] copy(buf.Data, b) return buf } It("sends a packet", func() { p := getPacket([]byte("foobar")) q.Send(p) written := make(chan struct{}) c.EXPECT().Write([]byte("foobar")).Do(func([]byte) { close(written) }) done := make(chan struct{}) go func() { defer GinkgoRecover() q.Run() close(done) }() Eventually(written).Should(BeClosed()) q.Close() Eventually(done).Should(BeClosed()) }) It("panics when Send() is called although there's no space in the queue", func() { for i := 0; i < sendQueueCapacity; i++ { Expect(q.WouldBlock()).To(BeFalse()) q.Send(getPacket([]byte("foobar"))) } Expect(q.WouldBlock()).To(BeTrue()) Expect(func() { q.Send(getPacket([]byte("raboof"))) }).To(Panic()) }) It("signals when sending is possible again", func() { Expect(q.WouldBlock()).To(BeFalse()) q.Send(getPacket([]byte("foobar1"))) Consistently(q.Available()).ShouldNot(Receive()) // now start sending out packets. This should free up queue space. c.EXPECT().Write(gomock.Any()).MinTimes(1).MaxTimes(2) done := make(chan struct{}) go func() { defer GinkgoRecover() q.Run() close(done) }() Eventually(q.Available()).Should(Receive()) Expect(q.WouldBlock()).To(BeFalse()) Expect(func() { q.Send(getPacket([]byte("foobar2"))) }).ToNot(Panic()) q.Close() Eventually(done).Should(BeClosed()) }) It("does not block pending send after the queue has stopped running", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() q.Run() close(done) }() // the run loop exits if there is a write error testErr := errors.New("test error") c.EXPECT().Write(gomock.Any()).Return(testErr) q.Send(getPacket([]byte("foobar"))) Eventually(done).Should(BeClosed()) sent := make(chan struct{}) go func() { defer GinkgoRecover() q.Send(getPacket([]byte("raboof"))) q.Send(getPacket([]byte("quux"))) close(sent) }() Eventually(sent).Should(BeClosed()) }) It("blocks Close() until the packet has been sent out", func() { written := make(chan []byte) c.EXPECT().Write(gomock.Any()).Do(func(p []byte) { written <- p }) done := make(chan struct{}) go func() { defer GinkgoRecover() q.Run() close(done) }() q.Send(getPacket([]byte("foobar"))) closed := make(chan struct{}) go func() { defer GinkgoRecover() q.Close() close(closed) }() Consistently(closed).ShouldNot(BeClosed()) // now write the packet Expect(written).To(Receive()) Eventually(done).Should(BeClosed()) Eventually(closed).Should(BeClosed()) }) }) quic-go-0.25.0/send_stream.go000066400000000000000000000316031417145451600160100ustar00rootroot00000000000000package quic import ( "context" "fmt" "sync" "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" ) type sendStreamI interface { SendStream handleStopSendingFrame(*wire.StopSendingFrame) hasData() bool popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool) closeForShutdown(error) updateSendWindow(protocol.ByteCount) } type sendStream struct { mutex sync.Mutex numOutstandingFrames int64 retransmissionQueue []*wire.StreamFrame ctx context.Context ctxCancel context.CancelFunc streamID protocol.StreamID sender streamSender writeOffset protocol.ByteCount cancelWriteErr error closeForShutdownErr error closedForShutdown bool // set when CloseForShutdown() is called finishedWriting bool // set once Close() is called canceledWrite bool // set when CancelWrite() is called, or a STOP_SENDING frame is received finSent bool // set when a STREAM_FRAME with FIN bit has been sent completed bool // set when this stream has been reported to the streamSender as completed dataForWriting []byte // during a Write() call, this slice is the part of p that still needs to be sent out nextFrame *wire.StreamFrame writeChan chan struct{} deadline time.Time flowController flowcontrol.StreamFlowController version protocol.VersionNumber } var ( _ SendStream = &sendStream{} _ sendStreamI = &sendStream{} ) func newSendStream( streamID protocol.StreamID, sender streamSender, flowController flowcontrol.StreamFlowController, version protocol.VersionNumber, ) *sendStream { s := &sendStream{ streamID: streamID, sender: sender, flowController: flowController, writeChan: make(chan struct{}, 1), version: version, } s.ctx, s.ctxCancel = context.WithCancel(context.Background()) return s } func (s *sendStream) StreamID() protocol.StreamID { return s.streamID // same for receiveStream and sendStream } func (s *sendStream) Write(p []byte) (int, error) { s.mutex.Lock() defer s.mutex.Unlock() if s.finishedWriting { return 0, fmt.Errorf("write on closed stream %d", s.streamID) } if s.canceledWrite { return 0, s.cancelWriteErr } if s.closeForShutdownErr != nil { return 0, s.closeForShutdownErr } if !s.deadline.IsZero() && !time.Now().Before(s.deadline) { return 0, errDeadline } if len(p) == 0 { return 0, nil } s.dataForWriting = p var ( deadlineTimer *utils.Timer bytesWritten int notifiedSender bool ) for { var copied bool var deadline time.Time // As soon as dataForWriting becomes smaller than a certain size x, we copy all the data to a STREAM frame (s.nextFrame), // which can the be popped the next time we assemble a packet. // This allows us to return Write() when all data but x bytes have been sent out. // When the user now calls Close(), this is much more likely to happen before we popped that last STREAM frame, // allowing us to set the FIN bit on that frame (instead of sending an empty STREAM frame with FIN). if s.canBufferStreamFrame() && len(s.dataForWriting) > 0 { if s.nextFrame == nil { f := wire.GetStreamFrame() f.Offset = s.writeOffset f.StreamID = s.streamID f.DataLenPresent = true f.Data = f.Data[:len(s.dataForWriting)] copy(f.Data, s.dataForWriting) s.nextFrame = f } else { l := len(s.nextFrame.Data) s.nextFrame.Data = s.nextFrame.Data[:l+len(s.dataForWriting)] copy(s.nextFrame.Data[l:], s.dataForWriting) } s.dataForWriting = nil bytesWritten = len(p) copied = true } else { bytesWritten = len(p) - len(s.dataForWriting) deadline = s.deadline if !deadline.IsZero() { if !time.Now().Before(deadline) { s.dataForWriting = nil return bytesWritten, errDeadline } if deadlineTimer == nil { deadlineTimer = utils.NewTimer() defer deadlineTimer.Stop() } deadlineTimer.Reset(deadline) } if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown { break } } s.mutex.Unlock() if !notifiedSender { s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex notifiedSender = true } if copied { s.mutex.Lock() break } if deadline.IsZero() { <-s.writeChan } else { select { case <-s.writeChan: case <-deadlineTimer.Chan(): deadlineTimer.SetRead() } } s.mutex.Lock() } if bytesWritten == len(p) { return bytesWritten, nil } if s.closeForShutdownErr != nil { return bytesWritten, s.closeForShutdownErr } else if s.cancelWriteErr != nil { return bytesWritten, s.cancelWriteErr } return bytesWritten, nil } func (s *sendStream) canBufferStreamFrame() bool { var l protocol.ByteCount if s.nextFrame != nil { l = s.nextFrame.DataLen() } return l+protocol.ByteCount(len(s.dataForWriting)) <= protocol.MaxPacketBufferSize } // popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream // maxBytes is the maximum length this frame (including frame header) will have. func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool /* has more data to send */) { s.mutex.Lock() f, hasMoreData := s.popNewOrRetransmittedStreamFrame(maxBytes) if f != nil { s.numOutstandingFrames++ } s.mutex.Unlock() if f == nil { return nil, hasMoreData } return &ackhandler.Frame{Frame: f, OnLost: s.queueRetransmission, OnAcked: s.frameAcked}, hasMoreData } func (s *sendStream) popNewOrRetransmittedStreamFrame(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more data to send */) { if s.canceledWrite || s.closeForShutdownErr != nil { return nil, false } if len(s.retransmissionQueue) > 0 { f, hasMoreRetransmissions := s.maybeGetRetransmission(maxBytes) if f != nil || hasMoreRetransmissions { if f == nil { return nil, true } // We always claim that we have more data to send. // This might be incorrect, in which case there'll be a spurious call to popStreamFrame in the future. return f, true } } if len(s.dataForWriting) == 0 && s.nextFrame == nil { if s.finishedWriting && !s.finSent { s.finSent = true return &wire.StreamFrame{ StreamID: s.streamID, Offset: s.writeOffset, DataLenPresent: true, Fin: true, }, false } return nil, false } sendWindow := s.flowController.SendWindowSize() if sendWindow == 0 { if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked { s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{ StreamID: s.streamID, MaximumStreamData: offset, }) return nil, false } return nil, true } f, hasMoreData := s.popNewStreamFrame(maxBytes, sendWindow) if dataLen := f.DataLen(); dataLen > 0 { s.writeOffset += f.DataLen() s.flowController.AddBytesSent(f.DataLen()) } f.Fin = s.finishedWriting && s.dataForWriting == nil && s.nextFrame == nil && !s.finSent if f.Fin { s.finSent = true } return f, hasMoreData } func (s *sendStream) popNewStreamFrame(maxBytes, sendWindow protocol.ByteCount) (*wire.StreamFrame, bool) { if s.nextFrame != nil { nextFrame := s.nextFrame s.nextFrame = nil maxDataLen := utils.MinByteCount(sendWindow, nextFrame.MaxDataLen(maxBytes, s.version)) if nextFrame.DataLen() > maxDataLen { s.nextFrame = wire.GetStreamFrame() s.nextFrame.StreamID = s.streamID s.nextFrame.Offset = s.writeOffset + maxDataLen s.nextFrame.Data = s.nextFrame.Data[:nextFrame.DataLen()-maxDataLen] s.nextFrame.DataLenPresent = true copy(s.nextFrame.Data, nextFrame.Data[maxDataLen:]) nextFrame.Data = nextFrame.Data[:maxDataLen] } else { s.signalWrite() } return nextFrame, s.nextFrame != nil || s.dataForWriting != nil } f := wire.GetStreamFrame() f.Fin = false f.StreamID = s.streamID f.Offset = s.writeOffset f.DataLenPresent = true f.Data = f.Data[:0] hasMoreData := s.popNewStreamFrameWithoutBuffer(f, maxBytes, sendWindow) if len(f.Data) == 0 && !f.Fin { f.PutBack() return nil, hasMoreData } return f, hasMoreData } func (s *sendStream) popNewStreamFrameWithoutBuffer(f *wire.StreamFrame, maxBytes, sendWindow protocol.ByteCount) bool { maxDataLen := f.MaxDataLen(maxBytes, s.version) if maxDataLen == 0 { // a STREAM frame must have at least one byte of data return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting } s.getDataForWriting(f, utils.MinByteCount(maxDataLen, sendWindow)) return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting } func (s *sendStream) maybeGetRetransmission(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more retransmissions */) { f := s.retransmissionQueue[0] newFrame, needsSplit := f.MaybeSplitOffFrame(maxBytes, s.version) if needsSplit { return newFrame, true } s.retransmissionQueue = s.retransmissionQueue[1:] return f, len(s.retransmissionQueue) > 0 } func (s *sendStream) hasData() bool { s.mutex.Lock() hasData := len(s.dataForWriting) > 0 s.mutex.Unlock() return hasData } func (s *sendStream) getDataForWriting(f *wire.StreamFrame, maxBytes protocol.ByteCount) { if protocol.ByteCount(len(s.dataForWriting)) <= maxBytes { f.Data = f.Data[:len(s.dataForWriting)] copy(f.Data, s.dataForWriting) s.dataForWriting = nil s.signalWrite() return } f.Data = f.Data[:maxBytes] copy(f.Data, s.dataForWriting) s.dataForWriting = s.dataForWriting[maxBytes:] if s.canBufferStreamFrame() { s.signalWrite() } } func (s *sendStream) frameAcked(f wire.Frame) { f.(*wire.StreamFrame).PutBack() s.mutex.Lock() if s.canceledWrite { s.mutex.Unlock() return } s.numOutstandingFrames-- if s.numOutstandingFrames < 0 { panic("numOutStandingFrames negative") } newlyCompleted := s.isNewlyCompleted() s.mutex.Unlock() if newlyCompleted { s.sender.onStreamCompleted(s.streamID) } } func (s *sendStream) isNewlyCompleted() bool { completed := (s.finSent || s.canceledWrite) && s.numOutstandingFrames == 0 && len(s.retransmissionQueue) == 0 if completed && !s.completed { s.completed = true return true } return false } func (s *sendStream) queueRetransmission(f wire.Frame) { sf := f.(*wire.StreamFrame) sf.DataLenPresent = true s.mutex.Lock() if s.canceledWrite { s.mutex.Unlock() return } s.retransmissionQueue = append(s.retransmissionQueue, sf) s.numOutstandingFrames-- if s.numOutstandingFrames < 0 { panic("numOutStandingFrames negative") } s.mutex.Unlock() s.sender.onHasStreamData(s.streamID) } func (s *sendStream) Close() error { s.mutex.Lock() if s.closedForShutdown { s.mutex.Unlock() return nil } if s.canceledWrite { s.mutex.Unlock() return fmt.Errorf("close called for canceled stream %d", s.streamID) } s.ctxCancel() s.finishedWriting = true s.mutex.Unlock() s.sender.onHasStreamData(s.streamID) // need to send the FIN, must be called without holding the mutex return nil } func (s *sendStream) CancelWrite(errorCode StreamErrorCode) { s.cancelWriteImpl(errorCode, fmt.Errorf("Write on stream %d canceled with error code %d", s.streamID, errorCode)) } // must be called after locking the mutex func (s *sendStream) cancelWriteImpl(errorCode qerr.StreamErrorCode, writeErr error) { s.mutex.Lock() if s.canceledWrite { s.mutex.Unlock() return } s.ctxCancel() s.canceledWrite = true s.cancelWriteErr = writeErr s.numOutstandingFrames = 0 s.retransmissionQueue = nil newlyCompleted := s.isNewlyCompleted() s.mutex.Unlock() s.signalWrite() s.sender.queueControlFrame(&wire.ResetStreamFrame{ StreamID: s.streamID, FinalSize: s.writeOffset, ErrorCode: errorCode, }) if newlyCompleted { s.sender.onStreamCompleted(s.streamID) } } func (s *sendStream) updateSendWindow(limit protocol.ByteCount) { s.mutex.Lock() hasStreamData := s.dataForWriting != nil || s.nextFrame != nil s.mutex.Unlock() s.flowController.UpdateSendWindow(limit) if hasStreamData { s.sender.onHasStreamData(s.streamID) } } func (s *sendStream) handleStopSendingFrame(frame *wire.StopSendingFrame) { s.cancelWriteImpl(frame.ErrorCode, &StreamError{ StreamID: s.streamID, ErrorCode: frame.ErrorCode, }) } func (s *sendStream) Context() context.Context { return s.ctx } func (s *sendStream) SetWriteDeadline(t time.Time) error { s.mutex.Lock() s.deadline = t s.mutex.Unlock() s.signalWrite() return nil } // CloseForShutdown closes a stream abruptly. // It makes Write unblock (and return the error) immediately. // The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. func (s *sendStream) closeForShutdown(err error) { s.mutex.Lock() s.ctxCancel() s.closedForShutdown = true s.closeForShutdownErr = err s.mutex.Unlock() s.signalWrite() } // signalWrite performs a non-blocking send on the writeChan func (s *sendStream) signalWrite() { select { case s.writeChan <- struct{}{}: default: } } quic-go-0.25.0/send_stream_test.go000066400000000000000000001161461417145451600170550ustar00rootroot00000000000000package quic import ( "bytes" "errors" "io" mrand "math/rand" "runtime" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) var _ = Describe("Send Stream", func() { const streamID protocol.StreamID = 1337 var ( str *sendStream strWithTimeout io.Writer // str wrapped with gbytes.TimeoutWriter mockFC *mocks.MockStreamFlowController mockSender *MockStreamSender ) BeforeEach(func() { mockSender = NewMockStreamSender(mockCtrl) mockFC = mocks.NewMockStreamFlowController(mockCtrl) str = newSendStream(streamID, mockSender, mockFC, protocol.VersionWhatever) timeout := scaleDuration(250 * time.Millisecond) strWithTimeout = gbytes.TimeoutWriter(str, timeout) }) expectedFrameHeaderLen := func(offset protocol.ByteCount) protocol.ByteCount { return (&wire.StreamFrame{ StreamID: streamID, Offset: offset, DataLenPresent: true, }).Length(protocol.VersionWhatever) } waitForWrite := func() { EventuallyWithOffset(0, func() bool { str.mutex.Lock() hasData := str.dataForWriting != nil || str.nextFrame != nil str.mutex.Unlock() return hasData }).Should(BeTrue()) } getDataAtOffset := func(offset, length protocol.ByteCount) []byte { b := make([]byte, length) for i := protocol.ByteCount(0); i < length; i++ { b[i] = uint8(offset + i) } return b } getData := func(length protocol.ByteCount) []byte { return getDataAtOffset(0, length) } It("gets stream id", func() { Expect(str.StreamID()).To(Equal(protocol.StreamID(1337))) }) Context("writing", func() { It("writes and gets all data at once", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) n, err := strWithTimeout.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) }() waitForWrite() mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) frame, _ := str.popStreamFrame(protocol.MaxByteCount) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("foobar"))) Expect(f.Fin).To(BeFalse()) Expect(f.Offset).To(BeZero()) Expect(f.DataLenPresent).To(BeTrue()) Expect(str.writeOffset).To(Equal(protocol.ByteCount(6))) Expect(str.dataForWriting).To(BeNil()) Eventually(done).Should(BeClosed()) }) It("writes and gets data in two turns", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() mockSender.EXPECT().onHasStreamData(streamID) n, err := strWithTimeout.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(6)) close(done) }() waitForWrite() mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3)).Times(2) frame, _ := str.popStreamFrame(expectedFrameHeaderLen(0) + 3) f := frame.Frame.(*wire.StreamFrame) Expect(f.Offset).To(BeZero()) Expect(f.Fin).To(BeFalse()) Expect(f.Data).To(Equal([]byte("foo"))) Expect(f.DataLenPresent).To(BeTrue()) frame, _ = str.popStreamFrame(protocol.MaxByteCount) f = frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("bar"))) Expect(f.Fin).To(BeFalse()) Expect(f.Offset).To(Equal(protocol.ByteCount(3))) Expect(f.DataLenPresent).To(BeTrue()) Expect(str.popStreamFrame(1000)).To(BeNil()) Eventually(done).Should(BeClosed()) }) It("bundles small writes", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() mockSender.EXPECT().onHasStreamData(streamID).Times(2) n, err := strWithTimeout.Write([]byte("foo")) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) n, err = strWithTimeout.Write([]byte("bar")) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) close(done) }() Eventually(done).Should(BeClosed()) // both Write calls returned without any data having been dequeued yet mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) frame, _ := str.popStreamFrame(protocol.MaxByteCount) f := frame.Frame.(*wire.StreamFrame) Expect(f.Offset).To(BeZero()) Expect(f.Fin).To(BeFalse()) Expect(f.Data).To(Equal([]byte("foobar"))) }) It("writes and gets data in multiple turns, for large writes", func() { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(5) var totalBytesSent protocol.ByteCount mockFC.EXPECT().AddBytesSent(gomock.Any()).Do(func(l protocol.ByteCount) { totalBytesSent += l }).Times(5) done := make(chan struct{}) go func() { defer GinkgoRecover() mockSender.EXPECT().onHasStreamData(streamID) n, err := strWithTimeout.Write(getData(5000)) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(5000)) close(done) }() waitForWrite() for i := 0; i < 5; i++ { frame, _ := str.popStreamFrame(1100) f := frame.Frame.(*wire.StreamFrame) Expect(f.Offset).To(BeNumerically("~", 1100*i, 10*i)) Expect(f.Fin).To(BeFalse()) Expect(f.Data).To(Equal(getDataAtOffset(f.Offset, f.DataLen()))) Expect(f.DataLenPresent).To(BeTrue()) } Expect(totalBytesSent).To(Equal(protocol.ByteCount(5000))) Eventually(done).Should(BeClosed()) }) It("unblocks Write as soon as a STREAM frame can be buffered", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) _, err := strWithTimeout.Write(getData(protocol.MaxPacketBufferSize + 3)) Expect(err).ToNot(HaveOccurred()) }() waitForWrite() mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2)) frame, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0) + 2) Expect(hasMoreData).To(BeTrue()) f := frame.Frame.(*wire.StreamFrame) Expect(f.DataLen()).To(Equal(protocol.ByteCount(2))) Consistently(done).ShouldNot(BeClosed()) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1)) frame, hasMoreData = str.popStreamFrame(expectedFrameHeaderLen(1) + 1) Expect(hasMoreData).To(BeTrue()) f = frame.Frame.(*wire.StreamFrame) Expect(f.DataLen()).To(Equal(protocol.ByteCount(1))) Eventually(done).Should(BeClosed()) }) It("only unblocks Write once a previously buffered STREAM frame has been fully dequeued", func() { mockSender.EXPECT().onHasStreamData(streamID) _, err := strWithTimeout.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) _, err := str.Write(getData(protocol.MaxPacketBufferSize)) Expect(err).ToNot(HaveOccurred()) }() waitForWrite() mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2)) frame, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0) + 2) Expect(hasMoreData).To(BeTrue()) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("fo"))) Consistently(done).ShouldNot(BeClosed()) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(4)) frame, hasMoreData = str.popStreamFrame(expectedFrameHeaderLen(2) + 4) Expect(hasMoreData).To(BeTrue()) f = frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("obar"))) Eventually(done).Should(BeClosed()) }) It("popStreamFrame returns nil if no data is available", func() { frame, hasMoreData := str.popStreamFrame(1000) Expect(frame).To(BeNil()) Expect(hasMoreData).To(BeFalse()) }) It("says if it has more data for writing", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) n, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 100)) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(100)) }() waitForWrite() mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2) frame, hasMoreData := str.popStreamFrame(50) Expect(frame).ToNot(BeNil()) Expect(frame.Frame.(*wire.StreamFrame).Fin).To(BeFalse()) Expect(hasMoreData).To(BeTrue()) frame, hasMoreData = str.popStreamFrame(protocol.MaxByteCount) Expect(frame).ToNot(BeNil()) Expect(frame.Frame.(*wire.StreamFrame).Fin).To(BeFalse()) Expect(hasMoreData).To(BeFalse()) frame, _ = str.popStreamFrame(protocol.MaxByteCount) Expect(frame).To(BeNil()) Eventually(done).Should(BeClosed()) }) It("copies the slice while writing", func() { frameHeaderSize := protocol.ByteCount(4) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1)) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2)) s := []byte("foo") done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) n, err := strWithTimeout.Write(s) Expect(err).ToNot(HaveOccurred()) Expect(n).To(Equal(3)) }() waitForWrite() frame, _ := str.popStreamFrame(frameHeaderSize + 1) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("f"))) frame, _ = str.popStreamFrame(100) Expect(frame).ToNot(BeNil()) f = frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("oo"))) s[1] = 'e' Expect(f.Data).To(Equal([]byte("oo"))) Eventually(done).Should(BeClosed()) }) It("returns when given a nil input", func() { n, err := strWithTimeout.Write(nil) Expect(n).To(BeZero()) Expect(err).ToNot(HaveOccurred()) }) It("returns when given an empty slice", func() { n, err := strWithTimeout.Write([]byte("")) Expect(n).To(BeZero()) Expect(err).ToNot(HaveOccurred()) }) It("cancels the context when Close is called", func() { mockSender.EXPECT().onHasStreamData(streamID) Expect(str.Context().Done()).ToNot(BeClosed()) Expect(str.Close()).To(Succeed()) Expect(str.Context().Done()).To(BeClosed()) }) Context("flow control blocking", func() { It("queues a BLOCKED frame if the stream is flow control blocked", func() { mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(0)) mockFC.EXPECT().IsNewlyBlocked().Return(true, protocol.ByteCount(12)) mockSender.EXPECT().queueControlFrame(&wire.StreamDataBlockedFrame{ StreamID: streamID, MaximumStreamData: 12, }) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) }() waitForWrite() f, hasMoreData := str.popStreamFrame(1000) Expect(f).To(BeNil()) Expect(hasMoreData).To(BeFalse()) // make the Write go routine return str.closeForShutdown(nil) Eventually(done).Should(BeClosed()) }) It("says that it doesn't have any more data, when it is flow control blocked", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) }() waitForWrite() // first pop a STREAM frame of the maximum size allowed by flow control mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(3)) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3)) f, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0) + 3) Expect(f).ToNot(BeNil()) Expect(hasMoreData).To(BeTrue()) // try to pop again, this time noticing that we're blocked mockFC.EXPECT().SendWindowSize() // don't use offset 3 here, to make sure the BLOCKED frame contains the number returned by the flow controller mockFC.EXPECT().IsNewlyBlocked().Return(true, protocol.ByteCount(10)) mockSender.EXPECT().queueControlFrame(&wire.StreamDataBlockedFrame{ StreamID: streamID, MaximumStreamData: 10, }) f, hasMoreData = str.popStreamFrame(1000) Expect(f).To(BeNil()) Expect(hasMoreData).To(BeFalse()) // make the Write go routine return str.closeForShutdown(nil) Eventually(done).Should(BeClosed()) }) }) Context("deadlines", func() { It("returns an error when Write is called after the deadline", func() { str.SetWriteDeadline(time.Now().Add(-time.Second)) n, err := strWithTimeout.Write([]byte("foobar")) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) }) It("unblocks after the deadline", func() { mockSender.EXPECT().onHasStreamData(streamID) deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) str.SetWriteDeadline(deadline) n, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond))) }) It("unblocks when the deadline is changed to the past", func() { mockSender.EXPECT().onHasStreamData(streamID) str.SetWriteDeadline(time.Now().Add(time.Hour)) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := str.Write(getData(5000)) Expect(err).To(MatchError(errDeadline)) close(done) }() Consistently(done).ShouldNot(BeClosed()) str.SetWriteDeadline(time.Now().Add(-time.Hour)) Eventually(done).Should(BeClosed()) }) It("returns the number of bytes written, when the deadline expires", func() { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes() mockFC.EXPECT().AddBytesSent(gomock.Any()) deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) str.SetWriteDeadline(deadline) var n int writeReturned := make(chan struct{}) go func() { defer GinkgoRecover() defer close(writeReturned) mockSender.EXPECT().onHasStreamData(streamID) var err error n, err = strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError(errDeadline)) Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond))) }() waitForWrite() frame, hasMoreData := str.popStreamFrame(50) Expect(frame).ToNot(BeNil()) Expect(hasMoreData).To(BeTrue()) Eventually(writeReturned, scaleDuration(80*time.Millisecond)).Should(BeClosed()) Expect(n).To(BeEquivalentTo(frame.Frame.(*wire.StreamFrame).DataLen())) }) It("doesn't pop any data after the deadline expired", func() { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes() mockFC.EXPECT().AddBytesSent(gomock.Any()) deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) str.SetWriteDeadline(deadline) writeReturned := make(chan struct{}) go func() { defer GinkgoRecover() defer close(writeReturned) mockSender.EXPECT().onHasStreamData(streamID) _, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError(errDeadline)) }() waitForWrite() frame, hasMoreData := str.popStreamFrame(50) Expect(frame).ToNot(BeNil()) Expect(hasMoreData).To(BeTrue()) Eventually(writeReturned, scaleDuration(80*time.Millisecond)).Should(BeClosed()) frame, hasMoreData = str.popStreamFrame(50) Expect(frame).To(BeNil()) Expect(hasMoreData).To(BeFalse()) }) It("doesn't unblock if the deadline is changed before the first one expires", func() { mockSender.EXPECT().onHasStreamData(streamID) deadline1 := time.Now().Add(scaleDuration(50 * time.Millisecond)) deadline2 := time.Now().Add(scaleDuration(100 * time.Millisecond)) str.SetWriteDeadline(deadline1) done := make(chan struct{}) go func() { defer GinkgoRecover() time.Sleep(scaleDuration(20 * time.Millisecond)) str.SetWriteDeadline(deadline2) // make sure that this was actually execute before the deadline expires Expect(time.Now()).To(BeTemporally("<", deadline1)) close(done) }() runtime.Gosched() n, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond))) Eventually(done).Should(BeClosed()) }) It("unblocks earlier, when a new deadline is set", func() { mockSender.EXPECT().onHasStreamData(streamID) deadline1 := time.Now().Add(scaleDuration(200 * time.Millisecond)) deadline2 := time.Now().Add(scaleDuration(50 * time.Millisecond)) done := make(chan struct{}) go func() { defer GinkgoRecover() time.Sleep(scaleDuration(10 * time.Millisecond)) str.SetWriteDeadline(deadline2) // make sure that this was actually execute before the deadline expires Expect(time.Now()).To(BeTemporally("<", deadline2)) close(done) }() str.SetWriteDeadline(deadline1) runtime.Gosched() _, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError(errDeadline)) Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond))) Eventually(done).Should(BeClosed()) }) It("doesn't unblock if the deadline is removed", func() { mockSender.EXPECT().onHasStreamData(streamID) deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) str.SetWriteDeadline(deadline) deadlineUnset := make(chan struct{}) go func() { defer GinkgoRecover() time.Sleep(scaleDuration(20 * time.Millisecond)) str.SetWriteDeadline(time.Time{}) // make sure that this was actually execute before the deadline expires Expect(time.Now()).To(BeTemporally("<", deadline)) close(deadlineUnset) }() done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError("test done")) close(done) }() runtime.Gosched() Eventually(deadlineUnset).Should(BeClosed()) Consistently(done, scaleDuration(100*time.Millisecond)).ShouldNot(BeClosed()) // make the go routine return str.closeForShutdown(errors.New("test done")) Eventually(done).Should(BeClosed()) }) }) Context("closing", func() { It("doesn't allow writes after it has been closed", func() { mockSender.EXPECT().onHasStreamData(streamID) str.Close() _, err := strWithTimeout.Write([]byte("foobar")) Expect(err).To(MatchError("write on closed stream 1337")) }) It("allows FIN", func() { mockSender.EXPECT().onHasStreamData(streamID) str.Close() frame, hasMoreData := str.popStreamFrame(1000) Expect(frame).ToNot(BeNil()) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(BeEmpty()) Expect(f.Fin).To(BeTrue()) Expect(f.DataLenPresent).To(BeTrue()) Expect(hasMoreData).To(BeFalse()) }) It("doesn't send a FIN when there's still data", func() { const frameHeaderLen protocol.ByteCount = 4 mockSender.EXPECT().onHasStreamData(streamID).Times(2) _, err := strWithTimeout.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) Expect(str.Close()).To(Succeed()) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2) frame, _ := str.popStreamFrame(3 + frameHeaderLen) Expect(frame).ToNot(BeNil()) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("foo"))) Expect(f.Fin).To(BeFalse()) frame, _ = str.popStreamFrame(protocol.MaxByteCount) f = frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal([]byte("bar"))) Expect(f.Fin).To(BeTrue()) }) It("doesn't send a FIN when there's still data, for long writes", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) mockSender.EXPECT().onHasStreamData(streamID) _, err := strWithTimeout.Write(getData(5000)) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().onHasStreamData(streamID) Expect(str.Close()).To(Succeed()) }() waitForWrite() for i := 1; i <= 5; i++ { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(gomock.Any()) if i == 5 { Eventually(done).Should(BeClosed()) } frame, _ := str.popStreamFrame(1100) Expect(frame).ToNot(BeNil()) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(Equal(getDataAtOffset(f.Offset, f.DataLen()))) Expect(f.Fin).To(Equal(i == 5)) // the last frame should have the FIN bit set } }) It("doesn't allow FIN after it is closed for shutdown", func() { str.closeForShutdown(errors.New("test")) f, hasMoreData := str.popStreamFrame(1000) Expect(f).To(BeNil()) Expect(hasMoreData).To(BeFalse()) Expect(str.Close()).To(Succeed()) f, hasMoreData = str.popStreamFrame(1000) Expect(f).To(BeNil()) Expect(hasMoreData).To(BeFalse()) }) It("doesn't allow FIN twice", func() { mockSender.EXPECT().onHasStreamData(streamID) str.Close() frame, _ := str.popStreamFrame(1000) Expect(frame).ToNot(BeNil()) f := frame.Frame.(*wire.StreamFrame) Expect(f.Data).To(BeEmpty()) Expect(f.Fin).To(BeTrue()) frame, hasMoreData := str.popStreamFrame(1000) Expect(frame).To(BeNil()) Expect(hasMoreData).To(BeFalse()) }) }) Context("closing for shutdown", func() { testErr := errors.New("test") It("returns errors when the stream is cancelled", func() { str.closeForShutdown(testErr) n, err := strWithTimeout.Write([]byte("foo")) Expect(n).To(BeZero()) Expect(err).To(MatchError(testErr)) }) It("doesn't get data for writing if an error occurred", func() { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(gomock.Any()) mockSender.EXPECT().onHasStreamData(streamID) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError(testErr)) close(done) }() waitForWrite() frame, hasMoreData := str.popStreamFrame(50) // get a STREAM frame containing some data, but not all Expect(frame).ToNot(BeNil()) Expect(hasMoreData).To(BeTrue()) str.closeForShutdown(testErr) frame, hasMoreData = str.popStreamFrame(1000) Expect(frame).To(BeNil()) Expect(hasMoreData).To(BeFalse()) Eventually(done).Should(BeClosed()) }) It("cancels the context", func() { Expect(str.Context().Done()).ToNot(BeClosed()) str.closeForShutdown(testErr) Expect(str.Context().Done()).To(BeClosed()) }) }) }) Context("handling MAX_STREAM_DATA frames", func() { It("informs the flow controller", func() { mockFC.EXPECT().UpdateSendWindow(protocol.ByteCount(0x1337)) str.updateSendWindow(0x1337) }) It("says when it has data for sending", func() { mockFC.EXPECT().UpdateSendWindow(gomock.Any()) mockSender.EXPECT().onHasStreamData(streamID) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) close(done) }() waitForWrite() mockSender.EXPECT().onHasStreamData(streamID) str.updateSendWindow(42) // make sure the Write go routine returns str.closeForShutdown(nil) Eventually(done).Should(BeClosed()) }) }) Context("stream cancellations", func() { Context("canceling writing", func() { It("queues a RESET_STREAM frame", func() { gomock.InOrder( mockSender.EXPECT().queueControlFrame(&wire.ResetStreamFrame{ StreamID: streamID, FinalSize: 1234, ErrorCode: 9876, }), mockSender.EXPECT().onStreamCompleted(streamID), ) str.writeOffset = 1234 str.CancelWrite(9876) }) // This test is inherently racy, as it tests a concurrent call to Write() and CancelRead(). // A single successful run of this test therefore doesn't mean a lot, // for reliable results it has to be run many times. It("returns a nil error when the whole slice has been sent out", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).MaxTimes(1) mockSender.EXPECT().onHasStreamData(streamID).MaxTimes(1) mockSender.EXPECT().onStreamCompleted(streamID).MaxTimes(1) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).MaxTimes(1) mockFC.EXPECT().AddBytesSent(gomock.Any()).MaxTimes(1) errChan := make(chan error) go func() { defer GinkgoRecover() n, err := strWithTimeout.Write(getData(100)) if n == 0 { errChan <- nil return } errChan <- err }() runtime.Gosched() go str.popStreamFrame(protocol.MaxByteCount) go str.CancelWrite(1234) Eventually(errChan).Should(Receive(Not(HaveOccurred()))) }) It("unblocks Write", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onHasStreamData(streamID) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(gomock.Any()) writeReturned := make(chan struct{}) var n int go func() { defer GinkgoRecover() var err error n, err = strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234")) close(writeReturned) }() waitForWrite() frame, _ := str.popStreamFrame(50) Expect(frame).ToNot(BeNil()) mockSender.EXPECT().onStreamCompleted(streamID) str.CancelWrite(1234) Eventually(writeReturned).Should(BeClosed()) Expect(n).To(BeEquivalentTo(frame.Frame.(*wire.StreamFrame).DataLen())) }) It("doesn't pop STREAM frames after being canceled", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onHasStreamData(streamID) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(gomock.Any()) writeReturned := make(chan struct{}) go func() { defer GinkgoRecover() strWithTimeout.Write(getData(100)) close(writeReturned) }() waitForWrite() frame, hasMoreData := str.popStreamFrame(50) Expect(hasMoreData).To(BeTrue()) Expect(frame).ToNot(BeNil()) mockSender.EXPECT().onStreamCompleted(streamID) str.CancelWrite(1234) frame, hasMoreData = str.popStreamFrame(10) Expect(frame).To(BeNil()) Expect(hasMoreData).To(BeFalse()) Eventually(writeReturned).Should(BeClosed()) }) It("doesn't pop STREAM frames after being canceled, for large writes", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onHasStreamData(streamID) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(gomock.Any()) writeReturned := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write(getData(5000)) Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234")) close(writeReturned) }() waitForWrite() frame, hasMoreData := str.popStreamFrame(50) Expect(hasMoreData).To(BeTrue()) Expect(frame).ToNot(BeNil()) mockSender.EXPECT().onStreamCompleted(streamID) str.CancelWrite(1234) frame, hasMoreData = str.popStreamFrame(10) Expect(hasMoreData).To(BeFalse()) Expect(frame).To(BeNil()) Eventually(writeReturned).Should(BeClosed()) }) It("ignores acknowledgements for STREAM frames after it was cancelled", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onHasStreamData(streamID) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(gomock.Any()) writeReturned := make(chan struct{}) go func() { defer GinkgoRecover() strWithTimeout.Write(getData(100)) close(writeReturned) }() waitForWrite() frame, hasMoreData := str.popStreamFrame(50) Expect(hasMoreData).To(BeTrue()) Expect(frame).ToNot(BeNil()) mockSender.EXPECT().onStreamCompleted(streamID) str.CancelWrite(1234) frame.OnAcked(frame.Frame) }) It("cancels the context", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onStreamCompleted(gomock.Any()) Expect(str.Context().Done()).ToNot(BeClosed()) str.CancelWrite(1234) Expect(str.Context().Done()).To(BeClosed()) }) It("doesn't allow further calls to Write", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onStreamCompleted(gomock.Any()) str.CancelWrite(1234) _, err := strWithTimeout.Write([]byte("foobar")) Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234")) }) It("only cancels once", func() { mockSender.EXPECT().queueControlFrame(&wire.ResetStreamFrame{StreamID: streamID, ErrorCode: 1234}) mockSender.EXPECT().onStreamCompleted(gomock.Any()) str.CancelWrite(1234) str.CancelWrite(4321) }) It("queues a RESET_STREAM frame, even if the stream was already closed", func() { mockSender.EXPECT().onHasStreamData(streamID) mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f).To(BeAssignableToTypeOf(&wire.ResetStreamFrame{})) }) mockSender.EXPECT().onStreamCompleted(gomock.Any()) Expect(str.Close()).To(Succeed()) // don't EXPECT any calls to queueControlFrame str.CancelWrite(123) }) }) Context("receiving STOP_SENDING frames", func() { It("queues a RESET_STREAM frames, and copies the error code from the STOP_SENDING frame", func() { mockSender.EXPECT().queueControlFrame(&wire.ResetStreamFrame{ StreamID: streamID, ErrorCode: 101, }) mockSender.EXPECT().onStreamCompleted(gomock.Any()) str.handleStopSendingFrame(&wire.StopSendingFrame{ StreamID: streamID, ErrorCode: 101, }) }) It("unblocks Write", func() { mockSender.EXPECT().onHasStreamData(streamID) mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onStreamCompleted(gomock.Any()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := str.Write(getData(5000)) Expect(err).To(MatchError(&StreamError{ StreamID: streamID, ErrorCode: 1234, })) close(done) }() waitForWrite() str.handleStopSendingFrame(&wire.StopSendingFrame{ StreamID: streamID, ErrorCode: 123, }) Eventually(done).Should(BeClosed()) }) It("doesn't allow further calls to Write", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().onStreamCompleted(gomock.Any()) str.handleStopSendingFrame(&wire.StopSendingFrame{ StreamID: streamID, ErrorCode: 123, }) _, err := str.Write([]byte("foobar")) Expect(err).To(MatchError(&StreamError{ StreamID: streamID, ErrorCode: 1234, })) }) }) }) Context("retransmissions", func() { It("queues and retrieves frames", func() { str.numOutstandingFrames = 1 f := &wire.StreamFrame{ Data: []byte("foobar"), Offset: 0x42, DataLenPresent: false, } mockSender.EXPECT().onHasStreamData(streamID) str.queueRetransmission(f) frame, _ := str.popStreamFrame(protocol.MaxByteCount) Expect(frame).ToNot(BeNil()) f = frame.Frame.(*wire.StreamFrame) Expect(f.Offset).To(Equal(protocol.ByteCount(0x42))) Expect(f.Data).To(Equal([]byte("foobar"))) Expect(f.DataLenPresent).To(BeTrue()) }) It("splits a retransmission", func() { str.numOutstandingFrames = 1 sf := &wire.StreamFrame{ Data: []byte("foobar"), Offset: 0x42, DataLenPresent: false, } mockSender.EXPECT().onHasStreamData(streamID) str.queueRetransmission(sf) frame, hasMoreData := str.popStreamFrame(sf.Length(str.version) - 3) Expect(frame).ToNot(BeNil()) f := frame.Frame.(*wire.StreamFrame) Expect(hasMoreData).To(BeTrue()) Expect(f.Offset).To(Equal(protocol.ByteCount(0x42))) Expect(f.Data).To(Equal([]byte("foo"))) Expect(f.DataLenPresent).To(BeTrue()) frame, _ = str.popStreamFrame(protocol.MaxByteCount) Expect(frame).ToNot(BeNil()) f = frame.Frame.(*wire.StreamFrame) Expect(f.Offset).To(Equal(protocol.ByteCount(0x45))) Expect(f.Data).To(Equal([]byte("bar"))) Expect(f.DataLenPresent).To(BeTrue()) }) It("returns nil if the size is too small", func() { str.numOutstandingFrames = 1 f := &wire.StreamFrame{ Data: []byte("foobar"), Offset: 0x42, DataLenPresent: false, } mockSender.EXPECT().onHasStreamData(streamID) str.queueRetransmission(f) frame, hasMoreData := str.popStreamFrame(2) Expect(hasMoreData).To(BeTrue()) Expect(frame).To(BeNil()) }) It("queues lost STREAM frames", func() { mockSender.EXPECT().onHasStreamData(streamID) mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) close(done) }() waitForWrite() frame, _ := str.popStreamFrame(protocol.MaxByteCount) Eventually(done).Should(BeClosed()) Expect(frame).ToNot(BeNil()) Expect(frame.Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar"))) // now lose the frame mockSender.EXPECT().onHasStreamData(streamID) frame.OnLost(frame.Frame) newFrame, _ := str.popStreamFrame(protocol.MaxByteCount) Expect(newFrame).ToNot(BeNil()) Expect(newFrame.Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar"))) }) It("doesn't queue retransmissions for a stream that was canceled", func() { mockSender.EXPECT().onHasStreamData(streamID) mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := str.Write([]byte("foobar")) Expect(err).ToNot(HaveOccurred()) close(done) }() waitForWrite() f, _ := str.popStreamFrame(100) Expect(f).ToNot(BeNil()) gomock.InOrder( mockSender.EXPECT().queueControlFrame(gomock.Any()), mockSender.EXPECT().onStreamCompleted(streamID), ) str.CancelWrite(9876) // don't EXPECT any calls to onHasStreamData f.OnLost(f.Frame) Expect(str.retransmissionQueue).To(BeEmpty()) }) }) Context("determining when a stream is completed", func() { BeforeEach(func() { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes() mockFC.EXPECT().AddBytesSent(gomock.Any()).AnyTimes() }) It("says when a stream is completed", func() { mockSender.EXPECT().onHasStreamData(streamID) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write(make([]byte, 100)) Expect(err).ToNot(HaveOccurred()) close(done) }() waitForWrite() // get a bunch of small frames (max. 20 bytes) var frames []ackhandler.Frame for { frame, hasMoreData := str.popStreamFrame(20) if frame == nil { continue } frames = append(frames, *frame) if !hasMoreData { break } } Eventually(done).Should(BeClosed()) // Acknowledge all frames. // We don't expect the stream to be completed, since we still need to send the FIN. for _, f := range frames { f.OnAcked(f.Frame) } // Now close the stream and acknowledge the FIN. mockSender.EXPECT().onHasStreamData(streamID) Expect(str.Close()).To(Succeed()) frame, _ := str.popStreamFrame(protocol.MaxByteCount) Expect(frame).ToNot(BeNil()) mockSender.EXPECT().onStreamCompleted(streamID) frame.OnAcked(frame.Frame) }) It("says when a stream is completed, if Close() is called before popping the frame", func() { mockSender.EXPECT().onHasStreamData(streamID).Times(2) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write(make([]byte, 100)) Expect(err).ToNot(HaveOccurred()) close(done) }() waitForWrite() Eventually(done).Should(BeClosed()) Expect(str.Close()).To(Succeed()) frame, hasMoreData := str.popStreamFrame(protocol.MaxByteCount) Expect(hasMoreData).To(BeFalse()) Expect(frame).ToNot(BeNil()) Expect(frame.Frame.(*wire.StreamFrame).Fin).To(BeTrue()) mockSender.EXPECT().onStreamCompleted(streamID) frame.OnAcked(frame.Frame) }) It("doesn't say it's completed when there are frames waiting to be retransmitted", func() { mockSender.EXPECT().onHasStreamData(streamID) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := strWithTimeout.Write(getData(100)) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().onHasStreamData(streamID) Expect(str.Close()).To(Succeed()) close(done) }() waitForWrite() // get a bunch of small frames (max. 20 bytes) var frames []ackhandler.Frame for { frame, _ := str.popStreamFrame(20) if frame == nil { continue } frames = append(frames, *frame) if frame.Frame.(*wire.StreamFrame).Fin { break } } Eventually(done).Should(BeClosed()) // lose the first frame, acknowledge all others for _, f := range frames[1:] { f.OnAcked(f.Frame) } mockSender.EXPECT().onHasStreamData(streamID) frames[0].OnLost(frames[0].Frame) // get the retransmission and acknowledge it ret, _ := str.popStreamFrame(protocol.MaxByteCount) Expect(ret).ToNot(BeNil()) mockSender.EXPECT().onStreamCompleted(streamID) ret.OnAcked(ret.Frame) }) // This test is kind of an integration test. // It writes 4 MB of data, and pops STREAM frames that sometimes are and sometimes aren't limited by flow control. // Half of these STREAM frames are then received and their content saved, while the other half is reported lost // and has to be retransmitted. It("retransmits data until everything has been acknowledged", func() { const dataLen = 1 << 22 // 4 MB mockSender.EXPECT().onHasStreamData(streamID).AnyTimes() mockFC.EXPECT().SendWindowSize().DoAndReturn(func() protocol.ByteCount { return protocol.ByteCount(mrand.Intn(500)) + 50 }).AnyTimes() mockFC.EXPECT().AddBytesSent(gomock.Any()).AnyTimes() data := make([]byte, dataLen) _, err := mrand.Read(data) Expect(err).ToNot(HaveOccurred()) done := make(chan struct{}) go func() { defer GinkgoRecover() defer close(done) _, err := str.Write(data) Expect(err).ToNot(HaveOccurred()) str.Close() }() var completed bool mockSender.EXPECT().onStreamCompleted(streamID).Do(func(protocol.StreamID) { completed = true }) received := make([]byte, dataLen) for { if completed { break } f, _ := str.popStreamFrame(protocol.ByteCount(mrand.Intn(300) + 100)) if f == nil { continue } sf := f.Frame.(*wire.StreamFrame) // 50%: acknowledge the frame and save the data // 50%: lose the frame if mrand.Intn(100) < 50 { copy(received[sf.Offset:sf.Offset+sf.DataLen()], sf.Data) f.OnAcked(f.Frame) } else { f.OnLost(f.Frame) } } Expect(received).To(Equal(data)) }) }) }) quic-go-0.25.0/server.go000066400000000000000000000506411417145451600150150ustar00rootroot00000000000000package quic import ( "bytes" "context" "crypto/rand" "crypto/tls" "errors" "fmt" "net" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" ) // packetHandler handles packets type packetHandler interface { handlePacket(*receivedPacket) shutdown() destroy(error) getPerspective() protocol.Perspective } type unknownPacketHandler interface { handlePacket(*receivedPacket) setCloseError(error) } type packetHandlerManager interface { AddWithConnID(protocol.ConnectionID, protocol.ConnectionID, func() packetHandler) bool Destroy() error sessionRunner SetServer(unknownPacketHandler) CloseServer() } type quicSession interface { EarlySession earlySessionReady() <-chan struct{} handlePacket(*receivedPacket) GetVersion() protocol.VersionNumber getPerspective() protocol.Perspective run() error destroy(error) shutdown() } // A Listener of QUIC type baseServer struct { mutex sync.Mutex acceptEarlySessions bool tlsConf *tls.Config config *Config conn connection // If the server is started with ListenAddr, we create a packet conn. // If it is started with Listen, we take a packet conn as a parameter. createdPacketConn bool tokenGenerator *handshake.TokenGenerator sessionHandler packetHandlerManager receivedPackets chan *receivedPacket // set as a member, so they can be set in the tests newSession func( sendConn, sessionRunner, protocol.ConnectionID, /* original dest connection ID */ *protocol.ConnectionID, /* retry src connection ID */ protocol.ConnectionID, /* client dest connection ID */ protocol.ConnectionID, /* destination connection ID */ protocol.ConnectionID, /* source connection ID */ protocol.StatelessResetToken, *Config, *tls.Config, *handshake.TokenGenerator, bool, /* enable 0-RTT */ logging.ConnectionTracer, uint64, utils.Logger, protocol.VersionNumber, ) quicSession serverError error errorChan chan struct{} closed bool running chan struct{} // closed as soon as run() returns sessionQueue chan quicSession sessionQueueLen int32 // to be used as an atomic logger utils.Logger } var ( _ Listener = &baseServer{} _ unknownPacketHandler = &baseServer{} ) type earlyServer struct{ *baseServer } var _ EarlyListener = &earlyServer{} func (s *earlyServer) Accept(ctx context.Context) (EarlySession, error) { return s.baseServer.accept(ctx) } // ListenAddr creates a QUIC server listening on a given address. // The tls.Config must not be nil and must contain a certificate configuration. // The quic.Config may be nil, in that case the default values will be used. func ListenAddr(addr string, tlsConf *tls.Config, config *Config) (Listener, error) { return listenAddr(addr, tlsConf, config, false) } // ListenAddrEarly works like ListenAddr, but it returns sessions before the handshake completes. func ListenAddrEarly(addr string, tlsConf *tls.Config, config *Config) (EarlyListener, error) { s, err := listenAddr(addr, tlsConf, config, true) if err != nil { return nil, err } return &earlyServer{s}, nil } func listenAddr(addr string, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) { udpAddr, err := net.ResolveUDPAddr("udp", addr) if err != nil { return nil, err } conn, err := net.ListenUDP("udp", udpAddr) if err != nil { return nil, err } serv, err := listen(conn, tlsConf, config, acceptEarly) if err != nil { return nil, err } serv.createdPacketConn = true return serv, nil } // Listen listens for QUIC connections on a given 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. A single net.PacketConn only be used for a single call to Listen. // The PacketConn can be used for simultaneous calls to Dial. QUIC connection // IDs are used for demultiplexing the different connections. The tls.Config // must not be nil and must contain a certificate configuration. The // tls.Config.CipherSuites allows setting of TLS 1.3 cipher suites. Furthermore, // it must define an application control (using NextProtos). The quic.Config may // be nil, in that case the default values will be used. func Listen(conn net.PacketConn, tlsConf *tls.Config, config *Config) (Listener, error) { return listen(conn, tlsConf, config, false) } // ListenEarly works like Listen, but it returns sessions before the handshake completes. func ListenEarly(conn net.PacketConn, tlsConf *tls.Config, config *Config) (EarlyListener, error) { s, err := listen(conn, tlsConf, config, true) if err != nil { return nil, err } return &earlyServer{s}, nil } func listen(conn net.PacketConn, tlsConf *tls.Config, config *Config, acceptEarly bool) (*baseServer, error) { if tlsConf == nil { return nil, errors.New("quic: tls.Config not set") } if err := validateConfig(config); err != nil { return nil, err } config = populateServerConfig(config) for _, v := range config.Versions { if !protocol.IsValidVersion(v) { return nil, fmt.Errorf("%s is not a valid QUIC version", v) } } sessionHandler, err := getMultiplexer().AddConn(conn, config.ConnectionIDLength, config.StatelessResetKey, config.Tracer) if err != nil { return nil, err } tokenGenerator, err := handshake.NewTokenGenerator(rand.Reader) if err != nil { return nil, err } c, err := wrapConn(conn) if err != nil { return nil, err } s := &baseServer{ conn: c, tlsConf: tlsConf, config: config, tokenGenerator: tokenGenerator, sessionHandler: sessionHandler, sessionQueue: make(chan quicSession), errorChan: make(chan struct{}), running: make(chan struct{}), receivedPackets: make(chan *receivedPacket, protocol.MaxServerUnprocessedPackets), newSession: newSession, logger: utils.DefaultLogger.WithPrefix("server"), acceptEarlySessions: acceptEarly, } go s.run() sessionHandler.SetServer(s) s.logger.Debugf("Listening for %s connections on %s", conn.LocalAddr().Network(), conn.LocalAddr().String()) return s, nil } func (s *baseServer) run() { defer close(s.running) for { select { case <-s.errorChan: return default: } select { case <-s.errorChan: return case p := <-s.receivedPackets: if bufferStillInUse := s.handlePacketImpl(p); !bufferStillInUse { p.buffer.Release() } } } } var defaultAcceptToken = func(clientAddr net.Addr, token *Token) bool { if token == nil { return false } validity := protocol.TokenValidity if token.IsRetryToken { validity = protocol.RetryTokenValidity } if time.Now().After(token.SentTime.Add(validity)) { return false } var sourceAddr string if udpAddr, ok := clientAddr.(*net.UDPAddr); ok { sourceAddr = udpAddr.IP.String() } else { sourceAddr = clientAddr.String() } return sourceAddr == token.RemoteAddr } // Accept returns sessions that already completed the handshake. // It is only valid if acceptEarlySessions is false. func (s *baseServer) Accept(ctx context.Context) (Session, error) { return s.accept(ctx) } func (s *baseServer) accept(ctx context.Context) (quicSession, error) { select { case <-ctx.Done(): return nil, ctx.Err() case sess := <-s.sessionQueue: atomic.AddInt32(&s.sessionQueueLen, -1) return sess, nil case <-s.errorChan: return nil, s.serverError } } // Close the server func (s *baseServer) Close() error { s.mutex.Lock() if s.closed { s.mutex.Unlock() return nil } if s.serverError == nil { s.serverError = errors.New("server closed") } // If the server was started with ListenAddr, we created the packet conn. // We need to close it in order to make the go routine reading from that conn return. createdPacketConn := s.createdPacketConn s.closed = true close(s.errorChan) s.mutex.Unlock() <-s.running s.sessionHandler.CloseServer() if createdPacketConn { return s.sessionHandler.Destroy() } return nil } func (s *baseServer) setCloseError(e error) { s.mutex.Lock() defer s.mutex.Unlock() if s.closed { return } s.closed = true s.serverError = e close(s.errorChan) } // Addr returns the server's network address func (s *baseServer) Addr() net.Addr { return s.conn.LocalAddr() } func (s *baseServer) handlePacket(p *receivedPacket) { select { case s.receivedPackets <- p: default: s.logger.Debugf("Dropping packet from %s (%d bytes). Server receive queue full.", p.remoteAddr, p.Size()) if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) } } } func (s *baseServer) handlePacketImpl(p *receivedPacket) bool /* is the buffer still in use? */ { if wire.IsVersionNegotiationPacket(p.data) { s.logger.Debugf("Dropping Version Negotiation packet.") if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) } return false } // If we're creating a new session, the packet will be passed to the session. // The header will then be parsed again. hdr, _, _, err := wire.ParsePacket(p.data, s.config.ConnectionIDLength) if err != nil && err != wire.ErrUnsupportedVersion { if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Error parsing packet: %s", err) return false } // Short header packets should never end up here in the first place if !hdr.IsLongHeader { panic(fmt.Sprintf("misrouted packet: %#v", hdr)) } if hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize { s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size()) if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) } return false } // send a Version Negotiation Packet if the client is speaking a different protocol version if !protocol.IsSupportedVersion(s.config.Versions, hdr.Version) { if p.Size() < protocol.MinUnknownVersionPacketSize { s.logger.Debugf("Dropping a packet with an unknown version that is too small (%d bytes)", p.Size()) if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket) } return false } if !s.config.DisableVersionNegotiationPackets { go s.sendVersionNegotiationPacket(p, hdr) } return false } if hdr.IsLongHeader && hdr.Type != protocol.PacketTypeInitial { // Drop long header packets. // There's little point in sending a Stateless Reset, since the client // might not have received the token yet. s.logger.Debugf("Dropping long header packet of type %s (%d bytes)", hdr.Type, len(p.data)) if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropUnexpectedPacket) } return false } s.logger.Debugf("<- Received Initial packet.") if err := s.handleInitialImpl(p, hdr); err != nil { s.logger.Errorf("Error occurred handling initial packet: %s", err) } // Don't put the packet buffer back. // handleInitialImpl deals with the buffer. return true } func (s *baseServer) handleInitialImpl(p *receivedPacket, hdr *wire.Header) error { if len(hdr.Token) == 0 && hdr.DestConnectionID.Len() < protocol.MinConnectionIDLenInitial { p.buffer.Release() if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) } return errors.New("too short connection ID") } var ( token *Token retrySrcConnID *protocol.ConnectionID ) origDestConnID := hdr.DestConnectionID if len(hdr.Token) > 0 { c, err := s.tokenGenerator.DecodeToken(hdr.Token) if err == nil { token = &Token{ IsRetryToken: c.IsRetryToken, RemoteAddr: c.RemoteAddr, SentTime: c.SentTime, } if token.IsRetryToken { origDestConnID = c.OriginalDestConnectionID retrySrcConnID = &c.RetrySrcConnectionID } } } if !s.config.AcceptToken(p.remoteAddr, token) { go func() { defer p.buffer.Release() if token != nil && token.IsRetryToken { if err := s.maybeSendInvalidToken(p, hdr); err != nil { s.logger.Debugf("Error sending INVALID_TOKEN error: %s", err) } return } if err := s.sendRetry(p.remoteAddr, hdr, p.info); err != nil { s.logger.Debugf("Error sending Retry: %s", err) } }() return nil } if queueLen := atomic.LoadInt32(&s.sessionQueueLen); queueLen >= protocol.MaxAcceptQueueSize { s.logger.Debugf("Rejecting new connection. Server currently busy. Accept queue length: %d (max %d)", queueLen, protocol.MaxAcceptQueueSize) go func() { defer p.buffer.Release() if err := s.sendConnectionRefused(p.remoteAddr, hdr, p.info); err != nil { s.logger.Debugf("Error rejecting connection: %s", err) } }() return nil } connID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) if err != nil { return err } s.logger.Debugf("Changing connection ID to %s.", connID) var sess quicSession tracingID := nextSessionTracingID() if added := s.sessionHandler.AddWithConnID(hdr.DestConnectionID, connID, func() packetHandler { var tracer logging.ConnectionTracer if s.config.Tracer != nil { // Use the same connection ID that is passed to the client's GetLogWriter callback. connID := hdr.DestConnectionID if origDestConnID.Len() > 0 { connID = origDestConnID } tracer = s.config.Tracer.TracerForConnection( context.WithValue(context.Background(), SessionTracingKey, tracingID), protocol.PerspectiveServer, connID, ) } sess = s.newSession( newSendConn(s.conn, p.remoteAddr, p.info), s.sessionHandler, origDestConnID, retrySrcConnID, hdr.DestConnectionID, hdr.SrcConnectionID, connID, s.sessionHandler.GetStatelessResetToken(connID), s.config, s.tlsConf, s.tokenGenerator, s.acceptEarlySessions, tracer, tracingID, s.logger, hdr.Version, ) sess.handlePacket(p) return sess }); !added { return nil } go sess.run() go s.handleNewSession(sess) if sess == nil { p.buffer.Release() return nil } return nil } func (s *baseServer) handleNewSession(sess quicSession) { sessCtx := sess.Context() if s.acceptEarlySessions { // wait until the early session is ready (or the handshake fails) select { case <-sess.earlySessionReady(): case <-sessCtx.Done(): return } } else { // wait until the handshake is complete (or fails) select { case <-sess.HandshakeComplete().Done(): case <-sessCtx.Done(): return } } atomic.AddInt32(&s.sessionQueueLen, 1) select { case s.sessionQueue <- sess: // blocks until the session is accepted case <-sessCtx.Done(): atomic.AddInt32(&s.sessionQueueLen, -1) // don't pass sessions that were already closed to Accept() } } func (s *baseServer) sendRetry(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error { // Log the Initial packet now. // If no Retry is sent, the packet will be logged by the session. (&wire.ExtendedHeader{Header: *hdr}).Log(s.logger) srcConnID, err := protocol.GenerateConnectionID(s.config.ConnectionIDLength) if err != nil { return err } token, err := s.tokenGenerator.NewRetryToken(remoteAddr, hdr.DestConnectionID, srcConnID) if err != nil { return err } replyHdr := &wire.ExtendedHeader{} replyHdr.IsLongHeader = true replyHdr.Type = protocol.PacketTypeRetry replyHdr.Version = hdr.Version replyHdr.SrcConnectionID = srcConnID replyHdr.DestConnectionID = hdr.SrcConnectionID replyHdr.Token = token if s.logger.Debug() { s.logger.Debugf("Changing connection ID to %s.", srcConnID) s.logger.Debugf("-> Sending Retry") replyHdr.Log(s.logger) } packetBuffer := getPacketBuffer() defer packetBuffer.Release() buf := bytes.NewBuffer(packetBuffer.Data) if err := replyHdr.Write(buf, hdr.Version); err != nil { return err } // append the Retry integrity tag tag := handshake.GetRetryIntegrityTag(buf.Bytes(), hdr.DestConnectionID, hdr.Version) buf.Write(tag[:]) if s.config.Tracer != nil { s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(buf.Len()), nil) } _, err = s.conn.WritePacket(buf.Bytes(), remoteAddr, info.OOB()) return err } func (s *baseServer) maybeSendInvalidToken(p *receivedPacket, hdr *wire.Header) error { // Only send INVALID_TOKEN if we can unprotect the packet. // This makes sure that we won't send it for packets that were corrupted. sealer, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version) data := p.data[:hdr.ParsedLen()+hdr.Length] extHdr, err := unpackHeader(opener, hdr, data, hdr.Version) if err != nil { if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropHeaderParseError) } // don't return the error here. Just drop the packet. return nil } hdrLen := extHdr.ParsedLen() if _, err := opener.Open(data[hdrLen:hdrLen], data[hdrLen:], extHdr.PacketNumber, data[:hdrLen]); err != nil { // don't return the error here. Just drop the packet. if s.config.Tracer != nil { s.config.Tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropPayloadDecryptError) } return nil } if s.logger.Debug() { s.logger.Debugf("Client sent an invalid retry token. Sending INVALID_TOKEN to %s.", p.remoteAddr) } return s.sendError(p.remoteAddr, hdr, sealer, qerr.InvalidToken, p.info) } func (s *baseServer) sendConnectionRefused(remoteAddr net.Addr, hdr *wire.Header, info *packetInfo) error { sealer, _ := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveServer, hdr.Version) return s.sendError(remoteAddr, hdr, sealer, qerr.ConnectionRefused, info) } // sendError sends the error as a response to the packet received with header hdr func (s *baseServer) sendError(remoteAddr net.Addr, hdr *wire.Header, sealer handshake.LongHeaderSealer, errorCode qerr.TransportErrorCode, info *packetInfo) error { packetBuffer := getPacketBuffer() defer packetBuffer.Release() buf := bytes.NewBuffer(packetBuffer.Data) ccf := &wire.ConnectionCloseFrame{ErrorCode: uint64(errorCode)} replyHdr := &wire.ExtendedHeader{} replyHdr.IsLongHeader = true replyHdr.Type = protocol.PacketTypeInitial replyHdr.Version = hdr.Version replyHdr.SrcConnectionID = hdr.DestConnectionID replyHdr.DestConnectionID = hdr.SrcConnectionID replyHdr.PacketNumberLen = protocol.PacketNumberLen4 replyHdr.Length = 4 /* packet number len */ + ccf.Length(hdr.Version) + protocol.ByteCount(sealer.Overhead()) if err := replyHdr.Write(buf, hdr.Version); err != nil { return err } payloadOffset := buf.Len() if err := ccf.Write(buf, hdr.Version); err != nil { return err } raw := buf.Bytes() _ = sealer.Seal(raw[payloadOffset:payloadOffset], raw[payloadOffset:], replyHdr.PacketNumber, raw[:payloadOffset]) raw = raw[0 : buf.Len()+sealer.Overhead()] pnOffset := payloadOffset - int(replyHdr.PacketNumberLen) sealer.EncryptHeader( raw[pnOffset+4:pnOffset+4+16], &raw[0], raw[pnOffset:payloadOffset], ) replyHdr.Log(s.logger) wire.LogFrame(s.logger, ccf, true) if s.config.Tracer != nil { s.config.Tracer.SentPacket(remoteAddr, &replyHdr.Header, protocol.ByteCount(len(raw)), []logging.Frame{ccf}) } _, err := s.conn.WritePacket(raw, remoteAddr, info.OOB()) return err } func (s *baseServer) sendVersionNegotiationPacket(p *receivedPacket, hdr *wire.Header) { s.logger.Debugf("Client offered version %s, sending Version Negotiation", hdr.Version) data, err := wire.ComposeVersionNegotiation(hdr.SrcConnectionID, hdr.DestConnectionID, s.config.Versions) if err != nil { s.logger.Debugf("Error composing Version Negotiation: %s", err) return } if s.config.Tracer != nil { s.config.Tracer.SentPacket( p.remoteAddr, &wire.Header{ IsLongHeader: true, DestConnectionID: hdr.SrcConnectionID, SrcConnectionID: hdr.DestConnectionID, }, protocol.ByteCount(len(data)), nil, ) } if _, err := s.conn.WritePacket(data, p.remoteAddr, p.info.OOB()); err != nil { s.logger.Debugf("Error sending Version Negotiation: %s", err) } } quic-go-0.25.0/server_test.go000066400000000000000000001312451417145451600160540ustar00rootroot00000000000000package quic import ( "bytes" "context" "crypto/rand" "crypto/tls" "errors" "net" "reflect" "runtime/pprof" "strings" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go/internal/handshake" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/testdata" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func areServersRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "quic-go.(*baseServer).run") } var _ = Describe("Server", func() { var ( conn *MockPacketConn tlsConf *tls.Config ) getPacket := func(hdr *wire.Header, p []byte) *receivedPacket { buffer := getPacketBuffer() buf := bytes.NewBuffer(buffer.Data) if hdr.IsLongHeader { hdr.Length = 4 + protocol.ByteCount(len(p)) + 16 } Expect((&wire.ExtendedHeader{ Header: *hdr, PacketNumber: 0x42, PacketNumberLen: protocol.PacketNumberLen4, }).Write(buf, protocol.VersionTLS)).To(Succeed()) n := buf.Len() buf.Write(p) data := buffer.Data[:buf.Len()] sealer, _ := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveClient, hdr.Version) _ = sealer.Seal(data[n:n], data[n:], 0x42, data[:n]) data = data[:len(data)+16] sealer.EncryptHeader(data[n:n+16], &data[0], data[n-4:n]) return &receivedPacket{ remoteAddr: &net.UDPAddr{IP: net.IPv4(4, 5, 6, 7), Port: 456}, data: data, buffer: buffer, } } getInitial := func(destConnID protocol.ConnectionID) *receivedPacket { senderAddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 42} hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: protocol.ConnectionID{5, 4, 3, 2, 1}, DestConnectionID: destConnID, Version: protocol.VersionTLS, } p := getPacket(hdr, make([]byte, protocol.MinInitialPacketSize)) p.buffer = getPacketBuffer() p.remoteAddr = senderAddr return p } getInitialWithRandomDestConnID := func() *receivedPacket { destConnID := make([]byte, 10) _, err := rand.Read(destConnID) Expect(err).ToNot(HaveOccurred()) return getInitial(destConnID) } parseHeader := func(data []byte) *wire.Header { hdr, _, _, err := wire.ParsePacket(data, 0) Expect(err).ToNot(HaveOccurred()) return hdr } BeforeEach(func() { conn = NewMockPacketConn(mockCtrl) conn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes() conn.EXPECT().ReadFrom(gomock.Any()).Do(func(_ []byte) { <-(make(chan struct{})) }).MaxTimes(1) tlsConf = testdata.GetTLSConfig() tlsConf.NextProtos = []string{"proto1"} }) AfterEach(func() { Eventually(areServersRunning).Should(BeFalse()) }) It("errors when no tls.Config is given", func() { _, err := ListenAddr("localhost:0", nil, nil) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("quic: tls.Config not set")) }) It("errors when the Config contains an invalid version", func() { version := protocol.VersionNumber(0x1234) _, err := Listen(nil, tlsConf, &Config{Versions: []protocol.VersionNumber{version}}) Expect(err).To(MatchError("0x1234 is not a valid QUIC version")) }) It("fills in default values if options are not set in the Config", func() { ln, err := Listen(conn, tlsConf, &Config{}) Expect(err).ToNot(HaveOccurred()) server := ln.(*baseServer) Expect(server.config.Versions).To(Equal(protocol.SupportedVersions)) Expect(server.config.HandshakeIdleTimeout).To(Equal(protocol.DefaultHandshakeIdleTimeout)) Expect(server.config.MaxIdleTimeout).To(Equal(protocol.DefaultIdleTimeout)) Expect(reflect.ValueOf(server.config.AcceptToken)).To(Equal(reflect.ValueOf(defaultAcceptToken))) Expect(server.config.KeepAlive).To(BeFalse()) // stop the listener Expect(ln.Close()).To(Succeed()) }) It("setups with the right values", func() { supportedVersions := []protocol.VersionNumber{protocol.VersionTLS} acceptToken := func(_ net.Addr, _ *Token) bool { return true } config := Config{ Versions: supportedVersions, AcceptToken: acceptToken, HandshakeIdleTimeout: 1337 * time.Hour, MaxIdleTimeout: 42 * time.Minute, KeepAlive: true, StatelessResetKey: []byte("foobar"), } ln, err := Listen(conn, tlsConf, &config) Expect(err).ToNot(HaveOccurred()) server := ln.(*baseServer) Expect(server.sessionHandler).ToNot(BeNil()) Expect(server.config.Versions).To(Equal(supportedVersions)) Expect(server.config.HandshakeIdleTimeout).To(Equal(1337 * time.Hour)) Expect(server.config.MaxIdleTimeout).To(Equal(42 * time.Minute)) Expect(reflect.ValueOf(server.config.AcceptToken)).To(Equal(reflect.ValueOf(acceptToken))) Expect(server.config.KeepAlive).To(BeTrue()) Expect(server.config.StatelessResetKey).To(Equal([]byte("foobar"))) // stop the listener Expect(ln.Close()).To(Succeed()) }) It("listens on a given address", func() { addr := "127.0.0.1:13579" ln, err := ListenAddr(addr, tlsConf, &Config{}) Expect(err).ToNot(HaveOccurred()) Expect(ln.Addr().String()).To(Equal(addr)) // stop the listener Expect(ln.Close()).To(Succeed()) }) It("errors if given an invalid address", func() { addr := "127.0.0.1" _, err := ListenAddr(addr, tlsConf, &Config{}) Expect(err).To(BeAssignableToTypeOf(&net.AddrError{})) }) It("errors if given an invalid address", func() { addr := "1.1.1.1:1111" _, err := ListenAddr(addr, tlsConf, &Config{}) Expect(err).To(BeAssignableToTypeOf(&net.OpError{})) }) Context("server accepting sessions that completed the handshake", func() { var ( serv *baseServer phm *MockPacketHandlerManager tracer *mocklogging.MockTracer ) BeforeEach(func() { tracer = mocklogging.NewMockTracer(mockCtrl) ln, err := Listen(conn, tlsConf, &Config{Tracer: tracer}) Expect(err).ToNot(HaveOccurred()) serv = ln.(*baseServer) phm = NewMockPacketHandlerManager(mockCtrl) serv.sessionHandler = phm }) AfterEach(func() { phm.EXPECT().CloseServer().MaxTimes(1) serv.Close() }) Context("handling packets", func() { It("drops Initial packets with a too short connection ID", func() { p := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4}, Version: serv.config.Versions[0], }, nil) tracer.EXPECT().DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) serv.handlePacket(p) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) }) It("drops too small Initial", func() { p := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, Version: serv.config.Versions[0], }, make([]byte, protocol.MinInitialPacketSize-100), ) tracer.EXPECT().DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket) serv.handlePacket(p) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) }) It("drops non-Initial packets", func() { p := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Version: serv.config.Versions[0], }, []byte("invalid")) tracer.EXPECT().DroppedPacket(p.remoteAddr, logging.PacketTypeHandshake, p.Size(), logging.PacketDropUnexpectedPacket) serv.handlePacket(p) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) }) It("decodes the token from the Token field", func() { raddr := &net.UDPAddr{ IP: net.IPv4(192, 168, 13, 37), Port: 1337, } done := make(chan struct{}) serv.config.AcceptToken = func(addr net.Addr, token *Token) bool { Expect(addr).To(Equal(raddr)) Expect(token).ToNot(BeNil()) close(done) return false } token, err := serv.tokenGenerator.NewRetryToken(raddr, nil, nil) Expect(err).ToNot(HaveOccurred()) packet := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Token: token, Version: serv.config.Versions[0], }, make([]byte, protocol.MinInitialPacketSize)) packet.remoteAddr = raddr conn.EXPECT().WriteTo(gomock.Any(), gomock.Any()).MaxTimes(1) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) serv.handlePacket(packet) Eventually(done).Should(BeClosed()) }) It("passes an empty token to the callback, if decoding fails", func() { raddr := &net.UDPAddr{ IP: net.IPv4(192, 168, 13, 37), Port: 1337, } done := make(chan struct{}) serv.config.AcceptToken = func(addr net.Addr, token *Token) bool { Expect(addr).To(Equal(raddr)) Expect(token).To(BeNil()) close(done) return false } packet := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, Token: []byte("foobar"), Version: serv.config.Versions[0], }, make([]byte, protocol.MinInitialPacketSize)) packet.remoteAddr = raddr conn.EXPECT().WriteTo(gomock.Any(), gomock.Any()).MaxTimes(1) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(1) serv.handlePacket(packet) Eventually(done).Should(BeClosed()) }) It("creates a session when the token is accepted", func() { serv.config.AcceptToken = func(_ net.Addr, token *Token) bool { return true } retryToken, err := serv.tokenGenerator.NewRetryToken( &net.UDPAddr{}, protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}, protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, ) Expect(err).ToNot(HaveOccurred()) hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: protocol.ConnectionID{5, 4, 3, 2, 1}, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Version: protocol.VersionTLS, Token: retryToken, } p := getPacket(hdr, make([]byte, protocol.MinInitialPacketSize)) run := make(chan struct{}) var token protocol.StatelessResetToken rand.Read(token[:]) var newConnID protocol.ConnectionID phm.EXPECT().AddWithConnID(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, gomock.Any(), gomock.Any()).DoAndReturn(func(_, c protocol.ConnectionID, fn func() packetHandler) bool { newConnID = c phm.EXPECT().GetStatelessResetToken(gomock.Any()).DoAndReturn(func(c protocol.ConnectionID) protocol.StatelessResetToken { newConnID = c return token }) fn() return true }) tracer.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveServer, protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}) sess := NewMockQuicSession(mockCtrl) serv.newSession = func( _ sendConn, _ sessionRunner, origDestConnID protocol.ConnectionID, retrySrcConnID *protocol.ConnectionID, clientDestConnID protocol.ConnectionID, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, tokenP protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, enable0RTT bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { Expect(enable0RTT).To(BeFalse()) Expect(origDestConnID).To(Equal(protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde})) Expect(retrySrcConnID).To(Equal(&protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad})) Expect(clientDestConnID).To(Equal(hdr.DestConnectionID)) Expect(destConnID).To(Equal(hdr.SrcConnectionID)) // make sure we're using a server-generated connection ID Expect(srcConnID).ToNot(Equal(hdr.DestConnectionID)) Expect(srcConnID).ToNot(Equal(hdr.SrcConnectionID)) Expect(srcConnID).To(Equal(newConnID)) Expect(tokenP).To(Equal(token)) sess.EXPECT().handlePacket(p) sess.EXPECT().run().Do(func() { close(run) }) sess.EXPECT().Context().Return(context.Background()) sess.EXPECT().HandshakeComplete().Return(context.Background()) return sess } done := make(chan struct{}) go func() { defer GinkgoRecover() serv.handlePacket(p) // the Handshake packet is written by the session. // Make sure there are no Write calls on the packet conn. time.Sleep(50 * time.Millisecond) close(done) }() // make sure we're using a server-generated connection ID Eventually(run).Should(BeClosed()) Eventually(done).Should(BeClosed()) }) It("sends a Version Negotiation Packet for unsupported versions", func() { srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5} destConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6} packet := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, SrcConnectionID: srcConnID, DestConnectionID: destConnID, Version: 0x42, }, make([]byte, protocol.MinUnknownVersionPacketSize)) raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} packet.remoteAddr = raddr tracer.EXPECT().SentPacket(packet.remoteAddr, gomock.Any(), gomock.Any(), nil).Do(func(_ net.Addr, replyHdr *logging.Header, _ logging.ByteCount, _ []logging.Frame) { Expect(replyHdr.IsLongHeader).To(BeTrue()) Expect(replyHdr.Version).To(BeZero()) Expect(replyHdr.SrcConnectionID).To(Equal(destConnID)) Expect(replyHdr.DestConnectionID).To(Equal(srcConnID)) }) done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), raddr).DoAndReturn(func(b []byte, _ net.Addr) (int, error) { defer close(done) Expect(wire.IsVersionNegotiationPacket(b)).To(BeTrue()) hdr, versions, err := wire.ParseVersionNegotiationPacket(bytes.NewReader(b)) Expect(err).ToNot(HaveOccurred()) Expect(hdr.DestConnectionID).To(Equal(srcConnID)) Expect(hdr.SrcConnectionID).To(Equal(destConnID)) Expect(versions).ToNot(ContainElement(protocol.VersionNumber(0x42))) return len(b), nil }) serv.handlePacket(packet) Eventually(done).Should(BeClosed()) }) It("doesn't send a Version Negotiation packets if sending them is disabled", func() { serv.config.DisableVersionNegotiationPackets = true srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5} destConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6} packet := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, SrcConnectionID: srcConnID, DestConnectionID: destConnID, Version: 0x42, }, make([]byte, protocol.MinUnknownVersionPacketSize)) raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} packet.remoteAddr = raddr done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), raddr).Do(func() { close(done) }).Times(0) serv.handlePacket(packet) Consistently(done, 50*time.Millisecond).ShouldNot(BeClosed()) }) It("ignores Version Negotiation packets", func() { data, err := wire.ComposeVersionNegotiation( protocol.ConnectionID{1, 2, 3, 4}, protocol.ConnectionID{4, 3, 2, 1}, []protocol.VersionNumber{1, 2, 3}, ) Expect(err).ToNot(HaveOccurred()) raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} done := make(chan struct{}) tracer.EXPECT().DroppedPacket(raddr, logging.PacketTypeVersionNegotiation, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket).Do(func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { close(done) }) serv.handlePacket(&receivedPacket{ remoteAddr: raddr, data: data, buffer: getPacketBuffer(), }) Eventually(done).Should(BeClosed()) // make sure no other packet is sent time.Sleep(scaleDuration(20 * time.Millisecond)) }) It("doesn't send a Version Negotiation Packet for unsupported versions, if the packet is too small", func() { srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5} destConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6} p := getPacket(&wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, SrcConnectionID: srcConnID, DestConnectionID: destConnID, Version: 0x42, }, make([]byte, protocol.MinUnknownVersionPacketSize-50)) Expect(p.Size()).To(BeNumerically("<", protocol.MinUnknownVersionPacketSize)) raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} p.remoteAddr = raddr done := make(chan struct{}) tracer.EXPECT().DroppedPacket(raddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnexpectedPacket).Do(func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { close(done) }) serv.handlePacket(p) Eventually(done).Should(BeClosed()) // make sure no other packet is sent time.Sleep(scaleDuration(20 * time.Millisecond)) }) It("replies with a Retry packet, if a Token is required", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return false } hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: protocol.ConnectionID{5, 4, 3, 2, 1}, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Version: protocol.VersionTLS, } packet := getPacket(hdr, make([]byte, protocol.MinInitialPacketSize)) raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} packet.remoteAddr = raddr tracer.EXPECT().SentPacket(packet.remoteAddr, gomock.Any(), gomock.Any(), nil).Do(func(_ net.Addr, replyHdr *logging.Header, _ logging.ByteCount, _ []logging.Frame) { Expect(replyHdr.Type).To(Equal(protocol.PacketTypeRetry)) Expect(replyHdr.SrcConnectionID).ToNot(Equal(hdr.DestConnectionID)) Expect(replyHdr.DestConnectionID).To(Equal(hdr.SrcConnectionID)) Expect(replyHdr.Token).ToNot(BeEmpty()) }) done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), raddr).DoAndReturn(func(b []byte, _ net.Addr) (int, error) { defer close(done) replyHdr := parseHeader(b) Expect(replyHdr.Type).To(Equal(protocol.PacketTypeRetry)) Expect(replyHdr.SrcConnectionID).ToNot(Equal(hdr.DestConnectionID)) Expect(replyHdr.DestConnectionID).To(Equal(hdr.SrcConnectionID)) Expect(replyHdr.Token).ToNot(BeEmpty()) Expect(b[len(b)-16:]).To(Equal(handshake.GetRetryIntegrityTag(b[:len(b)-16], hdr.DestConnectionID, hdr.Version)[:])) return len(b), nil }) serv.handlePacket(packet) Eventually(done).Should(BeClosed()) }) It("sends an INVALID_TOKEN error, if an invalid retry token is received", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return false } token, err := serv.tokenGenerator.NewRetryToken(&net.UDPAddr{}, nil, nil) Expect(err).ToNot(HaveOccurred()) hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: protocol.ConnectionID{5, 4, 3, 2, 1}, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Token: token, Version: protocol.VersionTLS, } packet := getPacket(hdr, make([]byte, protocol.MinInitialPacketSize)) packet.data = append(packet.data, []byte("coalesced packet")...) // add some garbage to simulate a coalesced packet raddr := &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} packet.remoteAddr = raddr tracer.EXPECT().SentPacket(packet.remoteAddr, gomock.Any(), gomock.Any(), gomock.Any()).Do(func(_ net.Addr, replyHdr *logging.Header, _ logging.ByteCount, frames []logging.Frame) { Expect(replyHdr.Type).To(Equal(protocol.PacketTypeInitial)) Expect(replyHdr.SrcConnectionID).To(Equal(hdr.DestConnectionID)) Expect(replyHdr.DestConnectionID).To(Equal(hdr.SrcConnectionID)) Expect(frames).To(HaveLen(1)) Expect(frames[0]).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := frames[0].(*logging.ConnectionCloseFrame) Expect(ccf.IsApplicationError).To(BeFalse()) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.InvalidToken)) }) done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), raddr).DoAndReturn(func(b []byte, _ net.Addr) (int, error) { defer close(done) replyHdr := parseHeader(b) Expect(replyHdr.Type).To(Equal(protocol.PacketTypeInitial)) Expect(replyHdr.SrcConnectionID).To(Equal(hdr.DestConnectionID)) Expect(replyHdr.DestConnectionID).To(Equal(hdr.SrcConnectionID)) _, opener := handshake.NewInitialAEAD(hdr.DestConnectionID, protocol.PerspectiveClient, replyHdr.Version) extHdr, err := unpackHeader(opener, replyHdr, b, hdr.Version) Expect(err).ToNot(HaveOccurred()) data, err := opener.Open(nil, b[extHdr.ParsedLen():], extHdr.PacketNumber, b[:extHdr.ParsedLen()]) Expect(err).ToNot(HaveOccurred()) f, err := wire.NewFrameParser(false, hdr.Version).ParseNext(bytes.NewReader(data), protocol.EncryptionInitial) Expect(err).ToNot(HaveOccurred()) Expect(f).To(BeAssignableToTypeOf(&wire.ConnectionCloseFrame{})) ccf := f.(*wire.ConnectionCloseFrame) Expect(ccf.ErrorCode).To(BeEquivalentTo(qerr.InvalidToken)) Expect(ccf.ReasonPhrase).To(BeEmpty()) return len(b), nil }) serv.handlePacket(packet) Eventually(done).Should(BeClosed()) }) It("doesn't send an INVALID_TOKEN error, if the packet is corrupted", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return false } token, err := serv.tokenGenerator.NewRetryToken(&net.UDPAddr{}, nil, nil) Expect(err).ToNot(HaveOccurred()) hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: protocol.ConnectionID{5, 4, 3, 2, 1}, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Token: token, Version: protocol.VersionTLS, } packet := getPacket(hdr, make([]byte, protocol.MinInitialPacketSize)) packet.data[len(packet.data)-10] ^= 0xff // corrupt the packet packet.remoteAddr = &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 1337} done := make(chan struct{}) tracer.EXPECT().DroppedPacket(packet.remoteAddr, logging.PacketTypeInitial, packet.Size(), logging.PacketDropPayloadDecryptError).Do(func(net.Addr, logging.PacketType, protocol.ByteCount, logging.PacketDropReason) { close(done) }) serv.handlePacket(packet) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) Eventually(done).Should(BeClosed()) }) It("creates a session, if no Token is required", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, SrcConnectionID: protocol.ConnectionID{5, 4, 3, 2, 1}, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, Version: protocol.VersionTLS, } p := getPacket(hdr, make([]byte, protocol.MinInitialPacketSize)) run := make(chan struct{}) var token protocol.StatelessResetToken rand.Read(token[:]) var newConnID protocol.ConnectionID phm.EXPECT().AddWithConnID(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, gomock.Any(), gomock.Any()).DoAndReturn(func(_, c protocol.ConnectionID, fn func() packetHandler) bool { newConnID = c phm.EXPECT().GetStatelessResetToken(gomock.Any()).DoAndReturn(func(c protocol.ConnectionID) protocol.StatelessResetToken { newConnID = c return token }) fn() return true }) tracer.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveServer, protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) sess := NewMockQuicSession(mockCtrl) serv.newSession = func( _ sendConn, _ sessionRunner, origDestConnID protocol.ConnectionID, retrySrcConnID *protocol.ConnectionID, clientDestConnID protocol.ConnectionID, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, tokenP protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, enable0RTT bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { Expect(enable0RTT).To(BeFalse()) Expect(origDestConnID).To(Equal(hdr.DestConnectionID)) Expect(retrySrcConnID).To(BeNil()) Expect(clientDestConnID).To(Equal(hdr.DestConnectionID)) Expect(destConnID).To(Equal(hdr.SrcConnectionID)) // make sure we're using a server-generated connection ID Expect(srcConnID).ToNot(Equal(hdr.DestConnectionID)) Expect(srcConnID).ToNot(Equal(hdr.SrcConnectionID)) Expect(srcConnID).To(Equal(newConnID)) Expect(tokenP).To(Equal(token)) sess.EXPECT().handlePacket(p) sess.EXPECT().run().Do(func() { close(run) }) sess.EXPECT().Context().Return(context.Background()) sess.EXPECT().HandshakeComplete().Return(context.Background()) return sess } done := make(chan struct{}) go func() { defer GinkgoRecover() serv.handlePacket(p) // the Handshake packet is written by the session // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) close(done) }() // make sure we're using a server-generated connection ID Eventually(run).Should(BeClosed()) Eventually(done).Should(BeClosed()) }) It("drops packets if the receive queue is full", func() { phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }).AnyTimes() tracer.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveServer, gomock.Any()).AnyTimes() serv.config.AcceptToken = func(net.Addr, *Token) bool { return true } acceptSession := make(chan struct{}) var counter uint32 // to be used as an atomic, so we query it in Eventually serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { <-acceptSession atomic.AddUint32(&counter, 1) sess := NewMockQuicSession(mockCtrl) sess.EXPECT().handlePacket(gomock.Any()).MaxTimes(1) sess.EXPECT().run().MaxTimes(1) sess.EXPECT().Context().Return(context.Background()).MaxTimes(1) sess.EXPECT().HandshakeComplete().Return(context.Background()).MaxTimes(1) return sess } p := getInitial(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}) serv.handlePacket(p) tracer.EXPECT().DroppedPacket(p.remoteAddr, logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention).MinTimes(1) var wg sync.WaitGroup for i := 0; i < 3*protocol.MaxServerUnprocessedPackets; i++ { wg.Add(1) go func() { defer GinkgoRecover() defer wg.Done() serv.handlePacket(getInitial(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8})) }() } wg.Wait() close(acceptSession) Eventually( func() uint32 { return atomic.LoadUint32(&counter) }, scaleDuration(100*time.Millisecond), ).Should(BeEquivalentTo(protocol.MaxServerUnprocessedPackets + 1)) Consistently(func() uint32 { return atomic.LoadUint32(&counter) }).Should(BeEquivalentTo(protocol.MaxServerUnprocessedPackets + 1)) }) It("only creates a single session for a duplicate Initial", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } var createdSession bool sess := NewMockQuicSession(mockCtrl) serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { createdSession = true return sess } p := getInitial(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9}) phm.EXPECT().AddWithConnID(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9}, gomock.Any(), gomock.Any()).Return(false) Expect(serv.handlePacketImpl(p)).To(BeTrue()) Expect(createdSession).To(BeFalse()) }) It("rejects new connection attempts if the accept queue is full", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { sess := NewMockQuicSession(mockCtrl) sess.EXPECT().handlePacket(gomock.Any()) sess.EXPECT().run() sess.EXPECT().Context().Return(context.Background()) ctx, cancel := context.WithCancel(context.Background()) cancel() sess.EXPECT().HandshakeComplete().Return(ctx) return sess } phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }).Times(protocol.MaxAcceptQueueSize) tracer.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveServer, gomock.Any()).Times(protocol.MaxAcceptQueueSize) var wg sync.WaitGroup wg.Add(protocol.MaxAcceptQueueSize) for i := 0; i < protocol.MaxAcceptQueueSize; i++ { go func() { defer GinkgoRecover() defer wg.Done() serv.handlePacket(getInitialWithRandomDestConnID()) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) }() } wg.Wait() p := getInitialWithRandomDestConnID() hdr, _, _, err := wire.ParsePacket(p.data, 0) Expect(err).ToNot(HaveOccurred()) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), p.remoteAddr).DoAndReturn(func(b []byte, _ net.Addr) (int, error) { defer close(done) rejectHdr := parseHeader(b) Expect(rejectHdr.Type).To(Equal(protocol.PacketTypeInitial)) Expect(rejectHdr.Version).To(Equal(hdr.Version)) Expect(rejectHdr.DestConnectionID).To(Equal(hdr.SrcConnectionID)) Expect(rejectHdr.SrcConnectionID).To(Equal(hdr.DestConnectionID)) return len(b), nil }) serv.handlePacket(p) Eventually(done).Should(BeClosed()) }) It("doesn't accept new sessions if they were closed in the mean time", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } p := getInitial(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) ctx, cancel := context.WithCancel(context.Background()) sessionCreated := make(chan struct{}) sess := NewMockQuicSession(mockCtrl) serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { sess.EXPECT().handlePacket(p) sess.EXPECT().run() sess.EXPECT().Context().Return(ctx) ctx, cancel := context.WithCancel(context.Background()) cancel() sess.EXPECT().HandshakeComplete().Return(ctx) close(sessionCreated) return sess } phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }) tracer.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveServer, gomock.Any()) serv.handlePacket(p) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) Eventually(sessionCreated).Should(BeClosed()) cancel() time.Sleep(scaleDuration(200 * time.Millisecond)) done := make(chan struct{}) go func() { defer GinkgoRecover() serv.Accept(context.Background()) close(done) }() Consistently(done).ShouldNot(BeClosed()) // make the go routine return phm.EXPECT().CloseServer() sess.EXPECT().getPerspective().MaxTimes(2) // once for every conn ID Expect(serv.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) }) Context("accepting sessions", func() { It("returns Accept when an error occurs", func() { testErr := errors.New("test err") done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := serv.Accept(context.Background()) Expect(err).To(MatchError(testErr)) close(done) }() serv.setCloseError(testErr) Eventually(done).Should(BeClosed()) }) It("returns immediately, if an error occurred before", func() { testErr := errors.New("test err") serv.setCloseError(testErr) for i := 0; i < 3; i++ { _, err := serv.Accept(context.Background()) Expect(err).To(MatchError(testErr)) } }) It("returns when the context is canceled", func() { ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := serv.Accept(ctx) Expect(err).To(MatchError("context canceled")) close(done) }() Consistently(done).ShouldNot(BeClosed()) cancel() Eventually(done).Should(BeClosed()) }) It("accepts new sessions when the handshake completes", func() { sess := NewMockQuicSession(mockCtrl) done := make(chan struct{}) go func() { defer GinkgoRecover() s, err := serv.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(s).To(Equal(sess)) close(done) }() ctx, cancel := context.WithCancel(context.Background()) // handshake context serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { sess.EXPECT().handlePacket(gomock.Any()) sess.EXPECT().HandshakeComplete().Return(ctx) sess.EXPECT().run().Do(func() {}) sess.EXPECT().Context().Return(context.Background()) return sess } phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }) tracer.EXPECT().TracerForConnection(gomock.Any(), protocol.PerspectiveServer, gomock.Any()) serv.handleInitialImpl( &receivedPacket{buffer: getPacketBuffer()}, &wire.Header{DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}}, ) Consistently(done).ShouldNot(BeClosed()) cancel() // complete the handshake Eventually(done).Should(BeClosed()) }) }) }) Context("server accepting sessions that haven't completed the handshake", func() { var ( serv *earlyServer phm *MockPacketHandlerManager ) BeforeEach(func() { ln, err := ListenEarly(conn, tlsConf, nil) Expect(err).ToNot(HaveOccurred()) serv = ln.(*earlyServer) phm = NewMockPacketHandlerManager(mockCtrl) serv.sessionHandler = phm }) AfterEach(func() { phm.EXPECT().CloseServer().MaxTimes(1) serv.Close() }) It("accepts new sessions when they become ready", func() { sess := NewMockQuicSession(mockCtrl) done := make(chan struct{}) go func() { defer GinkgoRecover() s, err := serv.Accept(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(s).To(Equal(sess)) close(done) }() ready := make(chan struct{}) serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, enable0RTT bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { Expect(enable0RTT).To(BeTrue()) sess.EXPECT().handlePacket(gomock.Any()) sess.EXPECT().run().Do(func() {}) sess.EXPECT().earlySessionReady().Return(ready) sess.EXPECT().Context().Return(context.Background()) return sess } phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }) serv.handleInitialImpl( &receivedPacket{buffer: getPacketBuffer()}, &wire.Header{DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}}, ) Consistently(done).ShouldNot(BeClosed()) close(ready) Eventually(done).Should(BeClosed()) }) It("rejects new connection attempts if the accept queue is full", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } senderAddr := &net.UDPAddr{IP: net.IPv4(1, 2, 3, 4), Port: 42} serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { ready := make(chan struct{}) close(ready) sess := NewMockQuicSession(mockCtrl) sess.EXPECT().handlePacket(gomock.Any()) sess.EXPECT().run() sess.EXPECT().earlySessionReady().Return(ready) sess.EXPECT().Context().Return(context.Background()) return sess } phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }).Times(protocol.MaxAcceptQueueSize) for i := 0; i < protocol.MaxAcceptQueueSize; i++ { serv.handlePacket(getInitialWithRandomDestConnID()) } Eventually(func() int32 { return atomic.LoadInt32(&serv.sessionQueueLen) }).Should(BeEquivalentTo(protocol.MaxAcceptQueueSize)) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) p := getInitialWithRandomDestConnID() hdr := parseHeader(p.data) done := make(chan struct{}) conn.EXPECT().WriteTo(gomock.Any(), senderAddr).DoAndReturn(func(b []byte, _ net.Addr) (int, error) { defer close(done) rejectHdr := parseHeader(b) Expect(rejectHdr.Type).To(Equal(protocol.PacketTypeInitial)) Expect(rejectHdr.Version).To(Equal(hdr.Version)) Expect(rejectHdr.DestConnectionID).To(Equal(hdr.SrcConnectionID)) Expect(rejectHdr.SrcConnectionID).To(Equal(hdr.DestConnectionID)) return len(b), nil }) serv.handlePacket(p) Eventually(done).Should(BeClosed()) }) It("doesn't accept new sessions if they were closed in the mean time", func() { serv.config.AcceptToken = func(_ net.Addr, _ *Token) bool { return true } p := getInitial(protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) ctx, cancel := context.WithCancel(context.Background()) sessionCreated := make(chan struct{}) sess := NewMockQuicSession(mockCtrl) serv.newSession = func( _ sendConn, runner sessionRunner, _ protocol.ConnectionID, _ *protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.ConnectionID, _ protocol.StatelessResetToken, _ *Config, _ *tls.Config, _ *handshake.TokenGenerator, _ bool, _ logging.ConnectionTracer, _ uint64, _ utils.Logger, _ protocol.VersionNumber, ) quicSession { sess.EXPECT().handlePacket(p) sess.EXPECT().run() sess.EXPECT().earlySessionReady() sess.EXPECT().Context().Return(ctx) close(sessionCreated) return sess } phm.EXPECT().AddWithConnID(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_, _ protocol.ConnectionID, fn func() packetHandler) bool { phm.EXPECT().GetStatelessResetToken(gomock.Any()) fn() return true }) serv.handlePacket(p) // make sure there are no Write calls on the packet conn time.Sleep(50 * time.Millisecond) Eventually(sessionCreated).Should(BeClosed()) cancel() time.Sleep(scaleDuration(200 * time.Millisecond)) done := make(chan struct{}) go func() { defer GinkgoRecover() serv.Accept(context.Background()) close(done) }() Consistently(done).ShouldNot(BeClosed()) // make the go routine return phm.EXPECT().CloseServer() sess.EXPECT().getPerspective().MaxTimes(2) // once for every conn ID Expect(serv.Close()).To(Succeed()) Eventually(done).Should(BeClosed()) }) }) }) var _ = Describe("default source address verification", func() { It("accepts a token", func() { remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)} token := &Token{ IsRetryToken: true, RemoteAddr: "192.168.0.1", SentTime: time.Now().Add(-protocol.RetryTokenValidity).Add(time.Second), // will expire in 1 second } Expect(defaultAcceptToken(remoteAddr, token)).To(BeTrue()) }) It("requests verification if no token is provided", func() { remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)} Expect(defaultAcceptToken(remoteAddr, nil)).To(BeFalse()) }) It("rejects a token if the address doesn't match", func() { remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)} token := &Token{ IsRetryToken: true, RemoteAddr: "127.0.0.1", SentTime: time.Now(), } Expect(defaultAcceptToken(remoteAddr, token)).To(BeFalse()) }) It("accepts a token for a remote address is not a UDP address", func() { remoteAddr := &net.TCPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} token := &Token{ IsRetryToken: true, RemoteAddr: "192.168.0.1:1337", SentTime: time.Now(), } Expect(defaultAcceptToken(remoteAddr, token)).To(BeTrue()) }) It("rejects an invalid token for a remote address is not a UDP address", func() { remoteAddr := &net.TCPAddr{IP: net.IPv4(192, 168, 0, 1), Port: 1337} token := &Token{ IsRetryToken: true, RemoteAddr: "192.168.0.1:7331", // mismatching port SentTime: time.Now(), } Expect(defaultAcceptToken(remoteAddr, token)).To(BeFalse()) }) It("rejects an expired token", func() { remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)} token := &Token{ IsRetryToken: true, RemoteAddr: "192.168.0.1", SentTime: time.Now().Add(-protocol.RetryTokenValidity).Add(-time.Second), // expired 1 second ago } Expect(defaultAcceptToken(remoteAddr, token)).To(BeFalse()) }) It("accepts a non-retry token", func() { Expect(protocol.RetryTokenValidity).To(BeNumerically("<", protocol.TokenValidity)) remoteAddr := &net.UDPAddr{IP: net.IPv4(192, 168, 0, 1)} token := &Token{ IsRetryToken: false, RemoteAddr: "192.168.0.1", // if this was a retry token, it would have expired one second ago SentTime: time.Now().Add(-protocol.RetryTokenValidity).Add(-time.Second), } Expect(defaultAcceptToken(remoteAddr, token)).To(BeTrue()) }) }) quic-go-0.25.0/session.go000066400000000000000000001730171417145451600151750ustar00rootroot00000000000000package quic import ( "bytes" "context" "crypto/tls" "errors" "fmt" "io" "net" "reflect" "sync" "sync/atomic" "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/logutils" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" ) type unpacker interface { Unpack(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error) } type streamGetter interface { GetOrOpenReceiveStream(protocol.StreamID) (receiveStreamI, error) GetOrOpenSendStream(protocol.StreamID) (sendStreamI, 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 { RunHandshake() ChangeConnectionID(protocol.ConnectionID) SetLargest1RTTAcked(protocol.PacketNumber) error SetHandshakeConfirmed() GetSessionTicket() ([]byte, error) io.Closer ConnectionState() handshake.ConnectionState } type packetInfo struct { addr net.IP ifIndex uint32 } type receivedPacket struct { buffer *packetBuffer remoteAddr net.Addr rcvTime time.Time data []byte ecn protocol.ECN info *packetInfo } 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 sessionRunner interface { Add(protocol.ConnectionID, packetHandler) bool GetStatelessResetToken(protocol.ConnectionID) protocol.StatelessResetToken Retire(protocol.ConnectionID) Remove(protocol.ConnectionID) ReplaceWithClosed(protocol.ConnectionID, packetHandler) AddResetToken(protocol.StatelessResetToken, packetHandler) RemoveResetToken(protocol.StatelessResetToken) } type handshakeRunner struct { onReceivedParams func(*wire.TransportParameters) onError func(error) dropKeys func(protocol.EncryptionLevel) onHandshakeComplete func() } func (r *handshakeRunner) OnReceivedParams(tp *wire.TransportParameters) { r.onReceivedParams(tp) } func (r *handshakeRunner) OnError(e error) { r.onError(e) } func (r *handshakeRunner) DropKeys(el protocol.EncryptionLevel) { r.dropKeys(el) } func (r *handshakeRunner) OnHandshakeComplete() { r.onHandshakeComplete() } type closeError struct { err error remote bool immediate bool } type errCloseForRecreating struct { nextPacketNumber protocol.PacketNumber nextVersion protocol.VersionNumber } func (e *errCloseForRecreating) Error() string { return "closing session in order to recreate it" } var sessionTracingID uint64 // to be accessed atomically func nextSessionTracingID() uint64 { return atomic.AddUint64(&sessionTracingID, 1) } func pathMTUDiscoveryEnabled(config *Config) bool { return !disablePathMTUDiscovery && !config.DisablePathMTUDiscovery } // A Session is a QUIC session type session 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.VersionNumber 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 windowUpdateQueue *windowUpdateQueue 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 handshake completes 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.CancelFunc handshakeCtx context.Context handshakeCtxCancel context.CancelFunc undecryptablePackets []*receivedPacket // undecryptable packets, waiting for a change in encryption level undecryptablePacketsToProcess []*receivedPacket clientHelloWritten <-chan *wire.TransportParameters earlySessionReadyChan chan struct{} handshakeCompleteChan chan struct{} // is closed when the handshake completes handshakeComplete bool handshakeConfirmed bool receivedRetry bool versionNegotiated bool receivedFirstPacket bool idleTimeout time.Duration sessionCreationTime 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 *utils.Timer // 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 logID string tracer logging.ConnectionTracer logger utils.Logger } var ( _ Session = &session{} _ EarlySession = &session{} _ streamSender = &session{} deadlineSendImmediately = time.Time{}.Add(42 * time.Millisecond) // any value > time.Time{} and before time.Now() is fine ) var newSession = func( conn sendConn, runner sessionRunner, origDestConnID protocol.ConnectionID, retrySrcConnID *protocol.ConnectionID, clientDestConnID protocol.ConnectionID, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, statelessResetToken protocol.StatelessResetToken, conf *Config, tlsConf *tls.Config, tokenGenerator *handshake.TokenGenerator, enable0RTT bool, tracer logging.ConnectionTracer, tracingID uint64, logger utils.Logger, v protocol.VersionNumber, ) quicSession { s := &session{ conn: conn, config: conf, handshakeDestConnID: destConnID, srcConnIDLen: srcConnID.Len(), tokenGenerator: tokenGenerator, oneRTTStream: newCryptoStream(), perspective: protocol.PerspectiveServer, handshakeCompleteChan: make(chan struct{}), tracer: tracer, logger: logger, version: v, } if origDestConnID != nil { 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, s.version, ) s.preSetup() s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID)) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( 0, getMaxPacketSize(s.conn.RemoteAddr()), s.rttStats, s.perspective, s.tracer, s.logger, s.version, ) initialStream := newCryptoStream() handshakeStream := newCryptoStream() 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, DisableActiveMigration: true, StatelessResetToken: &statelessResetToken, OriginalDestinationConnectionID: origDestConnID, ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, RetrySourceConnectionID: retrySrcConnID, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize } if s.tracer != nil { s.tracer.SentTransportParameters(params) } cs := handshake.NewCryptoSetupServer( initialStream, handshakeStream, clientDestConnID, conn.LocalAddr(), conn.RemoteAddr(), params, &handshakeRunner{ onReceivedParams: s.handleTransportParameters, onError: s.closeLocal, dropKeys: s.dropEncryptionLevel, onHandshakeComplete: func() { runner.Retire(clientDestConnID) close(s.handshakeCompleteChan) }, }, tlsConf, enable0RTT, s.rttStats, tracer, logger, s.version, ) s.cryptoStreamHandler = cs s.packer = newPacketPacker( srcConnID, s.connIDManager.Get, initialStream, handshakeStream, s.sentPacketHandler, s.retransmissionQueue, s.RemoteAddr(), cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective, s.version, ) s.unpacker = newPacketUnpacker(cs, s.version) s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, s.oneRTTStream) return s } // declare this as a variable, such that we can it mock it in the tests var newClientSession = func( conn sendConn, runner sessionRunner, destConnID protocol.ConnectionID, srcConnID protocol.ConnectionID, conf *Config, tlsConf *tls.Config, initialPacketNumber protocol.PacketNumber, enable0RTT bool, hasNegotiatedVersion bool, tracer logging.ConnectionTracer, tracingID uint64, logger utils.Logger, v protocol.VersionNumber, ) quicSession { s := &session{ conn: conn, config: conf, origDestConnID: destConnID, handshakeDestConnID: destConnID, srcConnIDLen: srcConnID.Len(), perspective: protocol.PerspectiveClient, handshakeCompleteChan: make(chan struct{}), 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, s.version, ) s.preSetup() s.ctx, s.ctxCancel = context.WithCancel(context.WithValue(context.Background(), SessionTracingKey, tracingID)) s.sentPacketHandler, s.receivedPacketHandler = ackhandler.NewAckHandler( initialPacketNumber, getMaxPacketSize(s.conn.RemoteAddr()), s.rttStats, s.perspective, s.tracer, s.logger, s.version, ) initialStream := newCryptoStream() handshakeStream := 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, AckDelayExponent: protocol.AckDelayExponent, DisableActiveMigration: true, ActiveConnectionIDLimit: protocol.MaxActiveConnectionIDs, InitialSourceConnectionID: srcConnID, } if s.config.EnableDatagrams { params.MaxDatagramFrameSize = protocol.MaxDatagramFrameSize } if s.tracer != nil { s.tracer.SentTransportParameters(params) } cs, clientHelloWritten := handshake.NewCryptoSetupClient( initialStream, handshakeStream, destConnID, conn.LocalAddr(), conn.RemoteAddr(), params, &handshakeRunner{ onReceivedParams: s.handleTransportParameters, onError: s.closeLocal, dropKeys: s.dropEncryptionLevel, onHandshakeComplete: func() { close(s.handshakeCompleteChan) }, }, tlsConf, enable0RTT, s.rttStats, tracer, logger, s.version, ) s.clientHelloWritten = clientHelloWritten s.cryptoStreamHandler = cs s.cryptoStreamManager = newCryptoStreamManager(cs, initialStream, handshakeStream, newCryptoStream()) s.unpacker = newPacketUnpacker(cs, s.version) s.packer = newPacketPacker( srcConnID, s.connIDManager.Get, initialStream, handshakeStream, s.sentPacketHandler, s.retransmissionQueue, s.RemoteAddr(), cs, s.framer, s.receivedPacketHandler, s.datagramQueue, s.perspective, s.version, ) 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 *session) preSetup() { s.sendQueue = newSendQueue(s.conn) s.retransmissionQueue = newRetransmissionQueue(s.version) s.frameParser = wire.NewFrameParser(s.config.EnableDatagrams, s.version) s.rttStats = &utils.RTTStats{} s.connFlowController = flowcontrol.NewConnectionFlowController( protocol.ByteCount(s.config.InitialConnectionReceiveWindow), protocol.ByteCount(s.config.MaxConnectionReceiveWindow), s.onHasConnectionWindowUpdate, func(size protocol.ByteCount) bool { if s.config.AllowConnectionWindowIncrease == nil { return true } return s.config.AllowConnectionWindowIncrease(s, uint64(size)) }, s.rttStats, s.logger, ) s.earlySessionReadyChan = make(chan struct{}) s.streamsMap = newStreamsMap( s, s.newFlowController, uint64(s.config.MaxIncomingStreams), uint64(s.config.MaxIncomingUniStreams), s.perspective, s.version, ) s.framer = newFramer(s.streamsMap, s.version) s.receivedPackets = make(chan *receivedPacket, protocol.MaxSessionUnprocessedPackets) s.closeChan = make(chan closeError, 1) s.sendingScheduled = make(chan struct{}, 1) s.handshakeCtx, s.handshakeCtxCancel = context.WithCancel(context.Background()) now := time.Now() s.lastPacketReceivedTime = now s.sessionCreationTime = now s.windowUpdateQueue = newWindowUpdateQueue(s.streamsMap, s.connFlowController, s.framer.QueueControlFrame) if s.config.EnableDatagrams { s.datagramQueue = newDatagramQueue(s.scheduleSending, s.logger) } } // run the session main loop func (s *session) run() error { defer s.ctxCancel() s.timer = utils.NewTimer() go s.cryptoStreamHandler.RunHandshake() go func() { if err := s.sendQueue.Run(); err != nil { s.destroyImpl(err) } }() if s.perspective == protocol.PerspectiveClient { select { case zeroRTTParams := <-s.clientHelloWritten: s.scheduleSending() if zeroRTTParams != nil { s.restoreTransportParameters(zeroRTTParams) close(s.earlySessionReadyChan) } case closeErr := <-s.closeChan: // put the close error back into the channel, so that the run loop can receive it s.closeChan <- closeErr } } var ( closeErr closeError sendQueueAvailable <-chan struct{} ) runLoop: for { // Close immediately if requested select { case closeErr = <-s.closeChan: break runLoop case <-s.handshakeCompleteChan: s.handleHandshakeComplete() 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 session. 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 session. 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 } case <-s.handshakeCompleteChan: s.handleHandshakeComplete() } } 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 session 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.sessionCreationTime) >= s.config.handshakeTimeout() { s.destroyImpl(qerr.ErrHandshakeTimeout) continue } else { idleTimeoutStartTime := s.idleTimeoutStartTime() if (!s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.config.HandshakeIdleTimeout) || (s.handshakeComplete && now.Sub(idleTimeoutStartTime) >= s.idleTimeout) { 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.sendPackets(); err != nil { s.closeLocal(err) } if s.sendQueue.WouldBlock() { sendQueueAvailable = s.sendQueue.Available() } else { sendQueueAvailable = nil } } s.handleCloseError(&closeErr) if e := (&errCloseForRecreating{}); !errors.As(closeErr.err, &e) && s.tracer != nil { s.tracer.Close() } s.logger.Infof("Connection %s closed.", s.logID) s.cryptoStreamHandler.Close() s.sendQueue.Close() s.timer.Stop() return closeErr.err } // blocks until the early session can be used func (s *session) earlySessionReady() <-chan struct{} { return s.earlySessionReadyChan } func (s *session) HandshakeComplete() context.Context { return s.handshakeCtx } func (s *session) Context() context.Context { return s.ctx } func (s *session) supportsDatagrams() bool { return s.peerParams.MaxDatagramFrameSize != protocol.InvalidByteCount } func (s *session) ConnectionState() ConnectionState { return ConnectionState{ TLS: s.cryptoStreamHandler.ConnectionState(), SupportsDatagrams: s.supportsDatagrams(), } } // Time when the next keep-alive packet should be sent. // It returns a zero time if no keep-alive should be sent. func (s *session) nextKeepAliveTime() time.Time { if !s.config.KeepAlive || s.keepAlivePingSent || !s.firstAckElicitingPacketAfterIdleSentTime.IsZero() { return time.Time{} } return s.lastPacketReceivedTime.Add(s.keepAliveInterval) } func (s *session) maybeResetTimer() { var deadline time.Time if !s.handshakeComplete { deadline = utils.MinTime( s.sessionCreationTime.Add(s.config.handshakeTimeout()), s.idleTimeoutStartTime().Add(s.config.HandshakeIdleTimeout), ) } else { if keepAliveTime := s.nextKeepAliveTime(); !keepAliveTime.IsZero() { deadline = keepAliveTime } else { deadline = s.idleTimeoutStartTime().Add(s.idleTimeout) } } if s.handshakeConfirmed && pathMTUDiscoveryEnabled(s.config) { if probeTime := s.mtuDiscoverer.NextProbeTime(); !probeTime.IsZero() { deadline = utils.MinTime(deadline, probeTime) } } if ackAlarm := s.receivedPacketHandler.GetAlarmTimeout(); !ackAlarm.IsZero() { deadline = utils.MinTime(deadline, ackAlarm) } if lossTime := s.sentPacketHandler.GetLossDetectionTimeout(); !lossTime.IsZero() { deadline = utils.MinTime(deadline, lossTime) } if !s.pacingDeadline.IsZero() { deadline = utils.MinTime(deadline, s.pacingDeadline) } s.timer.Reset(deadline) } func (s *session) idleTimeoutStartTime() time.Time { return utils.MaxTime(s.lastPacketReceivedTime, s.firstAckElicitingPacketAfterIdleSentTime) } func (s *session) handleHandshakeComplete() { s.handshakeComplete = true s.handshakeCompleteChan = nil // prevent this case from ever being selected again defer s.handshakeCtxCancel() // Once the handshake completes, we have derived 1-RTT keys. // There's no point in queueing undecryptable packets for later decryption any more. s.undecryptablePackets = nil s.connIDManager.SetHandshakeComplete() s.connIDGenerator.SetHandshakeComplete() if s.perspective == protocol.PerspectiveClient { s.applyTransportParameters() return } s.handleHandshakeConfirmed() ticket, err := s.cryptoStreamHandler.GetSessionTicket() if err != nil { s.closeLocal(err) } if ticket != nil { 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 { s.closeLocal(err) } s.queueControlFrame(&wire.NewTokenFrame{Token: token}) s.queueControlFrame(&wire.HandshakeDoneFrame{}) } func (s *session) handleHandshakeConfirmed() { s.handshakeConfirmed = true s.sentPacketHandler.SetHandshakeConfirmed() s.cryptoStreamHandler.SetHandshakeConfirmed() if pathMTUDiscoveryEnabled(s.config) { maxPacketSize := s.peerParams.MaxUDPPayloadSize if maxPacketSize == 0 { maxPacketSize = protocol.MaxByteCount } maxPacketSize = utils.MinByteCount(maxPacketSize, protocol.MaxPacketBufferSize) s.mtuDiscoverer = newMTUDiscoverer( s.rttStats, getMaxPacketSize(s.conn.RemoteAddr()), maxPacketSize, func(size protocol.ByteCount) { s.sentPacketHandler.SetMaxDatagramSize(size) s.packer.SetMaxPacketSize(size) }, ) } } func (s *session) 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 } hdr, packetData, rest, err := wire.ParsePacket(p.data, s.srcConnIDLen) if err != nil { if s.tracer != nil { dropReason := logging.PacketDropHeaderParseError if err == wire.ErrUnsupportedVersion { dropReason = logging.PacketDropUnsupportedVersion } s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, protocol.ByteCount(len(data)), dropReason) } s.logger.Debugf("error parsing packet: %s", err) break } if hdr.IsLongHeader && hdr.Version != s.version { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnexpectedVersion) } s.logger.Debugf("Dropping packet with version %x. Expected %x.", hdr.Version, s.version) break } if counter > 0 && !hdr.DestConnectionID.Equal(lastConnID) { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), protocol.ByteCount(len(data)), logging.PacketDropUnknownConnectionID) } s.logger.Debugf("coalesced packet has different destination connection ID: %s, expected %s", hdr.DestConnectionID, lastConnID) break } lastConnID = hdr.DestConnectionID 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.handleSinglePacket(p, hdr); wasProcessed { processed = true } data = rest } p.buffer.MaybeRelease() return processed } func (s *session) handleSinglePacket(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) } // 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.IsLongHeader && hdr.Type == protocol.PacketTypeInitial && !hdr.SrcConnectionID.Equal(s.handshakeDestConnID) { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeInitial, 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(logging.PacketType0RTT, p.Size(), logging.PacketDropKeyUnavailable) } return false } packet, err := s.unpacker.Unpack(hdr, p.rcvTime, p.data) if err != nil { switch err { case handshake.ErrKeysDropped: if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropKeyUnavailable) } s.logger.Debugf("Dropping %s packet (%d bytes) because we already dropped the keys.", hdr.PacketType(), p.Size()) case handshake.ErrKeysNotYetAvailable: // Sealer for this encryption level not yet available. // Try again later. wasQueued = true s.tryQueueingUndecryptablePacket(p, hdr) 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(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropPayloadDecryptError) } s.logger.Debugf("Dropping %s packet (%d bytes) that could not be unpacked. Error: %s", hdr.PacketType(), 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(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropHeaderParseError) } s.logger.Debugf("Dropping %s packet (%d bytes) for which we couldn't unpack the header. Error: %s", hdr.PacketType(), 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 } if s.logger.Debug() { s.logger.Debugf("<- Reading packet %d (%d bytes) for connection %s, %s", packet.packetNumber, p.Size(), hdr.DestConnectionID, packet.encryptionLevel) packet.hdr.Log(s.logger) } if s.receivedPacketHandler.IsPotentiallyDuplicate(packet.packetNumber, packet.encryptionLevel) { s.logger.Debugf("Dropping (potentially) duplicate packet.") if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeFromHeader(hdr), p.Size(), logging.PacketDropDuplicate) } return false } if err := s.handleUnpackedPacket(packet, p.ecn, p.rcvTime, p.Size()); err != nil { s.closeLocal(err) return false } return true } func (s *session) handleRetryPacket(hdr *wire.Header, data []byte) bool /* was this a valid Retry */ { if s.perspective == protocol.PerspectiveServer { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, protocol.ByteCount(len(data)), logging.PacketDropUnexpectedPacket) } s.logger.Debugf("Ignoring Retry.") return false } if s.receivedFirstPacket { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, 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.Equal(destConnID) { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeRetry, 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(logging.PacketTypeRetry, 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(hdr) } newDestConnID := hdr.SrcConnectionID s.receivedRetry = true if err := s.sentPacketHandler.ResetForRetry(); 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 *session) 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(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedPacket) } return } hdr, supportedVersions, err := wire.ParseVersionNegotiationPacket(bytes.NewReader(p.data)) if err != nil { if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeVersionNegotiation, 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(logging.PacketTypeVersionNegotiation, 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(hdr, 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(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 *session) handleUnpackedPacket( packet *unpackedPacket, ecn protocol.ECN, rcvTime time.Time, packetSize protocol.ByteCount, // only for logging ) error { if len(packet.data) == 0 { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "empty packet", } } if !s.receivedFirstPacket { s.receivedFirstPacket = true if !s.versionNegotiated && s.tracer != nil { var clientVersions, serverVersions []protocol.VersionNumber 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.IsLongHeader && !packet.hdr.SrcConnectionID.Equal(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 session 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 create a session 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.Equal(s.handshakeDestConnID) { s.handshakeDestConnID = packet.hdr.SrcConnectionID s.connIDManager.ChangeInitialConnID(packet.hdr.SrcConnectionID) } if s.tracer != nil { s.tracer.StartedConnection( s.conn.LocalAddr(), s.conn.RemoteAddr(), packet.hdr.SrcConnectionID, packet.hdr.DestConnectionID, ) } } } s.lastPacketReceivedTime = rcvTime s.firstAckElicitingPacketAfterIdleSentTime = time.Time{} s.keepAlivePingSent = false // Only used for tracing. // If we're not tracing, this slice will always remain empty. var frames []wire.Frame r := bytes.NewReader(packet.data) var isAckEliciting bool for { frame, err := s.frameParser.ParseNext(r, packet.encryptionLevel) if err != nil { return err } if frame == nil { break } if ackhandler.IsFrameAckEliciting(frame) { isAckEliciting = true } // Only process frames now if we're not logging. // If we're logging, we need to make sure that the packet_received event is logged first. if s.tracer == nil { if err := s.handleFrame(frame, packet.encryptionLevel, packet.hdr.DestConnectionID); err != nil { return err } } else { frames = append(frames, frame) } } if s.tracer != nil { fs := make([]logging.Frame, len(frames)) for i, frame := range frames { fs[i] = logutils.ConvertFrame(frame) } s.tracer.ReceivedPacket(packet.hdr, packetSize, fs) for _, frame := range frames { if err := s.handleFrame(frame, packet.encryptionLevel, packet.hdr.DestConnectionID); err != nil { return err } } } return s.receivedPacketHandler.ReceivedPacket(packet.packetNumber, ecn, packet.encryptionLevel, rcvTime, isAckEliciting) } func (s *session) 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 *session) handlePacket(p *receivedPacket) { // Discard packets once the amount of queued packets is larger than // the channel size, protocol.MaxSessionUnprocessedPackets select { case s.receivedPackets <- p: default: if s.tracer != nil { s.tracer.DroppedPacket(logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropDOSPrevention) } } } func (s *session) 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 *session) handleCryptoFrame(frame *wire.CryptoFrame, encLevel protocol.EncryptionLevel) error { encLevelChanged, err := s.cryptoStreamManager.HandleCryptoFrame(frame, encLevel) if err != nil { return err } if encLevelChanged { // Queue all packets for decryption that have been undecryptable so far. s.undecryptablePacketsToProcess = s.undecryptablePackets s.undecryptablePackets = nil } return nil } func (s *session) 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 *session) handleMaxDataFrame(frame *wire.MaxDataFrame) { s.connFlowController.UpdateSendWindow(frame.MaximumData) } func (s *session) 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 *session) handleMaxStreamsFrame(frame *wire.MaxStreamsFrame) { s.streamsMap.HandleMaxStreamsFrame(frame) } func (s *session) 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 *session) 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 *session) handlePathChallengeFrame(frame *wire.PathChallengeFrame) { s.queueControlFrame(&wire.PathResponseFrame{Data: frame.Data}) } func (s *session) 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 *session) handleNewConnectionIDFrame(f *wire.NewConnectionIDFrame) error { return s.connIDManager.Add(f) } func (s *session) handleRetireConnectionIDFrame(f *wire.RetireConnectionIDFrame, destConnID protocol.ConnectionID) error { return s.connIDGenerator.Retire(f.SequenceNumber, destConnID) } func (s *session) handleHandshakeDoneFrame() error { if s.perspective == protocol.PerspectiveServer { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received a HANDSHAKE_DONE frame", } } if !s.handshakeConfirmed { s.handleHandshakeConfirmed() } return nil } func (s *session) 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 } if s.perspective == protocol.PerspectiveClient && !s.handshakeConfirmed { s.handleHandshakeConfirmed() } return s.cryptoStreamHandler.SetLargest1RTTAcked(frame.LargestAcked()) } func (s *session) handleDatagramFrame(f *wire.DatagramFrame) error { if f.Length(s.version) > protocol.MaxDatagramFrameSize { return &qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "DATAGRAM frame too large", } } s.datagramQueue.HandleDatagramFrame(f) return nil } // closeLocal closes the session and send a CONNECTION_CLOSE containing the error func (s *session) closeLocal(e error) { s.closeOnce.Do(func() { if e == nil { s.logger.Infof("Closing session.") } else { s.logger.Errorf("Closing session with error: %s", e) } s.closeChan <- closeError{err: e, immediate: false, remote: false} }) } // destroy closes the session without sending the error on the wire func (s *session) destroy(e error) { s.destroyImpl(e) <-s.ctx.Done() } func (s *session) destroyImpl(e error) { s.closeOnce.Do(func() { if nerr, ok := e.(net.Error); ok && nerr.Timeout() { s.logger.Errorf("Destroying session: %s", e) } else { s.logger.Errorf("Destroying session with error: %s", e) } s.closeChan <- closeError{err: e, immediate: true, remote: false} }) } func (s *session) closeRemote(e error) { s.closeOnce.Do(func() { s.logger.Errorf("Peer closed session with error: %s", e) s.closeChan <- closeError{err: e, immediate: true, remote: true} }) } // Close the connection. It sends a NO_ERROR application error. // It waits until the run loop has stopped before returning func (s *session) shutdown() { s.closeLocal(nil) <-s.ctx.Done() } func (s *session) CloseWithError(code ApplicationErrorCode, desc string) error { s.closeLocal(&qerr.ApplicationError{ ErrorCode: code, ErrorMessage: desc, }) <-s.ctx.Done() return nil } func (s *session) 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 && !errors.As(e, &recreateErr) { s.tracer.ClosedConnection(e) } // If this is a remote close we're done here if closeErr.remote { s.connIDGenerator.ReplaceWithClosed(newClosedRemoteSession(s.perspective)) return } if closeErr.immediate { s.connIDGenerator.RemoveAll() return } connClosePacket, err := s.sendConnectionClose(e) if err != nil { s.logger.Debugf("Error sending CONNECTION_CLOSE: %s", err) } cs := newClosedLocalSession(s.conn, connClosePacket, s.perspective, s.logger) s.connIDGenerator.ReplaceWithClosed(cs) } func (s *session) dropEncryptionLevel(encLevel protocol.EncryptionLevel) { s.sentPacketHandler.DropPackets(encLevel) s.receivedPacketHandler.DropPackets(encLevel) if s.tracer != nil { s.tracer.DroppedEncryptionLevel(encLevel) } if encLevel == protocol.Encryption0RTT { s.streamsMap.ResetFor0RTT() if err := s.connFlowController.Reset(); err != nil { s.closeLocal(err) } if err := s.framer.Handle0RTTRejection(); err != nil { s.closeLocal(err) } } } // is called for the client, when restoring transport parameters saved for 0-RTT func (s *session) 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) } func (s *session) handleTransportParameters(params *wire.TransportParameters) { if err := s.checkTransportParameters(params); err != nil { s.closeLocal(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: err.Error(), }) } 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 session is ready as soon as we processed // the client's transport parameters. close(s.earlySessionReadyChan) } } func (s *session) checkTransportParameters(params *wire.TransportParameters) error { if s.logger.Debug() { s.logger.Debugf("Processed Transport Parameters: %s", params) } if s.tracer != nil { s.tracer.ReceivedTransportParameters(params) } // check the initial_source_connection_id if !params.InitialSourceConnectionID.Equal(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.Equal(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).Equal(*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 *session) applyTransportParameters() { params := s.peerParams // Our local idle timeout will always be > 0. s.idleTimeout = utils.MinNonZeroDuration(s.config.MaxIdleTimeout, params.MaxIdleTimeout) s.keepAliveInterval = utils.MinDuration(s.idleTimeout/2, protocol.MaxKeepAliveInterval) s.streamsMap.UpdateLimits(params) s.packer.HandleTransportParameters(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) } } func (s *session) sendPackets() error { s.pacingDeadline = time.Time{} var sentPacket bool // only used in for packets sent in send mode SendAny for { sendMode := s.sentPacketHandler.SendMode() if sendMode == ackhandler.SendAny && s.handshakeComplete && !s.sentPacketHandler.HasPacingBudget() { deadline := s.sentPacketHandler.TimeUntilSend() if deadline.IsZero() { deadline = deadlineSendImmediately } s.pacingDeadline = deadline // Allow sending of an ACK if we're pacing limit (if we haven't sent out a packet yet). // 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. if sentPacket { return nil } sendMode = ackhandler.SendAck } switch sendMode { case ackhandler.SendNone: return nil case ackhandler.SendAck: // If we already sent packets, and the send mode switches to SendAck, // as we've just become congestion limited. // There's no need to try to send an ACK at this moment. if sentPacket { return nil } // 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 pacingt timer. return s.maybeSendAckOnlyPacket() case ackhandler.SendPTOInitial: if err := s.sendProbePacket(protocol.EncryptionInitial); err != nil { return err } case ackhandler.SendPTOHandshake: if err := s.sendProbePacket(protocol.EncryptionHandshake); err != nil { return err } case ackhandler.SendPTOAppData: if err := s.sendProbePacket(protocol.Encryption1RTT); err != nil { return err } case ackhandler.SendAny: sent, err := s.sendPacket() if err != nil || !sent { return err } sentPacket = true default: return fmt.Errorf("BUG: invalid send mode %d", sendMode) } // Prioritize receiving of packets over sending out more packets. if len(s.receivedPackets) > 0 { s.pacingDeadline = deadlineSendImmediately return nil } if s.sendQueue.WouldBlock() { return nil } } } func (s *session) maybeSendAckOnlyPacket() error { packet, err := s.packer.MaybePackAckPacket(s.handshakeConfirmed) if err != nil { return err } if packet == nil { return nil } s.sendPackedPacket(packet, time.Now()) return nil } func (s *session) sendProbePacket(encLevel protocol.EncryptionLevel) error { // Queue probe packets until we actually send out a packet, // or until there are no more packets to queue. var packet *packedPacket for { if wasQueued := s.sentPacketHandler.QueueProbePacket(encLevel); !wasQueued { break } var err error packet, err = s.packer.MaybePackProbePacket(encLevel) if err != nil { return err } if packet != nil { break } } if packet == nil { //nolint:exhaustive // Cannot send probe packets for 0-RTT. switch encLevel { case protocol.EncryptionInitial: s.retransmissionQueue.AddInitial(&wire.PingFrame{}) case protocol.EncryptionHandshake: s.retransmissionQueue.AddHandshake(&wire.PingFrame{}) case protocol.Encryption1RTT: s.retransmissionQueue.AddAppData(&wire.PingFrame{}) default: panic("unexpected encryption level") } var err error packet, err = s.packer.MaybePackProbePacket(encLevel) if err != nil { return err } } if packet == nil || packet.packetContents == nil { return fmt.Errorf("session BUG: couldn't pack %s probe packet", encLevel) } s.sendPackedPacket(packet, time.Now()) return nil } func (s *session) sendPacket() (bool, error) { if isBlocked, offset := s.connFlowController.IsNewlyBlocked(); isBlocked { s.framer.QueueControlFrame(&wire.DataBlockedFrame{MaximumData: offset}) } s.windowUpdateQueue.QueueAll() now := time.Now() if !s.handshakeConfirmed { packet, err := s.packer.PackCoalescedPacket() if err != nil || packet == nil { return false, err } s.logCoalescedPacket(packet) for _, p := range packet.packets { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && p.IsAckEliciting() { s.firstAckElicitingPacketAfterIdleSentTime = now } s.sentPacketHandler.SentPacket(p.ToAckHandlerPacket(now, s.retransmissionQueue)) } s.connIDManager.SentPacket() s.sendQueue.Send(packet.buffer) return true, nil } if pathMTUDiscoveryEnabled(s.config) && s.mtuDiscoverer.ShouldSendProbe(now) { packet, err := s.packer.PackMTUProbePacket(s.mtuDiscoverer.GetPing()) if err != nil { return false, err } s.sendPackedPacket(packet, now) return true, nil } packet, err := s.packer.PackPacket() if err != nil || packet == nil { return false, err } s.sendPackedPacket(packet, now) return true, nil } func (s *session) sendPackedPacket(packet *packedPacket, now time.Time) { if s.firstAckElicitingPacketAfterIdleSentTime.IsZero() && packet.IsAckEliciting() { s.firstAckElicitingPacketAfterIdleSentTime = now } s.logPacket(packet) s.sentPacketHandler.SentPacket(packet.ToAckHandlerPacket(now, s.retransmissionQueue)) s.connIDManager.SentPacket() s.sendQueue.Send(packet.buffer) } func (s *session) 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) } else if errors.As(e, &applicationErr) { packet, err = s.packer.PackApplicationClose(applicationErr) } else { packet, err = s.packer.PackConnectionClose(&qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: fmt.Sprintf("session BUG: unspecified error type (msg: %s)", e.Error()), }) } if err != nil { return nil, err } s.logCoalescedPacket(packet) return packet.buffer.Data, s.conn.Write(packet.buffer.Data) } func (s *session) logPacketContents(p *packetContents) { // tracing if s.tracer != nil { frames := make([]logging.Frame, 0, len(p.frames)) for _, f := range p.frames { frames = append(frames, logutils.ConvertFrame(f.Frame)) } s.tracer.SentPacket(p.header, p.length, p.ack, frames) } // quic-go logging if !s.logger.Debug() { return } 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) } } func (s *session) logCoalescedPacket(packet *coalescedPacket) { if s.logger.Debug() { if len(packet.packets) > 1 { s.logger.Debugf("-> Sending coalesced packet (%d parts, %d bytes) for connection %s", len(packet.packets), packet.buffer.Len(), s.logID) } else { s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.packets[0].header.PacketNumber, packet.buffer.Len(), s.logID, packet.packets[0].EncryptionLevel()) } } for _, p := range packet.packets { s.logPacketContents(p) } } func (s *session) logPacket(packet *packedPacket) { if s.logger.Debug() { s.logger.Debugf("-> Sending packet %d (%d bytes) for connection %s, %s", packet.header.PacketNumber, packet.buffer.Len(), s.logID, packet.EncryptionLevel()) } s.logPacketContents(packet.packetContents) } // AcceptStream returns the next stream openend by the peer func (s *session) AcceptStream(ctx context.Context) (Stream, error) { return s.streamsMap.AcceptStream(ctx) } func (s *session) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { return s.streamsMap.AcceptUniStream(ctx) } // OpenStream opens a stream func (s *session) OpenStream() (Stream, error) { return s.streamsMap.OpenStream() } func (s *session) OpenStreamSync(ctx context.Context) (Stream, error) { return s.streamsMap.OpenStreamSync(ctx) } func (s *session) OpenUniStream() (SendStream, error) { return s.streamsMap.OpenUniStream() } func (s *session) OpenUniStreamSync(ctx context.Context) (SendStream, error) { return s.streamsMap.OpenUniStreamSync(ctx) } func (s *session) 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.onHasStreamWindowUpdate, s.rttStats, s.logger, ) } // scheduleSending signals that we have data for sending func (s *session) scheduleSending() { select { case s.sendingScheduled <- struct{}{}: default: } } func (s *session) tryQueueingUndecryptablePacket(p *receivedPacket, hdr *wire.Header) { 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(logging.PacketTypeFromHeader(hdr), 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(logging.PacketTypeFromHeader(hdr)) } s.undecryptablePackets = append(s.undecryptablePackets, p) } func (s *session) queueControlFrame(f wire.Frame) { s.framer.QueueControlFrame(f) s.scheduleSending() } func (s *session) onHasStreamWindowUpdate(id protocol.StreamID) { s.windowUpdateQueue.AddStream(id) s.scheduleSending() } func (s *session) onHasConnectionWindowUpdate() { s.windowUpdateQueue.AddConnection() s.scheduleSending() } func (s *session) onHasStreamData(id protocol.StreamID) { s.framer.AddActiveStream(id) s.scheduleSending() } func (s *session) onStreamCompleted(id protocol.StreamID) { if err := s.streamsMap.DeleteStream(id); err != nil { s.closeLocal(err) } } func (s *session) SendMessage(p []byte) error { f := &wire.DatagramFrame{DataLenPresent: true} if protocol.ByteCount(len(p)) > f.MaxDataLen(s.peerParams.MaxDatagramFrameSize, s.version) { return errors.New("message too large") } f.Data = make([]byte, len(p)) copy(f.Data, p) return s.datagramQueue.AddAndWait(f) } func (s *session) ReceiveMessage() ([]byte, error) { return s.datagramQueue.Receive() } func (s *session) LocalAddr() net.Addr { return s.conn.LocalAddr() } func (s *session) RemoteAddr() net.Addr { return s.conn.RemoteAddr() } func (s *session) getPerspective() protocol.Perspective { return s.perspective } func (s *session) GetVersion() protocol.VersionNumber { return s.version } func (s *session) NextSession() Session { <-s.HandshakeComplete().Done() s.streamsMap.UseResetMaps() return s } quic-go-0.25.0/session_test.go000066400000000000000000003427771417145451600162470ustar00rootroot00000000000000package quic import ( "bytes" "context" "crypto/rand" "crypto/tls" "errors" "fmt" "io" "net" "runtime" "runtime/pprof" "strings" "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/handshake" "github.com/lucas-clemente/quic-go/internal/mocks" mockackhandler "github.com/lucas-clemente/quic-go/internal/mocks/ackhandler" mocklogging "github.com/lucas-clemente/quic-go/internal/mocks/logging" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/testutils" "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/internal/wire" "github.com/lucas-clemente/quic-go/logging" "github.com/golang/mock/gomock" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func areSessionsRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "quic-go.(*session).run") } func areClosedSessionsRunning() bool { var b bytes.Buffer pprof.Lookup("goroutine").WriteTo(&b, 1) return strings.Contains(b.String(), "quic-go.(*closedLocalSession).run") } var _ = Describe("Session", func() { var ( sess *session sessionRunner *MockSessionRunner mconn *MockSendConn streamManager *MockStreamManager packer *MockPacker cryptoSetup *mocks.MockCryptoSetup tracer *mocklogging.MockConnectionTracer ) 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.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} destConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1} clientDestConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} getPacket := func(pn protocol.PacketNumber) *packedPacket { buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("foobar")...) return &packedPacket{ buffer: buffer, packetContents: &packetContents{ header: &wire.ExtendedHeader{PacketNumber: pn}, length: 6, // foobar }, } } expectReplaceWithClosed := func() { sessionRunner.EXPECT().ReplaceWithClosed(clientDestConnID, gomock.Any()).MaxTimes(1) sessionRunner.EXPECT().ReplaceWithClosed(srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) s.shutdown() Eventually(areClosedSessionsRunning).Should(BeFalse()) }) } BeforeEach(func() { Eventually(areSessionsRunning).Should(BeFalse()) sessionRunner = NewMockSessionRunner(mockCtrl) mconn = NewMockSendConn(mockCtrl) mconn.EXPECT().RemoteAddr().Return(remoteAddr).AnyTimes() mconn.EXPECT().LocalAddr().Return(localAddr).AnyTimes() tokenGenerator, err := handshake.NewTokenGenerator(rand.Reader) Expect(err).ToNot(HaveOccurred()) 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()) sess = newSession( mconn, sessionRunner, nil, nil, clientDestConnID, destConnID, srcConnID, protocol.StatelessResetToken{}, populateServerConfig(&Config{DisablePathMTUDiscovery: true}), nil, // tls.Config tokenGenerator, false, tracer, 1234, utils.DefaultLogger, protocol.VersionTLS, ).(*session) streamManager = NewMockStreamManager(mockCtrl) sess.streamsMap = streamManager packer = NewMockPacker(mockCtrl) sess.packer = packer cryptoSetup = mocks.NewMockCryptoSetup(mockCtrl) sess.cryptoStreamHandler = cryptoSetup sess.handshakeComplete = true sess.idleTimeout = time.Hour }) AfterEach(func() { Eventually(areSessionsRunning).Should(BeFalse()) }) 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(sess.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(sess.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(sess.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()) sess.sentPacketHandler = sph err := sess.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 := sess.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 := sess.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(sess.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) sess.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(sess.handleMaxStreamDataFrame(f)).To(Succeed()) }) It("updates the flow control window of the connection", func() { offset := protocol.ByteCount(0x800000) connFC.EXPECT().UpdateSendWindow(offset) sess.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(sess.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) sess.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 := sess.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(sess.handleFrame(&wire.StopSendingFrame{ StreamID: 3, ErrorCode: 1337, }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) }) }) It("handles NEW_CONNECTION_ID frames", func() { Expect(sess.handleFrame(&wire.NewConnectionIDFrame{ SequenceNumber: 10, ConnectionID: protocol.ConnectionID{1, 2, 3, 4}, }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) Expect(sess.connIDManager.queue.Back().Value.ConnectionID).To(Equal(protocol.ConnectionID{1, 2, 3, 4})) }) It("handles PING frames", func() { err := sess.handleFrame(&wire.PingFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("rejects PATH_RESPONSE frames", func() { err := sess.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 := sess.handleFrame(&wire.PathChallengeFrame{Data: data}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).ToNot(HaveOccurred()) frames, _ := sess.framer.AppendControlFrames(nil, 1000) Expect(frames).To(Equal([]ackhandler.Frame{{Frame: &wire.PathResponseFrame{Data: data}}})) }) It("rejects NEW_TOKEN frames", func() { err := sess.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 := sess.handleFrame(&wire.DataBlockedFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("handles STREAM_BLOCKED frames", func() { err := sess.handleFrame(&wire.StreamDataBlockedFrame{}, protocol.Encryption1RTT, protocol.ConnectionID{}) Expect(err).NotTo(HaveOccurred()) }) It("handles STREAMS_BLOCKED frames", func() { err := sess.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) sessionRunner.EXPECT().ReplaceWithClosed(srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { Expect(s).To(BeAssignableToTypeOf(&closedRemoteSession{})) }) sessionRunner.EXPECT().ReplaceWithClosed(clientDestConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { Expect(s).To(BeAssignableToTypeOf(&closedRemoteSession{})) }) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(expectedErr), tracer.EXPECT().Close(), ) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) Expect(sess.run()).To(MatchError(expectedErr)) }() Expect(sess.handleFrame(&wire.ConnectionCloseFrame{ ErrorCode: uint64(qerr.StreamLimitError), ReasonPhrase: "foobar", }, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) Eventually(sess.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) sessionRunner.EXPECT().ReplaceWithClosed(srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { Expect(s).To(BeAssignableToTypeOf(&closedRemoteSession{})) }) sessionRunner.EXPECT().ReplaceWithClosed(clientDestConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { Expect(s).To(BeAssignableToTypeOf(&closedRemoteSession{})) }) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(testErr), tracer.EXPECT().Close(), ) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) Expect(sess.run()).To(MatchError(testErr)) }() ccf := &wire.ConnectionCloseFrame{ ErrorCode: 0x1337, ReasonPhrase: "foobar", IsApplicationError: true, } Expect(sess.handleFrame(ccf, protocol.Encryption1RTT, protocol.ConnectionID{})).To(Succeed()) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("errors on HANDSHAKE_DONE frames", func() { Expect(sess.handleHandshakeDoneFrame()).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "received a HANDSHAKE_DONE frame", })) }) }) It("tells its versions", func() { sess.version = 4242 Expect(sess.GetVersion()).To(Equal(protocol.VersionNumber(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()) } }) runSession := func() { go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) runErr <- sess.run() }() Eventually(areSessionsRunning).Should(BeTrue()) } It("shuts down without error", func() { sess.handshakeComplete = true runSession() streamManager.EXPECT().CloseWithError(&qerr.ApplicationError{}) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("connection close")...) packer.EXPECT().PackApplicationClose(gomock.Any()).DoAndReturn(func(e *qerr.ApplicationError) (*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.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(), ) sess.shutdown() Eventually(areSessionsRunning).Should(BeFalse()) Expect(sess.Context().Done()).To(BeClosed()) }) It("only closes once", func() { runSession() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() sess.shutdown() Eventually(areSessionsRunning).Should(BeFalse()) Expect(sess.Context().Done()).To(BeClosed()) }) It("closes with an error", func() { runSession() expectedErr := &qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: "test error", } streamManager.EXPECT().CloseWithError(expectedErr) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackApplicationClose(expectedErr).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(expectedErr), tracer.EXPECT().Close(), ) sess.CloseWithError(0x1337, "test error") Eventually(areSessionsRunning).Should(BeFalse()) Expect(sess.Context().Done()).To(BeClosed()) }) It("includes the frame type in transport-level close frames", func() { runSession() expectedErr := &qerr.TransportError{ ErrorCode: 0x1337, FrameType: 0x42, ErrorMessage: "test error", } streamManager.EXPECT().CloseWithError(expectedErr) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(expectedErr).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) mconn.EXPECT().Write(gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(expectedErr), tracer.EXPECT().Close(), ) sess.closeLocal(expectedErr) Eventually(areSessionsRunning).Should(BeFalse()) Expect(sess.Context().Done()).To(BeClosed()) }) It("destroys the session", func() { runSession() testErr := errors.New("close") streamManager.EXPECT().CloseWithError(gomock.Any()) sessionRunner.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(), ) sess.destroy(testErr) Eventually(areSessionsRunning).Should(BeFalse()) expectedRunErr = &qerr.TransportError{ ErrorCode: qerr.InternalError, ErrorMessage: testErr.Error(), } }) It("cancels the context when the run loop exists", func() { runSession() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) returned := make(chan struct{}) go func() { defer GinkgoRecover() ctx := sess.Context() <-ctx.Done() Expect(ctx.Err()).To(MatchError(context.Canceled)) close(returned) }() Consistently(returned).ShouldNot(BeClosed()) mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(returned).Should(BeClosed()) }) It("doesn't send any more packets after receiving a CONNECTION_CLOSE", func() { unpacker := NewMockUnpacker(mockCtrl) sess.handshakeConfirmed = true sess.unpacker = unpacker runSession() cryptoSetup.EXPECT().Close() streamManager.EXPECT().CloseWithError(gomock.Any()) sessionRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()).AnyTimes() buf := &bytes.Buffer{} hdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumberLen: protocol.PacketNumberLen2, } Expect(hdr.Write(buf, sess.version)).To(Succeed()) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(*wire.Header, time.Time, []byte) (*unpackedPacket, error) { buf := &bytes.Buffer{} Expect((&wire.ConnectionCloseFrame{ErrorCode: uint64(qerr.StreamLimitError)}).Write(buf, sess.version)).To(Succeed()) return &unpackedPacket{ hdr: hdr, data: buf.Bytes(), encryptionLevel: protocol.Encryption1RTT, }, nil }) gomock.InOrder( tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()), tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()), tracer.EXPECT().ClosedConnection(gomock.Any()), tracer.EXPECT().Close(), ) // don't EXPECT any calls to packer.PackPacket() sess.handlePacket(&receivedPacket{ rcvTime: time.Now(), remoteAddr: &net.UDPAddr{}, buffer: getPacketBuffer(), data: buf.Bytes(), }) // Consistently(pack).ShouldNot(Receive()) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("closes when the sendQueue encounters an error", func() { sess.handshakeConfirmed = true conn := NewMockSendConn(mockCtrl) conn.EXPECT().Write(gomock.Any()).Return(io.ErrClosedPipe).AnyTimes() sess.sendQueue = newSendQueue(conn) sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().Return(time.Now().Add(time.Hour)).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() // only expect a single SentPacket() call sph.EXPECT().SentPacket(gomock.Any()) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() streamManager.EXPECT().CloseWithError(gomock.Any()) sessionRunner.EXPECT().Remove(gomock.Any()).AnyTimes() cryptoSetup.EXPECT().Close() sess.sentPacketHandler = sph p := getPacket(1) packer.EXPECT().PackPacket().Return(p, nil) packer.EXPECT().PackPacket().Return(nil, nil).AnyTimes() runSession() sess.queueControlFrame(&wire.PingFrame{}) sess.scheduleSending() Eventually(sess.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} runSession() 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()) sessionRunner.EXPECT().Remove(gomock.Any()).AnyTimes() cryptoSetup.EXPECT().Close() sess.destroy(&StatelessResetError{Token: token}) }) }) Context("receiving packets", func() { var unpacker *MockUnpacker BeforeEach(func() { unpacker = NewMockUnpacker(mockCtrl) sess.unpacker = unpacker }) getPacket := func(extHdr *wire.ExtendedHeader, data []byte) *receivedPacket { buf := &bytes.Buffer{} Expect(extHdr.Write(buf, sess.version)).To(Succeed()) return &receivedPacket{ data: append(buf.Bytes(), data...), buffer: getPacketBuffer(), rcvTime: time.Now(), } } It("drops Retry packets", func() { p := getPacket(&wire.ExtendedHeader{Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeRetry, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Version: sess.version, Token: []byte("foobar"), }}, make([]byte, 16) /* Retry integrity tag */) tracer.EXPECT().DroppedPacket(logging.PacketTypeRetry, p.Size(), logging.PacketDropUnexpectedPacket) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) It("drops Version Negotiation packets", func() { b, err := wire.ComposeVersionNegotiation(srcConnID, destConnID, sess.config.Versions) Expect(err).ToNot(HaveOccurred()) tracer.EXPECT().DroppedPacket(logging.PacketTypeVersionNegotiation, protocol.ByteCount(len(b)), logging.PacketDropUnexpectedPacket) Expect(sess.handlePacketImpl(&receivedPacket{ data: b, buffer: getPacketBuffer(), })).To(BeFalse()) }) It("drops packets for which header decryption fails", func() { p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Version: sess.version, }, PacketNumberLen: protocol.PacketNumberLen2, }, nil) p.data[0] ^= 0x40 // unset the QUIC bit tracer.EXPECT().DroppedPacket(logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropHeaderParseError) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) It("drops packets for which the version is unsupported", func() { p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, Version: sess.version + 1, }, PacketNumberLen: protocol.PacketNumberLen2, }, nil) tracer.EXPECT().DroppedPacket(logging.PacketTypeNotDetermined, p.Size(), logging.PacketDropUnsupportedVersion) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) It("drops packets with an unsupported version", func() { origSupportedVersions := make([]protocol.VersionNumber, len(protocol.SupportedVersions)) copy(origSupportedVersions, protocol.SupportedVersions) defer func() { protocol.SupportedVersions = origSupportedVersions }() protocol.SupportedVersions = append(protocol.SupportedVersions, sess.version+1) p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Version: sess.version + 1, }, PacketNumberLen: protocol.PacketNumberLen2, }, nil) tracer.EXPECT().DroppedPacket(logging.PacketTypeHandshake, p.Size(), logging.PacketDropUnexpectedVersion) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) It("informs the ReceivedPacketHandler about non-ack-eliciting packets", func() { hdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumber: 0x37, PacketNumberLen: protocol.PacketNumberLen1, } packet := getPacket(hdr, nil) packet.ecn = protocol.ECNCE rcvTime := time.Now().Add(-10 * time.Second) unpacker.EXPECT().Unpack(gomock.Any(), rcvTime, gomock.Any()).Return(&unpackedPacket{ packetNumber: 0x1337, encryptionLevel: protocol.EncryptionInitial, hdr: hdr, 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), ) sess.receivedPacketHandler = rph packet.rcvTime = rcvTime tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedPacket(hdr, protocol.ByteCount(len(packet.data)), []logging.Frame{}) Expect(sess.handlePacketImpl(packet)).To(BeTrue()) }) It("informs the ReceivedPacketHandler about ack-eliciting packets", func() { hdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumber: 0x37, PacketNumberLen: protocol.PacketNumberLen1, } rcvTime := time.Now().Add(-10 * time.Second) buf := &bytes.Buffer{} Expect((&wire.PingFrame{}).Write(buf, sess.version)).To(Succeed()) packet := getPacket(hdr, nil) packet.ecn = protocol.ECT1 unpacker.EXPECT().Unpack(gomock.Any(), rcvTime, gomock.Any()).Return(&unpackedPacket{ packetNumber: 0x1337, encryptionLevel: protocol.Encryption1RTT, hdr: hdr, data: buf.Bytes(), }, 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), ) sess.receivedPacketHandler = rph packet.rcvTime = rcvTime tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedPacket(hdr, protocol.ByteCount(len(packet.data)), []logging.Frame{&logging.PingFrame{}}) Expect(sess.handlePacketImpl(packet)).To(BeTrue()) }) It("drops duplicate packets", func() { hdr := &wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumber: 0x37, PacketNumberLen: protocol.PacketNumberLen1, } packet := getPacket(hdr, nil) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(&unpackedPacket{ packetNumber: 0x1337, encryptionLevel: protocol.Encryption1RTT, hdr: hdr, data: []byte("foobar"), }, nil) rph := mockackhandler.NewMockReceivedPacketHandler(mockCtrl) rph.EXPECT().IsPotentiallyDuplicate(protocol.PacketNumber(0x1337), protocol.Encryption1RTT).Return(true) sess.receivedPacketHandler = rph tracer.EXPECT().DroppedPacket(logging.PacketType1RTT, protocol.ByteCount(len(packet.data)), logging.PacketDropDuplicate) Expect(sess.handlePacketImpl(packet)).To(BeFalse()) }) It("drops a packet when unpacking fails", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrDecryptionFailed) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() expectReplaceWithClosed() p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: srcConnID, Version: sess.version, Length: 2 + 6, }, PacketNumber: 0x1337, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar")) tracer.EXPECT().DroppedPacket(logging.PacketTypeHandshake, p.Size(), logging.PacketDropPayloadDecryptError) sess.handlePacket(p) Consistently(sess.Context().Done()).ShouldNot(BeClosed()) // make the go routine return tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.closeLocal(errors.New("close")) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("processes multiple received packets before sending one", func() { sess.sessionCreationTime = time.Now() var pn protocol.PacketNumber unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error) { pn++ return &unpackedPacket{ data: []byte{0}, // PADDING frame encryptionLevel: protocol.Encryption1RTT, packetNumber: pn, hdr: &wire.ExtendedHeader{Header: *hdr}, }, nil }).Times(3) tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(hdr *wire.ExtendedHeader, _ protocol.ByteCount, _ []logging.Frame) { }).Times(3) packer.EXPECT().PackCoalescedPacket() // only expect a single call for i := 0; i < 3; i++ { sess.handlePacket(getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumber: 0x1337, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar"))) } go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() Consistently(sess.Context().Done()).ShouldNot(BeClosed()) // make the go routine return streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.closeLocal(errors.New("close")) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("doesn't processes multiple received packets before sending one before handshake completion", func() { sess.handshakeComplete = false sess.sessionCreationTime = time.Now() var pn protocol.PacketNumber unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(hdr *wire.Header, rcvTime time.Time, data []byte) (*unpackedPacket, error) { pn++ return &unpackedPacket{ data: []byte{0}, // PADDING frame encryptionLevel: protocol.Encryption1RTT, packetNumber: pn, hdr: &wire.ExtendedHeader{Header: *hdr}, }, nil }).Times(3) tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()).Do(func(hdr *wire.ExtendedHeader, _ protocol.ByteCount, _ []logging.Frame) { }).Times(3) packer.EXPECT().PackCoalescedPacket().Times(3) // only expect a single call for i := 0; i < 3; i++ { sess.handlePacket(getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumber: 0x1337, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar"))) } go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() Consistently(sess.Context().Done()).ShouldNot(BeClosed()) // make the go routine return streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.closeLocal(errors.New("close")) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("closes the session when unpacking fails because the reserved bits were incorrect", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, wire.ErrInvalidReservedBits) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) err := sess.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()) packet := getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumberLen: protocol.PacketNumberLen1, }, nil) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.handlePacket(packet) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("ignores packets when unpacking the header fails", func() { testErr := &headerParseError{errors.New("test error")} unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, testErr) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() runErr := make(chan error) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) runErr <- sess.run() }() expectReplaceWithClosed() tracer.EXPECT().DroppedPacket(logging.PacketType1RTT, gomock.Any(), logging.PacketDropHeaderParseError) sess.handlePacket(getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumberLen: protocol.PacketNumberLen1, }, nil)) Consistently(runErr).ShouldNot(Receive()) // make the go routine return packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("closes the session when unpacking fails because of an error other than a decryption error", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &qerr.TransportError{ErrorCode: qerr.ConnectionIDLimitError}) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) err := sess.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()) packet := getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumberLen: protocol.PacketNumberLen1, }, nil) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.handlePacket(packet) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("rejects packets with empty payload", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(&unpackedPacket{ hdr: &wire.ExtendedHeader{}, data: []byte{}, // no payload encryptionLevel: protocol.Encryption1RTT, }, nil) streamManager.EXPECT().CloseWithError(gomock.Any()) cryptoSetup.EXPECT().Close() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) Expect(sess.run()).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.ProtocolViolation, ErrorMessage: "empty packet", })) close(done) }() expectReplaceWithClosed() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.handlePacket(getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumberLen: protocol.PacketNumberLen1, }, nil)) Eventually(done).Should(BeClosed()) }) It("ignores packets with a different source connection ID", func() { hdr1 := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Length: 1, Version: sess.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 1, } hdr2 := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, Length: 1, Version: sess.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().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(&unpackedPacket{ encryptionLevel: protocol.Encryption1RTT, hdr: hdr1, data: []byte{0}, // one PADDING frame }, nil) p1 := getPacket(hdr1, nil) tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(p1.data)), gomock.Any()) Expect(sess.handlePacketImpl(p1)).To(BeTrue()) // The next packet has to be ignored, since the source connection ID doesn't match. p2 := getPacket(hdr2, nil) tracer.EXPECT().DroppedPacket(logging.PacketTypeInitial, protocol.ByteCount(len(p2.data)), logging.PacketDropUnknownConnectionID) Expect(sess.handlePacketImpl(p2)).To(BeFalse()) }) It("queues undecryptable packets", func() { sess.handshakeComplete = false hdr := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Length: 1, Version: sess.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 1, } unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrKeysNotYetAvailable) packet := getPacket(hdr, nil) tracer.EXPECT().BufferedPacket(logging.PacketTypeHandshake) Expect(sess.handlePacketImpl(packet)).To(BeFalse()) Expect(sess.undecryptablePackets).To(Equal([]*receivedPacket{packet})) }) Context("updating the remote address", func() { It("doesn't support connection migration", func() { unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(&unpackedPacket{ encryptionLevel: protocol.Encryption1RTT, hdr: &wire.ExtendedHeader{}, data: []byte{0}, // one PADDING frame }, nil) packet := getPacket(&wire.ExtendedHeader{ Header: wire.Header{DestConnectionID: srcConnID}, PacketNumberLen: protocol.PacketNumberLen1, }, nil) packet.remoteAddr = &net.IPAddr{IP: net.IPv4(192, 168, 0, 100)} tracer.EXPECT().StartedConnection(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(packet.data)), gomock.Any()) Expect(sess.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{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: connID, SrcConnectionID: destConnID, Version: protocol.VersionTLS, Length: length, }, PacketNumberLen: protocol.PacketNumberLen3, } hdrLen := hdr.GetLength(sess.version) b := make([]byte, 1) rand.Read(b) packet := getPacket(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().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, _ time.Time, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen + 456 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{}, }, nil }) tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(packet.data)), gomock.Any()) Expect(sess.handlePacketImpl(packet)).To(BeTrue()) }) It("handles coalesced packets", func() { hdrLen1, packet1 := getPacketWithLength(srcConnID, 456) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, _ time.Time, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen1 + 456 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, packetNumber: 1, hdr: &wire.ExtendedHeader{Header: wire.Header{SrcConnectionID: destConnID}}, }, nil }) hdrLen2, packet2 := getPacketWithLength(srcConnID, 123) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, _ time.Time, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen2 + 123 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, packetNumber: 2, hdr: &wire.ExtendedHeader{Header: wire.Header{SrcConnectionID: destConnID}}, }, nil }) gomock.InOrder( tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(packet1.data)), gomock.Any()), tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(packet2.data)), gomock.Any()), ) packet1.data = append(packet1.data, packet2.data...) Expect(sess.handlePacketImpl(packet1)).To(BeTrue()) }) It("works with undecryptable packets", func() { sess.handshakeComplete = false hdrLen1, packet1 := getPacketWithLength(srcConnID, 456) hdrLen2, packet2 := getPacketWithLength(srcConnID, 123) gomock.InOrder( unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, handshake.ErrKeysNotYetAvailable), unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, _ time.Time, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen2 + 123 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{}, }, nil }), ) gomock.InOrder( tracer.EXPECT().BufferedPacket(gomock.Any()), tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(packet2.data)), gomock.Any()), ) packet1.data = append(packet1.data, packet2.data...) Expect(sess.handlePacketImpl(packet1)).To(BeTrue()) Expect(sess.undecryptablePackets).To(HaveLen(1)) Expect(sess.undecryptablePackets[0].data).To(HaveLen(hdrLen1 + 456 - 3)) }) It("ignores coalesced packet parts if the destination connection IDs don't match", func() { wrongConnID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} Expect(srcConnID).ToNot(Equal(wrongConnID)) hdrLen1, packet1 := getPacketWithLength(srcConnID, 456) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(_ *wire.Header, _ time.Time, data []byte) (*unpackedPacket, error) { Expect(data).To(HaveLen(hdrLen1 + 456 - 3)) return &unpackedPacket{ encryptionLevel: protocol.EncryptionHandshake, data: []byte{0}, hdr: &wire.ExtendedHeader{}, }, nil }) _, packet2 := getPacketWithLength(wrongConnID, 123) // don't EXPECT any more calls to unpacker.Unpack() gomock.InOrder( tracer.EXPECT().ReceivedPacket(gomock.Any(), protocol.ByteCount(len(packet1.data)), gomock.Any()), tracer.EXPECT().DroppedPacket(gomock.Any(), protocol.ByteCount(len(packet2.data)), logging.PacketDropUnknownConnectionID), ) packet1.data = append(packet1.data, packet2.data...) Expect(sess.handlePacketImpl(packet1)).To(BeTrue()) }) }) }) Context("sending packets", func() { var ( sessionDone chan struct{} sender *MockSender ) BeforeEach(func() { sender = NewMockSender(mockCtrl) sender.EXPECT().Run() sender.EXPECT().WouldBlock().AnyTimes() sess.sendQueue = sender sessionDone = make(chan struct{}) }) AfterEach(func() { streamManager.EXPECT().CloseWithError(gomock.Any()) packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sender.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) Eventually(sessionDone).Should(BeClosed()) }) runSession := func() { go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() close(sessionDone) }() } It("sends packets", func() { sess.handshakeConfirmed = true sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SentPacket(gomock.Any()) sess.sentPacketHandler = sph runSession() p := getPacket(1) packer.EXPECT().PackPacket().Return(p, nil) packer.EXPECT().PackPacket().Return(nil, nil).AnyTimes() sent := make(chan struct{}) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()).Do(func(packet *packetBuffer) { close(sent) }) tracer.EXPECT().SentPacket(p.header, p.buffer.Len(), nil, []logging.Frame{}) sess.scheduleSending() Eventually(sent).Should(BeClosed()) }) It("doesn't send packets if there's nothing to send", func() { sess.handshakeConfirmed = true runSession() packer.EXPECT().PackPacket().Return(nil, nil).AnyTimes() sess.receivedPacketHandler.ReceivedPacket(0x035e, protocol.ECNNon, protocol.Encryption1RTT, time.Now(), true) sess.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure there are no calls to mconn.Write() }) It("sends ACK only packets", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAck) done := make(chan struct{}) packer.EXPECT().MaybePackAckPacket(false).Do(func(bool) { close(done) }) sess.sentPacketHandler = sph runSession() sess.scheduleSending() Eventually(done).Should(BeClosed()) }) It("adds a BLOCKED frame when it is connection-level flow control blocked", func() { sess.handshakeConfirmed = true sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SentPacket(gomock.Any()) sess.sentPacketHandler = sph fc := mocks.NewMockConnectionFlowController(mockCtrl) fc.EXPECT().IsNewlyBlocked().Return(true, protocol.ByteCount(1337)) fc.EXPECT().IsNewlyBlocked() p := getPacket(1) packer.EXPECT().PackPacket().Return(p, nil) packer.EXPECT().PackPacket().Return(nil, nil).AnyTimes() sess.connFlowController = fc runSession() sent := make(chan struct{}) sender.EXPECT().Send(gomock.Any()).Do(func(packet *packetBuffer) { close(sent) }) tracer.EXPECT().SentPacket(p.header, p.length, nil, []logging.Frame{}) sess.scheduleSending() Eventually(sent).Should(BeClosed()) frames, _ := sess.framer.AppendControlFrames(nil, 1000) Expect(frames).To(Equal([]ackhandler.Frame{{Frame: &logging.DataBlockedFrame{MaximumData: 1337}}})) }) It("doesn't send when the SentPacketHandler doesn't allow it", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendNone).AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sess.sentPacketHandler = sph runSession() sess.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) wire.Frame BeforeEach(func() { //nolint:exhaustive switch encLevel { case protocol.EncryptionInitial: sendMode = ackhandler.SendPTOInitial getFrame = sess.retransmissionQueue.GetInitialFrame case protocol.EncryptionHandshake: sendMode = ackhandler.SendPTOHandshake getFrame = sess.retransmissionQueue.GetHandshakeFrame case protocol.Encryption1RTT: sendMode = ackhandler.SendPTOAppData getFrame = sess.retransmissionQueue.GetAppDataFrame } }) It("sends a probe packet", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode().Return(sendMode) sph.EXPECT().SendMode().Return(ackhandler.SendNone) sph.EXPECT().QueueProbePacket(encLevel) p := getPacket(123) packer.EXPECT().MaybePackProbePacket(encLevel).Return(p, nil) sph.EXPECT().SentPacket(gomock.Any()).Do(func(packet *ackhandler.Packet) { Expect(packet.PacketNumber).To(Equal(protocol.PacketNumber(123))) }) sess.sentPacketHandler = sph runSession() sent := make(chan struct{}) sender.EXPECT().Send(gomock.Any()).Do(func(packet *packetBuffer) { close(sent) }) tracer.EXPECT().SentPacket(p.header, p.length, gomock.Any(), gomock.Any()) sess.scheduleSending() Eventually(sent).Should(BeClosed()) }) It("sends a PING as a probe packet", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode().Return(sendMode) sph.EXPECT().SendMode().Return(ackhandler.SendNone) sph.EXPECT().QueueProbePacket(encLevel).Return(false) p := getPacket(123) packer.EXPECT().MaybePackProbePacket(encLevel).Return(p, nil) sph.EXPECT().SentPacket(gomock.Any()).Do(func(packet *ackhandler.Packet) { Expect(packet.PacketNumber).To(Equal(protocol.PacketNumber(123))) }) sess.sentPacketHandler = sph runSession() sent := make(chan struct{}) sender.EXPECT().Send(gomock.Any()).Do(func(packet *packetBuffer) { close(sent) }) tracer.EXPECT().SentPacket(p.header, p.length, gomock.Any(), gomock.Any()) sess.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)).To(BeAssignableToTypeOf(&wire.PingFrame{})) }) }) } }) Context("packet pacing", func() { var ( sph *mockackhandler.MockSentPacketHandler sender *MockSender ) BeforeEach(func() { tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sph = mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sess.handshakeConfirmed = true sess.handshakeComplete = true sess.sentPacketHandler = sph sender = NewMockSender(mockCtrl) sender.EXPECT().Run() sess.sendQueue = sender streamManager.EXPECT().CloseWithError(gomock.Any()) }) AfterEach(func() { // make the go routine return packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sender.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("sends multiple packets one by one immediately", func() { sph.EXPECT().SentPacket(gomock.Any()).Times(2) sph.EXPECT().HasPacingBudget().Return(true).Times(2) sph.EXPECT().HasPacingBudget() sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)) sph.EXPECT().SendMode().Return(ackhandler.SendAny).Times(3) packer.EXPECT().PackPacket().Return(getPacket(10), nil) packer.EXPECT().PackPacket().Return(getPacket(11), nil) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()).Times(2) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.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()) sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).Times(2) packer.EXPECT().PackPacket().Return(getPacket(10), nil) packer.EXPECT().PackPacket().Return(nil, nil) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.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()) sph.EXPECT().HasPacingBudget() sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)) sph.EXPECT().SendMode().Return(ackhandler.SendAny) packer.EXPECT().MaybePackAckPacket(gomock.Any()).Return(getPacket(10), nil) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.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()) sph.EXPECT().HasPacingBudget().Return(true) sph.EXPECT().SendMode().Return(ackhandler.SendAny) sph.EXPECT().SendMode().Return(ackhandler.SendAck) packer.EXPECT().PackPacket().Return(getPacket(100), nil) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.scheduleSending() time.Sleep(50 * time.Millisecond) // make sure that only 1 packet is sent }) It("paces packets", func() { pacingDelay := scaleDuration(100 * time.Millisecond) sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() gomock.InOrder( sph.EXPECT().HasPacingBudget().Return(true), packer.EXPECT().PackPacket().Return(getPacket(100), nil), sph.EXPECT().SentPacket(gomock.Any()), sph.EXPECT().HasPacingBudget(), sph.EXPECT().TimeUntilSend().Return(time.Now().Add(pacingDelay)), sph.EXPECT().HasPacingBudget().Return(true), packer.EXPECT().PackPacket().Return(getPacket(101), nil), sph.EXPECT().SentPacket(gomock.Any()), sph.EXPECT().HasPacingBudget(), sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)), ) written := make(chan struct{}, 2) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { written <- struct{}{} }).Times(2) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.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()).Times(3) sph.EXPECT().HasPacingBudget().Return(true).Times(3) sph.EXPECT().HasPacingBudget() sph.EXPECT().TimeUntilSend().Return(time.Now().Add(time.Hour)) sph.EXPECT().SendMode().Return(ackhandler.SendAny).Times(4) packer.EXPECT().PackPacket().Return(getPacket(1000), nil) packer.EXPECT().PackPacket().Return(getPacket(1001), nil) packer.EXPECT().PackPacket().Return(getPacket(1002), nil) written := make(chan struct{}, 3) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { written <- struct{}{} }).Times(3) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.scheduleSending() Eventually(written).Should(HaveLen(3)) }) It("doesn't try to send if the send queue is full", func() { available := make(chan struct{}, 1) sender.EXPECT().WouldBlock().Return(true) sender.EXPECT().Available().Return(available) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.scheduleSending() time.Sleep(scaleDuration(50 * time.Millisecond)) written := make(chan struct{}) sender.EXPECT().WouldBlock().AnyTimes() sph.EXPECT().SentPacket(gomock.Any()) sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() packer.EXPECT().PackPacket().Return(getPacket(1000), nil) packer.EXPECT().PackPacket().Return(nil, nil) sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { 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().RunHandshake().MaxTimes(1) sess.run() }() written := make(chan struct{}) sender.EXPECT().WouldBlock().AnyTimes() sph.EXPECT().SentPacket(gomock.Any()).Do(func(*ackhandler.Packet) { sph.EXPECT().ReceivedBytes(gomock.Any()) sess.handlePacket(&receivedPacket{buffer: getPacketBuffer()}) }) sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() packer.EXPECT().PackPacket().Return(getPacket(1000), nil) packer.EXPECT().PackPacket().Return(nil, nil) sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { close(written) }) sess.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()) sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny) packer.EXPECT().PackPacket().Return(getPacket(1000), nil) written := make(chan struct{}, 1) sender.EXPECT().WouldBlock() sender.EXPECT().WouldBlock().Return(true).Times(2) sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { written <- struct{}{} }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() available := make(chan struct{}, 1) sender.EXPECT().Available().Return(available) sess.scheduleSending() Eventually(written).Should(Receive()) time.Sleep(scaleDuration(50 * time.Millisecond)) // now make room in the send queue sph.EXPECT().SentPacket(gomock.Any()) sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sender.EXPECT().WouldBlock().AnyTimes() packer.EXPECT().PackPacket().Return(getPacket(1001), nil) packer.EXPECT().PackPacket().Return(nil, nil) sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { 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().HasPacingBudget().Return(true) sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sender.EXPECT().WouldBlock().AnyTimes() packer.EXPECT().PackPacket() // don't EXPECT any calls to mconn.Write() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.scheduleSending() // no packet will get sent time.Sleep(50 * time.Millisecond) }) if runtime.GOOS != "windows" { // Path MTU Discovery is disabled on Windows It("sends a Path MTU probe packet", func() { mtuDiscoverer := NewMockMtuDiscoverer(mockCtrl) sess.mtuDiscoverer = mtuDiscoverer sess.config.DisablePathMTUDiscovery = false sph.EXPECT().SentPacket(gomock.Any()) sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny) sph.EXPECT().SendMode().Return(ackhandler.SendNone) written := make(chan struct{}, 1) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Send(gomock.Any()).DoAndReturn(func(p *packetBuffer) { written <- struct{}{} }) gomock.InOrder( mtuDiscoverer.EXPECT().NextProbeTime(), mtuDiscoverer.EXPECT().ShouldSendProbe(gomock.Any()).Return(true), mtuDiscoverer.EXPECT().NextProbeTime(), ) ping := ackhandler.Frame{Frame: &wire.PingFrame{}} mtuDiscoverer.EXPECT().GetPing().Return(ping, protocol.ByteCount(1234)) packer.EXPECT().PackMTUProbePacket(ping, protocol.ByteCount(1234)).Return(getPacket(1), nil) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.scheduleSending() Eventually(written).Should(Receive()) }) } }) Context("scheduling sending", func() { var sender *MockSender BeforeEach(func() { sender = NewMockSender(mockCtrl) sender.EXPECT().WouldBlock().AnyTimes() sender.EXPECT().Run() sess.sendQueue = sender sess.handshakeConfirmed = true }) AfterEach(func() { // make the go routine return expectReplaceWithClosed() streamManager.EXPECT().CloseWithError(gomock.Any()) packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sender.EXPECT().Close() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.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().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SentPacket(gomock.Any()) sess.sentPacketHandler = sph packer.EXPECT().PackPacket().Return(getPacket(1), nil) packer.EXPECT().PackPacket().Return(nil, nil) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.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()).Do(func(*packetBuffer) { close(written) }) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() sess.scheduleSending() Eventually(written).Should(BeClosed()) }) It("sets the timer to the ack timer", func() { packer.EXPECT().PackPacket().Return(getPacket(1234), nil) packer.EXPECT().PackPacket().Return(nil, nil) sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SentPacket(gomock.Any()).Do(func(p *ackhandler.Packet) { Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(1234))) }) sess.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) sess.receivedPacketHandler = rph written := make(chan struct{}) sender.EXPECT().Send(gomock.Any()).Do(func(*packetBuffer) { close(written) }) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() Eventually(written).Should(BeClosed()) }) }) It("sends coalesced packets before the handshake is confirmed", func() { sess.handshakeComplete = false sess.handshakeConfirmed = false sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sess.sentPacketHandler = sph buffer := getPacketBuffer() buffer.Data = append(buffer.Data, []byte("foobar")...) packer.EXPECT().PackCoalescedPacket().Return(&coalescedPacket{ buffer: buffer, packets: []*packetContents{ { header: &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, }, PacketNumber: 13, }, length: 123, }, { header: &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, }, PacketNumber: 37, }, length: 1234, }, }, }, nil) packer.EXPECT().PackCoalescedPacket().AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().TimeUntilSend().Return(time.Now()).AnyTimes() gomock.InOrder( sph.EXPECT().SentPacket(gomock.Any()).Do(func(p *ackhandler.Packet) { Expect(p.EncryptionLevel).To(Equal(protocol.EncryptionInitial)) Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(13))) Expect(p.Length).To(BeEquivalentTo(123)) }), sph.EXPECT().SentPacket(gomock.Any()).Do(func(p *ackhandler.Packet) { Expect(p.EncryptionLevel).To(Equal(protocol.EncryptionHandshake)) Expect(p.PacketNumber).To(Equal(protocol.PacketNumber(37))) Expect(p.Length).To(BeEquivalentTo(1234)) }), ) gomock.InOrder( tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(hdr *wire.ExtendedHeader, _ protocol.ByteCount, _ *wire.AckFrame, _ []logging.Frame) { Expect(hdr.Type).To(Equal(protocol.PacketTypeInitial)) }), tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Do(func(hdr *wire.ExtendedHeader, _ protocol.ByteCount, _ *wire.AckFrame, _ []logging.Frame) { Expect(hdr.Type).To(Equal(protocol.PacketTypeHandshake)) }), ) sent := make(chan struct{}) mconn.EXPECT().Write([]byte("foobar")).Do(func([]byte) { close(sent) }) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() sess.scheduleSending() Eventually(sent).Should(BeClosed()) // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("cancels the HandshakeComplete context when the handshake completes", func() { packer.EXPECT().PackCoalescedPacket().AnyTimes() finishHandshake := make(chan struct{}) sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sess.sentPacketHandler = sph sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().SendMode().AnyTimes() sph.EXPECT().SetHandshakeConfirmed() sessionRunner.EXPECT().Retire(clientDestConnID) go func() { defer GinkgoRecover() <-finishHandshake cryptoSetup.EXPECT().RunHandshake() cryptoSetup.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().GetSessionTicket() close(sess.handshakeCompleteChan) sess.run() }() handshakeCtx := sess.HandshakeComplete() Consistently(handshakeCtx.Done()).ShouldNot(BeClosed()) close(finishHandshake) Eventually(handshakeCtx.Done()).Should(BeClosed()) // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("sends a session ticket when the handshake completes", func() { const size = protocol.MaxPostHandshakeCryptoFrameSize * 3 / 2 packer.EXPECT().PackCoalescedPacket().AnyTimes() finishHandshake := make(chan struct{}) sessionRunner.EXPECT().Retire(clientDestConnID) go func() { defer GinkgoRecover() <-finishHandshake cryptoSetup.EXPECT().RunHandshake() cryptoSetup.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().GetSessionTicket().Return(make([]byte, size), nil) close(sess.handshakeCompleteChan) sess.run() }() handshakeCtx := sess.HandshakeComplete() Consistently(handshakeCtx.Done()).ShouldNot(BeClosed()) close(finishHandshake) var frames []ackhandler.Frame Eventually(func() []ackhandler.Frame { frames, _ = sess.framer.AppendControlFrames(nil, protocol.MaxByteCount) 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.Length(sess.version)).To(BeNumerically("<=", protocol.MaxPostHandshakeCryptoFrameSize)) } } Expect(size).To(BeEquivalentTo(s)) // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("doesn't cancel the HandshakeComplete context when the handshake fails", func() { packer.EXPECT().PackCoalescedPacket().AnyTimes() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake() sess.run() }() handshakeCtx := sess.HandshakeComplete() Consistently(handshakeCtx.Done()).ShouldNot(BeClosed()) mconn.EXPECT().Write(gomock.Any()) sess.closeLocal(errors.New("handshake error")) Consistently(handshakeCtx.Done()).ShouldNot(BeClosed()) Eventually(sess.Context().Done()).Should(BeClosed()) }) It("sends a HANDSHAKE_DONE frame when the handshake completes", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sph.EXPECT().SendMode().Return(ackhandler.SendAny).AnyTimes() sph.EXPECT().GetLossDetectionTimeout().AnyTimes() sph.EXPECT().TimeUntilSend().AnyTimes() sph.EXPECT().HasPacingBudget().Return(true).AnyTimes() sph.EXPECT().SetHandshakeConfirmed() sph.EXPECT().SentPacket(gomock.Any()) mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().SentPacket(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()) sess.sentPacketHandler = sph done := make(chan struct{}) sessionRunner.EXPECT().Retire(clientDestConnID) packer.EXPECT().PackPacket().DoAndReturn(func() (*packedPacket, error) { frames, _ := sess.framer.AppendControlFrames(nil, protocol.MaxByteCount) Expect(frames).ToNot(BeEmpty()) Expect(frames[0].Frame).To(BeEquivalentTo(&wire.HandshakeDoneFrame{})) defer close(done) return &packedPacket{ packetContents: &packetContents{ header: &wire.ExtendedHeader{}, }, buffer: getPacketBuffer(), }, nil }) packer.EXPECT().PackPacket().AnyTimes() go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake() cryptoSetup.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().GetSessionTicket() mconn.EXPECT().Write(gomock.Any()) close(sess.handshakeCompleteChan) sess.run() }() Eventually(done).Should(BeClosed()) // make sure the go routine returns streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("doesn't return a run error when closing", func() { done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) Expect(sess.run()).To(Succeed()) close(done) }() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(done).Should(BeClosed()) }) It("passes errors to the session runner", func() { testErr := errors.New("handshake error") done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) err := sess.run() Expect(err).To(MatchError(&qerr.ApplicationError{ ErrorCode: 0x1337, ErrorMessage: testErr.Error(), })) close(done) }() streamManager.EXPECT().CloseWithError(gomock.Any()) expectReplaceWithClosed() packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() Expect(sess.CloseWithError(0x1337, testErr.Error())).To(Succeed()) Eventually(done).Should(BeClosed()) }) 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().HandleTransportParameters(params) packer.EXPECT().PackCoalescedPacket().MaxTimes(3) Expect(sess.earlySessionReady()).ToNot(BeClosed()) sessionRunner.EXPECT().GetStatelessResetToken(gomock.Any()).Times(2) sessionRunner.EXPECT().Add(gomock.Any(), sess).Times(2) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) Expect(sess.earlySessionReady()).To(BeClosed()) }) }) Context("keep-alives", func() { setRemoteIdleTimeout := func(t time.Duration) { streamManager.EXPECT().UpdateLimits(gomock.Any()) packer.EXPECT().HandleTransportParameters(gomock.Any()) tracer.EXPECT().ReceivedTransportParameters(gomock.Any()) sess.handleTransportParameters(&wire.TransportParameters{ MaxIdleTimeout: t, InitialSourceConnectionID: destConnID, }) } runSession := func() { go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() } BeforeEach(func() { sess.config.MaxIdleTimeout = 30 * time.Second sess.config.KeepAlive = true sess.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()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("sends a PING as a keep-alive after half the idle timeout", func() { setRemoteIdleTimeout(5 * time.Second) sess.lastPacketReceivedTime = time.Now().Add(-5 * time.Second / 2) sent := make(chan struct{}) packer.EXPECT().PackCoalescedPacket().Do(func() (*packedPacket, error) { close(sent) return nil, nil }) runSession() Eventually(sent).Should(BeClosed()) }) It("sends a PING after a maximum of protocol.MaxKeepAliveInterval", func() { sess.config.MaxIdleTimeout = time.Hour setRemoteIdleTimeout(time.Hour) sess.lastPacketReceivedTime = time.Now().Add(-protocol.MaxKeepAliveInterval).Add(-time.Millisecond) sent := make(chan struct{}) packer.EXPECT().PackCoalescedPacket().Do(func() (*packedPacket, error) { close(sent) return nil, nil }) runSession() Eventually(sent).Should(BeClosed()) }) It("doesn't send a PING packet if keep-alive is disabled", func() { setRemoteIdleTimeout(5 * time.Second) sess.config.KeepAlive = false sess.lastPacketReceivedTime = time.Now().Add(-time.Second * 5 / 2) runSession() // 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() { sess.config.HandshakeIdleTimeout = time.Hour sess.handshakeComplete = false // Needs to be shorter than our idle timeout. // Otherwise we'll try to send a CONNECTION_CLOSE. sess.lastPacketReceivedTime = time.Now().Add(-20 * time.Second) runSession() // don't EXPECT() any calls to mconn.Write() time.Sleep(50 * time.Millisecond) }) }) Context("timeouts", func() { BeforeEach(func() { streamManager.EXPECT().CloseWithError(gomock.Any()) }) It("times out due to no network activity", func() { sessionRunner.EXPECT().Remove(gomock.Any()).Times(2) sess.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().RunHandshake().MaxTimes(1) err := sess.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() { sess.handshakeComplete = false sess.sessionCreationTime = time.Now().Add(-protocol.DefaultHandshakeTimeout).Add(-time.Second) sessionRunner.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().RunHandshake().MaxTimes(1) err := sess.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() { sess.handshakeComplete = false sess.config.HandshakeIdleTimeout = 9999 * time.Second sess.config.MaxIdleTimeout = 9999 * time.Second sess.lastPacketReceivedTime = time.Now().Add(-time.Minute) packer.EXPECT().PackApplicationClose(gomock.Any()).DoAndReturn(func(e *qerr.ApplicationError) (*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 session was created, // and not on the last network activity go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() Consistently(sess.Context().Done()).ShouldNot(BeClosed()) // make the go routine return expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("closes the session due to the idle timeout before handshake", func() { sess.config.HandshakeIdleTimeout = 0 packer.EXPECT().PackCoalescedPacket().AnyTimes() sessionRunner.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{}) sess.handshakeComplete = false go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1) err := sess.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 session due to the idle timeout after handshake", func() { packer.EXPECT().PackCoalescedPacket().AnyTimes() gomock.InOrder( sessionRunner.EXPECT().Retire(clientDestConnID), sessionRunner.EXPECT().Remove(gomock.Any()), ) cryptoSetup.EXPECT().Close() gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()).Do(func(e error) { Expect(e).To(MatchError(&IdleTimeoutError{})) }), tracer.EXPECT().Close(), ) sess.idleTimeout = 0 done := make(chan struct{}) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) cryptoSetup.EXPECT().GetSessionTicket().MaxTimes(1) cryptoSetup.EXPECT().SetHandshakeConfirmed().MaxTimes(1) close(sess.handshakeCompleteChan) err := sess.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() { sess.lastPacketReceivedTime = time.Now().Add(-time.Hour) sess.firstAckElicitingPacketAfterIdleSentTime = time.Now().Add(-time.Second) sess.idleTimeout = 30 * time.Second go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() Consistently(sess.Context().Done()).ShouldNot(BeClosed()) // make the go routine return packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) }) It("stores up to MaxSessionUnprocessedPackets packets", func() { done := make(chan struct{}) tracer.EXPECT().DroppedPacket(logging.PacketTypeNotDetermined, logging.ByteCount(6), logging.PacketDropDOSPrevention).Do(func(logging.PacketType, logging.ByteCount, logging.PacketDropReason) { close(done) }) // Nothing here should block for i := protocol.PacketNumber(0); i < protocol.MaxSessionUnprocessedPackets+1; i++ { sess.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 := sess.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 := sess.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 := sess.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 := sess.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 := sess.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 := sess.AcceptUniStream(ctx) Expect(err).ToNot(HaveOccurred()) Expect(str).To(Equal(mstr)) }) }) It("returns the local address", func() { Expect(sess.LocalAddr()).To(Equal(localAddr)) }) It("returns the remote address", func() { Expect(sess.RemoteAddr()).To(Equal(remoteAddr)) }) }) var _ = Describe("Client Session", func() { var ( sess *session sessionRunner *MockSessionRunner packer *MockPacker mconn *MockSendConn cryptoSetup *mocks.MockCryptoSetup tracer *mocklogging.MockConnectionTracer tlsConf *tls.Config quicConf *Config ) srcConnID := protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8} destConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1} getPacket := func(hdr *wire.ExtendedHeader, data []byte) *receivedPacket { buf := &bytes.Buffer{} Expect(hdr.Write(buf, sess.version)).To(Succeed()) return &receivedPacket{ data: append(buf.Bytes(), data...), buffer: getPacketBuffer(), } } expectReplaceWithClosed := func() { sessionRunner.EXPECT().ReplaceWithClosed(srcConnID, gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { s.shutdown() Eventually(areClosedSessionsRunning).Should(BeFalse()) }) } BeforeEach(func() { quicConf = populateClientConfig(&Config{}, true) tlsConf = nil }) JustBeforeEach(func() { Eventually(areSessionsRunning).Should(BeFalse()) mconn = NewMockSendConn(mockCtrl) mconn.EXPECT().RemoteAddr().Return(&net.UDPAddr{}).AnyTimes() mconn.EXPECT().LocalAddr().Return(&net.UDPAddr{}).AnyTimes() if tlsConf == nil { tlsConf = &tls.Config{} } sessionRunner = NewMockSessionRunner(mockCtrl) 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()) sess = newClientSession( mconn, sessionRunner, destConnID, protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, quicConf, tlsConf, 42, // initial packet number false, false, tracer, 1234, utils.DefaultLogger, protocol.VersionTLS, ).(*session) packer = NewMockPacker(mockCtrl) sess.packer = packer cryptoSetup = mocks.NewMockCryptoSetup(mockCtrl) sess.cryptoStreamHandler = cryptoSetup }) It("changes the connection ID when receiving the first packet from the server", func() { unpacker := NewMockUnpacker(mockCtrl) unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(hdr *wire.Header, _ time.Time, data []byte) (*unpackedPacket, error) { return &unpackedPacket{ encryptionLevel: protocol.Encryption1RTT, hdr: &wire.ExtendedHeader{Header: *hdr}, data: []byte{0}, // one PADDING frame }, nil }) sess.unpacker = unpacker go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) sess.run() }() newConnID := protocol.ConnectionID{1, 3, 3, 7, 1, 3, 3, 7} p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, SrcConnectionID: newConnID, DestConnectionID: srcConnID, Length: 2 + 6, Version: sess.version, }, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar")) tracer.EXPECT().ReceivedPacket(gomock.Any(), p.Size(), []logging.Frame{}) Expect(sess.handlePacketImpl(p)).To(BeTrue()) // make sure the go routine returns packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil) expectReplaceWithClosed() cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) tracer.EXPECT().ClosedConnection(gomock.Any()) tracer.EXPECT().Close() sess.shutdown() Eventually(sess.Context().Done()).Should(BeClosed()) }) It("continues accepting Long Header packets after using a new connection ID", func() { unpacker := NewMockUnpacker(mockCtrl) sess.unpacker = unpacker sessionRunner.EXPECT().AddResetToken(gomock.Any(), gomock.Any()) sess.connIDManager.SetHandshakeComplete() sess.handleNewConnectionIDFrame(&wire.NewConnectionIDFrame{ SequenceNumber: 1, ConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5}, }) Expect(sess.connIDManager.Get()).To(Equal(protocol.ConnectionID{1, 2, 3, 4, 5})) // now receive a packet with the original source connection ID unpacker.EXPECT().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(func(hdr *wire.Header, _ time.Time, _ []byte) (*unpackedPacket, error) { return &unpackedPacket{ hdr: &wire.ExtendedHeader{Header: *hdr}, data: []byte{0}, encryptionLevel: protocol.EncryptionHandshake, }, nil }) hdr := &wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeHandshake, DestConnectionID: srcConnID, SrcConnectionID: destConnID, } tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()) Expect(sess.handleSinglePacket(&receivedPacket{buffer: getPacketBuffer()}, hdr)).To(BeTrue()) }) It("handles HANDSHAKE_DONE frames", func() { sess.peerParams = &wire.TransportParameters{} sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sess.sentPacketHandler = sph sph.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().SetHandshakeConfirmed() Expect(sess.handleHandshakeDoneFrame()).To(Succeed()) }) It("interprets an ACK for 1-RTT packets as confirmation of the handshake", func() { sess.peerParams = &wire.TransportParameters{} sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sess.sentPacketHandler = sph ack := &wire.AckFrame{AckRanges: []wire.AckRange{{Smallest: 1, Largest: 3}}} sph.EXPECT().ReceivedAck(ack, protocol.Encryption1RTT, gomock.Any()).Return(true, nil) sph.EXPECT().SetHandshakeConfirmed() cryptoSetup.EXPECT().SetLargest1RTTAcked(protocol.PacketNumber(3)) cryptoSetup.EXPECT().SetHandshakeConfirmed() Expect(sess.handleAckFrame(ack, protocol.Encryption1RTT)).To(Succeed()) }) Context("handling tokens", func() { var mockTokenStore *MockTokenStore BeforeEach(func() { mockTokenStore = NewMockTokenStore(mockCtrl) tlsConf = &tls.Config{ServerName: "server"} quicConf.TokenStore = mockTokenStore mockTokenStore.EXPECT().Pop(gomock.Any()) quicConf.TokenStore = mockTokenStore }) It("handles NEW_TOKEN frames", func() { mockTokenStore.EXPECT().Put("server", &ClientToken{data: []byte("foobar")}) Expect(sess.handleNewTokenFrame(&wire.NewTokenFrame{Token: []byte("foobar")})).To(Succeed()) }) }) Context("handling Version Negotiation", func() { getVNP := func(versions ...protocol.VersionNumber) *receivedPacket { b, err := wire.ComposeVersionNegotiation(srcConnID, destConnID, versions) Expect(err).ToNot(HaveOccurred()) return &receivedPacket{ data: b, buffer: getPacketBuffer(), } } It("closes and returns the right error", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sess.sentPacketHandler = sph sph.EXPECT().ReceivedBytes(gomock.Any()) sph.EXPECT().PeekPacketNumber(protocol.EncryptionInitial).Return(protocol.PacketNumber(128), protocol.PacketNumberLen4) sess.config.Versions = []protocol.VersionNumber{1234, 4321} errChan := make(chan error, 1) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) errChan <- sess.run() }() sessionRunner.EXPECT().Remove(srcConnID) tracer.EXPECT().ReceivedVersionNegotiationPacket(gomock.Any(), gomock.Any()).Do(func(hdr *wire.Header, versions []logging.VersionNumber) { Expect(hdr.Version).To(BeZero()) Expect(versions).To(And( ContainElement(protocol.VersionNumber(4321)), ContainElement(protocol.VersionNumber(1337)), )) }) cryptoSetup.EXPECT().Close() Expect(sess.handlePacketImpl(getVNP(4321, 1337))).To(BeFalse()) 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.VersionNumber(4321))) Expect(recreateErr.nextPacketNumber).To(Equal(protocol.PacketNumber(128))) }) It("it closes when no matching version is found", func() { errChan := make(chan error, 1) go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) errChan <- sess.run() }() sessionRunner.EXPECT().Remove(srcConnID).MaxTimes(1) gomock.InOrder( tracer.EXPECT().ReceivedVersionNegotiationPacket(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.VersionNumber(12345678))) }), tracer.EXPECT().Close(), ) cryptoSetup.EXPECT().Close() Expect(sess.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(sess.version) tracer.EXPECT().DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropUnexpectedVersion) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) It("ignores unparseable Version Negotiation packets", func() { p := getVNP(sess.version) p.data = p.data[:len(p.data)-2] tracer.EXPECT().DroppedPacket(logging.PacketTypeVersionNegotiation, p.Size(), logging.PacketDropHeaderParseError) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) }) Context("handling Retry", func() { origDestConnID := protocol.ConnectionID{8, 7, 6, 5, 4, 3, 2, 1} var retryHdr *wire.ExtendedHeader JustBeforeEach(func() { retryHdr = &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeRetry, SrcConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, DestConnectionID: protocol.ConnectionID{1, 2, 3, 4, 5, 6, 7, 8}, Token: []byte("foobar"), Version: sess.version, }, } }) getRetryTag := func(hdr *wire.ExtendedHeader) []byte { buf := &bytes.Buffer{} hdr.Write(buf, sess.version) return handshake.GetRetryIntegrityTag(buf.Bytes(), origDestConnID, hdr.Version)[:] } It("handles Retry packets", func() { sph := mockackhandler.NewMockSentPacketHandler(mockCtrl) sess.sentPacketHandler = sph sph.EXPECT().ResetForRetry() sph.EXPECT().ReceivedBytes(gomock.Any()) cryptoSetup.EXPECT().ChangeConnectionID(protocol.ConnectionID{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)) }) Expect(sess.handlePacketImpl(getPacket(retryHdr, getRetryTag(retryHdr)))).To(BeTrue()) }) It("ignores Retry packets after receiving a regular packet", func() { sess.receivedFirstPacket = true p := getPacket(retryHdr, getRetryTag(retryHdr)) tracer.EXPECT().DroppedPacket(logging.PacketTypeRetry, p.Size(), logging.PacketDropUnexpectedPacket) Expect(sess.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, p.Size(), logging.PacketDropUnexpectedPacket) Expect(sess.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, p.Size(), logging.PacketDropPayloadDecryptError) Expect(sess.handlePacketImpl(p)).To(BeFalse()) }) }) Context("transport parameters", func() { var ( closed bool errChan chan error ) JustBeforeEach(func() { errChan = make(chan error, 1) closed = false go func() { defer GinkgoRecover() cryptoSetup.EXPECT().RunHandshake().MaxTimes(1) errChan <- sess.run() close(errChan) }() }) expectClose := func(applicationClose bool) { if !closed { sessionRunner.EXPECT().ReplaceWithClosed(gomock.Any(), gomock.Any()).Do(func(_ protocol.ConnectionID, s packetHandler) { Expect(s).To(BeAssignableToTypeOf(&closedLocalSession{})) s.shutdown() }) if applicationClose { packer.EXPECT().PackApplicationClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil).MaxTimes(1) } else { packer.EXPECT().PackConnectionClose(gomock.Any()).Return(&coalescedPacket{buffer: getPacketBuffer()}, nil).MaxTimes(1) } cryptoSetup.EXPECT().Close() mconn.EXPECT().Write(gomock.Any()) gomock.InOrder( tracer.EXPECT().ClosedConnection(gomock.Any()), tracer.EXPECT().Close(), ) } closed = true } AfterEach(func() { sess.shutdown() Eventually(sess.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: net.IPv4(127, 0, 0, 1), IPv6: net.IP{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, ConnectionID: protocol.ConnectionID{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().HandleTransportParameters(gomock.Any()) packer.EXPECT().PackCoalescedPacket().MaxTimes(1) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) sess.handleHandshakeComplete() // make sure the connection ID is not retired cf, _ := sess.framer.AppendControlFrames(nil, protocol.MaxByteCount) Expect(cf).To(BeEmpty()) sessionRunner.EXPECT().AddResetToken(protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}, sess) Expect(sess.connIDManager.Get()).To(Equal(protocol.ConnectionID{1, 2, 3, 4})) // shut down sessionRunner.EXPECT().RemoveResetToken(protocol.StatelessResetToken{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}) expectClose(true) }) It("uses the minimum of the peers' idle timeouts", func() { sess.config.MaxIdleTimeout = 19 * time.Second params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, MaxIdleTimeout: 18 * time.Second, } packer.EXPECT().HandleTransportParameters(gomock.Any()) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) sess.handleHandshakeComplete() Expect(sess.idleTimeout).To(Equal(18 * time.Second)) expectClose(true) }) It("errors if the transport parameters contain a wrong initial_source_connection_id", func() { sess.handshakeDestConnID = protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: protocol.ConnectionID{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) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) 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() { sess.retrySrcConnID = &protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} 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) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) 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() { sess.retrySrcConnID = &protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, RetrySourceConnectionID: &protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) 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() { params := &wire.TransportParameters{ OriginalDestinationConnectionID: destConnID, InitialSourceConnectionID: destConnID, RetrySourceConnectionID: &protocol.ConnectionID{0xde, 0xad, 0xc0, 0xde}, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) 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() { sess.origDestConnID = protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} params := &wire.TransportParameters{ OriginalDestinationConnectionID: protocol.ConnectionID{0xde, 0xca, 0xfb, 0xad}, InitialSourceConnectionID: sess.handshakeDestConnID, StatelessResetToken: &protocol.StatelessResetToken{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, } expectClose(false) tracer.EXPECT().ReceivedTransportParameters(params) sess.handleTransportParameters(params) Eventually(errChan).Should(Receive(MatchError(&qerr.TransportError{ ErrorCode: qerr.TransportParameterError, ErrorMessage: "expected original_destination_connection_id to equal deadbeef, is decafbad", }))) }) }) Context("handling potentially injected packets", func() { var unpacker *MockUnpacker getPacket := func(extHdr *wire.ExtendedHeader, data []byte) *receivedPacket { buf := &bytes.Buffer{} Expect(extHdr.Write(buf, sess.version)).To(Succeed()) return &receivedPacket{ data: append(buf.Bytes(), 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) sess.unpacker = unpacker hdr1 := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: srcConnID, Length: 1, Version: sess.version, }, PacketNumberLen: protocol.PacketNumberLen1, PacketNumber: 1, } hdr2 := &wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketTypeInitial, DestConnectionID: destConnID, SrcConnectionID: protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef}, Length: 1, Version: sess.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().Unpack(gomock.Any(), gomock.Any(), gomock.Any()).Return(&unpackedPacket{ encryptionLevel: protocol.EncryptionInitial, hdr: hdr1, data: []byte{0}, // one PADDING frame }, nil) tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()) Expect(sess.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(), gomock.Any(), gomock.Any()) Expect(sess.handlePacketImpl(getPacket(hdr2, nil))).To(BeFalse()) }) It("ignores 0-RTT packets", func() { p := getPacket(&wire.ExtendedHeader{ Header: wire.Header{ IsLongHeader: true, Type: protocol.PacketType0RTT, DestConnectionID: srcConnID, Length: 2 + 6, Version: sess.version, }, PacketNumber: 0x42, PacketNumberLen: protocol.PacketNumberLen2, }, []byte("foobar")) tracer.EXPECT().DroppedPacket(logging.PacketType0RTT, p.Size(), gomock.Any()) Expect(sess.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, sess.version, destConnID, []wire.Frame{ack}) tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()) Expect(sess.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, sess.version, destConnID, []wire.Frame{connCloseFrame}) tracer.EXPECT().ReceivedPacket(gomock.Any(), gomock.Any(), gomock.Any()) Expect(sess.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) sess.sentPacketHandler = sph sph.EXPECT().ReceivedBytes(gomock.Any()).Times(2) sph.EXPECT().ResetForRetry() newSrcConnID := protocol.ConnectionID{0xde, 0xad, 0xbe, 0xef} cryptoSetup.EXPECT().ChangeConnectionID(newSrcConnID) packer.EXPECT().SetToken([]byte("foobar")) tracer.EXPECT().ReceivedRetry(gomock.Any()) sess.handlePacketImpl(wrapPacket(testutils.ComposeRetryPacket(newSrcConnID, destConnID, destConnID, []byte("foobar"), sess.version))) initialPacket := testutils.ComposeInitialPacket(sess.connIDManager.Get(), srcConnID, sess.version, sess.connIDManager.Get(), nil) tracer.EXPECT().DroppedPacket(gomock.Any(), gomock.Any(), gomock.Any()) Expect(sess.handlePacketImpl(wrapPacket(initialPacket))).To(BeFalse()) }) }) }) quic-go-0.25.0/stream.go000066400000000000000000000105661417145451600150040ustar00rootroot00000000000000package quic import ( "net" "os" "sync" "time" "github.com/lucas-clemente/quic-go/internal/ackhandler" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) type deadlineError struct{} func (deadlineError) Error() string { return "deadline exceeded" } func (deadlineError) Temporary() bool { return true } func (deadlineError) Timeout() bool { return true } func (deadlineError) Unwrap() error { return os.ErrDeadlineExceeded } var errDeadline net.Error = &deadlineError{} // The streamSender is notified by the stream about various events. type streamSender interface { queueControlFrame(wire.Frame) onHasStreamData(protocol.StreamID) // must be called without holding the mutex that is acquired by closeForShutdown onStreamCompleted(protocol.StreamID) } // Each of the both stream halves gets its own uniStreamSender. // This is necessary in order to keep track when both halves have been completed. type uniStreamSender struct { streamSender onStreamCompletedImpl func() } func (s *uniStreamSender) queueControlFrame(f wire.Frame) { s.streamSender.queueControlFrame(f) } func (s *uniStreamSender) onHasStreamData(id protocol.StreamID) { s.streamSender.onHasStreamData(id) } func (s *uniStreamSender) onStreamCompleted(protocol.StreamID) { s.onStreamCompletedImpl() } var _ streamSender = &uniStreamSender{} type streamI interface { Stream closeForShutdown(error) // for receiving handleStreamFrame(*wire.StreamFrame) error handleResetStreamFrame(*wire.ResetStreamFrame) error getWindowUpdate() protocol.ByteCount // for sending hasData() bool handleStopSendingFrame(*wire.StopSendingFrame) popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool) updateSendWindow(protocol.ByteCount) } var ( _ receiveStreamI = (streamI)(nil) _ sendStreamI = (streamI)(nil) ) // A Stream assembles the data from StreamFrames and provides a super-convenient Read-Interface // // Read() and Write() may be called concurrently, but multiple calls to Read() or Write() individually must be synchronized manually. type stream struct { receiveStream sendStream completedMutex sync.Mutex sender streamSender receiveStreamCompleted bool sendStreamCompleted bool version protocol.VersionNumber } var _ Stream = &stream{} // newStream creates a new Stream func newStream(streamID protocol.StreamID, sender streamSender, flowController flowcontrol.StreamFlowController, version protocol.VersionNumber, ) *stream { s := &stream{sender: sender, version: version} senderForSendStream := &uniStreamSender{ streamSender: sender, onStreamCompletedImpl: func() { s.completedMutex.Lock() s.sendStreamCompleted = true s.checkIfCompleted() s.completedMutex.Unlock() }, } s.sendStream = *newSendStream(streamID, senderForSendStream, flowController, version) senderForReceiveStream := &uniStreamSender{ streamSender: sender, onStreamCompletedImpl: func() { s.completedMutex.Lock() s.receiveStreamCompleted = true s.checkIfCompleted() s.completedMutex.Unlock() }, } s.receiveStream = *newReceiveStream(streamID, senderForReceiveStream, flowController, version) return s } // need to define StreamID() here, since both receiveStream and readStream have a StreamID() func (s *stream) StreamID() protocol.StreamID { // the result is same for receiveStream and sendStream return s.sendStream.StreamID() } func (s *stream) Close() error { return s.sendStream.Close() } func (s *stream) SetDeadline(t time.Time) error { _ = s.SetReadDeadline(t) // SetReadDeadline never errors _ = s.SetWriteDeadline(t) // SetWriteDeadline never errors return nil } // CloseForShutdown closes a stream abruptly. // It makes Read and Write unblock (and return the error) immediately. // The peer will NOT be informed about this: the stream is closed without sending a FIN or RST. func (s *stream) closeForShutdown(err error) { s.sendStream.closeForShutdown(err) s.receiveStream.closeForShutdown(err) } // checkIfCompleted is called from the uniStreamSender, when one of the stream halves is completed. // It makes sure that the onStreamCompleted callback is only called if both receive and send side have completed. func (s *stream) checkIfCompleted() { if s.sendStreamCompleted && s.receiveStreamCompleted { s.sender.onStreamCompleted(s.StreamID()) } } quic-go-0.25.0/stream_test.go000066400000000000000000000063721417145451600160430ustar00rootroot00000000000000package quic import ( "errors" "io" "os" "strconv" "time" "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gbytes" ) // in the tests for the stream deadlines we set a deadline // and wait to make an assertion when Read / Write was unblocked // on the CIs, the timing is a lot less precise, so scale every duration by this factor 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("Stream", func() { const streamID protocol.StreamID = 1337 var ( str *stream strWithTimeout io.ReadWriter // str wrapped with gbytes.Timeout{Reader,Writer} mockFC *mocks.MockStreamFlowController mockSender *MockStreamSender ) BeforeEach(func() { mockSender = NewMockStreamSender(mockCtrl) mockFC = mocks.NewMockStreamFlowController(mockCtrl) str = newStream(streamID, mockSender, mockFC, protocol.VersionWhatever) timeout := scaleDuration(250 * time.Millisecond) strWithTimeout = struct { io.Reader io.Writer }{ gbytes.TimeoutReader(str, timeout), gbytes.TimeoutWriter(str, timeout), } }) It("gets stream id", func() { Expect(str.StreamID()).To(Equal(protocol.StreamID(1337))) }) Context("deadlines", func() { It("sets a write deadline, when SetDeadline is called", func() { str.SetDeadline(time.Now().Add(-time.Second)) n, err := strWithTimeout.Write([]byte("foobar")) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) }) It("sets a read deadline, when SetDeadline is called", func() { mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false).AnyTimes() f := &wire.StreamFrame{Data: []byte("foobar")} err := str.handleStreamFrame(f) Expect(err).ToNot(HaveOccurred()) str.SetDeadline(time.Now().Add(-time.Second)) b := make([]byte, 6) n, err := strWithTimeout.Read(b) Expect(err).To(MatchError(errDeadline)) Expect(n).To(BeZero()) }) }) Context("completing", func() { It("is not completed when only the receive side is completed", func() { // don't EXPECT a call to mockSender.onStreamCompleted() str.receiveStream.sender.onStreamCompleted(streamID) }) It("is not completed when only the send side is completed", func() { // don't EXPECT a call to mockSender.onStreamCompleted() str.sendStream.sender.onStreamCompleted(streamID) }) It("is completed when both sides are completed", func() { mockSender.EXPECT().onStreamCompleted(streamID) str.sendStream.sender.onStreamCompleted(streamID) str.receiveStream.sender.onStreamCompleted(streamID) }) }) }) var _ = Describe("Deadline Error", func() { It("is a net.Error that wraps os.ErrDeadlineError", func() { err := deadlineError{} Expect(err.Temporary()).To(BeTrue()) Expect(err.Timeout()).To(BeTrue()) Expect(errors.Is(err, os.ErrDeadlineExceeded)).To(BeTrue()) Expect(errors.Unwrap(err)).To(Equal(os.ErrDeadlineExceeded)) }) }) quic-go-0.25.0/streams_map.go000066400000000000000000000227271417145451600160260ustar00rootroot00000000000000package quic import ( "context" "errors" "fmt" "net" "sync" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" ) type streamError struct { message string nums []protocol.StreamNum } func (e streamError) Error() string { return e.message } func convertStreamError(err error, stype protocol.StreamType, pers protocol.Perspective) error { strError, ok := err.(streamError) if !ok { return err } ids := make([]interface{}, len(strError.nums)) for i, num := range strError.nums { ids[i] = num.StreamID(stype, pers) } return fmt.Errorf(strError.Error(), ids...) } type streamOpenErr struct{ error } var _ net.Error = &streamOpenErr{} func (e streamOpenErr) Temporary() bool { return e.error == errTooManyOpenStreams } func (streamOpenErr) Timeout() bool { return false } // errTooManyOpenStreams is used internally by the outgoing streams maps. var errTooManyOpenStreams = errors.New("too many open streams") type streamsMap struct { perspective protocol.Perspective version protocol.VersionNumber maxIncomingBidiStreams uint64 maxIncomingUniStreams uint64 sender streamSender newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController mutex sync.Mutex outgoingBidiStreams *outgoingBidiStreamsMap outgoingUniStreams *outgoingUniStreamsMap incomingBidiStreams *incomingBidiStreamsMap incomingUniStreams *incomingUniStreamsMap reset bool } var _ streamManager = &streamsMap{} func newStreamsMap( sender streamSender, newFlowController func(protocol.StreamID) flowcontrol.StreamFlowController, maxIncomingBidiStreams uint64, maxIncomingUniStreams uint64, perspective protocol.Perspective, version protocol.VersionNumber, ) streamManager { m := &streamsMap{ perspective: perspective, newFlowController: newFlowController, maxIncomingBidiStreams: maxIncomingBidiStreams, maxIncomingUniStreams: maxIncomingUniStreams, sender: sender, version: version, } m.initMaps() return m } func (m *streamsMap) initMaps() { m.outgoingBidiStreams = newOutgoingBidiStreamsMap( func(num protocol.StreamNum) streamI { id := num.StreamID(protocol.StreamTypeBidi, m.perspective) return newStream(id, m.sender, m.newFlowController(id), m.version) }, m.sender.queueControlFrame, ) m.incomingBidiStreams = newIncomingBidiStreamsMap( func(num protocol.StreamNum) streamI { id := num.StreamID(protocol.StreamTypeBidi, m.perspective.Opposite()) return newStream(id, m.sender, m.newFlowController(id), m.version) }, m.maxIncomingBidiStreams, m.sender.queueControlFrame, ) m.outgoingUniStreams = newOutgoingUniStreamsMap( func(num protocol.StreamNum) sendStreamI { id := num.StreamID(protocol.StreamTypeUni, m.perspective) return newSendStream(id, m.sender, m.newFlowController(id), m.version) }, m.sender.queueControlFrame, ) m.incomingUniStreams = newIncomingUniStreamsMap( func(num protocol.StreamNum) receiveStreamI { id := num.StreamID(protocol.StreamTypeUni, m.perspective.Opposite()) return newReceiveStream(id, m.sender, m.newFlowController(id), m.version) }, m.maxIncomingUniStreams, m.sender.queueControlFrame, ) } func (m *streamsMap) OpenStream() (Stream, error) { m.mutex.Lock() reset := m.reset mm := m.outgoingBidiStreams m.mutex.Unlock() if reset { return nil, Err0RTTRejected } str, err := mm.OpenStream() return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective) } func (m *streamsMap) OpenStreamSync(ctx context.Context) (Stream, error) { m.mutex.Lock() reset := m.reset mm := m.outgoingBidiStreams m.mutex.Unlock() if reset { return nil, Err0RTTRejected } str, err := mm.OpenStreamSync(ctx) return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective) } func (m *streamsMap) OpenUniStream() (SendStream, error) { m.mutex.Lock() reset := m.reset mm := m.outgoingUniStreams m.mutex.Unlock() if reset { return nil, Err0RTTRejected } str, err := mm.OpenStream() return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective) } func (m *streamsMap) OpenUniStreamSync(ctx context.Context) (SendStream, error) { m.mutex.Lock() reset := m.reset mm := m.outgoingUniStreams m.mutex.Unlock() if reset { return nil, Err0RTTRejected } str, err := mm.OpenStreamSync(ctx) return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective) } func (m *streamsMap) AcceptStream(ctx context.Context) (Stream, error) { m.mutex.Lock() reset := m.reset mm := m.incomingBidiStreams m.mutex.Unlock() if reset { return nil, Err0RTTRejected } str, err := mm.AcceptStream(ctx) return str, convertStreamError(err, protocol.StreamTypeBidi, m.perspective.Opposite()) } func (m *streamsMap) AcceptUniStream(ctx context.Context) (ReceiveStream, error) { m.mutex.Lock() reset := m.reset mm := m.incomingUniStreams m.mutex.Unlock() if reset { return nil, Err0RTTRejected } str, err := mm.AcceptStream(ctx) return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective.Opposite()) } func (m *streamsMap) DeleteStream(id protocol.StreamID) error { num := id.StreamNum() switch id.Type() { case protocol.StreamTypeUni: if id.InitiatedBy() == m.perspective { return convertStreamError(m.outgoingUniStreams.DeleteStream(num), protocol.StreamTypeUni, m.perspective) } return convertStreamError(m.incomingUniStreams.DeleteStream(num), protocol.StreamTypeUni, m.perspective.Opposite()) case protocol.StreamTypeBidi: if id.InitiatedBy() == m.perspective { return convertStreamError(m.outgoingBidiStreams.DeleteStream(num), protocol.StreamTypeBidi, m.perspective) } return convertStreamError(m.incomingBidiStreams.DeleteStream(num), protocol.StreamTypeBidi, m.perspective.Opposite()) } panic("") } func (m *streamsMap) GetOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) { str, err := m.getOrOpenReceiveStream(id) if err != nil { return nil, &qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: err.Error(), } } return str, nil } func (m *streamsMap) getOrOpenReceiveStream(id protocol.StreamID) (receiveStreamI, error) { num := id.StreamNum() switch id.Type() { case protocol.StreamTypeUni: if id.InitiatedBy() == m.perspective { // an outgoing unidirectional stream is a send stream, not a receive stream return nil, fmt.Errorf("peer attempted to open receive stream %d", id) } str, err := m.incomingUniStreams.GetOrOpenStream(num) return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective) case protocol.StreamTypeBidi: var str receiveStreamI var err error if id.InitiatedBy() == m.perspective { str, err = m.outgoingBidiStreams.GetStream(num) } else { str, err = m.incomingBidiStreams.GetOrOpenStream(num) } return str, convertStreamError(err, protocol.StreamTypeBidi, id.InitiatedBy()) } panic("") } func (m *streamsMap) GetOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) { str, err := m.getOrOpenSendStream(id) if err != nil { return nil, &qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: err.Error(), } } return str, nil } func (m *streamsMap) getOrOpenSendStream(id protocol.StreamID) (sendStreamI, error) { num := id.StreamNum() switch id.Type() { case protocol.StreamTypeUni: if id.InitiatedBy() == m.perspective { str, err := m.outgoingUniStreams.GetStream(num) return str, convertStreamError(err, protocol.StreamTypeUni, m.perspective) } // an incoming unidirectional stream is a receive stream, not a send stream return nil, fmt.Errorf("peer attempted to open send stream %d", id) case protocol.StreamTypeBidi: var str sendStreamI var err error if id.InitiatedBy() == m.perspective { str, err = m.outgoingBidiStreams.GetStream(num) } else { str, err = m.incomingBidiStreams.GetOrOpenStream(num) } return str, convertStreamError(err, protocol.StreamTypeBidi, id.InitiatedBy()) } panic("") } func (m *streamsMap) HandleMaxStreamsFrame(f *wire.MaxStreamsFrame) { switch f.Type { case protocol.StreamTypeUni: m.outgoingUniStreams.SetMaxStream(f.MaxStreamNum) case protocol.StreamTypeBidi: m.outgoingBidiStreams.SetMaxStream(f.MaxStreamNum) } } func (m *streamsMap) UpdateLimits(p *wire.TransportParameters) { m.outgoingBidiStreams.UpdateSendWindow(p.InitialMaxStreamDataBidiRemote) m.outgoingBidiStreams.SetMaxStream(p.MaxBidiStreamNum) m.outgoingUniStreams.UpdateSendWindow(p.InitialMaxStreamDataUni) m.outgoingUniStreams.SetMaxStream(p.MaxUniStreamNum) } func (m *streamsMap) CloseWithError(err error) { m.outgoingBidiStreams.CloseWithError(err) m.outgoingUniStreams.CloseWithError(err) m.incomingBidiStreams.CloseWithError(err) m.incomingUniStreams.CloseWithError(err) } // ResetFor0RTT resets is used when 0-RTT is rejected. In that case, the streams maps are // 1. closed with an Err0RTTRejected, making calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream return that error. // 2. reset to their initial state, such that we can immediately process new incoming stream data. // Afterwards, calls to Open{Uni}Stream{Sync} / Accept{Uni}Stream will continue to return the error, // until UseResetMaps() has been called. func (m *streamsMap) ResetFor0RTT() { m.mutex.Lock() defer m.mutex.Unlock() m.reset = true m.CloseWithError(Err0RTTRejected) m.initMaps() } func (m *streamsMap) UseResetMaps() { m.mutex.Lock() m.reset = false m.mutex.Unlock() } quic-go-0.25.0/streams_map_generic_helper.go000066400000000000000000000007711417145451600210540ustar00rootroot00000000000000package quic import ( "github.com/cheekybits/genny/generic" "github.com/lucas-clemente/quic-go/internal/protocol" ) // In the auto-generated streams maps, we need to be able to close the streams. // Therefore, extend the generic.Type with the stream close method. // This definition must be in a file that Genny doesn't process. type item interface { generic.Type updateSendWindow(protocol.ByteCount) closeForShutdown(error) } const streamTypeGeneric protocol.StreamType = protocol.StreamTypeUni quic-go-0.25.0/streams_map_incoming_bidi.go000066400000000000000000000126251417145451600206740ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package quic import ( "context" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) // When a stream is deleted before it was accepted, we can't delete it from the map immediately. // We need to wait until the application accepts it, and delete it then. type streamIEntry struct { stream streamI shouldDelete bool } type incomingBidiStreamsMap struct { mutex sync.RWMutex newStreamChan chan struct{} streams map[protocol.StreamNum]streamIEntry nextStreamToAccept protocol.StreamNum // the next stream that will be returned by AcceptStream() nextStreamToOpen protocol.StreamNum // the highest stream that the peer opened maxStream protocol.StreamNum // the highest stream that the peer is allowed to open maxNumStreams uint64 // maximum number of streams newStream func(protocol.StreamNum) streamI queueMaxStreamID func(*wire.MaxStreamsFrame) closeErr error } func newIncomingBidiStreamsMap( newStream func(protocol.StreamNum) streamI, maxStreams uint64, queueControlFrame func(wire.Frame), ) *incomingBidiStreamsMap { return &incomingBidiStreamsMap{ newStreamChan: make(chan struct{}, 1), streams: make(map[protocol.StreamNum]streamIEntry), maxStream: protocol.StreamNum(maxStreams), maxNumStreams: maxStreams, newStream: newStream, nextStreamToOpen: 1, nextStreamToAccept: 1, queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, } } func (m *incomingBidiStreamsMap) AcceptStream(ctx context.Context) (streamI, error) { // drain the newStreamChan, so we don't check the map twice if the stream doesn't exist select { case <-m.newStreamChan: default: } m.mutex.Lock() var num protocol.StreamNum var entry streamIEntry for { num = m.nextStreamToAccept if m.closeErr != nil { m.mutex.Unlock() return nil, m.closeErr } var ok bool entry, ok = m.streams[num] if ok { break } m.mutex.Unlock() select { case <-ctx.Done(): return nil, ctx.Err() case <-m.newStreamChan: } m.mutex.Lock() } m.nextStreamToAccept++ // If this stream was completed before being accepted, we can delete it now. if entry.shouldDelete { if err := m.deleteStream(num); err != nil { m.mutex.Unlock() return nil, err } } m.mutex.Unlock() return entry.stream, nil } func (m *incomingBidiStreamsMap) GetOrOpenStream(num protocol.StreamNum) (streamI, error) { m.mutex.RLock() if num > m.maxStream { m.mutex.RUnlock() return nil, streamError{ message: "peer tried to open stream %d (current limit: %d)", nums: []protocol.StreamNum{num, m.maxStream}, } } // if the num is smaller than the highest we accepted // * this stream exists in the map, and we can return it, or // * this stream was already closed, then we can return the nil if num < m.nextStreamToOpen { var s streamI // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. if entry, ok := m.streams[num]; ok && !entry.shouldDelete { s = entry.stream } m.mutex.RUnlock() return s, nil } m.mutex.RUnlock() m.mutex.Lock() // no need to check the two error conditions from above again // * maxStream can only increase, so if the id was valid before, it definitely is valid now // * highestStream is only modified by this function for newNum := m.nextStreamToOpen; newNum <= num; newNum++ { m.streams[newNum] = streamIEntry{stream: m.newStream(newNum)} select { case m.newStreamChan <- struct{}{}: default: } } m.nextStreamToOpen = num + 1 entry := m.streams[num] m.mutex.Unlock() return entry.stream, nil } func (m *incomingBidiStreamsMap) DeleteStream(num protocol.StreamNum) error { m.mutex.Lock() defer m.mutex.Unlock() return m.deleteStream(num) } func (m *incomingBidiStreamsMap) deleteStream(num protocol.StreamNum) error { if _, ok := m.streams[num]; !ok { return streamError{ message: "tried to delete unknown incoming stream %d", nums: []protocol.StreamNum{num}, } } // Don't delete this stream yet, if it was not yet accepted. // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. if num >= m.nextStreamToAccept { entry, ok := m.streams[num] if ok && entry.shouldDelete { return streamError{ message: "tried to delete incoming stream %d multiple times", nums: []protocol.StreamNum{num}, } } entry.shouldDelete = true m.streams[num] = entry // can't assign to struct in map, so we need to reassign return nil } delete(m.streams, num) // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream if m.maxNumStreams > uint64(len(m.streams)) { maxStream := m.nextStreamToOpen + protocol.StreamNum(m.maxNumStreams-uint64(len(m.streams))) - 1 // Never send a value larger than protocol.MaxStreamCount. if maxStream <= protocol.MaxStreamCount { m.maxStream = maxStream m.queueMaxStreamID(&wire.MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: m.maxStream, }) } } return nil } func (m *incomingBidiStreamsMap) CloseWithError(err error) { m.mutex.Lock() m.closeErr = err for _, entry := range m.streams { entry.stream.closeForShutdown(err) } m.mutex.Unlock() close(m.newStreamChan) } quic-go-0.25.0/streams_map_incoming_generic.go000066400000000000000000000127021417145451600213750ustar00rootroot00000000000000package quic import ( "context" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) // When a stream is deleted before it was accepted, we can't delete it from the map immediately. // We need to wait until the application accepts it, and delete it then. type itemEntry struct { stream item shouldDelete bool } //go:generate genny -in $GOFILE -out streams_map_incoming_bidi.go gen "item=streamI Item=BidiStream streamTypeGeneric=protocol.StreamTypeBidi" //go:generate genny -in $GOFILE -out streams_map_incoming_uni.go gen "item=receiveStreamI Item=UniStream streamTypeGeneric=protocol.StreamTypeUni" type incomingItemsMap struct { mutex sync.RWMutex newStreamChan chan struct{} streams map[protocol.StreamNum]itemEntry nextStreamToAccept protocol.StreamNum // the next stream that will be returned by AcceptStream() nextStreamToOpen protocol.StreamNum // the highest stream that the peer opened maxStream protocol.StreamNum // the highest stream that the peer is allowed to open maxNumStreams uint64 // maximum number of streams newStream func(protocol.StreamNum) item queueMaxStreamID func(*wire.MaxStreamsFrame) closeErr error } func newIncomingItemsMap( newStream func(protocol.StreamNum) item, maxStreams uint64, queueControlFrame func(wire.Frame), ) *incomingItemsMap { return &incomingItemsMap{ newStreamChan: make(chan struct{}, 1), streams: make(map[protocol.StreamNum]itemEntry), maxStream: protocol.StreamNum(maxStreams), maxNumStreams: maxStreams, newStream: newStream, nextStreamToOpen: 1, nextStreamToAccept: 1, queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, } } func (m *incomingItemsMap) AcceptStream(ctx context.Context) (item, error) { // drain the newStreamChan, so we don't check the map twice if the stream doesn't exist select { case <-m.newStreamChan: default: } m.mutex.Lock() var num protocol.StreamNum var entry itemEntry for { num = m.nextStreamToAccept if m.closeErr != nil { m.mutex.Unlock() return nil, m.closeErr } var ok bool entry, ok = m.streams[num] if ok { break } m.mutex.Unlock() select { case <-ctx.Done(): return nil, ctx.Err() case <-m.newStreamChan: } m.mutex.Lock() } m.nextStreamToAccept++ // If this stream was completed before being accepted, we can delete it now. if entry.shouldDelete { if err := m.deleteStream(num); err != nil { m.mutex.Unlock() return nil, err } } m.mutex.Unlock() return entry.stream, nil } func (m *incomingItemsMap) GetOrOpenStream(num protocol.StreamNum) (item, error) { m.mutex.RLock() if num > m.maxStream { m.mutex.RUnlock() return nil, streamError{ message: "peer tried to open stream %d (current limit: %d)", nums: []protocol.StreamNum{num, m.maxStream}, } } // if the num is smaller than the highest we accepted // * this stream exists in the map, and we can return it, or // * this stream was already closed, then we can return the nil if num < m.nextStreamToOpen { var s item // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. if entry, ok := m.streams[num]; ok && !entry.shouldDelete { s = entry.stream } m.mutex.RUnlock() return s, nil } m.mutex.RUnlock() m.mutex.Lock() // no need to check the two error conditions from above again // * maxStream can only increase, so if the id was valid before, it definitely is valid now // * highestStream is only modified by this function for newNum := m.nextStreamToOpen; newNum <= num; newNum++ { m.streams[newNum] = itemEntry{stream: m.newStream(newNum)} select { case m.newStreamChan <- struct{}{}: default: } } m.nextStreamToOpen = num + 1 entry := m.streams[num] m.mutex.Unlock() return entry.stream, nil } func (m *incomingItemsMap) DeleteStream(num protocol.StreamNum) error { m.mutex.Lock() defer m.mutex.Unlock() return m.deleteStream(num) } func (m *incomingItemsMap) deleteStream(num protocol.StreamNum) error { if _, ok := m.streams[num]; !ok { return streamError{ message: "tried to delete unknown incoming stream %d", nums: []protocol.StreamNum{num}, } } // Don't delete this stream yet, if it was not yet accepted. // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. if num >= m.nextStreamToAccept { entry, ok := m.streams[num] if ok && entry.shouldDelete { return streamError{ message: "tried to delete incoming stream %d multiple times", nums: []protocol.StreamNum{num}, } } entry.shouldDelete = true m.streams[num] = entry // can't assign to struct in map, so we need to reassign return nil } delete(m.streams, num) // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream if m.maxNumStreams > uint64(len(m.streams)) { maxStream := m.nextStreamToOpen + protocol.StreamNum(m.maxNumStreams-uint64(len(m.streams))) - 1 // Never send a value larger than protocol.MaxStreamCount. if maxStream <= protocol.MaxStreamCount { m.maxStream = maxStream m.queueMaxStreamID(&wire.MaxStreamsFrame{ Type: streamTypeGeneric, MaxStreamNum: m.maxStream, }) } } return nil } func (m *incomingItemsMap) CloseWithError(err error) { m.mutex.Lock() m.closeErr = err for _, entry := range m.streams { entry.stream.closeForShutdown(err) } m.mutex.Unlock() close(m.newStreamChan) } quic-go-0.25.0/streams_map_incoming_generic_test.go000066400000000000000000000234361417145451600224420ustar00rootroot00000000000000package quic import ( "bytes" "context" "errors" "math/rand" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) type mockGenericStream struct { num protocol.StreamNum closed bool closeErr error sendWindow protocol.ByteCount } func (s *mockGenericStream) closeForShutdown(err error) { s.closed = true s.closeErr = err } func (s *mockGenericStream) updateSendWindow(limit protocol.ByteCount) { s.sendWindow = limit } var _ = Describe("Streams Map (incoming)", func() { var ( m *incomingItemsMap newItemCounter int mockSender *MockStreamSender maxNumStreams uint64 ) // check that the frame can be serialized and deserialized checkFrameSerialization := func(f wire.Frame) { b := &bytes.Buffer{} ExpectWithOffset(1, f.Write(b, protocol.VersionTLS)).To(Succeed()) frame, err := wire.NewFrameParser(false, protocol.VersionTLS).ParseNext(bytes.NewReader(b.Bytes()), protocol.Encryption1RTT) ExpectWithOffset(1, err).ToNot(HaveOccurred()) Expect(f).To(Equal(frame)) } BeforeEach(func() { maxNumStreams = 5 }) JustBeforeEach(func() { newItemCounter = 0 mockSender = NewMockStreamSender(mockCtrl) m = newIncomingItemsMap( func(num protocol.StreamNum) item { newItemCounter++ return &mockGenericStream{num: num} }, maxNumStreams, mockSender.queueControlFrame, ) }) It("opens all streams up to the id on GetOrOpenStream", func() { _, err := m.GetOrOpenStream(4) Expect(err).ToNot(HaveOccurred()) Expect(newItemCounter).To(Equal(4)) }) It("starts opening streams at the right position", func() { // like the test above, but with 2 calls to GetOrOpenStream _, err := m.GetOrOpenStream(2) Expect(err).ToNot(HaveOccurred()) Expect(newItemCounter).To(Equal(2)) _, err = m.GetOrOpenStream(5) Expect(err).ToNot(HaveOccurred()) Expect(newItemCounter).To(Equal(5)) }) It("accepts streams in the right order", func() { _, err := m.GetOrOpenStream(2) // open streams 1 and 2 Expect(err).ToNot(HaveOccurred()) str, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) str, err = m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) }) It("allows opening the maximum stream ID", func() { str, err := m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) }) It("errors when trying to get a stream ID higher than the maximum", func() { _, err := m.GetOrOpenStream(6) Expect(err).To(HaveOccurred()) Expect(err.(streamError).TestError()).To(MatchError("peer tried to open stream 6 (current limit: 5)")) }) It("blocks AcceptStream until a new stream is available", func() { strChan := make(chan item) go func() { defer GinkgoRecover() str, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) strChan <- str }() Consistently(strChan).ShouldNot(Receive()) str, err := m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) var acceptedStr item Eventually(strChan).Should(Receive(&acceptedStr)) Expect(acceptedStr.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) }) It("unblocks AcceptStream when the context is canceled", func() { ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := m.AcceptStream(ctx) Expect(err).To(MatchError("context canceled")) close(done) }() Consistently(done).ShouldNot(BeClosed()) cancel() Eventually(done).Should(BeClosed()) }) It("unblocks AcceptStream when it is closed", func() { testErr := errors.New("test error") done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := m.AcceptStream(context.Background()) Expect(err).To(MatchError(testErr)) close(done) }() Consistently(done).ShouldNot(BeClosed()) m.CloseWithError(testErr) Eventually(done).Should(BeClosed()) }) It("errors AcceptStream immediately if it is closed", func() { testErr := errors.New("test error") m.CloseWithError(testErr) _, err := m.AcceptStream(context.Background()) Expect(err).To(MatchError(testErr)) }) It("closes all streams when CloseWithError is called", func() { str1, err := m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) str2, err := m.GetOrOpenStream(3) Expect(err).ToNot(HaveOccurred()) testErr := errors.New("test err") m.CloseWithError(testErr) Expect(str1.(*mockGenericStream).closed).To(BeTrue()) Expect(str1.(*mockGenericStream).closeErr).To(MatchError(testErr)) Expect(str2.(*mockGenericStream).closed).To(BeTrue()) Expect(str2.(*mockGenericStream).closeErr).To(MatchError(testErr)) }) It("deletes streams", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) _, err := m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) str, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) Expect(m.DeleteStream(1)).To(Succeed()) str, err = m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeNil()) }) It("waits until a stream is accepted before actually deleting it", func() { _, err := m.GetOrOpenStream(2) Expect(err).ToNot(HaveOccurred()) Expect(m.DeleteStream(2)).To(Succeed()) str, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) // when accepting this stream, it will get deleted, and a MAX_STREAMS frame is queued mockSender.EXPECT().queueControlFrame(gomock.Any()) str, err = m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) }) It("doesn't return a stream queued for deleting from GetOrOpenStream", func() { str, err := m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str).ToNot(BeNil()) Expect(m.DeleteStream(1)).To(Succeed()) str, err = m.GetOrOpenStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeNil()) // when accepting this stream, it will get deleted, and a MAX_STREAMS frame is queued mockSender.EXPECT().queueControlFrame(gomock.Any()) str, err = m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).ToNot(BeNil()) }) It("errors when deleting a non-existing stream", func() { err := m.DeleteStream(1337) Expect(err).To(HaveOccurred()) Expect(err.(streamError).TestError()).To(MatchError("tried to delete unknown incoming stream 1337")) }) It("sends MAX_STREAMS frames when streams are deleted", func() { // open a bunch of streams _, err := m.GetOrOpenStream(5) Expect(err).ToNot(HaveOccurred()) // accept all streams for i := 0; i < 5; i++ { _, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) } mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.MaxStreamsFrame).MaxStreamNum).To(Equal(protocol.StreamNum(maxNumStreams + 1))) checkFrameSerialization(f) }) Expect(m.DeleteStream(3)).To(Succeed()) mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.MaxStreamsFrame).MaxStreamNum).To(Equal(protocol.StreamNum(maxNumStreams + 2))) checkFrameSerialization(f) }) Expect(m.DeleteStream(4)).To(Succeed()) }) Context("using high stream limits", func() { BeforeEach(func() { maxNumStreams = uint64(protocol.MaxStreamCount) - 2 }) It("doesn't send MAX_STREAMS frames if they would overflow 2^60 (the maximum stream count)", func() { // open a bunch of streams _, err := m.GetOrOpenStream(5) Expect(err).ToNot(HaveOccurred()) // accept all streams for i := 0; i < 5; i++ { _, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) } mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.MaxStreamsFrame).MaxStreamNum).To(Equal(protocol.MaxStreamCount - 1)) checkFrameSerialization(f) }) Expect(m.DeleteStream(4)).To(Succeed()) mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.MaxStreamsFrame).MaxStreamNum).To(Equal(protocol.MaxStreamCount)) checkFrameSerialization(f) }) Expect(m.DeleteStream(3)).To(Succeed()) // at this point, we can't increase the stream limit any further, so no more MAX_STREAMS frames will be sent Expect(m.DeleteStream(2)).To(Succeed()) Expect(m.DeleteStream(1)).To(Succeed()) }) }) Context("randomized tests", func() { const num = 1000 BeforeEach(func() { maxNumStreams = num }) It("opens and accepts streams", func() { rand.Seed(GinkgoRandomSeed()) ids := make([]protocol.StreamNum, num) for i := 0; i < num; i++ { ids[i] = protocol.StreamNum(i + 1) } rand.Shuffle(len(ids), func(i, j int) { ids[i], ids[j] = ids[j], ids[i] }) const timeout = 5 * time.Second done := make(chan struct{}, 2) go func() { defer GinkgoRecover() ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() for i := 0; i < num; i++ { _, err := m.AcceptStream(ctx) Expect(err).ToNot(HaveOccurred()) } done <- struct{}{} }() go func() { defer GinkgoRecover() for i := 0; i < num; i++ { _, err := m.GetOrOpenStream(ids[i]) Expect(err).ToNot(HaveOccurred()) } done <- struct{}{} }() Eventually(done, timeout*3/2).Should(Receive()) Eventually(done, timeout*3/2).Should(Receive()) }) }) }) quic-go-0.25.0/streams_map_incoming_uni.go000066400000000000000000000127301417145451600205550ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package quic import ( "context" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) // When a stream is deleted before it was accepted, we can't delete it from the map immediately. // We need to wait until the application accepts it, and delete it then. type receiveStreamIEntry struct { stream receiveStreamI shouldDelete bool } type incomingUniStreamsMap struct { mutex sync.RWMutex newStreamChan chan struct{} streams map[protocol.StreamNum]receiveStreamIEntry nextStreamToAccept protocol.StreamNum // the next stream that will be returned by AcceptStream() nextStreamToOpen protocol.StreamNum // the highest stream that the peer opened maxStream protocol.StreamNum // the highest stream that the peer is allowed to open maxNumStreams uint64 // maximum number of streams newStream func(protocol.StreamNum) receiveStreamI queueMaxStreamID func(*wire.MaxStreamsFrame) closeErr error } func newIncomingUniStreamsMap( newStream func(protocol.StreamNum) receiveStreamI, maxStreams uint64, queueControlFrame func(wire.Frame), ) *incomingUniStreamsMap { return &incomingUniStreamsMap{ newStreamChan: make(chan struct{}, 1), streams: make(map[protocol.StreamNum]receiveStreamIEntry), maxStream: protocol.StreamNum(maxStreams), maxNumStreams: maxStreams, newStream: newStream, nextStreamToOpen: 1, nextStreamToAccept: 1, queueMaxStreamID: func(f *wire.MaxStreamsFrame) { queueControlFrame(f) }, } } func (m *incomingUniStreamsMap) AcceptStream(ctx context.Context) (receiveStreamI, error) { // drain the newStreamChan, so we don't check the map twice if the stream doesn't exist select { case <-m.newStreamChan: default: } m.mutex.Lock() var num protocol.StreamNum var entry receiveStreamIEntry for { num = m.nextStreamToAccept if m.closeErr != nil { m.mutex.Unlock() return nil, m.closeErr } var ok bool entry, ok = m.streams[num] if ok { break } m.mutex.Unlock() select { case <-ctx.Done(): return nil, ctx.Err() case <-m.newStreamChan: } m.mutex.Lock() } m.nextStreamToAccept++ // If this stream was completed before being accepted, we can delete it now. if entry.shouldDelete { if err := m.deleteStream(num); err != nil { m.mutex.Unlock() return nil, err } } m.mutex.Unlock() return entry.stream, nil } func (m *incomingUniStreamsMap) GetOrOpenStream(num protocol.StreamNum) (receiveStreamI, error) { m.mutex.RLock() if num > m.maxStream { m.mutex.RUnlock() return nil, streamError{ message: "peer tried to open stream %d (current limit: %d)", nums: []protocol.StreamNum{num, m.maxStream}, } } // if the num is smaller than the highest we accepted // * this stream exists in the map, and we can return it, or // * this stream was already closed, then we can return the nil if num < m.nextStreamToOpen { var s receiveStreamI // If the stream was already queued for deletion, and is just waiting to be accepted, don't return it. if entry, ok := m.streams[num]; ok && !entry.shouldDelete { s = entry.stream } m.mutex.RUnlock() return s, nil } m.mutex.RUnlock() m.mutex.Lock() // no need to check the two error conditions from above again // * maxStream can only increase, so if the id was valid before, it definitely is valid now // * highestStream is only modified by this function for newNum := m.nextStreamToOpen; newNum <= num; newNum++ { m.streams[newNum] = receiveStreamIEntry{stream: m.newStream(newNum)} select { case m.newStreamChan <- struct{}{}: default: } } m.nextStreamToOpen = num + 1 entry := m.streams[num] m.mutex.Unlock() return entry.stream, nil } func (m *incomingUniStreamsMap) DeleteStream(num protocol.StreamNum) error { m.mutex.Lock() defer m.mutex.Unlock() return m.deleteStream(num) } func (m *incomingUniStreamsMap) deleteStream(num protocol.StreamNum) error { if _, ok := m.streams[num]; !ok { return streamError{ message: "tried to delete unknown incoming stream %d", nums: []protocol.StreamNum{num}, } } // Don't delete this stream yet, if it was not yet accepted. // Just save it to streamsToDelete map, to make sure it is deleted as soon as it gets accepted. if num >= m.nextStreamToAccept { entry, ok := m.streams[num] if ok && entry.shouldDelete { return streamError{ message: "tried to delete incoming stream %d multiple times", nums: []protocol.StreamNum{num}, } } entry.shouldDelete = true m.streams[num] = entry // can't assign to struct in map, so we need to reassign return nil } delete(m.streams, num) // queue a MAX_STREAM_ID frame, giving the peer the option to open a new stream if m.maxNumStreams > uint64(len(m.streams)) { maxStream := m.nextStreamToOpen + protocol.StreamNum(m.maxNumStreams-uint64(len(m.streams))) - 1 // Never send a value larger than protocol.MaxStreamCount. if maxStream <= protocol.MaxStreamCount { m.maxStream = maxStream m.queueMaxStreamID(&wire.MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: m.maxStream, }) } } return nil } func (m *incomingUniStreamsMap) CloseWithError(err error) { m.mutex.Lock() m.closeErr = err for _, entry := range m.streams { entry.stream.closeForShutdown(err) } m.mutex.Unlock() close(m.newStreamChan) } quic-go-0.25.0/streams_map_outgoing_bidi.go000066400000000000000000000127061417145451600207240ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package quic import ( "context" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) type outgoingBidiStreamsMap struct { mutex sync.RWMutex streams map[protocol.StreamNum]streamI openQueue map[uint64]chan struct{} lowestInQueue uint64 highestInQueue uint64 nextStream protocol.StreamNum // stream ID of the stream returned by OpenStream(Sync) maxStream protocol.StreamNum // the maximum stream ID we're allowed to open blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream newStream func(protocol.StreamNum) streamI queueStreamIDBlocked func(*wire.StreamsBlockedFrame) closeErr error } func newOutgoingBidiStreamsMap( newStream func(protocol.StreamNum) streamI, queueControlFrame func(wire.Frame), ) *outgoingBidiStreamsMap { return &outgoingBidiStreamsMap{ streams: make(map[protocol.StreamNum]streamI), openQueue: make(map[uint64]chan struct{}), maxStream: protocol.InvalidStreamNum, nextStream: 1, newStream: newStream, queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, } } func (m *outgoingBidiStreamsMap) OpenStream() (streamI, error) { m.mutex.Lock() defer m.mutex.Unlock() if m.closeErr != nil { return nil, m.closeErr } // if there are OpenStreamSync calls waiting, return an error here if len(m.openQueue) > 0 || m.nextStream > m.maxStream { m.maybeSendBlockedFrame() return nil, streamOpenErr{errTooManyOpenStreams} } return m.openStream(), nil } func (m *outgoingBidiStreamsMap) OpenStreamSync(ctx context.Context) (streamI, error) { m.mutex.Lock() defer m.mutex.Unlock() if m.closeErr != nil { return nil, m.closeErr } if err := ctx.Err(); err != nil { return nil, err } if len(m.openQueue) == 0 && m.nextStream <= m.maxStream { return m.openStream(), nil } waitChan := make(chan struct{}, 1) queuePos := m.highestInQueue m.highestInQueue++ if len(m.openQueue) == 0 { m.lowestInQueue = queuePos } m.openQueue[queuePos] = waitChan m.maybeSendBlockedFrame() for { m.mutex.Unlock() select { case <-ctx.Done(): m.mutex.Lock() delete(m.openQueue, queuePos) return nil, ctx.Err() case <-waitChan: } m.mutex.Lock() if m.closeErr != nil { return nil, m.closeErr } if m.nextStream > m.maxStream { // no stream available. Continue waiting continue } str := m.openStream() delete(m.openQueue, queuePos) m.lowestInQueue = queuePos + 1 m.unblockOpenSync() return str, nil } } func (m *outgoingBidiStreamsMap) openStream() streamI { s := m.newStream(m.nextStream) m.streams[m.nextStream] = s m.nextStream++ return s } // maybeSendBlockedFrame queues a STREAMS_BLOCKED frame for the current stream offset, // if we haven't sent one for this offset yet func (m *outgoingBidiStreamsMap) maybeSendBlockedFrame() { if m.blockedSent { return } var streamNum protocol.StreamNum if m.maxStream != protocol.InvalidStreamNum { streamNum = m.maxStream } m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ Type: protocol.StreamTypeBidi, StreamLimit: streamNum, }) m.blockedSent = true } func (m *outgoingBidiStreamsMap) GetStream(num protocol.StreamNum) (streamI, error) { m.mutex.RLock() if num >= m.nextStream { m.mutex.RUnlock() return nil, streamError{ message: "peer attempted to open stream %d", nums: []protocol.StreamNum{num}, } } s := m.streams[num] m.mutex.RUnlock() return s, nil } func (m *outgoingBidiStreamsMap) DeleteStream(num protocol.StreamNum) error { m.mutex.Lock() defer m.mutex.Unlock() if _, ok := m.streams[num]; !ok { return streamError{ message: "tried to delete unknown outgoing stream %d", nums: []protocol.StreamNum{num}, } } delete(m.streams, num) return nil } func (m *outgoingBidiStreamsMap) SetMaxStream(num protocol.StreamNum) { m.mutex.Lock() defer m.mutex.Unlock() if num <= m.maxStream { return } m.maxStream = num m.blockedSent = false if m.maxStream < m.nextStream-1+protocol.StreamNum(len(m.openQueue)) { m.maybeSendBlockedFrame() } m.unblockOpenSync() } // UpdateSendWindow is called when the peer's transport parameters are received. // Only in the case of a 0-RTT handshake will we have open streams at this point. // We might need to update the send window, in case the server increased it. func (m *outgoingBidiStreamsMap) UpdateSendWindow(limit protocol.ByteCount) { m.mutex.Lock() for _, str := range m.streams { str.updateSendWindow(limit) } m.mutex.Unlock() } // unblockOpenSync unblocks the next OpenStreamSync go-routine to open a new stream func (m *outgoingBidiStreamsMap) unblockOpenSync() { if len(m.openQueue) == 0 { return } for qp := m.lowestInQueue; qp <= m.highestInQueue; qp++ { c, ok := m.openQueue[qp] if !ok { // entry was deleted because the context was canceled continue } // unblockOpenSync is called both from OpenStreamSync and from SetMaxStream. // It's sufficient to only unblock OpenStreamSync once. select { case c <- struct{}{}: default: } return } } func (m *outgoingBidiStreamsMap) CloseWithError(err error) { m.mutex.Lock() m.closeErr = err for _, str := range m.streams { str.closeForShutdown(err) } for _, c := range m.openQueue { if c != nil { close(c) } } m.mutex.Unlock() } quic-go-0.25.0/streams_map_outgoing_generic.go000066400000000000000000000127331417145451600214310ustar00rootroot00000000000000package quic import ( "context" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) //go:generate genny -in $GOFILE -out streams_map_outgoing_bidi.go gen "item=streamI Item=BidiStream streamTypeGeneric=protocol.StreamTypeBidi" //go:generate genny -in $GOFILE -out streams_map_outgoing_uni.go gen "item=sendStreamI Item=UniStream streamTypeGeneric=protocol.StreamTypeUni" type outgoingItemsMap struct { mutex sync.RWMutex streams map[protocol.StreamNum]item openQueue map[uint64]chan struct{} lowestInQueue uint64 highestInQueue uint64 nextStream protocol.StreamNum // stream ID of the stream returned by OpenStream(Sync) maxStream protocol.StreamNum // the maximum stream ID we're allowed to open blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream newStream func(protocol.StreamNum) item queueStreamIDBlocked func(*wire.StreamsBlockedFrame) closeErr error } func newOutgoingItemsMap( newStream func(protocol.StreamNum) item, queueControlFrame func(wire.Frame), ) *outgoingItemsMap { return &outgoingItemsMap{ streams: make(map[protocol.StreamNum]item), openQueue: make(map[uint64]chan struct{}), maxStream: protocol.InvalidStreamNum, nextStream: 1, newStream: newStream, queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, } } func (m *outgoingItemsMap) OpenStream() (item, error) { m.mutex.Lock() defer m.mutex.Unlock() if m.closeErr != nil { return nil, m.closeErr } // if there are OpenStreamSync calls waiting, return an error here if len(m.openQueue) > 0 || m.nextStream > m.maxStream { m.maybeSendBlockedFrame() return nil, streamOpenErr{errTooManyOpenStreams} } return m.openStream(), nil } func (m *outgoingItemsMap) OpenStreamSync(ctx context.Context) (item, error) { m.mutex.Lock() defer m.mutex.Unlock() if m.closeErr != nil { return nil, m.closeErr } if err := ctx.Err(); err != nil { return nil, err } if len(m.openQueue) == 0 && m.nextStream <= m.maxStream { return m.openStream(), nil } waitChan := make(chan struct{}, 1) queuePos := m.highestInQueue m.highestInQueue++ if len(m.openQueue) == 0 { m.lowestInQueue = queuePos } m.openQueue[queuePos] = waitChan m.maybeSendBlockedFrame() for { m.mutex.Unlock() select { case <-ctx.Done(): m.mutex.Lock() delete(m.openQueue, queuePos) return nil, ctx.Err() case <-waitChan: } m.mutex.Lock() if m.closeErr != nil { return nil, m.closeErr } if m.nextStream > m.maxStream { // no stream available. Continue waiting continue } str := m.openStream() delete(m.openQueue, queuePos) m.lowestInQueue = queuePos + 1 m.unblockOpenSync() return str, nil } } func (m *outgoingItemsMap) openStream() item { s := m.newStream(m.nextStream) m.streams[m.nextStream] = s m.nextStream++ return s } // maybeSendBlockedFrame queues a STREAMS_BLOCKED frame for the current stream offset, // if we haven't sent one for this offset yet func (m *outgoingItemsMap) maybeSendBlockedFrame() { if m.blockedSent { return } var streamNum protocol.StreamNum if m.maxStream != protocol.InvalidStreamNum { streamNum = m.maxStream } m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ Type: streamTypeGeneric, StreamLimit: streamNum, }) m.blockedSent = true } func (m *outgoingItemsMap) GetStream(num protocol.StreamNum) (item, error) { m.mutex.RLock() if num >= m.nextStream { m.mutex.RUnlock() return nil, streamError{ message: "peer attempted to open stream %d", nums: []protocol.StreamNum{num}, } } s := m.streams[num] m.mutex.RUnlock() return s, nil } func (m *outgoingItemsMap) DeleteStream(num protocol.StreamNum) error { m.mutex.Lock() defer m.mutex.Unlock() if _, ok := m.streams[num]; !ok { return streamError{ message: "tried to delete unknown outgoing stream %d", nums: []protocol.StreamNum{num}, } } delete(m.streams, num) return nil } func (m *outgoingItemsMap) SetMaxStream(num protocol.StreamNum) { m.mutex.Lock() defer m.mutex.Unlock() if num <= m.maxStream { return } m.maxStream = num m.blockedSent = false if m.maxStream < m.nextStream-1+protocol.StreamNum(len(m.openQueue)) { m.maybeSendBlockedFrame() } m.unblockOpenSync() } // UpdateSendWindow is called when the peer's transport parameters are received. // Only in the case of a 0-RTT handshake will we have open streams at this point. // We might need to update the send window, in case the server increased it. func (m *outgoingItemsMap) UpdateSendWindow(limit protocol.ByteCount) { m.mutex.Lock() for _, str := range m.streams { str.updateSendWindow(limit) } m.mutex.Unlock() } // unblockOpenSync unblocks the next OpenStreamSync go-routine to open a new stream func (m *outgoingItemsMap) unblockOpenSync() { if len(m.openQueue) == 0 { return } for qp := m.lowestInQueue; qp <= m.highestInQueue; qp++ { c, ok := m.openQueue[qp] if !ok { // entry was deleted because the context was canceled continue } // unblockOpenSync is called both from OpenStreamSync and from SetMaxStream. // It's sufficient to only unblock OpenStreamSync once. select { case c <- struct{}{}: default: } return } } func (m *outgoingItemsMap) CloseWithError(err error) { m.mutex.Lock() m.closeErr = err for _, str := range m.streams { str.closeForShutdown(err) } for _, c := range m.openQueue { if c != nil { close(c) } } m.mutex.Unlock() } quic-go-0.25.0/streams_map_outgoing_generic_test.go000066400000000000000000000376641417145451600225020ustar00rootroot00000000000000package quic import ( "context" "errors" "fmt" "math/rand" "sort" "sync" "time" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Streams Map (outgoing)", func() { var ( m *outgoingItemsMap newItem func(num protocol.StreamNum) item mockSender *MockStreamSender ) // waitForEnqueued waits until there are n go routines waiting on OpenStreamSync() waitForEnqueued := func(n int) { Eventually(func() int { m.mutex.Lock() defer m.mutex.Unlock() return len(m.openQueue) }, 50*time.Millisecond, 100*time.Microsecond).Should(Equal(n)) } BeforeEach(func() { newItem = func(num protocol.StreamNum) item { return &mockGenericStream{num: num} } mockSender = NewMockStreamSender(mockCtrl) m = newOutgoingItemsMap(newItem, mockSender.queueControlFrame) }) Context("no stream ID limit", func() { BeforeEach(func() { m.SetMaxStream(0xffffffff) }) It("opens streams", func() { str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) str, err = m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) }) It("doesn't open streams after it has been closed", func() { testErr := errors.New("close") m.CloseWithError(testErr) _, err := m.OpenStream() Expect(err).To(MatchError(testErr)) }) It("gets streams", func() { _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) str, err := m.GetStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) }) It("errors when trying to get a stream that has not yet been opened", func() { _, err := m.GetStream(1) Expect(err).To(HaveOccurred()) Expect(err.(streamError).TestError()).To(MatchError("peer attempted to open stream 1")) }) It("deletes streams", func() { _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(m.DeleteStream(1)).To(Succeed()) Expect(err).ToNot(HaveOccurred()) str, err := m.GetStream(1) Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeNil()) }) It("errors when deleting a non-existing stream", func() { err := m.DeleteStream(1337) Expect(err).To(HaveOccurred()) Expect(err.(streamError).TestError()).To(MatchError("tried to delete unknown outgoing stream 1337")) }) It("errors when deleting a stream twice", func() { _, err := m.OpenStream() // opens firstNewStream Expect(err).ToNot(HaveOccurred()) Expect(m.DeleteStream(1)).To(Succeed()) err = m.DeleteStream(1) Expect(err).To(HaveOccurred()) Expect(err.(streamError).TestError()).To(MatchError("tried to delete unknown outgoing stream 1")) }) It("closes all streams when CloseWithError is called", func() { str1, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) str2, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) testErr := errors.New("test err") m.CloseWithError(testErr) Expect(str1.(*mockGenericStream).closed).To(BeTrue()) Expect(str1.(*mockGenericStream).closeErr).To(MatchError(testErr)) Expect(str2.(*mockGenericStream).closed).To(BeTrue()) Expect(str2.(*mockGenericStream).closeErr).To(MatchError(testErr)) }) It("updates the send window", func() { str1, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) str2, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) m.UpdateSendWindow(1337) Expect(str1.(*mockGenericStream).sendWindow).To(BeEquivalentTo(1337)) Expect(str2.(*mockGenericStream).sendWindow).To(BeEquivalentTo(1337)) }) }) Context("with stream ID limits", func() { It("errors when no stream can be opened immediately", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) _, err := m.OpenStream() expectTooManyStreamsError(err) }) It("returns immediately when called with a canceled context", func() { ctx, cancel := context.WithCancel(context.Background()) cancel() _, err := m.OpenStreamSync(ctx) Expect(err).To(MatchError("context canceled")) }) It("blocks until a stream can be opened synchronously", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) done := make(chan struct{}) go func() { defer GinkgoRecover() str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) close(done) }() waitForEnqueued(1) m.SetMaxStream(1) Eventually(done).Should(BeClosed()) }) It("unblocks when the context is canceled", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(ctx) Expect(err).To(MatchError("context canceled")) close(done) }() waitForEnqueued(1) cancel() Eventually(done).Should(BeClosed()) // make sure that the next stream opened is stream 1 m.SetMaxStream(1000) str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) }) It("opens streams in the right order", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes() done1 := make(chan struct{}) go func() { defer GinkgoRecover() str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) close(done1) }() waitForEnqueued(1) done2 := make(chan struct{}) go func() { defer GinkgoRecover() str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) close(done2) }() waitForEnqueued(2) m.SetMaxStream(1) Eventually(done1).Should(BeClosed()) Consistently(done2).ShouldNot(BeClosed()) m.SetMaxStream(2) Eventually(done2).Should(BeClosed()) }) It("opens streams in the right order, when one of the contexts is canceled", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes() done1 := make(chan struct{}) go func() { defer GinkgoRecover() str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) close(done1) }() waitForEnqueued(1) done2 := make(chan struct{}) ctx, cancel := context.WithCancel(context.Background()) go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(ctx) Expect(err).To(MatchError(context.Canceled)) close(done2) }() waitForEnqueued(2) done3 := make(chan struct{}) go func() { defer GinkgoRecover() str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) close(done3) }() waitForEnqueued(3) cancel() Eventually(done2).Should(BeClosed()) m.SetMaxStream(1000) Eventually(done1).Should(BeClosed()) Eventually(done3).Should(BeClosed()) }) It("unblocks multiple OpenStreamSync calls at the same time", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes() done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) done <- struct{}{} }() go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) done <- struct{}{} }() waitForEnqueued(2) go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(context.Background()) Expect(err).To(MatchError("test done")) done <- struct{}{} }() waitForEnqueued(3) m.SetMaxStream(2) Eventually(done).Should(Receive()) Eventually(done).Should(Receive()) Consistently(done).ShouldNot(Receive()) m.CloseWithError(errors.New("test done")) Eventually(done).Should(Receive()) }) It("returns an error for OpenStream while an OpenStreamSync call is blocking", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).MaxTimes(2) openedSync := make(chan struct{}) go func() { defer GinkgoRecover() str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(1))) close(openedSync) }() waitForEnqueued(1) start := make(chan struct{}) openend := make(chan struct{}) go func() { defer GinkgoRecover() var hasStarted bool for { str, err := m.OpenStream() if err == nil { Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) close(openend) return } expectTooManyStreamsError(err) if !hasStarted { close(start) hasStarted = true } } }() Eventually(start).Should(BeClosed()) m.SetMaxStream(1) Eventually(openedSync).Should(BeClosed()) Consistently(openend).ShouldNot(BeClosed()) m.SetMaxStream(2) Eventually(openend).Should(BeClosed()) }) It("stops opening synchronously when it is closed", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) testErr := errors.New("test error") done := make(chan struct{}) go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(context.Background()) Expect(err).To(MatchError(testErr)) close(done) }() Consistently(done).ShouldNot(BeClosed()) m.CloseWithError(testErr) Eventually(done).Should(BeClosed()) }) It("doesn't reduce the stream limit", func() { m.SetMaxStream(2) m.SetMaxStream(1) _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(2))) }) It("queues a STREAMS_BLOCKED frame if no stream can be opened", func() { m.SetMaxStream(6) // open the 6 allowed streams for i := 0; i < 6; i++ { _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) } mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(6)) }) _, err := m.OpenStream() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal(errTooManyOpenStreams.Error())) }) It("only sends one STREAMS_BLOCKED frame for one stream ID", func() { m.SetMaxStream(1) mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(1)) }) _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) // try to open a stream twice, but expect only one STREAMS_BLOCKED to be sent _, err = m.OpenStream() expectTooManyStreamsError(err) _, err = m.OpenStream() expectTooManyStreamsError(err) }) It("queues a STREAMS_BLOCKED frame when there more streams waiting for OpenStreamSync than MAX_STREAMS allows", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(0)) }) done := make(chan struct{}, 2) go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) done <- struct{}{} }() go func() { defer GinkgoRecover() _, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) done <- struct{}{} }() waitForEnqueued(2) mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(1)) }) m.SetMaxStream(1) Eventually(done).Should(Receive()) Consistently(done).ShouldNot(Receive()) m.SetMaxStream(2) Eventually(done).Should(Receive()) }) }) Context("randomized tests", func() { It("opens streams", func() { rand.Seed(GinkgoRandomSeed()) const n = 100 fmt.Fprintf(GinkgoWriter, "Opening %d streams concurrently.\n", n) var blockedAt []protocol.StreamNum mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { blockedAt = append(blockedAt, f.(*wire.StreamsBlockedFrame).StreamLimit) }).AnyTimes() done := make(map[int]chan struct{}) for i := 1; i <= n; i++ { c := make(chan struct{}) done[i] = c go func(doneChan chan struct{}, id protocol.StreamNum) { defer GinkgoRecover() defer close(doneChan) str, err := m.OpenStreamSync(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str.(*mockGenericStream).num).To(Equal(id)) }(c, protocol.StreamNum(i)) waitForEnqueued(i) } var limit int limits := []protocol.StreamNum{0} for limit < n { limit += rand.Intn(n/5) + 1 if limit <= n { limits = append(limits, protocol.StreamNum(limit)) } fmt.Fprintf(GinkgoWriter, "Setting stream limit to %d.\n", limit) m.SetMaxStream(protocol.StreamNum(limit)) for i := 1; i <= n; i++ { if i <= limit { Eventually(done[i]).Should(BeClosed()) } else { Expect(done[i]).ToNot(BeClosed()) } } str, err := m.OpenStream() if limit <= n { Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal(errTooManyOpenStreams.Error())) } else { Expect(str.(*mockGenericStream).num).To(Equal(protocol.StreamNum(n + 1))) } } Expect(blockedAt).To(Equal(limits)) }) It("opens streams, when some of them are getting canceled", func() { rand.Seed(GinkgoRandomSeed()) const n = 100 fmt.Fprintf(GinkgoWriter, "Opening %d streams concurrently.\n", n) var blockedAt []protocol.StreamNum mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { blockedAt = append(blockedAt, f.(*wire.StreamsBlockedFrame).StreamLimit) }).AnyTimes() ctx, cancel := context.WithCancel(context.Background()) streamsToCancel := make(map[protocol.StreamNum]struct{}) // used as a set for i := 0; i < 10; i++ { id := protocol.StreamNum(rand.Intn(n) + 1) fmt.Fprintf(GinkgoWriter, "Canceling stream %d.\n", id) streamsToCancel[id] = struct{}{} } streamWillBeCanceled := func(id protocol.StreamNum) bool { _, ok := streamsToCancel[id] return ok } var streamIDs []int var mutex sync.Mutex done := make(map[int]chan struct{}) for i := 1; i <= n; i++ { c := make(chan struct{}) done[i] = c go func(doneChan chan struct{}, id protocol.StreamNum) { defer GinkgoRecover() defer close(doneChan) cont := context.Background() if streamWillBeCanceled(id) { cont = ctx } str, err := m.OpenStreamSync(cont) if streamWillBeCanceled(id) { Expect(err).To(MatchError(context.Canceled)) return } Expect(err).ToNot(HaveOccurred()) mutex.Lock() streamIDs = append(streamIDs, int(str.(*mockGenericStream).num)) mutex.Unlock() }(c, protocol.StreamNum(i)) waitForEnqueued(i) } cancel() for id := range streamsToCancel { Eventually(done[int(id)]).Should(BeClosed()) } var limit int numStreams := n - len(streamsToCancel) var limits []protocol.StreamNum for limit < numStreams { limits = append(limits, protocol.StreamNum(limit)) limit += rand.Intn(n/5) + 1 fmt.Fprintf(GinkgoWriter, "Setting stream limit to %d.\n", limit) m.SetMaxStream(protocol.StreamNum(limit)) l := limit if l > numStreams { l = numStreams } Eventually(func() int { mutex.Lock() defer mutex.Unlock() return len(streamIDs) }).Should(Equal(l)) // check that all stream IDs were used Expect(streamIDs).To(HaveLen(l)) sort.Ints(streamIDs) for i := 0; i < l; i++ { Expect(streamIDs[i]).To(Equal(i + 1)) } } Expect(blockedAt).To(Equal(limits)) }) }) }) quic-go-0.25.0/streams_map_outgoing_uni.go000066400000000000000000000127271417145451600206130ustar00rootroot00000000000000// This file was automatically generated by genny. // Any changes will be lost if this file is regenerated. // see https://github.com/cheekybits/genny package quic import ( "context" "sync" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) type outgoingUniStreamsMap struct { mutex sync.RWMutex streams map[protocol.StreamNum]sendStreamI openQueue map[uint64]chan struct{} lowestInQueue uint64 highestInQueue uint64 nextStream protocol.StreamNum // stream ID of the stream returned by OpenStream(Sync) maxStream protocol.StreamNum // the maximum stream ID we're allowed to open blockedSent bool // was a STREAMS_BLOCKED sent for the current maxStream newStream func(protocol.StreamNum) sendStreamI queueStreamIDBlocked func(*wire.StreamsBlockedFrame) closeErr error } func newOutgoingUniStreamsMap( newStream func(protocol.StreamNum) sendStreamI, queueControlFrame func(wire.Frame), ) *outgoingUniStreamsMap { return &outgoingUniStreamsMap{ streams: make(map[protocol.StreamNum]sendStreamI), openQueue: make(map[uint64]chan struct{}), maxStream: protocol.InvalidStreamNum, nextStream: 1, newStream: newStream, queueStreamIDBlocked: func(f *wire.StreamsBlockedFrame) { queueControlFrame(f) }, } } func (m *outgoingUniStreamsMap) OpenStream() (sendStreamI, error) { m.mutex.Lock() defer m.mutex.Unlock() if m.closeErr != nil { return nil, m.closeErr } // if there are OpenStreamSync calls waiting, return an error here if len(m.openQueue) > 0 || m.nextStream > m.maxStream { m.maybeSendBlockedFrame() return nil, streamOpenErr{errTooManyOpenStreams} } return m.openStream(), nil } func (m *outgoingUniStreamsMap) OpenStreamSync(ctx context.Context) (sendStreamI, error) { m.mutex.Lock() defer m.mutex.Unlock() if m.closeErr != nil { return nil, m.closeErr } if err := ctx.Err(); err != nil { return nil, err } if len(m.openQueue) == 0 && m.nextStream <= m.maxStream { return m.openStream(), nil } waitChan := make(chan struct{}, 1) queuePos := m.highestInQueue m.highestInQueue++ if len(m.openQueue) == 0 { m.lowestInQueue = queuePos } m.openQueue[queuePos] = waitChan m.maybeSendBlockedFrame() for { m.mutex.Unlock() select { case <-ctx.Done(): m.mutex.Lock() delete(m.openQueue, queuePos) return nil, ctx.Err() case <-waitChan: } m.mutex.Lock() if m.closeErr != nil { return nil, m.closeErr } if m.nextStream > m.maxStream { // no stream available. Continue waiting continue } str := m.openStream() delete(m.openQueue, queuePos) m.lowestInQueue = queuePos + 1 m.unblockOpenSync() return str, nil } } func (m *outgoingUniStreamsMap) openStream() sendStreamI { s := m.newStream(m.nextStream) m.streams[m.nextStream] = s m.nextStream++ return s } // maybeSendBlockedFrame queues a STREAMS_BLOCKED frame for the current stream offset, // if we haven't sent one for this offset yet func (m *outgoingUniStreamsMap) maybeSendBlockedFrame() { if m.blockedSent { return } var streamNum protocol.StreamNum if m.maxStream != protocol.InvalidStreamNum { streamNum = m.maxStream } m.queueStreamIDBlocked(&wire.StreamsBlockedFrame{ Type: protocol.StreamTypeUni, StreamLimit: streamNum, }) m.blockedSent = true } func (m *outgoingUniStreamsMap) GetStream(num protocol.StreamNum) (sendStreamI, error) { m.mutex.RLock() if num >= m.nextStream { m.mutex.RUnlock() return nil, streamError{ message: "peer attempted to open stream %d", nums: []protocol.StreamNum{num}, } } s := m.streams[num] m.mutex.RUnlock() return s, nil } func (m *outgoingUniStreamsMap) DeleteStream(num protocol.StreamNum) error { m.mutex.Lock() defer m.mutex.Unlock() if _, ok := m.streams[num]; !ok { return streamError{ message: "tried to delete unknown outgoing stream %d", nums: []protocol.StreamNum{num}, } } delete(m.streams, num) return nil } func (m *outgoingUniStreamsMap) SetMaxStream(num protocol.StreamNum) { m.mutex.Lock() defer m.mutex.Unlock() if num <= m.maxStream { return } m.maxStream = num m.blockedSent = false if m.maxStream < m.nextStream-1+protocol.StreamNum(len(m.openQueue)) { m.maybeSendBlockedFrame() } m.unblockOpenSync() } // UpdateSendWindow is called when the peer's transport parameters are received. // Only in the case of a 0-RTT handshake will we have open streams at this point. // We might need to update the send window, in case the server increased it. func (m *outgoingUniStreamsMap) UpdateSendWindow(limit protocol.ByteCount) { m.mutex.Lock() for _, str := range m.streams { str.updateSendWindow(limit) } m.mutex.Unlock() } // unblockOpenSync unblocks the next OpenStreamSync go-routine to open a new stream func (m *outgoingUniStreamsMap) unblockOpenSync() { if len(m.openQueue) == 0 { return } for qp := m.lowestInQueue; qp <= m.highestInQueue; qp++ { c, ok := m.openQueue[qp] if !ok { // entry was deleted because the context was canceled continue } // unblockOpenSync is called both from OpenStreamSync and from SetMaxStream. // It's sufficient to only unblock OpenStreamSync once. select { case c <- struct{}{}: default: } return } } func (m *outgoingUniStreamsMap) CloseWithError(err error) { m.mutex.Lock() m.closeErr = err for _, str := range m.streams { str.closeForShutdown(err) } for _, c := range m.openQueue { if c != nil { close(c) } } m.mutex.Unlock() } quic-go-0.25.0/streams_map_test.go000066400000000000000000000424731417145451600170650ustar00rootroot00000000000000package quic import ( "context" "errors" "fmt" "net" "github.com/golang/mock/gomock" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/qerr" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) func (e streamError) TestError() error { nums := make([]interface{}, len(e.nums)) for i, num := range e.nums { nums[i] = num } return fmt.Errorf(e.message, nums...) } type streamMapping struct { firstIncomingBidiStream protocol.StreamID firstIncomingUniStream protocol.StreamID firstOutgoingBidiStream protocol.StreamID firstOutgoingUniStream protocol.StreamID } func expectTooManyStreamsError(err error) { ExpectWithOffset(1, err).To(HaveOccurred()) ExpectWithOffset(1, err.Error()).To(Equal(errTooManyOpenStreams.Error())) nerr, ok := err.(net.Error) ExpectWithOffset(1, ok).To(BeTrue()) ExpectWithOffset(1, nerr.Temporary()).To(BeTrue()) ExpectWithOffset(1, nerr.Timeout()).To(BeFalse()) } var _ = Describe("Streams Map", func() { newFlowController := func(protocol.StreamID) flowcontrol.StreamFlowController { return mocks.NewMockStreamFlowController(mockCtrl) } serverStreamMapping := streamMapping{ firstIncomingBidiStream: 0, firstOutgoingBidiStream: 1, firstIncomingUniStream: 2, firstOutgoingUniStream: 3, } clientStreamMapping := streamMapping{ firstIncomingBidiStream: 1, firstOutgoingBidiStream: 0, firstIncomingUniStream: 3, firstOutgoingUniStream: 2, } for _, p := range []protocol.Perspective{protocol.PerspectiveServer, protocol.PerspectiveClient} { perspective := p var ids streamMapping if perspective == protocol.PerspectiveClient { ids = clientStreamMapping } else { ids = serverStreamMapping } Context(perspective.String(), func() { var ( m *streamsMap mockSender *MockStreamSender ) const ( MaxBidiStreamNum = 111 MaxUniStreamNum = 222 ) allowUnlimitedStreams := func() { m.UpdateLimits(&wire.TransportParameters{ MaxBidiStreamNum: protocol.MaxStreamCount, MaxUniStreamNum: protocol.MaxStreamCount, }) } BeforeEach(func() { mockSender = NewMockStreamSender(mockCtrl) m = newStreamsMap(mockSender, newFlowController, MaxBidiStreamNum, MaxUniStreamNum, perspective, protocol.VersionWhatever).(*streamsMap) }) Context("opening", func() { It("opens bidirectional streams", func() { allowUnlimitedStreams() str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeAssignableToTypeOf(&stream{})) Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream)) str, err = m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeAssignableToTypeOf(&stream{})) Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream + 4)) }) It("opens unidirectional streams", func() { allowUnlimitedStreams() str, err := m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeAssignableToTypeOf(&sendStream{})) Expect(str.StreamID()).To(Equal(ids.firstOutgoingUniStream)) str, err = m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeAssignableToTypeOf(&sendStream{})) Expect(str.StreamID()).To(Equal(ids.firstOutgoingUniStream + 4)) }) }) Context("accepting", func() { It("accepts bidirectional streams", func() { _, err := m.GetOrOpenReceiveStream(ids.firstIncomingBidiStream) Expect(err).ToNot(HaveOccurred()) str, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeAssignableToTypeOf(&stream{})) Expect(str.StreamID()).To(Equal(ids.firstIncomingBidiStream)) }) It("accepts unidirectional streams", func() { _, err := m.GetOrOpenReceiveStream(ids.firstIncomingUniStream) Expect(err).ToNot(HaveOccurred()) str, err := m.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).To(BeAssignableToTypeOf(&receiveStream{})) Expect(str.StreamID()).To(Equal(ids.firstIncomingUniStream)) }) }) Context("deleting", func() { BeforeEach(func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes() allowUnlimitedStreams() }) It("deletes outgoing bidirectional streams", func() { id := ids.firstOutgoingBidiStream str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) Expect(m.DeleteStream(id)).To(Succeed()) dstr, err := m.GetOrOpenSendStream(id) Expect(err).ToNot(HaveOccurred()) Expect(dstr).To(BeNil()) }) It("deletes incoming bidirectional streams", func() { id := ids.firstIncomingBidiStream str, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) Expect(m.DeleteStream(id)).To(Succeed()) dstr, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(dstr).To(BeNil()) }) It("accepts bidirectional streams after they have been deleted", func() { id := ids.firstIncomingBidiStream _, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(m.DeleteStream(id)).To(Succeed()) str, err := m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).ToNot(BeNil()) Expect(str.StreamID()).To(Equal(id)) }) It("deletes outgoing unidirectional streams", func() { id := ids.firstOutgoingUniStream str, err := m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) Expect(m.DeleteStream(id)).To(Succeed()) dstr, err := m.GetOrOpenSendStream(id) Expect(err).ToNot(HaveOccurred()) Expect(dstr).To(BeNil()) }) It("deletes incoming unidirectional streams", func() { id := ids.firstIncomingUniStream str, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) Expect(m.DeleteStream(id)).To(Succeed()) dstr, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(dstr).To(BeNil()) }) It("accepts unirectional streams after they have been deleted", func() { id := ids.firstIncomingUniStream _, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(m.DeleteStream(id)).To(Succeed()) str, err := m.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) Expect(str).ToNot(BeNil()) Expect(str.StreamID()).To(Equal(id)) }) It("errors when deleting unknown incoming unidirectional streams", func() { id := ids.firstIncomingUniStream + 4 Expect(m.DeleteStream(id)).To(MatchError(fmt.Sprintf("tried to delete unknown incoming stream %d", id))) }) It("errors when deleting unknown outgoing unidirectional streams", func() { id := ids.firstOutgoingUniStream + 4 Expect(m.DeleteStream(id)).To(MatchError(fmt.Sprintf("tried to delete unknown outgoing stream %d", id))) }) It("errors when deleting unknown incoming bidirectional streams", func() { id := ids.firstIncomingBidiStream + 4 Expect(m.DeleteStream(id)).To(MatchError(fmt.Sprintf("tried to delete unknown incoming stream %d", id))) }) It("errors when deleting unknown outgoing bidirectional streams", func() { id := ids.firstOutgoingBidiStream + 4 Expect(m.DeleteStream(id)).To(MatchError(fmt.Sprintf("tried to delete unknown outgoing stream %d", id))) }) }) Context("getting streams", func() { BeforeEach(func() { allowUnlimitedStreams() }) Context("send streams", func() { It("gets an outgoing bidirectional stream", func() { // need to open the stream ourselves first // the peer is not allowed to create a stream initiated by us _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) str, err := m.GetOrOpenSendStream(ids.firstOutgoingBidiStream) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream)) }) It("errors when the peer tries to open a higher outgoing bidirectional stream", func() { id := ids.firstOutgoingBidiStream + 5*4 _, err := m.GetOrOpenSendStream(id) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: fmt.Sprintf("peer attempted to open stream %d", id), })) }) It("gets an outgoing unidirectional stream", func() { // need to open the stream ourselves first // the peer is not allowed to create a stream initiated by us _, err := m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) str, err := m.GetOrOpenSendStream(ids.firstOutgoingUniStream) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingUniStream)) }) It("errors when the peer tries to open a higher outgoing bidirectional stream", func() { id := ids.firstOutgoingUniStream + 5*4 _, err := m.GetOrOpenSendStream(id) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: fmt.Sprintf("peer attempted to open stream %d", id), })) }) It("gets an incoming bidirectional stream", func() { id := ids.firstIncomingBidiStream + 4*7 str, err := m.GetOrOpenSendStream(id) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) }) It("errors when trying to get an incoming unidirectional stream", func() { id := ids.firstIncomingUniStream _, err := m.GetOrOpenSendStream(id) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: fmt.Sprintf("peer attempted to open send stream %d", id), })) }) }) Context("receive streams", func() { It("gets an outgoing bidirectional stream", func() { // need to open the stream ourselves first // the peer is not allowed to create a stream initiated by us _, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) str, err := m.GetOrOpenReceiveStream(ids.firstOutgoingBidiStream) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream)) }) It("errors when the peer tries to open a higher outgoing bidirectional stream", func() { id := ids.firstOutgoingBidiStream + 5*4 _, err := m.GetOrOpenReceiveStream(id) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: fmt.Sprintf("peer attempted to open stream %d", id), })) }) It("gets an incoming bidirectional stream", func() { id := ids.firstIncomingBidiStream + 4*7 str, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) }) It("gets an incoming unidirectional stream", func() { id := ids.firstIncomingUniStream + 4*10 str, err := m.GetOrOpenReceiveStream(id) Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(id)) }) It("errors when trying to get an outgoing unidirectional stream", func() { id := ids.firstOutgoingUniStream _, err := m.GetOrOpenReceiveStream(id) Expect(err).To(MatchError(&qerr.TransportError{ ErrorCode: qerr.StreamStateError, ErrorMessage: fmt.Sprintf("peer attempted to open receive stream %d", id), })) }) }) }) It("processes the parameter for outgoing streams", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()) _, err := m.OpenStream() expectTooManyStreamsError(err) m.UpdateLimits(&wire.TransportParameters{ MaxBidiStreamNum: 5, MaxUniStreamNum: 8, }) mockSender.EXPECT().queueControlFrame(gomock.Any()).Times(2) // test we can only 5 bidirectional streams for i := 0; i < 5; i++ { str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream + protocol.StreamID(4*i))) } _, err = m.OpenStream() expectTooManyStreamsError(err) // test we can only 8 unidirectional streams for i := 0; i < 8; i++ { str, err := m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingUniStream + protocol.StreamID(4*i))) } _, err = m.OpenUniStream() expectTooManyStreamsError(err) }) if perspective == protocol.PerspectiveClient { It("applies parameters to existing streams (needed for 0-RTT)", func() { m.UpdateLimits(&wire.TransportParameters{ MaxBidiStreamNum: 1000, MaxUniStreamNum: 1000, }) flowControllers := make(map[protocol.StreamID]*mocks.MockStreamFlowController) m.newFlowController = func(id protocol.StreamID) flowcontrol.StreamFlowController { fc := mocks.NewMockStreamFlowController(mockCtrl) flowControllers[id] = fc return fc } str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) unistr, err := m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(flowControllers).To(HaveKey(str.StreamID())) flowControllers[str.StreamID()].EXPECT().UpdateSendWindow(protocol.ByteCount(4321)) Expect(flowControllers).To(HaveKey(unistr.StreamID())) flowControllers[unistr.StreamID()].EXPECT().UpdateSendWindow(protocol.ByteCount(1234)) m.UpdateLimits(&wire.TransportParameters{ MaxBidiStreamNum: 1000, InitialMaxStreamDataUni: 1234, MaxUniStreamNum: 1000, InitialMaxStreamDataBidiRemote: 4321, }) }) } Context("handling MAX_STREAMS frames", func() { BeforeEach(func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes() }) It("processes IDs for outgoing bidirectional streams", func() { _, err := m.OpenStream() expectTooManyStreamsError(err) m.HandleMaxStreamsFrame(&wire.MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: 1, }) str, err := m.OpenStream() Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream)) _, err = m.OpenStream() expectTooManyStreamsError(err) }) It("processes IDs for outgoing unidirectional streams", func() { _, err := m.OpenUniStream() expectTooManyStreamsError(err) m.HandleMaxStreamsFrame(&wire.MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: 1, }) str, err := m.OpenUniStream() Expect(err).ToNot(HaveOccurred()) Expect(str.StreamID()).To(Equal(ids.firstOutgoingUniStream)) _, err = m.OpenUniStream() expectTooManyStreamsError(err) }) }) Context("sending MAX_STREAMS frames", func() { It("sends a MAX_STREAMS frame for bidirectional streams", func() { _, err := m.GetOrOpenReceiveStream(ids.firstIncomingBidiStream) Expect(err).ToNot(HaveOccurred()) _, err = m.AcceptStream(context.Background()) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().queueControlFrame(&wire.MaxStreamsFrame{ Type: protocol.StreamTypeBidi, MaxStreamNum: MaxBidiStreamNum + 1, }) Expect(m.DeleteStream(ids.firstIncomingBidiStream)).To(Succeed()) }) It("sends a MAX_STREAMS frame for unidirectional streams", func() { _, err := m.GetOrOpenReceiveStream(ids.firstIncomingUniStream) Expect(err).ToNot(HaveOccurred()) _, err = m.AcceptUniStream(context.Background()) Expect(err).ToNot(HaveOccurred()) mockSender.EXPECT().queueControlFrame(&wire.MaxStreamsFrame{ Type: protocol.StreamTypeUni, MaxStreamNum: MaxUniStreamNum + 1, }) Expect(m.DeleteStream(ids.firstIncomingUniStream)).To(Succeed()) }) }) It("closes", func() { testErr := errors.New("test error") m.CloseWithError(testErr) _, err := m.OpenStream() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal(testErr.Error())) _, err = m.OpenUniStream() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal(testErr.Error())) _, err = m.AcceptStream(context.Background()) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal(testErr.Error())) _, err = m.AcceptUniStream(context.Background()) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(Equal(testErr.Error())) }) if perspective == protocol.PerspectiveClient { It("resets for 0-RTT", func() { mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes() m.ResetFor0RTT() // make sure that calls to open / accept streams fail _, err := m.OpenStream() Expect(err).To(MatchError(Err0RTTRejected)) _, err = m.AcceptStream(context.Background()) Expect(err).To(MatchError(Err0RTTRejected)) // make sure that we can still get new streams, as the server might be sending us data str, err := m.GetOrOpenReceiveStream(3) Expect(err).ToNot(HaveOccurred()) Expect(str).ToNot(BeNil()) // now switch to using the new streams map m.UseResetMaps() _, err = m.OpenStream() Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("too many open streams")) }) } }) } }) quic-go-0.25.0/token_store.go000066400000000000000000000050171417145451600160400ustar00rootroot00000000000000package quic import ( "container/list" "sync" "github.com/lucas-clemente/quic-go/internal/utils" ) type singleOriginTokenStore struct { tokens []*ClientToken len int p int } func newSingleOriginTokenStore(size int) *singleOriginTokenStore { return &singleOriginTokenStore{tokens: make([]*ClientToken, size)} } func (s *singleOriginTokenStore) Add(token *ClientToken) { s.tokens[s.p] = token s.p = s.index(s.p + 1) s.len = utils.Min(s.len+1, len(s.tokens)) } func (s *singleOriginTokenStore) Pop() *ClientToken { s.p = s.index(s.p - 1) token := s.tokens[s.p] s.tokens[s.p] = nil s.len = utils.Max(s.len-1, 0) return token } func (s *singleOriginTokenStore) Len() int { return s.len } func (s *singleOriginTokenStore) index(i int) int { mod := len(s.tokens) return (i + mod) % mod } type lruTokenStoreEntry struct { key string cache *singleOriginTokenStore } type lruTokenStore struct { mutex sync.Mutex m map[string]*list.Element q *list.List capacity int singleOriginSize int } var _ TokenStore = &lruTokenStore{} // NewLRUTokenStore creates a new LRU cache for tokens received by the client. // maxOrigins specifies how many origins this cache is saving tokens for. // tokensPerOrigin specifies the maximum number of tokens per origin. func NewLRUTokenStore(maxOrigins, tokensPerOrigin int) TokenStore { return &lruTokenStore{ m: make(map[string]*list.Element), q: list.New(), capacity: maxOrigins, singleOriginSize: tokensPerOrigin, } } func (s *lruTokenStore) Put(key string, token *ClientToken) { s.mutex.Lock() defer s.mutex.Unlock() if el, ok := s.m[key]; ok { entry := el.Value.(*lruTokenStoreEntry) entry.cache.Add(token) s.q.MoveToFront(el) return } if s.q.Len() < s.capacity { entry := &lruTokenStoreEntry{ key: key, cache: newSingleOriginTokenStore(s.singleOriginSize), } entry.cache.Add(token) s.m[key] = s.q.PushFront(entry) return } elem := s.q.Back() entry := elem.Value.(*lruTokenStoreEntry) delete(s.m, entry.key) entry.key = key entry.cache = newSingleOriginTokenStore(s.singleOriginSize) entry.cache.Add(token) s.q.MoveToFront(elem) s.m[key] = elem } func (s *lruTokenStore) Pop(key string) *ClientToken { s.mutex.Lock() defer s.mutex.Unlock() var token *ClientToken if el, ok := s.m[key]; ok { s.q.MoveToFront(el) cache := el.Value.(*lruTokenStoreEntry).cache token = cache.Pop() if cache.Len() == 0 { s.q.Remove(el) delete(s.m, key) } } return token } quic-go-0.25.0/token_store_test.go000066400000000000000000000062631417145451600171030ustar00rootroot00000000000000package quic import ( "fmt" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Token Cache", func() { var s TokenStore BeforeEach(func() { s = NewLRUTokenStore(3, 4) }) mockToken := func(num int) *ClientToken { return &ClientToken{data: []byte(fmt.Sprintf("%d", num))} } Context("for a single origin", func() { const origin = "localhost" It("adds and gets tokens", func() { s.Put(origin, mockToken(1)) s.Put(origin, mockToken(2)) Expect(s.Pop(origin)).To(Equal(mockToken(2))) Expect(s.Pop(origin)).To(Equal(mockToken(1))) Expect(s.Pop(origin)).To(BeNil()) }) It("overwrites old tokens", func() { s.Put(origin, mockToken(1)) s.Put(origin, mockToken(2)) s.Put(origin, mockToken(3)) s.Put(origin, mockToken(4)) s.Put(origin, mockToken(5)) Expect(s.Pop(origin)).To(Equal(mockToken(5))) Expect(s.Pop(origin)).To(Equal(mockToken(4))) Expect(s.Pop(origin)).To(Equal(mockToken(3))) Expect(s.Pop(origin)).To(Equal(mockToken(2))) Expect(s.Pop(origin)).To(BeNil()) }) It("continues after getting a token", func() { s.Put(origin, mockToken(1)) s.Put(origin, mockToken(2)) s.Put(origin, mockToken(3)) Expect(s.Pop(origin)).To(Equal(mockToken(3))) s.Put(origin, mockToken(4)) s.Put(origin, mockToken(5)) Expect(s.Pop(origin)).To(Equal(mockToken(5))) Expect(s.Pop(origin)).To(Equal(mockToken(4))) Expect(s.Pop(origin)).To(Equal(mockToken(2))) Expect(s.Pop(origin)).To(Equal(mockToken(1))) Expect(s.Pop(origin)).To(BeNil()) }) }) Context("for multiple origins", func() { It("adds and gets tokens", func() { s.Put("host1", mockToken(1)) s.Put("host2", mockToken(2)) Expect(s.Pop("host1")).To(Equal(mockToken(1))) Expect(s.Pop("host1")).To(BeNil()) Expect(s.Pop("host2")).To(Equal(mockToken(2))) Expect(s.Pop("host2")).To(BeNil()) }) It("evicts old entries", func() { s.Put("host1", mockToken(1)) s.Put("host2", mockToken(2)) s.Put("host3", mockToken(3)) s.Put("host4", mockToken(4)) Expect(s.Pop("host1")).To(BeNil()) Expect(s.Pop("host2")).To(Equal(mockToken(2))) Expect(s.Pop("host3")).To(Equal(mockToken(3))) Expect(s.Pop("host4")).To(Equal(mockToken(4))) }) It("moves old entries to the front, when new tokens are added", func() { s.Put("host1", mockToken(1)) s.Put("host2", mockToken(2)) s.Put("host3", mockToken(3)) s.Put("host1", mockToken(11)) // make sure one is evicted s.Put("host4", mockToken(4)) Expect(s.Pop("host2")).To(BeNil()) Expect(s.Pop("host1")).To(Equal(mockToken(11))) Expect(s.Pop("host1")).To(Equal(mockToken(1))) Expect(s.Pop("host3")).To(Equal(mockToken(3))) Expect(s.Pop("host4")).To(Equal(mockToken(4))) }) It("deletes hosts that are empty", func() { s.Put("host1", mockToken(1)) s.Put("host2", mockToken(2)) s.Put("host3", mockToken(3)) Expect(s.Pop("host2")).To(Equal(mockToken(2))) Expect(s.Pop("host2")).To(BeNil()) // host2 is now empty and should have been deleted, making space for host4 s.Put("host4", mockToken(4)) Expect(s.Pop("host1")).To(Equal(mockToken(1))) Expect(s.Pop("host3")).To(Equal(mockToken(3))) Expect(s.Pop("host4")).To(Equal(mockToken(4))) }) }) }) quic-go-0.25.0/tools.go000066400000000000000000000001771417145451600146460ustar00rootroot00000000000000//go:build tools // +build tools package quic import ( _ "github.com/cheekybits/genny" _ "github.com/onsi/ginkgo/ginkgo" ) quic-go-0.25.0/window_update_queue.go000066400000000000000000000035041417145451600175600ustar00rootroot00000000000000package quic import ( "sync" "github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" ) type windowUpdateQueue struct { mutex sync.Mutex queue map[protocol.StreamID]struct{} // used as a set queuedConn bool // connection-level window update streamGetter streamGetter connFlowController flowcontrol.ConnectionFlowController callback func(wire.Frame) } func newWindowUpdateQueue( streamGetter streamGetter, connFC flowcontrol.ConnectionFlowController, cb func(wire.Frame), ) *windowUpdateQueue { return &windowUpdateQueue{ queue: make(map[protocol.StreamID]struct{}), streamGetter: streamGetter, connFlowController: connFC, callback: cb, } } func (q *windowUpdateQueue) AddStream(id protocol.StreamID) { q.mutex.Lock() q.queue[id] = struct{}{} q.mutex.Unlock() } func (q *windowUpdateQueue) AddConnection() { q.mutex.Lock() q.queuedConn = true q.mutex.Unlock() } func (q *windowUpdateQueue) QueueAll() { q.mutex.Lock() // queue a connection-level window update if q.queuedConn { q.callback(&wire.MaxDataFrame{MaximumData: q.connFlowController.GetWindowUpdate()}) q.queuedConn = false } // queue all stream-level window updates for id := range q.queue { delete(q.queue, id) str, err := q.streamGetter.GetOrOpenReceiveStream(id) if err != nil || str == nil { // the stream can be nil if it was completed before dequeing the window update continue } offset := str.getWindowUpdate() if offset == 0 { // can happen if we received a final offset, right after queueing the window update continue } q.callback(&wire.MaxStreamDataFrame{ StreamID: id, MaximumStreamData: offset, }) } q.mutex.Unlock() } quic-go-0.25.0/window_update_queue_test.go000066400000000000000000000075421417145451600206250ustar00rootroot00000000000000package quic import ( "github.com/lucas-clemente/quic-go/internal/mocks" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/wire" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Window Update Queue", func() { var ( q *windowUpdateQueue streamGetter *MockStreamGetter connFC *mocks.MockConnectionFlowController queuedFrames []wire.Frame ) BeforeEach(func() { streamGetter = NewMockStreamGetter(mockCtrl) connFC = mocks.NewMockConnectionFlowController(mockCtrl) queuedFrames = queuedFrames[:0] q = newWindowUpdateQueue(streamGetter, connFC, func(f wire.Frame) { queuedFrames = append(queuedFrames, f) }) }) It("adds stream offsets and gets MAX_STREAM_DATA frames", func() { stream1 := NewMockStreamI(mockCtrl) stream1.EXPECT().getWindowUpdate().Return(protocol.ByteCount(10)) stream3 := NewMockStreamI(mockCtrl) stream3.EXPECT().getWindowUpdate().Return(protocol.ByteCount(30)) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(3)).Return(stream3, nil) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(1)).Return(stream1, nil) q.AddStream(3) q.AddStream(1) q.QueueAll() Expect(queuedFrames).To(ContainElement(&wire.MaxStreamDataFrame{StreamID: 1, MaximumStreamData: 10})) Expect(queuedFrames).To(ContainElement(&wire.MaxStreamDataFrame{StreamID: 3, MaximumStreamData: 30})) }) It("deletes the entry after getting the MAX_STREAM_DATA frame", func() { stream10 := NewMockStreamI(mockCtrl) stream10.EXPECT().getWindowUpdate().Return(protocol.ByteCount(100)) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(10)).Return(stream10, nil) q.AddStream(10) q.QueueAll() Expect(queuedFrames).To(HaveLen(1)) q.QueueAll() Expect(queuedFrames).To(HaveLen(1)) }) It("doesn't queue a MAX_STREAM_DATA for a closed stream", func() { q.AddStream(12) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(12)).Return(nil, nil) q.QueueAll() Expect(queuedFrames).To(BeEmpty()) }) It("removes closed streams from the queue", func() { q.AddStream(12) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(12)).Return(nil, nil) q.QueueAll() Expect(queuedFrames).To(BeEmpty()) // don't EXPECT any further calls to GetOrOpenReceiveStream q.QueueAll() Expect(queuedFrames).To(BeEmpty()) }) It("doesn't queue a MAX_STREAM_DATA if the flow controller returns an offset of 0", func() { stream5 := NewMockStreamI(mockCtrl) stream5.EXPECT().getWindowUpdate().Return(protocol.ByteCount(0)) q.AddStream(5) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(5)).Return(stream5, nil) q.QueueAll() Expect(queuedFrames).To(BeEmpty()) }) It("removes streams for which the flow controller returns an offset of 0 from the queue", func() { stream5 := NewMockStreamI(mockCtrl) stream5.EXPECT().getWindowUpdate().Return(protocol.ByteCount(0)) q.AddStream(5) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(5)).Return(stream5, nil) q.QueueAll() Expect(queuedFrames).To(BeEmpty()) // don't EXPECT any further calls to GetOrOpenReveiveStream and to getWindowUpdate q.QueueAll() Expect(queuedFrames).To(BeEmpty()) }) It("queues MAX_DATA frames", func() { connFC.EXPECT().GetWindowUpdate().Return(protocol.ByteCount(0x1337)) q.AddConnection() q.QueueAll() Expect(queuedFrames).To(Equal([]wire.Frame{ &wire.MaxDataFrame{MaximumData: 0x1337}, })) }) It("deduplicates", func() { stream10 := NewMockStreamI(mockCtrl) stream10.EXPECT().getWindowUpdate().Return(protocol.ByteCount(200)) streamGetter.EXPECT().GetOrOpenReceiveStream(protocol.StreamID(10)).Return(stream10, nil) q.AddStream(10) q.AddStream(10) q.QueueAll() Expect(queuedFrames).To(Equal([]wire.Frame{ &wire.MaxStreamDataFrame{StreamID: 10, MaximumStreamData: 200}, })) }) })