diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f3f14061b00288..ba85d7acf4e149 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -200,3 +200,11 @@ /lib/url.js @nodejs/url /src/node_url.* @nodejs/url /test/fixtures/wpt/url @nodejs/url + +# SQLite +/deps/sqlite/ @nodejs/sqlite +/doc/api/sqlite.md @nodejs/sqlite +/src/node_sqlite.* @nodejs/sqlite +/test/parallel/test-sqlite* @nodejs/sqlite +/test/sqlite/ @nodejs/sqlite +/tools/dep_updaters/update-sqlite.sh @nodejs/sqlite diff --git a/.github/workflows/build-tarball.yml b/.github/workflows/build-tarball.yml index 118cf523e1df06..383bb674b939b0 100644 --- a/.github/workflows/build-tarball.yml +++ b/.github/workflows/build-tarball.yml @@ -46,7 +46,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache @@ -76,11 +76,11 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache - uses: mozilla-actions/sccache-action@9e326ebed976843c9932b3aa0e021c6f50310eb4 # v0.0.6 + uses: mozilla-actions/sccache-action@054db53350805f83040bf3e6e9b8cf5a139aa7c9 # v0.0.7 with: version: v0.8.1 - name: Environment Information diff --git a/.github/workflows/close-stale-feature-requests.yml b/.github/workflows/close-stale-feature-requests.yml index 301dbff620c602..17bac3b83f50d9 100644 --- a/.github/workflows/close-stale-feature-requests.yml +++ b/.github/workflows/close-stale-feature-requests.yml @@ -41,7 +41,7 @@ jobs: if: github.repository == 'nodejs/node' runs-on: ubuntu-latest steps: - - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 + - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 180 diff --git a/.github/workflows/close-stalled.yml b/.github/workflows/close-stalled.yml index 7ece19e2e2d6b2..6bf526baf01034 100644 --- a/.github/workflows/close-stalled.yml +++ b/.github/workflows/close-stalled.yml @@ -20,7 +20,7 @@ jobs: if: github.repository == 'nodejs/node' runs-on: ubuntu-latest steps: - - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0 + - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-close: 30 diff --git a/.github/workflows/coverage-linux-without-intl.yml b/.github/workflows/coverage-linux-without-intl.yml index 5e76aa5a0f22fd..c3c4714a5d4dd2 100644 --- a/.github/workflows/coverage-linux-without-intl.yml +++ b/.github/workflows/coverage-linux-without-intl.yml @@ -52,7 +52,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache @@ -79,6 +79,6 @@ jobs: - name: Clean tmp run: rm -rf coverage/tmp && rm -rf out - name: Upload - uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7 + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 with: directory: ./coverage diff --git a/.github/workflows/coverage-linux.yml b/.github/workflows/coverage-linux.yml index 7907815026d7de..fbd5dd53017944 100644 --- a/.github/workflows/coverage-linux.yml +++ b/.github/workflows/coverage-linux.yml @@ -52,7 +52,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache @@ -79,6 +79,6 @@ jobs: - name: Clean tmp run: rm -rf coverage/tmp && rm -rf out - name: Upload - uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7 + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 with: directory: ./coverage diff --git a/.github/workflows/coverage-windows.yml b/.github/workflows/coverage-windows.yml index fada006e321520..8aa925fd4f727a 100644 --- a/.github/workflows/coverage-windows.yml +++ b/.github/workflows/coverage-windows.yml @@ -49,7 +49,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install deps @@ -71,6 +71,6 @@ jobs: - name: Clean tmp run: npx rimraf ./coverage/tmp - name: Upload - uses: codecov/codecov-action@015f24e6818733317a2da2edd6290ab26238649a # v5.0.7 + uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5.3.1 with: directory: ./coverage diff --git a/.github/workflows/daily-wpt-fyi.yml b/.github/workflows/daily-wpt-fyi.yml index c90aac58315832..7074b7120c24cf 100644 --- a/.github/workflows/daily-wpt-fyi.yml +++ b/.github/workflows/daily-wpt-fyi.yml @@ -39,7 +39,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 30e4ff35d1d26b..c2165543bdaf0c 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -44,7 +44,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -64,7 +64,7 @@ jobs: with: node-version: ${{ env.NODE_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -122,7 +122,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -139,7 +139,7 @@ jobs: with: persist-credentials: false - name: Use Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/major-release.yml b/.github/workflows/major-release.yml index a90be1798fac85..dfe49605429583 100644 --- a/.github/workflows/major-release.yml +++ b/.github/workflows/major-release.yml @@ -9,6 +9,7 @@ permissions: jobs: create-issue: + if: github.repository == 'nodejs/node' runs-on: ubuntu-latest permissions: issues: write diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 819bd0539029ed..abfd3e30f2a56a 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2 + uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -73,6 +73,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 + uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8 with: sarif_file: results.sarif diff --git a/.github/workflows/test-internet.yml b/.github/workflows/test-internet.yml index eced01cfbdaa0e..7c3060e4d58d39 100644 --- a/.github/workflows/test-internet.yml +++ b/.github/workflows/test-internet.yml @@ -48,7 +48,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 1d3827ed202347..bf083a26befdc0 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -35,14 +35,18 @@ permissions: jobs: test-linux: if: github.event.pull_request.draft == false - runs-on: ubuntu-24.04 + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, ubuntu-24.04-arm] steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: persist-credentials: false path: node - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index 18254521e0fe1c..7b4c24c37638a8 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -52,7 +52,7 @@ jobs: persist-credentials: false path: node - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up Xcode ${{ env.XCODE_VERSION }} diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index b768638d9d9cbd..a976eb262bafb1 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -294,7 +294,7 @@ jobs: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} if: matrix.id == 'icu' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 + uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 with: python-version: ${{ env.PYTHON_VERSION }} - run: ${{ matrix.run }} @@ -305,7 +305,7 @@ jobs: if: env.COMMIT_MSG == '' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) run: | echo "COMMIT_MSG=${{ matrix.subsystem }}: update ${{ matrix.id }} to ${{ env.NEW_VERSION }}" >> "$GITHUB_ENV" - - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 + - uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 if: github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id # Creates a PR or update the Action's existing PR, or # no-op if the base branch is already up-to-date. diff --git a/.github/workflows/update-v8.yml b/.github/workflows/update-v8.yml index 8b3f9c811d150b..010fcb58384ffb 100644 --- a/.github/workflows/update-v8.yml +++ b/.github/workflows/update-v8.yml @@ -20,7 +20,7 @@ jobs: with: persist-credentials: false - name: Cache node modules and update-v8 - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.1.2 + uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0 id: cache-v8-npm env: cache-name: cache-v8-npm @@ -45,7 +45,7 @@ jobs: cat temp-output tail -n1 temp-output | grep "NEW_VERSION=" >> "$GITHUB_ENV" || true rm temp-output - - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7.0.5 + - uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7.0.6 # Creates a PR or update the Action's existing PR, or # no-op if the base branch is already up-to-date. with: diff --git a/.mailmap b/.mailmap index c197dd83e271a0..a060a422d49cce 100644 --- a/.mailmap +++ b/.mailmap @@ -240,7 +240,8 @@ James Ide James M Snell James Nimlos James Sumners -Jan Krems +Jan Martin +Jan Martin Jem Bezooyen Jem Bezooyen Jenna Vuong diff --git a/BUILDING.md b/BUILDING.md index 0c969a527f9010..f50bfb36184726 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -31,6 +31,8 @@ file a new issue. * [Building a debug build](#building-a-debug-build) * [Building an ASan build](#building-an-asan-build) * [Speeding up frequent rebuilds when developing](#speeding-up-frequent-rebuilds-when-developing) + * [ccache](#ccache) + * [Loading JS files from disk instead of embedding](#loading-js-files-from-disk-instead-of-embedding) * [Troubleshooting Unix and macOS builds](#troubleshooting-unix-and-macos-builds) * [Windows](#windows) * [Windows Prerequisites](#windows-prerequisites) @@ -163,7 +165,7 @@ Binaries at are produced on: | ----------------------- | ------------------------------------------------------------------------------------------------------------- | | aix-ppc64 | AIX 7.2 TL04 on PPC64BE with GCC 12[^5] | | darwin-x64 | macOS 13, Xcode 16 with -mmacosx-version-min=11.0 | -| darwin-arm64 (and .pkg) | macOS 13 (arm64), Xcode 14 with -mmacosx-version-min=11.0 | +| darwin-arm64 (and .pkg) | macOS 13 (arm64), Xcode 16 with -mmacosx-version-min=11.0 | | linux-arm64 | RHEL 8 with gcc-toolset-12[^6] | | linux-armv7l | Cross-compiled on RHEL 9 x64 with a [custom GCC toolchain](https://github.com/rvagg/rpi-newer-crosstools)[^7] | | linux-ppc64le | RHEL 8 with gcc-toolset-12[^6] | @@ -540,6 +542,8 @@ make test-only #### Speeding up frequent rebuilds when developing +##### ccache + Tips: The `ccache` utility is widely used and should generally work fine. If you encounter any difficulties, consider disabling `mold` as a troubleshooting step. @@ -575,17 +579,26 @@ export CXX="ccache c++" # add to ~/.zshrc or other shell config file On Windows: -Tips: follow , and you -should notice that obj file will be bigger the normal one. +Follow , and you +should notice that obj file will be bigger than the normal one. -First, install ccache, assume ccache install to c:\ccache, copy -c:\ccache\ccache.exe to c:\ccache\cl.exe with this command +First, install ccache. Assuming the installation of ccache is in `c:\ccache` +(where you can find `ccache.exe`), copy `c:\ccache\ccache.exe` to `c:\ccache\cl.exe` +with this command. ```powershell cp c:\ccache\ccache.exe c:\ccache\cl.exe ``` -When building Node.js provide a path to your ccache via the option +With newer version of Visual Studio, it may need the copy to be `clang-cl.exe` +instead. If the output of `vcbuild.bat` suggestion missing `clang-cl.exe`, copy +it differently: + +```powershell +cp c:\ccache\ccache.exe c:\ccache\clang-cl.exe +``` + +When building Node.js, provide a path to your ccache via the option: ```powershell .\vcbuild.bat ccache c:\ccache\ @@ -594,6 +607,14 @@ When building Node.js provide a path to your ccache via the option This will allow for near-instantaneous rebuilds when switching branches back and forth that were built with cache. +To use it with ClangCL, run this instead: + +```powershell +.\vcbuild.bat clang-cl ccache c:\ccache\ +``` + +##### Loading JS files from disk instead of embedding + When modifying only the JS layer in `lib`, it is possible to externally load it without modifying the executable: @@ -678,14 +699,19 @@ Optional requirements for compiling for Windows on ARM (ARM64): * Visual C++ ATL for ARM64 * Windows 10 SDK 10.0.17763.0 or newer -Optional requirements for compiling with ClangCL: +Optional requirements for compiling with ClangCL (search for `clang` in Visual Studio +Installer's "individual component" tab): -* Visual Studio optional components +* Visual Studio individual components * C++ Clang Compiler for Windows * MSBuild support for LLVM toolset NOTE: Currently we only support compiling with Clang that comes from Visual Studio. +When building with ClangCL, if the output from `vcbuild.bat` shows that the components are not installed +even when the Visual Studio Installer shows that they are installed, try removing the components +first and then reinstalling them again. + ##### Option 2: Automated install with WinGet [WinGet configuration files](https://github.com/nodejs/node/tree/main/.configurations) @@ -702,7 +728,7 @@ easily. These files will install the following To install Node.js prerequisites from Powershell Terminal: ```powershell -winget configure .\configurations\configuration.dsc.yaml +winget configure .\.configurations\configuration.dsc.yaml ``` Alternatively, you can use [Dev Home](https://learn.microsoft.com/en-us/windows/dev-home/) diff --git a/CHANGELOG.md b/CHANGELOG.md index caa3168d643d08..02528747f50d42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,7 +39,8 @@ release. -23.8.0
+23.9.0
+23.8.0
23.7.0
23.6.1
23.6.0
diff --git a/LICENSE b/LICENSE index 2b4b47df13d0e2..69fb75c37766b3 100644 --- a/LICENSE +++ b/LICENSE @@ -106,16 +106,16 @@ The externally maintained libraries used by Node.js are: - cjs-module-lexer, located at deps/cjs-module-lexer, is licensed as follows: """ - MIT License - ----------- - - Copyright (C) 2018-2020 Guy Bedford - - 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. + MIT License + ----------- + + Copyright (C) 2018-2020 Guy Bedford + + 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. """ - ittapi, located at deps/v8/third_party/ittapi, is licensed as follows: @@ -1759,7 +1759,7 @@ The externally maintained libraries used by Node.js are: same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2023 The simdjson authors + Copyright 2018-2025 The simdjson authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index ecee2fa5d9d2a3..405e15b54cc2c2 100644 --- a/Makefile +++ b/Makefile @@ -780,7 +780,9 @@ test-v8 test-v8-intl test-v8-benchmarks test-v8-all: endif apidoc_dirs = out/doc out/doc/api out/doc/api/assets -apidoc_sources = $(wildcard doc/api/*.md) +skip_apidoc_files = doc/api/quic.md + +apidoc_sources = $(filter-out $(skip_apidoc_files), $(wildcard doc/api/*.md)) apidocs_html = $(addprefix out/,$(apidoc_sources:.md=.html)) apidocs_json = $(addprefix out/,$(apidoc_sources:.md=.json)) @@ -1406,6 +1408,7 @@ lint-md: lint-js-doc | tools/.mdlintstamp ## Lint the markdown documents maintai run-format-md = tools/lint-md/lint-md.mjs --format $(LINT_MD_FILES) .PHONY: format-md format-md: tools/lint-md/node_modules/remark-parse/package.json ## Format the markdown documents maintained by us in the codebase. + $(info Formatting Markdown...) @$(call available-node,$(run-format-md)) diff --git a/README.md b/README.md index f10243bb78447b..c62291f3e251f4 100644 --- a/README.md +++ b/README.md @@ -358,7 +358,7 @@ For information about the governance of the Node.js project, see * [jazelly](https://github.com/jazelly) - **Jason Zhang** <> (he/him) * [jkrems](https://github.com/jkrems) - - **Jan Krems** <> (he/him) + **Jan Martin** <> (he/him) * [joyeecheung](https://github.com/joyeecheung) - **Joyee Cheung** <> (she/her) * [juanarbol](https://github.com/juanarbol) - diff --git a/benchmark/fs/bench-openSync.js b/benchmark/fs/bench-openSync.js index eaa56139dcbf3c..b17c0a1e67f8a7 100644 --- a/benchmark/fs/bench-openSync.js +++ b/benchmark/fs/bench-openSync.js @@ -10,6 +10,18 @@ const bench = common.createBenchmark(main, { n: [1e5], }); +function runBench({ n, path }) { + for (let i = 0; i < n; i++) { + try { + const fd = fs.openSync(path, 'r', 0o666); + fs.closeSync(fd); + } catch { + // do nothing + } + } +} + + function main({ n, type }) { let path; @@ -24,14 +36,9 @@ function main({ n, type }) { new Error('Invalid type'); } + runBench({ n, path }); + bench.start(); - for (let i = 0; i < n; i++) { - try { - const fd = fs.openSync(path, 'r', 0o666); - fs.closeSync(fd); - } catch { - // do nothing - } - } + runBench({ n, path }); bench.end(n); } diff --git a/configure.py b/configure.py index 8baf075384671e..f2dac8aab26555 100755 --- a/configure.py +++ b/configure.py @@ -38,7 +38,6 @@ sys.path.insert(0, 'tools') import getmoduleversion import getnapibuildversion -import getsharedopensslhasquic from gyp_node import run_gyp from utils import SearchFiles @@ -847,6 +846,12 @@ # End dummy list. +parser.add_argument('--with-quic', + action='store_true', + dest='quic', + default=None, + help='build with QUIC support') + parser.add_argument('--without-ssl', action='store_true', dest='without_ssl', @@ -1743,6 +1748,7 @@ def configure_openssl(o): variables['node_shared_ngtcp2'] = b(options.shared_ngtcp2) variables['node_shared_nghttp3'] = b(options.shared_nghttp3) variables['openssl_is_fips'] = b(options.openssl_is_fips) + variables['node_quic'] = b(options.quic) variables['node_fipsinstall'] = b(False) if options.openssl_no_asm: @@ -1804,13 +1810,8 @@ def without_ssl_error(option): if options.openssl_is_fips and not options.shared_openssl: variables['node_fipsinstall'] = b(True) - if options.shared_openssl: - has_quic = getsharedopensslhasquic.get_has_quic(options.__dict__['shared_openssl_includes']) - else: - has_quic = getsharedopensslhasquic.get_has_quic('deps/openssl/openssl/include') - - variables['openssl_quic'] = b(has_quic) - if has_quic: + variables['openssl_quic'] = b(options.quic) + if options.quic: o['defines'] += ['NODE_OPENSSL_HAS_QUIC'] configure_library('openssl', o) diff --git a/deps/ada/ada.cpp b/deps/ada/ada.cpp index c9fa03b1a7b8f0..a60102854930be 100644 --- a/deps/ada/ada.cpp +++ b/deps/ada/ada.cpp @@ -1,4 +1,4 @@ -/* auto-generated on 2025-01-30 18:48:55 -0500. Do not edit! */ +/* auto-generated on 2025-02-11 09:47:50 -0500. Do not edit! */ /* begin file src/ada.cpp */ #include "ada.h" /* begin file src/checkers.cpp */ diff --git a/deps/ada/ada.h b/deps/ada/ada.h index c32e3cfaa03201..c997f0981ce36b 100644 --- a/deps/ada/ada.h +++ b/deps/ada/ada.h @@ -1,4 +1,4 @@ -/* auto-generated on 2025-01-30 18:48:55 -0500. Do not edit! */ +/* auto-generated on 2025-02-11 09:47:50 -0500. Do not edit! */ /* begin file include/ada.h */ /** * @file ada.h @@ -1219,6 +1219,7 @@ constexpr ada::scheme::type get_scheme_type(std::string_view scheme) noexcept; #endif // ADA_SCHEME_H /* end file include/ada/scheme.h */ +#include #include namespace ada { @@ -1352,6 +1353,7 @@ struct url_base { #endif /* end file include/ada/url_base.h */ +#include #include #include @@ -4118,6 +4120,9 @@ void swap(expected &lhs, #ifndef ADA_URL_PATTERN_REGEX_H #define ADA_URL_PATTERN_REGEX_H +#include +#include + #ifdef ADA_USE_UNSAFE_STD_REGEX_PROVIDER #include #endif // ADA_USE_UNSAFE_STD_REGEX_PROVIDER @@ -4216,7 +4221,9 @@ concept url_pattern_encoding_callback = requires(F f, std::string_view sv) { // either a string or a URLPatternInit struct. If a string is given, // it will be parsed to create a URLPatternInit. The URLPatternInit // API is defined as part of the URLPattern specification. +// All provided strings must be valid UTF-8. struct url_pattern_init { + // All strings must be valid UTF-8. // @see https://urlpattern.spec.whatwg.org/#process-a-urlpatterninit static tl::expected process( url_pattern_init init, std::string_view type, @@ -4276,15 +4283,23 @@ struct url_pattern_init { #endif // ADA_TESTING bool operator==(const url_pattern_init&) const; - + // If present, must be valid UTF-8. std::optional protocol{}; + // If present, must be valid UTF-8. std::optional username{}; + // If present, must be valid UTF-8. std::optional password{}; + // If present, must be valid UTF-8. std::optional hostname{}; + // If present, must be valid UTF-8. std::optional port{}; + // If present, must be valid UTF-8. std::optional pathname{}; + // If present, must be valid UTF-8. std::optional search{}; + // If present, must be valid UTF-8. std::optional hash{}; + // If present, must be valid UTF-8. std::optional base_url{}; }; } // namespace ada @@ -4366,6 +4381,7 @@ tl::expected, errors> parse_url_pattern_impl( #ifndef ADA_IMPLEMENTATION_H #define ADA_IMPLEMENTATION_H +#include #include #include @@ -4379,6 +4395,7 @@ tl::expected, errors> parse_url_pattern_impl( #include #include +#include #include #include @@ -5040,7 +5057,9 @@ std::string href_from_file(std::string_view path); #endif // ADA_IMPLEMENTATION_H /* end file include/ada/implementation.h */ +#include #include +#include #include #include #include @@ -5219,6 +5238,8 @@ class url_pattern_component { bool has_regexp_groups = false; }; +// A URLPattern input can be either a string or a URLPatternInit object. +// If it is a string, it must be a valid UTF-8 string. using url_pattern_input = std::variant; // A struct providing the URLPattern matching results for all @@ -5251,12 +5272,16 @@ struct url_pattern_options { // defined in https://wicg.github.io/urlpattern. // More information about the URL Pattern syntax can be found at // https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API +// +// We require all strings to be valid UTF-8: it is the user's responsibility +// to ensure that the provided strings are valid UTF-8. template class url_pattern { public: url_pattern() = default; /** + * If non-null, base_url must pointer at a valid UTF-8 string. * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-exec */ result> exec( @@ -5264,6 +5289,7 @@ class url_pattern { const std::string_view* base_url = nullptr); /** + * If non-null, base_url must pointer at a valid UTF-8 string. * @see https://urlpattern.spec.whatwg.org/#dom-urlpattern-test */ result test(const url_pattern_input& input, @@ -5725,6 +5751,7 @@ std::string generate_segment_wildcard_regexp( #endif /* end file include/ada/url_pattern_helpers.h */ +#include #include #include @@ -6257,6 +6284,7 @@ ada_warn_unused std::string to_string(ada::state s); #include +#include #include /** @@ -6870,6 +6898,7 @@ namespace ada { #ifndef ADA_URL_AGGREGATOR_H #define ADA_URL_AGGREGATOR_H +#include #include #include #include @@ -7255,6 +7284,7 @@ ada_really_inline size_t percent_encode_index(const std::string_view input, /* end file include/ada/unicode-inl.h */ #include +#include #include namespace ada { @@ -8406,6 +8436,8 @@ using url_search_params_entries_iter = url_search_params_iter_type::ENTRIES>; /** + * We require all strings to be valid UTF-8. It is the user's responsibility to + * ensure that the provided strings are valid UTF-8. * @see https://url.spec.whatwg.org/#interface-urlsearchparams */ struct url_search_params { @@ -8428,6 +8460,7 @@ struct url_search_params { [[nodiscard]] inline size_t size() const noexcept; /** + * Both key and value must be valid UTF-8. * @see https://url.spec.whatwg.org/#dom-urlsearchparams-append */ inline void append(std::string_view key, std::string_view value); @@ -8455,6 +8488,7 @@ struct url_search_params { inline bool has(std::string_view key, std::string_view value) noexcept; /** + * Both key and value must be valid UTF-8. * @see https://url.spec.whatwg.org/#dom-urlsearchparams-set */ inline void set(std::string_view key, std::string_view value); @@ -8518,6 +8552,7 @@ struct url_search_params { std::vector params{}; /** + * The init parameter must be valid UTF-8. * @see https://url.spec.whatwg.org/#concept-urlencoded-parser */ void initialize(std::string_view init); @@ -8724,10 +8759,80 @@ inline void url_search_params::remove(const std::string_view key, } inline void url_search_params::sort() { - std::ranges::stable_sort( - params, [](const key_value_pair &lhs, const key_value_pair &rhs) { - return lhs.first < rhs.first; - }); + // We rely on the fact that the content is valid UTF-8. + std::ranges::stable_sort(params, [](const key_value_pair &lhs, + const key_value_pair &rhs) { + size_t i = 0, j = 0; + uint32_t low_surrogate1 = 0, low_surrogate2 = 0; + while ((i < lhs.first.size() || low_surrogate1 != 0) && + (j < rhs.first.size() || low_surrogate2 != 0)) { + uint32_t codePoint1 = 0, codePoint2 = 0; + + if (low_surrogate1 != 0) { + codePoint1 = low_surrogate1; + low_surrogate1 = 0; + } else { + uint8_t c1 = uint8_t(lhs.first[i]); + if (c1 <= 0x7F) { + codePoint1 = c1; + i++; + } else if (c1 <= 0xDF) { + codePoint1 = ((c1 & 0x1F) << 6) | (uint8_t(lhs.first[i + 1]) & 0x3F); + i += 2; + } else if (c1 <= 0xEF) { + codePoint1 = ((c1 & 0x0F) << 12) | + ((uint8_t(lhs.first[i + 1]) & 0x3F) << 6) | + (uint8_t(lhs.first[i + 2]) & 0x3F); + i += 3; + } else { + codePoint1 = ((c1 & 0x07) << 18) | + ((uint8_t(lhs.first[i + 1]) & 0x3F) << 12) | + ((uint8_t(lhs.first[i + 2]) & 0x3F) << 6) | + (uint8_t(lhs.first[i + 3]) & 0x3F); + i += 4; + + codePoint1 -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (codePoint1 >> 10)); + low_surrogate1 = uint16_t(0xDC00 + (codePoint1 & 0x3FF)); + codePoint1 = high_surrogate; + } + } + + if (low_surrogate2 != 0) { + codePoint2 = low_surrogate2; + low_surrogate2 = 0; + } else { + uint8_t c2 = uint8_t(rhs.first[j]); + if (c2 <= 0x7F) { + codePoint2 = c2; + j++; + } else if (c2 <= 0xDF) { + codePoint2 = ((c2 & 0x1F) << 6) | (uint8_t(rhs.first[j + 1]) & 0x3F); + j += 2; + } else if (c2 <= 0xEF) { + codePoint2 = ((c2 & 0x0F) << 12) | + ((uint8_t(rhs.first[j + 1]) & 0x3F) << 6) | + (uint8_t(rhs.first[j + 2]) & 0x3F); + j += 3; + } else { + codePoint2 = ((c2 & 0x07) << 18) | + ((uint8_t(rhs.first[j + 1]) & 0x3F) << 12) | + ((uint8_t(rhs.first[j + 2]) & 0x3F) << 6) | + (uint8_t(rhs.first[j + 3]) & 0x3F); + j += 4; + codePoint2 -= 0x10000; + uint16_t high_surrogate = uint16_t(0xD800 + (codePoint2 >> 10)); + low_surrogate2 = uint16_t(0xDC00 + (codePoint2 & 0x3FF)); + codePoint2 = high_surrogate; + } + } + + if (codePoint1 != codePoint2) { + return (codePoint1 < codePoint2); + } + } + return (j < rhs.first.size() || low_surrogate2 != 0); + }); } inline url_search_params_keys_iter url_search_params::get_keys() { @@ -10330,14 +10435,14 @@ constructor_string_parser::parse(std::string_view input) { #ifndef ADA_ADA_VERSION_H #define ADA_ADA_VERSION_H -#define ADA_VERSION "3.0.1" +#define ADA_VERSION "3.1.0" namespace ada { enum { ADA_VERSION_MAJOR = 3, - ADA_VERSION_MINOR = 0, - ADA_VERSION_REVISION = 1, + ADA_VERSION_MINOR = 1, + ADA_VERSION_REVISION = 0, }; } // namespace ada diff --git a/deps/cjs-module-lexer/README.md b/deps/cjs-module-lexer/README.md index cc7ca50cc72243..addf5edc6b1193 100644 --- a/deps/cjs-module-lexer/README.md +++ b/deps/cjs-module-lexer/README.md @@ -456,6 +456,25 @@ If you need to build lib/lexer.wat (optional) you must first install [wabt](https://github.com/WebAssembly/wabt) as a sibling folder to this project. The wat file is then build by running `make lib/lexer.wat` +### Creating a Release +These are the steps to create and publish a release. You will need docker +installed as well as having installed [wabt](https://github.com/WebAssembly/wabt) +as outlined above: + +- [ ] Figure out if the release should be semver patch, minor or major based on the changes since + the last release and determine the new version. +- [ ] Update the package.json version, and run a full build and test + - npm install + - npm run build + - npm run test +- [ ] Commit and tag the changes, pushing up to main and the tag + - For example + - `git tag -a 1.4.2 -m "1.4.2"` + - `git push origin tag 1.4.2` +- [ ] Create the GitHub release +- [ ] Run npm publish from an account with access (asking somebody with access + the nodejs-foundation account is an option if you don't have access. + ### License MIT diff --git a/deps/cjs-module-lexer/dist/lexer.mjs b/deps/cjs-module-lexer/dist/lexer.mjs index 806bb46287259d..b6f5fb548bb73f 100644 --- a/deps/cjs-module-lexer/dist/lexer.mjs +++ b/deps/cjs-module-lexer/dist/lexer.mjs @@ -1,2 +1,2 @@ -/* cjs-module-lexer 2.0.0 */ +/* cjs-module-lexer 2.1.0 */ let A;const B=1===new Uint8Array(new Uint16Array([1]).buffer)[0];export function parse(I,C="@"){if(!A)throw new Error("Not initialized");const w=I.length+1,D=(A.__heap_base.value||A.__heap_base)+4*w-A.memory.buffer.byteLength;D>0&&A.memory.grow(Math.ceil(D/65536));const G=A.sa(w);(B?g:E)(I,new Uint16Array(A.memory.buffer,G,w));const S=A.parseCJS(G,I.length,0,0,0);if(S){const B=new Error(`Parse error ${C}${A.e()}:${I.slice(0,A.e()).split("\n").length}:${A.e()-I.lastIndexOf("\n",A.e()-1)}`);throw Object.assign(B,{idx:A.e()}),5!==S&&6!==S&&7!==S||Object.assign(B,{code:"ERR_LEXER_ESM_SYNTAX"}),B}let R=new Set,o=new Set,H=new Set;for(;A.rre();){const B=Q(I.slice(A.res(),A.ree()));B&&o.add(B)}for(;A.ru();)H.add(Q(I.slice(A.us(),A.ue())));for(;A.re();){let B=Q(I.slice(A.es(),A.ee()));void 0===B||H.has(B)||R.add(B)}return{exports:[...R],reexports:[...o]}}function Q(A){if('"'!==A[0]&&"'"!==A[0])return A;try{const B=(0,eval)(A);for(let A=0;A>>8}}function g(A,B){const Q=A.length;let E=0;for(;EA.charCodeAt(0))}let C;export function init(){return C||(C=(async()=>{const B=await WebAssembly.compile(I()),{exports:Q}=await WebAssembly.instantiate(B);A=Q})())}export function initSync(){if(A)return;const B=new WebAssembly.Module(I()),{exports:Q}=new WebAssembly.Instance(B);A=Q} \ No newline at end of file diff --git a/deps/cjs-module-lexer/src/README.md b/deps/cjs-module-lexer/src/README.md index cc7ca50cc72243..addf5edc6b1193 100755 --- a/deps/cjs-module-lexer/src/README.md +++ b/deps/cjs-module-lexer/src/README.md @@ -456,6 +456,25 @@ If you need to build lib/lexer.wat (optional) you must first install [wabt](https://github.com/WebAssembly/wabt) as a sibling folder to this project. The wat file is then build by running `make lib/lexer.wat` +### Creating a Release +These are the steps to create and publish a release. You will need docker +installed as well as having installed [wabt](https://github.com/WebAssembly/wabt) +as outlined above: + +- [ ] Figure out if the release should be semver patch, minor or major based on the changes since + the last release and determine the new version. +- [ ] Update the package.json version, and run a full build and test + - npm install + - npm run build + - npm run test +- [ ] Commit and tag the changes, pushing up to main and the tag + - For example + - `git tag -a 1.4.2 -m "1.4.2"` + - `git push origin tag 1.4.2` +- [ ] Create the GitHub release +- [ ] Run npm publish from an account with access (asking somebody with access + the nodejs-foundation account is an option if you don't have access. + ### License MIT diff --git a/deps/cjs-module-lexer/src/build/Makefile b/deps/cjs-module-lexer/src/build/Makefile index f13c390b8cdb96..b8e96d82534f7d 100755 --- a/deps/cjs-module-lexer/src/build/Makefile +++ b/deps/cjs-module-lexer/src/build/Makefile @@ -1,10 +1,21 @@ +# These flags depend on the system and may be overridden +WASM_CC := clang +WASM_CFLAGS := --sysroot=/usr/share/wasi-sysroot +WASM_LDFLAGS := -nostartfiles + +# These are project-specific and are expected to be kept intact +WASM_TARGET := -target wasm32-unknown-wasi +WASM_EXTRA_CFLAGS := -I include-wasm/ -Wno-logical-op-parentheses -Wno-parentheses -Oz +WASM_EXTRA_LDFLAGS := -Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all +WASM_EXTRA_LDFLAGS += -Wl,--export=__heap_base,--export=parseCJS,--export=sa +WASM_EXTRA_LDFLAGS += -Wl,--export=e,--export=re,--export=es,--export=ee +WASM_EXTRA_LDFLAGS += -Wl,--export=rre,--export=ree,--export=res,--export=ru,--export=us,--export=ue + lib/lexer.wasm: include-wasm/cjs-module-lexer.h src/lexer.c @mkdir -p lib - clang --sysroot=/usr/share/wasi-sysroot -target wasm32-unknown-wasi src/lexer.c -I include-wasm -o lib/lexer.wasm -nostartfiles \ - -Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all,--export=__heap_base,\ - --export=parseCJS,--export=sa,--export=e,--export=re,--export=es,--export=ee,--export=rre,--export=ree,--export=res,--export=ru,--export=us,--export=ue \ - -Wno-logical-op-parentheses -Wno-parentheses \ - -Oz + $(WASM_CC) $(WASM_CFLAGS) $(WASM_TARGET) $(WASM_EXTRA_CFLAGS) \ + src/lexer.c -o lib/lexer.wasm \ + $(WASM_LDFLAGS) $(WASM_EXTRA_LDFLAGS) optimize: lib/lexer.wasm ${WASM_OPT} -Oz lib/lexer.wasm -o lib/lexer.wasm diff --git a/deps/cjs-module-lexer/src/package-lock.json b/deps/cjs-module-lexer/src/package-lock.json index 3c5f50d681e7b3..e8de2e3e921611 100644 --- a/deps/cjs-module-lexer/src/package-lock.json +++ b/deps/cjs-module-lexer/src/package-lock.json @@ -1,12 +1,12 @@ { "name": "cjs-module-lexer", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cjs-module-lexer", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "devDependencies": { "@babel/cli": "^7.5.5", diff --git a/deps/cjs-module-lexer/src/package.json b/deps/cjs-module-lexer/src/package.json index d59353db6da501..9ef0db81e12b46 100755 --- a/deps/cjs-module-lexer/src/package.json +++ b/deps/cjs-module-lexer/src/package.json @@ -1,6 +1,6 @@ { "name": "cjs-module-lexer", - "version": "2.0.0", + "version": "2.1.0", "description": "Lexes CommonJS modules, returning their named exports metadata", "main": "lexer.js", "exports": { diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h index 68093d18b71b14..4eaf615bd309f3 100644 --- a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -331,7 +331,7 @@ ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to * set negotiated AEAD and message digest algorithm. After the * successful call of this function, application can use - * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_0rtt_crypto_ctx` * if |level| == * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to * get :type:`ngtcp2_crypto_ctx`. @@ -378,7 +378,7 @@ NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key( * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to * set negotiated AEAD and message digest algorithm. After the * successful call of this function, application can use - * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx` + * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_0rtt_crypto_ctx` * if |level| == * :enum:`ngtcp2_encryption_level.NGTCP2_ENCRYPTION_LEVEL_0RTT`) to * get :type:`ngtcp2_crypto_ctx`. diff --git a/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c index c3f14f5d444bac..98ebf9e876c053 100644 --- a/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c +++ b/deps/ngtcp2/ngtcp2/crypto/picotls/picotls.c @@ -125,7 +125,7 @@ static int supported_cipher_suite(ptls_cipher_suite_t *cs) { #ifdef PTLS_OPENSSL_HAVE_CHACHA20_POLY1305 || cs->aead == &ptls_openssl_chacha20poly1305 #endif /* defined(PTLS_OPENSSL_HAVE_CHACHA20_POLY1305) */ - ; + ; } ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, @@ -146,7 +146,7 @@ ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, ctx->hp.native_handle = (void *)crypto_cipher_suite_get_hp(cs); ctx->max_encryption = crypto_cipher_suite_get_aead_max_encryption(cs); ctx->max_decryption_failure = - crypto_cipher_suite_get_aead_max_decryption_failure(cs); + crypto_cipher_suite_get_aead_max_decryption_failure(cs); return ctx; } @@ -329,19 +329,22 @@ int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, const uint8_t *nonce, size_t noncelen, const uint8_t *aad, size_t aadlen) { ptls_aead_context_t *actx = aead_ctx->native_handle; + size_t nwrite; (void)aead; ptls_aead_xor_iv(actx, nonce, noncelen); - if (ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad, - aadlen) == SIZE_MAX) { - return -1; - } + nwrite = + ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad, aadlen); /* zero-out static iv once again */ ptls_aead_xor_iv(actx, nonce, noncelen); + if (nwrite == SIZE_MAX) { + return -1; + } + return 0; } @@ -360,13 +363,13 @@ int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, } int ngtcp2_crypto_read_write_crypto_data( - ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, - const uint8_t *data, size_t datalen) { + ngtcp2_conn *conn, ngtcp2_encryption_level encryption_level, + const uint8_t *data, size_t datalen) { ngtcp2_crypto_picotls_ctx *cptls = ngtcp2_conn_get_tls_native_handle(conn); ptls_buffer_t sendbuf; size_t epoch_offsets[5] = {0}; size_t epoch = - ngtcp2_crypto_picotls_from_ngtcp2_encryption_level(encryption_level); + ngtcp2_crypto_picotls_from_ngtcp2_encryption_level(encryption_level); size_t epoch_datalen; size_t i; int rv; @@ -388,7 +391,7 @@ int ngtcp2_crypto_read_write_crypto_data( if (!ngtcp2_conn_is_server(conn) && cptls->handshake_properties.client.early_data_acceptance == - PTLS_EARLY_DATA_REJECTED) { + PTLS_EARLY_DATA_REJECTED) { rv = ngtcp2_conn_tls_early_data_rejected(conn); if (rv != 0) { rv = -1; @@ -405,8 +408,8 @@ int ngtcp2_crypto_read_write_crypto_data( assert(i != 1); if (ngtcp2_conn_submit_crypto_data( - conn, ngtcp2_crypto_picotls_from_epoch(i), - sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) { + conn, ngtcp2_crypto_picotls_from_epoch(i), + sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) { rv = -1; goto fin; } @@ -463,7 +466,7 @@ ngtcp2_encryption_level ngtcp2_crypto_picotls_from_epoch(size_t epoch) { } size_t ngtcp2_crypto_picotls_from_ngtcp2_encryption_level( - ngtcp2_encryption_level encryption_level) { + ngtcp2_encryption_level encryption_level) { switch (encryption_level) { case NGTCP2_ENCRYPTION_LEVEL_INITIAL: return 0; @@ -532,8 +535,8 @@ static int set_additional_extensions(ptls_handshake_properties_t *hsprops, } int ngtcp2_crypto_picotls_collect_extension( - ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, - uint16_t type) { + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + uint16_t type) { (void)ptls; (void)properties; @@ -541,8 +544,8 @@ int ngtcp2_crypto_picotls_collect_extension( } int ngtcp2_crypto_picotls_collected_extensions( - ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, - ptls_raw_extension_t *extensions) { + ptls_t *ptls, struct st_ptls_handshake_properties_t *properties, + ptls_raw_extension_t *extensions) { ngtcp2_crypto_conn_ref *conn_ref; ngtcp2_conn *conn; int rv; @@ -558,7 +561,7 @@ int ngtcp2_crypto_picotls_collected_extensions( conn = conn_ref->get_conn(conn_ref); rv = ngtcp2_conn_decode_and_set_remote_transport_params( - conn, extensions->data.base, extensions->data.len); + conn, extensions->data.base, extensions->data.len); if (rv != 0) { ngtcp2_conn_set_tls_error(conn, rv); return -1; @@ -613,7 +616,7 @@ static int update_traffic_key_server_cb(ptls_update_traffic_key_t *self, } static ptls_update_traffic_key_t update_traffic_key_server = { - update_traffic_key_server_cb, + update_traffic_key_server_cb, }; static int update_traffic_key_cb(ptls_update_traffic_key_t *self, ptls_t *ptls, @@ -661,7 +664,7 @@ int ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx) { } int ngtcp2_crypto_picotls_configure_server_session( - ngtcp2_crypto_picotls_ctx *cptls) { + ngtcp2_crypto_picotls_ctx *cptls) { ptls_handshake_properties_t *hsprops = &cptls->handshake_properties; hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension; @@ -671,7 +674,7 @@ int ngtcp2_crypto_picotls_configure_server_session( } int ngtcp2_crypto_picotls_configure_client_session( - ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) { + ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) { ptls_handshake_properties_t *hsprops = &cptls->handshake_properties; hsprops->client.max_early_data_size = calloc(1, sizeof(size_t)); @@ -692,7 +695,7 @@ int ngtcp2_crypto_picotls_configure_client_session( } void ngtcp2_crypto_picotls_deconfigure_session( - ngtcp2_crypto_picotls_ctx *cptls) { + ngtcp2_crypto_picotls_ctx *cptls) { ptls_handshake_properties_t *hsprops; ptls_raw_extension_t *exts; diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h index 87976b1444b95d..d2a2fe1fe1be0d 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -167,8 +167,12 @@ typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *user_data); * } * * void conn_new() { - * ngtcp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, - * my_realloc_cb}; + * ngtcp2_mem mem = { + * .malloc = my_malloc_cb, + * .free = my_free_cb, + * .calloc = my_calloc_cb, + * .realloc = my_realloc_cb, + * }; * * ... * } diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h index 20c4ac24d2ebcd..bb983f5cf44be3 100644 --- a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -36,7 +36,7 @@ * * Version number of the ngtcp2 library release. */ -#define NGTCP2_VERSION "1.10.0" +#define NGTCP2_VERSION "1.11.0" /** * @macro @@ -46,6 +46,6 @@ * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 * becomes 0x010203. */ -#define NGTCP2_VERSION_NUM 0x010a00 +#define NGTCP2_VERSION_NUM 0x010b00 #endif /* !defined(NGTCP2_VERSION_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c index 0e5bfe069e9fab..776dc0c2c3ef1a 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c @@ -25,6 +25,7 @@ #include "ngtcp2_acktr.h" #include +#include #include "ngtcp2_macro.h" #include "ngtcp2_tstamp.h" @@ -70,6 +71,9 @@ void ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, acktr->flags = NGTCP2_ACKTR_FLAG_NONE; acktr->first_unacked_ts = UINT64_MAX; acktr->rx_npkt = 0; + acktr->max_pkt_num = -1; + acktr->max_pkt_ts = UINT64_MAX; + memset(&acktr->ecn, 0, sizeof(acktr->ecn)); } void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { @@ -180,6 +184,11 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, ngtcp2_acktr_entry_objalloc_del(delent, &acktr->objalloc); } + if (acktr->max_pkt_num < pkt_num) { + acktr->max_pkt_num = pkt_num; + acktr->max_pkt_ts = ts; + } + return 0; } @@ -323,3 +332,108 @@ int ngtcp2_acktr_require_active_ack(const ngtcp2_acktr *acktr, void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK; } + +ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, + ngtcp2_frame *fr, uint8_t type, + ngtcp2_tstamp ts, + ngtcp2_duration ack_delay, + uint64_t ack_delay_exponent) { + int64_t last_pkt_num; + ngtcp2_ack_range *range; + ngtcp2_ksl_it it; + ngtcp2_acktr_entry *rpkt; + ngtcp2_ack *ack = &fr->ack; + ngtcp2_tstamp largest_ack_ts; + size_t num_acks; + + if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) { + ack_delay = 0; + } + + if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) { + return NULL; + } + + it = ngtcp2_acktr_get(acktr); + if (ngtcp2_ksl_it_end(&it)) { + ngtcp2_acktr_commit_ack(acktr); + return NULL; + } + + num_acks = ngtcp2_ksl_len(&acktr->ents); + + if (acktr->ecn.ect0 || acktr->ecn.ect1 || acktr->ecn.ce) { + ack->type = NGTCP2_FRAME_ACK_ECN; + ack->ecn.ect0 = acktr->ecn.ect0; + ack->ecn.ect1 = acktr->ecn.ect1; + ack->ecn.ce = acktr->ecn.ce; + } else { + ack->type = NGTCP2_FRAME_ACK; + } + ack->rangecnt = 0; + + rpkt = ngtcp2_ksl_it_get(&it); + + if (rpkt->pkt_num == acktr->max_pkt_num) { + last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); + largest_ack_ts = rpkt->tstamp; + ack->largest_ack = rpkt->pkt_num; + ack->first_ack_range = rpkt->len - 1; + + ngtcp2_ksl_it_next(&it); + --num_acks; + } else if (rpkt->pkt_num + 1 == acktr->max_pkt_num) { + last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); + largest_ack_ts = acktr->max_pkt_ts; + ack->largest_ack = acktr->max_pkt_num; + ack->first_ack_range = rpkt->len; + + ngtcp2_ksl_it_next(&it); + --num_acks; + } else { + assert(rpkt->pkt_num < acktr->max_pkt_num); + + last_pkt_num = acktr->max_pkt_num; + largest_ack_ts = acktr->max_pkt_ts; + ack->largest_ack = acktr->max_pkt_num; + ack->first_ack_range = 0; + } + + if (type == NGTCP2_PKT_1RTT) { + ack->ack_delay_unscaled = ts - largest_ack_ts; + ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / + (1ULL << ack_delay_exponent); + } else { + ack->ack_delay_unscaled = 0; + ack->ack_delay = 0; + } + + num_acks = ngtcp2_min_size(num_acks, NGTCP2_MAX_ACK_RANGES); + + for (; ack->rangecnt < num_acks; ngtcp2_ksl_it_next(&it)) { + rpkt = ngtcp2_ksl_it_get(&it); + + range = &ack->ranges[ack->rangecnt++]; + range->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2); + range->len = rpkt->len - 1; + + last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); + } + + return fr; +} + +void ngtcp2_acktr_increase_ecn_counts(ngtcp2_acktr *acktr, + const ngtcp2_pkt_info *pi) { + switch (pi->ecn & NGTCP2_ECN_MASK) { + case NGTCP2_ECN_ECT_0: + ++acktr->ecn.ect0; + break; + case NGTCP2_ECN_ECT_1: + ++acktr->ecn.ect1; + break; + case NGTCP2_ECN_CE: + ++acktr->ecn.ce; + break; + } +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h index 16aee42f275f67..cf75a774db3e82 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h @@ -128,6 +128,26 @@ typedef struct ngtcp2_acktr { /* rx_npkt is the number of ACK eliciting packets received without sending ACK. */ size_t rx_npkt; + /* max_pkt_num is the largest packet number received so far. */ + int64_t max_pkt_num; + /* max_pkt_ts is the timestamp when max_pkt_num packet is + received. */ + ngtcp2_tstamp max_pkt_ts; + + struct { + /* ect0, ect1, and ce are the number of QUIC packets received + with those markings. */ + size_t ect0; + size_t ect1; + size_t ce; + struct { + /* ect0, ect1, ce are the ECN counts received in the latest + ACK frame. */ + uint64_t ect0; + uint64_t ect1; + uint64_t ce; + } ack; + } ecn; } ngtcp2_acktr; /* @@ -165,7 +185,7 @@ int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent); /* - * ngtcp2_acktr_get returns the pointer to pointer to the entry which + * ngtcp2_acktr_get returns the iterator to pointer to the entry which * has the largest packet number to be acked. If there is no entry, * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. */ @@ -213,4 +233,26 @@ int ngtcp2_acktr_require_active_ack(const ngtcp2_acktr *acktr, */ void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr); +/* + * ngtcp2_acktr_create_ack_frame creates ACK frame in the object + * pointed by |fr|, and returns |fr| if there are any received packets + * to acknowledge. If there are no packets to acknowledge, this + * function returns NULL. fr->ack.ranges must be able to contain at + * least NGTCP2_MAX_ACK_RANGES elements. + * + * Call ngtcp2_acktr_commit_ack after a created ACK frame is + * successfully serialized into a packet. + */ +ngtcp2_frame *ngtcp2_acktr_create_ack_frame(ngtcp2_acktr *acktr, + ngtcp2_frame *fr, uint8_t type, + ngtcp2_tstamp ts, + ngtcp2_duration ack_delay, + uint64_t ack_delay_exponent); + +/* + * ngtcp2_acktr_increase_ecn_counts increases ECN counts from |pi|. + */ +void ngtcp2_acktr_increase_ecn_counts(ngtcp2_acktr *acktr, + const ngtcp2_pkt_info *pi); + #endif /* !defined(NGTCP2_ACKTR_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c index f389abe76d71c8..1fb273d494e8e2 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c @@ -51,8 +51,10 @@ void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const ngtcp2_sockaddr *addr, } } -static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { - assert(a->sa_family == b->sa_family); +int ngtcp2_sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { + if (a->sa_family != b->sa_family) { + return 0; + } switch (a->sa_family) { case NGTCP2_AF_INET: { @@ -73,17 +75,16 @@ static int sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b) { } int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b) { - return a->addr->sa_family == b->addr->sa_family && - sockaddr_eq(a->addr, b->addr); + return ngtcp2_sockaddr_eq(a->addr, b->addr); } -uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { - uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE; +uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { + uint32_t flags = NGTCP2_ADDR_CMP_FLAG_NONE; const ngtcp2_sockaddr *a = aa->addr; const ngtcp2_sockaddr *b = bb->addr; if (a->sa_family != b->sa_family) { - return NGTCP2_ADDR_COMPARE_FLAG_FAMILY; + return NGTCP2_ADDR_CMP_FLAG_FAMILY; } switch (a->sa_family) { @@ -91,10 +92,10 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { const ngtcp2_sockaddr_in *ai = (const ngtcp2_sockaddr_in *)(void *)a, *bi = (const ngtcp2_sockaddr_in *)(void *)b; if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) { - flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; + flags |= NGTCP2_ADDR_CMP_FLAG_ADDR; } if (ai->sin_port != bi->sin_port) { - flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT; + flags |= NGTCP2_ADDR_CMP_FLAG_PORT; } return flags; } @@ -102,10 +103,10 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { const ngtcp2_sockaddr_in6 *ai = (const ngtcp2_sockaddr_in6 *)(void *)a, *bi = (const ngtcp2_sockaddr_in6 *)(void *)b; if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) { - flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; + flags |= NGTCP2_ADDR_CMP_FLAG_ADDR; } if (ai->sin6_port != bi->sin6_port) { - flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT; + flags |= NGTCP2_ADDR_CMP_FLAG_PORT; } return flags; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h index 65ee7cd9f3006b..c2224f85cd9c11 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h @@ -45,22 +45,21 @@ void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); */ int ngtcp2_addr_eq(const ngtcp2_addr *a, const ngtcp2_addr *b); -/* NGTCP2_ADDR_COMPARE_FLAG_NONE indicates that no flag set. */ -#define NGTCP2_ADDR_COMPARE_FLAG_NONE 0x0u -/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not +/* NGTCP2_ADDR_CMP_FLAG_NONE indicates that no flag set. */ +#define NGTCP2_ADDR_CMP_FLAG_NONE 0x0u +/* NGTCP2_ADDR_CMP_FLAG_ADDR indicates IP addresses do not match. */ +#define NGTCP2_ADDR_CMP_FLAG_ADDR 0x1u +/* NGTCP2_ADDR_CMP_FLAG_PORT indicates ports do not match. */ +#define NGTCP2_ADDR_CMP_FLAG_PORT 0x2u +/* NGTCP2_ADDR_CMP_FLAG_FAMILY indicates address families do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1u -/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2u -/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not - match. */ -#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4u +#define NGTCP2_ADDR_CMP_FLAG_FAMILY 0x4u /* - * ngtcp2_addr_compare compares address and port between |a| and |b|, - * and returns zero or more of NGTCP2_ADDR_COMPARE_FLAG_*. + * ngtcp2_addr_cmp compares address and port between |a| and |b|, and + * returns zero or more of NGTCP2_ADDR_CMP_FLAG_*. */ -uint32_t ngtcp2_addr_compare(const ngtcp2_addr *a, const ngtcp2_addr *b); +uint32_t ngtcp2_addr_cmp(const ngtcp2_addr *a, const ngtcp2_addr *b); /* * ngtcp2_addr_empty returns nonzero if |addr| has zero length @@ -68,4 +67,11 @@ uint32_t ngtcp2_addr_compare(const ngtcp2_addr *a, const ngtcp2_addr *b); */ int ngtcp2_addr_empty(const ngtcp2_addr *addr); +/** + * @function + * + * `ngtcp2_sockaddr_eq` returns nonzero if |a| equals |b|. + */ +int ngtcp2_sockaddr_eq(const ngtcp2_sockaddr *a, const ngtcp2_sockaddr *b); + #endif /* !defined(NGTCP2_ADDR_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c index 8777ca4c8a1632..a20f04521e36ca 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.c @@ -39,7 +39,7 @@ #define NGTCP2_BBR_EXTRA_ACKED_FILTERLEN 10 #define NGTCP2_BBR_STARTUP_PACING_GAIN_H 277 -#define NGTCP2_BBR_DRAIN_PACING_GAIN_H 35 +#define NGTCP2_BBR_DRAIN_PACING_GAIN_H 50 #define NGTCP2_BBR_DEFAULT_CWND_GAIN_H 200 @@ -130,6 +130,8 @@ static void bbr_start_round(ngtcp2_cc_bbr *bbr); static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr); +static int bbr_is_probing_bw(ngtcp2_cc_bbr *bbr); + static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_ack *ack, @@ -137,8 +139,8 @@ static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, static void bbr_enter_drain(ngtcp2_cc_bbr *bbr); -static void bbr_check_drain(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts); +static void bbr_check_drain_done(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); static void bbr_enter_probe_bw(ngtcp2_cc_bbr *bbr, ngtcp2_tstamp ts); @@ -277,7 +279,8 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, ngtcp2_window_filter_init(&bbr->extra_acked_filter, NGTCP2_BBR_EXTRA_ACKED_FILTERLEN); - bbr->min_rtt = UINT64_MAX; + bbr->min_rtt = + cstat->first_rtt_sample_ts == UINT64_MAX ? UINT64_MAX : cstat->smoothed_rtt; bbr->min_rtt_stamp = initial_ts; /* remark: Use UINT64_MAX instead of 0 for consistency. */ bbr->probe_rtt_done_stamp = UINT64_MAX; @@ -335,6 +338,8 @@ static void bbr_on_init(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, bbr->max_inflight = 0; bbr->congestion_recovery_start_ts = UINT64_MAX; + + bbr->bdp = 0; } static void bbr_reset_congestion_signals(ngtcp2_cc_bbr *bbr) { @@ -404,8 +409,10 @@ static void bbr_check_startup_high_loss(ngtcp2_cc_bbr *bbr) { } static void bbr_init_pacing_rate(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - cstat->pacing_interval = NGTCP2_MILLISECONDS * 100 / - NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd; + cstat->pacing_interval = + (cstat->first_rtt_sample_ts == UINT64_MAX ? NGTCP2_MILLISECONDS + : cstat->smoothed_rtt) * + 100 / NGTCP2_BBR_STARTUP_PACING_GAIN_H / bbr->initial_cwnd; } static void bbr_set_pacing_rate_with_gain(ngtcp2_cc_bbr *bbr, @@ -465,7 +472,7 @@ static void bbr_update_model_and_state(ngtcp2_cc_bbr *bbr, bbr_update_ack_aggregation(bbr, cstat, ack, ts); bbr_check_full_bw_reached(bbr, cstat); bbr_check_startup_done(bbr); - bbr_check_drain(bbr, cstat, ts); + bbr_check_drain_done(bbr, cstat, ts); bbr_update_probe_bw_cycle_phase(bbr, cstat, ack, ts); bbr_update_min_rtt(bbr, ack, ts); bbr_check_probe_rtt(bbr, cstat, ts); @@ -528,7 +535,7 @@ static void bbr_update_congestion_signals(ngtcp2_cc_bbr *bbr, static void bbr_adapt_lower_bounds_from_congestion(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - if (bbr_is_in_probe_bw_state(bbr)) { + if (bbr_is_probing_bw(bbr)) { return; } @@ -607,6 +614,17 @@ static int bbr_is_in_probe_bw_state(ngtcp2_cc_bbr *bbr) { } } +static int bbr_is_probing_bw(ngtcp2_cc_bbr *bbr) { + switch (bbr->state) { + case NGTCP2_BBR_STATE_STARTUP: + case NGTCP2_BBR_STATE_PROBE_BW_REFILL: + case NGTCP2_BBR_STATE_PROBE_BW_UP: + return 1; + default: + return 0; + } +} + static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, const ngtcp2_cc_ack *ack, @@ -622,8 +640,13 @@ static void bbr_update_ack_aggregation(ngtcp2_cc_bbr *bbr, } bbr->extra_acked_delivered += ack->bytes_delivered; - extra = bbr->extra_acked_delivered - expected_delivered; - extra = ngtcp2_min_uint64(extra, cstat->cwnd); + + if (bbr->extra_acked_delivered <= expected_delivered) { + extra = 0; + } else { + extra = bbr->extra_acked_delivered - expected_delivered; + extra = ngtcp2_min_uint64(extra, cstat->cwnd); + } if (bbr->full_bw_reached) { bbr->extra_acked_filter.window_length = NGTCP2_BBR_EXTRA_ACKED_FILTERLEN; @@ -645,8 +668,8 @@ static void bbr_enter_drain(ngtcp2_cc_bbr *bbr) { bbr->cwnd_gain_h = NGTCP2_BBR_DEFAULT_CWND_GAIN_H; } -static void bbr_check_drain(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, - ngtcp2_tstamp ts) { +static void bbr_check_drain_done(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { if (bbr->state == NGTCP2_BBR_STATE_DRAIN && cstat->bytes_in_flight <= bbr_inflight(bbr, cstat, 100)) { bbr_enter_probe_bw(bbr, ts); @@ -825,8 +848,7 @@ static void bbr_raise_inflight_hi_slope(ngtcp2_cc_bbr *bbr, << bbr->bw_probe_up_rounds; bbr->bw_probe_up_rounds = ngtcp2_min_size(bbr->bw_probe_up_rounds + 1, 30); - bbr->probe_up_cnt = ngtcp2_max_uint64(cstat->cwnd / growth_this_round, 1) * - cstat->max_tx_udp_payload_size; + bbr->probe_up_cnt = ngtcp2_max_uint64(cstat->cwnd / growth_this_round, 1); } static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, @@ -840,10 +862,12 @@ static void bbr_probe_inflight_hi_upward(ngtcp2_cc_bbr *bbr, bbr->bw_probe_up_acks += ack->bytes_delivered; - if (bbr->bw_probe_up_acks >= bbr->probe_up_cnt) { + if (bbr->probe_up_cnt != UINT64_MAX && + bbr->bw_probe_up_acks >= + bbr->probe_up_cnt * cstat->max_tx_udp_payload_size) { delta = bbr->bw_probe_up_acks / bbr->probe_up_cnt; bbr->bw_probe_up_acks -= delta * bbr->probe_up_cnt; - bbr->inflight_hi += delta * cstat->max_tx_udp_payload_size; + bbr->inflight_hi += delta; } if (bbr->round_start) { @@ -901,8 +925,7 @@ static void bbr_pick_probe_wait(ngtcp2_cc_bbr *bbr) { bbr->rand(&rand, 1, &bbr->rand_ctx); - bbr->bw_probe_wait = - 2 * NGTCP2_SECONDS + (ngtcp2_tstamp)(NGTCP2_SECONDS * rand / 255); + bbr->bw_probe_wait = 2 * NGTCP2_SECONDS + NGTCP2_SECONDS * rand / 255; } static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, @@ -915,9 +938,7 @@ static int bbr_is_reno_coexistence_probe_time(ngtcp2_cc_bbr *bbr, static uint64_t bbr_target_inflight(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat) { - uint64_t bdp = bbr_bdp_multiple(bbr, bbr->cwnd_gain_h); - - return ngtcp2_min_uint64(bdp, cstat->cwnd); + return ngtcp2_min_uint64(bbr->bdp, cstat->cwnd); } static int bbr_check_inflight_too_high(ngtcp2_cc_bbr *bbr, @@ -957,12 +978,11 @@ static void bbr_handle_inflight_too_high(ngtcp2_cc_bbr *bbr, } static void bbr_note_loss(ngtcp2_cc_bbr *bbr) { - if (bbr->loss_in_round) { - return; + if (!bbr->loss_in_round) { + bbr->loss_round_delivered = bbr->rst->delivered; } bbr->loss_in_round = 1; - bbr->loss_round_delivered = bbr->rst->delivered; } static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, @@ -977,6 +997,7 @@ static void bbr_handle_lost_packet(ngtcp2_cc_bbr *bbr, ngtcp2_conn_stat *cstat, rs.tx_in_flight = pkt->tx_in_flight; /* bbr->rst->lost is not incremented for pkt yet */ + assert(bbr->rst->lost + pkt->pktlen >= pkt->lost); rs.lost = bbr->rst->lost + pkt->pktlen - pkt->lost; rs.is_app_limited = pkt->is_app_limited; @@ -1143,15 +1164,13 @@ static void bbr_handle_restart_from_idle(ngtcp2_cc_bbr *bbr, } static uint64_t bbr_bdp_multiple(ngtcp2_cc_bbr *bbr, uint64_t gain_h) { - uint64_t bdp; - if (bbr->min_rtt == UINT64_MAX) { return bbr->initial_cwnd; } - bdp = ngtcp2_max_uint64(bbr->bw * bbr->min_rtt / NGTCP2_SECONDS, 1); + bbr->bdp = ngtcp2_max_uint64(bbr->bw * bbr->min_rtt / NGTCP2_SECONDS, 1); - return (uint64_t)(bdp * gain_h / 100); + return (uint64_t)(bbr->bdp * gain_h / 100); } static uint64_t min_pipe_cwnd(size_t max_udp_payload_size) { @@ -1324,10 +1343,6 @@ static void bbr_cc_on_pkt_lost(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts) { ngtcp2_cc_bbr *bbr = ngtcp2_struct_of(cc, ngtcp2_cc_bbr, cc); - if (bbr->state == NGTCP2_BBR_STATE_STARTUP) { - return; - } - bbr_update_on_loss(bbr, cstat, pkt, ts); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h index f266ec5d71e4e4..74eb2d640bd3f2 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_bbr.h @@ -131,6 +131,7 @@ typedef struct ngtcp2_cc_bbr { uint64_t round_count_at_recovery; uint64_t max_inflight; ngtcp2_tstamp congestion_recovery_start_ts; + uint64_t bdp; } ngtcp2_cc_bbr; void ngtcp2_cc_bbr_init(ngtcp2_cc_bbr *bbr, ngtcp2_log *log, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c index 1ff59f315c5b66..508a5d9ec1d8a7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c @@ -252,13 +252,15 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, uint64_t w_cubic, w_cubic_next, target, m; ngtcp2_duration rtt_thresh; int round_start; + int is_app_limited = + cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited; if (in_congestion_recovery(cstat, ack->largest_pkt_sent_ts)) { return; } if (cubic->current.state == NGTCP2_CUBIC_STATE_CONGESTION_AVOIDANCE) { - if (cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited) { + if (is_app_limited) { if (cubic->current.app_limited_start_ts == UINT64_MAX) { cubic->current.app_limited_start_ts = ts; } @@ -271,7 +273,7 @@ void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, ts - cubic->current.app_limited_start_ts; cubic->current.app_limited_start_ts = UINT64_MAX; } - } else if (cubic->rst->rs.is_app_limited && !cubic->rst->is_cwnd_limited) { + } else if (is_app_limited) { return; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c index 181850cfcbc87a..8bff06c0c1f2da 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -141,8 +141,10 @@ int ngtcp2_dcid_verify_uniqueness(const ngtcp2_dcid *dcid, uint64_t seq, } int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, + const ngtcp2_path *path, const uint8_t *token) { - return (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && + return ngtcp2_path_eq(&dcid->ps.path, path) && + (dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT) && ngtcp2_cmemeq(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN) ? 0 : NGTCP2_ERR_INVALID_ARGUMENT; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h index 6372ef113d6454..c6ab16831a38c4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h @@ -149,7 +149,9 @@ void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src); /* * ngtcp2_dcid_copy_cid_token behaves like ngtcp2_dcid_copy, but it - * only copies cid, seq, and token. + * only copies cid, seq, and token. dest->flags should be initialized + * before this call because NGTCP2_DCID_FLAG_TOKEN_PRESENT is set or + * unset. */ void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src); @@ -162,15 +164,16 @@ int ngtcp2_dcid_verify_uniqueness(const ngtcp2_dcid *dcid, uint64_t seq, /* * ngtcp2_dcid_verify_stateless_reset_token verifies stateless reset - * token |token| against the one included in |dcid|. Tokens are - * compared in constant time. This function returns 0 if the - * verification succeeds, or one of the following negative error - * codes: + * token |token| received on |path| against the one included in + * |dcid|. Tokens are compared in constant time. This function + * returns 0 if the verification succeeds, or one of the following + * negative error codes: * * NGTCP2_ERR_INVALID_ARGUMENT * Tokens do not match; or |dcid| does not contain a token. */ int ngtcp2_dcid_verify_stateless_reset_token(const ngtcp2_dcid *dcid, + const ngtcp2_path *path, const uint8_t *token); #endif /* !defined(NGTCP2_CID_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c index 765e4a877e00d7..59eb90f6ea1afa 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -297,13 +297,13 @@ static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_pv *pv, flags |= NGTCP2_PATH_VALIDATION_FLAG_PREFERRED_ADDR; } - if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { old_path = &pv->fallback_dcid.ps.path; } if (conn->server && old_path && - (ngtcp2_addr_compare(&pv->dcid.ps.path.remote, &old_path->remote) & - (NGTCP2_ADDR_COMPARE_FLAG_ADDR | NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) { + (ngtcp2_addr_cmp(&pv->dcid.ps.path.remote, &old_path->remote) & + (NGTCP2_ADDR_CMP_FLAG_ADDR | NGTCP2_ADDR_CMP_FLAG_FAMILY))) { flags |= NGTCP2_PATH_VALIDATION_FLAG_NEW_TOKEN; } @@ -679,7 +679,6 @@ static void pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, pktns->tx.last_pkt_num = initial_pkt_num - 1; pktns->tx.non_ack_pkt_start_ts = UINT64_MAX; - pktns->rx.max_pkt_num = -1; pktns->rx.max_ack_eliciting_pkt_num = -1; pktns->id = pktns_id; @@ -1073,9 +1072,11 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, assert(settings->max_tx_udp_payload_size); assert(settings->max_tx_udp_payload_size <= NGTCP2_HARD_MAX_UDP_PAYLOAD_SIZE); assert(settings->initial_pkt_num <= INT32_MAX); + assert(settings->initial_rtt); assert(params->active_connection_id_limit >= NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT); - assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); + assert(params->active_connection_id_limit <= + NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE); assert(params->initial_max_data <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_local <= NGTCP2_MAX_VARINT); assert(params->initial_max_stream_data_bidi_remote <= NGTCP2_MAX_VARINT); @@ -1157,11 +1158,7 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_objalloc_rtb_entry_init(&(*pconn)->rtb_entry_objalloc, 16, mem); ngtcp2_objalloc_strm_init(&(*pconn)->strm_objalloc, 16, mem); - ngtcp2_static_ringbuf_dcid_bound_init(&(*pconn)->dcid.bound); - - ngtcp2_static_ringbuf_dcid_unused_init(&(*pconn)->dcid.unused); - - ngtcp2_static_ringbuf_dcid_retired_init(&(*pconn)->dcid.retired); + ngtcp2_dcidtr_init(&(*pconn)->dcid.dtr); ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); @@ -1218,6 +1215,11 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); ngtcp2_dcid_set_path(&(*pconn)->dcid.current, path); + assert((size_t)path->local.addrlen <= sizeof((*pconn)->hs_local_addr)); + + memcpy(&(*pconn)->hs_local_addr, path->local.addr, + (size_t)path->local.addrlen); + rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1); if (rv != 0) { goto fail_seqgap_push; @@ -1380,10 +1382,12 @@ static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, (*pconn)->mem = mem; (*pconn)->user_data = user_data; (*pconn)->idle_ts = settings->initial_ts; + (*pconn)->handshake_confirmed_ts = UINT64_MAX; (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; (*pconn)->tx.last_max_data_ts = UINT64_MAX; (*pconn)->tx.pacing.next_ts = UINT64_MAX; (*pconn)->tx.last_blocked_offset = UINT64_MAX; + (*pconn)->rx.preferred_addr.pkt_num = -1; (*pconn)->early.discard_started_ts = UINT64_MAX; conn_reset_ecn_validation_state(*pconn); @@ -1608,7 +1612,6 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_idtr_free(&conn->remote.uni.idtr); ngtcp2_idtr_free(&conn->remote.bidi.idtr); - ngtcp2_mem_free(conn->mem, conn->tx.ack); ngtcp2_pq_free(&conn->tx.strmq); ngtcp2_map_each(&conn->strms, delete_strms_each, (void *)conn); ngtcp2_map_free(&conn->strms); @@ -1625,40 +1628,6 @@ void ngtcp2_conn_del(ngtcp2_conn *conn) { ngtcp2_mem_free(conn->mem, conn); } -/* - * conn_ensure_ack_ranges makes sure that conn->tx.ack->ack.ranges can - * contain at least |n| additional ngtcp2_ack_range. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_ensure_ack_ranges(ngtcp2_conn *conn, size_t n) { - ngtcp2_frame *fr; - size_t max = conn->tx.max_ack_ranges; - - if (n <= max) { - return 0; - } - - max *= 2; - - assert(max >= n); - - fr = ngtcp2_mem_realloc(conn->mem, conn->tx.ack, - sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_range) * max); - if (fr == NULL) { - return NGTCP2_ERR_NOMEM; - } - - conn->tx.ack = fr; - conn->tx.max_ack_ranges = max; - - return 0; -} - /* * conn_compute_ack_delay computes ACK delay for outgoing protected * ACK. @@ -1669,124 +1638,6 @@ static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) { ngtcp2_max_uint64(conn->cstat.smoothed_rtt / 8, NGTCP2_NANOSECONDS)); } -int ngtcp2_conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, - ngtcp2_pktns *pktns, uint8_t type, - ngtcp2_tstamp ts, ngtcp2_duration ack_delay, - uint64_t ack_delay_exponent) { - /* TODO Measure an actual size of ACK blocks to find the best - default value. */ - const size_t initial_max_ack_ranges = 8; - int64_t last_pkt_num; - ngtcp2_acktr *acktr = &pktns->acktr; - ngtcp2_ack_range *range; - ngtcp2_ksl_it it; - ngtcp2_acktr_entry *rpkt; - ngtcp2_ack *ack; - size_t range_idx; - ngtcp2_tstamp largest_ack_ts; - int rv; - - if (acktr->flags & NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK) { - ack_delay = 0; - } - - if (!ngtcp2_acktr_require_active_ack(acktr, ack_delay, ts)) { - return 0; - } - - it = ngtcp2_acktr_get(acktr); - if (ngtcp2_ksl_it_end(&it)) { - ngtcp2_acktr_commit_ack(acktr); - return 0; - } - - if (conn->tx.ack == NULL) { - conn->tx.ack = ngtcp2_mem_malloc( - conn->mem, - sizeof(ngtcp2_ack) + sizeof(ngtcp2_ack_range) * initial_max_ack_ranges); - if (conn->tx.ack == NULL) { - return NGTCP2_ERR_NOMEM; - } - conn->tx.max_ack_ranges = initial_max_ack_ranges; - } - - ack = &conn->tx.ack->ack; - - if (pktns->rx.ecn.ect0 || pktns->rx.ecn.ect1 || pktns->rx.ecn.ce) { - ack->type = NGTCP2_FRAME_ACK_ECN; - ack->ecn.ect0 = pktns->rx.ecn.ect0; - ack->ecn.ect1 = pktns->rx.ecn.ect1; - ack->ecn.ce = pktns->rx.ecn.ce; - } else { - ack->type = NGTCP2_FRAME_ACK; - } - ack->rangecnt = 0; - - rpkt = ngtcp2_ksl_it_get(&it); - - if (rpkt->pkt_num == pktns->rx.max_pkt_num) { - last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); - largest_ack_ts = rpkt->tstamp; - ack->largest_ack = rpkt->pkt_num; - ack->first_ack_range = rpkt->len - 1; - - ngtcp2_ksl_it_next(&it); - } else if (rpkt->pkt_num + 1 == pktns->rx.max_pkt_num) { - last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); - largest_ack_ts = pktns->rx.max_pkt_ts; - ack->largest_ack = pktns->rx.max_pkt_num; - ack->first_ack_range = rpkt->len; - - ngtcp2_ksl_it_next(&it); - } else { - assert(rpkt->pkt_num < pktns->rx.max_pkt_num); - - last_pkt_num = pktns->rx.max_pkt_num; - largest_ack_ts = pktns->rx.max_pkt_ts; - ack->largest_ack = pktns->rx.max_pkt_num; - ack->first_ack_range = 0; - } - - if (type == NGTCP2_PKT_1RTT) { - ack->ack_delay_unscaled = ts - largest_ack_ts; - ack->ack_delay = ack->ack_delay_unscaled / NGTCP2_MICROSECONDS / - (1ULL << ack_delay_exponent); - } else { - ack->ack_delay_unscaled = 0; - ack->ack_delay = 0; - } - - for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { - if (ack->rangecnt == NGTCP2_MAX_ACK_RANGES) { - break; - } - - rpkt = ngtcp2_ksl_it_get(&it); - - range_idx = ack->rangecnt++; - rv = conn_ensure_ack_ranges(conn, ack->rangecnt); - if (rv != 0) { - return rv; - } - ack = &conn->tx.ack->ack; - range = &ack->ranges[range_idx]; - range->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2); - range->len = rpkt->len - 1; - - last_pkt_num = rpkt->pkt_num - (int64_t)(rpkt->len - 1); - } - - /* TODO Just remove entries which cannot fit into a single ACK frame - for now. */ - if (!ngtcp2_ksl_it_end(&it)) { - ngtcp2_acktr_forget(acktr, ngtcp2_ksl_it_get(&it)); - } - - *pfr = conn->tx.ack; - - return 0; -} - /* * conn_ppe_write_frame writes |fr| to |ppe|. If |hd_logged| is not * NULL and |*hd_logged| is zero, packet header is logged, and 1 is @@ -1894,22 +1745,13 @@ static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { return 1; } -/* - * conn_get_cwnd returns cwnd for the current path. - */ -static uint64_t conn_get_cwnd(ngtcp2_conn *conn) { - return conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) - ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_tx_udp_payload_size) - : conn->cstat.cwnd; -} - /* * conn_cwnd_is_zero returns nonzero if the number of bytes the local * endpoint can sent at this time is zero. */ static int conn_cwnd_is_zero(ngtcp2_conn *conn) { uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; - uint64_t cwnd = conn_get_cwnd(conn); + uint64_t cwnd = conn->cstat.cwnd; if (bytes_in_flight >= cwnd) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_LDC, @@ -2145,33 +1987,36 @@ void ngtcp2_conn_set_keep_alive_timeout(ngtcp2_conn *conn, conn->keep_alive.timeout = timeout; } -/* - * NGTCP2_PKT_PACING_OVERHEAD defines overhead of userspace event - * loop. Packet pacing might require sub milliseconds packet spacing, - * but userspace event loop might not offer such precision. - * Typically, if delay is 0.5 microseconds, the actual delay after - * which we can send packet is well over 1 millisecond when event loop - * is involved (which includes other stuff, like reading packets etc - * in a typical single threaded use case). - */ -#define NGTCP2_PKT_PACING_OVERHEAD NGTCP2_MILLISECONDS - static void conn_cancel_expired_pkt_tx_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { if (conn->tx.pacing.next_ts == UINT64_MAX) { return; } - if (conn->tx.pacing.next_ts > ts + NGTCP2_PKT_PACING_OVERHEAD) { + if (conn->tx.pacing.next_ts > ts) { return; } + if (ts > conn->tx.pacing.next_ts) { + conn->tx.pacing.compensation += ts - conn->tx.pacing.next_ts; + } + conn->tx.pacing.next_ts = UINT64_MAX; } static int conn_pacing_pkt_tx_allowed(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - return conn->tx.pacing.next_ts == UINT64_MAX || - conn->tx.pacing.next_ts <= ts + NGTCP2_PKT_PACING_OVERHEAD; + if (conn->tx.pacing.next_ts == UINT64_MAX) { + return 1; + } + + if (conn->tx.pacing.next_ts > ts) { + return 0; + } + + conn->tx.pacing.compensation += ts - conn->tx.pacing.next_ts; + conn->tx.pacing.next_ts = UINT64_MAX; + + return 1; } static uint8_t conn_pkt_flags(ngtcp2_conn *conn) { @@ -2225,6 +2070,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, ngtcp2_pkt_hd hd; ngtcp2_frame_chain *frq = NULL, **pfrc = &frq; ngtcp2_frame_chain *nfrc; + ngtcp2_max_frame mfr; ngtcp2_frame *ackfr = NULL, lfr; ngtcp2_ssize spktlen; ngtcp2_crypto_cc cc; @@ -2280,7 +2126,7 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, ngtcp2_pkt_hd_init( &hd, conn_pkt_flags_long(conn), type, &conn->dcid.current.cid, &conn->oscid, - pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), version, 0); + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), version); if (!conn->server && type == NGTCP2_PKT_INITIAL && conn->local.settings.tokenlen) { @@ -2300,14 +2146,9 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, return 0; } - rv = ngtcp2_conn_create_ack_frame(conn, &ackfr, pktns, type, ts, - /* ack_delay = */ 0, - NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); - if (rv != 0) { - ngtcp2_frame_chain_list_objalloc_del(frq, &conn->frc_objalloc, conn->mem); - return rv; - } - + ackfr = ngtcp2_acktr_create_ack_frame(&pktns->acktr, &mfr.fr, type, ts, + /* ack_delay = */ 0, + NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); if (ackfr) { rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr); if (rv != 0) { @@ -2530,12 +2371,12 @@ conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, uint8_t type, ngtcp2_tstamp ts) { - int rv; ngtcp2_frame *ackfr; ngtcp2_pktns *pktns; ngtcp2_duration ack_delay; uint64_t ack_delay_exponent; ngtcp2_ssize spktlen; + ngtcp2_max_frame mfr; assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); @@ -2564,13 +2405,8 @@ static ngtcp2_ssize conn_write_ack_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, return 0; } - ackfr = NULL; - rv = ngtcp2_conn_create_ack_frame(conn, &ackfr, pktns, type, ts, ack_delay, - ack_delay_exponent); - if (rv != 0) { - return rv; - } - + ackfr = ngtcp2_acktr_create_ack_frame(&pktns->acktr, &mfr.fr, type, ts, + ack_delay, ack_delay_exponent); if (!ackfr) { return 0; } @@ -3035,6 +2871,10 @@ static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { return 0; } +static int dcidtr_on_deactivate(const ngtcp2_dcid *dcid, void *user_data) { + return conn_call_deactivate_dcid(user_data, dcid); +} + /* * conn_remove_retired_connection_id removes the already retired * connection ID. It waits PTO before actually removing a connection @@ -3054,7 +2894,6 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_duration timeout = pto; ngtcp2_scid *scid; - ngtcp2_dcid *dcid; int rv; for (; !ngtcp2_pq_empty(&conn->scid.used);) { @@ -3079,18 +2918,10 @@ static int conn_remove_retired_connection_id(ngtcp2_conn *conn, --conn->scid.num_retired; } - for (; ngtcp2_ringbuf_len(&conn->dcid.retired.rb);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); - if (dcid->retired_ts + timeout >= ts) { - break; - } - - rv = conn_call_deactivate_dcid(conn, dcid); - if (rv != 0) { - return rv; - } - - ngtcp2_ringbuf_pop_front(&conn->dcid.retired.rb); + rv = ngtcp2_dcidtr_remove_stale_retired_dcid(&conn->dcid.dtr, timeout, ts, + dcidtr_on_deactivate, conn); + if (rv != 0) { + return rv; } return 0; @@ -3197,6 +3028,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_crypto_cc *cc = &conn->pkt.cc; ngtcp2_ppe *ppe = &conn->pkt.ppe; ngtcp2_pkt_hd *hd = &conn->pkt.hd; + ngtcp2_max_frame mfr; ngtcp2_frame *ackfr = NULL, lfr; ngtcp2_ssize nwrite; ngtcp2_frame_chain **pfrc, *nfrc, *frc; @@ -3348,35 +3180,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, nfrc->fr.max_data.max_data; } - if (stream_blocked && conn_should_send_max_data(conn)) { - rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); - if (rv != 0) { - return rv; - } - - nfrc->fr.type = NGTCP2_FRAME_DATA_BLOCKED; - nfrc->fr.data_blocked.offset = conn->tx.max_offset; - nfrc->next = pktns->tx.frq; - pktns->tx.frq = nfrc; - - conn->tx.last_blocked_offset = conn->tx.max_offset; - } - - if (stream_blocked && !ngtcp2_strm_is_tx_queued(vmsg->stream.strm) && - strm_should_send_stream_data_blocked(vmsg->stream.strm)) { - assert(vmsg); - assert(vmsg->type == NGTCP2_VMSG_TYPE_STREAM); - - vmsg->stream.strm->cycle = conn_tx_strmq_first_cycle(conn); - rv = ngtcp2_conn_tx_strmq_push(conn, vmsg->stream.strm); - if (rv != 0) { - return rv; - } - } - ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, scid, pktns->tx.last_pkt_num + 1, - pktns_select_pkt_numlen(pktns), version, 0); + pktns_select_pkt_numlen(pktns), version); ngtcp2_ppe_init(ppe, dest, destlen, dgram_offset, cc); @@ -3415,14 +3221,9 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, } } - rv = ngtcp2_conn_create_ack_frame( - conn, &ackfr, pktns, type, ts, conn_compute_ack_delay(conn), + ackfr = ngtcp2_acktr_create_ack_frame( + &pktns->acktr, &mfr.fr, type, ts, conn_compute_ack_delay(conn), conn->local.transport_params.ack_delay_exponent); - if (rv != 0) { - assert(ngtcp2_err_is_fatal(rv)); - return rv; - } - if (ackfr) { rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, ackfr); if (rv != 0) { @@ -3907,7 +3708,7 @@ static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, left = ngtcp2_ppe_left(ppe); - if (*pfrc == NULL && send_stream && + if (*pfrc == NULL && send_stream && ngtcp2_pq_empty(&conn->tx.strmq) && (ndatalen = ngtcp2_pkt_stream_max_datalen( vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, left)) != (size_t)-1 && @@ -4342,7 +4143,7 @@ ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( ngtcp2_pkt_hd_init(&hd, hd_flags, type, dcid, scid, pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), - version, 0); + version); ngtcp2_ppe_init(&ppe, dest, destlen, 0, &cc); @@ -4521,31 +4322,20 @@ static int conn_handshake_remnants_left(ngtcp2_conn *conn) { } /* - * conn_retire_dcid_seq retires destination connection ID denoted by - * |seq|. + * conn_enqueue_retire_connection_id enqueues RETIRE_CONNECTION_ID + * frame with |seq|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: * * NGTCP2_ERR_NOMEM * Out of memory. - * NGTCP2_ERR_CONNECTION_ID_LIMIT - * The number of unacknowledged retirement exceeds the limit. */ -static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { +static int conn_enqueue_retire_connection_id(ngtcp2_conn *conn, uint64_t seq) { ngtcp2_pktns *pktns = &conn->pktns; ngtcp2_frame_chain *nfrc; int rv; - if (ngtcp2_conn_check_retired_dcid_tracked(conn, seq)) { - return 0; - } - - rv = ngtcp2_conn_track_retired_dcid_seq(conn, seq); - if (rv != 0) { - return rv; - } - rv = ngtcp2_frame_chain_objalloc_new(&nfrc, &conn->frc_objalloc); if (rv != 0) { return rv; @@ -4559,8 +4349,12 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { return 0; } +static int dcidtr_on_retire(const ngtcp2_dcid *dcid, void *user_data) { + return conn_enqueue_retire_connection_id(user_data, dcid->seq); +} + /* - * conn_retire_dcid retires |dcid|. + * conn_retire_active_dcid retires the activated |dcid|. * * This function returns 0 if it succeeds, or one of the following * negative error codes: @@ -4568,29 +4362,19 @@ static int conn_retire_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { * NGTCP2_ERR_NOMEM * Out of memory */ -static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, - ngtcp2_tstamp ts) { - ngtcp2_ringbuf *rb = &conn->dcid.retired.rb; - ngtcp2_dcid *dest, *stale_dcid; +static int conn_retire_active_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts) { int rv; assert(dcid->cid.datalen); - if (ngtcp2_ringbuf_full(rb)) { - stale_dcid = ngtcp2_ringbuf_get(rb, 0); - rv = conn_call_deactivate_dcid(conn, stale_dcid); - if (rv != 0) { - return rv; - } - - ngtcp2_ringbuf_pop_front(rb); + rv = ngtcp2_dcidtr_retire_active_dcid(&conn->dcid.dtr, dcid, ts, + dcidtr_on_deactivate, conn); + if (rv != 0) { + return rv; } - dest = ngtcp2_ringbuf_push_back(rb); - ngtcp2_dcid_copy(dest, dcid); - dest->retired_ts = ts; - - return conn_retire_dcid_seq(conn, dcid->seq); + return conn_enqueue_retire_connection_id(conn, dcid->seq); } /* @@ -4609,61 +4393,28 @@ static int conn_retire_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, */ static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, const ngtcp2_path *path, ngtcp2_tstamp ts) { - ngtcp2_dcid *dcid, *ndcid; - ngtcp2_cid cid; - size_t i, len; - int rv; - assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)); assert(!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)); - assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + assert(!conn->pv || !(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) || !ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)); - len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); - - if (ngtcp2_path_eq(&dcid->ps.path, path)) { - *pdcid = dcid; - return 0; - } + *pdcid = ngtcp2_dcidtr_find_bound_dcid(&conn->dcid.dtr, path); + if (*pdcid) { + return 0; } if (conn->dcid.current.cid.datalen == 0) { - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); - ngtcp2_cid_zero(&cid); - ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL); - ngtcp2_dcid_set_path(ndcid, path); - - *pdcid = ndcid; + *pdcid = ngtcp2_dcidtr_bind_zerolen_dcid(&conn->dcid.dtr, path); return 0; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - if (ngtcp2_ringbuf_full(&conn->dcid.bound.rb)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0); - rv = conn_retire_dcid(conn, dcid, ts); - if (rv != 0) { - return rv; - } - } - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); - ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound.rb); - - ngtcp2_dcid_copy(ndcid, dcid); - ndcid->bound_ts = ts; - ngtcp2_dcid_set_path(ndcid, path); - - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); - - *pdcid = ndcid; - - return 0; + return ngtcp2_dcidtr_bind_dcid(&conn->dcid.dtr, pdcid, path, ts, + dcidtr_on_retire, conn); } static int conn_start_pmtud(ngtcp2_conn *conn) { @@ -4773,17 +4524,17 @@ static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } if (pv->dcid.cid.datalen && pv->dcid.seq != conn->dcid.current.seq) { - rv = conn_retire_dcid(conn, &pv->dcid, ts); + rv = conn_retire_active_dcid(conn, &pv->dcid, ts); if (rv != 0) { goto fin; } } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && pv->fallback_dcid.cid.datalen && pv->fallback_dcid.seq != conn->dcid.current.seq && pv->fallback_dcid.seq != pv->dcid.seq) { - rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); + rv = conn_retire_active_dcid(conn, &pv->fallback_dcid, ts); if (rv != 0) { goto fin; } @@ -4874,7 +4625,7 @@ static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, } } - if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid); conn_reset_congestion_state(conn, ts); } @@ -5024,7 +4775,7 @@ static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, dcid = &pv->dcid; break; } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && ngtcp2_path_eq(&pv->fallback_dcid.ps.path, &pcent->ps.path)) { dcid = &pv->fallback_dcid; break; @@ -5377,8 +5128,15 @@ static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, */ static void assign_recved_ack_delay_unscaled(ngtcp2_ack *fr, uint64_t ack_delay_exponent) { - fr->ack_delay_unscaled = - fr->ack_delay * (1ULL << ack_delay_exponent) * NGTCP2_MICROSECONDS; + const ngtcp2_tstamp max_ack_delay = ((1 << 14) - 1) * NGTCP2_MILLISECONDS; + uint64_t exp = (1ULL << ack_delay_exponent) * NGTCP2_MICROSECONDS; + + if (fr->ack_delay > max_ack_delay / exp) { + fr->ack_delay_unscaled = max_ack_delay; + return; + } + + fr->ack_delay_unscaled = fr->ack_delay * exp; } /* @@ -5482,6 +5240,17 @@ static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) { conn->tx.max_offset = ngtcp2_max_uint64(conn->tx.max_offset, fr->max_data); } +/* + * should_buffer_1rtt_pkt returns nonzero if 1RTT packet |pkt| of + * length |pktlen| should be buffered. + */ +static int should_buffer_1rtt_pkt(const uint8_t *pkt, size_t pktlen) { + /* A packet starting with 21 bytes zeros are most likely padding + bytes. */ + return pktlen >= NGTCP2_MIN_QUIC_PKTLEN && + (pkt[0] != 0 || memcmp(pkt, pkt + 1, NGTCP2_MIN_QUIC_PKTLEN - 1) != 0); +} + /* * conn_buffer_pkt buffers |pkt| of length |pktlen|, chaining it from * |*ppc|. @@ -5786,15 +5555,52 @@ static void conn_reset_congestion_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->hs_pktns->tx.last_pkt_num + 1); } ngtcp2_rtb_reset_cc_state(&conn->pktns.rtb, conn->pktns.tx.last_pkt_num + 1); - ngtcp2_rst_init(&conn->rst); + ngtcp2_rst_reset(&conn->rst); conn->tx.pacing.next_ts = UINT64_MAX; + conn->tx.pacing.compensation = 0; } -static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, - ngtcp2_tstamp ts) { +/* + * conn_server_preferred_addr_migration returns nonzero if + * |local_addr| equals to one of the preferred addresses. + */ +static int conn_server_preferred_addr_migration(const ngtcp2_conn *conn, + const ngtcp2_addr *local_addr) { + const ngtcp2_preferred_addr *paddr; + + assert(conn->server); + + if (!conn->local.transport_params.preferred_addr_present) { + return 0; + } + + paddr = &conn->local.transport_params.preferred_addr; + + switch (local_addr->addr->sa_family) { + case NGTCP2_AF_INET: + if (!paddr->ipv4_present) { + return 0; + } + + return ngtcp2_sockaddr_eq((const ngtcp2_sockaddr *)&paddr->ipv4, + local_addr->addr); + case NGTCP2_AF_INET6: + if (!paddr->ipv6_present) { + return 0; + } + + return ngtcp2_sockaddr_eq((const ngtcp2_sockaddr *)&paddr->ipv6, + local_addr->addr); + } + + return 0; +} + +static int conn_recv_path_response(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + ngtcp2_path_response *fr, ngtcp2_tstamp ts) { int rv; - ngtcp2_pv *pv = conn->pv, *npv; + ngtcp2_pv *pv = conn->pv, *npv = NULL; uint8_t ent_flags; if (!pv) { @@ -5809,18 +5615,26 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, } if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { - if (pv->dcid.seq != conn->dcid.current.seq) { - assert(!conn->server); - assert(conn->dcid.current.cid.datalen); - - rv = conn_retire_dcid(conn, &conn->dcid.current, ts); - if (rv != 0) { - return rv; + if (pv->dcid.seq != conn->dcid.current.seq || + !ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) { + if (conn->dcid.current.cid.datalen && + pv->dcid.seq != conn->dcid.current.seq) { + rv = conn_retire_active_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + return rv; + } } + ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); + + if (conn->server && conn->rx.preferred_addr.pkt_num == -1 && + conn_server_preferred_addr_migration( + conn, &conn->dcid.current.ps.path.local)) { + conn->rx.preferred_addr.pkt_num = hd->pkt_num; + } } assert(ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)); @@ -5847,46 +5661,45 @@ static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, } } - if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { - if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) { - assert(conn->server); - - /* Validate path again */ - rv = ngtcp2_pv_new(&npv, &pv->dcid, conn_compute_pv_timeout(conn), - NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, &conn->log, - conn->mem); - if (rv != 0) { - return rv; - } + if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) { + assert(conn->server); - npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; - ngtcp2_dcid_copy(&npv->fallback_dcid, &pv->fallback_dcid); - npv->fallback_pto = pv->fallback_pto; - } else { - rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, - conn_compute_pv_timeout_pto(conn, pv->fallback_pto), - NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem); - if (rv != 0) { - return rv; - } + /* Validate path again */ + rv = ngtcp2_pv_new(&npv, &pv->dcid, conn_compute_pv_timeout(conn), + NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); + if (rv != 0) { + return rv; } - /* Unset the flag bit so that conn_stop_pv does not retire - DCID. */ - pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; + npv->dcid.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; - rv = conn_stop_pv(conn, ts); + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { + ngtcp2_pv_set_fallback(npv, &pv->fallback_dcid, pv->fallback_pto); + } + } else if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { + rv = ngtcp2_pv_new(&npv, &pv->fallback_dcid, + conn_compute_pv_timeout_pto(conn, pv->fallback_pto), + NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem); if (rv != 0) { - ngtcp2_pv_del(npv); return rv; } + } - conn->pv = npv; + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) { + /* Unset the flag bit so that conn_stop_pv does not retire + DCID. */ + pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT; + } - return 0; + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + ngtcp2_pv_del(npv); + return rv; } - return conn_stop_pv(conn, ts); + conn->pv = npv; + + return 0; } /* @@ -5899,12 +5712,24 @@ static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) { /* * pktns_commit_recv_pkt_num marks packet number |pkt_num| as - * received. + * received. It stores |pkt_num| and its reception timestamp |ts| in + * order to send its ACK. It also increase ECN counts from |pi|. + * |require_ack| is nonzero if the received packet is ack-eliciting. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_PROTO + * Same packet number has already been added. */ static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num, - int ack_eliciting, ngtcp2_tstamp ts) { - int rv; + const ngtcp2_pkt_info *pi, int require_ack, + ngtcp2_tstamp ts) { + ngtcp2_acktr *acktr = &pktns->acktr; ngtcp2_range r; + int rv; rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1); if (rv != 0) { @@ -5915,7 +5740,7 @@ static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num, ngtcp2_gaptr_drop_first_gap(&pktns->rx.pngap); } - if (ack_eliciting) { + if (require_ack) { if (pktns->rx.max_ack_eliciting_pkt_num != -1) { if (pkt_num < pktns->rx.max_ack_eliciting_pkt_num) { ngtcp2_acktr_immediate_ack(&pktns->acktr); @@ -5934,9 +5759,12 @@ static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num, } } - if (pktns->rx.max_pkt_num < pkt_num) { - pktns->rx.max_pkt_num = pkt_num; - pktns->rx.max_pkt_ts = ts; + ngtcp2_acktr_increase_ecn_counts(acktr, pi); + + rv = ngtcp2_acktr_add(acktr, pkt_num, require_ack, ts); + if (rv != 0) { + assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); + return rv; } return 0; @@ -5954,21 +5782,6 @@ static int verify_token(const uint8_t *token, size_t tokenlen, return NGTCP2_ERR_PROTO; } -static void pktns_increase_ecn_counts(ngtcp2_pktns *pktns, - const ngtcp2_pkt_info *pi) { - switch (pi->ecn & NGTCP2_ECN_MASK) { - case NGTCP2_ECN_ECT_0: - ++pktns->rx.ecn.ect0; - break; - case NGTCP2_ECN_ECT_1: - ++pktns->rx.ecn.ect1; - break; - case NGTCP2_ECN_CE: - ++pktns->rx.ecn.ce; - break; - } -} - /* * vneg_available_versions_includes returns nonzero if * |available_versions| of length |available_versionslen| includes @@ -6111,14 +5924,11 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, int invalid_reserved_bits = 0; size_t num_ack_processed = 0; - if (pktlen == 0) { - return 0; - } - if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { - if (conn->state == NGTCP2_CS_SERVER_INITIAL) { - /* Ignore 1RTT packet unless server's first Handshake packet has - been transmitted. */ + /* Ignore 1RTT packet unless server's first Handshake packet has + been transmitted. */ + if (conn->state == NGTCP2_CS_SERVER_INITIAL || + !should_buffer_1rtt_pkt(pkt, pktlen)) { return (ngtcp2_ssize)pktlen; } @@ -6432,8 +6242,8 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, payload = pkt + hdpktlen; payloadlen = hd.len - hd.pkt_numlen; - hd.pkt_num = - ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, hd.pkt_numlen); + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->acktr.max_pkt_num, hd.pkt_num, + hd.pkt_numlen); if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); @@ -6623,20 +6433,13 @@ conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); - rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); + rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, pi, require_ack, pkt_ts); if (rv != 0) { return rv; } - pktns_increase_ecn_counts(pktns, pi); - /* Initial and Handshake are always acknowledged without delay. No need to call ngtcp2_acktr_immediate_ack(). */ - rv = - ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, pkt_ts); - if (rv != 0) { - return rv; - } conn_restart_timer_on_read(conn, ts); @@ -6710,7 +6513,7 @@ static ngtcp2_ssize conn_recv_handshake_cpkt(ngtcp2_conn *conn, and the current connection state might wrongly discard valid packet and prevent the handshake from completing. */ - if (conn->in_pktns && conn->in_pktns->rx.max_pkt_num == -1) { + if (conn->in_pktns && conn->in_pktns->acktr.max_pkt_num == -1) { return NGTCP2_ERR_DROP_CONN; } @@ -7516,9 +7319,8 @@ static int conn_recv_stop_sending(ngtcp2_conn *conn, static int check_stateless_reset(const ngtcp2_dcid *dcid, const ngtcp2_path *path, const ngtcp2_pkt_stateless_reset *sr) { - return ngtcp2_path_eq(&dcid->ps.path, path) && - ngtcp2_dcid_verify_stateless_reset_token( - dcid, sr->stateless_reset_token) == 0; + return ngtcp2_dcid_verify_stateless_reset_token( + dcid, path, sr->stateless_reset_token) == 0; } /* @@ -7540,11 +7342,9 @@ static int check_stateless_reset(const ngtcp2_dcid *dcid, */ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, const uint8_t *payload, size_t payloadlen) { - int rv = 1; + int rv; ngtcp2_pv *pv = conn->pv; - ngtcp2_dcid *dcid; ngtcp2_pkt_stateless_reset sr; - size_t len, i; rv = ngtcp2_pkt_decode_stateless_reset(&sr, payload, payloadlen); if (rv != 0) { @@ -7553,28 +7353,12 @@ static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, if (!check_stateless_reset(&conn->dcid.current, path, &sr) && (!pv || (!check_stateless_reset(&pv->dcid, path, &sr) && - (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) || !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { - len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); - if (check_stateless_reset(dcid, path, &sr)) { - break; - } - } - - if (i == len) { - len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); - if (check_stateless_reset(dcid, path, &sr)) { - break; - } - } - - if (i == len) { - return NGTCP2_ERR_INVALID_ARGUMENT; - } + rv = ngtcp2_dcidtr_verify_stateless_reset(&conn->dcid.dtr, path, + sr.stateless_reset_token); + if (rv != 0) { + return rv; } } @@ -7624,42 +7408,6 @@ static int conn_recv_max_streams(ngtcp2_conn *conn, return 0; } -static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, - uint64_t retire_prior_to) { - size_t i; - ngtcp2_dcid *dcid, *last; - int rv; - - for (i = 0; i < ngtcp2_ringbuf_len(rb);) { - dcid = ngtcp2_ringbuf_get(rb, i); - if (dcid->seq >= retire_prior_to) { - ++i; - continue; - } - - rv = conn_retire_dcid_seq(conn, dcid->seq); - if (rv != 0) { - return rv; - } - - if (i == 0) { - ngtcp2_ringbuf_pop_front(rb); - continue; - } - - if (i == ngtcp2_ringbuf_len(rb) - 1) { - ngtcp2_ringbuf_pop_back(rb); - break; - } - - last = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); - ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(rb); - } - - return 0; -} - /* * conn_recv_new_connection_id processes the incoming * NEW_CONNECTION_ID frame |fr|. @@ -7673,8 +7421,7 @@ static int conn_retire_dcid_prior_to(ngtcp2_conn *conn, ngtcp2_ringbuf *rb, */ static int conn_recv_new_connection_id(ngtcp2_conn *conn, const ngtcp2_new_connection_id *fr) { - size_t i, len; - ngtcp2_dcid *dcid; + size_t len; ngtcp2_pv *pv = conn->pv; int rv; int found = 0; @@ -7693,60 +7440,32 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, if (rv != 0) { return rv; } - if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) { - found = 1; - } - - if (pv) { - rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid, - fr->stateless_reset_token); - if (rv != 0) { - return rv; - } - if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) { - found = 1; - } + if (ngtcp2_cid_eq(&conn->dcid.current.cid, &fr->cid)) { + found = 1; } - len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); - - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); - rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, + if (pv) { + rv = ngtcp2_dcid_verify_uniqueness(&pv->dcid, fr->seq, &fr->cid, fr->stateless_reset_token); if (rv != 0) { - return NGTCP2_ERR_PROTO; + return rv; } - if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) { + if (ngtcp2_cid_eq(&pv->dcid.cid, &fr->cid)) { found = 1; } } - len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); - - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, i); - rv = ngtcp2_dcid_verify_uniqueness(dcid, fr->seq, &fr->cid, - fr->stateless_reset_token); - if (rv != 0) { - return NGTCP2_ERR_PROTO; - } - if (ngtcp2_cid_eq(&dcid->cid, &fr->cid)) { - found = 1; - } + rv = ngtcp2_dcidtr_verify_token_uniqueness( + &conn->dcid.dtr, &found, fr->seq, &fr->cid, fr->stateless_reset_token); + if (rv != 0) { + return rv; } if (conn->dcid.retire_prior_to < fr->retire_prior_to) { conn->dcid.retire_prior_to = fr->retire_prior_to; - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.bound.rb, - fr->retire_prior_to); - if (rv != 0) { - return rv; - } - - rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused.rb, - conn->dcid.retire_prior_to); + rv = ngtcp2_dcidtr_retire_inactive_dcid_prior_to( + &conn->dcid.dtr, fr->retire_prior_to, dcidtr_on_retire, conn); if (rv != 0) { return rv; } @@ -7759,7 +7478,16 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, For example, a peer might send seq = 50000 and retire_prior_to = 50000. Then send NEW_CONNECTION_ID frames with seq < 50000. */ - return conn_retire_dcid_seq(conn, fr->seq); + if (ngtcp2_dcidtr_check_retired_seq_tracked(&conn->dcid.dtr, fr->seq)) { + return 0; + } + + rv = ngtcp2_dcidtr_track_retired_seq(&conn->dcid.dtr, fr->seq); + if (rv != 0) { + return rv; + } + + return conn_enqueue_retire_connection_id(conn, fr->seq); } if (found) { @@ -7779,7 +7507,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); } - len = ngtcp2_ringbuf_len(&conn->dcid.unused.rb); + len = ngtcp2_dcidtr_inactive_len(&conn->dcid.dtr); if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { ++extra_dcid; @@ -7789,7 +7517,7 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, pv->dcid.seq >= conn->dcid.retire_prior_to) { ++extra_dcid; } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && pv->fallback_dcid.seq != conn->dcid.current.seq && pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) { ++extra_dcid; @@ -7801,13 +7529,13 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, return NGTCP2_ERR_CONNECTION_ID_LIMIT; } - if (len >= NGTCP2_MAX_DCID_POOL_SIZE) { + if (len >= NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "too many connection ID"); return 0; } - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); - ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); + ngtcp2_dcidtr_push_unused(&conn->dcid.dtr, fr->seq, &fr->cid, + fr->stateless_reset_token); return 0; } @@ -7827,32 +7555,32 @@ static int conn_recv_new_connection_id(ngtcp2_conn *conn, static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_pv *pv = conn->pv; - ngtcp2_dcid *dcid; + ngtcp2_dcid dcid; int rv; if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { return 0; } - rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + rv = conn_retire_active_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + ngtcp2_dcidtr_pop_unused_cid_token(&conn->dcid.dtr, &dcid); + if (pv) { if (conn->dcid.current.seq == pv->dcid.seq) { - ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); + ngtcp2_dcid_copy_cid_token(&pv->dcid, &dcid); } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && conn->dcid.current.seq == pv->fallback_dcid.seq) { - ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); + ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, &dcid); } } - ngtcp2_dcid_copy_cid_token(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + ngtcp2_dcid_copy_cid_token(&conn->dcid.current, &dcid); rv = conn_call_activate_dcid(conn, &conn->dcid.current); if (rv != 0) { @@ -7862,54 +7590,52 @@ static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, if (pv) { if (pv->dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { - rv = conn_retire_dcid(conn, &pv->dcid, ts); - if (rv != 0) { - return rv; - } - - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); - - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->dcid.seq == pv->fallback_dcid.seq) { - ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); - } - - ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); - - rv = conn_call_activate_dcid(conn, &pv->dcid); - if (rv != 0) { - return rv; - } - } else { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, "path migration is aborted because connection ID is" "retired and no unused connection ID is available"); return conn_abort_pv(conn, ts); } - } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && - pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb)) { - rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); - if (rv != 0) { - return rv; - } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); - ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + rv = conn_retire_active_dcid(conn, &pv->dcid, ts); + if (rv != 0) { + return rv; + } - rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); - if (rv != 0) { - return rv; - } - } else { + ngtcp2_dcidtr_pop_unused_cid_token(&conn->dcid.dtr, &dcid); + + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && + pv->dcid.seq == pv->fallback_dcid.seq) { + ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, &dcid); + } + + ngtcp2_dcid_copy_cid_token(&pv->dcid, &dcid); + + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && + pv->fallback_dcid.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { /* Now we have no fallback dcid. */ return conn_abort_pv(conn, ts); } + + rv = conn_retire_active_dcid(conn, &pv->fallback_dcid, ts); + if (rv != 0) { + return rv; + } + + ngtcp2_dcidtr_pop_unused_cid_token(&conn->dcid.dtr, &dcid); + ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, &dcid); + + rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); + if (rv != 0) { + return rv; + } } } @@ -8185,9 +7911,9 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { ngtcp2_path_storage ps; int rv; ngtcp2_pv *pv; - ngtcp2_dcid *dcid; + ngtcp2_dcid dcid; - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { return 0; } @@ -8199,24 +7925,22 @@ static int conn_select_preferred_addr(ngtcp2_conn *conn) { return rv; } - if (ps.path.remote.addrlen == 0 || + if (ngtcp2_addr_empty(&ps.path.remote) || ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &ps.path.remote)) { return 0; } assert(conn->pv == NULL); - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); - ngtcp2_dcid_set_path(dcid, &ps.path); + ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid); + ngtcp2_dcid_set_path(&dcid, &ps.path); - rv = ngtcp2_pv_new(&pv, dcid, conn_compute_pv_timeout(conn), + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), NGTCP2_PV_FLAG_PREFERRED_ADDR, &conn->log, conn->mem); if (rv != 0) { - /* TODO Call ngtcp2_dcid_free here if it is introduced */ return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); conn->pv = pv; return conn_call_activate_dcid(conn, &pv->dcid); @@ -8250,7 +7974,7 @@ static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED | NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; - conn->pktns.rtb.persistent_congestion_start_ts = ts; + conn->handshake_confirmed_ts = ts; ngtcp2_conn_discard_handshake_state(conn, ts); @@ -8443,18 +8167,18 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, size_t dgramlen, int new_cid_used, ngtcp2_tstamp ts) { - ngtcp2_dcid dcid, *bound_dcid, *last; + ngtcp2_dcid dcid; ngtcp2_pv *pv; int rv; ngtcp2_duration pto; int require_new_cid; int local_addr_eq; + int pref_addr_migration; uint32_t remote_addr_cmp; - size_t len, i; assert(conn->server); - if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && ngtcp2_path_eq(&conn->pv->fallback_dcid.ps.path, path)) { /* If new path equals fallback path, that means connection migrated back to the original path. Fallback path is @@ -8478,9 +8202,11 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, } remote_addr_cmp = - ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote); + ngtcp2_addr_cmp(&conn->dcid.current.ps.path.remote, &path->remote); local_addr_eq = ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); + pref_addr_migration = + !local_addr_eq && conn_server_preferred_addr_migration(conn, &path->local); /* * When to change DCID? RFC 9002 section 9.5 says: @@ -8504,44 +8230,25 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "non-probing packet was received from new remote address"); - len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); + if (ngtcp2_dcidtr_pop_bound_dcid(&conn->dcid.dtr, &dcid, path) == 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "Found DCID which has already been bound to the new path"); - for (i = 0; i < len; ++i) { - bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); - if (ngtcp2_path_eq(&bound_dcid->ps.path, path)) { - ngtcp2_log_info( - &conn->log, NGTCP2_LOG_EVENT_CON, - "Found DCID which has already been bound to the new path"); - - ngtcp2_dcid_copy(&dcid, bound_dcid); - if (i == 0) { - ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); - } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { - ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); - } else { - last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, len - 1); - ngtcp2_dcid_copy(bound_dcid, last); - ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); - } - require_new_cid = 0; + require_new_cid = 0; - if (dcid.cid.datalen) { - rv = conn_call_activate_dcid(conn, &dcid); - if (rv != 0) { - return rv; - } + if (dcid.cid.datalen) { + rv = conn_call_activate_dcid(conn, &dcid); + if (rv != 0) { + return rv; } - break; } - } - - if (i == len) { + } else { if (require_new_cid) { - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { return NGTCP2_ERR_CONN_ID_BLOCKED; } - ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0)); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + + ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid); rv = conn_call_activate_dcid(conn, &dcid); if (rv != 0) { @@ -8564,32 +8271,33 @@ static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, pto = conn_compute_pto(conn, &conn->pktns); rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout_pto(conn, pto), - NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, &conn->log, conn->mem); + NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); if (rv != 0) { return rv; } - if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { - ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->pv->fallback_dcid); - pv->fallback_pto = conn->pv->fallback_pto; + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT)) { + ngtcp2_pv_set_fallback(pv, &conn->pv->fallback_dcid, + conn->pv->fallback_pto); /* Unset the flag bit so that conn_stop_pv does not retire DCID. */ - conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; - } else { - ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); - pv->fallback_pto = pto; + conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_PRESENT; + } else if (!pref_addr_migration) { + ngtcp2_pv_set_fallback(pv, &conn->dcid.current, pto); } - ngtcp2_dcid_copy(&conn->dcid.current, &dcid); + if (!pref_addr_migration) { + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); - if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_COMPARE_FLAG_ADDR | - NGTCP2_ADDR_COMPARE_FLAG_FAMILY))) { - conn_reset_congestion_state(conn, ts); - } + if (!local_addr_eq || (remote_addr_cmp & (NGTCP2_ADDR_CMP_FLAG_ADDR | + NGTCP2_ADDR_CMP_FLAG_FAMILY))) { + conn_reset_congestion_state(conn, ts); + } - conn_reset_ecn_validation_state(conn); + conn_reset_ecn_validation_state(conn); - ngtcp2_conn_stop_pmtud(conn); + ngtcp2_conn_stop_pmtud(conn); + } if (conn->pv) { ngtcp2_log_info( @@ -8634,7 +8342,7 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, return 0; } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)) { pv->fallback_dcid.bytes_recv += dgramlen; return 0; @@ -8650,7 +8358,6 @@ static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, return rv; } - ngtcp2_dcid_set_path(bound_dcid, path); bound_dcid->bytes_recv += dgramlen; return 0; @@ -8763,20 +8470,13 @@ conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen); - rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num, require_ack, pkt_ts); + rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num, pi, require_ack, pkt_ts); if (rv != 0) { return rv; } - pktns_increase_ecn_counts(pktns, pi); - /* Initial and Handshake are always acknowledged without delay. No need to call ngtcp2_acktr_immediate_ack(). */ - rv = ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd->pkt_num, require_ack, - pkt_ts); - if (rv != 0) { - return rv; - } conn_restart_timer_on_read(conn, ts); @@ -8794,8 +8494,6 @@ static int conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn, const ngtcp2_path *path) { uint32_t remote_addr_cmp; - const ngtcp2_preferred_addr *paddr; - ngtcp2_addr addr; assert(conn->server); assert(conn->local.transport_params.disable_active_migration); @@ -8804,40 +8502,15 @@ conn_allow_path_change_under_disable_active_migration(ngtcp2_conn *conn, (NAT rebinding). */ if (ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local)) { remote_addr_cmp = - ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote); + ngtcp2_addr_cmp(&conn->dcid.current.ps.path.remote, &path->remote); - return (remote_addr_cmp | NGTCP2_ADDR_COMPARE_FLAG_PORT) == - NGTCP2_ADDR_COMPARE_FLAG_PORT; + return (remote_addr_cmp | NGTCP2_ADDR_CMP_FLAG_PORT) == + NGTCP2_ADDR_CMP_FLAG_PORT; } /* If local address changes, it must be one of the preferred addresses. */ - - if (!conn->local.transport_params.preferred_addr_present) { - return 0; - } - - paddr = &conn->local.transport_params.preferred_addr; - - if (paddr->ipv4_present) { - ngtcp2_addr_init(&addr, (const ngtcp2_sockaddr *)&paddr->ipv4, - sizeof(paddr->ipv4)); - - if (ngtcp2_addr_eq(&addr, &path->local)) { - return 1; - } - } - - if (paddr->ipv6_present) { - ngtcp2_addr_init(&addr, (const ngtcp2_sockaddr *)&paddr->ipv6, - sizeof(paddr->ipv6)); - - if (ngtcp2_addr_eq(&addr, &path->local)) { - return 1; - } - } - - return 0; + return conn_server_preferred_addr_migration(conn, &path->local); } /* @@ -8904,13 +8577,28 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, int path_challenge_recved = 0; size_t num_ack_processed = 0; - if (conn->server && conn->local.transport_params.disable_active_migration && - !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && - !conn_allow_path_change_under_disable_active_migration(conn, path)) { - ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, - "packet is discarded because active migration is disabled"); + if (conn->server) { + if (conn->local.transport_params.disable_active_migration && + !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && + !conn_allow_path_change_under_disable_active_migration(conn, path)) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "packet is discarded because local active migration is disabled"); - return NGTCP2_ERR_DISCARD_PKT; + return NGTCP2_ERR_DISCARD_PKT; + } + + assert(conn->remote.transport_params); + + if (conn->remote.transport_params->disable_active_migration && + !ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local) && + !conn_server_preferred_addr_migration(conn, &path->local)) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "packet is discarded because remote active migration is disabled"); + + return NGTCP2_ERR_DISCARD_PKT; + } } if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { @@ -9035,8 +8723,8 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, payload = pkt + hdpktlen; payloadlen = pktlen - hdpktlen; - hd.pkt_num = - ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, hd.pkt_numlen); + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->acktr.max_pkt_num, hd.pkt_num, + hd.pkt_numlen); if (hd.pkt_num > NGTCP2_MAX_PKT_NUM) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "pkn=%" PRId64 " is greater than maximum pkn", hd.pkt_num); @@ -9046,6 +8734,19 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_log_rx_pkt_hd(&conn->log, &hd); if (hd.type == NGTCP2_PKT_1RTT) { + if (conn->server && conn->rx.preferred_addr.pkt_num != -1 && + conn->rx.preferred_addr.pkt_num < hd.pkt_num && + ngtcp2_sockaddr_eq((const ngtcp2_sockaddr *)&conn->hs_local_addr, + path->local.addr)) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "pkt=%" PRId64 + " is discarded because it was received on handshake local " + "address after preferred address migration", + hd.pkt_num); + return NGTCP2_ERR_DISCARD_PKT; + } + key_phase_bit_changed = conn_key_phase_changed(conn, &hd); } @@ -9067,7 +8768,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } else { force_decrypt_failure = 1; } - } else if (pktns->rx.max_pkt_num < hd.pkt_num) { + } else if (pktns->acktr.max_pkt_num < hd.pkt_num) { assert(ckm->pkt_num < hd.pkt_num); if (!conn->crypto.key_update.new_rx_ckm) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, @@ -9336,7 +9037,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, path_challenge_recved = 1; break; case NGTCP2_FRAME_PATH_RESPONSE: - rv = conn_recv_path_response(conn, &fr->path_response, ts); + rv = conn_recv_path_response(conn, &hd, &fr->path_response, ts); if (rv != 0) { return rv; } @@ -9426,7 +9127,7 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, if (conn->server && hd.type == NGTCP2_PKT_1RTT && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { - if (non_probing_pkt && pktns->rx.max_pkt_num < hd.pkt_num && + if (non_probing_pkt && pktns->acktr.max_pkt_num < hd.pkt_num && !conn_path_validation_in_progress(conn, path)) { rv = conn_recv_non_probing_pkt_on_new_path(conn, path, dgramlen, new_cid_used, ts); @@ -9470,25 +9171,17 @@ static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, } } - rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); + rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, pi, require_ack, pkt_ts); if (rv != 0) { return rv; } - pktns_increase_ecn_counts(pktns, pi); - if (require_ack && (++pktns->acktr.rx_npkt >= conn->local.settings.ack_thresh || (pi->ecn & NGTCP2_ECN_MASK) == NGTCP2_ECN_CE)) { ngtcp2_acktr_immediate_ack(&pktns->acktr); } - rv = - ngtcp2_conn_sched_ack(conn, &pktns->acktr, hd.pkt_num, require_ack, pkt_ts); - if (rv != 0) { - return rv; - } - conn_restart_timer_on_read(conn, ts); ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); @@ -9721,24 +9414,6 @@ static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, return 0; } -/* - * conn_is_retired_path returns nonzero if |path| is included in - * retired path list. - */ -static int conn_is_retired_path(ngtcp2_conn *conn, const ngtcp2_path *path) { - size_t i, len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); - ngtcp2_dcid *dcid; - - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); - if (ngtcp2_path_eq(&dcid->ps.path, path)) { - return 1; - } - } - - return 0; -} - /* * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for * transmission. @@ -9875,7 +9550,7 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, } } - if (conn->hs_pktns->rx.max_pkt_num != -1) { + if (conn->hs_pktns->acktr.max_pkt_num != -1) { ngtcp2_conn_discard_initial_state(conn, ts); } @@ -9956,7 +9631,7 @@ static ngtcp2_ssize conn_read_handshake(ngtcp2_conn *conn, } } - conn->pktns.rtb.persistent_congestion_start_ts = ts; + conn->handshake_confirmed_ts = ts; /* Re-arm loss detection timer here after handshake has been confirmed. */ @@ -9996,7 +9671,7 @@ int ngtcp2_conn_read_pkt_versioned(ngtcp2_conn *conn, const ngtcp2_path *path, /* client does not expect a packet from unknown path. */ if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path) && (!conn->pv || !ngtcp2_path_eq(&conn->pv->dcid.ps.path, path)) && - !conn_is_retired_path(conn, path)) { + !ngtcp2_dcidtr_check_path_retired(&conn->dcid.dtr, path)) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "ignore packet from unknown path"); return 0; @@ -10172,7 +9847,6 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; size_t origlen = destlen; uint64_t pending_early_datalen; - ngtcp2_dcid *dcid; ngtcp2_preferred_addr *paddr; switch (conn->state) { @@ -10290,11 +9964,11 @@ static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, assert(conn->remote.transport_params); if (conn->remote.transport_params->preferred_addr_present) { - assert(!ngtcp2_ringbuf_full(&conn->dcid.unused.rb)); + assert(!ngtcp2_dcidtr_unused_full(&conn->dcid.dtr)); paddr = &conn->remote.transport_params->preferred_addr; - dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused.rb); - ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); + ngtcp2_dcidtr_push_unused(&conn->dcid.dtr, 1, &paddr->cid, + paddr->stateless_reset_token); rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); if (rv != 0) { @@ -10519,20 +10193,6 @@ int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED); } -int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, - int64_t pkt_num, int active_ack, ngtcp2_tstamp ts) { - int rv; - (void)conn; - - rv = ngtcp2_acktr_add(acktr, pkt_num, active_ack, ts); - if (rv != 0) { - assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); - return rv; - } - - return 0; -} - int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, size_t pktlen) { ngtcp2_ssize nread; ngtcp2_pkt_hd hd, *p; @@ -10904,68 +10564,14 @@ int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { return conn_initiate_key_update(conn, ts); } -/* - * conn_retire_stale_bound_dcid retires stale destination connection - * ID in conn->dcid.bound to keep some unused destination connection - * IDs available. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -static int conn_retire_stale_bound_dcid(ngtcp2_conn *conn, - ngtcp2_duration timeout, - ngtcp2_tstamp ts) { - size_t i; - ngtcp2_dcid *dcid, *last; - int rv; - - for (i = 0; i < ngtcp2_ringbuf_len(&conn->dcid.bound.rb);) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); - - assert(dcid->cid.datalen); - - if (ngtcp2_tstamp_not_elapsed(dcid->bound_ts, timeout, ts)) { - ++i; - continue; - } - - rv = conn_retire_dcid_seq(conn, dcid->seq); - if (rv != 0) { - return rv; - } - - if (i == 0) { - ngtcp2_ringbuf_pop_front(&conn->dcid.bound.rb); - continue; - } - - if (i == ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1) { - ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); - break; - } - - last = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, - ngtcp2_ringbuf_len(&conn->dcid.bound.rb) - 1); - ngtcp2_dcid_copy(dcid, last); - ngtcp2_ringbuf_pop_back(&conn->dcid.bound.rb); - } - - return 0; -} - ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn) { return conn->cstat.loss_detection_timer; } ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { - ngtcp2_tstamp res = UINT64_MAX; + ngtcp2_tstamp res = UINT64_MAX, ts; ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); ngtcp2_scid *scid; - ngtcp2_dcid *dcid; - size_t i, len; if (conn->pv) { res = ngtcp2_pv_next_expiry(conn->pv); @@ -10982,20 +10588,15 @@ ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn) { } } - if (ngtcp2_ringbuf_len(&conn->dcid.retired.rb)) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, 0); - res = ngtcp2_min_uint64(res, dcid->retired_ts + pto); + ts = ngtcp2_dcidtr_earliest_retired_ts(&conn->dcid.dtr); + if (ts != UINT64_MAX) { + res = ngtcp2_min_uint64(res, ts + pto); } if (conn->dcid.current.cid.datalen) { - len = ngtcp2_ringbuf_len(&conn->dcid.bound.rb); - for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, i); - - assert(dcid->cid.datalen); - assert(dcid->bound_ts != UINT64_MAX); - - res = ngtcp2_min_uint64(res, dcid->bound_ts + 3 * pto); + ts = ngtcp2_dcidtr_earliest_bound_ts(&conn->dcid.dtr); + if (ts != UINT64_MAX) { + res = ngtcp2_min_uint64(res, ts + 3 * pto); } } @@ -11081,7 +10682,8 @@ int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { } if (conn->dcid.current.cid.datalen) { - rv = conn_retire_stale_bound_dcid(conn, 3 * pto, ts); + rv = ngtcp2_dcidtr_retire_stale_bound_dcid(&conn->dcid.dtr, 3 * pto, ts, + dcidtr_on_retire, conn); if (rv != 0) { return rv; } @@ -11578,7 +11180,8 @@ int ngtcp2_conn_set_local_transport_params_versioned( assert(conn->server); assert(params->active_connection_id_limit >= NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT); - assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); + assert(params->active_connection_id_limit <= + NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE); if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) { return NGTCP2_ERR_INVALID_STATE; @@ -11739,8 +11342,7 @@ static ngtcp2_ssize conn_write_vmsg_wrapper(ngtcp2_conn *conn, if (cstat->bytes_in_flight >= cstat->cwnd) { conn->rst.is_cwnd_limited = 1; - } else if (conn->rst.app_limited == 0 && - (cstat->cwnd >= cstat->ssthresh || + } else if ((cstat->cwnd >= cstat->ssthresh || cstat->bytes_in_flight * 2 < cstat->cwnd) && nwrite == 0 && conn_pacing_pkt_tx_allowed(conn, ts) && (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { @@ -12930,7 +12532,7 @@ static void conn_get_loss_time_and_pktns(ngtcp2_conn *conn, static ngtcp2_tstamp conn_get_earliest_pto_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { - ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_pktns *const ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; size_t i; ngtcp2_tstamp earliest_ts = UINT64_MAX, t; ngtcp2_conn_stat *cstat = &conn->cstat; @@ -13249,18 +12851,22 @@ static size_t conn_get_num_active_dcid(ngtcp2_conn *conn) { size_t n = 1; /* for conn->dcid.current */ ngtcp2_pv *pv = conn->pv; + if (conn->dcid.current.cid.datalen == 0) { + return n; + } + if (pv) { if (pv->dcid.seq != conn->dcid.current.seq) { ++n; } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && pv->fallback_dcid.seq != conn->dcid.current.seq && pv->fallback_dcid.seq != pv->dcid.seq) { ++n; } } - n += ngtcp2_ringbuf_len(&conn->dcid.retired.rb); + n += ngtcp2_dcidtr_retired_len(&conn->dcid.dtr); return n; } @@ -13293,12 +12899,16 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { copy_dcid_to_cid_token(dest, &conn->dcid.current); ++dest; + if (conn->dcid.current.cid.datalen == 0) { + return 1; + } + if (pv) { if (pv->dcid.seq != conn->dcid.current.seq) { copy_dcid_to_cid_token(dest, &pv->dcid); ++dest; } - if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_PRESENT) && pv->fallback_dcid.seq != conn->dcid.current.seq && pv->fallback_dcid.seq != pv->dcid.seq) { copy_dcid_to_cid_token(dest, &pv->fallback_dcid); @@ -13306,9 +12916,9 @@ size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { } } - len = ngtcp2_ringbuf_len(&conn->dcid.retired.rb); + len = ngtcp2_ringbuf_len(&conn->dcid.dtr.retired.rb); for (i = 0; i < len; ++i) { - dcid = ngtcp2_ringbuf_get(&conn->dcid.retired.rb, i); + dcid = ngtcp2_ringbuf_get(&conn->dcid.dtr.retired.rb, i); copy_dcid_to_cid_token(dest, dcid); ++dest; } @@ -13353,7 +12963,7 @@ static int conn_initiate_migration_precheck(ngtcp2_conn *conn, return NGTCP2_ERR_INVALID_STATE; } - if (ngtcp2_ringbuf_len(&conn->dcid.unused.rb) == 0) { + if (ngtcp2_dcidtr_unused_empty(&conn->dcid.dtr)) { return NGTCP2_ERR_CONN_ID_BLOCKED; } @@ -13368,7 +12978,7 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts) { int rv; - ngtcp2_dcid *dcid; + ngtcp2_dcid dcid; ngtcp2_pv *pv; assert(!conn->server); @@ -13389,16 +12999,15 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, } } - rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + rv = conn_retire_active_dcid(conn, &conn->dcid.current, ts); if (rv != 0) { return rv; } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); - ngtcp2_dcid_set_path(dcid, path); + ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid); + ngtcp2_dcid_set_path(&dcid, path); - ngtcp2_dcid_copy(&conn->dcid.current, dcid); - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); + ngtcp2_dcid_copy(&conn->dcid.current, &dcid); conn_reset_congestion_state(conn, ts); conn_reset_ecn_validation_state(conn); @@ -13407,7 +13016,7 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, connection should be closed if this path validation failed. The current design allows an application to continue, by migrating into yet another path. */ - rv = ngtcp2_pv_new(&pv, dcid, conn_compute_pv_timeout(conn), + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); if (rv != 0) { return rv; @@ -13421,7 +13030,7 @@ int ngtcp2_conn_initiate_immediate_migration(ngtcp2_conn *conn, int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, ngtcp2_tstamp ts) { int rv; - ngtcp2_dcid *dcid; + ngtcp2_dcid dcid; ngtcp2_pv *pv; assert(!conn->server); @@ -13440,16 +13049,15 @@ int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, } } - dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); - ngtcp2_dcid_set_path(dcid, path); + ngtcp2_dcidtr_pop_unused(&conn->dcid.dtr, &dcid); + ngtcp2_dcid_set_path(&dcid, path); - rv = ngtcp2_pv_new(&pv, dcid, conn_compute_pv_timeout(conn), + rv = ngtcp2_pv_new(&pv, &dcid, conn_compute_pv_timeout(conn), NGTCP2_PV_FLAG_NONE, &conn->log, conn->mem); if (rv != 0) { return rv; } - ngtcp2_ringbuf_pop_front(&conn->dcid.unused.rb); conn->pv = pv; return conn_call_activate_dcid(conn, &pv->dcid); @@ -13487,7 +13095,7 @@ uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { uint64_t ngtcp2_conn_get_cwnd_left(ngtcp2_conn *conn) { uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; - uint64_t cwnd = conn_get_cwnd(conn); + uint64_t cwnd = conn->cstat.cwnd; if (cwnd > bytes_in_flight) { return cwnd - bytes_in_flight; @@ -13630,7 +13238,7 @@ int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { ngtcp2_duration pacing_interval; - ngtcp2_duration wait; + ngtcp2_duration wait, d; conn_update_timestamp(conn, ts); @@ -13651,6 +13259,12 @@ void ngtcp2_conn_update_pkt_tx_time(ngtcp2_conn *conn, ngtcp2_tstamp ts) { wait = (ngtcp2_duration)(conn->tx.pacing.pktlen * pacing_interval); + if (conn->tx.pacing.compensation >= NGTCP2_MILLISECONDS) { + d = ngtcp2_min_uint64(wait, conn->tx.pacing.compensation); + wait -= d; + conn->tx.pacing.compensation -= d; + } + conn->tx.pacing.next_ts = ts + wait; conn->tx.pacing.pktlen = 0; } @@ -13659,48 +13273,6 @@ size_t ngtcp2_conn_get_send_quantum(ngtcp2_conn *conn) { return conn->cstat.send_quantum; } -int ngtcp2_conn_track_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { - if (conn->dcid.retire_unacked.len >= - ngtcp2_arraylen(conn->dcid.retire_unacked.seqs)) { - return NGTCP2_ERR_CONNECTION_ID_LIMIT; - } - - conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len++] = seq; - - return 0; -} - -void ngtcp2_conn_untrack_retired_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { - size_t i; - - for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { - if (conn->dcid.retire_unacked.seqs[i] != seq) { - continue; - } - - if (i != conn->dcid.retire_unacked.len - 1) { - conn->dcid.retire_unacked.seqs[i] = - conn->dcid.retire_unacked.seqs[conn->dcid.retire_unacked.len - 1]; - } - - --conn->dcid.retire_unacked.len; - - return; - } -} - -int ngtcp2_conn_check_retired_dcid_tracked(ngtcp2_conn *conn, uint64_t seq) { - size_t i; - - for (i = 0; i < conn->dcid.retire_unacked.len; ++i) { - if (conn->dcid.retire_unacked.seqs[i] == seq) { - return 1; - } - } - - return 0; -} - size_t ngtcp2_conn_get_stream_loss_count(ngtcp2_conn *conn, int64_t stream_id) { ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); @@ -13737,8 +13309,7 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( int rv; ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_INITIAL, dcid, - scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version, - /* len = */ 0); + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version); ngtcp2_vec_init(&ckm.secret, NULL, 0); ngtcp2_vec_init(&ckm.iv, iv, 12); @@ -13767,6 +13338,7 @@ ngtcp2_ssize ngtcp2_pkt_write_connection_close( fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; + fr.connection_close.frame_type = 0; fr.connection_close.reasonlen = reasonlen; fr.connection_close.reason = (uint8_t *)reason; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h index 55073fcc828d73..0ba8d6efcc6ac1 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -51,6 +51,7 @@ #include "ngtcp2_qlog.h" #include "ngtcp2_rst.h" #include "ngtcp2_conn_stat.h" +#include "ngtcp2_dcidtr.h" typedef enum { /* Client specific handshake states */ @@ -78,18 +79,6 @@ typedef enum { accept. */ #define NGTCP2_MAX_RETRIES 3 -/* NGTCP2_MAX_BOUND_DCID_POOL_SIZE is the maximum number of - destination connection ID which have been bound to a particular - path, but not yet used as primary path and path validation is not - performed from the local endpoint. */ -#define NGTCP2_MAX_BOUND_DCID_POOL_SIZE 4 -/* NGTCP2_MAX_DCID_POOL_SIZE is the maximum number of destination - connection ID the remote endpoint provides to store. It must be - the power of 2. */ -#define NGTCP2_MAX_DCID_POOL_SIZE 8 -/* NGTCP2_MAX_DCID_RETIRED_SIZE is the maximum number of retired DCID - kept to catch in-flight packet on retired path. */ -#define NGTCP2_MAX_DCID_RETIRED_SIZE 2 /* NGTCP2_MAX_SCID_POOL_SIZE is the maximum number of source connection ID the local endpoint provides to the remote endpoint. The chosen value was described in old draft. Now a remote endpoint @@ -239,11 +228,6 @@ typedef struct ngtcp2_pktns { /* pngap tracks received packet number in order to suppress duplicated packet number. */ ngtcp2_gaptr pngap; - /* max_pkt_num is the largest packet number received so far. */ - int64_t max_pkt_num; - /* max_pkt_ts is the timestamp when max_pkt_num packet is - received. */ - ngtcp2_tstamp max_pkt_ts; /* max_ack_eliciting_pkt_num is the largest ack-eliciting packet number received so far. */ int64_t max_ack_eliciting_pkt_num; @@ -268,21 +252,6 @@ typedef struct ngtcp2_pktns { * ngtcp2_pktns. */ ngtcp2_pkt_chain *buffed_pkts; - - struct { - /* ect0, ect1, and ce are the number of QUIC packets received - with those markings. */ - size_t ect0; - size_t ect1; - size_t ce; - struct { - /* ect0, ect1, ce are the ECN counts received in the latest - ACK frame. */ - uint64_t ect0; - uint64_t ect1; - uint64_t ce; - } ack; - } ecn; } rx; struct { @@ -336,12 +305,6 @@ typedef struct ngtcp2_early_transport_params { uint64_t max_datagram_frame_size; } ngtcp2_early_transport_params; -ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_MAX_BOUND_DCID_POOL_SIZE, - sizeof(ngtcp2_dcid)) -ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_MAX_DCID_POOL_SIZE, - sizeof(ngtcp2_dcid)) -ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_MAX_DCID_RETIRED_SIZE, - sizeof(ngtcp2_dcid)) ngtcp2_static_ringbuf_def(path_challenge, 4, sizeof(ngtcp2_path_challenge_entry)) @@ -366,6 +329,8 @@ struct ngtcp2_conn { records it in order to verify retry_source_connection_id transport parameter. Server does not use this field. */ ngtcp2_cid retry_scid; + /* hs_local_addr is a local address used during handshake. */ + ngtcp2_sockaddr_union hs_local_addr; ngtcp2_pktns *in_pktns; ngtcp2_pktns *hs_pktns; ngtcp2_pktns pktns; @@ -373,31 +338,13 @@ struct ngtcp2_conn { struct { /* current is the current destination connection ID. */ ngtcp2_dcid current; - /* bound is a set of destination connection IDs which are bound to - particular paths. These paths are not validated yet. */ - ngtcp2_static_ringbuf_dcid_bound bound; - /* unused is a set of unused CID received from peer. */ - ngtcp2_static_ringbuf_dcid_unused unused; - /* retired is a set of CID retired by local endpoint. Keep them - in 3*PTO to catch packets in flight along the old path. */ - ngtcp2_static_ringbuf_dcid_retired retired; + ngtcp2_dcidtr dtr; /* seqgap tracks received sequence numbers in order to ignore retransmitted duplicated NEW_CONNECTION_ID frame. */ ngtcp2_gaptr seqgap; /* retire_prior_to is the largest retire_prior_to received so far. */ uint64_t retire_prior_to; - struct { - /* seqs contains sequence number of Connection ID whose - retirement is not acknowledged by the remote endpoint yet. */ - uint64_t seqs[NGTCP2_MAX_DCID_POOL_SIZE * 2]; - /* len is the number of sequence numbers that seq contains. */ - size_t len; - } retire_unacked; - /* zerolen_seq is a pseudo sequence number of zero-length - Destination Connection ID in order to distinguish between - them. */ - uint64_t zerolen_seq; } dcid; struct { @@ -421,11 +368,6 @@ struct ngtcp2_conn { struct { /* strmq contains ngtcp2_strm which has frames to send. */ ngtcp2_pq strmq; - /* ack is ACK frame. The underlying buffer is reused. */ - ngtcp2_frame *ack; - /* max_ack_ranges is the number of additional ngtcp2_ack_range - which ack can contain. */ - size_t max_ack_ranges; /* offset is the offset the local endpoint has sent to the remote endpoint. */ uint64_t offset; @@ -458,6 +400,15 @@ struct ngtcp2_conn { /* next_ts is the time to send next packet. It is UINT64_MAX if packet pacing is disabled or expired.*/ ngtcp2_tstamp next_ts; + /* compensation is the amount of time that a local endpoint + waits too long for pacing. This happens because there is an + overhead before start writing packets after pacing timer + expires. If multiple QUIC connections are handled by a + single thread, which is typical use case for event loop based + servers, each processing of QUIC connection adds overhead, + for example, TLS handshake, and packet encryption/decryption, + etc. */ + ngtcp2_duration compensation; } pacing; } tx; @@ -478,6 +429,12 @@ struct ngtcp2_conn { ngtcp2_static_ringbuf_path_challenge path_challenge; /* ccerr is the received connection close error. */ ngtcp2_ccerr ccerr; + + struct { + /* pkt_num is the lowest incoming packet number of the packet + that server verified preferred address usage of client. */ + int64_t pkt_num; + } preferred_addr; } rx; struct { @@ -671,6 +628,9 @@ struct ngtcp2_conn { const ngtcp2_mem *mem; /* idle_ts is the time instant when idle timer started. */ ngtcp2_tstamp idle_ts; + /* handshake_confirmed_ts is the time instant when handshake is + confirmed. For server, it is confirmed when completed. */ + ngtcp2_tstamp handshake_confirmed_ts; void *user_data; uint32_t client_chosen_version; uint32_t negotiated_version; @@ -724,21 +684,6 @@ typedef struct ngtcp2_vmsg { }; } ngtcp2_vmsg; -/* - * ngtcp2_conn_sched_ack stores packet number |pkt_num| and its - * reception timestamp |ts| in order to send its ACK. - * - * It returns 0 if it succeeds, or one of the following negative error - * codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory - * NGTCP2_ERR_PROTO - * Same packet number has already been added. - */ -int ngtcp2_conn_sched_ack(ngtcp2_conn *conn, ngtcp2_acktr *acktr, - int64_t pkt_num, int active_ack, ngtcp2_tstamp ts); - /* * ngtcp2_conn_find_stream returns a stream whose stream ID is * |stream_id|. If no such stream is found, it returns NULL. @@ -1137,28 +1082,6 @@ int ngtcp2_conn_set_remote_transport_params( int ngtcp2_conn_set_0rtt_remote_transport_params( ngtcp2_conn *conn, const ngtcp2_transport_params *params); -/* - * ngtcp2_conn_create_ack_frame creates ACK frame, and assigns its - * pointer to |*pfr| if there are any received packets to acknowledge. - * If there are no packets to acknowledge, this function returns 0, - * and |*pfr| is untouched. The caller is advised to set |*pfr| to - * NULL before calling this function, and check it after this function - * returns. - * - * Call ngtcp2_acktr_commit_ack after a created ACK frame is - * successfully serialized into a packet. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_conn_create_ack_frame(ngtcp2_conn *conn, ngtcp2_frame **pfr, - ngtcp2_pktns *pktns, uint8_t type, - ngtcp2_tstamp ts, ngtcp2_duration ack_delay, - uint64_t ack_delay_exponent); - /* * ngtcp2_conn_discard_initial_state discards state for Initial packet * number space. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c new file mode 100644 index 00000000000000..8a8d7733797176 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.c @@ -0,0 +1,497 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * 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. + */ +#include "ngtcp2_dcidtr.h" + +#include + +#include "ngtcp2_tstamp.h" +#include "ngtcp2_macro.h" + +void ngtcp2_dcidtr_init(ngtcp2_dcidtr *dtr) { + ngtcp2_static_ringbuf_dcid_unused_init(&dtr->unused); + ngtcp2_static_ringbuf_dcid_bound_init(&dtr->bound); + ngtcp2_static_ringbuf_dcid_retired_init(&dtr->retired); + + dtr->retire_unacked.len = 0; +} + +int ngtcp2_dcidtr_track_retired_seq(ngtcp2_dcidtr *dtr, uint64_t seq) { + if (dtr->retire_unacked.len >= ngtcp2_arraylen(dtr->retire_unacked.seqs)) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + dtr->retire_unacked.seqs[dtr->retire_unacked.len++] = seq; + + return 0; +} + +void ngtcp2_dcidtr_untrack_retired_seq(ngtcp2_dcidtr *dtr, uint64_t seq) { + size_t i; + + for (i = 0; i < dtr->retire_unacked.len; ++i) { + if (dtr->retire_unacked.seqs[i] != seq) { + continue; + } + + if (i != dtr->retire_unacked.len - 1) { + dtr->retire_unacked.seqs[i] = + dtr->retire_unacked.seqs[dtr->retire_unacked.len - 1]; + } + + --dtr->retire_unacked.len; + + return; + } +} + +int ngtcp2_dcidtr_check_retired_seq_tracked(const ngtcp2_dcidtr *dtr, + uint64_t seq) { + size_t i; + + for (i = 0; i < dtr->retire_unacked.len; ++i) { + if (dtr->retire_unacked.seqs[i] == seq) { + return 1; + } + } + + return 0; +} + +static int dcidtr_on_retire(ngtcp2_dcidtr *dtr, const ngtcp2_dcid *dcid, + ngtcp2_dcidtr_cb on_retire, void *user_data) { + int rv; + + if (ngtcp2_dcidtr_check_retired_seq_tracked(dtr, dcid->seq)) { + return 0; + } + + rv = ngtcp2_dcidtr_track_retired_seq(dtr, dcid->seq); + if (rv != 0) { + return rv; + } + + if (!on_retire) { + return 0; + } + + return on_retire(dcid, user_data); +} + +ngtcp2_dcid *ngtcp2_dcidtr_find_bound_dcid(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path) { + ngtcp2_dcid *dcid; + const ngtcp2_ringbuf *rb = &dtr->bound.rb; + size_t i, len = ngtcp2_ringbuf_len(rb); + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(rb, i); + + if (ngtcp2_path_eq(&dcid->ps.path, path)) { + return dcid; + } + } + + return NULL; +} + +ngtcp2_dcid *ngtcp2_dcidtr_bind_zerolen_dcid(ngtcp2_dcidtr *dtr, + const ngtcp2_path *path) { + ngtcp2_dcid *dcid = ngtcp2_ringbuf_push_back(&dtr->bound.rb); + ngtcp2_cid cid; + + ngtcp2_cid_zero(&cid); + ngtcp2_dcid_init(dcid, 0, &cid, NULL); + ngtcp2_dcid_set_path(dcid, path); + + return dcid; +} + +int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest, + const ngtcp2_path *path, ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_retire, void *user_data) { + const ngtcp2_dcid *src; + ngtcp2_dcid *dest; + int rv; + + if (ngtcp2_ringbuf_full(&dtr->bound.rb)) { + rv = dcidtr_on_retire(dtr, ngtcp2_ringbuf_get(&dtr->bound.rb, 0), on_retire, + user_data); + if (rv != 0) { + return rv; + } + } + + src = ngtcp2_ringbuf_get(&dtr->unused.rb, 0); + dest = ngtcp2_ringbuf_push_back(&dtr->bound.rb); + + ngtcp2_dcid_copy(dest, src); + dest->bound_ts = ts; + ngtcp2_dcid_set_path(dest, path); + + ngtcp2_ringbuf_pop_front(&dtr->unused.rb); + + *pdest = dest; + + return 0; +} + +static int verify_stateless_reset(const ngtcp2_ringbuf *rb, + const ngtcp2_path *path, + const uint8_t *token) { + const ngtcp2_dcid *dcid; + size_t i, len = ngtcp2_ringbuf_len(rb); + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(rb, i); + if (ngtcp2_dcid_verify_stateless_reset_token(dcid, path, token) == 0) { + return 0; + } + } + + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path, + const uint8_t *token) { + int rv; + + rv = verify_stateless_reset(&dtr->retired.rb, path, token); + if (rv == 0) { + return 0; + } + + return verify_stateless_reset(&dtr->bound.rb, path, token); +} + +static int verify_token_uniqueness(const ngtcp2_ringbuf *rb, int *pfound, + uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token) { + const ngtcp2_dcid *dcid; + size_t i, len = ngtcp2_ringbuf_len(rb); + int rv; + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(rb, i); + rv = ngtcp2_dcid_verify_uniqueness(dcid, seq, cid, token); + if (rv != 0) { + return NGTCP2_ERR_PROTO; + } + + if (ngtcp2_cid_eq(&dcid->cid, cid)) { + *pfound = 1; + } + } + + return 0; +} + +int ngtcp2_dcidtr_verify_token_uniqueness(const ngtcp2_dcidtr *dtr, int *pfound, + uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token) { + int rv; + + rv = verify_token_uniqueness(&dtr->bound.rb, pfound, seq, cid, token); + if (rv != 0) { + return rv; + } + + return verify_token_uniqueness(&dtr->unused.rb, pfound, seq, cid, token); +} + +static void remove_dcid_at(ngtcp2_ringbuf *rb, size_t at) { + const ngtcp2_dcid *src; + ngtcp2_dcid *dest; + + if (at == 0) { + ngtcp2_ringbuf_pop_front(rb); + return; + } + + if (at == ngtcp2_ringbuf_len(rb) - 1) { + ngtcp2_ringbuf_pop_back(rb); + return; + } + + src = ngtcp2_ringbuf_get(rb, ngtcp2_ringbuf_len(rb) - 1); + dest = ngtcp2_ringbuf_get(rb, at); + + ngtcp2_dcid_copy(dest, src); + ngtcp2_ringbuf_pop_back(rb); +} + +static int dcidtr_retire_dcid_prior_to(ngtcp2_dcidtr *dtr, ngtcp2_ringbuf *rb, + uint64_t seq, ngtcp2_dcidtr_cb on_retire, + void *user_data) { + size_t i; + const ngtcp2_dcid *dcid; + int rv; + + for (i = 0; i < ngtcp2_ringbuf_len(rb);) { + dcid = ngtcp2_ringbuf_get(rb, i); + if (dcid->seq >= seq) { + ++i; + continue; + } + + rv = dcidtr_on_retire(dtr, dcid, on_retire, user_data); + if (rv != 0) { + return rv; + } + + remove_dcid_at(rb, i); + } + + return 0; +} + +int ngtcp2_dcidtr_retire_inactive_dcid_prior_to(ngtcp2_dcidtr *dtr, + uint64_t seq, + ngtcp2_dcidtr_cb on_retire, + void *user_data) { + int rv; + + rv = + dcidtr_retire_dcid_prior_to(dtr, &dtr->bound.rb, seq, on_retire, user_data); + if (rv != 0) { + return rv; + } + + return dcidtr_retire_dcid_prior_to(dtr, &dtr->unused.rb, seq, on_retire, + user_data); +} + +int ngtcp2_dcidtr_retire_active_dcid(ngtcp2_dcidtr *dtr, + const ngtcp2_dcid *dcid, ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_deactivate, + void *user_data) { + ngtcp2_ringbuf *rb = &dtr->retired.rb; + const ngtcp2_dcid *stale_dcid; + ngtcp2_dcid *dest; + int rv; + + assert(dcid->cid.datalen); + + if (ngtcp2_ringbuf_full(rb)) { + stale_dcid = ngtcp2_ringbuf_get(rb, 0); + rv = on_deactivate(stale_dcid, user_data); + if (rv != 0) { + return rv; + } + } + + dest = ngtcp2_ringbuf_push_back(rb); + ngtcp2_dcid_copy(dest, dcid); + dest->retired_ts = ts; + + return dcidtr_on_retire(dtr, dest, NULL, NULL); +} + +int ngtcp2_dcidtr_remove_stale_retired_dcid(ngtcp2_dcidtr *dtr, + ngtcp2_duration timeout, + ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_deactivate, + void *user_data) { + ngtcp2_ringbuf *rb = &dtr->retired.rb; + const ngtcp2_dcid *dcid; + int rv; + + for (; ngtcp2_ringbuf_len(rb);) { + dcid = ngtcp2_ringbuf_get(rb, 0); + if (ngtcp2_tstamp_not_elapsed(dcid->retired_ts, timeout, ts)) { + break; + } + + rv = on_deactivate(dcid, user_data); + if (rv != 0) { + return rv; + } + + ngtcp2_ringbuf_pop_front(rb); + } + + return 0; +} + +int ngtcp2_dcidtr_pop_bound_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest, + const ngtcp2_path *path) { + const ngtcp2_dcid *src; + ngtcp2_ringbuf *rb = &dtr->bound.rb; + size_t len = ngtcp2_ringbuf_len(rb); + size_t i; + + for (i = 0; i < len; ++i) { + src = ngtcp2_ringbuf_get(rb, i); + if (ngtcp2_path_eq(&src->ps.path, path)) { + ngtcp2_dcid_copy(dest, src); + remove_dcid_at(rb, i); + + return 0; + } + } + + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +int ngtcp2_dcidtr_retire_stale_bound_dcid(ngtcp2_dcidtr *dtr, + ngtcp2_duration timeout, + ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_retire, + void *user_data) { + ngtcp2_ringbuf *rb = &dtr->bound.rb; + size_t i; + const ngtcp2_dcid *dcid; + int rv; + + for (i = 0; i < ngtcp2_ringbuf_len(rb);) { + dcid = ngtcp2_ringbuf_get(rb, i); + + assert(dcid->cid.datalen); + + if (ngtcp2_tstamp_not_elapsed(dcid->bound_ts, timeout, ts)) { + ++i; + continue; + } + + rv = dcidtr_on_retire(dtr, dcid, on_retire, user_data); + if (rv != 0) { + return rv; + } + + remove_dcid_at(rb, i); + } + + return 0; +} + +ngtcp2_tstamp ngtcp2_dcidtr_earliest_bound_ts(const ngtcp2_dcidtr *dtr) { + const ngtcp2_ringbuf *rb = &dtr->bound.rb; + size_t i, len = ngtcp2_ringbuf_len(rb); + ngtcp2_tstamp res = UINT64_MAX; + const ngtcp2_dcid *dcid; + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(rb, i); + + assert(dcid->cid.datalen); + assert(dcid->bound_ts != UINT64_MAX); + + res = ngtcp2_min_uint64(res, dcid->bound_ts); + } + + return res; +} + +ngtcp2_tstamp ngtcp2_dcidtr_earliest_retired_ts(const ngtcp2_dcidtr *dtr) { + const ngtcp2_ringbuf *rb = &dtr->retired.rb; + const ngtcp2_dcid *dcid; + + if (ngtcp2_ringbuf_len(rb) == 0) { + return UINT64_MAX; + } + + dcid = ngtcp2_ringbuf_get(rb, 0); + + return dcid->retired_ts; +} + +void ngtcp2_dcidtr_push_unused(ngtcp2_dcidtr *dtr, uint64_t seq, + const ngtcp2_cid *cid, const uint8_t *token) { + ngtcp2_dcid *dcid = ngtcp2_ringbuf_push_back(&dtr->unused.rb); + + ngtcp2_dcid_init(dcid, seq, cid, token); +} + +void ngtcp2_dcidtr_pop_unused_cid_token(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest) { + ngtcp2_ringbuf *rb = &dtr->unused.rb; + const ngtcp2_dcid *src; + + assert(ngtcp2_ringbuf_len(rb)); + + src = ngtcp2_ringbuf_get(rb, 0); + + dest->flags = NGTCP2_DCID_FLAG_NONE; + ngtcp2_dcid_copy_cid_token(dest, src); + + ngtcp2_ringbuf_pop_front(rb); +} + +void ngtcp2_dcidtr_pop_unused(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest) { + ngtcp2_ringbuf *rb = &dtr->unused.rb; + const ngtcp2_dcid *src; + + assert(ngtcp2_ringbuf_len(rb)); + + src = ngtcp2_ringbuf_get(rb, 0); + + ngtcp2_dcid_copy(dest, src); + + ngtcp2_ringbuf_pop_front(rb); +} + +int ngtcp2_dcidtr_check_path_retired(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path) { + const ngtcp2_ringbuf *rb = &dtr->retired.rb; + size_t i, len = ngtcp2_ringbuf_len(rb); + const ngtcp2_dcid *dcid; + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(rb, i); + if (ngtcp2_path_eq(&dcid->ps.path, path)) { + return 1; + } + } + + return 0; +} + +size_t ngtcp2_dcidtr_unused_len(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_len(&dtr->unused.rb); +} + +size_t ngtcp2_dcidtr_bound_len(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_len(&dtr->bound.rb); +} + +size_t ngtcp2_dcidtr_retired_len(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_len(&dtr->retired.rb); +} + +size_t ngtcp2_dcidtr_inactive_len(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_len(&dtr->unused.rb) + + ngtcp2_ringbuf_len(&dtr->bound.rb); +} + +int ngtcp2_dcidtr_unused_full(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_full(&dtr->unused.rb); +} + +int ngtcp2_dcidtr_unused_empty(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_len(&dtr->unused.rb) == 0; +} + +int ngtcp2_dcidtr_bound_full(const ngtcp2_dcidtr *dtr) { + return ngtcp2_ringbuf_full(&dtr->bound.rb); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h new file mode 100644 index 00000000000000..17942389b814d1 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_dcidtr.h @@ -0,0 +1,343 @@ +/* + * ngtcp2 + * + * Copyright (c) 2025 ngtcp2 contributors + * + * 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. + */ +#ifndef NGTCP2_DCIDTR_H +#define NGTCP2_DCIDTR_H + +#ifdef HAVE_CONFIG_H +# include +#endif /* defined(HAVE_CONFIG_H) */ + +#include + +#include "ngtcp2_cid.h" +#include "ngtcp2_ringbuf.h" + +/* NGTCP2_DCIDTR_MAX_BOUND_DCID_SIZE is the maximum number of + Destination Connection ID which has been bound to a particular + path, but not yet used as primary path, and path validation is not + performed from the local endpoint. It must be the power of 2. */ +#define NGTCP2_DCIDTR_MAX_BOUND_DCID_SIZE 4 +/* NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE is the maximum number of + Destination Connection ID the remote endpoint provides to store. + It must be the power of 2. */ +#define NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE 8 +/* NGTCP2_DCIDTR_MAX_RETIRED_DCID_SIZE is the maximum number of + retired Destination Connection ID kept to catch in-flight packet on + a retired path. It must be the power of 2. */ +#define NGTCP2_DCIDTR_MAX_RETIRED_DCID_SIZE 2 + +ngtcp2_static_ringbuf_def(dcid_bound, NGTCP2_DCIDTR_MAX_BOUND_DCID_SIZE, + sizeof(ngtcp2_dcid)) +ngtcp2_static_ringbuf_def(dcid_unused, NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE, + sizeof(ngtcp2_dcid)) +ngtcp2_static_ringbuf_def(dcid_retired, NGTCP2_DCIDTR_MAX_RETIRED_DCID_SIZE, + sizeof(ngtcp2_dcid)) + +/* + * ngtcp2_dcidtr stores unused, bound, and retired Destination + * Connection IDs. + */ +typedef struct ngtcp2_dcidtr { + /* unused is a set of unused Destination Connection ID received from + a remote endpoint. They are considered inactive. */ + ngtcp2_static_ringbuf_dcid_unused unused; + /* bound is a set of Destination Connection IDs which are bound to + particular paths. These paths are not validated yet. They are + considered inactive. */ + ngtcp2_static_ringbuf_dcid_bound bound; + /* retired is a set of Destination Connection ID retired by local + endpoint. Keep them in 3*PTO to catch packets in flight along + the old path. They are considered active. */ + ngtcp2_static_ringbuf_dcid_retired retired; + struct { + /* seqs contains sequence number of Destination Connection ID + whose retirement is not acknowledged by the remote endpoint + yet. */ + uint64_t seqs[NGTCP2_DCIDTR_MAX_UNUSED_DCID_SIZE * 2]; + /* len is the number of sequence numbers that seq contains. */ + size_t len; + } retire_unacked; +} ngtcp2_dcidtr; + +typedef int (*ngtcp2_dcidtr_cb)(const ngtcp2_dcid *dcid, void *user_data); + +/* + * ngtcp2_dcidtr_init initializes |dtr|. + */ +void ngtcp2_dcidtr_init(ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_track_retired_seq tracks the sequence number |seq| of + * unacknowledged retiring Destination Connection ID. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONNECTION_ID_LIMIT + * The number of unacknowledged retirement exceeds the limit. + */ +int ngtcp2_dcidtr_track_retired_seq(ngtcp2_dcidtr *dtr, uint64_t seq); + +/* + * ngtcp2_dcidtr_untrack_retired_seq deletes the sequence number |seq| + * of unacknowledged retiring Destination Connection ID. It is fine + * if such sequence number is not found. + */ +void ngtcp2_dcidtr_untrack_retired_seq(ngtcp2_dcidtr *dtr, uint64_t seq); + +/* + * ngtcp2_dcidtr_check_retired_seq_tracked returns nonzero if |seq| + * has already been tracked. + */ +int ngtcp2_dcidtr_check_retired_seq_tracked(const ngtcp2_dcidtr *dtr, + uint64_t seq); + +/* + * ngtcp2_dcidtr_find_bound_dcid returns the pointer to ngtcp2_dcid + * that bound to |path|. It returns NULL if there is no such + * ngtcp2_dcid. + */ +ngtcp2_dcid *ngtcp2_dcidtr_find_bound_dcid(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path); + +/* + * ngtcp2_dcidtr_bind_zerolen_dcid binds zero-length Destination + * Connection ID to |path|, and returns the pointer to the bound + * ngtcp2_dcid. + */ +ngtcp2_dcid *ngtcp2_dcidtr_bind_zerolen_dcid(ngtcp2_dcidtr *dtr, + const ngtcp2_path *path); + +/* + * ngtcp2_dcidtr_bind_dcid binds non-zero Destination Connection ID to + * |path|. |ts| is the current timestamp. The buffer space of bound + * Destination Connection ID is limited. If it is full, the earliest + * one is removed. |on_retire|, if specified, is called for the + * removed ngtcp2_dcid with |user_data|. This function assigns the + * pointer to bound ngtcp2_dcid to |*pdest|. + * + * This function returns 0 if it succeeds, or negative error code that + * |on_retire| returns. + */ +int ngtcp2_dcidtr_bind_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid **pdest, + const ngtcp2_path *path, ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_retire, void *user_data); + +/* + * ngtcp2_dcidtr_verify_stateless_reset verifies the stateless reset + * token |token| received from |path|. It returns 0 if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * There is no Destination Connection ID that matches the given + * |path| and |token|. + */ +int ngtcp2_dcidtr_verify_stateless_reset(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path, + const uint8_t *token); + +/* + * ngtcp2_dcidtr_verify_token_uniqueness verifies that the uniqueness + * of the combination of |seq|, |cid|, and |token| against the exiting + * Destination Connection IDs. That is: + * + * - If they do not share the same seq, then their Connection IDs must + * be different. + * + * - If they share the same seq, then their Connection IDs and tokens + * must be the same. + * + * If this function succeeds, and there is Destination Connection ID + * which shares |seq|, |cid|, and |token|, |*pfound| is set to + * nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * The given combination of values does not satisfy the above + * conditions. + */ +int ngtcp2_dcidtr_verify_token_uniqueness(const ngtcp2_dcidtr *dtr, int *pfound, + uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token); + +/* + * ngtcp2_dcidtr_retire_inactive_dcid_prior_to retires inactive + * Destination Connection IDs (unused or bound) whose seq is less than + * |seq|. For each retired ngtcp2_dcid, |on_retire|, if specified, is + * called with |user_data|. + * + * This function returns 0 if it succeeds, or negative error code that + * |on_retire| returns. + */ +int ngtcp2_dcidtr_retire_inactive_dcid_prior_to(ngtcp2_dcidtr *dtr, + uint64_t seq, + ngtcp2_dcidtr_cb on_retire, + void *user_data); + +/* + * ngtcp2_dcidtr_retire_active_dcid adds an active |dcid| to the + * retired Destination Connection ID buffer. The buffer space of + * retired Destination Connection ID is limited. If it is full, the + * earliest one is removed. |on_deactivate| is called for the removed + * ngtcp2_dcid with |user_data|. + * + * This function returns 0 if it succeeds, or negative error code that + * |on_deactivate| returns. + */ +int ngtcp2_dcidtr_retire_active_dcid(ngtcp2_dcidtr *dtr, + const ngtcp2_dcid *dcid, ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_deactivate, + void *user_data); + +/* + * ngtcp2_dcidtr_retire_stale_bound_dcid retires stale bound + * Destination Connection ID. For each retired ngtcp2_dcid, + * |on_retire|, if specified, is called with |user_data|. + */ +int ngtcp2_dcidtr_retire_stale_bound_dcid(ngtcp2_dcidtr *dtr, + ngtcp2_duration timeout, + ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_retire, + void *user_data); + +/* + * ngtcp2_dcidtr_remove_stale_retired_dcid removes stale retired + * Destination Connection ID. For each removed ngtcp2_dcid, + * |on_deactivate| is called with |user_data|. + * + * This function returns 0 if it succeeds, or negative error code that + * |on_deactivate| returns. + */ +int ngtcp2_dcidtr_remove_stale_retired_dcid(ngtcp2_dcidtr *dtr, + ngtcp2_duration timeout, + ngtcp2_tstamp ts, + ngtcp2_dcidtr_cb on_deactivate, + void *user_data); + +/* + * ngtcp2_dcidtr_pop_bound_dcid removes Destination Connection ID that + * is bound to |path|, and copies it into the object pointed by + * |dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * No ngtcp2_dcid bound to |path| found. + */ +int ngtcp2_dcidtr_pop_bound_dcid(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dest, + const ngtcp2_path *path); + +/* + * ngtcp2_dcidtr_earliest_bound_ts returns earliest timestamp when a + * Destination Connection ID is bound. If there is no bound + * Destination Connection ID, this function returns UINT64_MAX. + */ +ngtcp2_tstamp ngtcp2_dcidtr_earliest_bound_ts(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_earliest_retired_ts returns earliest timestamp when a + * Destination Connection ID is retired. If there is no retired + * Destination Connection ID, this function returns UINT64_MAX. + */ +ngtcp2_tstamp ngtcp2_dcidtr_earliest_retired_ts(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_push_unused adds new Destination Connection ID to the + * unused buffer. |seq| is its sequence number, |cid| is its + * Connection ID, and |token| is its stateless reset token. If the + * buffer space is full, the earliest ngtcp2_dcid is removed. + */ +void ngtcp2_dcidtr_push_unused(ngtcp2_dcidtr *dtr, uint64_t seq, + const ngtcp2_cid *cid, const uint8_t *token); + +/* + * ngtcp2_dcidtr_pop_unused_cid_token removes an unused Destination + * Connection ID, and copies it into the object pointed by |dcid| with + * ngtcp2_dcid_copy_cid_token. This function assumes that there is at + * least one unused Destination Connection ID. + */ +void ngtcp2_dcidtr_pop_unused_cid_token(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dcid); + +/* + * ngtcp2_dcidtr_pop_unused removes an unused Destination Connection + * ID, and copies it into the object pointed by |dcid| with + * ngtcp2_dcid_copy. This function assumes that there is at least one + * unused Destination Connection ID. + */ +void ngtcp2_dcidtr_pop_unused(ngtcp2_dcidtr *dtr, ngtcp2_dcid *dcid); + +/* + * ngtcp2_dcidtr_check_path_retired returns nonzero if |path| is + * included in retired Destination Connection IDs. + */ +int ngtcp2_dcidtr_check_path_retired(const ngtcp2_dcidtr *dtr, + const ngtcp2_path *path); + +/* + * ngtcp2_dcidtr_unused_len returns the number of unused Destination + * Connection ID. + */ +size_t ngtcp2_dcidtr_unused_len(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_bound_len returns the number of bound Destination + * Connection ID. + */ +size_t ngtcp2_dcidtr_bound_len(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_retired_len returns the number of retired Destination + * Connection ID. + */ +size_t ngtcp2_dcidtr_retired_len(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_inactive_len returns the number of unused and bound + * Destination Connection ID. + */ +size_t ngtcp2_dcidtr_inactive_len(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_unused_full returns nonzero if the buffer of unused + * Destination Connection ID is full. + */ +int ngtcp2_dcidtr_unused_full(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_unused_empty returns nonzero if the buffer of unused + * Destination Connection ID is empty. + */ +int ngtcp2_dcidtr_unused_empty(const ngtcp2_dcidtr *dtr); + +/* + * ngtcp2_dcidtr_bound_full returns nonzero if the buffer of bound + * Destination Connection ID is full. + */ +int ngtcp2_dcidtr_bound_full(const ngtcp2_dcidtr *dtr); + +#endif /* NGTCP2_DCIDTR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.c index 6a8a22c3f0d010..0f6b06a788d174 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.c @@ -29,17 +29,6 @@ ngtcp2_objalloc_def(frame_chain, ngtcp2_frame_chain, oplent) -int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem) { - *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain)); - if (*pfrc == NULL) { - return NGTCP2_ERR_NOMEM; - } - - ngtcp2_frame_chain_init(*pfrc); - - return 0; -} - int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, ngtcp2_objalloc *objalloc) { *pfrc = ngtcp2_objalloc_frame_chain_get(objalloc); @@ -83,13 +72,13 @@ int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, size_t tokenlen, ngtcp2_objalloc *objalloc, const ngtcp2_mem *mem) { - size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); int rv; uint8_t *p; ngtcp2_frame *fr; - if (tokenlen > avail) { - rv = ngtcp2_frame_chain_extralen_new(pfrc, tokenlen - avail, mem); + if (tokenlen > NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES) { + rv = ngtcp2_frame_chain_extralen_new( + pfrc, tokenlen - NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES, mem); } else { rv = ngtcp2_frame_chain_objalloc_new(pfrc, objalloc); } @@ -144,8 +133,7 @@ void ngtcp2_frame_chain_objalloc_del(ngtcp2_frame_chain *frc, break; case NGTCP2_FRAME_NEW_TOKEN: - if (frc->fr.new_token.tokenlen > - sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) { + if (frc->fr.new_token.tokenlen > NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES) { ngtcp2_frame_chain_del(frc, mem); return; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h index e5b6779c0f03c2..e7b33632529cd4 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_frame_chain.h @@ -95,28 +95,15 @@ int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, #define NGTCP2_MAX_STREAM_DATACNT 256 /* - * ngtcp2_frame_chain_new allocates ngtcp2_frame_chain object and - * assigns its pointer to |*pfrc|. - * - * This function returns 0 if it succeeds, or one of the following - * negative error codes: - * - * NGTCP2_ERR_NOMEM - * Out of memory. - */ -int ngtcp2_frame_chain_new(ngtcp2_frame_chain **pfrc, const ngtcp2_mem *mem); - -/* - * ngtcp2_frame_chain_objalloc_new behaves like - * ngtcp2_frame_chain_new, but it uses |objalloc| to allocate the object. + * ngtcp2_frame_chain_objalloc_new allocates ngtcp2_frame_chain using + * |objalloc|. */ int ngtcp2_frame_chain_objalloc_new(ngtcp2_frame_chain **pfrc, ngtcp2_objalloc *objalloc); /* - * ngtcp2_frame_chain_extralen_new works like ngtcp2_frame_chain_new, - * but it allocates extra memory |extralen| in order to extend - * ngtcp2_frame. + * ngtcp2_frame_chain_extralen_new allocates ngtcp2_frame_chain + * followed by |extralen| bytes. */ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, const ngtcp2_mem *mem); @@ -134,12 +121,18 @@ int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, #define NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES \ (NGTCP2_FRAME_CHAIN_STREAM_AVAIL / sizeof(ngtcp2_vec) + 1) +/* NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES is the length of a token that + changes allocation method. If the length is more than this value, + ngtcp2_frame_chain is allocated without ngtcp2_objalloc. + Otherwise, it is allocated using ngtcp2_objalloc. */ +#define NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES \ + (sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token)) + /* - * ngtcp2_frame_chain_stream_datacnt_objalloc_new works like - * ngtcp2_frame_chain_new, but it allocates enough data to store - * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_stream - * object. If no additional space is required, in other words, - * |datacnt| <= NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES, + * ngtcp2_frame_chain_stream_datacnt_objalloc_new allocates enough + * data to store additional |datacnt| - 1 ngtcp2_vec object after + * ngtcp2_stream object. If no additional space is required, in other + * words, |datacnt| <= NGTCP2_FRAME_CHAIN_STREAM_DATACNT_THRES, * ngtcp2_frame_chain_objalloc_new is called internally. Otherwise, * ngtcp2_frame_chain_extralen_new is used and objalloc is not used. * Therefore, it is important to call ngtcp2_frame_chain_objalloc_del @@ -150,6 +143,13 @@ int ngtcp2_frame_chain_stream_datacnt_objalloc_new(ngtcp2_frame_chain **pfrc, ngtcp2_objalloc *objalloc, const ngtcp2_mem *mem); +/* + * ngtcp2_frame_chain_new_token_objalloc_new allocates enough space to + * store the given token. If |tokenlen| <= + * NGTCP2_FRAME_CHAIN_NEW_TOKEN_THRES, ngtcp2_frame_chain_objalloc_new + * is called internally. Otherwise, ngtcp2_frame_chain_extralen_new + * is used, and objalloc is not used. + */ int ngtcp2_frame_chain_new_token_objalloc_new(ngtcp2_frame_chain **pfrc, const uint8_t *token, size_t tokenlen, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c index 3bfa398480c382..d04b9634c20528 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c @@ -35,7 +35,9 @@ void ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { } static int gaptr_gap_init(ngtcp2_gaptr *gaptr) { - ngtcp2_range range = {0, UINT64_MAX}; + ngtcp2_range range = { + .end = UINT64_MAX, + }; return ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); } @@ -50,7 +52,11 @@ void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) { int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { int rv; - ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; + ngtcp2_range k, m, l, r; + ngtcp2_range q = { + .begin = offset, + .end = offset + datalen, + }; ngtcp2_ksl_it it; if (ngtcp2_ksl_len(&gaptr->gap) == 0) { @@ -110,11 +116,16 @@ uint64_t ngtcp2_gaptr_first_gap_offset(const ngtcp2_gaptr *gaptr) { ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr, uint64_t offset) { - ngtcp2_range q = {offset, offset + 1}; + ngtcp2_range q = { + .begin = offset, + .end = offset + 1, + }; ngtcp2_ksl_it it; if (ngtcp2_ksl_len(&gaptr->gap) == 0) { - ngtcp2_range r = {0, UINT64_MAX}; + ngtcp2_range r = { + .end = UINT64_MAX, + }; return r; } @@ -128,7 +139,10 @@ ngtcp2_range ngtcp2_gaptr_get_first_gap_after(const ngtcp2_gaptr *gaptr, int ngtcp2_gaptr_is_pushed(const ngtcp2_gaptr *gaptr, uint64_t offset, uint64_t datalen) { - ngtcp2_range q = {offset, offset + datalen}; + ngtcp2_range q = { + .begin = offset, + .end = offset + datalen, + }; ngtcp2_ksl_it it; ngtcp2_range m; @@ -138,6 +152,9 @@ int ngtcp2_gaptr_is_pushed(const ngtcp2_gaptr *gaptr, uint64_t offset, it = ngtcp2_ksl_lower_bound_search(&gaptr->gap, &q, ngtcp2_ksl_range_exclusive_search); + + assert(!ngtcp2_ksl_it_end(&it)); + m = ngtcp2_range_intersect(&q, (ngtcp2_range *)ngtcp2_ksl_it_key(&it)); return ngtcp2_range_len(&m) == 0; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c index 5e74f647241816..22c131a1ac677c 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c @@ -33,7 +33,7 @@ #include "ngtcp2_mem.h" #include "ngtcp2_range.h" -static ngtcp2_ksl_blk null_blk = {{{NULL, NULL, 0, 0, {0}}}}; +static ngtcp2_ksl_blk null_blk; ngtcp2_objalloc_def(ksl_blk, ngtcp2_ksl_blk, oplent) diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c index 0b66fceac6c993..5e4726e63ff7ef 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -119,7 +119,11 @@ void ngtcp2_map_print_distance(const ngtcp2_map *map) { static int insert(ngtcp2_map_bucket *table, size_t hashbits, ngtcp2_map_key_type key, void *data) { size_t idx = hash(key, hashbits); - ngtcp2_map_bucket b = {0, key, data}, *bkt; + ngtcp2_map_bucket b = { + .key = key, + .data = data, + }; + ngtcp2_map_bucket *bkt; size_t mask = (1u << hashbits) - 1; for (;;) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c index d30e1f986e97d3..48f430f474f126 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c @@ -51,8 +51,12 @@ static void *default_realloc(void *ptr, size_t size, void *user_data) { return realloc(ptr, size); } -static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, - default_calloc, default_realloc}; +static const ngtcp2_mem mem_default = { + .malloc = default_malloc, + .free = default_free, + .calloc = default_calloc, + .realloc = default_realloc, +}; const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c index 5c82e1bd503f98..d78978492ca8e7 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -145,8 +145,7 @@ int ngtcp2_pkt_decode_version_cid(ngtcp2_version_cid *dest, const uint8_t *data, void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - int64_t pkt_num, size_t pkt_numlen, uint32_t version, - size_t len) { + int64_t pkt_num, size_t pkt_numlen, uint32_t version) { hd->flags = flags; hd->type = type; @@ -167,7 +166,7 @@ void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, hd->tokenlen = 0; hd->pkt_numlen = pkt_numlen; hd->version = version; - hd->len = len; + hd->len = 0; } ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, @@ -2285,8 +2284,7 @@ ngtcp2_ssize ngtcp2_pkt_write_retry( } ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid, - scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version, - /* len = */ 0); + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version); pseudo_retrylen = ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h index 86ebecef7bc870..756076e7a7f258 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -57,6 +57,10 @@ #define NGTCP2_STREAM_LEN_BIT 0x02 #define NGTCP2_STREAM_OFF_BIT 0x04 +/* NGTCP2_MIN_QUIC_PKTLEN is the minimum length of a valid QUIC + packet. */ +#define NGTCP2_MIN_QUIC_PKTLEN 21 + /* NGTCP2_STREAM_OVERHEAD is the maximum number of bytes required other than payload for STREAM frame. That is from type field to the beginning of the payload. */ @@ -407,13 +411,11 @@ void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem); * |dcid| and/or |scid| is NULL, Destination Connection ID and/or * Source Connection ID of |hd| is empty respectively. |pkt_numlen| * is the number of bytes used to encode |pkt_num| and either 1, 2, or - * 4. |version| is QUIC version for long header. |len| is the length - * field of Initial, 0RTT, and Handshake packets. + * 4. |version| is QUIC version for long header. */ void ngtcp2_pkt_hd_init(ngtcp2_pkt_hd *hd, uint8_t flags, uint8_t type, const ngtcp2_cid *dcid, const ngtcp2_cid *scid, - int64_t pkt_num, size_t pkt_numlen, uint32_t version, - size_t len); + int64_t pkt_num, size_t pkt_numlen, uint32_t version); /* * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c index 19e3e3e36aa5e6..162bed00156755 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c @@ -161,19 +161,3 @@ void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { int ngtcp2_pq_empty(const ngtcp2_pq *pq) { return pq->length == 0; } size_t ngtcp2_pq_size(const ngtcp2_pq *pq) { return pq->length; } - -int ngtcp2_pq_each(const ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg) { - size_t i; - - if (pq->length == 0) { - return 0; - } - - for (i = 0; i < pq->length; ++i) { - if ((*fun)(pq->q[i], arg)) { - return 1; - } - } - - return 0; -} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h index 84961c9143978c..aa195a9f7baee3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h @@ -109,17 +109,6 @@ int ngtcp2_pq_empty(const ngtcp2_pq *pq); */ size_t ngtcp2_pq_size(const ngtcp2_pq *pq); -typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg); - -/* - * ngtcp2_pq_each applies |fun| to each item in |pq|. The |arg| is - * passed as arg parameter to callback function. This function must - * not change the ordering key. If the return value from callback is - * nonzero, this function returns 1 immediately without iterating - * remaining items. Otherwise this function returns 0. - */ -int ngtcp2_pq_each(const ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg); - /* * ngtcp2_pq_remove removes |item| from |pq|. |pq| must contain * |item| otherwise the behavior is undefined. diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c index e4fee94eb558d3..471f84c76440fe 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c @@ -170,3 +170,10 @@ void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts) { pv->flags |= NGTCP2_PV_FLAG_CANCEL_TIMER; } + +void ngtcp2_pv_set_fallback(ngtcp2_pv *pv, const ngtcp2_dcid *dcid, + ngtcp2_duration pto) { + pv->flags |= NGTCP2_PV_FLAG_FALLBACK_PRESENT; + ngtcp2_dcid_copy(&pv->fallback_dcid, dcid); + pv->fallback_pto = pto; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h index e9573da497bee4..2d07e41648db2e 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h @@ -71,11 +71,13 @@ void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, /* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is cancelled. */ #define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02u -/* NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE indicates that fallback DCID is - available in ngtcp2_pv. If path validation fails, fallback to the - fallback DCID. If path validation succeeds, fallback DCID is - retired if it does not equal to the current DCID. */ -#define NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE 0x04u +/* NGTCP2_PV_FLAG_FALLBACK_PRESENT indicates that a fallback + Destination Connection ID and PTO are available in ngtcp2_pv. If + path validation fails, then fallback to them. If path validation + succeeds, the fallback Destination Connection ID is retired if it + is not zero length, and does not equal to the current Destination + Connection ID. */ +#define NGTCP2_PV_FLAG_FALLBACK_PRESENT 0x04u /* NGTCP2_PV_FLAG_PREFERRED_ADDR indicates that client is migrating to server's preferred address. This flag is only used by client. */ #define NGTCP2_PV_FLAG_PREFERRED_ADDR 0x10u @@ -191,4 +193,10 @@ ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv); */ void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts); +/* + * ngtcp2_pv_set_fallback sets |dcid| and |pto| as fallback. + */ +void ngtcp2_pv_set_fallback(ngtcp2_pv *pv, const ngtcp2_dcid *dcid, + ngtcp2_duration pto); + #endif /* !defined(NGTCP2_PV_H) */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c index 7bbefc0175c595..e89891532936e8 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c @@ -32,7 +32,7 @@ void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) { ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, const ngtcp2_range *b) { - ngtcp2_range r = {0, 0}; + ngtcp2_range r = {0}; uint64_t begin = ngtcp2_max_uint64(a->begin, b->begin); uint64_t end = ngtcp2_min_uint64(a->end, b->end); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c index 41446739bf699d..353afca4d48fb5 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c @@ -122,4 +122,6 @@ void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset) { return &rb->buf[offset * rb->size]; } -int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->mask + 1; } +int ngtcp2_ringbuf_full(const ngtcp2_ringbuf *rb) { + return rb->len == rb->mask + 1; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h index 6953ea6278f88d..d490524805b1e9 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -113,7 +113,7 @@ void *ngtcp2_ringbuf_get(const ngtcp2_ringbuf *rb, size_t offset); #define ngtcp2_ringbuf_len(RB) ((RB)->len) /* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ -int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); +int ngtcp2_ringbuf_full(const ngtcp2_ringbuf *rb); /* ngtcp2_static_ringbuf_def defines ngtcp2_ringbuf struct wrapper which uses a statically allocated buffer. ngtcp2_ringbuf_free diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c index ce6c2113ddf1ce..853f1d650eaf54 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c @@ -122,7 +122,10 @@ static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, size_t n; int rv; ngtcp2_rob_data *d; - ngtcp2_range range = {offset, offset + len}; + ngtcp2_range range = { + .begin = offset, + .end = offset + len, + }; ngtcp2_ksl_it it; for (it = ngtcp2_ksl_lower_bound_search(&rob->dataksl, &range, @@ -163,7 +166,11 @@ int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, size_t datalen) { int rv; ngtcp2_rob_gap *g; - ngtcp2_range m, l, r, q = {offset, offset + datalen}; + ngtcp2_range m, l, r; + ngtcp2_range q = { + .begin = offset, + .end = offset + datalen, + }; ngtcp2_ksl_it it; it = ngtcp2_ksl_lower_bound_search(&rob->gapksl, &q, diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c index 89c89acdc265a2..181691f3e69401 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c @@ -46,6 +46,11 @@ void ngtcp2_rs_init(ngtcp2_rs *rs) { } void ngtcp2_rst_init(ngtcp2_rst *rst) { + rst->last_seq = -1; + ngtcp2_rst_reset(rst); +} + +void ngtcp2_rst_reset(ngtcp2_rst *rst) { ngtcp2_rs_init(&rst->rs); rst->delivered = 0; rst->delivered_ts = 0; @@ -53,7 +58,7 @@ void ngtcp2_rst_init(ngtcp2_rst *rst) { rst->app_limited = 0; rst->is_cwnd_limited = 0; rst->lost = 0; - rst->last_seq = -1; + rst->valid_after_seq = rst->last_seq; } void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, @@ -108,6 +113,10 @@ void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, ngtcp2_tstamp ts) { ngtcp2_rs *rs = &rst->rs; + if (ent->rst.end_seq <= rst->valid_after_seq) { + return; + } + rst->delivered += ent->pktlen; rst->delivered_ts = ts; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h index 95616eee97d99f..c2580306cc59fe 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -73,11 +73,17 @@ typedef struct ngtcp2_rst { across all packet number spaces, we can replace this with a packet number. */ int64_t last_seq; + /* valid_after_seq is the sequence number, and ignore a packet if + the sequence number of the packet is less than or equal to this + number. */ + int64_t valid_after_seq; int is_cwnd_limited; } ngtcp2_rst; void ngtcp2_rst_init(ngtcp2_rst *rst); +void ngtcp2_rst_reset(ngtcp2_rst *rst); + void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, const ngtcp2_conn_stat *cstat); void ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c index 090355f5dbc938..f7a7f5724b7ac3 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -101,7 +101,6 @@ void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_rst *rst, ngtcp2_cc *cc, rtb->probe_pkt_left = 0; rtb->cc_pkt_num = cc_pkt_num; rtb->cc_bytes_in_flight = 0; - rtb->persistent_congestion_start_ts = UINT64_MAX; rtb->num_lost_pkts = 0; rtb->num_lost_pmtud_pkts = 0; } @@ -672,8 +671,8 @@ static int process_acked_pkt(ngtcp2_rtb_entry *ent, ngtcp2_conn *conn, break; case NGTCP2_FRAME_RETIRE_CONNECTION_ID: - ngtcp2_conn_untrack_retired_dcid_seq(conn, - frc->fr.retire_connection_id.seq); + ngtcp2_dcidtr_untrack_retired_seq(&conn->dcid.dtr, + frc->fr.retire_connection_id.seq); break; case NGTCP2_FRAME_NEW_CONNECTION_ID: assert(conn->scid.num_in_flight); @@ -734,11 +733,11 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns, if ((ecn_acked && fr->type == NGTCP2_FRAME_ACK) || (fr->type == NGTCP2_FRAME_ACK_ECN && - (pktns->rx.ecn.ack.ect0 > fr->ecn.ect0 || - pktns->rx.ecn.ack.ect1 > fr->ecn.ect1 || - pktns->rx.ecn.ack.ce > fr->ecn.ce || - (fr->ecn.ect0 - pktns->rx.ecn.ack.ect0) + - (fr->ecn.ce - pktns->rx.ecn.ack.ce) < + (pktns->acktr.ecn.ack.ect0 > fr->ecn.ect0 || + pktns->acktr.ecn.ack.ect1 > fr->ecn.ect1 || + pktns->acktr.ecn.ack.ce > fr->ecn.ce || + (fr->ecn.ect0 - pktns->acktr.ecn.ack.ect0) + + (fr->ecn.ce - pktns->acktr.ecn.ack.ce) < ecn_acked || fr->ecn.ect0 > pktns->tx.ecn.ect0 || fr->ecn.ect1))) { ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, @@ -755,13 +754,13 @@ static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns, if (fr->type == NGTCP2_FRAME_ACK_ECN) { if (cc->congestion_event && largest_pkt_sent_ts != UINT64_MAX && - fr->ecn.ce > pktns->rx.ecn.ack.ce) { + fr->ecn.ce > pktns->acktr.ecn.ack.ce) { cc->congestion_event(cc, cstat, largest_pkt_sent_ts, 0, ts); } - pktns->rx.ecn.ack.ect0 = fr->ecn.ect0; - pktns->rx.ecn.ack.ect1 = fr->ecn.ect1; - pktns->rx.ecn.ack.ce = fr->ecn.ce; + pktns->acktr.ecn.ack.ect0 = fr->ecn.ect0; + pktns->acktr.ecn.ack.ect1 = fr->ecn.ect1; + pktns->acktr.ecn.ack.ce = fr->ecn.ce; } } @@ -1052,7 +1051,7 @@ static int rtb_detect_lost_pkt(ngtcp2_rtb *rtb, uint64_t *ppkt_lost, max_ack_delay) * NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; - start_ts = ngtcp2_max_uint64(rtb->persistent_congestion_start_ts, + start_ts = ngtcp2_max_uint64(conn->handshake_confirmed_ts, cstat->first_rtt_sample_ts); for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h index 2ef772b2e14f4b..3a9397eac5bb6f 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -184,10 +184,6 @@ typedef struct ngtcp2_rtb { count a packet whose packet number is greater than or equals to cc_pkt_num. */ uint64_t cc_bytes_in_flight; - /* persistent_congestion_start_ts is the time when persistent - congestion evaluation is started. It happens roughly after - handshake is confirmed. */ - ngtcp2_tstamp persistent_congestion_start_ts; /* num_lost_pkts is the number entries in ents which has NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED flag set. */ size_t num_lost_pkts; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c index dda59c48858185..ca517532e3a695 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_transport_params.c @@ -410,7 +410,7 @@ static int decode_varint(uint64_t *pdest, const uint8_t **pp, } len = ngtcp2_get_uvarintlen(p); - if ((uint64_t)(end - p) < len) { + if ((size_t)(end - p) < len) { return -1; } @@ -530,8 +530,11 @@ int ngtcp2_transport_params_decode_versioned(int transport_params_version, params->active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; - p = data; - end = data + datalen; + p = end = data; + + if (datalen) { + end += datalen; + } for (; (size_t)(end - p) >= 2;) { if (decode_varint(¶m_type, &p, end) != 0) { diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c index 0b9c92d47d7d88..dbca8691d64888 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c @@ -35,33 +35,6 @@ ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) { return vec; } -int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, - const ngtcp2_mem *mem) { - size_t len; - uint8_t *p; - - len = sizeof(ngtcp2_vec) + datalen; - - *pvec = ngtcp2_mem_malloc(mem, len); - if (*pvec == NULL) { - return NGTCP2_ERR_NOMEM; - } - - p = (uint8_t *)(*pvec) + sizeof(ngtcp2_vec); - (*pvec)->base = p; - (*pvec)->len = datalen; - - if (datalen) { - /* p = */ ngtcp2_cpymem(p, data, datalen); - } - - return 0; -} - -void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem) { - ngtcp2_mem_free(mem, vec); -} - uint64_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { size_t i; size_t res = 0; @@ -225,13 +198,14 @@ size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t dstcnt, continue; } - dst[j] = src[i]; - - if (dst[j].len > left) { + if (src[i].len > left) { + dst[j].base = src[i].base; dst[j].len = left; + return j + 1; } + dst[j] = src[i]; left -= dst[j].len; ++i; ++j; diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h index f7611efcb7d6ce..55e735d164edcb 100644 --- a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -46,22 +46,6 @@ */ ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len); -/* - * ngtcp2_vec_new allocates and initializes |*pvec| with given |data| - * of length |datalen|. This function allocates memory for |*pvec| - * and the given data with a single allocation, and the contents - * pointed by |data| is copied into the allocated memory space. To - * free the allocated memory, call ngtcp2_vec_del. - */ -int ngtcp2_vec_new(ngtcp2_vec **pvec, const uint8_t *data, size_t datalen, - const ngtcp2_mem *mem); - -/* - * ngtcp2_vec_del frees the memory allocated by |vec| which is - * allocated and initialized by ngtcp2_vec_new. - */ -void ngtcp2_vec_del(ngtcp2_vec *vec, const ngtcp2_mem *mem); - /* * ngtcp2_vec_len returns the sum of length in |vec| of |n| elements. */ diff --git a/deps/sqlite/sqlite3.c b/deps/sqlite/sqlite3.c index 8293fa99188633..37b534afb2787c 100644 --- a/deps/sqlite/sqlite3.c +++ b/deps/sqlite/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.49.0. By combining all the individual C code files into this +** version 3.49.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -18,7 +18,7 @@ ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in -** 4a7dd425dc2a0e5082a9049c9b4a9d4f199a with changes in files: +** 873d4e274b4988d260ba8354a9718324a1c2 with changes in files: ** ** */ @@ -465,9 +465,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.49.0" -#define SQLITE_VERSION_NUMBER 3049000 -#define SQLITE_SOURCE_ID "2025-02-06 11:55:18 4a7dd425dc2a0e5082a9049c9b4a9d4f199a71583d014c24b4cfe276c5a77cde" +#define SQLITE_VERSION "3.49.1" +#define SQLITE_VERSION_NUMBER 3049001 +#define SQLITE_SOURCE_ID "2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -131067,7 +131067,7 @@ static void concatFuncCore( for(i=0; i65528 ) sz = 65528; if( cnt<0 ) cnt = 0; + szAlloc = (i64)sz*(i64)cnt; if( sz==0 || cnt==0 ){ sz = 0; pStart = 0; @@ -182339,10 +182341,10 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){ #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE if( sz>=LOOKASIDE_SMALL*3 ){ nBig = szAlloc/(3*LOOKASIDE_SMALL+sz); - nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL; + nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL; }else if( sz>=LOOKASIDE_SMALL*2 ){ nBig = szAlloc/(LOOKASIDE_SMALL+sz); - nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL; + nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL; }else #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ if( sz>0 ){ @@ -255872,7 +255874,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2025-02-06 11:55:18 4a7dd425dc2a0e5082a9049c9b4a9d4f199a71583d014c24b4cfe276c5a77cde", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70", -1, SQLITE_TRANSIENT); } /* diff --git a/deps/sqlite/sqlite3.h b/deps/sqlite/sqlite3.h index 0153128ee20c54..082a9f9dc44e93 100644 --- a/deps/sqlite/sqlite3.h +++ b/deps/sqlite/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.49.0" -#define SQLITE_VERSION_NUMBER 3049000 -#define SQLITE_SOURCE_ID "2025-02-06 11:55:18 4a7dd425dc2a0e5082a9049c9b4a9d4f199a71583d014c24b4cfe276c5a77cde" +#define SQLITE_VERSION "3.49.1" +#define SQLITE_VERSION_NUMBER 3049001 +#define SQLITE_SOURCE_ID "2025-02-18 13:38:58 873d4e274b4988d260ba8354a9718324a1c26187a4ab4c1cc0227c03d0f10e70" /* ** CAPI3REF: Run-Time Library Version Numbers diff --git a/deps/uv/unofficial.gni b/deps/uv/unofficial.gni index 864414475fa69c..0944d6ddd241b1 100644 --- a/deps/uv/unofficial.gni +++ b/deps/uv/unofficial.gni @@ -90,7 +90,7 @@ template("uv_gn_build") { ldflags = [ "-pthread" ] } if (is_linux) { - libs += [ + libs = [ "m", "dl", "rt", diff --git a/deps/zstd/unofficial.gni b/deps/zstd/unofficial.gni index 46504122764869..fe6c2b425eac2a 100644 --- a/deps/zstd/unofficial.gni +++ b/deps/zstd/unofficial.gni @@ -19,6 +19,7 @@ template("zstd_gn_build") { public_configs = [ ":zstd_config" ] sources = gypi_values.zstd_sources defines = [ "XXH_NAMESPACE=ZSTD_", "ZSTD_MULTITHREAD", "ZSTD_DISABLE_ASM" ] + cflags_c = [ "-Wno-unused-function" ] if (is_posix) { ldflags = [ "-pthread" ] diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 2cca29d06c525a..08756bb9ca23ae 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -1098,7 +1098,7 @@ added: --> * `view` {TypedArray} The {TypedArray} to copy. -* `offset` {integer} The starting offset within `view`. **Default:**: `0`. +* `offset` {integer} The starting offset within `view`. **Default:** `0`. * `length` {integer} The number of elements from `view` to copy. **Default:** `view.length - offset`. * Returns: {Buffer} diff --git a/doc/api/cli.md b/doc/api/cli.md index c70cdd3be45d4c..b88342c3e6a799 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -586,14 +586,14 @@ Disable the ability of starting a debugging session by sending a ### `--disable-warning=code-or-type` -> Stability: 1.1 - Active development - +> Stability: 1.1 - Active development + Disable specific process warnings by `code` or `type`. Warnings emitted from [`process.emitWarning()`][emit_warning] may contain a @@ -795,19 +795,17 @@ node --entry-url 'data:text/javascript,console.log("Hello")' ### `--env-file-if-exists=config` -> Stability: 1.1 - Active development - +> Stability: 1.1 - Active development + Behavior is the same as [`--env-file`][], but an error is not thrown if the file does not exist. ### `--env-file=config` -> Stability: 1.1 - Active development - +> Stability: 1.1 - Active development + Loads environment variables from a file relative to the current directory, making them available to applications on `process.env`. The [environment variables which configure Node.js][environment_variables], such as `NODE_OPTIONS`, @@ -998,14 +998,6 @@ If the ES module being `require()`'d contains top-level `await`, this flag allows Node.js to evaluate the module, try to locate the top-level awaits, and print their location to help users find them. -### `--experimental-quic` - - - -Enables the experimental `node:quic` built-in module. - ### `--experimental-require-module` -If `--use-openssl-ca` is enabled, this overrides and sets OpenSSL's directory +If `--use-openssl-ca` is enabled, or if `--use-system-ca` is enabled on +platforms other than macOS and Windows, this overrides and sets OpenSSL's directory containing trusted certificates. Be aware that unless the child environment is explicitly set, this environment @@ -3525,7 +3535,8 @@ may cause them to trust the same CAs as node. added: v7.7.0 --> -If `--use-openssl-ca` is enabled, this overrides and sets OpenSSL's file +If `--use-openssl-ca` is enabled, or if `--use-system-ca` is enabled on +platforms other than macOS and Windows, this overrides and sets OpenSSL's file containing trusted certificates. Be aware that unless the child environment is explicitly set, this environment diff --git a/doc/api/dns.md b/doc/api/dns.md index af71918496a883..f60397237740fd 100644 --- a/doc/api/dns.md +++ b/doc/api/dns.md @@ -129,6 +129,7 @@ The following methods from the `node:dns` module are available: * [`resolver.resolvePtr()`][`dns.resolvePtr()`] * [`resolver.resolveSoa()`][`dns.resolveSoa()`] * [`resolver.resolveSrv()`][`dns.resolveSrv()`] +* [`resolver.resolveTlsa()`][`dns.resolveTlsa()`] * [`resolver.resolveTxt()`][`dns.resolveTxt()`] * [`resolver.reverse()`][`dns.reverse()`] * [`resolver.setServers()`][`dns.setServers()`] @@ -444,6 +445,7 @@ records. The type and structure of individual results varies based on `rrtype`: | `'PTR'` | pointer records | {string} | [`dns.resolvePtr()`][] | | `'SOA'` | start of authority records | {Object} | [`dns.resolveSoa()`][] | | `'SRV'` | service records | {Object} | [`dns.resolveSrv()`][] | +| `'TLSA'` | certificate associations | {Object} | [`dns.resolveTlsa()`][] | | `'TXT'` | text records | {string\[]} | [`dns.resolveTxt()`][] | On error, `err` is an [`Error`][] object, where `err.code` is one of the @@ -543,6 +545,7 @@ will be present on the object: | `'PTR'` | `value` | | `'SOA'` | Refer to [`dns.resolveSoa()`][] | | `'SRV'` | Refer to [`dns.resolveSrv()`][] | +| `'TLSA'` | Refer to [`dns.resolveTlsa()`][] | | `'TXT'` | This type of record contains an array property called `entries` which refers to [`dns.resolveTxt()`][], e.g. `{ entries: ['...'], type: 'TXT' }` | Here is an example of the `ret` object passed to the callback: @@ -802,6 +805,41 @@ be an array of objects with the following properties: } ``` +## `dns.resolveTlsa(hostname, callback)` + + + + + +* `hostname` {string} +* `callback` {Function} + * `err` {Error} + * `records` {Object\[]} + + + +Uses the DNS protocol to resolve certificate associations (`TLSA` records) for +the `hostname`. The `records` argument passed to the `callback` function is an +array of objects with these properties: + +* `certUsage` +* `selector` +* `match` +* `data` + + + +```js +{ + certUsage: 3, + selector: 1, + match: 1, + data: [ArrayBuffer] +} +``` + ## `dns.resolveTxt(hostname, callback)` + +* `hostname` {string} + +Uses the DNS protocol to resolve certificate associations (`TLSA` records) for +the `hostname`. On success, the `Promise` is resolved with an array of objects +with these properties: + +* `certUsage` +* `selector` +* `match` +* `data` + + + +```js +{ + certUsage: 3, + selector: 1, + match: 1, + data: [ArrayBuffer] +} +``` + ### `dnsPromises.resolveTxt(hostname)` diff --git a/doc/api/inspector.md b/doc/api/inspector.md index 2892779403d256..a0852134511151 100644 --- a/doc/api/inspector.md +++ b/doc/api/inspector.md @@ -31,12 +31,12 @@ const inspector = require('node:inspector'); ## Promises API -> Stability: 1 - Experimental - +> Stability: 1 - Experimental + ### Class: `inspector.Session` * Extends: {EventEmitter} diff --git a/doc/api/module.md b/doc/api/module.md index 7f9137a25f068e..3eb2c7d4833e21 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -1156,7 +1156,11 @@ changes: Node.js default `load` hook after the last user-supplied `load` hook * `url` {string} * `context` {Object|undefined} When omitted, defaults are provided. When provided, defaults are - merged in with preference to the provided properties. + merged in with preference to the provided properties. In the default `nextLoad`, if + the module pointed to by `url` does not have explicit module type information, + `context.format` is mandatory. + * Returns: {Object|Promise} The asynchronous version takes either an object containing the following properties, or a `Promise` that will resolve to such an object. The synchronous version only accepts an object returned synchronously. @@ -1354,36 +1358,32 @@ transpiler hooks should only be used for development and testing purposes. ```mjs // coffeescript-hooks.mjs import { readFile } from 'node:fs/promises'; -import { dirname, extname, resolve as resolvePath } from 'node:path'; -import { cwd } from 'node:process'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { findPackageJSON } from 'node:module'; import coffeescript from 'coffeescript'; const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/; export async function load(url, context, nextLoad) { if (extensionsRegex.test(url)) { - // CoffeeScript files can be either CommonJS or ES modules, so we want any - // CoffeeScript file to be treated by Node.js the same as a .js file at the - // same location. To determine how Node.js would interpret an arbitrary .js - // file, search up the file system for the nearest parent package.json file - // and read its "type" field. - const format = await getPackageType(url); - - const { source: rawSource } = await nextLoad(url, { ...context, format }); + // CoffeeScript files can be either CommonJS or ES modules. Use a custom format + // to tell Node.js not to detect its module type. + const { source: rawSource } = await nextLoad(url, { ...context, format: 'coffee' }); // This hook converts CoffeeScript source code into JavaScript source code // for all imported CoffeeScript files. const transformedSource = coffeescript.compile(rawSource.toString(), url); + // To determine how Node.js would interpret the transpilation result, + // search up the file system for the nearest parent package.json file + // and read its "type" field. return { - format, + format: await getPackageType(url), shortCircuit: true, source: transformedSource, }; } // Let Node.js handle all other URLs. - return nextLoad(url); + return nextLoad(url, context); } async function getPackageType(url) { @@ -1394,25 +1394,12 @@ async function getPackageType(url) { // this simple truthy check for whether `url` contains a file extension will // work for most projects but does not cover some edge-cases (such as // extensionless files or a url ending in a trailing space) - const isFilePath = !!extname(url); - // If it is a file path, get the directory it's in - const dir = isFilePath ? - dirname(fileURLToPath(url)) : - url; - // Compose a file path to a package.json in the same directory, - // which may or may not exist - const packagePath = resolvePath(dir, 'package.json'); - // Try to read the possibly nonexistent package.json - const type = await readFile(packagePath, { encoding: 'utf8' }) - .then((filestring) => JSON.parse(filestring).type) - .catch((err) => { - if (err?.code !== 'ENOENT') console.error(err); - }); - // If package.json existed and contained a `type` field with a value, voilà - if (type) return type; - // Otherwise, (if not at the root) continue checking the next directory up - // If at the root, stop and return false - return dir.length > 1 && getPackageType(resolvePath(dir, '..')); + const pJson = findPackageJSON(url); + + return readFile(pJson, 'utf8') + .then(JSON.parse) + .then((json) => json?.type) + .catch(() => undefined); } ``` @@ -1420,46 +1407,38 @@ async function getPackageType(url) { ```mjs // coffeescript-sync-hooks.mjs -import { readFileSync } from 'node:fs/promises'; -import { registerHooks } from 'node:module'; -import { dirname, extname, resolve as resolvePath } from 'node:path'; -import { cwd } from 'node:process'; -import { fileURLToPath, pathToFileURL } from 'node:url'; +import { readFileSync } from 'node:fs'; +import { registerHooks, findPackageJSON } from 'node:module'; import coffeescript from 'coffeescript'; const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/; function load(url, context, nextLoad) { if (extensionsRegex.test(url)) { - const format = getPackageType(url); - - const { source: rawSource } = nextLoad(url, { ...context, format }); + const { source: rawSource } = nextLoad(url, { ...context, format: 'coffee' }); const transformedSource = coffeescript.compile(rawSource.toString(), url); return { - format, + format: getPackageType(url), shortCircuit: true, source: transformedSource, }; } - return nextLoad(url); + return nextLoad(url, context); } function getPackageType(url) { - const isFilePath = !!extname(url); - const dir = isFilePath ? dirname(fileURLToPath(url)) : url; - const packagePath = resolvePath(dir, 'package.json'); - - let type; + const pJson = findPackageJSON(url); + if (!pJson) { + return undefined; + } try { - const filestring = readFileSync(packagePath, { encoding: 'utf8' }); - type = JSON.parse(filestring).type; - } catch (err) { - if (err?.code !== 'ENOENT') console.error(err); + const file = readFileSync(pJson, 'utf-8'); + return JSON.parse(file)?.type; + } catch { + return undefined; } - if (type) return type; - return dir.length > 1 && getPackageType(resolvePath(dir, '..')); } registerHooks({ load }); @@ -1481,6 +1460,21 @@ console.log "Brought to you by Node.js version #{version}" export scream = (str) -> str.toUpperCase() ``` +For the sake of running the example, add a `package.json` file containing the +module type of the CoffeeScript files. + +```json +{ + "type": "module" +} +``` + +This is only for running the example. In real world loaders, `getPackageType()` must be +able to return an `format` known to Node.js even in the absence of an explicit type in a +`package.json`, or otherwise the `nextLoad` call would throw `ERR_UNKNOWN_FILE_EXTENSION` +(if undefined) or `ERR_UNKNOWN_MODULE_FORMAT` (if it's not a known format listed in +the [load hook][] documentation). + With the preceding hooks modules, running `node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee` or `node --import ./coffeescript-sync-hooks.mjs ./main.coffee` @@ -1658,6 +1652,13 @@ added: #### `new SourceMap(payload[, { lineLengths }])` + + * `payload` {Object} * `lineLengths` {number\[]} @@ -1720,6 +1721,12 @@ columnNumber)` #### `sourceMap.findOrigin(lineNumber, columnNumber)` + + * `lineNumber` {number} The 1-indexed line number of the call site in the generated source * `columnNumber` {number} The 1-indexed column number diff --git a/doc/api/modules.md b/doc/api/modules.md index 204809f5b779b0..47c9c93e571fa1 100644 --- a/doc/api/modules.md +++ b/doc/api/modules.md @@ -203,8 +203,8 @@ regarding which files are parsed as ECMAScript modules. 3. The file has a `.js` extension, the closest `package.json` does not contain `"type": "commonjs"`, and the module contains ES module syntax. -If the ES Module being loaded meet the requirements, `require()` can load it and -return the module namespace object. In this case it is similar to dynamic +If the ES Module being loaded meets the requirements, `require()` can load it and +return the [module namespace object][]. In this case it is similar to dynamic `import()` but is run synchronously and returns the name space object directly. @@ -212,7 +212,7 @@ With the following ES Modules: ```mjs // distance.mjs -export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; } +export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); } ``` ```mjs @@ -248,8 +248,8 @@ This property is experimental and can change in the future. It should only be us by tools converting ES modules into CommonJS modules, following existing ecosystem conventions. Code authored directly in CommonJS should avoid depending on it. -When a ES Module contains both named exports and a default export, the result returned by `require()` -is the module namespace object, which places the default export in the `.default` property, similar to +When an ES Module contains both named exports and a default export, the result returned by `require()` +is the [module namespace object][], which places the default export in the `.default` property, similar to the results returned by `import()`. To customize what should be returned by `require(esm)` directly, the ES Module can export the desired value using the string name `"module.exports"`. @@ -264,7 +264,7 @@ export default class Point { // `distance` is lost to CommonJS consumers of this module, unless it's // added to `Point` as a static property. -export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; } +export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); } export { Point as 'module.exports' } ``` @@ -288,7 +288,7 @@ named exports attached to it as properties. For example with the example above, ```mjs -export function distance(a, b) { return (b.x - a.x) ** 2 + (b.y - a.y) ** 2; } +export function distance(a, b) { return Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); } export default class Point { constructor(x, y) { this.x = x; this.y = y; } @@ -341,7 +341,7 @@ require(X) from module at path Y a. return the core module b. STOP 2. If X begins with '/' - a. set Y to be the file system root + a. set Y to the file system root 3. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) @@ -368,7 +368,7 @@ LOAD_AS_FILE(X) 1. MAYBE_DETECT_AND_LOAD(X.js) c. If the SCOPE/package.json contains "type" field, 1. If the "type" field is "module", load X.js as an ECMAScript module. STOP. - 2. If the "type" field is "commonjs", load X.js as an CommonJS module. STOP. + 2. If the "type" field is "commonjs", load X.js as a CommonJS module. STOP. d. MAYBE_DETECT_AND_LOAD(X.js) 3. If X.json is a file, load X.json to a JavaScript Object. STOP 4. If X.node is a file, load X.node as binary addon. STOP @@ -379,7 +379,7 @@ LOAD_INDEX(X) b. If no scope was found, load X/index.js as a CommonJS module. STOP. c. If the SCOPE/package.json contains "type" field, 1. If the "type" field is "module", load X/index.js as an ECMAScript module. STOP. - 2. Else, load X/index.js as an CommonJS module. STOP. + 2. Else, load X/index.js as a CommonJS module. STOP. 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP 3. If X/index.node is a file, load X/index.node as binary addon. STOP @@ -511,9 +511,11 @@ by that name. Some built-in modules are always preferentially loaded if their identifier is passed to `require()`. For instance, `require('http')` will always -return the built-in HTTP module, even if there is a file by that name. The list -of built-in modules that can be loaded without using the `node:` prefix is exposed -in [`module.builtinModules`][], listed without the prefix. +return the built-in HTTP module, even if there is a file by that name. + +The list of all the built-in modules can be retrieved from [`module.builtinModules`][]. +The modules being all listed without the `node:` prefix, except those that mandate such +prefix (as explained in the next section). ### Built-in modules with mandatory `node:` prefix @@ -1264,9 +1266,9 @@ This section was moved to * `module.findSourceMap(path)` * Class: `module.SourceMap` - * `new SourceMap(payload)` + * `new SourceMap(payload)` * `sourceMap.payload` - * `sourceMap.findEntry(lineNumber, columnNumber)` + * `sourceMap.findEntry(lineNumber, columnNumber)` [Determining module system]: packages.md#determining-module-system [ECMAScript Modules]: esm.md @@ -1294,6 +1296,7 @@ This section was moved to [`process.features.require_module`]: process.md#processfeaturesrequire_module [`require.main`]: #requiremain [exports shortcut]: #exports-shortcut +[module namespace object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import#module_namespace_object [module resolution]: #all-together [native addons]: addons.md [subpath exports]: packages.md#subpath-exports diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 58f4114b39a3de..01963535c47852 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -2097,7 +2097,7 @@ const obs = new PerformanceObserver((list, observer) => { performance.clearMeasures(); observer.disconnect(); }); -obs.observe({ entryTypes: ['measure'], buffered: true }); +obs.observe({ entryTypes: ['measure'] }); setTimeout(() => {}, 1000); ``` @@ -2154,7 +2154,7 @@ const obs = new PerformanceObserver((list) => { performance.clearMeasures(); obs.disconnect(); }); -obs.observe({ entryTypes: ['function'], buffered: true }); +obs.observe({ entryTypes: ['function'] }); require('some-module'); ``` diff --git a/doc/api/process.md b/doc/api/process.md index 5d3a1f2d214c83..f03cfa88d83513 100644 --- a/doc/api/process.md +++ b/doc/api/process.md @@ -4204,6 +4204,25 @@ Thrown: [DeprecationWarning: test] { name: 'DeprecationWarning' } ``` +## `process.threadCpuUsage([previousValue])` + + + +* `previousValue` {Object} A previous return value from calling + `process.cpuUsage()` +* Returns: {Object} + * `user` {integer} + * `system` {integer} + +The `process.threadCpuUsage()` method returns the user and system CPU time usage of +the current worker thread, in an object with properties `user` and `system`, whose +values are microsecond values (millionth of a second). + +The result of a previous call to `process.threadCpuUsage()` can be passed as the +argument to the function, to get a diff reading. + ## `process.title` * `count` {number} The number of assertions and subtests that are expected to run. +* `options` {Object} Additional options for the plan. + * `wait` {boolean|number} The wait time for the plan: + * If `true`, the plan waits indefinitely for all assertions and subtests to run. + * If `false`, the plan performs an immediate check after the test function completes, + without waiting for any pending assertions or subtests. + Any assertions or subtests that complete after this check will not be counted towards the plan. + * If a number, it specifies the maximum wait time in milliseconds + before timing out while waiting for expected assertions and subtests to be matched. + If the timeout is reached, the test will fail. + **Default:** `false`. This function is used to set the number of assertions and subtests that are expected to run within the test. If the number of assertions and subtests that run does not match the @@ -3449,6 +3463,26 @@ test('planning with streams', (t, done) => { }); ``` +When using the `wait` option, you can control how long the test will wait for the expected assertions. +For example, setting a maximum wait time ensures that the test will wait for asynchronous assertions +to complete within the specified timeframe: + +```js +test('plan with wait: 2000 waits for async assertions', (t) => { + t.plan(1, { wait: 2000 }); // Waits for up to 2 seconds for the assertion to complete. + + const asyncActivity = () => { + setTimeout(() => { + t.assert.ok(true, 'Async assertion completed within the wait time'); + }, 1000); // Completes after 1 second, within the 2-second wait time. + }; + + asyncActivity(); // The test will pass because the assertion is completed in time. +}); +``` + +Note: If a `wait` timeout is specified, it begins counting down only after the test function finishes executing. + ### `context.runOnly(shouldRunOnlyTests)` + + > Stability: 1.1 - Active development ## Enabling diff --git a/doc/api/url.md b/doc/api/url.md index b37076da9bd38e..a0b1e8ffbef86e 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -716,12 +716,12 @@ if `input` is not a valid. ### Class: `URLPattern` -> Stability: 1 - Experimental - +> Stability: 1 - Experimental + The `URLPattern` API provides an interface to match URLs or parts of URLs against a pattern. diff --git a/doc/api/util.md b/doc/api/util.md index 9d0cd21ca9b971..8ca762317a7099 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -366,8 +366,6 @@ util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 }); ## `util.getCallSites(frameCountOrOptions, [options])` -> Stability: 1.1 - Active development - +> Stability: 1.1 - Active development + * `frameCount` {number} Optional number of frames to capture as call site objects. **Default:** `10`. Allowable range is between 1 and 200. * `options` {Object} Optional @@ -1721,14 +1721,14 @@ $ node negate.js --no-logfile --logfile=test.log --color --no-color ## `util.parseEnv(content)` -> Stability: 1.1 - Active development - +> Stability: 1.1 - Active development + * `content` {string} The raw contents of a `.env` file. @@ -1922,8 +1922,6 @@ console.log(util.stripVTControlCharacters('\u001B[4mvalue\u001B[0m')); ## `util.styleText(format, text[, options])` -> Stability: 2 - Stable. -