From 01dcf4d1d8d564107f399d806f6f95a78dea36fb Mon Sep 17 00:00:00 2001 From: cjihrig <cjihrig@gmail.com> Date: Fri, 12 Mar 2021 18:12:15 -0500 Subject: [PATCH 01/85] tools: update ESLint to 7.22.0 Update ESLint to 7.22.0 PR-URL: https://github.com/nodejs/node/pull/37734 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> --- .../node_modules/bail/package.json | 98 +- .../character-entities-legacy/package.json | 92 +- .../character-entities/package.json | 94 +- .../character-reference-invalid/package.json | 94 +- .../collapse-white-space/package.json | 94 +- .../node_modules/extend/package.json | 92 +- .../node_modules/inherits/package.json | 35 +- .../node_modules/is-alphabetical/package.json | 98 +- .../is-alphanumerical/package.json | 102 +- .../node_modules/is-buffer/package.json | 13 +- .../node_modules/is-decimal/package.json | 98 +- .../node_modules/is-hexadecimal/package.json | 98 +- .../node_modules/is-plain-obj/package.json | 33 +- .../is-whitespace-character/package.json | 102 +- .../is-word-character/package.json | 98 +- .../markdown-escapes/package.json | 96 +- .../node_modules/parse-entities/package.json | 96 +- .../node_modules/remark-parse/package.json | 64 +- .../node_modules/repeat-string/package.json | 78 +- .../node_modules/replace-ext/package.json | 69 +- .../node_modules/state-toggle/package.json | 94 +- .../trim-trailing-lines/package.json | 86 +- .../node_modules/trim/package.json | 30 +- .../node_modules/trough/package.json | 94 +- .../node_modules/unherit/package.json | 84 +- .../node_modules/unified/package.json | 90 +- .../node_modules/unist-util-is/package.json | 100 +- .../unist-util-remove-position/package.json | 98 +- .../package.json | 90 +- .../unist-util-visit-parents/package.json | 80 +- .../unist-util-visit/package.json | 104 +- .../node_modules/vfile-location/package.json | 86 +- .../node_modules/vfile-message/package.json | 78 +- .../node_modules/vfile/package.json | 119 +- .../node_modules/x-is-string/package.json | 46 +- .../node_modules/xtend/package.json | 60 +- .../eslint-plugin-markdown/package.json | 77 +- tools/node_modules/eslint/README.md | 4 +- .../eslint/lib/cli-engine/formatters/html.js | 4 +- .../lib/cli-engine/lint-result-cache.js | 1 - .../eslint/lib/linter/report-translator.js | 17 + .../eslint/lib/rule-tester/rule-tester.js | 4 +- .../node_modules/eslint/lib/rules/eol-last.js | 2 +- .../eslint/lib/rules/lines-around-comment.js | 7 +- .../eslint/lib/rules/max-lines.js | 2 +- .../eslint/lib/rules/object-curly-newline.js | 2 +- .../eslint/lib/shared/runtime-info.js | 3 +- .../@babel/code-frame/package.json | 34 +- .../helper-validator-identifier/package.json | 30 +- .../@babel/highlight/lib/index.js | 23 +- .../node_modules/ansi-styles/index.js | 165 -- .../node_modules/ansi-styles/package.json | 56 - .../highlight/node_modules/chalk/package.json | 149 +- .../node_modules/color-convert/index.js | 78 - .../node_modules/color-name/package.json | 25 - .../highlight/node_modules/has-flag/index.js | 8 - .../node_modules/supports-color/package.json | 53 - .../@babel/highlight/package.json | 36 +- .../node_modules/globals/globals.json | 1586 +++++++++++++++++ .../eslintrc/node_modules/globals/index.js | 2 + .../eslintrc/node_modules/globals/license | 9 + .../node_modules/globals/package.json | 61 + .../eslintrc/node_modules/globals/readme.md | 56 + .../@eslint/eslintrc/package.json | 91 +- .../node_modules/acorn-jsx/package.json | 29 +- .../eslint/node_modules/acorn/package.json | 33 +- .../eslint/node_modules/ajv/package.json | 132 +- .../node_modules/ansi-colors/package.json | 74 +- .../node_modules/ansi-regex/package.json | 117 +- .../eslint/node_modules/ansi-styles/index.js | 136 +- .../node_modules/ansi-styles/package.json | 119 +- .../eslint/node_modules/ansi-styles/readme.md | 33 +- .../eslint/node_modules/argparse/package.json | 55 +- .../node_modules/astral-regex/package.json | 73 +- .../node_modules/balanced-match/package.json | 41 +- .../node_modules/brace-expansion/package.json | 43 +- .../node_modules/callsites/package.json | 85 +- .../chalk/node_modules/ansi-styles/index.js | 163 ++ .../node_modules/ansi-styles/license | 0 .../node_modules/ansi-styles/package.json | 65 + .../node_modules/ansi-styles/readme.md | 33 +- .../node_modules/color-convert/LICENSE | 0 .../node_modules/color-convert/README.md | 0 .../node_modules/color-convert/conversions.js | 591 +++--- .../chalk/node_modules/color-convert/index.js | 81 + .../node_modules/color-convert/package.json | 58 +- .../node_modules/color-convert/route.js | 44 +- .../node_modules/color-name/LICENSE | 0 .../node_modules/color-name/README.md | 0 .../node_modules/color-name/index.js | 0 .../node_modules/color-name/package.json | 33 + .../chalk/node_modules/has-flag/index.js | 8 + .../node_modules/has-flag/license | 0 .../node_modules/has-flag/package.json | 41 +- .../node_modules/has-flag/readme.md | 19 + .../node_modules/supports-color/browser.js | 0 .../node_modules/supports-color/index.js | 52 +- .../node_modules/supports-color/license | 0 .../node_modules/supports-color/package.json | 62 + .../node_modules/supports-color/readme.md | 16 +- .../eslint/node_modules/chalk/package.json | 143 +- .../node_modules/color-convert/conversions.js | 591 +++--- .../node_modules/color-convert/index.js | 51 +- .../node_modules/color-convert/package.json | 58 +- .../node_modules/color-convert/route.js | 44 +- .../node_modules/color-name/package.json | 58 +- .../node_modules/concat-map/package.json | 104 +- .../node_modules/cross-spawn/package.json | 94 +- .../eslint/node_modules/debug/package.json | 87 +- .../eslint/node_modules/deep-is/package.json | 46 +- .../eslint/node_modules/doctrine/package.json | 72 +- .../node_modules/emoji-regex/package.json | 60 +- .../eslint/node_modules/enquirer/package.json | 64 +- .../escape-string-regexp/package.json | 51 +- .../node_modules/eslint-scope/package.json | 57 +- .../eslint-visitor-keys/package.json | 54 +- .../node_modules/eslint-utils/package.json | 64 +- .../eslint-visitor-keys/package.json | 52 +- .../eslint-visitor-keys/package.json | 54 +- .../eslint/node_modules/espree/package.json | 54 +- .../eslint/node_modules/esprima/package.json | 116 +- .../node_modules/estraverse/package.json | 47 +- .../eslint/node_modules/esquery/package.json | 103 +- .../node_modules/estraverse/package.json | 47 +- .../node_modules/esrecurse/package.json | 55 +- .../node_modules/estraverse/package.json | 47 +- .../eslint/node_modules/esutils/package.json | 49 +- .../node_modules/fast-deep-equal/package.json | 72 +- .../fast-json-stable-stringify/package.json | 47 +- .../fast-levenshtein/package.json | 48 +- .../file-entry-cache/package.json | 99 +- .../node_modules/flat-cache/package.json | 111 +- .../eslint/node_modules/flatted/package.json | 70 +- .../node_modules/fs.realpath/package.json | 44 +- .../functional-red-black-tree/package.json | 39 +- .../eslint/node_modules/glob-parent/index.js | 3 +- .../node_modules/glob-parent/package.json | 69 +- .../eslint/node_modules/glob/package.json | 58 +- .../eslint/node_modules/globals/globals.json | 174 +- .../globals/node_modules/type-fest/license | 9 + .../node_modules/type-fest/package.json | 67 + .../globals/node_modules/type-fest/readme.md | 658 +++++++ .../eslint/node_modules/globals/package.json | 114 +- .../eslint/node_modules/globals/readme.md | 8 +- .../eslint/node_modules/has-flag/index.js | 10 +- .../eslint/node_modules/has-flag/package.json | 97 +- .../eslint/node_modules/has-flag/readme.md | 19 - .../eslint/node_modules/ignore/package.json | 81 +- .../node_modules/import-fresh/package.json | 93 +- .../node_modules/imurmurhash/package.json | 46 +- .../eslint/node_modules/inflight/package.json | 40 +- .../eslint/node_modules/inherits/package.json | 35 +- .../node_modules/is-extglob/package.json | 46 +- .../is-fullwidth-code-point/package.json | 91 +- .../eslint/node_modules/is-glob/package.json | 63 +- .../eslint/node_modules/isexe/package.json | 40 +- .../node_modules/js-tokens/package.json | 47 +- .../eslint/node_modules/js-yaml/package.json | 79 +- .../json-schema-traverse/package.json | 48 +- .../package.json | 60 +- .../eslint/node_modules/levn/package.json | 51 +- .../eslint/node_modules/lodash/package.json | 45 +- .../node_modules/lru-cache/package.json | 60 +- .../node_modules/minimatch/package.json | 48 +- .../eslint/node_modules/ms/package.json | 49 +- .../node_modules/natural-compare/package.json | 56 +- .../eslint/node_modules/once/package.json | 40 +- .../node_modules/optionator/package.json | 59 +- .../node_modules/parent-module/package.json | 99 +- .../path-is-absolute/package.json | 33 +- .../eslint/node_modules/path-key/package.json | 85 +- .../node_modules/prelude-ls/package.json | 51 +- .../eslint/node_modules/progress/package.json | 55 +- .../eslint/node_modules/punycode/package.json | 74 +- .../eslint/node_modules/regexpp/package.json | 83 +- .../require-from-string/package.json | 39 +- .../node_modules/resolve-from/package.json | 75 +- .../eslint/node_modules/rimraf/package.json | 53 +- .../eslint/node_modules/semver/package.json | 51 +- .../node_modules/shebang-command/package.json | 75 +- .../node_modules/shebang-regex/package.json | 77 +- .../node_modules/ansi-styles/index.js | 163 ++ .../node_modules/ansi-styles/license | 9 + .../node_modules/ansi-styles/package.json | 65 + .../node_modules/ansi-styles/readme.md | 152 ++ .../node_modules/color-convert/LICENSE | 21 + .../node_modules/color-convert/README.md | 68 + .../node_modules/color-convert/conversions.js | 839 +++++++++ .../node_modules/color-convert/index.js | 81 + .../node_modules/color-convert/package.json | 60 + .../node_modules/color-convert/route.js | 97 + .../node_modules/color-name/LICENSE | 8 + .../node_modules/color-name/README.md | 11 + .../node_modules/color-name/index.js | 152 ++ .../node_modules/color-name/package.json | 33 + .../node_modules/slice-ansi/package.json | 111 +- .../node_modules/sprintf-js/package.json | 51 +- .../node_modules/string-width/package.json | 119 +- .../node_modules/strip-ansi/package.json | 115 +- .../strip-json-comments/package.json | 101 +- .../node_modules/supports-color/index.js | 52 +- .../node_modules/supports-color/package.json | 113 +- .../node_modules/supports-color/readme.md | 16 +- .../table/node_modules/ajv/.tonic_example.js | 10 +- .../table/node_modules/ajv/LICENSE | 2 +- .../table/node_modules/ajv/README.md | 279 +-- .../ajv/dist/compile/codegen/index.js | 14 + .../node_modules/ajv/dist/compile/index.js | 4 +- .../ajv/dist/compile/jtd/parse.js | 341 ++++ .../ajv/dist/compile/jtd/serialize.js | 223 +++ .../ajv/dist/compile/jtd/types.js | 14 + .../node_modules/ajv/dist/compile/names.js | 5 + .../ajv/dist/compile/subschema.js | 1 + .../ajv/dist/compile/timestamp.js | 2 + .../node_modules/ajv/dist/compile/util.js | 9 +- .../dist/compile/validate/applicability.js | 3 +- .../table/node_modules/ajv/dist/core.js | 2 + .../table/node_modules/ajv/dist/jtd.js | 24 + .../ajv/dist/runtime/parseJson.js | 183 ++ .../node_modules/ajv/dist/runtime/quote.js | 28 + .../node_modules/ajv/dist/types/jtd-schema.js | 3 + .../applicator/additionalProperties.js | 7 +- .../vocabularies/applicator/dependencies.js | 6 +- .../vocabularies/applicator/properties.js | 6 +- .../ajv/dist/vocabularies/code.js | 29 +- .../ajv/dist/vocabularies/jtd/properties.js | 9 +- .../ajv/dist/vocabularies/jtd/ref.js | 18 +- .../ajv/dist/vocabularies/jtd/type.js | 23 +- .../dist/vocabularies/validation/required.js | 16 +- .../table/node_modules/ajv/package.json | 141 +- .../node_modules/ajv/scripts/publish-bundles | 4 +- .../node_modules/ajv/scripts/publish-gh-pages | 31 - .../node_modules/ajv/scripts/publish-site | 34 + .../json-schema-traverse/package.json | 50 +- .../eslint/node_modules/table/package.json | 12 +- .../node_modules/text-table/package.json | 92 +- .../node_modules/type-check/package.json | 51 +- .../node_modules/type-fest/package.json | 109 +- .../eslint/node_modules/uri-js/package.json | 79 +- .../v8-compile-cache/package.json | 51 +- .../v8-compile-cache/v8-compile-cache.js | 14 +- .../eslint/node_modules/which/package.json | 56 +- .../node_modules/word-wrap/package.json | 95 +- .../eslint/node_modules/wrappy/package.json | 42 +- .../eslint/node_modules/yallist/package.json | 42 +- tools/node_modules/eslint/package.json | 121 +- 246 files changed, 12641 insertions(+), 6050 deletions(-) delete mode 100644 tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/index.js delete mode 100644 tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/package.json delete mode 100644 tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/index.js delete mode 100644 tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/package.json delete mode 100644 tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/index.js delete mode 100644 tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/package.json create mode 100644 tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/globals.json create mode 100644 tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/index.js create mode 100644 tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/license create mode 100644 tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/package.json create mode 100644 tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/readme.md create mode 100644 tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/index.js rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/ansi-styles/license (100%) create mode 100644 tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/package.json rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/ansi-styles/readme.md (77%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-convert/LICENSE (100%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-convert/README.md (100%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-convert/conversions.js (51%) create mode 100644 tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/index.js rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-convert/package.json (53%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-convert/route.js (59%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-name/LICENSE (100%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-name/README.md (100%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/color-name/index.js (100%) create mode 100644 tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/package.json create mode 100644 tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/index.js rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/has-flag/license (100%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/has-flag/package.json (53%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/has-flag/readme.md (61%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/supports-color/browser.js (100%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/supports-color/index.js (62%) rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/supports-color/license (100%) create mode 100644 tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/package.json rename tools/node_modules/eslint/node_modules/{@babel/highlight => chalk}/node_modules/supports-color/readme.md (67%) create mode 100644 tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/license create mode 100644 tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/package.json create mode 100644 tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/readme.md create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/index.js create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/license create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/package.json create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/readme.md create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/LICENSE create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/README.md create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/conversions.js create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/index.js create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/package.json create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/route.js create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/LICENSE create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/README.md create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/index.js create mode 100644 tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/package.json create mode 100644 tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/parse.js create mode 100644 tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/serialize.js create mode 100644 tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/types.js create mode 100644 tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/parseJson.js create mode 100644 tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/quote.js create mode 100644 tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/types/jtd-schema.js delete mode 100755 tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-gh-pages create mode 100755 tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-site diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/bail/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/bail/package.json index 8f8539d32b89b6..1544ac972fde04 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/bail/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/bail/package.json @@ -1,29 +1,23 @@ { - "name": "bail", - "version": "1.0.5", - "description": "Throw a given error", - "license": "MIT", - "keywords": [ - "fail", - "bail", - "throw", - "callback", - "error" - ], - "repository": "wooorm/bail", - "bugs": "https://github.com/wooorm/bail/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/bail/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Throw a given error", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -34,14 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify index.js -s bail -o bail.js", - "build-mangle": "browserify index.js -s bail -p tinyify -o bail.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/bail#readme", + "keywords": [ + "fail", + "bail", + "throw", + "callback", + "error" + ], + "license": "MIT", + "name": "bail", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -51,22 +59,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/bail.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.js -s bail -o bail.js", + "build-mangle": "browserify index.js -s bail -p tinyify -o bail.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.5", "xo": { "prettier": true, "esnext": false, "ignores": [ "bail.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities-legacy/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities-legacy/package.json index e532b8714df3e8..701c76396e0c2f 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities-legacy/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities-legacy/package.json @@ -1,32 +1,23 @@ { - "name": "character-entities-legacy", - "version": "1.1.4", - "description": "HTML legacy character entity information", - "license": "MIT", - "keywords": [ - "html", - "entity", - "entities", - "character", - "reference", - "name", - "replacement" - ], - "repository": "wooorm/character-entities-legacy", - "bugs": "https://github.com/wooorm/character-entities-legacy/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/character-entities-legacy/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "main": "index.json", - "files": [ - "index.json" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "HTML legacy character entity information", "devDependencies": { "bail": "^1.0.0", "browserify": "^16.0.0", @@ -38,15 +29,26 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "generate": "node build", - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify index.json -s characterEntitiesLegacy -o character-entities-legacy.js", - "build-mangle": "browserify index.json -s characterEntitiesLegacy -p tinyify -o character-entities-legacy.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test": "npm run generate && npm run format && npm run build && npm run test-api" + "files": [ + "index.json" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" }, + "homepage": "https://github.com/wooorm/character-entities-legacy#readme", + "keywords": [ + "html", + "entity", + "entities", + "character", + "reference", + "name", + "replacement" + ], + "license": "MIT", + "main": "index.json", + "name": "character-entities-legacy", "prettier": { "tabWidth": 2, "useTabs": false, @@ -55,16 +57,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/character-entities-legacy.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.json -s characterEntitiesLegacy -o character-entities-legacy.js", + "build-mangle": "browserify index.json -s characterEntitiesLegacy -p tinyify -o character-entities-legacy.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "generate": "node build", + "test": "npm run generate && npm run format && npm run build && npm run test-api", + "test-api": "node test" + }, + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "character-entities-legacy.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities/package.json index e1151d249e23dd..62ea9531be971e 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/character-entities/package.json @@ -1,32 +1,23 @@ { - "name": "character-entities", - "version": "1.2.4", - "description": "HTML character entity information", - "license": "MIT", - "keywords": [ - "html", - "entity", - "entities", - "character", - "reference", - "name", - "replacement" - ], - "repository": "wooorm/character-entities", - "bugs": "https://github.com/wooorm/character-entities/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/character-entities/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "main": "index.json", - "files": [ - "index.json" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "HTML character entity information", "devDependencies": { "bail": "^1.0.0", "browserify": "^16.0.0", @@ -38,16 +29,26 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "generate": "node build", - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify index.json -s characterEntities -o character-entities.js", - "build-mangle": "browserify index.json -s characterEntities -p tinyify -o character-entities.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "lint": "xo", - "test-api": "node test", - "test": "npm run generate && npm run format && npm run build && npm run test-api" + "files": [ + "index.json" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" }, + "homepage": "https://github.com/wooorm/character-entities#readme", + "keywords": [ + "html", + "entity", + "entities", + "character", + "reference", + "name", + "replacement" + ], + "license": "MIT", + "main": "index.json", + "name": "character-entities", "prettier": { "tabWidth": 2, "useTabs": false, @@ -56,16 +57,31 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/character-entities.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.json -s characterEntities -o character-entities.js", + "build-mangle": "browserify index.json -s characterEntities -p tinyify -o character-entities.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "generate": "node build", + "lint": "xo", + "test": "npm run generate && npm run format && npm run build && npm run test-api", + "test-api": "node test" + }, + "version": "1.2.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "character-entities.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/character-reference-invalid/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/character-reference-invalid/package.json index 069dc4b2b53901..52c4e1361f7c8e 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/character-reference-invalid/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/character-reference-invalid/package.json @@ -1,33 +1,23 @@ { - "name": "character-reference-invalid", - "version": "1.1.4", - "description": "HTML invalid numeric character reference information", - "license": "MIT", - "keywords": [ - "html", - "entity", - "numeric", - "character", - "reference", - "replacement", - "invalid", - "name" - ], - "repository": "wooorm/character-reference-invalid", - "bugs": "https://github.com/wooorm/character-reference-invalid/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/character-reference-invalid/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "main": "index.json", - "files": [ - "index.json" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "HTML invalid numeric character reference information", "devDependencies": { "bail": "^1.0.0", "browserify": "^16.0.0", @@ -42,15 +32,27 @@ "unified": "^8.0.0", "xo": "^0.25.0" }, - "scripts": { - "generate": "node build", - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify index.json -s characterReferenceInvalid -o character-reference-invalid.js", - "build-mangle": "browserify index.json -s characterReferenceInvalid -p tinyify -o character-reference-invalid.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test": "npm run generate && npm run format && npm run build && npm run test-api" + "files": [ + "index.json" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" }, + "homepage": "https://github.com/wooorm/character-reference-invalid#readme", + "keywords": [ + "html", + "entity", + "numeric", + "character", + "reference", + "replacement", + "invalid", + "name" + ], + "license": "MIT", + "main": "index.json", + "name": "character-reference-invalid", "prettier": { "tabWidth": 2, "useTabs": false, @@ -59,16 +61,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/character-reference-invalid.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.json -s characterReferenceInvalid -o character-reference-invalid.js", + "build-mangle": "browserify index.json -s characterReferenceInvalid -p tinyify -o character-reference-invalid.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "generate": "node build", + "test": "npm run generate && npm run format && npm run build && npm run test-api", + "test-api": "node test" + }, + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "character-reference-invalid.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/collapse-white-space/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/collapse-white-space/package.json index 6c9e8f348f4bc1..fd02ee65e2ed13 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/collapse-white-space/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/collapse-white-space/package.json @@ -1,27 +1,23 @@ { - "name": "collapse-white-space", - "version": "1.0.6", - "description": "Replace multiple white-space characters with a single space", - "license": "MIT", - "keywords": [ - "collapse", - "white", - "space" - ], - "repository": "wooorm/collapse-white-space", - "bugs": "https://github.com/wooorm/collapse-white-space/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/collapse-white-space/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Replace multiple white-space characters with a single space", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -32,14 +28,26 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s collapseWhiteSpace -o collapse-white-space.js", - "build-mangle": "browserify . -s collapseWhiteSpace -p tinyify -o collapse-white-space.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/collapse-white-space#readme", + "keywords": [ + "collapse", + "white", + "space" + ], + "license": "MIT", + "name": "collapse-white-space", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -49,22 +57,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/collapse-white-space.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s collapseWhiteSpace -o collapse-white-space.js", + "build-mangle": "browserify . -s collapseWhiteSpace -p tinyify -o collapse-white-space.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.6", "xo": { "prettier": true, "esnext": false, "ignores": [ "collapse-white-space.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/extend/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/extend/package.json index 85279f78054e5c..429364c0c37d04 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/extend/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/extend/package.json @@ -1,42 +1,52 @@ { - "name": "extend", - "author": "Stefan Thomas <justmoon@members.fsf.org> (http://www.justmoon.net)", - "version": "3.0.2", - "description": "Port of jQuery.extend for node.js and the browser", - "main": "index", - "scripts": { - "pretest": "npm run lint", - "test": "npm run tests-only", - "posttest": "npm run coverage-quiet", - "tests-only": "node test", - "coverage": "covert test/index.js", - "coverage-quiet": "covert test/index.js --quiet", - "lint": "npm run jscs && npm run eslint", - "jscs": "jscs *.js */*.js", - "eslint": "eslint *.js */*.js" - }, - "contributors": [ - { - "name": "Jordan Harband", - "url": "https://github.com/ljharb" - } - ], - "keywords": [ - "extend", - "clone", - "merge" - ], - "repository": { - "type": "git", - "url": "https://github.com/justmoon/node-extend.git" - }, - "dependencies": {}, - "devDependencies": { - "@ljharb/eslint-config": "^12.2.1", - "covert": "^1.1.0", - "eslint": "^4.19.1", - "jscs": "^3.0.7", - "tape": "^4.9.1" - }, - "license": "MIT" -} + "author": { + "name": "Stefan Thomas", + "email": "justmoon@members.fsf.org", + "url": "http://www.justmoon.net" + }, + "bugs": { + "url": "https://github.com/justmoon/node-extend/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Jordan Harband", + "url": "https://github.com/ljharb" + } + ], + "dependencies": {}, + "deprecated": false, + "description": "Port of jQuery.extend for node.js and the browser", + "devDependencies": { + "@ljharb/eslint-config": "^12.2.1", + "covert": "^1.1.0", + "eslint": "^4.19.1", + "jscs": "^3.0.7", + "tape": "^4.9.1" + }, + "homepage": "https://github.com/justmoon/node-extend#readme", + "keywords": [ + "extend", + "clone", + "merge" + ], + "license": "MIT", + "main": "index", + "name": "extend", + "repository": { + "type": "git", + "url": "git+https://github.com/justmoon/node-extend.git" + }, + "scripts": { + "coverage": "covert test/index.js", + "coverage-quiet": "covert test/index.js --quiet", + "eslint": "eslint *.js */*.js", + "jscs": "jscs *.js */*.js", + "lint": "npm run jscs && npm run eslint", + "posttest": "npm run coverage-quiet", + "pretest": "npm run lint", + "test": "npm run tests-only", + "tests-only": "node test" + }, + "version": "3.0.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/inherits/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/inherits/package.json index 37b4366b83e63e..9642125b71d65f 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/inherits/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/inherits/package.json @@ -1,7 +1,19 @@ { - "name": "inherits", + "browser": "./inherits_browser.js", + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", - "version": "2.0.4", + "devDependencies": { + "tap": "^14.2.4" + }, + "files": [ + "inherits.js", + "inherits_browser.js" + ], + "homepage": "https://github.com/isaacs/inherits#readme", "keywords": [ "inheritance", "class", @@ -12,18 +24,15 @@ "browser", "browserify" ], - "main": "./inherits.js", - "browser": "./inherits_browser.js", - "repository": "git://github.com/isaacs/inherits", "license": "ISC", + "main": "./inherits.js", + "name": "inherits", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, "scripts": { "test": "tap" }, - "devDependencies": { - "tap": "^14.2.4" - }, - "files": [ - "inherits.js", - "inherits_browser.js" - ] -} + "version": "2.0.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphabetical/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphabetical/package.json index c7764f2a09e8c8..312a56e6e0add2 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphabetical/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphabetical/package.json @@ -1,29 +1,23 @@ { - "name": "is-alphabetical", - "version": "1.0.4", - "description": "Check if a character is alphabetical", - "license": "MIT", - "keywords": [ - "string", - "character", - "char", - "code", - "alphabetical" - ], - "repository": "wooorm/is-alphabetical", - "bugs": "https://github.com/wooorm/is-alphabetical/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/is-alphabetical/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Check if a character is alphabetical", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -34,14 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify -s isAlphabetical -o is-alphabetical.js", - "build-mangle": "browserify -s isAlphabetical -p tinyify -o is-alphabetical.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/is-alphabetical#readme", + "keywords": [ + "string", + "character", + "char", + "code", + "alphabetical" + ], + "license": "MIT", + "name": "is-alphabetical", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -51,6 +59,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/is-alphabetical.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify -s isAlphabetical -o is-alphabetical.js", + "build-mangle": "browserify -s isAlphabetical -p tinyify -o is-alphabetical.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, @@ -60,16 +87,5 @@ "ignores": [ "is-alphabetical.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphanumerical/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphanumerical/package.json index edaa31e66f0ea1..09c2212afe3978 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphanumerical/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-alphanumerical/package.json @@ -1,34 +1,26 @@ { - "name": "is-alphanumerical", - "version": "1.0.4", - "description": "Check if a character is alphanumerical", - "license": "MIT", - "keywords": [ - "string", - "character", - "char", - "code", - "alphabetical", - "numerical", - "alphanumerical" - ], - "repository": "wooorm/is-alphanumerical", - "bugs": "https://github.com/wooorm/is-alphanumerical/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/is-alphanumerical/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": { "is-alphabetical": "^1.0.0", "is-decimal": "^1.0.0" }, + "deprecated": false, + "description": "Check if a character is alphanumerical", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -39,14 +31,30 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s isAlphanumerical -o is-alphanumerical.js", - "build-mangle": "browserify . -s isAlphanumerical -p tinyify -o is-alphanumerical.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/is-alphanumerical#readme", + "keywords": [ + "string", + "character", + "char", + "code", + "alphabetical", + "numerical", + "alphanumerical" + ], + "license": "MIT", + "name": "is-alphanumerical", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -56,22 +64,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/is-alphanumerical.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s isAlphanumerical -o is-alphanumerical.js", + "build-mangle": "browserify . -s isAlphanumerical -p tinyify -o is-alphanumerical.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "is-alphanumerical.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-buffer/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-buffer/package.json index ea12137a63cf0f..ccde4062936080 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-buffer/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-buffer/package.json @@ -1,7 +1,4 @@ { - "name": "is-buffer", - "description": "Determine if an object is a Buffer", - "version": "1.1.6", "author": { "name": "Feross Aboukhadijeh", "email": "feross@feross.org", @@ -10,12 +7,16 @@ "bugs": { "url": "https://github.com/feross/is-buffer/issues" }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Determine if an object is a Buffer", "devDependencies": { "standard": "*", "tape": "^4.0.0", "zuul": "^3.0.0" }, + "homepage": "https://github.com/feross/is-buffer#readme", "keywords": [ "buffer", "buffers", @@ -35,6 +36,7 @@ ], "license": "MIT", "main": "index.js", + "name": "is-buffer", "repository": { "type": "git", "url": "git://github.com/feross/is-buffer.git" @@ -47,5 +49,6 @@ }, "testling": { "files": "test/*.js" - } -} + }, + "version": "1.1.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-decimal/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-decimal/package.json index 5589c2a4f1f1b7..9c9d45e06dc51d 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-decimal/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-decimal/package.json @@ -1,29 +1,23 @@ { - "name": "is-decimal", - "version": "1.0.4", - "description": "Check if a character is decimal", - "license": "MIT", - "keywords": [ - "string", - "character", - "char", - "code", - "decimal" - ], - "repository": "wooorm/is-decimal", - "bugs": "https://github.com/wooorm/is-decimal/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/is-decimal/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Check if a character is decimal", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -34,14 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s isDecimal -o is-decimal.js", - "build-mangle": "browserify . -s isDecimal -p tinyify -o is-decimal.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/is-decimal#readme", + "keywords": [ + "string", + "character", + "char", + "code", + "decimal" + ], + "license": "MIT", + "name": "is-decimal", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -51,22 +59,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/is-decimal.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s isDecimal -o is-decimal.js", + "build-mangle": "browserify . -s isDecimal -p tinyify -o is-decimal.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "is-decimal.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-hexadecimal/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-hexadecimal/package.json index b8741fe102945a..acd830adc42206 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-hexadecimal/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-hexadecimal/package.json @@ -1,29 +1,23 @@ { - "name": "is-hexadecimal", - "version": "1.0.4", - "description": "Check if a character is hexadecimal", - "license": "MIT", - "keywords": [ - "string", - "character", - "char", - "code", - "hexadecimal" - ], - "repository": "wooorm/is-hexadecimal", - "bugs": "https://github.com/wooorm/is-hexadecimal/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/is-hexadecimal/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Check if a character is hexadecimal", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -34,14 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s isHexadecimal -o is-hexadecimal.js", - "build-mangle": "browserify . -s isHexadecimal -p tinyify -o is-hexadecimal.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/is-hexadecimal#readme", + "keywords": [ + "string", + "character", + "char", + "code", + "hexadecimal" + ], + "license": "MIT", + "name": "is-hexadecimal", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -51,22 +59,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/is-hexadecimal.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s isHexadecimal -o is-hexadecimal.js", + "build-mangle": "browserify . -s isHexadecimal -p tinyify -o is-hexadecimal.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "is-hexadecimal.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-plain-obj/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-plain-obj/package.json index d331f6e8169900..64475946dac06c 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-plain-obj/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-plain-obj/package.json @@ -1,23 +1,25 @@ { - "name": "is-plain-obj", - "version": "1.1.0", - "description": "Check if a value is a plain object", - "license": "MIT", - "repository": "sindresorhus/is-plain-obj", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "sindresorhus.com" }, + "bugs": { + "url": "https://github.com/sindresorhus/is-plain-obj/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Check if a value is a plain object", + "devDependencies": { + "ava": "0.0.4" + }, "engines": { "node": ">=0.10.0" }, - "scripts": { - "test": "node test.js" - }, "files": [ "index.js" ], + "homepage": "https://github.com/sindresorhus/is-plain-obj#readme", "keywords": [ "obj", "object", @@ -30,7 +32,14 @@ "pure", "simple" ], - "devDependencies": { - "ava": "0.0.4" - } -} + "license": "MIT", + "name": "is-plain-obj", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/is-plain-obj.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-whitespace-character/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-whitespace-character/package.json index d6b35d9a0b5df4..6a7a6597798e5e 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-whitespace-character/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-whitespace-character/package.json @@ -1,31 +1,23 @@ { - "name": "is-whitespace-character", - "version": "1.0.4", - "description": "Check if a character is a whitespace character", - "license": "MIT", - "keywords": [ - "string", - "character", - "char", - "code", - "whitespace", - "white", - "space" - ], - "repository": "wooorm/is-whitespace-character", - "bugs": "https://github.com/wooorm/is-whitespace-character/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/is-whitespace-character/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Check if a character is a whitespace character", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -36,14 +28,30 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s isWhitespaceCharacter -o is-whitespace-character.js", - "build-mangle": "browserify . -s isWhitespaceCharacter -p tinyify -o is-whitespace-character.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/is-whitespace-character#readme", + "keywords": [ + "string", + "character", + "char", + "code", + "whitespace", + "white", + "space" + ], + "license": "MIT", + "name": "is-whitespace-character", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -53,22 +61,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/is-whitespace-character.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s isWhitespaceCharacter -o is-whitespace-character.js", + "build-mangle": "browserify . -s isWhitespaceCharacter -p tinyify -o is-whitespace-character.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "is-whitespace-character.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/is-word-character/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/is-word-character/package.json index 42c262cf64fa63..b2fc0a1558ce2d 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/is-word-character/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/is-word-character/package.json @@ -1,29 +1,23 @@ { - "name": "is-word-character", - "version": "1.0.4", - "description": "Check if a character is a word character", - "license": "MIT", - "keywords": [ - "string", - "character", - "char", - "code", - "word" - ], - "repository": "wooorm/is-word-character", - "bugs": "https://github.com/wooorm/is-word-character/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/is-word-character/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Check if a character is a word character", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -34,14 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s isWordCharacter -o is-word-character.js", - "build-mangle": "browserify . -s isWordCharacter -p tinyify -o is-word-character.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/is-word-character#readme", + "keywords": [ + "string", + "character", + "char", + "code", + "word" + ], + "license": "MIT", + "name": "is-word-character", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -51,22 +59,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/is-word-character.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s isWordCharacter -o is-word-character.js", + "build-mangle": "browserify . -s isWordCharacter -p tinyify -o is-word-character.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "is-word-character.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/markdown-escapes/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/markdown-escapes/package.json index 7f94d86ad7acc9..0e4eed169f2b65 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/markdown-escapes/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/markdown-escapes/package.json @@ -1,29 +1,23 @@ { - "name": "markdown-escapes", - "version": "1.0.4", - "description": "List of escapable characters in markdown", - "license": "MIT", - "keywords": [ - "markdown", - "escape", - "pedantic", - "gfm", - "commonmark" - ], - "repository": "wooorm/markdown-escapes", - "bugs": "https://github.com/wooorm/markdown-escapes/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/markdown-escapes/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "List of escapable characters in markdown", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -34,19 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s markdownEscapes -o markdown-escapes.js", - "build-mangle": "browserify . -s markdownEscapes -p tinyify -o markdown-escapes.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] + "homepage": "https://github.com/wooorm/markdown-escapes#readme", + "keywords": [ + "markdown", + "escape", + "pedantic", + "gfm", + "commonmark" + ], + "license": "MIT", + "name": "markdown-escapes", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -56,17 +59,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/markdown-escapes.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s markdownEscapes -o markdown-escapes.js", + "build-mangle": "browserify . -s markdownEscapes -p tinyify -o markdown-escapes.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "markdown-escapes.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/parse-entities/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/parse-entities/package.json index a5e1bc46f6a9ba..f2eee3452e6f79 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/parse-entities/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/parse-entities/package.json @@ -1,32 +1,22 @@ { - "name": "parse-entities", - "version": "1.2.2", - "description": "Parse HTML character references: fast, spec-compliant, positional information", - "license": "MIT", - "keywords": [ - "parse", - "html", - "character", - "reference", - "entity", - "entities" - ], - "repository": "wooorm/parse-entities", - "bugs": "https://github.com/wooorm/parse-entities/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", - "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, "browser": { "./decode-entity.js": "./decode-entity.browser.js" }, - "react-native": { - "./decode-entity.js": "./decode-entity.js" + "bugs": { + "url": "https://github.com/wooorm/parse-entities/issues" }, - "files": [ - "index.js", - "decode-entity.js", - "decode-entity.browser.js" + "bundleDependencies": false, + "contributors": [ + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": { "character-entities": "^1.0.0", @@ -36,6 +26,8 @@ "is-decimal": "^1.0.0", "is-hexadecimal": "^1.0.0" }, + "deprecated": false, + "description": "Parse HTML character references: fast, spec-compliant, positional information", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -47,16 +39,22 @@ "tinyify": "^2.4.3", "xo": "^0.24.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s parseEntities > parse-entities.js", - "build-mangle": "browserify . -s parseEntities -p tinyify > parse-entities.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test-browser": "browserify test.js | tape-run", - "test": "npm run format && npm run build && npm run test-coverage && npm run test-browser" - }, + "files": [ + "index.js", + "decode-entity.js", + "decode-entity.browser.js" + ], + "homepage": "https://github.com/wooorm/parse-entities#readme", + "keywords": [ + "parse", + "html", + "character", + "reference", + "entity", + "entities" + ], + "license": "MIT", + "name": "parse-entities", "nyc": { "check-coverage": true, "lines": 100, @@ -71,6 +69,29 @@ "semi": false, "trailingComma": "none" }, + "react-native": { + "./decode-entity.js": "./decode-entity.js" + }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/parse-entities.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s parseEntities > parse-entities.js", + "build-mangle": "browserify . -s parseEntities -p tinyify > parse-entities.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage && npm run test-browser", + "test-api": "node test", + "test-browser": "browserify test.js | tape-run", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.2.2", "xo": { "prettier": true, "esnext": false, @@ -82,10 +103,5 @@ "ignores": [ "parse-entities.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/remark-parse/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/remark-parse/package.json index 822420cb25200a..b47866c95b256b 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/remark-parse/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/remark-parse/package.json @@ -1,27 +1,23 @@ { - "name": "remark-parse", - "version": "5.0.0", - "description": "Markdown parser for remark", - "license": "MIT", - "keywords": [ - "markdown", - "abstract", - "syntax", - "tree", - "ast", - "parse" - ], - "homepage": "http://remark.js.org", - "repository": "https://github.com/remarkjs/remark/tree/master/packages/remark-parse", - "bugs": "https://github.com/remarkjs/remark/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + }, + "bugs": { + "url": "https://github.com/remarkjs/remark/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", - "Eugene Sharygin <eush77@gmail.com>" - ], - "files": [ - "index.js", - "lib" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + }, + { + "name": "Eugene Sharygin", + "email": "eush77@gmail.com" + } ], "dependencies": { "collapse-white-space": "^1.0.2", @@ -40,5 +36,27 @@ "vfile-location": "^2.0.0", "xtend": "^4.0.1" }, + "deprecated": false, + "description": "Markdown parser for remark", + "files": [ + "index.js", + "lib" + ], + "homepage": "http://remark.js.org", + "keywords": [ + "markdown", + "abstract", + "syntax", + "tree", + "ast", + "parse" + ], + "license": "MIT", + "name": "remark-parse", + "repository": { + "type": "git", + "url": "https://github.com/remarkjs/remark/tree/master/packages/remark-parse" + }, + "version": "5.0.0", "xo": false -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/repeat-string/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/repeat-string/package.json index 09f889299b6683..90fa9a34d8afb7 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/repeat-string/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/repeat-string/package.json @@ -1,31 +1,41 @@ { - "name": "repeat-string", - "description": "Repeat the given string n times. Fastest implementation for repeating a string.", - "version": "1.6.1", - "homepage": "https://github.com/jonschlinkert/repeat-string", - "author": "Jon Schlinkert (http://github.com/jonschlinkert)", - "contributors": [ - "Brian Woodward <brian.woodward@gmail.com> (https://github.com/doowb)", - "Jon Schlinkert <jon.schlinkert@sellside.com> (http://twitter.com/jonschlinkert)", - "Linus Unnebäck <linus@folkdatorn.se> (http://linus.unnebäck.se)", - "Thijs Busser <tbusser@gmail.com> (http://tbusser.net)", - "Titus <tituswormer@gmail.com> (wooorm.com)" - ], - "repository": "jonschlinkert/repeat-string", + "author": { + "name": "Jon Schlinkert", + "url": "http://github.com/jonschlinkert" + }, "bugs": { "url": "https://github.com/jonschlinkert/repeat-string/issues" }, - "license": "MIT", - "files": [ - "index.js" + "bundleDependencies": false, + "contributors": [ + { + "name": "Brian Woodward", + "email": "brian.woodward@gmail.com", + "url": "https://github.com/doowb" + }, + { + "name": "Jon Schlinkert", + "email": "jon.schlinkert@sellside.com", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Linus Unnebäck", + "email": "linus@folkdatorn.se", + "url": "http://linus.unnebäck.se" + }, + { + "name": "Thijs Busser", + "email": "tbusser@gmail.com", + "url": "http://tbusser.net" + }, + { + "name": "Titus", + "email": "tituswormer@gmail.com", + "url": "wooorm.com" + } ], - "main": "index.js", - "engines": { - "node": ">=0.10" - }, - "scripts": { - "test": "mocha" - }, + "deprecated": false, + "description": "Repeat the given string n times. Fastest implementation for repeating a string.", "devDependencies": { "ansi-cyan": "^0.1.1", "benchmarked": "^0.2.5", @@ -36,6 +46,13 @@ "text-table": "^0.2.0", "yargs-parser": "^4.0.2" }, + "engines": { + "node": ">=0.10" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/jonschlinkert/repeat-string", "keywords": [ "fast", "fastest", @@ -53,6 +70,16 @@ "string", "times" ], + "license": "MIT", + "main": "index.js", + "name": "repeat-string", + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/repeat-string.git" + }, + "scripts": { + "test": "mocha" + }, "verb": { "toc": false, "layout": "default", @@ -73,5 +100,6 @@ "reflinks": [ "verb" ] - } -} + }, + "version": "1.6.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/replace-ext/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/replace-ext/package.json index 27dbe31042d0ff..e616e7c2258223 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/replace-ext/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/replace-ext/package.json @@ -1,30 +1,26 @@ { - "name": "replace-ext", - "version": "1.0.0", - "description": "Replaces a file extension with another one", - "author": "Gulp Team <team@gulpjs.com> (http://gulpjs.com/)", - "contributors": [ - "Eric Schoffstall <yo@contra.io>", - "Blaine Bublitz <blaine.bublitz@gmail.com>" - ], - "repository": "gulpjs/replace-ext", - "license": "MIT", - "engines": { - "node": ">= 0.10" + "author": { + "name": "Gulp Team", + "email": "team@gulpjs.com", + "url": "http://gulpjs.com/" }, - "main": "index.js", - "files": [ - "LICENSE", - "index.js" - ], - "scripts": { - "lint": "eslint . && jscs index.js test/", - "pretest": "npm run lint", - "test": "mocha --async-only", - "cover": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly", - "coveralls": "npm run cover && istanbul-coveralls" + "bugs": { + "url": "https://github.com/gulpjs/replace-ext/issues" }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Eric Schoffstall", + "email": "yo@contra.io" + }, + { + "name": "Blaine Bublitz", + "email": "blaine.bublitz@gmail.com" + } + ], "dependencies": {}, + "deprecated": false, + "description": "Replaces a file extension with another one", "devDependencies": { "eslint": "^1.10.3", "eslint-config-gulp": "^2.0.0", @@ -35,10 +31,33 @@ "jscs-preset-gulp": "^1.0.0", "mocha": "^2.4.5" }, + "engines": { + "node": ">= 0.10" + }, + "files": [ + "LICENSE", + "index.js" + ], + "homepage": "https://github.com/gulpjs/replace-ext#readme", "keywords": [ "gulp", "extensions", "filepath", "basename" - ] -} + ], + "license": "MIT", + "main": "index.js", + "name": "replace-ext", + "repository": { + "type": "git", + "url": "git+https://github.com/gulpjs/replace-ext.git" + }, + "scripts": { + "cover": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly", + "coveralls": "npm run cover && istanbul-coveralls", + "lint": "eslint . && jscs index.js test/", + "pretest": "npm run lint", + "test": "mocha --async-only" + }, + "version": "1.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/state-toggle/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/state-toggle/package.json index 1111db063669a9..573df7117bfe98 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/state-toggle/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/state-toggle/package.json @@ -1,27 +1,23 @@ { - "name": "state-toggle", - "version": "1.0.3", - "description": "Enter/exit a state", - "license": "MIT", - "keywords": [ - "enter", - "exit", - "state" - ], - "repository": "wooorm/state-toggle", - "bugs": "https://github.com/wooorm/state-toggle/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/state-toggle/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Enter/exit a state", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -32,14 +28,26 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s stateToggle -o state-toggle.js", - "build-mangle": "browserify . -s stateToggle -p tinyify -o state-toggle.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/state-toggle#readme", + "keywords": [ + "enter", + "exit", + "state" + ], + "license": "MIT", + "name": "state-toggle", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -49,22 +57,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/state-toggle.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s stateToggle -o state-toggle.js", + "build-mangle": "browserify . -s stateToggle -p tinyify -o state-toggle.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.3", "xo": { "prettier": true, "esnext": false, "ignores": [ "state-toggle.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/trim-trailing-lines/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/trim-trailing-lines/package.json index 861198394c3220..c0242dc1299bdc 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/trim-trailing-lines/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/trim-trailing-lines/package.json @@ -1,29 +1,23 @@ { - "name": "trim-trailing-lines", - "version": "1.1.4", - "description": "Remove final line feeds from a string", - "license": "MIT", - "keywords": [ - "trim", - "final", - "line", - "newline", - "characters" - ], - "repository": "wooorm/trim-trailing-lines", - "bugs": "https://github.com/wooorm/trim-trailing-lines/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/trim-trailing-lines/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Remove final line feeds from a string", "devDependencies": { "browserify": "^17.0.0", "nyc": "^15.0.0", @@ -34,15 +28,23 @@ "tinyify": "^3.0.0", "xo": "^0.34.0" }, - "scripts": { - "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", - "build-bundle": "browserify . -s trimTrailingLines -o trim-trailing-lines.js", - "build-mangle": "browserify . -s trimTrailingLines -p tinyify -o trim-trailing-lines.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" }, + "homepage": "https://github.com/wooorm/trim-trailing-lines#readme", + "keywords": [ + "trim", + "final", + "line", + "newline", + "characters" + ], + "license": "MIT", + "name": "trim-trailing-lines", "nyc": { "check-coverage": true, "lines": 100, @@ -57,16 +59,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/trim-trailing-lines.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s trimTrailingLines -o trim-trailing-lines.js", + "build-mangle": "browserify . -s trimTrailingLines -p tinyify -o trim-trailing-lines.min.js", + "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "trim-trailing-lines.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/trim/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/trim/package.json index 64ee5c69d84c32..b6c977af444974 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/trim/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/trim/package.json @@ -1,18 +1,26 @@ { - "name": "trim", - "version": "0.0.1", - "description": "Trim string whitespace", - "keywords": ["string", "trim"], - "author": "TJ Holowaychuk <tj@vision-media.ca>", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "bundleDependencies": false, + "component": { + "scripts": { + "trim/index.js": "index.js" + } + }, "dependencies": {}, + "deprecated": false, + "description": "Trim string whitespace", "devDependencies": { "mocha": "*", "should": "*" }, + "keywords": [ + "string", + "trim" + ], "main": "index", - "component": { - "scripts": { - "trim/index.js": "index.js" - } - } -} + "name": "trim", + "version": "0.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/trough/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/trough/package.json index cbf7782f89a445..d4203f3a15d71f 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/trough/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/trough/package.json @@ -1,27 +1,23 @@ { - "name": "trough", - "version": "1.0.5", - "description": "Middleware: a channel used to convey a liquid", - "license": "MIT", - "keywords": [ - "middleware", - "ware" - ], - "repository": "wooorm/trough", - "bugs": "https://github.com/wooorm/trough/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/wooorm/trough/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js", - "wrap.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Middleware: a channel used to convey a liquid", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -32,14 +28,26 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify index.js -s trough > trough.js", - "build-mangle": "browserify index.js -s trough -p tinyify > trough.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js", + "wrap.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "homepage": "https://github.com/wooorm/trough#readme", + "keywords": [ + "middleware", + "ware" + ], + "license": "MIT", + "name": "trough", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -49,6 +57,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/trough.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.js -s trough > trough.js", + "build-mangle": "browserify index.js -s trough -p tinyify > trough.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.0.5", "xo": { "prettier": true, "esnext": false, @@ -60,16 +87,5 @@ "ignores": [ "trough.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unherit/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unherit/package.json index 445a500650255e..b3cb27e60d4ebe 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unherit/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unherit/package.json @@ -1,31 +1,26 @@ { - "name": "unherit", - "version": "1.1.3", - "description": "Clone a constructor without affecting the super-class", - "license": "MIT", - "keywords": [ - "clone", - "super", - "class", - "constructor" - ], - "repository": "wooorm/unherit", - "bugs": "https://github.com/wooorm/unherit/issues", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/wooorm/unherit/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": { "inherits": "^2.0.0", "xtend": "^4.0.0" }, + "deprecated": false, + "description": "Clone a constructor without affecting the super-class", "devDependencies": { "browserify": "^16.0.0", "nyc": "^15.0.0", @@ -36,15 +31,22 @@ "tinyify": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s unherit -o unherit.js", - "build-mangle": "browserify . -s unherit -p tinyify -o unherit.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" }, + "homepage": "https://github.com/wooorm/unherit#readme", + "keywords": [ + "clone", + "super", + "class", + "constructor" + ], + "license": "MIT", + "name": "unherit", "prettier": { "tabWidth": 2, "useTabs": false, @@ -53,6 +55,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/wooorm/unherit.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s unherit -o unherit.js", + "build-mangle": "browserify . -s unherit -p tinyify -o unherit.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.1.3", "xo": { "prettier": true, "esnext": false, @@ -63,10 +84,5 @@ "ignores": [ "unherit.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unified/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unified/package.json index 21777216934520..5142140ddfdf9c 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unified/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unified/package.json @@ -1,27 +1,19 @@ { - "name": "unified", - "version": "6.2.0", - "description": "Pluggable text processing interface", - "license": "MIT", - "keywords": [ - "process", - "parse", - "transform", - "compile", - "stringify", - "rehype", - "retext", - "remark" - ], - "repository": "unifiedjs/unified", - "bugs": "https://github.com/unifiedjs/unified/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + }, + "bugs": { + "url": "https://github.com/unifiedjs/unified/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)" - ], - "files": [ - "index.js", - "lib" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + } ], "dependencies": { "bail": "^1.0.0", @@ -31,6 +23,8 @@ "vfile": "^2.0.0", "x-is-string": "^0.1.0" }, + "deprecated": false, + "description": "Pluggable text processing interface", "devDependencies": { "browserify": "^16.0.0", "esmangle": "^1.0.0", @@ -41,15 +35,23 @@ "tape": "^4.4.0", "xo": "^0.20.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", - "build-bundle": "browserify index.js -s unified > unified.js", - "build-mangle": "esmangle unified.js > unified.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test", - "test": "npm run format && npm run build && npm run test-coverage" - }, + "files": [ + "index.js", + "lib" + ], + "homepage": "https://github.com/unifiedjs/unified#readme", + "keywords": [ + "process", + "parse", + "transform", + "compile", + "stringify", + "rehype", + "retext", + "remark" + ], + "license": "MIT", + "name": "unified", "nyc": { "check-coverage": true, "lines": 100, @@ -64,6 +66,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/unifiedjs/unified.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.js -s unified > unified.js", + "build-mangle": "esmangle unified.js > unified.min.js", + "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test" + }, + "version": "6.2.0", "xo": { "prettier": true, "esnext": false, @@ -77,10 +98,5 @@ "ignores": [ "unified.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-is/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-is/package.json index 25193acddd8a0d..780e6d7618a9fd 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-is/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-is/package.json @@ -1,29 +1,23 @@ { - "name": "unist-util-is", - "version": "3.0.0", - "description": "Utility to check if a node passes a test", - "license": "MIT", - "keywords": [ - "unist", - "node", - "is", - "equal", - "test", - "type", - "util", - "utility" - ], - "repository": "syntax-tree/unist-util-is", - "bugs": "https://github.com/syntax-tree/unist-util-is/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/syntax-tree/unist-util-is/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js", - "convert.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Utility to check if a node passes a test", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -34,14 +28,28 @@ "tinyify": "^2.0.0", "xo": "^0.24.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s unistUtilIs > unist-util-is.js", - "build-mangle": "browserify . -s unistUtilIs -p tinyify > unist-util-is.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js", + "convert.js" + ], + "homepage": "https://github.com/syntax-tree/unist-util-is#readme", + "keywords": [ + "unist", + "node", + "is", + "equal", + "test", + "type", + "util", + "utility" + ], + "license": "MIT", + "name": "unist-util-is", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -51,6 +59,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/syntax-tree/unist-util-is.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s unistUtilIs > unist-util-is.js", + "build-mangle": "browserify . -s unistUtilIs -p tinyify > unist-util-is.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "3.0.0", "xo": { "prettier": true, "esnext": false, @@ -60,16 +87,5 @@ "ignore": [ "unist-util-is.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-remove-position/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-remove-position/package.json index e1166471c37d6e..768b0c3e6cbc56 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-remove-position/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-remove-position/package.json @@ -1,31 +1,25 @@ { - "name": "unist-util-remove-position", - "version": "1.1.4", - "description": "Remove `position`s from a unist tree", - "license": "MIT", - "keywords": [ - "unist", - "utility", - "remove", - "position", - "location" - ], - "repository": "syntax-tree/unist-util-remove-position", - "bugs": "https://github.com/syntax-tree/unist-util-remove-position/issues", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bugs": { + "url": "https://github.com/syntax-tree/unist-util-remove-position/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": { "unist-util-visit": "^1.1.0" }, + "deprecated": false, + "description": "Remove `position`s from a unist tree", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -38,14 +32,28 @@ "unist-builder": "^2.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s unistUtilRemovePosition > unist-util-remove-position.js", - "build-mangle": "browserify . -s unistUtilRemovePosition -p tinyify > unist-util-remove-position.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "homepage": "https://github.com/syntax-tree/unist-util-remove-position#readme", + "keywords": [ + "unist", + "utility", + "remove", + "position", + "location" + ], + "license": "MIT", + "name": "unist-util-remove-position", + "nyc": { + "check-coverage": true, + "lines": 100, + "functions": 100, + "branches": 100 }, "prettier": { "tabWidth": 2, @@ -55,22 +63,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/syntax-tree/unist-util-remove-position.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s unistUtilRemovePosition > unist-util-remove-position.js", + "build-mangle": "browserify . -s unistUtilRemovePosition -p tinyify > unist-util-remove-position.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.1.4", "xo": { "prettier": true, "esnext": false, "ignores": [ "unist-util-remove-position.js" ] - }, - "nyc": { - "check-coverage": true, - "lines": 100, - "functions": 100, - "branches": 100 - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-stringify-position/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-stringify-position/package.json index 2e20b672051031..fe85a400cb8590 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-stringify-position/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-stringify-position/package.json @@ -1,29 +1,23 @@ { - "name": "unist-util-stringify-position", - "version": "1.1.2", - "description": "Stringify a Unist node, position, or point", - "license": "MIT", - "keywords": [ - "unist", - "position", - "location", - "point", - "node", - "stringify", - "tostring", - "util", - "utility" - ], - "repository": "syntax-tree/unist-util-stringify-position", - "bugs": "https://github.com/syntax-tree/unist-util-stringify-position/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + }, + "bugs": { + "url": "https://github.com/syntax-tree/unist-util-stringify-position/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Stringify a Unist node, position, or point", "devDependencies": { "browserify": "^16.0.0", "esmangle": "^1.0.0", @@ -34,15 +28,23 @@ "tape": "^4.5.1", "xo": "^0.20.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", - "build-bundle": "browserify index.js --no-builtins -s unistUtilStringifyPosition > unist-util-stringify-position.js", - "build-mangle": "esmangle unist-util-stringify-position.js > unist-util-stringify-position.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" - }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/syntax-tree/unist-util-stringify-position#readme", + "keywords": [ + "unist", + "position", + "location", + "point", + "node", + "stringify", + "tostring", + "util", + "utility" + ], + "license": "MIT", + "name": "unist-util-stringify-position", "nyc": { "check-coverage": true, "lines": 100, @@ -57,6 +59,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/syntax-tree/unist-util-stringify-position.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.js --no-builtins -s unistUtilStringifyPosition > unist-util-stringify-position.js", + "build-mangle": "esmangle unist-util-stringify-position.js > unist-util-stringify-position.min.js", + "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.1.2", "xo": { "prettier": true, "esnext": false, @@ -68,10 +89,5 @@ "ignores": [ "unist-util-stringify-position.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit-parents/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit-parents/package.json index 26e187202d145b..79a66d8458bd3b 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit-parents/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit-parents/package.json @@ -1,26 +1,25 @@ { - "name": "unist-util-visit-parents", - "version": "2.1.2", - "description": "Recursively walk over unist nodes, with ancestral information", - "license": "MIT", - "keywords": [ - "unist", - "walk", - "util", - "utility" - ], - "repository": "syntax-tree/unist-util-visit-parents", - "bugs": "https://github.com/syntax-tree/unist-util-visit-parents/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/syntax-tree/unist-util-visit-parents/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": { "unist-util-is": "^3.0.0" }, + "deprecated": false, + "description": "Recursively walk over unist nodes, with ancestral information", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -32,15 +31,18 @@ "tinyify": "^2.0.0", "xo": "^0.24.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify index.js -s unistUtilVisitParents > unist-util-visit-parents.js", - "build-mangle": "browserify index.js -s unistUtilVisitParents -p tinyify > unist-util-visit-parents.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" - }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/syntax-tree/unist-util-visit-parents#readme", + "keywords": [ + "unist", + "walk", + "util", + "utility" + ], + "license": "MIT", + "name": "unist-util-visit-parents", "nyc": { "check-coverage": true, "lines": 100, @@ -55,16 +57,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/syntax-tree/unist-util-visit-parents.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.js -s unistUtilVisitParents > unist-util-visit-parents.js", + "build-mangle": "browserify index.js -s unistUtilVisitParents -p tinyify > unist-util-visit-parents.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "2.1.2", "xo": { "prettier": true, "esnext": false, "ignores": [ "unist-util-visit-parents.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit/package.json index 44b8bd4897b398..a0ef4a3cc172d3 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/unist-util-visit/package.json @@ -1,35 +1,33 @@ { - "name": "unist-util-visit", - "version": "1.4.1", - "description": "Recursively walk over unist nodes", - "license": "MIT", - "keywords": [ - "unist", - "remark", - "markdown", - "retext", - "natural", - "language", - "node", - "visit", - "walk", - "util", - "utility" - ], - "repository": "syntax-tree/unist-util-visit", - "bugs": "https://github.com/syntax-tree/unist-util-visit/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/syntax-tree/unist-util-visit/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", - "Eugene Sharygin <eush77@gmail.com>", - "Richard Gibson <richard.gibson@gmail.com>" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + { + "name": "Eugene Sharygin", + "email": "eush77@gmail.com" + }, + { + "name": "Richard Gibson", + "email": "richard.gibson@gmail.com" + } ], "dependencies": { "unist-util-visit-parents": "^2.0.0" }, + "deprecated": false, + "description": "Recursively walk over unist nodes", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -41,15 +39,25 @@ "tinyify": "^2.0.0", "xo": "^0.24.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s unistUtilVisit > unist-util-visit.js", - "build-mangle": "browserify . -s unistUtilVisit -p tinyify > unist-util-visit.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" - }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/syntax-tree/unist-util-visit#readme", + "keywords": [ + "unist", + "remark", + "markdown", + "retext", + "natural", + "language", + "node", + "visit", + "walk", + "util", + "utility" + ], + "license": "MIT", + "name": "unist-util-visit", "nyc": { "check-coverage": true, "lines": 100, @@ -64,16 +72,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/syntax-tree/unist-util-visit.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s unistUtilVisit > unist-util-visit.js", + "build-mangle": "browserify . -s unistUtilVisit -p tinyify > unist-util-visit.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.4.1", "xo": { "prettier": true, "esnext": false, "ignores": [ "unist-util-visit.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-location/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-location/package.json index f66857cf1c9cec..9db9207b5cb3c6 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-location/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-location/package.json @@ -1,29 +1,23 @@ { - "name": "vfile-location", - "version": "2.0.6", - "description": "Convert between positions (line and column-based) and offsets (range-based) locations in a virtual file", - "license": "MIT", - "keywords": [ - "remark", - "comment", - "message", - "marker", - "control" - ], - "repository": "vfile/vfile-location", - "bugs": "https://github.com/vfile/vfile-location/issues", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/vfile/vfile-location/issues" }, - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": {}, + "deprecated": false, + "description": "Convert between positions (line and column-based) and offsets (range-based) locations in a virtual file", "devDependencies": { "browserify": "^16.0.0", "nyc": "^14.0.0", @@ -35,15 +29,23 @@ "vfile": "^4.0.0", "xo": "^0.25.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", - "build-bundle": "browserify . -s vfileLocation > vfile-location.js", - "build-mangle": "browserify . -s vfileLocation -p tinyify > vfile-location.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" + "files": [ + "index.js" + ], + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" }, + "homepage": "https://github.com/vfile/vfile-location#readme", + "keywords": [ + "remark", + "comment", + "message", + "marker", + "control" + ], + "license": "MIT", + "name": "vfile-location", "nyc": { "check-coverage": true, "lines": 100, @@ -58,16 +60,30 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vfile/vfile-location.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s vfileLocation > vfile-location.js", + "build-mangle": "browserify . -s vfileLocation -p tinyify > vfile-location.min.js", + "format": "remark . -qfo && prettier --write \"**/*.js\" && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "2.0.6", "xo": { "prettier": true, "esnext": false, "ignores": [ "vfile-location.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-message/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-message/package.json index 7396bb68e54f70..4a7b5a9867ff74 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-message/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/vfile-message/package.json @@ -1,25 +1,25 @@ { - "name": "vfile-message", - "version": "1.1.1", - "description": "Create a virtual message", - "license": "MIT", - "keywords": [ - "vfile", - "virtual", - "message" - ], - "repository": "vfile/vfile-message", - "bugs": "https://github.com/vfile/vfile-message/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + }, + "bugs": { + "url": "https://github.com/vfile/vfile-message/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" - ], - "files": [ - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "https://wooorm.com" + } ], "dependencies": { "unist-util-stringify-position": "^1.1.1" }, + "deprecated": false, + "description": "Create a virtual message", "devDependencies": { "browserify": "^16.0.0", "nyc": "^13.0.0", @@ -30,15 +30,17 @@ "tinyify": "^2.4.3", "xo": "^0.23.0" }, - "scripts": { - "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", - "build-bundle": "browserify . -s vfileMessage > vfile-message.js", - "build-mangle": "browserify . -s vfileMessage -p tinyify > vfile-message.min.js", - "build": "npm run build-bundle && npm run build-mangle", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run format && npm run build && npm run test-coverage" - }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/vfile/vfile-message#readme", + "keywords": [ + "vfile", + "virtual", + "message" + ], + "license": "MIT", + "name": "vfile-message", "nyc": { "check-coverage": true, "lines": 100, @@ -53,6 +55,25 @@ "semi": false, "trailingComma": "none" }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vfile/vfile-message.git" + }, + "scripts": { + "build": "npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify . -s vfileMessage > vfile-message.js", + "build-mangle": "browserify . -s vfileMessage -p tinyify > vfile-message.min.js", + "format": "remark . -qfo && prettier --write '**/*.js' && xo --fix", + "test": "npm run format && npm run build && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "1.1.1", "xo": { "prettier": true, "esnext": false, @@ -64,10 +85,5 @@ "ignores": [ "vfile-message.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/vfile/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/vfile/package.json index aba5f34dd9d5ab..98a004f48b3ce3 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/vfile/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/vfile/package.json @@ -1,33 +1,39 @@ { - "name": "vfile", - "version": "2.3.0", - "description": "Virtual file format for text processing", - "license": "MIT", - "keywords": [ - "virtual", - "file", - "text", - "processing", - "message", - "warning", - "error", - "remark", - "retext" - ], - "repository": "vfile/vfile", - "bugs": "https://github.com/vfile/vfile/issues", - "author": "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", + "author": { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + }, + "bugs": { + "url": "https://github.com/vfile/vfile/issues" + }, + "bundleDependencies": false, "contributors": [ - "Titus Wormer <tituswormer@gmail.com> (http://wooorm.com)", - "Brendan Abbott <brendan.abbott@temando.com>", - "Denys Dovhan <email@denysdovhan.com>", - "Kyle Mathews <mathews.kyle@gmail.com>", - "Shinnosuke Watanabe <snnskwtnb@gmail.com>", - "Sindre Sorhus <sindresorhus@gmail.com>" - ], - "files": [ - "core.js", - "index.js" + { + "name": "Titus Wormer", + "email": "tituswormer@gmail.com", + "url": "http://wooorm.com" + }, + { + "name": "Brendan Abbott", + "email": "brendan.abbott@temando.com" + }, + { + "name": "Denys Dovhan", + "email": "email@denysdovhan.com" + }, + { + "name": "Kyle Mathews", + "email": "mathews.kyle@gmail.com" + }, + { + "name": "Shinnosuke Watanabe", + "email": "snnskwtnb@gmail.com" + }, + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com" + } ], "dependencies": { "is-buffer": "^1.1.4", @@ -35,6 +41,8 @@ "unist-util-stringify-position": "^1.0.0", "vfile-message": "^1.0.0" }, + "deprecated": false, + "description": "Virtual file format for text processing", "devDependencies": { "browserify": "^14.0.0", "esmangle": "^1.0.0", @@ -44,22 +52,50 @@ "tape": "^4.4.0", "xo": "^0.18.0" }, - "scripts": { - "build-md": "remark . -qfo", - "build-bundle": "browserify index.js -s VFile > vfile.js", - "build-mangle": "esmangle vfile.js > vfile.min.js", - "build": "npm run build-md && npm run build-bundle && npm run build-mangle", - "lint": "xo", - "test-api": "node test", - "test-coverage": "nyc --reporter lcov tape test.js", - "test": "npm run build && npm run lint && npm run test-coverage" - }, + "files": [ + "core.js", + "index.js" + ], + "homepage": "https://github.com/vfile/vfile#readme", + "keywords": [ + "virtual", + "file", + "text", + "processing", + "message", + "warning", + "error", + "remark", + "retext" + ], + "license": "MIT", + "name": "vfile", "nyc": { "check-coverage": true, "lines": 100, "functions": 100, "branches": 100 }, + "remarkConfig": { + "plugins": [ + "preset-wooorm" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vfile/vfile.git" + }, + "scripts": { + "build": "npm run build-md && npm run build-bundle && npm run build-mangle", + "build-bundle": "browserify index.js -s VFile > vfile.js", + "build-mangle": "esmangle vfile.js > vfile.min.js", + "build-md": "remark . -qfo", + "lint": "xo", + "test": "npm run build && npm run lint && npm run test-coverage", + "test-api": "node test", + "test-coverage": "nyc --reporter lcov tape test.js" + }, + "version": "2.3.0", "xo": { "space": true, "esnext": false, @@ -69,10 +105,5 @@ "ignores": [ "vfile.js" ] - }, - "remarkConfig": { - "plugins": [ - "preset-wooorm" - ] } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/x-is-string/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/x-is-string/package.json index ea267ce35112e1..95f98fc9577d31 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/x-is-string/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/x-is-string/package.json @@ -1,40 +1,47 @@ { - "name": "x-is-string", - "version": "0.1.0", - "description": "Simple string test", - "keywords": [], - "author": "Matt-Esch <matt@mattesch.info>", - "repository": "git://github.com/Matt-Esch/x-is-string.git", - "main": "index", - "homepage": "https://github.com/Matt-Esch/x-is-string", + "author": { + "name": "Matt-Esch", + "email": "matt@mattesch.info" + }, + "bugs": { + "url": "https://github.com/Matt-Esch/x-is-string/issues", + "email": "matt@mattesch.info" + }, + "bundleDependencies": false, "contributors": [ { "name": "Matt-Esch" } ], - "bugs": { - "url": "https://github.com/Matt-Esch/x-is-string/issues", - "email": "matt@mattesch.info" - }, "dependencies": {}, + "deprecated": false, + "description": "Simple string test", "devDependencies": { "tape": "^2.12.2" }, + "homepage": "https://github.com/Matt-Esch/x-is-string", + "keywords": [], "licenses": [ { "type": "MIT", "url": "http://github.com/Matt-Esch/x-is-string/raw/master/LICENSE" } ], + "main": "index", + "name": "x-is-string", + "repository": { + "type": "git", + "url": "git://github.com/Matt-Esch/x-is-string.git" + }, "scripts": { - "test": "node ./test/index.js", + "cover": "istanbul cover --report none --print detail ./test/index.js", "start": "node ./index.js", - "watch": "nodemon -w ./index.js index.js", + "test": "node ./test/index.js", + "test-browser": "testem-browser ./test/browser/index.js", + "testem": "testem-both -b=./test/browser/index.js", "travis-test": "istanbul cover ./test/index.js && ((cat coverage/lcov.info | coveralls) || exit 0)", - "cover": "istanbul cover --report none --print detail ./test/index.js", "view-cover": "istanbul report html && google-chrome ./coverage/index.html", - "test-browser": "testem-browser ./test/browser/index.js", - "testem": "testem-both -b=./test/browser/index.js" + "watch": "nodemon -w ./index.js index.js" }, "testling": { "files": "test/index.js", @@ -51,5 +58,6 @@ "iphone/6.0..latest", "android-browser/4.2..latest" ] - } -} + }, + "version": "0.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/node_modules/xtend/package.json b/tools/node_modules/eslint-plugin-markdown/node_modules/xtend/package.json index f7a39d10af5f5e..970fcf29de706d 100644 --- a/tools/node_modules/eslint-plugin-markdown/node_modules/xtend/package.json +++ b/tools/node_modules/eslint-plugin-markdown/node_modules/xtend/package.json @@ -1,7 +1,31 @@ { - "name": "xtend", - "version": "4.0.2", + "author": { + "name": "Raynos", + "email": "raynos2@gmail.com" + }, + "bugs": { + "url": "https://github.com/Raynos/xtend/issues", + "email": "raynos2@gmail.com" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Jake Verbaten" + }, + { + "name": "Matt Esch" + } + ], + "dependencies": {}, + "deprecated": false, "description": "extend like a boss", + "devDependencies": { + "tape": "~1.1.0" + }, + "engines": { + "node": ">=0.4" + }, + "homepage": "https://github.com/Raynos/xtend", "keywords": [ "extend", "merge", @@ -10,30 +34,16 @@ "object", "array" ], - "author": "Raynos <raynos2@gmail.com>", - "repository": "git://github.com/Raynos/xtend.git", + "license": "MIT", "main": "immutable", + "name": "xtend", + "repository": { + "type": "git", + "url": "git://github.com/Raynos/xtend.git" + }, "scripts": { "test": "node test" }, - "dependencies": {}, - "devDependencies": { - "tape": "~1.1.0" - }, - "homepage": "https://github.com/Raynos/xtend", - "contributors": [ - { - "name": "Jake Verbaten" - }, - { - "name": "Matt Esch" - } - ], - "bugs": { - "url": "https://github.com/Raynos/xtend/issues", - "email": "raynos2@gmail.com" - }, - "license": "MIT", "testling": { "files": "test.js", "browsers": [ @@ -49,7 +59,5 @@ "iphone/6.0..latest" ] }, - "engines": { - "node": ">=0.4" - } -} + "version": "4.0.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint-plugin-markdown/package.json b/tools/node_modules/eslint-plugin-markdown/package.json index ffdf6f45d0133d..493af337f1a5eb 100644 --- a/tools/node_modules/eslint-plugin-markdown/package.json +++ b/tools/node_modules/eslint-plugin-markdown/package.json @@ -1,41 +1,18 @@ { - "name": "eslint-plugin-markdown", - "version": "2.0.0", - "description": "An ESLint plugin to lint JavaScript in Markdown code fences.", - "license": "MIT", "author": { "name": "Brandon Mills", "url": "https://github.com/btmills" }, - "repository": "eslint/eslint-plugin-markdown", "bugs": { "url": "https://github.com/eslint/eslint-plugin-markdown/issues" }, - "homepage": "https://github.com/eslint/eslint-plugin-markdown#readme", - "keywords": [ - "eslint", - "eslintplugin", - "markdown", - "lint", - "linter" - ], - "scripts": { - "lint": "eslint --ext js,md .", - "prepare": "node ./npm-prepare.js", - "test": "npm run lint && npm run test-cov", - "test-cov": "nyc _mocha -- -c tests/{examples,lib}/**/*.js", - "generate-release": "eslint-generate-release", - "generate-alpharelease": "eslint-generate-prerelease alpha", - "generate-betarelease": "eslint-generate-prerelease beta", - "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" + "bundleDependencies": false, + "dependencies": { + "remark-parse": "^5.0.0", + "unified": "^6.1.2" }, - "main": "index.js", - "files": [ - "index.js", - "lib/index.js", - "lib/processor.js" - ], + "deprecated": false, + "description": "An ESLint plugin to lint JavaScript in Markdown code fences.", "devDependencies": { "chai": "^4.2.0", "eslint": "^6.8.0", @@ -46,14 +23,42 @@ "mocha": "^6.2.2", "nyc": "^14.1.1" }, - "dependencies": { - "remark-parse": "^5.0.0", - "unified": "^6.1.2" + "engines": { + "node": "^8.10.0 || ^10.12.0 || >= 12.0.0" }, + "files": [ + "index.js", + "lib/index.js", + "lib/processor.js" + ], + "homepage": "https://github.com/eslint/eslint-plugin-markdown#readme", + "keywords": [ + "eslint", + "eslintplugin", + "markdown", + "lint", + "linter" + ], + "license": "MIT", + "main": "index.js", + "name": "eslint-plugin-markdown", "peerDependencies": { "eslint": ">=6.0.0" }, - "engines": { - "node": "^8.10.0 || ^10.12.0 || >= 12.0.0" - } -} + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslint-plugin-markdown.git" + }, + "scripts": { + "generate-alpharelease": "eslint-generate-prerelease alpha", + "generate-betarelease": "eslint-generate-prerelease beta", + "generate-rcrelease": "eslint-generate-prerelease rc", + "generate-release": "eslint-generate-release", + "lint": "eslint --ext js,md .", + "prepare": "node ./npm-prepare.js", + "publish-release": "eslint-publish-release", + "test": "npm run lint && npm run test-cov", + "test-cov": "nyc _mocha -- -c tests/{examples,lib}/**/*.js" + }, + "version": "2.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/README.md b/tools/node_modules/eslint/README.md index f384aa8e687fb4..2edb836bbaf68b 100644 --- a/tools/node_modules/eslint/README.md +++ b/tools/node_modules/eslint/README.md @@ -282,8 +282,8 @@ The following companies, organizations, and individuals support ESLint's ongoing <h3>Platinum Sponsors</h3> <p><a href="https://automattic.com"><img src="https://images.opencollective.com/photomatt/d0ef3e1/logo.png" alt="Automattic" height="undefined"></a></p><h3>Gold Sponsors</h3> <p><a href="https://nx.dev"><img src="https://images.opencollective.com/nx/0efbe42/logo.png" alt="Nx (by Nrwl)" height="96"></a> <a href="https://google.com/chrome"><img src="https://images.opencollective.com/chrome/dc55bd4/logo.png" alt="Chrome's Web Framework & Tools Performance Fund" height="96"></a> <a href="https://www.shopify.com"><img src="https://images.opencollective.com/shopify/e780cd4/logo.png" alt="Shopify" height="96"></a> <a href="https://www.salesforce.com"><img src="https://images.opencollective.com/salesforce/ca8f997/logo.png" alt="Salesforce" height="96"></a> <a href="https://www.airbnb.com/"><img src="https://images.opencollective.com/airbnb/d327d66/logo.png" alt="Airbnb" height="96"></a> <a href="https://substack.com/"><img src="https://avatars.githubusercontent.com/u/53023767?v=4" alt="Substack" height="96"></a> <a href="https://aka.ms/microsoftfossfund"><img src="https://avatars.githubusercontent.com/u/67931232?u=7fddc652a464d7151b97e8f108392af7d54fa3e8&v=4" alt="Microsoft FOSS Fund Sponsorships" height="96"></a></p><h3>Silver Sponsors</h3> -<p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a> <a href="https://www.ampproject.org/"><img src="https://images.opencollective.com/amp/c8a3b25/logo.png" alt="AMP Project" height="64"></a></p><h3>Bronze Sponsors</h3> -<p><a href="https://papersowl.com/pay-for-research-paper"><img src="https://images.opencollective.com/papersowl2/9ef85ac/logo.png" alt="Papersowl" height="32"></a> <a href="https://writersperhour.com"><img src="https://images.opencollective.com/writersperhour/5787d4b/logo.png" alt="Writers Per Hour" height="32"></a> <a href="https://www.betacalendars.com/march-calendar.html"><img src="https://images.opencollective.com/betacalendars/9334b33/logo.png" alt="March 2021 calendar" height="32"></a> <a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/b282e39/logo.png" alt="Buy.Fineproxy.Org" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p> +<p><a href="https://retool.com/"><img src="https://images.opencollective.com/retool/98ea68e/logo.png" alt="Retool" height="64"></a> <a href="https://liftoff.io/"><img src="https://images.opencollective.com/liftoff/5c4fa84/logo.png" alt="Liftoff" height="64"></a></p><h3>Bronze Sponsors</h3> +<p><a href="https://buy.fineproxy.org/eng/"><img src="https://images.opencollective.com/buy-fineproxy-org/b282e39/logo.png" alt="Buy.Fineproxy.Org" height="32"></a> <a href="https://www.crosswordsolver.org/anagram-solver/"><img src="https://images.opencollective.com/anagram-solver/2666271/logo.png" alt="Anagram Solver" height="32"></a> <a href="null"><img src="https://images.opencollective.com/bugsnag-stability-monitoring/c2cef36/logo.png" alt="Bugsnag Stability Monitoring" height="32"></a> <a href="https://mixpanel.com"><img src="https://images.opencollective.com/mixpanel/cd682f7/logo.png" alt="Mixpanel" height="32"></a> <a href="https://www.vpsserver.com"><img src="https://images.opencollective.com/vpsservercom/logo.png" alt="VPS Server" height="32"></a> <a href="https://icons8.com"><img src="https://images.opencollective.com/icons8/7fa1641/logo.png" alt="Icons8: free icons, photos, illustrations, and music" height="32"></a> <a href="https://discordapp.com"><img src="https://images.opencollective.com/discordapp/7e3d9a9/logo.png" alt="Discord" height="32"></a> <a href="https://themeisle.com"><img src="https://images.opencollective.com/themeisle/d5592fe/logo.png" alt="ThemeIsle" height="32"></a> <a href="https://www.firesticktricks.com"><img src="https://images.opencollective.com/fire-stick-tricks/b8fbe2c/logo.png" alt="Fire Stick Tricks" height="32"></a></p> <!--sponsorsend--> ## <a name="technology-sponsors"></a>Technology Sponsors diff --git a/tools/node_modules/eslint/lib/cli-engine/formatters/html.js b/tools/node_modules/eslint/lib/cli-engine/formatters/html.js index 69f7395550f2e2..5d4b7e5606038c 100644 --- a/tools/node_modules/eslint/lib/cli-engine/formatters/html.js +++ b/tools/node_modules/eslint/lib/cli-engine/formatters/html.js @@ -72,7 +72,7 @@ function renderMessages(messages, parentIndex, rulesMeta) { * @param {Object} message Message. * @returns {string} HTML (table row) describing a message. */ - return lodash.map(messages, message => { + return messages.map(message => { const lineNumber = message.line || 0; const columnNumber = message.column || 0; let ruleUrl; @@ -103,7 +103,7 @@ function renderMessages(messages, parentIndex, rulesMeta) { * @returns {string} HTML string describing the results. */ function renderResults(results, rulesMeta) { - return lodash.map(results, (result, index) => resultTemplate({ + return results.map((result, index) => resultTemplate({ index, color: renderColor(result.errorCount, result.warningCount), filePath: result.filePath, diff --git a/tools/node_modules/eslint/lib/cli-engine/lint-result-cache.js b/tools/node_modules/eslint/lib/cli-engine/lint-result-cache.js index 25b856f2f91612..3304d429debe85 100644 --- a/tools/node_modules/eslint/lib/cli-engine/lint-result-cache.js +++ b/tools/node_modules/eslint/lib/cli-engine/lint-result-cache.js @@ -67,7 +67,6 @@ class LintResultCache { /** * Creates a new LintResultCache instance. * @param {string} cacheFileLocation The cache file location. - * configuration lookup by file path). * @param {"metadata" | "content"} cacheStrategy The cache strategy to use. */ constructor(cacheFileLocation, cacheStrategy) { diff --git a/tools/node_modules/eslint/lib/linter/report-translator.js b/tools/node_modules/eslint/lib/linter/report-translator.js index bed5af81e5dcec..75005c16e58089 100644 --- a/tools/node_modules/eslint/lib/linter/report-translator.js +++ b/tools/node_modules/eslint/lib/linter/report-translator.js @@ -115,6 +115,17 @@ function normalizeReportLoc(descriptor) { return descriptor.node.loc; } +/** + * Check that a fix has a valid range. + * @param {Fix|null} fix The fix to validate. + * @returns {void} + */ +function assertValidFix(fix) { + if (fix) { + assert(fix.range && typeof fix.range[0] === "number" && typeof fix.range[1] === "number", `Fix has invalid range: ${JSON.stringify(fix, null, 2)}`); + } +} + /** * Compares items in a fixes array by range. * @param {Fix} a The first message. @@ -133,6 +144,10 @@ function compareFixesByRange(a, b) { * @returns {{text: string, range: number[]}} The merged fixes */ function mergeFixes(fixes, sourceCode) { + for (const fix of fixes) { + assertValidFix(fix); + } + if (fixes.length === 0) { return null; } @@ -181,6 +196,8 @@ function normalizeFixes(descriptor, sourceCode) { if (fix && Symbol.iterator in fix) { return mergeFixes(Array.from(fix), sourceCode); } + + assertValidFix(fix); return fix; } diff --git a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js index 905f3418121882..23b78fba279130 100644 --- a/tools/node_modules/eslint/lib/rule-tester/rule-tester.js +++ b/tools/node_modules/eslint/lib/rule-tester/rule-tester.js @@ -427,12 +427,12 @@ class RuleTester { scenarioErrors = [], linter = this.linter; - if (lodash.isNil(test) || typeof test !== "object") { + if (!test || typeof test !== "object") { throw new TypeError(`Test Scenarios for rule ${ruleName} : Could not find test scenario object`); } requiredScenarios.forEach(scenarioType => { - if (lodash.isNil(test[scenarioType])) { + if (!test[scenarioType]) { scenarioErrors.push(`Could not find any ${scenarioType} test scenarios`); } }); diff --git a/tools/node_modules/eslint/lib/rules/eol-last.js b/tools/node_modules/eslint/lib/rules/eol-last.js index fbba6c8f5e8be5..89c76acb2023a8 100644 --- a/tools/node_modules/eslint/lib/rules/eol-last.js +++ b/tools/node_modules/eslint/lib/rules/eol-last.js @@ -54,7 +54,7 @@ module.exports = { }, LF = "\n", CRLF = `\r${LF}`, - endsWithNewline = lodash.endsWith(src, LF); + endsWithNewline = src.endsWith(LF); /* * Empty source is always valid: No content in file so we don't diff --git a/tools/node_modules/eslint/lib/rules/lines-around-comment.js b/tools/node_modules/eslint/lib/rules/lines-around-comment.js index 5e1b83cdd5d24c..6806e793cd193b 100644 --- a/tools/node_modules/eslint/lib/rules/lines-around-comment.js +++ b/tools/node_modules/eslint/lib/rules/lines-around-comment.js @@ -8,8 +8,7 @@ // Requirements //------------------------------------------------------------------------------ -const lodash = require("lodash"), - astUtils = require("./utils/ast-utils"); +const astUtils = require("./utils/ast-utils"); //------------------------------------------------------------------------------ // Helpers @@ -347,7 +346,7 @@ module.exports = { const nextTokenOrComment = sourceCode.getTokenAfter(token, { includeComments: true }); // check for newline before - if (!exceptionStartAllowed && before && !lodash.includes(commentAndEmptyLines, prevLineNum) && + if (!exceptionStartAllowed && before && !commentAndEmptyLines.includes(prevLineNum) && !(astUtils.isCommentToken(previousTokenOrComment) && astUtils.isTokenOnSameLine(previousTokenOrComment, token))) { const lineStart = token.range[0] - token.loc.start.column; const range = [lineStart, lineStart]; @@ -362,7 +361,7 @@ module.exports = { } // check for newline after - if (!exceptionEndAllowed && after && !lodash.includes(commentAndEmptyLines, nextLineNum) && + if (!exceptionEndAllowed && after && !commentAndEmptyLines.includes(nextLineNum) && !(astUtils.isCommentToken(nextTokenOrComment) && astUtils.isTokenOnSameLine(token, nextTokenOrComment))) { context.report({ node: token, diff --git a/tools/node_modules/eslint/lib/rules/max-lines.js b/tools/node_modules/eslint/lib/rules/max-lines.js index 0c7e761fa613b4..ceb014aff71be2 100644 --- a/tools/node_modules/eslint/lib/rules/max-lines.js +++ b/tools/node_modules/eslint/lib/rules/max-lines.js @@ -151,7 +151,7 @@ module.exports = { ); lines = lines.filter( - l => !lodash.includes(commentLines, l.lineNumber) + l => !commentLines.includes(l.lineNumber) ); } diff --git a/tools/node_modules/eslint/lib/rules/object-curly-newline.js b/tools/node_modules/eslint/lib/rules/object-curly-newline.js index 616d59851d646d..c5c098e130150c 100644 --- a/tools/node_modules/eslint/lib/rules/object-curly-newline.js +++ b/tools/node_modules/eslint/lib/rules/object-curly-newline.js @@ -82,7 +82,7 @@ function normalizeOptionValue(value) { function normalizeOptions(options) { const isNodeSpecificOption = lodash.overSome([lodash.isPlainObject, lodash.isString]); - if (lodash.isPlainObject(options) && lodash.some(options, isNodeSpecificOption)) { + if (lodash.isPlainObject(options) && Object.values(options).some(isNodeSpecificOption)) { return { ObjectExpression: normalizeOptionValue(options.ObjectExpression), ObjectPattern: normalizeOptionValue(options.ObjectPattern), diff --git a/tools/node_modules/eslint/lib/shared/runtime-info.js b/tools/node_modules/eslint/lib/shared/runtime-info.js index feed005330e622..3f16c9152e352b 100644 --- a/tools/node_modules/eslint/lib/shared/runtime-info.js +++ b/tools/node_modules/eslint/lib/shared/runtime-info.js @@ -11,7 +11,6 @@ const path = require("path"); const spawn = require("cross-spawn"); -const { isEmpty } = require("lodash"); const log = require("../shared/logging"); const packageJson = require("../../package.json"); @@ -107,7 +106,7 @@ function environment() { * Checking globally returns an empty JSON object, while local checks * include the name and version of the local project. */ - if (isEmpty(parsedStdout) || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) { + if (Object.keys(parsedStdout).length === 0 || !(parsedStdout.dependencies && parsedStdout.dependencies.eslint)) { return "Not found"; } diff --git a/tools/node_modules/eslint/node_modules/@babel/code-frame/package.json b/tools/node_modules/eslint/node_modules/@babel/code-frame/package.json index 07a28a6bda4ec3..213c1916a9ec81 100644 --- a/tools/node_modules/eslint/node_modules/@babel/code-frame/package.json +++ b/tools/node_modules/eslint/node_modules/@babel/code-frame/package.json @@ -1,25 +1,33 @@ { - "name": "@babel/code-frame", - "version": "7.12.11", + "author": { + "name": "Sebastian McKenzie", + "email": "sebmck@gmail.com" + }, + "bugs": { + "url": "https://github.com/babel/babel/issues" + }, + "bundleDependencies": false, + "dependencies": { + "@babel/highlight": "^7.10.4" + }, + "deprecated": false, "description": "Generate errors that contain a code frame that point to source locations.", - "author": "Sebastian McKenzie <sebmck@gmail.com>", + "devDependencies": { + "@types/chalk": "^2.0.0", + "chalk": "^2.0.0", + "strip-ansi": "^4.0.0" + }, "homepage": "https://babeljs.io/", "license": "MIT", + "main": "lib/index.js", + "name": "@babel/code-frame", "publishConfig": { "access": "public" }, "repository": { "type": "git", - "url": "https://github.com/babel/babel.git", + "url": "git+https://github.com/babel/babel.git", "directory": "packages/babel-code-frame" }, - "main": "lib/index.js", - "dependencies": { - "@babel/highlight": "^7.10.4" - }, - "devDependencies": { - "@types/chalk": "^2.0.0", - "chalk": "^2.0.0", - "strip-ansi": "^4.0.0" - } + "version": "7.12.11" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/helper-validator-identifier/package.json b/tools/node_modules/eslint/node_modules/@babel/helper-validator-identifier/package.json index 464dbfa3aace49..e70625729dd6c3 100644 --- a/tools/node_modules/eslint/node_modules/@babel/helper-validator-identifier/package.json +++ b/tools/node_modules/eslint/node_modules/@babel/helper-validator-identifier/package.json @@ -1,20 +1,26 @@ { - "name": "@babel/helper-validator-identifier", - "version": "7.12.11", + "bugs": { + "url": "https://github.com/babel/babel/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "Validate identifier/keywords name", - "repository": { - "type": "git", - "url": "https://github.com/babel/babel.git", - "directory": "packages/babel-helper-validator-identifier" + "devDependencies": { + "charcodes": "^0.2.0", + "unicode-13.0.0": "^0.8.0" }, + "exports": "./lib/index.js", + "homepage": "https://github.com/babel/babel#readme", "license": "MIT", + "main": "./lib/index.js", + "name": "@babel/helper-validator-identifier", "publishConfig": { "access": "public" }, - "main": "./lib/index.js", - "exports": "./lib/index.js", - "devDependencies": { - "charcodes": "^0.2.0", - "unicode-13.0.0": "^0.8.0" - } + "repository": { + "type": "git", + "url": "git+https://github.com/babel/babel.git", + "directory": "packages/babel-helper-validator-identifier" + }, + "version": "7.12.11" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/lib/index.js b/tools/node_modules/eslint/node_modules/@babel/highlight/lib/index.js index fa8897134a57f5..14ca2e36f86904 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/lib/index.js +++ b/tools/node_modules/eslint/node_modules/@babel/highlight/lib/index.js @@ -7,17 +7,11 @@ exports.shouldHighlight = shouldHighlight; exports.getChalk = getChalk; exports.default = highlight; -var jsTokensNs = _interopRequireWildcard(require("js-tokens")); - var _helperValidatorIdentifier = require("@babel/helper-validator-identifier"); -var _chalk = _interopRequireDefault(require("chalk")); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; } +const jsTokens = require("js-tokens"); -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } +const Chalk = require("chalk"); const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]); @@ -39,9 +33,6 @@ const NEWLINE = /\r\n|[\n\r\u2028\u2029]/; const BRACKET = /^[()[\]{}]$/; let tokenize; { - const { - matchToToken - } = jsTokensNs; const JSX_TAG = /^[a-z][\w-]*$/i; const getTokenType = function (token, offset, text) { @@ -73,8 +64,8 @@ let tokenize; tokenize = function* (text) { let match; - while (match = jsTokensNs.default.exec(text)) { - const token = matchToToken(match); + while (match = jsTokens.default.exec(text)) { + const token = jsTokens.matchToToken(match); yield { type: getTokenType(token, match.index, text), value: token.value @@ -103,14 +94,14 @@ function highlightTokens(defs, text) { } function shouldHighlight(options) { - return !!_chalk.default.supportsColor || options.forceColor; + return !!Chalk.supportsColor || options.forceColor; } function getChalk(options) { - return options.forceColor ? new _chalk.default.constructor({ + return options.forceColor ? new Chalk.constructor({ enabled: true, level: 1 - }) : _chalk.default; + }) : Chalk; } function highlight(code, options = {}) { diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/index.js b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/index.js deleted file mode 100644 index 90a871c4d78f6f..00000000000000 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/index.js +++ /dev/null @@ -1,165 +0,0 @@ -'use strict'; -const colorConvert = require('color-convert'); - -const wrapAnsi16 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${code + offset}m`; -}; - -const wrapAnsi256 = (fn, offset) => function () { - const code = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};5;${code}m`; -}; - -const wrapAnsi16m = (fn, offset) => function () { - const rgb = fn.apply(colorConvert, arguments); - return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; -}; - -function assembleStyles() { - const codes = new Map(); - const styles = { - modifier: { - reset: [0, 0], - // 21 isn't widely supported and 22 does the same thing - bold: [1, 22], - dim: [2, 22], - italic: [3, 23], - underline: [4, 24], - inverse: [7, 27], - hidden: [8, 28], - strikethrough: [9, 29] - }, - color: { - black: [30, 39], - red: [31, 39], - green: [32, 39], - yellow: [33, 39], - blue: [34, 39], - magenta: [35, 39], - cyan: [36, 39], - white: [37, 39], - gray: [90, 39], - - // Bright color - redBright: [91, 39], - greenBright: [92, 39], - yellowBright: [93, 39], - blueBright: [94, 39], - magentaBright: [95, 39], - cyanBright: [96, 39], - whiteBright: [97, 39] - }, - bgColor: { - bgBlack: [40, 49], - bgRed: [41, 49], - bgGreen: [42, 49], - bgYellow: [43, 49], - bgBlue: [44, 49], - bgMagenta: [45, 49], - bgCyan: [46, 49], - bgWhite: [47, 49], - - // Bright color - bgBlackBright: [100, 49], - bgRedBright: [101, 49], - bgGreenBright: [102, 49], - bgYellowBright: [103, 49], - bgBlueBright: [104, 49], - bgMagentaBright: [105, 49], - bgCyanBright: [106, 49], - bgWhiteBright: [107, 49] - } - }; - - // Fix humans - styles.color.grey = styles.color.gray; - - for (const groupName of Object.keys(styles)) { - const group = styles[groupName]; - - for (const styleName of Object.keys(group)) { - const style = group[styleName]; - - styles[styleName] = { - open: `\u001B[${style[0]}m`, - close: `\u001B[${style[1]}m` - }; - - group[styleName] = styles[styleName]; - - codes.set(style[0], style[1]); - } - - Object.defineProperty(styles, groupName, { - value: group, - enumerable: false - }); - - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); - } - - const ansi2ansi = n => n; - const rgb2rgb = (r, g, b) => [r, g, b]; - - styles.color.close = '\u001B[39m'; - styles.bgColor.close = '\u001B[49m'; - - styles.color.ansi = { - ansi: wrapAnsi16(ansi2ansi, 0) - }; - styles.color.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 0) - }; - styles.color.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 0) - }; - - styles.bgColor.ansi = { - ansi: wrapAnsi16(ansi2ansi, 10) - }; - styles.bgColor.ansi256 = { - ansi256: wrapAnsi256(ansi2ansi, 10) - }; - styles.bgColor.ansi16m = { - rgb: wrapAnsi16m(rgb2rgb, 10) - }; - - for (let key of Object.keys(colorConvert)) { - if (typeof colorConvert[key] !== 'object') { - continue; - } - - const suite = colorConvert[key]; - - if (key === 'ansi16') { - key = 'ansi'; - } - - if ('ansi16' in suite) { - styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); - styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); - } - - if ('ansi256' in suite) { - styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); - styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); - } - - if ('rgb' in suite) { - styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); - styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); - } - } - - return styles; -} - -// Make the export immutable -Object.defineProperty(module, 'exports', { - enumerable: true, - get: assembleStyles -}); diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/package.json b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/package.json deleted file mode 100644 index 65edb48c399c5c..00000000000000 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/package.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "name": "ansi-styles", - "version": "3.2.1", - "description": "ANSI escape codes for styling strings in the terminal", - "license": "MIT", - "repository": "chalk/ansi-styles", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=4" - }, - "scripts": { - "test": "xo && ava", - "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor" - }, - "files": [ - "index.js" - ], - "keywords": [ - "ansi", - "styles", - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "string", - "tty", - "escape", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "log", - "logging", - "command-line", - "text" - ], - "dependencies": { - "color-convert": "^1.9.0" - }, - "devDependencies": { - "ava": "*", - "babel-polyfill": "^6.23.0", - "svg-term-cli": "^2.1.1", - "xo": "*" - }, - "ava": { - "require": "babel-polyfill" - } -} diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/chalk/package.json b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/chalk/package.json index bc324685a7625f..270fecdc347d42 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/chalk/package.json +++ b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/chalk/package.json @@ -1,71 +1,80 @@ { - "name": "chalk", - "version": "2.4.2", - "description": "Terminal string styling done right", - "license": "MIT", - "repository": "chalk/chalk", - "engines": { - "node": ">=4" - }, - "scripts": { - "test": "xo && tsc --project types && flow --max-warnings=0 && nyc ava", - "bench": "matcha benchmark.js", - "coveralls": "nyc report --reporter=text-lcov | coveralls" - }, - "files": [ - "index.js", - "templates.js", - "types/index.d.ts", - "index.js.flow" - ], - "keywords": [ - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "string", - "str", - "ansi", - "style", - "styles", - "tty", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "log", - "logging", - "command-line", - "text" - ], - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "devDependencies": { - "ava": "*", - "coveralls": "^3.0.0", - "execa": "^0.9.0", - "flow-bin": "^0.68.0", - "import-fresh": "^2.0.0", - "matcha": "^0.7.0", - "nyc": "^11.0.2", - "resolve-from": "^4.0.0", - "typescript": "^2.5.3", - "xo": "*" - }, - "types": "types/index.d.ts", - "xo": { - "envs": [ - "node", - "mocha" - ], - "ignores": [ - "test/_flow.js" - ] - } -} + "bugs": { + "url": "https://github.com/chalk/chalk/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "deprecated": false, + "description": "Terminal string styling done right", + "devDependencies": { + "ava": "*", + "coveralls": "^3.0.0", + "execa": "^0.9.0", + "flow-bin": "^0.68.0", + "import-fresh": "^2.0.0", + "matcha": "^0.7.0", + "nyc": "^11.0.2", + "resolve-from": "^4.0.0", + "typescript": "^2.5.3", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js", + "templates.js", + "types/index.d.ts", + "index.js.flow" + ], + "homepage": "https://github.com/chalk/chalk#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "str", + "ansi", + "style", + "styles", + "tty", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "chalk", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/chalk.git" + }, + "scripts": { + "bench": "matcha benchmark.js", + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "test": "xo && tsc --project types && flow --max-warnings=0 && nyc ava" + }, + "types": "types/index.d.ts", + "version": "2.4.2", + "xo": { + "envs": [ + "node", + "mocha" + ], + "ignores": [ + "test/_flow.js" + ] + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/index.js b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/index.js deleted file mode 100644 index e65b5d775da353..00000000000000 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/index.js +++ /dev/null @@ -1,78 +0,0 @@ -var conversions = require('./conversions'); -var route = require('./route'); - -var convert = {}; - -var models = Object.keys(conversions); - -function wrapRaw(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - return fn(args); - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -function wrapRounded(fn) { - var wrappedFn = function (args) { - if (args === undefined || args === null) { - return args; - } - - if (arguments.length > 1) { - args = Array.prototype.slice.call(arguments); - } - - var result = fn(args); - - // we're assuming the result is an array here. - // see notice in conversions.js; don't use box types - // in conversion functions. - if (typeof result === 'object') { - for (var len = result.length, i = 0; i < len; i++) { - result[i] = Math.round(result[i]); - } - } - - return result; - }; - - // preserve .conversion property if there is one - if ('conversion' in fn) { - wrappedFn.conversion = fn.conversion; - } - - return wrappedFn; -} - -models.forEach(function (fromModel) { - convert[fromModel] = {}; - - Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); - Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - - var routes = route(fromModel); - var routeModels = Object.keys(routes); - - routeModels.forEach(function (toModel) { - var fn = routes[toModel]; - - convert[fromModel][toModel] = wrapRounded(fn); - convert[fromModel][toModel].raw = wrapRaw(fn); - }); -}); - -module.exports = convert; diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/package.json b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/package.json deleted file mode 100644 index d061123ef02f40..00000000000000 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/package.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "color-name", - "version": "1.1.3", - "description": "A list of color names and its values", - "main": "index.js", - "scripts": { - "test": "node test.js" - }, - "repository": { - "type": "git", - "url": "git@github.com:dfcreative/color-name.git" - }, - "keywords": [ - "color-name", - "color", - "color-keyword", - "keyword" - ], - "author": "DY <dfcreative@gmail.com>", - "license": "MIT", - "bugs": { - "url": "https://github.com/dfcreative/color-name/issues" - }, - "homepage": "https://github.com/dfcreative/color-name" -} diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/index.js b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/index.js deleted file mode 100644 index 5139728fba6a26..00000000000000 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/index.js +++ /dev/null @@ -1,8 +0,0 @@ -'use strict'; -module.exports = (flag, argv) => { - argv = argv || process.argv; - const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const pos = argv.indexOf(prefix + flag); - const terminatorPos = argv.indexOf('--'); - return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); -}; diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/package.json b/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/package.json deleted file mode 100644 index ad199f5cdb0436..00000000000000 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/package.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "supports-color", - "version": "5.5.0", - "description": "Detect whether a terminal supports color", - "license": "MIT", - "repository": "chalk/supports-color", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=4" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js", - "browser.js" - ], - "keywords": [ - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "ansi", - "styles", - "tty", - "rgb", - "256", - "shell", - "xterm", - "command-line", - "support", - "supports", - "capability", - "detect", - "truecolor", - "16m" - ], - "dependencies": { - "has-flag": "^3.0.0" - }, - "devDependencies": { - "ava": "^0.25.0", - "import-fresh": "^2.0.0", - "xo": "^0.20.0" - }, - "browser": "browser.js" -} diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/package.json b/tools/node_modules/eslint/node_modules/@babel/highlight/package.json index 4504cd15a14ed5..09dc745cb47d97 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/package.json +++ b/tools/node_modules/eslint/node_modules/@babel/highlight/package.json @@ -1,26 +1,34 @@ { - "name": "@babel/highlight", - "version": "7.13.8", - "description": "Syntax highlight JavaScript strings for output in terminals.", - "author": "suchipi <me@suchipi.com>", - "homepage": "https://babel.dev/docs/en/next/babel-highlight", - "license": "MIT", - "publishConfig": { - "access": "public" + "author": { + "name": "suchipi", + "email": "me@suchipi.com" }, - "repository": { - "type": "git", - "url": "https://github.com/babel/babel.git", - "directory": "packages/babel-highlight" + "bugs": { + "url": "https://github.com/babel/babel/issues" }, - "main": "lib/index.js", + "bundleDependencies": false, "dependencies": { "@babel/helper-validator-identifier": "^7.12.11", "chalk": "^2.0.0", "js-tokens": "^4.0.0" }, + "deprecated": false, + "description": "Syntax highlight JavaScript strings for output in terminals.", "devDependencies": { "@types/chalk": "^2.0.0", "strip-ansi": "^4.0.0" - } + }, + "homepage": "https://babel.dev/docs/en/next/babel-highlight", + "license": "MIT", + "main": "lib/index.js", + "name": "@babel/highlight", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/babel/babel.git", + "directory": "packages/babel-highlight" + }, + "version": "7.13.10" } \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/globals.json b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/globals.json new file mode 100644 index 00000000000000..b85dc3f80d5148 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/globals.json @@ -0,0 +1,1586 @@ +{ + "builtin": { + "Array": false, + "ArrayBuffer": false, + "Atomics": false, + "BigInt": false, + "BigInt64Array": false, + "BigUint64Array": false, + "Boolean": false, + "constructor": false, + "DataView": false, + "Date": false, + "decodeURI": false, + "decodeURIComponent": false, + "encodeURI": false, + "encodeURIComponent": false, + "Error": false, + "escape": false, + "eval": false, + "EvalError": false, + "Float32Array": false, + "Float64Array": false, + "Function": false, + "globalThis": false, + "hasOwnProperty": false, + "Infinity": false, + "Int16Array": false, + "Int32Array": false, + "Int8Array": false, + "isFinite": false, + "isNaN": false, + "isPrototypeOf": false, + "JSON": false, + "Map": false, + "Math": false, + "NaN": false, + "Number": false, + "Object": false, + "parseFloat": false, + "parseInt": false, + "Promise": false, + "propertyIsEnumerable": false, + "Proxy": false, + "RangeError": false, + "ReferenceError": false, + "Reflect": false, + "RegExp": false, + "Set": false, + "SharedArrayBuffer": false, + "String": false, + "Symbol": false, + "SyntaxError": false, + "toLocaleString": false, + "toString": false, + "TypeError": false, + "Uint16Array": false, + "Uint32Array": false, + "Uint8Array": false, + "Uint8ClampedArray": false, + "undefined": false, + "unescape": false, + "URIError": false, + "valueOf": false, + "WeakMap": false, + "WeakSet": false + }, + "es5": { + "Array": false, + "Boolean": false, + "constructor": false, + "Date": false, + "decodeURI": false, + "decodeURIComponent": false, + "encodeURI": false, + "encodeURIComponent": false, + "Error": false, + "escape": false, + "eval": false, + "EvalError": false, + "Function": false, + "hasOwnProperty": false, + "Infinity": false, + "isFinite": false, + "isNaN": false, + "isPrototypeOf": false, + "JSON": false, + "Math": false, + "NaN": false, + "Number": false, + "Object": false, + "parseFloat": false, + "parseInt": false, + "propertyIsEnumerable": false, + "RangeError": false, + "ReferenceError": false, + "RegExp": false, + "String": false, + "SyntaxError": false, + "toLocaleString": false, + "toString": false, + "TypeError": false, + "undefined": false, + "unescape": false, + "URIError": false, + "valueOf": false + }, + "es2015": { + "Array": false, + "ArrayBuffer": false, + "Boolean": false, + "constructor": false, + "DataView": false, + "Date": false, + "decodeURI": false, + "decodeURIComponent": false, + "encodeURI": false, + "encodeURIComponent": false, + "Error": false, + "escape": false, + "eval": false, + "EvalError": false, + "Float32Array": false, + "Float64Array": false, + "Function": false, + "hasOwnProperty": false, + "Infinity": false, + "Int16Array": false, + "Int32Array": false, + "Int8Array": false, + "isFinite": false, + "isNaN": false, + "isPrototypeOf": false, + "JSON": false, + "Map": false, + "Math": false, + "NaN": false, + "Number": false, + "Object": false, + "parseFloat": false, + "parseInt": false, + "Promise": false, + "propertyIsEnumerable": false, + "Proxy": false, + "RangeError": false, + "ReferenceError": false, + "Reflect": false, + "RegExp": false, + "Set": false, + "String": false, + "Symbol": false, + "SyntaxError": false, + "toLocaleString": false, + "toString": false, + "TypeError": false, + "Uint16Array": false, + "Uint32Array": false, + "Uint8Array": false, + "Uint8ClampedArray": false, + "undefined": false, + "unescape": false, + "URIError": false, + "valueOf": false, + "WeakMap": false, + "WeakSet": false + }, + "es2017": { + "Array": false, + "ArrayBuffer": false, + "Atomics": false, + "Boolean": false, + "constructor": false, + "DataView": false, + "Date": false, + "decodeURI": false, + "decodeURIComponent": false, + "encodeURI": false, + "encodeURIComponent": false, + "Error": false, + "escape": false, + "eval": false, + "EvalError": false, + "Float32Array": false, + "Float64Array": false, + "Function": false, + "hasOwnProperty": false, + "Infinity": false, + "Int16Array": false, + "Int32Array": false, + "Int8Array": false, + "isFinite": false, + "isNaN": false, + "isPrototypeOf": false, + "JSON": false, + "Map": false, + "Math": false, + "NaN": false, + "Number": false, + "Object": false, + "parseFloat": false, + "parseInt": false, + "Promise": false, + "propertyIsEnumerable": false, + "Proxy": false, + "RangeError": false, + "ReferenceError": false, + "Reflect": false, + "RegExp": false, + "Set": false, + "SharedArrayBuffer": false, + "String": false, + "Symbol": false, + "SyntaxError": false, + "toLocaleString": false, + "toString": false, + "TypeError": false, + "Uint16Array": false, + "Uint32Array": false, + "Uint8Array": false, + "Uint8ClampedArray": false, + "undefined": false, + "unescape": false, + "URIError": false, + "valueOf": false, + "WeakMap": false, + "WeakSet": false + }, + "browser": { + "AbortController": false, + "AbortSignal": false, + "addEventListener": false, + "alert": false, + "AnalyserNode": false, + "Animation": false, + "AnimationEffectReadOnly": false, + "AnimationEffectTiming": false, + "AnimationEffectTimingReadOnly": false, + "AnimationEvent": false, + "AnimationPlaybackEvent": false, + "AnimationTimeline": false, + "applicationCache": false, + "ApplicationCache": false, + "ApplicationCacheErrorEvent": false, + "atob": false, + "Attr": false, + "Audio": false, + "AudioBuffer": false, + "AudioBufferSourceNode": false, + "AudioContext": false, + "AudioDestinationNode": false, + "AudioListener": false, + "AudioNode": false, + "AudioParam": false, + "AudioProcessingEvent": false, + "AudioScheduledSourceNode": false, + "AudioWorkletGlobalScope ": false, + "AudioWorkletNode": false, + "AudioWorkletProcessor": false, + "BarProp": false, + "BaseAudioContext": false, + "BatteryManager": false, + "BeforeUnloadEvent": false, + "BiquadFilterNode": false, + "Blob": false, + "BlobEvent": false, + "blur": false, + "BroadcastChannel": false, + "btoa": false, + "BudgetService": false, + "ByteLengthQueuingStrategy": false, + "Cache": false, + "caches": false, + "CacheStorage": false, + "cancelAnimationFrame": false, + "cancelIdleCallback": false, + "CanvasCaptureMediaStreamTrack": false, + "CanvasGradient": false, + "CanvasPattern": false, + "CanvasRenderingContext2D": false, + "ChannelMergerNode": false, + "ChannelSplitterNode": false, + "CharacterData": false, + "clearInterval": false, + "clearTimeout": false, + "clientInformation": false, + "ClipboardEvent": false, + "close": false, + "closed": false, + "CloseEvent": false, + "Comment": false, + "CompositionEvent": false, + "confirm": false, + "console": false, + "ConstantSourceNode": false, + "ConvolverNode": false, + "CountQueuingStrategy": false, + "createImageBitmap": false, + "Credential": false, + "CredentialsContainer": false, + "crypto": false, + "Crypto": false, + "CryptoKey": false, + "CSS": false, + "CSSConditionRule": false, + "CSSFontFaceRule": false, + "CSSGroupingRule": false, + "CSSImportRule": false, + "CSSKeyframeRule": false, + "CSSKeyframesRule": false, + "CSSMediaRule": false, + "CSSNamespaceRule": false, + "CSSPageRule": false, + "CSSRule": false, + "CSSRuleList": false, + "CSSStyleDeclaration": false, + "CSSStyleRule": false, + "CSSStyleSheet": false, + "CSSSupportsRule": false, + "CustomElementRegistry": false, + "customElements": false, + "CustomEvent": false, + "DataTransfer": false, + "DataTransferItem": false, + "DataTransferItemList": false, + "defaultstatus": false, + "defaultStatus": false, + "DelayNode": false, + "DeviceMotionEvent": false, + "DeviceOrientationEvent": false, + "devicePixelRatio": false, + "dispatchEvent": false, + "document": false, + "Document": false, + "DocumentFragment": false, + "DocumentType": false, + "DOMError": false, + "DOMException": false, + "DOMImplementation": false, + "DOMMatrix": false, + "DOMMatrixReadOnly": false, + "DOMParser": false, + "DOMPoint": false, + "DOMPointReadOnly": false, + "DOMQuad": false, + "DOMRect": false, + "DOMRectReadOnly": false, + "DOMStringList": false, + "DOMStringMap": false, + "DOMTokenList": false, + "DragEvent": false, + "DynamicsCompressorNode": false, + "Element": false, + "ErrorEvent": false, + "event": false, + "Event": false, + "EventSource": false, + "EventTarget": false, + "external": false, + "fetch": false, + "File": false, + "FileList": false, + "FileReader": false, + "find": false, + "focus": false, + "FocusEvent": false, + "FontFace": false, + "FontFaceSetLoadEvent": false, + "FormData": false, + "frameElement": false, + "frames": false, + "GainNode": false, + "Gamepad": false, + "GamepadButton": false, + "GamepadEvent": false, + "getComputedStyle": false, + "getSelection": false, + "HashChangeEvent": false, + "Headers": false, + "history": false, + "History": false, + "HTMLAllCollection": false, + "HTMLAnchorElement": false, + "HTMLAreaElement": false, + "HTMLAudioElement": false, + "HTMLBaseElement": false, + "HTMLBodyElement": false, + "HTMLBRElement": false, + "HTMLButtonElement": false, + "HTMLCanvasElement": false, + "HTMLCollection": false, + "HTMLContentElement": false, + "HTMLDataElement": false, + "HTMLDataListElement": false, + "HTMLDetailsElement": false, + "HTMLDialogElement": false, + "HTMLDirectoryElement": false, + "HTMLDivElement": false, + "HTMLDListElement": false, + "HTMLDocument": false, + "HTMLElement": false, + "HTMLEmbedElement": false, + "HTMLFieldSetElement": false, + "HTMLFontElement": false, + "HTMLFormControlsCollection": false, + "HTMLFormElement": false, + "HTMLFrameElement": false, + "HTMLFrameSetElement": false, + "HTMLHeadElement": false, + "HTMLHeadingElement": false, + "HTMLHRElement": false, + "HTMLHtmlElement": false, + "HTMLIFrameElement": false, + "HTMLImageElement": false, + "HTMLInputElement": false, + "HTMLLabelElement": false, + "HTMLLegendElement": false, + "HTMLLIElement": false, + "HTMLLinkElement": false, + "HTMLMapElement": false, + "HTMLMarqueeElement": false, + "HTMLMediaElement": false, + "HTMLMenuElement": false, + "HTMLMetaElement": false, + "HTMLMeterElement": false, + "HTMLModElement": false, + "HTMLObjectElement": false, + "HTMLOListElement": false, + "HTMLOptGroupElement": false, + "HTMLOptionElement": false, + "HTMLOptionsCollection": false, + "HTMLOutputElement": false, + "HTMLParagraphElement": false, + "HTMLParamElement": false, + "HTMLPictureElement": false, + "HTMLPreElement": false, + "HTMLProgressElement": false, + "HTMLQuoteElement": false, + "HTMLScriptElement": false, + "HTMLSelectElement": false, + "HTMLShadowElement": false, + "HTMLSlotElement": false, + "HTMLSourceElement": false, + "HTMLSpanElement": false, + "HTMLStyleElement": false, + "HTMLTableCaptionElement": false, + "HTMLTableCellElement": false, + "HTMLTableColElement": false, + "HTMLTableElement": false, + "HTMLTableRowElement": false, + "HTMLTableSectionElement": false, + "HTMLTemplateElement": false, + "HTMLTextAreaElement": false, + "HTMLTimeElement": false, + "HTMLTitleElement": false, + "HTMLTrackElement": false, + "HTMLUListElement": false, + "HTMLUnknownElement": false, + "HTMLVideoElement": false, + "IDBCursor": false, + "IDBCursorWithValue": false, + "IDBDatabase": false, + "IDBFactory": false, + "IDBIndex": false, + "IDBKeyRange": false, + "IDBObjectStore": false, + "IDBOpenDBRequest": false, + "IDBRequest": false, + "IDBTransaction": false, + "IDBVersionChangeEvent": false, + "IdleDeadline": false, + "IIRFilterNode": false, + "Image": false, + "ImageBitmap": false, + "ImageBitmapRenderingContext": false, + "ImageCapture": false, + "ImageData": false, + "indexedDB": false, + "innerHeight": false, + "innerWidth": false, + "InputEvent": false, + "IntersectionObserver": false, + "IntersectionObserverEntry": false, + "Intl": false, + "isSecureContext": false, + "KeyboardEvent": false, + "KeyframeEffect": false, + "KeyframeEffectReadOnly": false, + "length": false, + "localStorage": false, + "location": true, + "Location": false, + "locationbar": false, + "matchMedia": false, + "MediaDeviceInfo": false, + "MediaDevices": false, + "MediaElementAudioSourceNode": false, + "MediaEncryptedEvent": false, + "MediaError": false, + "MediaKeyMessageEvent": false, + "MediaKeySession": false, + "MediaKeyStatusMap": false, + "MediaKeySystemAccess": false, + "MediaList": false, + "MediaQueryList": false, + "MediaQueryListEvent": false, + "MediaRecorder": false, + "MediaSettingsRange": false, + "MediaSource": false, + "MediaStream": false, + "MediaStreamAudioDestinationNode": false, + "MediaStreamAudioSourceNode": false, + "MediaStreamEvent": false, + "MediaStreamTrack": false, + "MediaStreamTrackEvent": false, + "menubar": false, + "MessageChannel": false, + "MessageEvent": false, + "MessagePort": false, + "MIDIAccess": false, + "MIDIConnectionEvent": false, + "MIDIInput": false, + "MIDIInputMap": false, + "MIDIMessageEvent": false, + "MIDIOutput": false, + "MIDIOutputMap": false, + "MIDIPort": false, + "MimeType": false, + "MimeTypeArray": false, + "MouseEvent": false, + "moveBy": false, + "moveTo": false, + "MutationEvent": false, + "MutationObserver": false, + "MutationRecord": false, + "name": false, + "NamedNodeMap": false, + "NavigationPreloadManager": false, + "navigator": false, + "Navigator": false, + "NetworkInformation": false, + "Node": false, + "NodeFilter": false, + "NodeIterator": false, + "NodeList": false, + "Notification": false, + "OfflineAudioCompletionEvent": false, + "OfflineAudioContext": false, + "offscreenBuffering": false, + "OffscreenCanvas": true, + "onabort": true, + "onafterprint": true, + "onanimationend": true, + "onanimationiteration": true, + "onanimationstart": true, + "onappinstalled": true, + "onauxclick": true, + "onbeforeinstallprompt": true, + "onbeforeprint": true, + "onbeforeunload": true, + "onblur": true, + "oncancel": true, + "oncanplay": true, + "oncanplaythrough": true, + "onchange": true, + "onclick": true, + "onclose": true, + "oncontextmenu": true, + "oncuechange": true, + "ondblclick": true, + "ondevicemotion": true, + "ondeviceorientation": true, + "ondeviceorientationabsolute": true, + "ondrag": true, + "ondragend": true, + "ondragenter": true, + "ondragleave": true, + "ondragover": true, + "ondragstart": true, + "ondrop": true, + "ondurationchange": true, + "onemptied": true, + "onended": true, + "onerror": true, + "onfocus": true, + "ongotpointercapture": true, + "onhashchange": true, + "oninput": true, + "oninvalid": true, + "onkeydown": true, + "onkeypress": true, + "onkeyup": true, + "onlanguagechange": true, + "onload": true, + "onloadeddata": true, + "onloadedmetadata": true, + "onloadstart": true, + "onlostpointercapture": true, + "onmessage": true, + "onmessageerror": true, + "onmousedown": true, + "onmouseenter": true, + "onmouseleave": true, + "onmousemove": true, + "onmouseout": true, + "onmouseover": true, + "onmouseup": true, + "onmousewheel": true, + "onoffline": true, + "ononline": true, + "onpagehide": true, + "onpageshow": true, + "onpause": true, + "onplay": true, + "onplaying": true, + "onpointercancel": true, + "onpointerdown": true, + "onpointerenter": true, + "onpointerleave": true, + "onpointermove": true, + "onpointerout": true, + "onpointerover": true, + "onpointerup": true, + "onpopstate": true, + "onprogress": true, + "onratechange": true, + "onrejectionhandled": true, + "onreset": true, + "onresize": true, + "onscroll": true, + "onsearch": true, + "onseeked": true, + "onseeking": true, + "onselect": true, + "onstalled": true, + "onstorage": true, + "onsubmit": true, + "onsuspend": true, + "ontimeupdate": true, + "ontoggle": true, + "ontransitionend": true, + "onunhandledrejection": true, + "onunload": true, + "onvolumechange": true, + "onwaiting": true, + "onwheel": true, + "open": false, + "openDatabase": false, + "opener": false, + "Option": false, + "origin": false, + "OscillatorNode": false, + "outerHeight": false, + "outerWidth": false, + "PageTransitionEvent": false, + "pageXOffset": false, + "pageYOffset": false, + "PannerNode": false, + "parent": false, + "Path2D": false, + "PaymentAddress": false, + "PaymentRequest": false, + "PaymentRequestUpdateEvent": false, + "PaymentResponse": false, + "performance": false, + "Performance": false, + "PerformanceEntry": false, + "PerformanceLongTaskTiming": false, + "PerformanceMark": false, + "PerformanceMeasure": false, + "PerformanceNavigation": false, + "PerformanceNavigationTiming": false, + "PerformanceObserver": false, + "PerformanceObserverEntryList": false, + "PerformancePaintTiming": false, + "PerformanceResourceTiming": false, + "PerformanceTiming": false, + "PeriodicWave": false, + "Permissions": false, + "PermissionStatus": false, + "personalbar": false, + "PhotoCapabilities": false, + "Plugin": false, + "PluginArray": false, + "PointerEvent": false, + "PopStateEvent": false, + "postMessage": false, + "Presentation": false, + "PresentationAvailability": false, + "PresentationConnection": false, + "PresentationConnectionAvailableEvent": false, + "PresentationConnectionCloseEvent": false, + "PresentationConnectionList": false, + "PresentationReceiver": false, + "PresentationRequest": false, + "print": false, + "ProcessingInstruction": false, + "ProgressEvent": false, + "PromiseRejectionEvent": false, + "prompt": false, + "PushManager": false, + "PushSubscription": false, + "PushSubscriptionOptions": false, + "queueMicrotask": false, + "RadioNodeList": false, + "Range": false, + "ReadableStream": false, + "registerProcessor": false, + "RemotePlayback": false, + "removeEventListener": false, + "Request": false, + "requestAnimationFrame": false, + "requestIdleCallback": false, + "resizeBy": false, + "ResizeObserver": false, + "ResizeObserverEntry": false, + "resizeTo": false, + "Response": false, + "RTCCertificate": false, + "RTCDataChannel": false, + "RTCDataChannelEvent": false, + "RTCDtlsTransport": false, + "RTCIceCandidate": false, + "RTCIceGatherer": false, + "RTCIceTransport": false, + "RTCPeerConnection": false, + "RTCPeerConnectionIceEvent": false, + "RTCRtpContributingSource": false, + "RTCRtpReceiver": false, + "RTCRtpSender": false, + "RTCSctpTransport": false, + "RTCSessionDescription": false, + "RTCStatsReport": false, + "RTCTrackEvent": false, + "screen": false, + "Screen": false, + "screenLeft": false, + "ScreenOrientation": false, + "screenTop": false, + "screenX": false, + "screenY": false, + "ScriptProcessorNode": false, + "scroll": false, + "scrollbars": false, + "scrollBy": false, + "scrollTo": false, + "scrollX": false, + "scrollY": false, + "SecurityPolicyViolationEvent": false, + "Selection": false, + "self": false, + "ServiceWorker": false, + "ServiceWorkerContainer": false, + "ServiceWorkerRegistration": false, + "sessionStorage": false, + "setInterval": false, + "setTimeout": false, + "ShadowRoot": false, + "SharedWorker": false, + "SourceBuffer": false, + "SourceBufferList": false, + "speechSynthesis": false, + "SpeechSynthesisEvent": false, + "SpeechSynthesisUtterance": false, + "StaticRange": false, + "status": false, + "statusbar": false, + "StereoPannerNode": false, + "stop": false, + "Storage": false, + "StorageEvent": false, + "StorageManager": false, + "styleMedia": false, + "StyleSheet": false, + "StyleSheetList": false, + "SubtleCrypto": false, + "SVGAElement": false, + "SVGAngle": false, + "SVGAnimatedAngle": false, + "SVGAnimatedBoolean": false, + "SVGAnimatedEnumeration": false, + "SVGAnimatedInteger": false, + "SVGAnimatedLength": false, + "SVGAnimatedLengthList": false, + "SVGAnimatedNumber": false, + "SVGAnimatedNumberList": false, + "SVGAnimatedPreserveAspectRatio": false, + "SVGAnimatedRect": false, + "SVGAnimatedString": false, + "SVGAnimatedTransformList": false, + "SVGAnimateElement": false, + "SVGAnimateMotionElement": false, + "SVGAnimateTransformElement": false, + "SVGAnimationElement": false, + "SVGCircleElement": false, + "SVGClipPathElement": false, + "SVGComponentTransferFunctionElement": false, + "SVGDefsElement": false, + "SVGDescElement": false, + "SVGDiscardElement": false, + "SVGElement": false, + "SVGEllipseElement": false, + "SVGFEBlendElement": false, + "SVGFEColorMatrixElement": false, + "SVGFEComponentTransferElement": false, + "SVGFECompositeElement": false, + "SVGFEConvolveMatrixElement": false, + "SVGFEDiffuseLightingElement": false, + "SVGFEDisplacementMapElement": false, + "SVGFEDistantLightElement": false, + "SVGFEDropShadowElement": false, + "SVGFEFloodElement": false, + "SVGFEFuncAElement": false, + "SVGFEFuncBElement": false, + "SVGFEFuncGElement": false, + "SVGFEFuncRElement": false, + "SVGFEGaussianBlurElement": false, + "SVGFEImageElement": false, + "SVGFEMergeElement": false, + "SVGFEMergeNodeElement": false, + "SVGFEMorphologyElement": false, + "SVGFEOffsetElement": false, + "SVGFEPointLightElement": false, + "SVGFESpecularLightingElement": false, + "SVGFESpotLightElement": false, + "SVGFETileElement": false, + "SVGFETurbulenceElement": false, + "SVGFilterElement": false, + "SVGForeignObjectElement": false, + "SVGGElement": false, + "SVGGeometryElement": false, + "SVGGradientElement": false, + "SVGGraphicsElement": false, + "SVGImageElement": false, + "SVGLength": false, + "SVGLengthList": false, + "SVGLinearGradientElement": false, + "SVGLineElement": false, + "SVGMarkerElement": false, + "SVGMaskElement": false, + "SVGMatrix": false, + "SVGMetadataElement": false, + "SVGMPathElement": false, + "SVGNumber": false, + "SVGNumberList": false, + "SVGPathElement": false, + "SVGPatternElement": false, + "SVGPoint": false, + "SVGPointList": false, + "SVGPolygonElement": false, + "SVGPolylineElement": false, + "SVGPreserveAspectRatio": false, + "SVGRadialGradientElement": false, + "SVGRect": false, + "SVGRectElement": false, + "SVGScriptElement": false, + "SVGSetElement": false, + "SVGStopElement": false, + "SVGStringList": false, + "SVGStyleElement": false, + "SVGSVGElement": false, + "SVGSwitchElement": false, + "SVGSymbolElement": false, + "SVGTextContentElement": false, + "SVGTextElement": false, + "SVGTextPathElement": false, + "SVGTextPositioningElement": false, + "SVGTitleElement": false, + "SVGTransform": false, + "SVGTransformList": false, + "SVGTSpanElement": false, + "SVGUnitTypes": false, + "SVGUseElement": false, + "SVGViewElement": false, + "TaskAttributionTiming": false, + "Text": false, + "TextDecoder": false, + "TextEncoder": false, + "TextEvent": false, + "TextMetrics": false, + "TextTrack": false, + "TextTrackCue": false, + "TextTrackCueList": false, + "TextTrackList": false, + "TimeRanges": false, + "toolbar": false, + "top": false, + "Touch": false, + "TouchEvent": false, + "TouchList": false, + "TrackEvent": false, + "TransitionEvent": false, + "TreeWalker": false, + "UIEvent": false, + "URL": false, + "URLSearchParams": false, + "ValidityState": false, + "visualViewport": false, + "VisualViewport": false, + "VTTCue": false, + "WaveShaperNode": false, + "WebAssembly": false, + "WebGL2RenderingContext": false, + "WebGLActiveInfo": false, + "WebGLBuffer": false, + "WebGLContextEvent": false, + "WebGLFramebuffer": false, + "WebGLProgram": false, + "WebGLQuery": false, + "WebGLRenderbuffer": false, + "WebGLRenderingContext": false, + "WebGLSampler": false, + "WebGLShader": false, + "WebGLShaderPrecisionFormat": false, + "WebGLSync": false, + "WebGLTexture": false, + "WebGLTransformFeedback": false, + "WebGLUniformLocation": false, + "WebGLVertexArrayObject": false, + "WebSocket": false, + "WheelEvent": false, + "window": false, + "Window": false, + "Worker": false, + "WritableStream": false, + "XMLDocument": false, + "XMLHttpRequest": false, + "XMLHttpRequestEventTarget": false, + "XMLHttpRequestUpload": false, + "XMLSerializer": false, + "XPathEvaluator": false, + "XPathExpression": false, + "XPathResult": false, + "XSLTProcessor": false + }, + "worker": { + "addEventListener": false, + "applicationCache": false, + "atob": false, + "Blob": false, + "BroadcastChannel": false, + "btoa": false, + "Cache": false, + "caches": false, + "clearInterval": false, + "clearTimeout": false, + "close": true, + "console": false, + "fetch": false, + "FileReaderSync": false, + "FormData": false, + "Headers": false, + "IDBCursor": false, + "IDBCursorWithValue": false, + "IDBDatabase": false, + "IDBFactory": false, + "IDBIndex": false, + "IDBKeyRange": false, + "IDBObjectStore": false, + "IDBOpenDBRequest": false, + "IDBRequest": false, + "IDBTransaction": false, + "IDBVersionChangeEvent": false, + "ImageData": false, + "importScripts": true, + "indexedDB": false, + "location": false, + "MessageChannel": false, + "MessagePort": false, + "name": false, + "navigator": false, + "Notification": false, + "onclose": true, + "onconnect": true, + "onerror": true, + "onlanguagechange": true, + "onmessage": true, + "onoffline": true, + "ononline": true, + "onrejectionhandled": true, + "onunhandledrejection": true, + "performance": false, + "Performance": false, + "PerformanceEntry": false, + "PerformanceMark": false, + "PerformanceMeasure": false, + "PerformanceNavigation": false, + "PerformanceResourceTiming": false, + "PerformanceTiming": false, + "postMessage": true, + "Promise": false, + "queueMicrotask": false, + "removeEventListener": false, + "Request": false, + "Response": false, + "self": true, + "ServiceWorkerRegistration": false, + "setInterval": false, + "setTimeout": false, + "TextDecoder": false, + "TextEncoder": false, + "URL": false, + "URLSearchParams": false, + "WebSocket": false, + "Worker": false, + "WorkerGlobalScope": false, + "XMLHttpRequest": false + }, + "node": { + "__dirname": false, + "__filename": false, + "Buffer": false, + "clearImmediate": false, + "clearInterval": false, + "clearTimeout": false, + "console": false, + "exports": true, + "global": false, + "Intl": false, + "module": false, + "process": false, + "queueMicrotask": false, + "require": false, + "setImmediate": false, + "setInterval": false, + "setTimeout": false, + "TextDecoder": false, + "TextEncoder": false, + "URL": false, + "URLSearchParams": false + }, + "nodeBuiltin": { + "Buffer": false, + "clearImmediate": false, + "clearInterval": false, + "clearTimeout": false, + "console": false, + "global": false, + "Intl": false, + "process": false, + "queueMicrotask": false, + "setImmediate": false, + "setInterval": false, + "setTimeout": false, + "TextDecoder": false, + "TextEncoder": false, + "URL": false, + "URLSearchParams": false + }, + "commonjs": { + "exports": true, + "global": false, + "module": false, + "require": false + }, + "amd": { + "define": false, + "require": false + }, + "mocha": { + "after": false, + "afterEach": false, + "before": false, + "beforeEach": false, + "context": false, + "describe": false, + "it": false, + "mocha": false, + "run": false, + "setup": false, + "specify": false, + "suite": false, + "suiteSetup": false, + "suiteTeardown": false, + "teardown": false, + "test": false, + "xcontext": false, + "xdescribe": false, + "xit": false, + "xspecify": false + }, + "jasmine": { + "afterAll": false, + "afterEach": false, + "beforeAll": false, + "beforeEach": false, + "describe": false, + "expect": false, + "expectAsync": false, + "fail": false, + "fdescribe": false, + "fit": false, + "it": false, + "jasmine": false, + "pending": false, + "runs": false, + "spyOn": false, + "spyOnAllFunctions": false, + "spyOnProperty": false, + "waits": false, + "waitsFor": false, + "xdescribe": false, + "xit": false + }, + "jest": { + "afterAll": false, + "afterEach": false, + "beforeAll": false, + "beforeEach": false, + "describe": false, + "expect": false, + "fdescribe": false, + "fit": false, + "it": false, + "jest": false, + "pit": false, + "require": false, + "test": false, + "xdescribe": false, + "xit": false, + "xtest": false + }, + "qunit": { + "asyncTest": false, + "deepEqual": false, + "equal": false, + "expect": false, + "module": false, + "notDeepEqual": false, + "notEqual": false, + "notOk": false, + "notPropEqual": false, + "notStrictEqual": false, + "ok": false, + "propEqual": false, + "QUnit": false, + "raises": false, + "start": false, + "stop": false, + "strictEqual": false, + "test": false, + "throws": false + }, + "phantomjs": { + "console": true, + "exports": true, + "phantom": true, + "require": true, + "WebPage": true + }, + "couch": { + "emit": false, + "exports": false, + "getRow": false, + "log": false, + "module": false, + "provides": false, + "require": false, + "respond": false, + "send": false, + "start": false, + "sum": false + }, + "rhino": { + "defineClass": false, + "deserialize": false, + "gc": false, + "help": false, + "importClass": false, + "importPackage": false, + "java": false, + "load": false, + "loadClass": false, + "Packages": false, + "print": false, + "quit": false, + "readFile": false, + "readUrl": false, + "runCommand": false, + "seal": false, + "serialize": false, + "spawn": false, + "sync": false, + "toint32": false, + "version": false + }, + "nashorn": { + "__DIR__": false, + "__FILE__": false, + "__LINE__": false, + "com": false, + "edu": false, + "exit": false, + "java": false, + "Java": false, + "javafx": false, + "JavaImporter": false, + "javax": false, + "JSAdapter": false, + "load": false, + "loadWithNewGlobal": false, + "org": false, + "Packages": false, + "print": false, + "quit": false + }, + "wsh": { + "ActiveXObject": true, + "CollectGarbage": true, + "Debug": true, + "Enumerator": true, + "GetObject": true, + "RuntimeObject": true, + "ScriptEngine": true, + "ScriptEngineBuildVersion": true, + "ScriptEngineMajorVersion": true, + "ScriptEngineMinorVersion": true, + "VBArray": true, + "WScript": true, + "WSH": true, + "XDomainRequest": true + }, + "jquery": { + "$": false, + "jQuery": false + }, + "yui": { + "YAHOO": false, + "YAHOO_config": false, + "YUI": false, + "YUI_config": false + }, + "shelljs": { + "cat": false, + "cd": false, + "chmod": false, + "config": false, + "cp": false, + "dirs": false, + "echo": false, + "env": false, + "error": false, + "exec": false, + "exit": false, + "find": false, + "grep": false, + "ln": false, + "ls": false, + "mkdir": false, + "mv": false, + "popd": false, + "pushd": false, + "pwd": false, + "rm": false, + "sed": false, + "set": false, + "target": false, + "tempdir": false, + "test": false, + "touch": false, + "which": false + }, + "prototypejs": { + "$": false, + "$$": false, + "$A": false, + "$break": false, + "$continue": false, + "$F": false, + "$H": false, + "$R": false, + "$w": false, + "Abstract": false, + "Ajax": false, + "Autocompleter": false, + "Builder": false, + "Class": false, + "Control": false, + "Draggable": false, + "Draggables": false, + "Droppables": false, + "Effect": false, + "Element": false, + "Enumerable": false, + "Event": false, + "Field": false, + "Form": false, + "Hash": false, + "Insertion": false, + "ObjectRange": false, + "PeriodicalExecuter": false, + "Position": false, + "Prototype": false, + "Scriptaculous": false, + "Selector": false, + "Sortable": false, + "SortableObserver": false, + "Sound": false, + "Template": false, + "Toggle": false, + "Try": false + }, + "meteor": { + "_": false, + "$": false, + "Accounts": false, + "AccountsClient": false, + "AccountsCommon": false, + "AccountsServer": false, + "App": false, + "Assets": false, + "Blaze": false, + "check": false, + "Cordova": false, + "DDP": false, + "DDPRateLimiter": false, + "DDPServer": false, + "Deps": false, + "EJSON": false, + "Email": false, + "HTTP": false, + "Log": false, + "Match": false, + "Meteor": false, + "Mongo": false, + "MongoInternals": false, + "Npm": false, + "Package": false, + "Plugin": false, + "process": false, + "Random": false, + "ReactiveDict": false, + "ReactiveVar": false, + "Router": false, + "ServiceConfiguration": false, + "Session": false, + "share": false, + "Spacebars": false, + "Template": false, + "Tinytest": false, + "Tracker": false, + "UI": false, + "Utils": false, + "WebApp": false, + "WebAppInternals": false + }, + "mongo": { + "_isWindows": false, + "_rand": false, + "BulkWriteResult": false, + "cat": false, + "cd": false, + "connect": false, + "db": false, + "getHostName": false, + "getMemInfo": false, + "hostname": false, + "ISODate": false, + "listFiles": false, + "load": false, + "ls": false, + "md5sumFile": false, + "mkdir": false, + "Mongo": false, + "NumberInt": false, + "NumberLong": false, + "ObjectId": false, + "PlanCache": false, + "print": false, + "printjson": false, + "pwd": false, + "quit": false, + "removeFile": false, + "rs": false, + "sh": false, + "UUID": false, + "version": false, + "WriteResult": false + }, + "applescript": { + "$": false, + "Application": false, + "Automation": false, + "console": false, + "delay": false, + "Library": false, + "ObjC": false, + "ObjectSpecifier": false, + "Path": false, + "Progress": false, + "Ref": false + }, + "serviceworker": { + "addEventListener": false, + "applicationCache": false, + "atob": false, + "Blob": false, + "BroadcastChannel": false, + "btoa": false, + "Cache": false, + "caches": false, + "CacheStorage": false, + "clearInterval": false, + "clearTimeout": false, + "Client": false, + "clients": false, + "Clients": false, + "close": true, + "console": false, + "ExtendableEvent": false, + "ExtendableMessageEvent": false, + "fetch": false, + "FetchEvent": false, + "FileReaderSync": false, + "FormData": false, + "Headers": false, + "IDBCursor": false, + "IDBCursorWithValue": false, + "IDBDatabase": false, + "IDBFactory": false, + "IDBIndex": false, + "IDBKeyRange": false, + "IDBObjectStore": false, + "IDBOpenDBRequest": false, + "IDBRequest": false, + "IDBTransaction": false, + "IDBVersionChangeEvent": false, + "ImageData": false, + "importScripts": false, + "indexedDB": false, + "location": false, + "MessageChannel": false, + "MessagePort": false, + "name": false, + "navigator": false, + "Notification": false, + "onclose": true, + "onconnect": true, + "onerror": true, + "onfetch": true, + "oninstall": true, + "onlanguagechange": true, + "onmessage": true, + "onmessageerror": true, + "onnotificationclick": true, + "onnotificationclose": true, + "onoffline": true, + "ononline": true, + "onpush": true, + "onpushsubscriptionchange": true, + "onrejectionhandled": true, + "onsync": true, + "onunhandledrejection": true, + "performance": false, + "Performance": false, + "PerformanceEntry": false, + "PerformanceMark": false, + "PerformanceMeasure": false, + "PerformanceNavigation": false, + "PerformanceResourceTiming": false, + "PerformanceTiming": false, + "postMessage": true, + "Promise": false, + "queueMicrotask": false, + "registration": false, + "removeEventListener": false, + "Request": false, + "Response": false, + "self": false, + "ServiceWorker": false, + "ServiceWorkerContainer": false, + "ServiceWorkerGlobalScope": false, + "ServiceWorkerMessageEvent": false, + "ServiceWorkerRegistration": false, + "setInterval": false, + "setTimeout": false, + "skipWaiting": false, + "TextDecoder": false, + "TextEncoder": false, + "URL": false, + "URLSearchParams": false, + "WebSocket": false, + "WindowClient": false, + "Worker": false, + "WorkerGlobalScope": false, + "XMLHttpRequest": false + }, + "atomtest": { + "advanceClock": false, + "fakeClearInterval": false, + "fakeClearTimeout": false, + "fakeSetInterval": false, + "fakeSetTimeout": false, + "resetTimeouts": false, + "waitsForPromise": false + }, + "embertest": { + "andThen": false, + "click": false, + "currentPath": false, + "currentRouteName": false, + "currentURL": false, + "fillIn": false, + "find": false, + "findAll": false, + "findWithAssert": false, + "keyEvent": false, + "pauseTest": false, + "resumeTest": false, + "triggerEvent": false, + "visit": false, + "wait": false + }, + "protractor": { + "$": false, + "$$": false, + "browser": false, + "by": false, + "By": false, + "DartObject": false, + "element": false, + "protractor": false + }, + "shared-node-browser": { + "clearInterval": false, + "clearTimeout": false, + "console": false, + "setInterval": false, + "setTimeout": false, + "URL": false, + "URLSearchParams": false + }, + "webextensions": { + "browser": false, + "chrome": false, + "opr": false + }, + "greasemonkey": { + "cloneInto": false, + "createObjectIn": false, + "exportFunction": false, + "GM": false, + "GM_addStyle": false, + "GM_deleteValue": false, + "GM_getResourceText": false, + "GM_getResourceURL": false, + "GM_getValue": false, + "GM_info": false, + "GM_listValues": false, + "GM_log": false, + "GM_openInTab": false, + "GM_registerMenuCommand": false, + "GM_setClipboard": false, + "GM_setValue": false, + "GM_xmlhttpRequest": false, + "unsafeWindow": false + }, + "devtools": { + "$": false, + "$_": false, + "$$": false, + "$0": false, + "$1": false, + "$2": false, + "$3": false, + "$4": false, + "$x": false, + "chrome": false, + "clear": false, + "copy": false, + "debug": false, + "dir": false, + "dirxml": false, + "getEventListeners": false, + "inspect": false, + "keys": false, + "monitor": false, + "monitorEvents": false, + "profile": false, + "profileEnd": false, + "queryObjects": false, + "table": false, + "undebug": false, + "unmonitor": false, + "unmonitorEvents": false, + "values": false + } +} diff --git a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/index.js b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/index.js new file mode 100644 index 00000000000000..a951582e4176e8 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/index.js @@ -0,0 +1,2 @@ +'use strict'; +module.exports = require('./globals.json'); diff --git a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/license b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/license new file mode 100644 index 00000000000000..fa7ceba3eb4a96 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com) + +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. diff --git a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/package.json b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/package.json new file mode 100644 index 00000000000000..66435a6834e8f4 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/package.json @@ -0,0 +1,61 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/globals/issues" + }, + "bundleDependencies": false, + "dependencies": { + "type-fest": "^0.8.1" + }, + "deprecated": false, + "description": "Global identifiers from different JavaScript environments", + "devDependencies": { + "ava": "^2.2.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts", + "globals.json" + ], + "funding": "https://github.com/sponsors/sindresorhus", + "homepage": "https://github.com/sindresorhus/globals#readme", + "keywords": [ + "globals", + "global", + "identifiers", + "variables", + "vars", + "jshint", + "eslint", + "environments" + ], + "license": "MIT", + "name": "globals", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/globals.git" + }, + "scripts": { + "test": "xo && ava" + }, + "tsd": { + "compilerOptions": { + "resolveJsonModule": true + } + }, + "version": "12.4.0", + "xo": { + "ignores": [ + "get-browser-globals.js" + ] + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/readme.md b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/readme.md new file mode 100644 index 00000000000000..fdcfa087ab1107 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/node_modules/globals/readme.md @@ -0,0 +1,56 @@ +# globals [](https://travis-ci.org/sindresorhus/globals) + +> Global identifiers from different JavaScript environments + +Extracted from [JSHint](https://github.com/jshint/jshint/blob/3a8efa979dbb157bfb5c10b5826603a55a33b9ad/src/vars.js) and [ESLint](https://github.com/eslint/eslint/blob/b648406218f8a2d7302b98f5565e23199f44eb31/conf/environments.json) and merged. + +It's just a [JSON file](globals.json), so use it in whatever environment you like. + +**This module [no longer accepts](https://github.com/sindresorhus/globals/issues/82) new environments. If you need it for ESLint, just [create a plugin](http://eslint.org/docs/developer-guide/working-with-plugins#environments-in-plugins).** + +## Install + +``` +$ npm install globals +``` + +## Usage + +```js +const globals = require('globals'); + +console.log(globals.browser); +/* +{ + addEventListener: false, + applicationCache: false, + ArrayBuffer: false, + atob: false, + … +} +*/ +``` + +Each global is given a value of `true` or `false`. A value of `true` indicates that the variable may be overwritten. A value of `false` indicates that the variable should be considered read-only. This information is used by static analysis tools to flag incorrect behavior. We assume all variables should be `false` unless we hear otherwise. + +For Node.js this package provides two sets of globals: + +- `globals.nodeBuiltin`: Globals available to all code running in Node.js. + These will usually be available as properties on the `global` object and include `process`, `Buffer`, but not CommonJS arguments like `require`. + See: https://nodejs.org/api/globals.html +- `globals.node`: A combination of the globals from `nodeBuiltin` plus all CommonJS arguments ("CommonJS module scope"). + See: https://nodejs.org/api/modules.html#modules_the_module_scope + +When analyzing code that is known to run outside of a CommonJS wrapper, for example, JavaScript modules, `nodeBuiltin` can find accidental CommonJS references. + +--- + +<div align="center"> + <b> + <a href="https://tidelift.com/subscription/pkg/npm-globals?utm_source=npm-globals&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> + </b> + <br> + <sub> + Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. + </sub> +</div> diff --git a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/package.json b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/package.json index d4dbb4b71342c2..9a730ea76f3c2c 100644 --- a/tools/node_modules/eslint/node_modules/@eslint/eslintrc/package.json +++ b/tools/node_modules/eslint/node_modules/@eslint/eslintrc/package.json @@ -1,37 +1,24 @@ { - "name": "@eslint/eslintrc", - "version": "0.4.0", - "description": "The legacy ESLintRC config file format for ESLint", - "main": "lib/index.js", - "files": [ - "lib", - "conf", - "LICENSE" - ], - "publishConfig": { - "access": "public" + "author": { + "name": "Nicholas C. Zakas" }, - "scripts": { - "lint": "eslint .", - "test": "mocha -R progress -c 'tests/lib/**/*.js'", - "generate-release": "eslint-generate-release", - "generate-alpharelease": "eslint-generate-prerelease alpha", - "generate-betarelease": "eslint-generate-prerelease beta", - "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" - }, - "repository": "eslint/eslintrc", - "keywords": [ - "ESLint", - "ESLintRC", - "Configuration" - ], - "author": "Nicholas C. Zakas", - "license": "MIT", "bugs": { "url": "https://github.com/eslint/eslintrc/issues" }, - "homepage": "https://github.com/eslint/eslintrc#readme", + "bundleDependencies": false, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "deprecated": false, + "description": "The legacy ESLintRC config file format for ESLint", "devDependencies": { "chai": "^4.2.0", "eslint": "^7.7.0", @@ -45,18 +32,38 @@ "sinon": "^9.2.0", "temp-dir": "^2.0.0" }, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, "engines": { "node": "^10.12.0 || >=12.0.0" - } -} + }, + "files": [ + "lib", + "conf", + "LICENSE" + ], + "homepage": "https://github.com/eslint/eslintrc#readme", + "keywords": [ + "ESLint", + "ESLintRC", + "Configuration" + ], + "license": "MIT", + "main": "lib/index.js", + "name": "@eslint/eslintrc", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslintrc.git" + }, + "scripts": { + "generate-alpharelease": "eslint-generate-prerelease alpha", + "generate-betarelease": "eslint-generate-prerelease beta", + "generate-rcrelease": "eslint-generate-prerelease rc", + "generate-release": "eslint-generate-release", + "lint": "eslint .", + "publish-release": "eslint-publish-release", + "test": "mocha -R progress -c 'tests/lib/**/*.js'" + }, + "version": "0.4.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/acorn-jsx/package.json b/tools/node_modules/eslint/node_modules/acorn-jsx/package.json index f42a7ea1437c8c..e26bc863624eb8 100644 --- a/tools/node_modules/eslint/node_modules/acorn-jsx/package.json +++ b/tools/node_modules/eslint/node_modules/acorn-jsx/package.json @@ -1,27 +1,32 @@ { - "name": "acorn-jsx", + "bugs": { + "url": "https://github.com/acornjs/acorn-jsx/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "Modern, fast React.js JSX parser", + "devDependencies": { + "acorn": "^8.0.1" + }, "homepage": "https://github.com/acornjs/acorn-jsx", - "version": "5.3.1", + "license": "MIT", "maintainers": [ { "name": "Ingvar Stepanyan", "email": "me@rreverser.com", - "web": "http://rreverser.com/" + "url": "http://rreverser.com/" } ], + "name": "acorn-jsx", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, "repository": { "type": "git", - "url": "https://github.com/acornjs/acorn-jsx" + "url": "git+https://github.com/acornjs/acorn-jsx.git" }, - "license": "MIT", "scripts": { "test": "node test/run.js" }, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "devDependencies": { - "acorn": "^8.0.1" - } -} + "version": "5.3.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/acorn/package.json b/tools/node_modules/eslint/node_modules/acorn/package.json index 10699306d369f8..a0944f1372f605 100644 --- a/tools/node_modules/eslint/node_modules/acorn/package.json +++ b/tools/node_modules/eslint/node_modules/acorn/package.json @@ -1,35 +1,44 @@ { - "name": "acorn", + "bin": { + "acorn": "bin/acorn" + }, + "bugs": { + "url": "https://github.com/acornjs/acorn/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "ECMAScript parser", + "engines": { + "node": ">=0.4.0" + }, "homepage": "https://github.com/acornjs/acorn", + "license": "MIT", "main": "dist/acorn.js", - "types": "dist/acorn.d.ts", - "module": "dist/acorn.mjs", - "version": "7.4.1", - "engines": {"node": ">=0.4.0"}, "maintainers": [ { "name": "Marijn Haverbeke", "email": "marijnh@gmail.com", - "web": "https://marijnhaverbeke.nl" + "url": "https://marijnhaverbeke.nl" }, { "name": "Ingvar Stepanyan", "email": "me@rreverser.com", - "web": "https://rreverser.com/" + "url": "https://rreverser.com/" }, { "name": "Adrian Heine", - "web": "http://adrianheine.de" + "url": "http://adrianheine.de" } ], + "module": "dist/acorn.mjs", + "name": "acorn", "repository": { "type": "git", - "url": "https://github.com/acornjs/acorn.git" + "url": "git+https://github.com/acornjs/acorn.git" }, - "license": "MIT", "scripts": { "prepare": "cd ..; npm run build:main && npm run build:bin" }, - "bin": {"acorn": "./bin/acorn"} -} + "types": "dist/acorn.d.ts", + "version": "7.4.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ajv/package.json b/tools/node_modules/eslint/node_modules/ajv/package.json index 559a933c8c1c03..6657aaf6c4ab24 100644 --- a/tools/node_modules/eslint/node_modules/ajv/package.json +++ b/tools/node_modules/eslint/node_modules/ajv/package.json @@ -1,72 +1,23 @@ { - "name": "ajv", - "version": "6.12.6", - "description": "Another JSON Schema Validator", - "main": "lib/ajv.js", - "typings": "lib/ajv.d.ts", - "files": [ - "lib/", - "dist/", - "scripts/", - "LICENSE", - ".tonic_example.js" - ], - "scripts": { - "eslint": "eslint lib/{compile/,}*.js spec/{**/,}*.js scripts --ignore-pattern spec/JSON-Schema-Test-Suite", - "jshint": "jshint lib/{compile/,}*.js", - "lint": "npm run jshint && npm run eslint", - "test-spec": "mocha spec/{**/,}*.spec.js -R spec", - "test-fast": "AJV_FAST_TEST=true npm run test-spec", - "test-debug": "npm run test-spec -- --inspect-brk", - "test-cov": "nyc npm run test-spec", - "test-ts": "tsc --target ES5 --noImplicitAny --noEmit spec/typescript/index.ts", - "bundle": "del-cli dist && node ./scripts/bundle.js . Ajv pure_getters", - "bundle-beautify": "node ./scripts/bundle.js js-beautify", - "build": "del-cli lib/dotjs/*.js \"!lib/dotjs/index.js\" && node scripts/compile-dots.js", - "test-karma": "karma start", - "test-browser": "del-cli .browser && npm run bundle && scripts/prepare-tests && npm run test-karma", - "test-all": "npm run test-cov && if-node-version 10 npm run test-browser", - "test": "npm run lint && npm run build && npm run test-all", - "prepublish": "npm run build && npm run bundle", - "watch": "watch \"npm run build\" ./lib/dot" - }, - "nyc": { - "exclude": [ - "**/spec/**", - "node_modules" - ], - "reporter": [ - "lcov", - "text-summary" - ] - }, - "repository": { - "type": "git", - "url": "https://github.com/ajv-validator/ajv.git" + "author": { + "name": "Evgeny Poberezkin" }, - "keywords": [ - "JSON", - "schema", - "validator", - "validation", - "jsonschema", - "json-schema", - "json-schema-validator", - "json-schema-validation" - ], - "author": "Evgeny Poberezkin", - "license": "MIT", "bugs": { "url": "https://github.com/ajv-validator/ajv/issues" }, - "homepage": "https://github.com/ajv-validator/ajv", - "tonicExampleFilename": ".tonic_example.js", + "bundleDependencies": false, + "collective": { + "type": "opencollective", + "url": "https://opencollective.com/ajv" + }, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, + "deprecated": false, + "description": "Another JSON Schema Validator", "devDependencies": { "ajv-async": "^1.0.0", "bluebird": "^3.5.3", @@ -95,12 +46,65 @@ "uglify-js": "^3.6.9", "watch": "^1.0.0" }, - "collective": { - "type": "opencollective", - "url": "https://opencollective.com/ajv" - }, + "files": [ + "lib/", + "dist/", + "scripts/", + "LICENSE", + ".tonic_example.js" + ], "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" - } -} + }, + "homepage": "https://github.com/ajv-validator/ajv", + "keywords": [ + "JSON", + "schema", + "validator", + "validation", + "jsonschema", + "json-schema", + "json-schema-validator", + "json-schema-validation" + ], + "license": "MIT", + "main": "lib/ajv.js", + "name": "ajv", + "nyc": { + "exclude": [ + "**/spec/**", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "repository": { + "type": "git", + "url": "git+https://github.com/ajv-validator/ajv.git" + }, + "scripts": { + "build": "del-cli lib/dotjs/*.js \"!lib/dotjs/index.js\" && node scripts/compile-dots.js", + "bundle": "del-cli dist && node ./scripts/bundle.js . Ajv pure_getters", + "bundle-beautify": "node ./scripts/bundle.js js-beautify", + "eslint": "eslint lib/{compile/,}*.js spec/{**/,}*.js scripts --ignore-pattern spec/JSON-Schema-Test-Suite", + "jshint": "jshint lib/{compile/,}*.js", + "lint": "npm run jshint && npm run eslint", + "prepublish": "npm run build && npm run bundle", + "test": "npm run lint && npm run build && npm run test-all", + "test-all": "npm run test-cov && if-node-version 10 npm run test-browser", + "test-browser": "del-cli .browser && npm run bundle && scripts/prepare-tests && npm run test-karma", + "test-cov": "nyc npm run test-spec", + "test-debug": "npm run test-spec -- --inspect-brk", + "test-fast": "AJV_FAST_TEST=true npm run test-spec", + "test-karma": "karma start", + "test-spec": "mocha spec/{**/,}*.spec.js -R spec", + "test-ts": "tsc --target ES5 --noImplicitAny --noEmit spec/typescript/index.ts", + "watch": "watch \"npm run build\" ./lib/dot" + }, + "tonicExampleFilename": ".tonic_example.js", + "typings": "lib/ajv.d.ts", + "version": "6.12.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-colors/package.json b/tools/node_modules/eslint/node_modules/ansi-colors/package.json index e11093140b3277..8c23184218ac6b 100644 --- a/tools/node_modules/eslint/node_modules/ansi-colors/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-colors/package.json @@ -1,33 +1,32 @@ { - "name": "ansi-colors", - "description": "Easily add ANSI colors to your text and symbols in the terminal. A faster drop-in replacement for chalk, kleur and turbocolor (without the dependencies and rendering bugs).", - "version": "4.1.1", - "homepage": "https://github.com/doowb/ansi-colors", - "author": "Brian Woodward (https://github.com/doowb)", - "contributors": [ - "Brian Woodward (https://twitter.com/doowb)", - "Jason Schilling (https://sourecode.de)", - "Jon Schlinkert (http://twitter.com/jonschlinkert)", - "Jordan (https://github.com/Silic0nS0ldier)" - ], - "repository": "doowb/ansi-colors", + "author": { + "name": "Brian Woodward", + "url": "https://github.com/doowb" + }, "bugs": { "url": "https://github.com/doowb/ansi-colors/issues" }, - "license": "MIT", - "files": [ - "index.js", - "symbols.js", - "types/index.d.ts" + "bundleDependencies": false, + "contributors": [ + { + "name": "Brian Woodward", + "url": "https://twitter.com/doowb" + }, + { + "name": "Jason Schilling", + "url": "https://sourecode.de" + }, + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Jordan", + "url": "https://github.com/Silic0nS0ldier" + } ], - "main": "index.js", - "types": "./types/index.d.ts", - "engines": { - "node": ">=6" - }, - "scripts": { - "test": "mocha" - }, + "deprecated": false, + "description": "Easily add ANSI colors to your text and symbols in the terminal. A faster drop-in replacement for chalk, kleur and turbocolor (without the dependencies and rendering bugs).", "devDependencies": { "decache": "^4.5.1", "gulp-format-md": "^2.0.0", @@ -35,6 +34,15 @@ "mocha": "^6.1.4", "text-table": "^0.2.0" }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "symbols.js", + "types/index.d.ts" + ], + "homepage": "https://github.com/doowb/ansi-colors", "keywords": [ "ansi", "bgblack", @@ -75,6 +83,17 @@ "white", "yellow" ], + "license": "MIT", + "main": "index.js", + "name": "ansi-colors", + "repository": { + "type": "git", + "url": "git+https://github.com/doowb/ansi-colors.git" + }, + "scripts": { + "test": "mocha" + }, + "types": "./types/index.d.ts", "verb": { "toc": false, "layout": "default", @@ -105,5 +124,6 @@ "colors", "kleur" ] - } -} + }, + "version": "4.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-regex/package.json b/tools/node_modules/eslint/node_modules/ansi-regex/package.json index 7af801f3522206..d0574afde48599 100644 --- a/tools/node_modules/eslint/node_modules/ansi-regex/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-regex/package.json @@ -1,55 +1,64 @@ { - "name": "ansi-regex", - "version": "5.0.0", - "description": "Regular expression for matching ANSI escape codes", - "license": "MIT", - "repository": "chalk/ansi-regex", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd", - "view-supported": "node fixtures/view-codes.js" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "ansi", - "styles", - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "string", - "tty", - "escape", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "command-line", - "text", - "regex", - "regexp", - "re", - "match", - "test", - "find", - "pattern" - ], - "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.9.0", - "xo": "^0.25.3" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Regular expression for matching ANSI escape codes", + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.9.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/chalk/ansi-regex#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "text", + "regex", + "regexp", + "re", + "match", + "test", + "find", + "pattern" + ], + "license": "MIT", + "name": "ansi-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-regex.git" + }, + "scripts": { + "test": "xo && ava && tsd", + "view-supported": "node fixtures/view-codes.js" + }, + "version": "5.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-styles/index.js b/tools/node_modules/eslint/node_modules/ansi-styles/index.js index 5d82581a13f990..90a871c4d78f6f 100644 --- a/tools/node_modules/eslint/node_modules/ansi-styles/index.js +++ b/tools/node_modules/eslint/node_modules/ansi-styles/index.js @@ -1,63 +1,21 @@ 'use strict'; +const colorConvert = require('color-convert'); -const wrapAnsi16 = (fn, offset) => (...args) => { - const code = fn(...args); +const wrapAnsi16 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); return `\u001B[${code + offset}m`; }; -const wrapAnsi256 = (fn, offset) => (...args) => { - const code = fn(...args); +const wrapAnsi256 = (fn, offset) => function () { + const code = fn.apply(colorConvert, arguments); return `\u001B[${38 + offset};5;${code}m`; }; -const wrapAnsi16m = (fn, offset) => (...args) => { - const rgb = fn(...args); +const wrapAnsi16m = (fn, offset) => function () { + const rgb = fn.apply(colorConvert, arguments); return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; }; -const ansi2ansi = n => n; -const rgb2rgb = (r, g, b) => [r, g, b]; - -const setLazyProperty = (object, property, get) => { - Object.defineProperty(object, property, { - get: () => { - const value = get(); - - Object.defineProperty(object, property, { - value, - enumerable: true, - configurable: true - }); - - return value; - }, - enumerable: true, - configurable: true - }); -}; - -/** @type {typeof import('color-convert')} */ -let colorConvert; -const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { - if (colorConvert === undefined) { - colorConvert = require('color-convert'); - } - - const offset = isBackground ? 10 : 0; - const styles = {}; - - for (const [sourceSpace, suite] of Object.entries(colorConvert)) { - const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; - if (sourceSpace === targetSpace) { - styles[name] = wrap(identity, offset); - } else if (typeof suite === 'object') { - styles[name] = wrap(suite[targetSpace], offset); - } - } - - return styles; -}; - function assembleStyles() { const codes = new Map(); const styles = { @@ -81,9 +39,9 @@ function assembleStyles() { magenta: [35, 39], cyan: [36, 39], white: [37, 39], + gray: [90, 39], // Bright color - blackBright: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], @@ -114,14 +72,15 @@ function assembleStyles() { } }; - // Alias bright black as gray (and grey) - styles.color.gray = styles.color.blackBright; - styles.bgColor.bgGray = styles.bgColor.bgBlackBright; - styles.color.grey = styles.color.blackBright; - styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + // Fix humans + styles.color.grey = styles.color.gray; + + for (const groupName of Object.keys(styles)) { + const group = styles[groupName]; + + for (const styleName of Object.keys(group)) { + const style = group[styleName]; - for (const [groupName, group] of Object.entries(styles)) { - for (const [styleName, style] of Object.entries(group)) { styles[styleName] = { open: `\u001B[${style[0]}m`, close: `\u001B[${style[1]}m` @@ -136,22 +95,65 @@ function assembleStyles() { value: group, enumerable: false }); + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); } - Object.defineProperty(styles, 'codes', { - value: codes, - enumerable: false - }); + const ansi2ansi = n => n; + const rgb2rgb = (r, g, b) => [r, g, b]; styles.color.close = '\u001B[39m'; styles.bgColor.close = '\u001B[49m'; - setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); - setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); - setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); - setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); - setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); - setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); + styles.color.ansi = { + ansi: wrapAnsi16(ansi2ansi, 0) + }; + styles.color.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 0) + }; + styles.color.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 0) + }; + + styles.bgColor.ansi = { + ansi: wrapAnsi16(ansi2ansi, 10) + }; + styles.bgColor.ansi256 = { + ansi256: wrapAnsi256(ansi2ansi, 10) + }; + styles.bgColor.ansi16m = { + rgb: wrapAnsi16m(rgb2rgb, 10) + }; + + for (let key of Object.keys(colorConvert)) { + if (typeof colorConvert[key] !== 'object') { + continue; + } + + const suite = colorConvert[key]; + + if (key === 'ansi16') { + key = 'ansi'; + } + + if ('ansi16' in suite) { + styles.color.ansi[key] = wrapAnsi16(suite.ansi16, 0); + styles.bgColor.ansi[key] = wrapAnsi16(suite.ansi16, 10); + } + + if ('ansi256' in suite) { + styles.color.ansi256[key] = wrapAnsi256(suite.ansi256, 0); + styles.bgColor.ansi256[key] = wrapAnsi256(suite.ansi256, 10); + } + + if ('rgb' in suite) { + styles.color.ansi16m[key] = wrapAnsi16m(suite.rgb, 0); + styles.bgColor.ansi16m[key] = wrapAnsi16m(suite.rgb, 10); + } + } return styles; } diff --git a/tools/node_modules/eslint/node_modules/ansi-styles/package.json b/tools/node_modules/eslint/node_modules/ansi-styles/package.json index 75393284d7e474..5663ace24b4607 100644 --- a/tools/node_modules/eslint/node_modules/ansi-styles/package.json +++ b/tools/node_modules/eslint/node_modules/ansi-styles/package.json @@ -1,56 +1,65 @@ { - "name": "ansi-styles", - "version": "4.3.0", - "description": "ANSI escape codes for styling strings in the terminal", - "license": "MIT", - "repository": "chalk/ansi-styles", - "funding": "https://github.com/chalk/ansi-styles?sponsor=1", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd", - "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "ansi", - "styles", - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "string", - "tty", - "escape", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "log", - "logging", - "command-line", - "text" - ], - "dependencies": { - "color-convert": "^2.0.1" - }, - "devDependencies": { - "@types/color-convert": "^1.9.0", - "ava": "^2.3.0", - "svg-term-cli": "^2.1.1", - "tsd": "^0.11.0", - "xo": "^0.25.3" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "ava": { + "require": "babel-polyfill" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-styles/issues" + }, + "bundleDependencies": false, + "dependencies": { + "color-convert": "^1.9.0" + }, + "deprecated": false, + "description": "ANSI escape codes for styling strings in the terminal", + "devDependencies": { + "ava": "*", + "babel-polyfill": "^6.23.0", + "svg-term-cli": "^2.1.1", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/chalk/ansi-styles#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "ansi-styles", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-styles.git" + }, + "scripts": { + "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor", + "test": "xo && ava" + }, + "version": "3.2.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ansi-styles/readme.md b/tools/node_modules/eslint/node_modules/ansi-styles/readme.md index 24883de808be6a..3158e2df59ce66 100644 --- a/tools/node_modules/eslint/node_modules/ansi-styles/readme.md +++ b/tools/node_modules/eslint/node_modules/ansi-styles/readme.md @@ -1,10 +1,11 @@ # ansi-styles [](https://travis-ci.org/chalk/ansi-styles) -> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal +> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings. -<img src="screenshot.svg" width="900"> +<img src="https://cdn.rawgit.com/chalk/ansi-styles/8261697c95bf34b6c7767e2cbe9941a851d59385/screenshot.svg" width="900"> + ## Install @@ -12,6 +13,7 @@ You probably want the higher-level [chalk](https://github.com/chalk/chalk) modul $ npm install ansi-styles ``` + ## Usage ```js @@ -27,13 +29,14 @@ console.log(`${style.green.open}Hello world!${style.green.close}`); // original color. console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close); console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close); -console.log(style.color.ansi16m.hex('#abcdef') + 'Hello world!' + style.color.close); +console.log(style.color.ansi16m.hex('#ABCDEF') + 'Hello world!' + style.color.close); ``` ## API Each style has an `open` and `close` property. + ## Styles ### Modifiers @@ -57,7 +60,7 @@ Each style has an `open` and `close` property. - `magenta` - `cyan` - `white` -- `blackBright` (alias: `gray`, `grey`) +- `gray` ("bright black") - `redBright` - `greenBright` - `yellowBright` @@ -76,7 +79,7 @@ Each style has an `open` and `close` property. - `bgMagenta` - `bgCyan` - `bgWhite` -- `bgBlackBright` (alias: `bgGray`, `bgGrey`) +- `bgBlackBright` - `bgRedBright` - `bgGreenBright` - `bgYellowBright` @@ -85,6 +88,7 @@ Each style has an `open` and `close` property. - `bgCyanBright` - `bgWhiteBright` + ## Advanced usage By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module. @@ -108,21 +112,11 @@ console.log(style.codes.get(36)); //=> 39 ``` + ## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728) `ansi-styles` uses the [`color-convert`](https://github.com/Qix-/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors. -The following color spaces from `color-convert` are supported: - -- `rgb` -- `hex` -- `keyword` -- `hsl` -- `hsv` -- `hwb` -- `ansi` -- `ansi256` - To use these, call the associated conversion function with the intended output, for example: ```js @@ -136,17 +130,18 @@ style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code ``` + ## Related - [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal + ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) - [Josh Junon](https://github.com/qix-) -## For enterprise -Available as part of the Tidelift Subscription. +## License -The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) +MIT diff --git a/tools/node_modules/eslint/node_modules/argparse/package.json b/tools/node_modules/eslint/node_modules/argparse/package.json index 62fba0a9fcfc87..12f9cf1395b90e 100644 --- a/tools/node_modules/eslint/node_modules/argparse/package.json +++ b/tools/node_modules/eslint/node_modules/argparse/package.json @@ -1,7 +1,32 @@ { - "name": "argparse", + "bugs": { + "url": "https://github.com/nodeca/argparse/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Eugene Shkuropat" + }, + { + "name": "Paul Jacobson" + } + ], + "dependencies": { + "sprintf-js": "~1.0.2" + }, + "deprecated": false, "description": "Very powerful CLI arguments parser. Native port of argparse - python's options parsing library", - "version": "1.0.10", + "devDependencies": { + "eslint": "^2.13.1", + "istanbul": "^0.4.5", + "mocha": "^3.1.0", + "ndoc": "^5.0.1" + }, + "files": [ + "index.js", + "lib/" + ], + "homepage": "https://github.com/nodeca/argparse#readme", "keywords": [ "cli", "parser", @@ -9,26 +34,14 @@ "option", "args" ], - "contributors": [ - "Eugene Shkuropat", - "Paul Jacobson" - ], - "files": [ - "index.js", - "lib/" - ], "license": "MIT", - "repository": "nodeca/argparse", + "name": "argparse", + "repository": { + "type": "git", + "url": "git+https://github.com/nodeca/argparse.git" + }, "scripts": { "test": "make test" }, - "dependencies": { - "sprintf-js": "~1.0.2" - }, - "devDependencies": { - "eslint": "^2.13.1", - "istanbul": "^0.4.5", - "mocha": "^3.1.0", - "ndoc": "^5.0.1" - } -} + "version": "1.0.10" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/astral-regex/package.json b/tools/node_modules/eslint/node_modules/astral-regex/package.json index d1ceea7f3add66..50ad84da9d6243 100644 --- a/tools/node_modules/eslint/node_modules/astral-regex/package.json +++ b/tools/node_modules/eslint/node_modules/astral-regex/package.json @@ -1,33 +1,42 @@ { - "name": "astral-regex", - "version": "2.0.0", - "description": "Regular expression for matching astral symbols", - "license": "MIT", - "repository": "kevva/astral-regex", - "author": { - "name": "Kevin Mårtensson", - "email": "kevinmartensson@gmail.com", - "url": "github.com/kevva" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "astral", - "emoji", - "regex", - "surrogate" - ], - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} + "author": { + "name": "Kevin Mårtensson", + "email": "kevinmartensson@gmail.com", + "url": "github.com/kevva" + }, + "bugs": { + "url": "https://github.com/kevva/astral-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Regular expression for matching astral symbols", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/kevva/astral-regex#readme", + "keywords": [ + "astral", + "emoji", + "regex", + "surrogate" + ], + "license": "MIT", + "name": "astral-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/kevva/astral-regex.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "2.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/balanced-match/package.json b/tools/node_modules/eslint/node_modules/balanced-match/package.json index 61349c6edad627..354df6cf2f70da 100644 --- a/tools/node_modules/eslint/node_modules/balanced-match/package.json +++ b/tools/node_modules/eslint/node_modules/balanced-match/package.json @@ -1,22 +1,21 @@ { - "name": "balanced-match", - "description": "Match balanced character pairs, like \"{\" and \"}\"", - "version": "1.0.0", - "repository": { - "type": "git", - "url": "git://github.com/juliangruber/balanced-match.git" + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" }, - "homepage": "https://github.com/juliangruber/balanced-match", - "main": "index.js", - "scripts": { - "test": "make test", - "bench": "make bench" + "bugs": { + "url": "https://github.com/juliangruber/balanced-match/issues" }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Match balanced character pairs, like \"{\" and \"}\"", "devDependencies": { "matcha": "^0.7.0", "tape": "^4.6.0" }, + "homepage": "https://github.com/juliangruber/balanced-match", "keywords": [ "match", "regexp", @@ -24,12 +23,17 @@ "balanced", "parse" ], - "author": { - "name": "Julian Gruber", - "email": "mail@juliangruber.com", - "url": "http://juliangruber.com" - }, "license": "MIT", + "main": "index.js", + "name": "balanced-match", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/balanced-match.git" + }, + "scripts": { + "bench": "make bench", + "test": "make test" + }, "testling": { "files": "test/*.js", "browsers": [ @@ -45,5 +49,6 @@ "iphone/6.0..latest", "android-browser/4.2..latest" ] - } -} + }, + "version": "1.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/brace-expansion/package.json b/tools/node_modules/eslint/node_modules/brace-expansion/package.json index a18faa8fd67b82..af3bc67cac86e1 100644 --- a/tools/node_modules/eslint/node_modules/brace-expansion/package.json +++ b/tools/node_modules/eslint/node_modules/brace-expansion/package.json @@ -1,33 +1,37 @@ { - "name": "brace-expansion", - "description": "Brace expansion as known from sh/bash", - "version": "1.1.11", - "repository": { - "type": "git", - "url": "git://github.com/juliangruber/brace-expansion.git" + "author": { + "name": "Julian Gruber", + "email": "mail@juliangruber.com", + "url": "http://juliangruber.com" }, - "homepage": "https://github.com/juliangruber/brace-expansion", - "main": "index.js", - "scripts": { - "test": "tape test/*.js", - "gentest": "bash test/generate.sh", - "bench": "matcha test/perf/bench.js" + "bugs": { + "url": "https://github.com/juliangruber/brace-expansion/issues" }, + "bundleDependencies": false, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" }, + "deprecated": false, + "description": "Brace expansion as known from sh/bash", "devDependencies": { "matcha": "^0.7.0", "tape": "^4.6.0" }, + "homepage": "https://github.com/juliangruber/brace-expansion", "keywords": [], - "author": { - "name": "Julian Gruber", - "email": "mail@juliangruber.com", - "url": "http://juliangruber.com" - }, "license": "MIT", + "main": "index.js", + "name": "brace-expansion", + "repository": { + "type": "git", + "url": "git://github.com/juliangruber/brace-expansion.git" + }, + "scripts": { + "bench": "matcha test/perf/bench.js", + "gentest": "bash test/generate.sh", + "test": "tape test/*.js" + }, "testling": { "files": "test/*.js", "browsers": [ @@ -43,5 +47,6 @@ "iphone/6.0..latest", "android-browser/4.2..latest" ] - } -} + }, + "version": "1.1.11" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/callsites/package.json b/tools/node_modules/eslint/node_modules/callsites/package.json index 93463c34b25dab..b36dc6b150f396 100644 --- a/tools/node_modules/eslint/node_modules/callsites/package.json +++ b/tools/node_modules/eslint/node_modules/callsites/package.json @@ -1,39 +1,48 @@ { - "name": "callsites", - "version": "3.1.0", - "description": "Get callsites from the V8 stack trace API", - "license": "MIT", - "repository": "sindresorhus/callsites", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=6" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "stacktrace", - "v8", - "callsite", - "callsites", - "stack", - "trace", - "function", - "file", - "line", - "debug" - ], - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/callsites/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Get callsites from the V8 stack trace API", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/callsites#readme", + "keywords": [ + "stacktrace", + "v8", + "callsite", + "callsites", + "stack", + "trace", + "function", + "file", + "line", + "debug" + ], + "license": "MIT", + "name": "callsites", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/callsites.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "3.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/index.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/index.js new file mode 100644 index 00000000000000..5d82581a13f990 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/index.js @@ -0,0 +1,163 @@ +'use strict'; + +const wrapAnsi16 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${code + offset}m`; +}; + +const wrapAnsi256 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${38 + offset};5;${code}m`; +}; + +const wrapAnsi16m = (fn, offset) => (...args) => { + const rgb = fn(...args); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; + +const ansi2ansi = n => n; +const rgb2rgb = (r, g, b) => [r, g, b]; + +const setLazyProperty = (object, property, get) => { + Object.defineProperty(object, property, { + get: () => { + const value = get(); + + Object.defineProperty(object, property, { + value, + enumerable: true, + configurable: true + }); + + return value; + }, + enumerable: true, + configurable: true + }); +}; + +/** @type {typeof import('color-convert')} */ +let colorConvert; +const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { + if (colorConvert === undefined) { + colorConvert = require('color-convert'); + } + + const offset = isBackground ? 10 : 0; + const styles = {}; + + for (const [sourceSpace, suite] of Object.entries(colorConvert)) { + const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; + if (sourceSpace === targetSpace) { + styles[name] = wrap(identity, offset); + } else if (typeof suite === 'object') { + styles[name] = wrap(suite[targetSpace], offset); + } + } + + return styles; +}; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; + + // Alias bright black as gray (and grey) + styles.color.gray = styles.color.blackBright; + styles.bgColor.bgGray = styles.bgColor.bgBlackBright; + styles.color.grey = styles.color.blackBright; + styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); + setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); + + return styles; +} + +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/license b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/license similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/license rename to tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/license diff --git a/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/package.json b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/package.json new file mode 100644 index 00000000000000..d276e03d2549c8 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/package.json @@ -0,0 +1,65 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-styles/issues" + }, + "bundleDependencies": false, + "dependencies": { + "color-convert": "^2.0.1" + }, + "deprecated": false, + "description": "ANSI escape codes for styling strings in the terminal", + "devDependencies": { + "@types/color-convert": "^1.9.0", + "ava": "^2.3.0", + "svg-term-cli": "^2.1.1", + "tsd": "^0.11.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "funding": "https://github.com/chalk/ansi-styles?sponsor=1", + "homepage": "https://github.com/chalk/ansi-styles#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "ansi-styles", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-styles.git" + }, + "scripts": { + "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor", + "test": "xo && ava && tsd" + }, + "version": "4.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/readme.md b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/readme.md similarity index 77% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/readme.md rename to tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/readme.md index 3158e2df59ce66..24883de808be6a 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/ansi-styles/readme.md +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/ansi-styles/readme.md @@ -1,11 +1,10 @@ # ansi-styles [](https://travis-ci.org/chalk/ansi-styles) -> [ANSI escape codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal +> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings. -<img src="https://cdn.rawgit.com/chalk/ansi-styles/8261697c95bf34b6c7767e2cbe9941a851d59385/screenshot.svg" width="900"> - +<img src="screenshot.svg" width="900"> ## Install @@ -13,7 +12,6 @@ You probably want the higher-level [chalk](https://github.com/chalk/chalk) modul $ npm install ansi-styles ``` - ## Usage ```js @@ -29,14 +27,13 @@ console.log(`${style.green.open}Hello world!${style.green.close}`); // original color. console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close); console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close); -console.log(style.color.ansi16m.hex('#ABCDEF') + 'Hello world!' + style.color.close); +console.log(style.color.ansi16m.hex('#abcdef') + 'Hello world!' + style.color.close); ``` ## API Each style has an `open` and `close` property. - ## Styles ### Modifiers @@ -60,7 +57,7 @@ Each style has an `open` and `close` property. - `magenta` - `cyan` - `white` -- `gray` ("bright black") +- `blackBright` (alias: `gray`, `grey`) - `redBright` - `greenBright` - `yellowBright` @@ -79,7 +76,7 @@ Each style has an `open` and `close` property. - `bgMagenta` - `bgCyan` - `bgWhite` -- `bgBlackBright` +- `bgBlackBright` (alias: `bgGray`, `bgGrey`) - `bgRedBright` - `bgGreenBright` - `bgYellowBright` @@ -88,7 +85,6 @@ Each style has an `open` and `close` property. - `bgCyanBright` - `bgWhiteBright` - ## Advanced usage By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module. @@ -112,11 +108,21 @@ console.log(style.codes.get(36)); //=> 39 ``` - ## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728) `ansi-styles` uses the [`color-convert`](https://github.com/Qix-/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors. +The following color spaces from `color-convert` are supported: + +- `rgb` +- `hex` +- `keyword` +- `hsl` +- `hsv` +- `hwb` +- `ansi` +- `ansi256` + To use these, call the associated conversion function with the intended output, for example: ```js @@ -130,18 +136,17 @@ style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code ``` - ## Related - [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal - ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) - [Josh Junon](https://github.com/qix-) +## For enterprise -## License +Available as part of the Tidelift Subscription. -MIT +The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/LICENSE b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/LICENSE similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/LICENSE rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/LICENSE diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/README.md b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/README.md similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/README.md rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/README.md diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/conversions.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/conversions.js similarity index 51% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/conversions.js rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/conversions.js index 32172007ec0b17..2657f265c9e102 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/conversions.js +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/conversions.js @@ -1,18 +1,17 @@ /* MIT license */ -var cssKeywords = require('color-name'); +/* eslint-disable no-mixed-operators */ +const cssKeywords = require('color-name'); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). // do not use box values types (i.e. Number(), String(), etc.) -var reverseKeywords = {}; -for (var key in cssKeywords) { - if (cssKeywords.hasOwnProperty(key)) { - reverseKeywords[cssKeywords[key]] = key; - } +const reverseKeywords = {}; +for (const key of Object.keys(cssKeywords)) { + reverseKeywords[cssKeywords[key]] = key; } -var convert = module.exports = { +const convert = { rgb: {channels: 3, labels: 'rgb'}, hsl: {channels: 3, labels: 'hsl'}, hsv: {channels: 3, labels: 'hsv'}, @@ -30,40 +29,38 @@ var convert = module.exports = { gray: {channels: 1, labels: ['gray']} }; -// hide .channels and .labels properties -for (var model in convert) { - if (convert.hasOwnProperty(model)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } +module.exports = convert; - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } +// Hide .channels and .labels properties +for (const model of Object.keys(convert)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); - } + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } - var channels = convert[model].channels; - var labels = convert[model].labels; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); } + + const {channels, labels} = convert[model]; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); } convert.rgb.hsl = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var min = Math.min(r, g, b); - var max = Math.max(r, g, b); - var delta = max - min; - var h; - var s; - var l; + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; if (max === min) { h = 0; @@ -81,7 +78,7 @@ convert.rgb.hsl = function (rgb) { h += 360; } - l = (min + max) / 2; + const l = (min + max) / 2; if (max === min) { s = 0; @@ -95,23 +92,24 @@ convert.rgb.hsl = function (rgb) { }; convert.rgb.hsv = function (rgb) { - var rdif; - var gdif; - var bdif; - var h; - var s; - - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var v = Math.max(r, g, b); - var diff = v - Math.min(r, g, b); - var diffc = function (c) { + let rdif; + let gdif; + let bdif; + let h; + let s; + + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const v = Math.max(r, g, b); + const diff = v - Math.min(r, g, b); + const diffc = function (c) { return (v - c) / 6 / diff + 1 / 2; }; if (diff === 0) { - h = s = 0; + h = 0; + s = 0; } else { s = diff / v; rdif = diffc(r); @@ -125,6 +123,7 @@ convert.rgb.hsv = function (rgb) { } else if (b === v) { h = (2 / 3) + gdif - rdif; } + if (h < 0) { h += 1; } else if (h > 1) { @@ -140,11 +139,11 @@ convert.rgb.hsv = function (rgb) { }; convert.rgb.hwb = function (rgb) { - var r = rgb[0]; - var g = rgb[1]; - var b = rgb[2]; - var h = convert.rgb.hsl(rgb)[0]; - var w = 1 / 255 * Math.min(r, Math.min(g, b)); + const r = rgb[0]; + const g = rgb[1]; + let b = rgb[2]; + const h = convert.rgb.hsl(rgb)[0]; + const w = 1 / 255 * Math.min(r, Math.min(g, b)); b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); @@ -152,54 +151,48 @@ convert.rgb.hwb = function (rgb) { }; convert.rgb.cmyk = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var c; - var m; - var y; - var k; - - k = Math.min(1 - r, 1 - g, 1 - b); - c = (1 - r - k) / (1 - k) || 0; - m = (1 - g - k) / (1 - k) || 0; - y = (1 - b - k) / (1 - k) || 0; + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + + const k = Math.min(1 - r, 1 - g, 1 - b); + const c = (1 - r - k) / (1 - k) || 0; + const m = (1 - g - k) / (1 - k) || 0; + const y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; }; -/** - * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - * */ function comparativeDistance(x, y) { + /* + See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + */ return ( - Math.pow(x[0] - y[0], 2) + - Math.pow(x[1] - y[1], 2) + - Math.pow(x[2] - y[2], 2) + ((x[0] - y[0]) ** 2) + + ((x[1] - y[1]) ** 2) + + ((x[2] - y[2]) ** 2) ); } convert.rgb.keyword = function (rgb) { - var reversed = reverseKeywords[rgb]; + const reversed = reverseKeywords[rgb]; if (reversed) { return reversed; } - var currentClosestDistance = Infinity; - var currentClosestKeyword; + let currentClosestDistance = Infinity; + let currentClosestKeyword; - for (var keyword in cssKeywords) { - if (cssKeywords.hasOwnProperty(keyword)) { - var value = cssKeywords[keyword]; + for (const keyword of Object.keys(cssKeywords)) { + const value = cssKeywords[keyword]; - // Compute comparative distance - var distance = comparativeDistance(rgb, value); + // Compute comparative distance + const distance = comparativeDistance(rgb, value); - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; - } + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; } } @@ -211,55 +204,50 @@ convert.keyword.rgb = function (keyword) { }; convert.rgb.xyz = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; + let r = rgb[0] / 255; + let g = rgb[1] / 255; + let b = rgb[2] / 255; - // assume sRGB - r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); - g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); - b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); + // Assume sRGB + r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); + g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); + b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); - var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y * 100, z * 100]; }; convert.rgb.lab = function (rgb) { - var xyz = convert.rgb.xyz(rgb); - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; + const xyz = convert.rgb.xyz(rgb); + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; x /= 95.047; y /= 100; z /= 108.883; - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); return [l, a, b]; }; convert.hsl.rgb = function (hsl) { - var h = hsl[0] / 360; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var t1; - var t2; - var t3; - var rgb; - var val; + const h = hsl[0] / 360; + const s = hsl[1] / 100; + const l = hsl[2] / 100; + let t2; + let t3; + let val; if (s === 0) { val = l * 255; @@ -272,14 +260,15 @@ convert.hsl.rgb = function (hsl) { t2 = l + s - l * s; } - t1 = 2 * l - t2; + const t1 = 2 * l - t2; - rgb = [0, 0, 0]; - for (var i = 0; i < 3; i++) { + const rgb = [0, 0, 0]; + for (let i = 0; i < 3; i++) { t3 = h + 1 / 3 * -(i - 1); if (t3 < 0) { t3++; } + if (t3 > 1) { t3--; } @@ -301,33 +290,31 @@ convert.hsl.rgb = function (hsl) { }; convert.hsl.hsv = function (hsl) { - var h = hsl[0]; - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var smin = s; - var lmin = Math.max(l, 0.01); - var sv; - var v; + const h = hsl[0]; + let s = hsl[1] / 100; + let l = hsl[2] / 100; + let smin = s; + const lmin = Math.max(l, 0.01); l *= 2; s *= (l <= 1) ? l : 2 - l; smin *= lmin <= 1 ? lmin : 2 - lmin; - v = (l + s) / 2; - sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + const v = (l + s) / 2; + const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); return [h, sv * 100, v * 100]; }; convert.hsv.rgb = function (hsv) { - var h = hsv[0] / 60; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var hi = Math.floor(h) % 6; - - var f = h - Math.floor(h); - var p = 255 * v * (1 - s); - var q = 255 * v * (1 - (s * f)); - var t = 255 * v * (1 - (s * (1 - f))); + const h = hsv[0] / 60; + const s = hsv[1] / 100; + let v = hsv[2] / 100; + const hi = Math.floor(h) % 6; + + const f = h - Math.floor(h); + const p = 255 * v * (1 - s); + const q = 255 * v * (1 - (s * f)); + const t = 255 * v * (1 - (s * (1 - f))); v *= 255; switch (hi) { @@ -347,16 +334,15 @@ convert.hsv.rgb = function (hsv) { }; convert.hsv.hsl = function (hsv) { - var h = hsv[0]; - var s = hsv[1] / 100; - var v = hsv[2] / 100; - var vmin = Math.max(v, 0.01); - var lmin; - var sl; - var l; + const h = hsv[0]; + const s = hsv[1] / 100; + const v = hsv[2] / 100; + const vmin = Math.max(v, 0.01); + let sl; + let l; l = (2 - s) * v; - lmin = (2 - s) * vmin; + const lmin = (2 - s) * vmin; sl = s * vmin; sl /= (lmin <= 1) ? lmin : 2 - lmin; sl = sl || 0; @@ -367,87 +353,83 @@ convert.hsv.hsl = function (hsv) { // http://dev.w3.org/csswg/css-color/#hwb-to-rgb convert.hwb.rgb = function (hwb) { - var h = hwb[0] / 360; - var wh = hwb[1] / 100; - var bl = hwb[2] / 100; - var ratio = wh + bl; - var i; - var v; - var f; - var n; - - // wh + bl cant be > 1 + const h = hwb[0] / 360; + let wh = hwb[1] / 100; + let bl = hwb[2] / 100; + const ratio = wh + bl; + let f; + + // Wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } - i = Math.floor(6 * h); - v = 1 - bl; + const i = Math.floor(6 * h); + const v = 1 - bl; f = 6 * h - i; if ((i & 0x01) !== 0) { f = 1 - f; } - n = wh + f * (v - wh); // linear interpolation + const n = wh + f * (v - wh); // Linear interpolation - var r; - var g; - var b; + let r; + let g; + let b; + /* eslint-disable max-statements-per-line,no-multi-spaces */ switch (i) { default: case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; } + /* eslint-enable max-statements-per-line,no-multi-spaces */ return [r * 255, g * 255, b * 255]; }; convert.cmyk.rgb = function (cmyk) { - var c = cmyk[0] / 100; - var m = cmyk[1] / 100; - var y = cmyk[2] / 100; - var k = cmyk[3] / 100; - var r; - var g; - var b; - - r = 1 - Math.min(1, c * (1 - k) + k); - g = 1 - Math.min(1, m * (1 - k) + k); - b = 1 - Math.min(1, y * (1 - k) + k); + const c = cmyk[0] / 100; + const m = cmyk[1] / 100; + const y = cmyk[2] / 100; + const k = cmyk[3] / 100; + + const r = 1 - Math.min(1, c * (1 - k) + k); + const g = 1 - Math.min(1, m * (1 - k) + k); + const b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; }; convert.xyz.rgb = function (xyz) { - var x = xyz[0] / 100; - var y = xyz[1] / 100; - var z = xyz[2] / 100; - var r; - var g; - var b; + const x = xyz[0] / 100; + const y = xyz[1] / 100; + const z = xyz[2] / 100; + let r; + let g; + let b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); - // assume sRGB + // Assume sRGB r = r > 0.0031308 - ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) + ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) : r * 12.92; g = g > 0.0031308 - ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) + ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) : g * 12.92; b = b > 0.0031308 - ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) + ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) : b * 12.92; r = Math.min(Math.max(0, r), 1); @@ -458,43 +440,40 @@ convert.xyz.rgb = function (xyz) { }; convert.xyz.lab = function (xyz) { - var x = xyz[0]; - var y = xyz[1]; - var z = xyz[2]; - var l; - var a; - var b; + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; x /= 95.047; y /= 100; z /= 108.883; - x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); - l = (116 * y) - 16; - a = 500 * (x - y); - b = 200 * (y - z); + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); return [l, a, b]; }; convert.lab.xyz = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var x; - var y; - var z; + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let x; + let y; + let z; y = (l + 16) / 116; x = a / 500 + y; z = y - b / 200; - var y2 = Math.pow(y, 3); - var x2 = Math.pow(x, 3); - var z2 = Math.pow(z, 3); + const y2 = y ** 3; + const x2 = x ** 3; + const z2 = z ** 3; y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; @@ -507,45 +486,38 @@ convert.lab.xyz = function (lab) { }; convert.lab.lch = function (lab) { - var l = lab[0]; - var a = lab[1]; - var b = lab[2]; - var hr; - var h; - var c; - - hr = Math.atan2(b, a); + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let h; + + const hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } - c = Math.sqrt(a * a + b * b); + const c = Math.sqrt(a * a + b * b); return [l, c, h]; }; convert.lch.lab = function (lch) { - var l = lch[0]; - var c = lch[1]; - var h = lch[2]; - var a; - var b; - var hr; + const l = lch[0]; + const c = lch[1]; + const h = lch[2]; - hr = h / 360 * 2 * Math.PI; - a = c * Math.cos(hr); - b = c * Math.sin(hr); + const hr = h / 360 * 2 * Math.PI; + const a = c * Math.cos(hr); + const b = c * Math.sin(hr); return [l, a, b]; }; -convert.rgb.ansi16 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; - var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization +convert.rgb.ansi16 = function (args, saturation = null) { + const [r, g, b] = args; + let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization value = Math.round(value / 50); @@ -553,7 +525,7 @@ convert.rgb.ansi16 = function (args) { return 30; } - var ansi = 30 + let ansi = 30 + ((Math.round(b / 255) << 2) | (Math.round(g / 255) << 1) | Math.round(r / 255)); @@ -566,17 +538,17 @@ convert.rgb.ansi16 = function (args) { }; convert.hsv.ansi16 = function (args) { - // optimization here; we already know the value and don't need to get + // Optimization here; we already know the value and don't need to get // it converted for us. return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); }; convert.rgb.ansi256 = function (args) { - var r = args[0]; - var g = args[1]; - var b = args[2]; + const r = args[0]; + const g = args[1]; + const b = args[2]; - // we use the extended greyscale palette here, with the exception of + // We use the extended greyscale palette here, with the exception of // black and white. normal palette only has 4 greyscale shades. if (r === g && g === b) { if (r < 8) { @@ -590,7 +562,7 @@ convert.rgb.ansi256 = function (args) { return Math.round(((r - 8) / 247) * 24) + 232; } - var ansi = 16 + const ansi = 16 + (36 * Math.round(r / 255 * 5)) + (6 * Math.round(g / 255 * 5)) + Math.round(b / 255 * 5); @@ -599,9 +571,9 @@ convert.rgb.ansi256 = function (args) { }; convert.ansi16.rgb = function (args) { - var color = args % 10; + let color = args % 10; - // handle greyscale + // Handle greyscale if (color === 0 || color === 7) { if (args > 50) { color += 3.5; @@ -612,71 +584,71 @@ convert.ansi16.rgb = function (args) { return [color, color, color]; } - var mult = (~~(args > 50) + 1) * 0.5; - var r = ((color & 1) * mult) * 255; - var g = (((color >> 1) & 1) * mult) * 255; - var b = (((color >> 2) & 1) * mult) * 255; + const mult = (~~(args > 50) + 1) * 0.5; + const r = ((color & 1) * mult) * 255; + const g = (((color >> 1) & 1) * mult) * 255; + const b = (((color >> 2) & 1) * mult) * 255; return [r, g, b]; }; convert.ansi256.rgb = function (args) { - // handle greyscale + // Handle greyscale if (args >= 232) { - var c = (args - 232) * 10 + 8; + const c = (args - 232) * 10 + 8; return [c, c, c]; } args -= 16; - var rem; - var r = Math.floor(args / 36) / 5 * 255; - var g = Math.floor((rem = args % 36) / 6) / 5 * 255; - var b = (rem % 6) / 5 * 255; + let rem; + const r = Math.floor(args / 36) / 5 * 255; + const g = Math.floor((rem = args % 36) / 6) / 5 * 255; + const b = (rem % 6) / 5 * 255; return [r, g, b]; }; convert.rgb.hex = function (args) { - var integer = ((Math.round(args[0]) & 0xFF) << 16) + const integer = ((Math.round(args[0]) & 0xFF) << 16) + ((Math.round(args[1]) & 0xFF) << 8) + (Math.round(args[2]) & 0xFF); - var string = integer.toString(16).toUpperCase(); + const string = integer.toString(16).toUpperCase(); return '000000'.substring(string.length) + string; }; convert.hex.rgb = function (args) { - var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); if (!match) { return [0, 0, 0]; } - var colorString = match[0]; + let colorString = match[0]; if (match[0].length === 3) { - colorString = colorString.split('').map(function (char) { + colorString = colorString.split('').map(char => { return char + char; }).join(''); } - var integer = parseInt(colorString, 16); - var r = (integer >> 16) & 0xFF; - var g = (integer >> 8) & 0xFF; - var b = integer & 0xFF; + const integer = parseInt(colorString, 16); + const r = (integer >> 16) & 0xFF; + const g = (integer >> 8) & 0xFF; + const b = integer & 0xFF; return [r, g, b]; }; convert.rgb.hcg = function (rgb) { - var r = rgb[0] / 255; - var g = rgb[1] / 255; - var b = rgb[2] / 255; - var max = Math.max(Math.max(r, g), b); - var min = Math.min(Math.min(r, g), b); - var chroma = (max - min); - var grayscale; - var hue; + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const max = Math.max(Math.max(r, g), b); + const min = Math.min(Math.min(r, g), b); + const chroma = (max - min); + let grayscale; + let hue; if (chroma < 1) { grayscale = min / (1 - chroma); @@ -693,7 +665,7 @@ convert.rgb.hcg = function (rgb) { if (max === g) { hue = 2 + (b - r) / chroma; } else { - hue = 4 + (r - g) / chroma + 4; + hue = 4 + (r - g) / chroma; } hue /= 6; @@ -703,17 +675,12 @@ convert.rgb.hcg = function (rgb) { }; convert.hsl.hcg = function (hsl) { - var s = hsl[1] / 100; - var l = hsl[2] / 100; - var c = 1; - var f = 0; + const s = hsl[1] / 100; + const l = hsl[2] / 100; - if (l < 0.5) { - c = 2.0 * s * l; - } else { - c = 2.0 * s * (1.0 - l); - } + const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); + let f = 0; if (c < 1.0) { f = (l - 0.5 * c) / (1.0 - c); } @@ -722,11 +689,11 @@ convert.hsl.hcg = function (hsl) { }; convert.hsv.hcg = function (hsv) { - var s = hsv[1] / 100; - var v = hsv[2] / 100; + const s = hsv[1] / 100; + const v = hsv[2] / 100; - var c = s * v; - var f = 0; + const c = s * v; + let f = 0; if (c < 1.0) { f = (v - c) / (1 - c); @@ -736,20 +703,21 @@ convert.hsv.hcg = function (hsv) { }; convert.hcg.rgb = function (hcg) { - var h = hcg[0] / 360; - var c = hcg[1] / 100; - var g = hcg[2] / 100; + const h = hcg[0] / 360; + const c = hcg[1] / 100; + const g = hcg[2] / 100; if (c === 0.0) { return [g * 255, g * 255, g * 255]; } - var pure = [0, 0, 0]; - var hi = (h % 1) * 6; - var v = hi % 1; - var w = 1 - v; - var mg = 0; + const pure = [0, 0, 0]; + const hi = (h % 1) * 6; + const v = hi % 1; + const w = 1 - v; + let mg = 0; + /* eslint-disable max-statements-per-line */ switch (Math.floor(hi)) { case 0: pure[0] = 1; pure[1] = v; pure[2] = 0; break; @@ -764,6 +732,7 @@ convert.hcg.rgb = function (hcg) { default: pure[0] = 1; pure[1] = 0; pure[2] = w; } + /* eslint-enable max-statements-per-line */ mg = (1.0 - c) * g; @@ -775,11 +744,11 @@ convert.hcg.rgb = function (hcg) { }; convert.hcg.hsv = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; + const c = hcg[1] / 100; + const g = hcg[2] / 100; - var v = c + g * (1.0 - c); - var f = 0; + const v = c + g * (1.0 - c); + let f = 0; if (v > 0.0) { f = c / v; @@ -789,11 +758,11 @@ convert.hcg.hsv = function (hcg) { }; convert.hcg.hsl = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; + const c = hcg[1] / 100; + const g = hcg[2] / 100; - var l = g * (1.0 - c) + 0.5 * c; - var s = 0; + const l = g * (1.0 - c) + 0.5 * c; + let s = 0; if (l > 0.0 && l < 0.5) { s = c / (2 * l); @@ -806,18 +775,18 @@ convert.hcg.hsl = function (hcg) { }; convert.hcg.hwb = function (hcg) { - var c = hcg[1] / 100; - var g = hcg[2] / 100; - var v = c + g * (1.0 - c); + const c = hcg[1] / 100; + const g = hcg[2] / 100; + const v = c + g * (1.0 - c); return [hcg[0], (v - c) * 100, (1 - v) * 100]; }; convert.hwb.hcg = function (hwb) { - var w = hwb[1] / 100; - var b = hwb[2] / 100; - var v = 1 - b; - var c = v - w; - var g = 0; + const w = hwb[1] / 100; + const b = hwb[2] / 100; + const v = 1 - b; + const c = v - w; + let g = 0; if (c < 1) { g = (v - c) / (1 - c); @@ -838,10 +807,12 @@ convert.gray.rgb = function (args) { return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; }; -convert.gray.hsl = convert.gray.hsv = function (args) { +convert.gray.hsl = function (args) { return [0, 0, args[0]]; }; +convert.gray.hsv = convert.gray.hsl; + convert.gray.hwb = function (gray) { return [0, 100, gray[0]]; }; @@ -855,14 +826,14 @@ convert.gray.lab = function (gray) { }; convert.gray.hex = function (gray) { - var val = Math.round(gray[0] / 100 * 255) & 0xFF; - var integer = (val << 16) + (val << 8) + val; + const val = Math.round(gray[0] / 100 * 255) & 0xFF; + const integer = (val << 16) + (val << 8) + val; - var string = integer.toString(16).toUpperCase(); + const string = integer.toString(16).toUpperCase(); return '000000'.substring(string.length) + string; }; convert.rgb.gray = function (rgb) { - var val = (rgb[0] + rgb[1] + rgb[2]) / 3; + const val = (rgb[0] + rgb[1] + rgb[2]) / 3; return [val / 255 * 100]; }; diff --git a/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/index.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/index.js new file mode 100644 index 00000000000000..b648e5737be616 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/index.js @@ -0,0 +1,81 @@ +const conversions = require('./conversions'); +const route = require('./route'); + +const convert = {}; + +const models = Object.keys(conversions); + +function wrapRaw(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + if (arg0 === undefined || arg0 === null) { + return arg0; + } + + if (arg0.length > 1) { + args = arg0; + } + + return fn(args); + }; + + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + + if (arg0 === undefined || arg0 === null) { + return arg0; + } + + if (arg0.length > 1) { + args = arg0; + } + + const result = fn(args); + + // We're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (let len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(fromModel => { + convert[fromModel] = {}; + + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); + + const routes = route(fromModel); + const routeModels = Object.keys(routes); + + routeModels.forEach(toModel => { + const fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +module.exports = convert; diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/package.json b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/package.json similarity index 53% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/package.json rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/package.json index dfbc471407ff4c..427616bd0587d8 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/package.json +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/package.json @@ -1,14 +1,30 @@ { - "name": "color-convert", + "author": { + "name": "Heather Arthur", + "email": "fayearthur@gmail.com" + }, + "bugs": { + "url": "https://github.com/Qix-/color-convert/issues" + }, + "bundleDependencies": false, + "dependencies": { + "color-name": "~1.1.4" + }, + "deprecated": false, "description": "Plain color conversion functions", - "version": "1.9.3", - "author": "Heather Arthur <fayearthur@gmail.com>", - "license": "MIT", - "repository": "Qix-/color-convert", - "scripts": { - "pretest": "xo", - "test": "node test/basic.js" + "devDependencies": { + "chalk": "^2.4.2", + "xo": "^0.24.0" }, + "engines": { + "node": ">=7.0.0" + }, + "files": [ + "index.js", + "conversions.js", + "route.js" + ], + "homepage": "https://github.com/Qix-/color-convert#readme", "keywords": [ "color", "colour", @@ -23,24 +39,22 @@ "ansi", "ansi16" ], - "files": [ - "index.js", - "conversions.js", - "css-keywords.js", - "route.js" - ], + "license": "MIT", + "name": "color-convert", + "repository": { + "type": "git", + "url": "git+https://github.com/Qix-/color-convert.git" + }, + "scripts": { + "pretest": "xo", + "test": "node test/basic.js" + }, + "version": "2.0.1", "xo": { "rules": { "default-case": 0, "no-inline-comments": 0, "operator-linebreak": 0 } - }, - "devDependencies": { - "chalk": "1.1.1", - "xo": "0.11.2" - }, - "dependencies": { - "color-name": "1.1.3" } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/route.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/route.js similarity index 59% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/route.js rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/route.js index 0a1fdea689e2a7..1a08521b5a0017 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-convert/route.js +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-convert/route.js @@ -1,7 +1,7 @@ -var conversions = require('./conversions'); +const conversions = require('./conversions'); /* - this function routes a model to all other models. + This function routes a model to all other models. all functions that are routed have a property `.conversion` attached to the returned synthetic function. This property is an array @@ -12,11 +12,11 @@ var conversions = require('./conversions'); */ function buildGraph() { - var graph = {}; + const graph = {}; // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - var models = Object.keys(conversions); + const models = Object.keys(conversions); - for (var len = models.length, i = 0; i < len; i++) { + for (let len = models.length, i = 0; i < len; i++) { graph[models[i]] = { // http://jsperf.com/1-vs-infinity // micro-opt, but this is simple. @@ -30,18 +30,18 @@ function buildGraph() { // https://en.wikipedia.org/wiki/Breadth-first_search function deriveBFS(fromModel) { - var graph = buildGraph(); - var queue = [fromModel]; // unshift -> queue -> pop + const graph = buildGraph(); + const queue = [fromModel]; // Unshift -> queue -> pop graph[fromModel].distance = 0; while (queue.length) { - var current = queue.pop(); - var adjacents = Object.keys(conversions[current]); + const current = queue.pop(); + const adjacents = Object.keys(conversions[current]); - for (var len = adjacents.length, i = 0; i < len; i++) { - var adjacent = adjacents[i]; - var node = graph[adjacent]; + for (let len = adjacents.length, i = 0; i < len; i++) { + const adjacent = adjacents[i]; + const node = graph[adjacent]; if (node.distance === -1) { node.distance = graph[current].distance + 1; @@ -61,10 +61,10 @@ function link(from, to) { } function wrapConversion(toModel, graph) { - var path = [graph[toModel].parent, toModel]; - var fn = conversions[graph[toModel].parent][toModel]; + const path = [graph[toModel].parent, toModel]; + let fn = conversions[graph[toModel].parent][toModel]; - var cur = graph[toModel].parent; + let cur = graph[toModel].parent; while (graph[cur].parent) { path.unshift(graph[cur].parent); fn = link(conversions[graph[cur].parent][cur], fn); @@ -76,16 +76,16 @@ function wrapConversion(toModel, graph) { } module.exports = function (fromModel) { - var graph = deriveBFS(fromModel); - var conversion = {}; + const graph = deriveBFS(fromModel); + const conversion = {}; - var models = Object.keys(graph); - for (var len = models.length, i = 0; i < len; i++) { - var toModel = models[i]; - var node = graph[toModel]; + const models = Object.keys(graph); + for (let len = models.length, i = 0; i < len; i++) { + const toModel = models[i]; + const node = graph[toModel]; if (node.parent === null) { - // no possible conversion, or this node is the source model. + // No possible conversion, or this node is the source model. continue; } diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/LICENSE b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/LICENSE similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/LICENSE rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/LICENSE diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/README.md b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/README.md similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/README.md rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/README.md diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/index.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/index.js similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/color-name/index.js rename to tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/index.js diff --git a/tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/package.json b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/package.json new file mode 100644 index 00000000000000..07b8f6ece8170a --- /dev/null +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/color-name/package.json @@ -0,0 +1,33 @@ +{ + "author": { + "name": "DY", + "email": "dfcreative@gmail.com" + }, + "bugs": { + "url": "https://github.com/colorjs/color-name/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "A list of color names and its values", + "files": [ + "index.js" + ], + "homepage": "https://github.com/colorjs/color-name", + "keywords": [ + "color-name", + "color", + "color-keyword", + "keyword" + ], + "license": "MIT", + "main": "index.js", + "name": "color-name", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/colorjs/color-name.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.1.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/index.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/index.js new file mode 100644 index 00000000000000..b6f80b1f8ffd76 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/index.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = (flag, argv = process.argv) => { + const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); + const position = argv.indexOf(prefix + flag); + const terminatorPosition = argv.indexOf('--'); + return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); +}; diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/license b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/license similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/license rename to tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/license diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/package.json b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/package.json similarity index 53% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/package.json rename to tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/package.json index e1eb17a15ed880..c71a95689f337f 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/package.json +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/package.json @@ -1,23 +1,28 @@ { - "name": "has-flag", - "version": "3.0.0", - "description": "Check if argv has a specific flag", - "license": "MIT", - "repository": "sindresorhus/has-flag", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "sindresorhus.com" }, - "engines": { - "node": ">=4" + "bugs": { + "url": "https://github.com/sindresorhus/has-flag/issues" }, - "scripts": { - "test": "xo && ava" + "bundleDependencies": false, + "deprecated": false, + "description": "Check if argv has a specific flag", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" }, "files": [ - "index.js" + "index.js", + "index.d.ts" ], + "homepage": "https://github.com/sindresorhus/has-flag#readme", "keywords": [ "has", "check", @@ -37,8 +42,14 @@ "minimist", "optimist" ], - "devDependencies": { - "ava": "*", - "xo": "*" - } -} + "license": "MIT", + "name": "has-flag", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/has-flag.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "4.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/readme.md b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/readme.md similarity index 61% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/readme.md rename to tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/readme.md index 677893c278a2e3..3f72dff29a6961 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/has-flag/readme.md +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/has-flag/readme.md @@ -4,6 +4,20 @@ Correctly stops looking after an `--` argument terminator. +--- + +<div align="center"> + <b> + <a href="https://tidelift.com/subscription/pkg/npm-has-flag?utm_source=npm-has-flag&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> + </b> + <br> + <sub> + Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. + </sub> +</div> + +--- + ## Install @@ -65,6 +79,11 @@ Default: `process.argv` CLI arguments. +## Security + +To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. + + ## License MIT © [Sindre Sorhus](https://sindresorhus.com) diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/browser.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/browser.js similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/browser.js rename to tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/browser.js diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/index.js b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/index.js similarity index 62% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/index.js rename to tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/index.js index 1704131bdf6c8f..6fada390fb88d8 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/index.js +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/index.js @@ -1,22 +1,31 @@ 'use strict'; const os = require('os'); +const tty = require('tty'); const hasFlag = require('has-flag'); -const env = process.env; +const {env} = process; let forceColor; if (hasFlag('no-color') || hasFlag('no-colors') || - hasFlag('color=false')) { - forceColor = false; + hasFlag('color=false') || + hasFlag('color=never')) { + forceColor = 0; } else if (hasFlag('color') || hasFlag('colors') || hasFlag('color=true') || hasFlag('color=always')) { - forceColor = true; + forceColor = 1; } + if ('FORCE_COLOR' in env) { - forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; + if (env.FORCE_COLOR === 'true') { + forceColor = 1; + } else if (env.FORCE_COLOR === 'false') { + forceColor = 0; + } else { + forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); + } } function translateLevel(level) { @@ -32,8 +41,8 @@ function translateLevel(level) { }; } -function supportsColor(stream) { - if (forceColor === false) { +function supportsColor(haveStream, streamIsTTY) { + if (forceColor === 0) { return 0; } @@ -47,22 +56,21 @@ function supportsColor(stream) { return 2; } - if (stream && !stream.isTTY && forceColor !== true) { + if (haveStream && !streamIsTTY && forceColor === undefined) { return 0; } - const min = forceColor ? 1 : 0; + const min = forceColor || 0; + + if (env.TERM === 'dumb') { + return min; + } if (process.platform === 'win32') { - // Node.js 7.5.0 is the first version of Node.js to include a patch to - // libuv that enables 256 color output on Windows. Anything earlier and it - // won't work. However, here we target Node.js 8 at minimum as it is an LTS - // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows - // release that supports 256 colors. Windows 10 build 14931 is the first release - // that supports 16m/TrueColor. + // Windows 10 build 10586 is the first Windows release that supports 256 colors. + // Windows 10 build 14931 is the first release that supports 16m/TrueColor. const osRelease = os.release().split('.'); if ( - Number(process.versions.node.split('.')[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586 ) { @@ -73,7 +81,7 @@ function supportsColor(stream) { } if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { return 1; } @@ -112,20 +120,16 @@ function supportsColor(stream) { return 1; } - if (env.TERM === 'dumb') { - return min; - } - return min; } function getSupportLevel(stream) { - const level = supportsColor(stream); + const level = supportsColor(stream, stream && stream.isTTY); return translateLevel(level); } module.exports = { supportsColor: getSupportLevel, - stdout: getSupportLevel(process.stdout), - stderr: getSupportLevel(process.stderr) + stdout: translateLevel(supportsColor(true, tty.isatty(1))), + stderr: translateLevel(supportsColor(true, tty.isatty(2))) }; diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/license b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/license similarity index 100% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/license rename to tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/license diff --git a/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/package.json b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/package.json new file mode 100644 index 00000000000000..5f90dfc93a7936 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/package.json @@ -0,0 +1,62 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/chalk/supports-color/issues" + }, + "bundleDependencies": false, + "dependencies": { + "has-flag": "^4.0.0" + }, + "deprecated": false, + "description": "Detect whether a terminal supports color", + "devDependencies": { + "ava": "^1.4.1", + "import-fresh": "^3.0.0", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "browser.js" + ], + "homepage": "https://github.com/chalk/supports-color#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect", + "truecolor", + "16m" + ], + "license": "MIT", + "name": "supports-color", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/supports-color.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "7.2.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/readme.md b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/readme.md similarity index 67% rename from tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/readme.md rename to tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/readme.md index f6e40195730ae8..36542285863330 100644 --- a/tools/node_modules/eslint/node_modules/@babel/highlight/node_modules/supports-color/readme.md +++ b/tools/node_modules/eslint/node_modules/chalk/node_modules/supports-color/readme.md @@ -44,7 +44,7 @@ The `stdout`/`stderr` objects specifies a level of support for color through a ` It obeys the `--color` and `--no-color` CLI flags. -Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add the environment variable `FORCE_COLOR=1` to forcefully enable color or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks. +For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks. Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively. @@ -61,6 +61,16 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color= - [Josh Junon](https://github.com/qix-) -## License +--- -MIT +<div align="center"> + <b> + <a href="https://tidelift.com/subscription/pkg/npm-supports-color?utm_source=npm-supports-color&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> + </b> + <br> + <sub> + Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. + </sub> +</div> + +--- diff --git a/tools/node_modules/eslint/node_modules/chalk/package.json b/tools/node_modules/eslint/node_modules/chalk/package.json index 0d99f0f28621f2..fbb12850511d0e 100644 --- a/tools/node_modules/eslint/node_modules/chalk/package.json +++ b/tools/node_modules/eslint/node_modules/chalk/package.json @@ -1,68 +1,77 @@ { - "name": "chalk", - "version": "4.1.0", - "description": "Terminal string styling done right", - "license": "MIT", - "repository": "chalk/chalk", - "funding": "https://github.com/chalk/chalk?sponsor=1", - "main": "source", - "engines": { - "node": ">=10" - }, - "scripts": { - "test": "xo && nyc ava && tsd", - "bench": "matcha benchmark.js" - }, - "files": [ - "source", - "index.d.ts" - ], - "keywords": [ - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "string", - "str", - "ansi", - "style", - "styles", - "tty", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "log", - "logging", - "command-line", - "text" - ], - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "devDependencies": { - "ava": "^2.4.0", - "coveralls": "^3.0.7", - "execa": "^4.0.0", - "import-fresh": "^3.1.0", - "matcha": "^0.7.0", - "nyc": "^15.0.0", - "resolve-from": "^5.0.0", - "tsd": "^0.7.4", - "xo": "^0.28.2" - }, - "xo": { - "rules": { - "unicorn/prefer-string-slice": "off", - "unicorn/prefer-includes": "off", - "@typescript-eslint/member-ordering": "off", - "no-redeclare": "off", - "unicorn/string-content": "off", - "unicorn/better-regex": "off" - } - } -} + "bugs": { + "url": "https://github.com/chalk/chalk/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "deprecated": false, + "description": "Terminal string styling done right", + "devDependencies": { + "ava": "^2.4.0", + "coveralls": "^3.0.7", + "execa": "^4.0.0", + "import-fresh": "^3.1.0", + "matcha": "^0.7.0", + "nyc": "^15.0.0", + "resolve-from": "^5.0.0", + "tsd": "^0.7.4", + "xo": "^0.28.2" + }, + "engines": { + "node": ">=10" + }, + "files": [ + "source", + "index.d.ts" + ], + "funding": "https://github.com/chalk/chalk?sponsor=1", + "homepage": "https://github.com/chalk/chalk#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "str", + "ansi", + "style", + "styles", + "tty", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "main": "source", + "name": "chalk", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/chalk.git" + }, + "scripts": { + "bench": "matcha benchmark.js", + "test": "xo && nyc ava && tsd" + }, + "version": "4.1.0", + "xo": { + "rules": { + "unicorn/prefer-string-slice": "off", + "unicorn/prefer-includes": "off", + "@typescript-eslint/member-ordering": "off", + "no-redeclare": "off", + "unicorn/string-content": "off", + "unicorn/better-regex": "off" + } + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/color-convert/conversions.js b/tools/node_modules/eslint/node_modules/color-convert/conversions.js index 2657f265c9e102..32172007ec0b17 100644 --- a/tools/node_modules/eslint/node_modules/color-convert/conversions.js +++ b/tools/node_modules/eslint/node_modules/color-convert/conversions.js @@ -1,17 +1,18 @@ /* MIT license */ -/* eslint-disable no-mixed-operators */ -const cssKeywords = require('color-name'); +var cssKeywords = require('color-name'); // NOTE: conversions should only return primitive values (i.e. arrays, or // values that give correct `typeof` results). // do not use box values types (i.e. Number(), String(), etc.) -const reverseKeywords = {}; -for (const key of Object.keys(cssKeywords)) { - reverseKeywords[cssKeywords[key]] = key; +var reverseKeywords = {}; +for (var key in cssKeywords) { + if (cssKeywords.hasOwnProperty(key)) { + reverseKeywords[cssKeywords[key]] = key; + } } -const convert = { +var convert = module.exports = { rgb: {channels: 3, labels: 'rgb'}, hsl: {channels: 3, labels: 'hsl'}, hsv: {channels: 3, labels: 'hsv'}, @@ -29,38 +30,40 @@ const convert = { gray: {channels: 1, labels: ['gray']} }; -module.exports = convert; +// hide .channels and .labels properties +for (var model in convert) { + if (convert.hasOwnProperty(model)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } -// Hide .channels and .labels properties -for (const model of Object.keys(convert)) { - if (!('channels' in convert[model])) { - throw new Error('missing channels property: ' + model); - } + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } - if (!('labels' in convert[model])) { - throw new Error('missing channel labels property: ' + model); - } + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } - if (convert[model].labels.length !== convert[model].channels) { - throw new Error('channel and label counts mismatch: ' + model); + var channels = convert[model].channels; + var labels = convert[model].labels; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); } - - const {channels, labels} = convert[model]; - delete convert[model].channels; - delete convert[model].labels; - Object.defineProperty(convert[model], 'channels', {value: channels}); - Object.defineProperty(convert[model], 'labels', {value: labels}); } convert.rgb.hsl = function (rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - const min = Math.min(r, g, b); - const max = Math.max(r, g, b); - const delta = max - min; - let h; - let s; + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var min = Math.min(r, g, b); + var max = Math.max(r, g, b); + var delta = max - min; + var h; + var s; + var l; if (max === min) { h = 0; @@ -78,7 +81,7 @@ convert.rgb.hsl = function (rgb) { h += 360; } - const l = (min + max) / 2; + l = (min + max) / 2; if (max === min) { s = 0; @@ -92,24 +95,23 @@ convert.rgb.hsl = function (rgb) { }; convert.rgb.hsv = function (rgb) { - let rdif; - let gdif; - let bdif; - let h; - let s; - - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - const v = Math.max(r, g, b); - const diff = v - Math.min(r, g, b); - const diffc = function (c) { + var rdif; + var gdif; + var bdif; + var h; + var s; + + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var v = Math.max(r, g, b); + var diff = v - Math.min(r, g, b); + var diffc = function (c) { return (v - c) / 6 / diff + 1 / 2; }; if (diff === 0) { - h = 0; - s = 0; + h = s = 0; } else { s = diff / v; rdif = diffc(r); @@ -123,7 +125,6 @@ convert.rgb.hsv = function (rgb) { } else if (b === v) { h = (2 / 3) + gdif - rdif; } - if (h < 0) { h += 1; } else if (h > 1) { @@ -139,11 +140,11 @@ convert.rgb.hsv = function (rgb) { }; convert.rgb.hwb = function (rgb) { - const r = rgb[0]; - const g = rgb[1]; - let b = rgb[2]; - const h = convert.rgb.hsl(rgb)[0]; - const w = 1 / 255 * Math.min(r, Math.min(g, b)); + var r = rgb[0]; + var g = rgb[1]; + var b = rgb[2]; + var h = convert.rgb.hsl(rgb)[0]; + var w = 1 / 255 * Math.min(r, Math.min(g, b)); b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); @@ -151,48 +152,54 @@ convert.rgb.hwb = function (rgb) { }; convert.rgb.cmyk = function (rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - - const k = Math.min(1 - r, 1 - g, 1 - b); - const c = (1 - r - k) / (1 - k) || 0; - const m = (1 - g - k) / (1 - k) || 0; - const y = (1 - b - k) / (1 - k) || 0; + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var c; + var m; + var y; + var k; + + k = Math.min(1 - r, 1 - g, 1 - b); + c = (1 - r - k) / (1 - k) || 0; + m = (1 - g - k) / (1 - k) || 0; + y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; }; +/** + * See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + * */ function comparativeDistance(x, y) { - /* - See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance - */ return ( - ((x[0] - y[0]) ** 2) + - ((x[1] - y[1]) ** 2) + - ((x[2] - y[2]) ** 2) + Math.pow(x[0] - y[0], 2) + + Math.pow(x[1] - y[1], 2) + + Math.pow(x[2] - y[2], 2) ); } convert.rgb.keyword = function (rgb) { - const reversed = reverseKeywords[rgb]; + var reversed = reverseKeywords[rgb]; if (reversed) { return reversed; } - let currentClosestDistance = Infinity; - let currentClosestKeyword; + var currentClosestDistance = Infinity; + var currentClosestKeyword; - for (const keyword of Object.keys(cssKeywords)) { - const value = cssKeywords[keyword]; + for (var keyword in cssKeywords) { + if (cssKeywords.hasOwnProperty(keyword)) { + var value = cssKeywords[keyword]; - // Compute comparative distance - const distance = comparativeDistance(rgb, value); + // Compute comparative distance + var distance = comparativeDistance(rgb, value); - // Check if its less, if so set as closest - if (distance < currentClosestDistance) { - currentClosestDistance = distance; - currentClosestKeyword = keyword; + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } } } @@ -204,50 +211,55 @@ convert.keyword.rgb = function (keyword) { }; convert.rgb.xyz = function (rgb) { - let r = rgb[0] / 255; - let g = rgb[1] / 255; - let b = rgb[2] / 255; + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; - // Assume sRGB - r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); - g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); - b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); + // assume sRGB + r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); + g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); + b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); - const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); - const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); - const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y * 100, z * 100]; }; convert.rgb.lab = function (rgb) { - const xyz = convert.rgb.xyz(rgb); - let x = xyz[0]; - let y = xyz[1]; - let z = xyz[2]; + var xyz = convert.rgb.xyz(rgb); + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; x /= 95.047; y /= 100; z /= 108.883; - x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - const l = (116 * y) - 16; - const a = 500 * (x - y); - const b = 200 * (y - z); + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); return [l, a, b]; }; convert.hsl.rgb = function (hsl) { - const h = hsl[0] / 360; - const s = hsl[1] / 100; - const l = hsl[2] / 100; - let t2; - let t3; - let val; + var h = hsl[0] / 360; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var t1; + var t2; + var t3; + var rgb; + var val; if (s === 0) { val = l * 255; @@ -260,15 +272,14 @@ convert.hsl.rgb = function (hsl) { t2 = l + s - l * s; } - const t1 = 2 * l - t2; + t1 = 2 * l - t2; - const rgb = [0, 0, 0]; - for (let i = 0; i < 3; i++) { + rgb = [0, 0, 0]; + for (var i = 0; i < 3; i++) { t3 = h + 1 / 3 * -(i - 1); if (t3 < 0) { t3++; } - if (t3 > 1) { t3--; } @@ -290,31 +301,33 @@ convert.hsl.rgb = function (hsl) { }; convert.hsl.hsv = function (hsl) { - const h = hsl[0]; - let s = hsl[1] / 100; - let l = hsl[2] / 100; - let smin = s; - const lmin = Math.max(l, 0.01); + var h = hsl[0]; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var smin = s; + var lmin = Math.max(l, 0.01); + var sv; + var v; l *= 2; s *= (l <= 1) ? l : 2 - l; smin *= lmin <= 1 ? lmin : 2 - lmin; - const v = (l + s) / 2; - const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + v = (l + s) / 2; + sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); return [h, sv * 100, v * 100]; }; convert.hsv.rgb = function (hsv) { - const h = hsv[0] / 60; - const s = hsv[1] / 100; - let v = hsv[2] / 100; - const hi = Math.floor(h) % 6; - - const f = h - Math.floor(h); - const p = 255 * v * (1 - s); - const q = 255 * v * (1 - (s * f)); - const t = 255 * v * (1 - (s * (1 - f))); + var h = hsv[0] / 60; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var hi = Math.floor(h) % 6; + + var f = h - Math.floor(h); + var p = 255 * v * (1 - s); + var q = 255 * v * (1 - (s * f)); + var t = 255 * v * (1 - (s * (1 - f))); v *= 255; switch (hi) { @@ -334,15 +347,16 @@ convert.hsv.rgb = function (hsv) { }; convert.hsv.hsl = function (hsv) { - const h = hsv[0]; - const s = hsv[1] / 100; - const v = hsv[2] / 100; - const vmin = Math.max(v, 0.01); - let sl; - let l; + var h = hsv[0]; + var s = hsv[1] / 100; + var v = hsv[2] / 100; + var vmin = Math.max(v, 0.01); + var lmin; + var sl; + var l; l = (2 - s) * v; - const lmin = (2 - s) * vmin; + lmin = (2 - s) * vmin; sl = s * vmin; sl /= (lmin <= 1) ? lmin : 2 - lmin; sl = sl || 0; @@ -353,83 +367,87 @@ convert.hsv.hsl = function (hsv) { // http://dev.w3.org/csswg/css-color/#hwb-to-rgb convert.hwb.rgb = function (hwb) { - const h = hwb[0] / 360; - let wh = hwb[1] / 100; - let bl = hwb[2] / 100; - const ratio = wh + bl; - let f; - - // Wh + bl cant be > 1 + var h = hwb[0] / 360; + var wh = hwb[1] / 100; + var bl = hwb[2] / 100; + var ratio = wh + bl; + var i; + var v; + var f; + var n; + + // wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } - const i = Math.floor(6 * h); - const v = 1 - bl; + i = Math.floor(6 * h); + v = 1 - bl; f = 6 * h - i; if ((i & 0x01) !== 0) { f = 1 - f; } - const n = wh + f * (v - wh); // Linear interpolation + n = wh + f * (v - wh); // linear interpolation - let r; - let g; - let b; - /* eslint-disable max-statements-per-line,no-multi-spaces */ + var r; + var g; + var b; switch (i) { default: case 6: - case 0: r = v; g = n; b = wh; break; - case 1: r = n; g = v; b = wh; break; - case 2: r = wh; g = v; b = n; break; - case 3: r = wh; g = n; b = v; break; - case 4: r = n; g = wh; b = v; break; - case 5: r = v; g = wh; b = n; break; + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; } - /* eslint-enable max-statements-per-line,no-multi-spaces */ return [r * 255, g * 255, b * 255]; }; convert.cmyk.rgb = function (cmyk) { - const c = cmyk[0] / 100; - const m = cmyk[1] / 100; - const y = cmyk[2] / 100; - const k = cmyk[3] / 100; - - const r = 1 - Math.min(1, c * (1 - k) + k); - const g = 1 - Math.min(1, m * (1 - k) + k); - const b = 1 - Math.min(1, y * (1 - k) + k); + var c = cmyk[0] / 100; + var m = cmyk[1] / 100; + var y = cmyk[2] / 100; + var k = cmyk[3] / 100; + var r; + var g; + var b; + + r = 1 - Math.min(1, c * (1 - k) + k); + g = 1 - Math.min(1, m * (1 - k) + k); + b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; }; convert.xyz.rgb = function (xyz) { - const x = xyz[0] / 100; - const y = xyz[1] / 100; - const z = xyz[2] / 100; - let r; - let g; - let b; + var x = xyz[0] / 100; + var y = xyz[1] / 100; + var z = xyz[2] / 100; + var r; + var g; + var b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); - // Assume sRGB + // assume sRGB r = r > 0.0031308 - ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) + ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) : r * 12.92; g = g > 0.0031308 - ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) + ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) : g * 12.92; b = b > 0.0031308 - ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) + ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) : b * 12.92; r = Math.min(Math.max(0, r), 1); @@ -440,40 +458,43 @@ convert.xyz.rgb = function (xyz) { }; convert.xyz.lab = function (xyz) { - let x = xyz[0]; - let y = xyz[1]; - let z = xyz[2]; + var x = xyz[0]; + var y = xyz[1]; + var z = xyz[2]; + var l; + var a; + var b; x /= 95.047; y /= 100; z /= 108.883; - x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); - y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); - z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + x = x > 0.008856 ? Math.pow(x, 1 / 3) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? Math.pow(y, 1 / 3) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? Math.pow(z, 1 / 3) : (7.787 * z) + (16 / 116); - const l = (116 * y) - 16; - const a = 500 * (x - y); - const b = 200 * (y - z); + l = (116 * y) - 16; + a = 500 * (x - y); + b = 200 * (y - z); return [l, a, b]; }; convert.lab.xyz = function (lab) { - const l = lab[0]; - const a = lab[1]; - const b = lab[2]; - let x; - let y; - let z; + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var x; + var y; + var z; y = (l + 16) / 116; x = a / 500 + y; z = y - b / 200; - const y2 = y ** 3; - const x2 = x ** 3; - const z2 = z ** 3; + var y2 = Math.pow(y, 3); + var x2 = Math.pow(x, 3); + var z2 = Math.pow(z, 3); y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; @@ -486,38 +507,45 @@ convert.lab.xyz = function (lab) { }; convert.lab.lch = function (lab) { - const l = lab[0]; - const a = lab[1]; - const b = lab[2]; - let h; - - const hr = Math.atan2(b, a); + var l = lab[0]; + var a = lab[1]; + var b = lab[2]; + var hr; + var h; + var c; + + hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } - const c = Math.sqrt(a * a + b * b); + c = Math.sqrt(a * a + b * b); return [l, c, h]; }; convert.lch.lab = function (lch) { - const l = lch[0]; - const c = lch[1]; - const h = lch[2]; + var l = lch[0]; + var c = lch[1]; + var h = lch[2]; + var a; + var b; + var hr; - const hr = h / 360 * 2 * Math.PI; - const a = c * Math.cos(hr); - const b = c * Math.sin(hr); + hr = h / 360 * 2 * Math.PI; + a = c * Math.cos(hr); + b = c * Math.sin(hr); return [l, a, b]; }; -convert.rgb.ansi16 = function (args, saturation = null) { - const [r, g, b] = args; - let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization +convert.rgb.ansi16 = function (args) { + var r = args[0]; + var g = args[1]; + var b = args[2]; + var value = 1 in arguments ? arguments[1] : convert.rgb.hsv(args)[2]; // hsv -> ansi16 optimization value = Math.round(value / 50); @@ -525,7 +553,7 @@ convert.rgb.ansi16 = function (args, saturation = null) { return 30; } - let ansi = 30 + var ansi = 30 + ((Math.round(b / 255) << 2) | (Math.round(g / 255) << 1) | Math.round(r / 255)); @@ -538,17 +566,17 @@ convert.rgb.ansi16 = function (args, saturation = null) { }; convert.hsv.ansi16 = function (args) { - // Optimization here; we already know the value and don't need to get + // optimization here; we already know the value and don't need to get // it converted for us. return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); }; convert.rgb.ansi256 = function (args) { - const r = args[0]; - const g = args[1]; - const b = args[2]; + var r = args[0]; + var g = args[1]; + var b = args[2]; - // We use the extended greyscale palette here, with the exception of + // we use the extended greyscale palette here, with the exception of // black and white. normal palette only has 4 greyscale shades. if (r === g && g === b) { if (r < 8) { @@ -562,7 +590,7 @@ convert.rgb.ansi256 = function (args) { return Math.round(((r - 8) / 247) * 24) + 232; } - const ansi = 16 + var ansi = 16 + (36 * Math.round(r / 255 * 5)) + (6 * Math.round(g / 255 * 5)) + Math.round(b / 255 * 5); @@ -571,9 +599,9 @@ convert.rgb.ansi256 = function (args) { }; convert.ansi16.rgb = function (args) { - let color = args % 10; + var color = args % 10; - // Handle greyscale + // handle greyscale if (color === 0 || color === 7) { if (args > 50) { color += 3.5; @@ -584,71 +612,71 @@ convert.ansi16.rgb = function (args) { return [color, color, color]; } - const mult = (~~(args > 50) + 1) * 0.5; - const r = ((color & 1) * mult) * 255; - const g = (((color >> 1) & 1) * mult) * 255; - const b = (((color >> 2) & 1) * mult) * 255; + var mult = (~~(args > 50) + 1) * 0.5; + var r = ((color & 1) * mult) * 255; + var g = (((color >> 1) & 1) * mult) * 255; + var b = (((color >> 2) & 1) * mult) * 255; return [r, g, b]; }; convert.ansi256.rgb = function (args) { - // Handle greyscale + // handle greyscale if (args >= 232) { - const c = (args - 232) * 10 + 8; + var c = (args - 232) * 10 + 8; return [c, c, c]; } args -= 16; - let rem; - const r = Math.floor(args / 36) / 5 * 255; - const g = Math.floor((rem = args % 36) / 6) / 5 * 255; - const b = (rem % 6) / 5 * 255; + var rem; + var r = Math.floor(args / 36) / 5 * 255; + var g = Math.floor((rem = args % 36) / 6) / 5 * 255; + var b = (rem % 6) / 5 * 255; return [r, g, b]; }; convert.rgb.hex = function (args) { - const integer = ((Math.round(args[0]) & 0xFF) << 16) + var integer = ((Math.round(args[0]) & 0xFF) << 16) + ((Math.round(args[1]) & 0xFF) << 8) + (Math.round(args[2]) & 0xFF); - const string = integer.toString(16).toUpperCase(); + var string = integer.toString(16).toUpperCase(); return '000000'.substring(string.length) + string; }; convert.hex.rgb = function (args) { - const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + var match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); if (!match) { return [0, 0, 0]; } - let colorString = match[0]; + var colorString = match[0]; if (match[0].length === 3) { - colorString = colorString.split('').map(char => { + colorString = colorString.split('').map(function (char) { return char + char; }).join(''); } - const integer = parseInt(colorString, 16); - const r = (integer >> 16) & 0xFF; - const g = (integer >> 8) & 0xFF; - const b = integer & 0xFF; + var integer = parseInt(colorString, 16); + var r = (integer >> 16) & 0xFF; + var g = (integer >> 8) & 0xFF; + var b = integer & 0xFF; return [r, g, b]; }; convert.rgb.hcg = function (rgb) { - const r = rgb[0] / 255; - const g = rgb[1] / 255; - const b = rgb[2] / 255; - const max = Math.max(Math.max(r, g), b); - const min = Math.min(Math.min(r, g), b); - const chroma = (max - min); - let grayscale; - let hue; + var r = rgb[0] / 255; + var g = rgb[1] / 255; + var b = rgb[2] / 255; + var max = Math.max(Math.max(r, g), b); + var min = Math.min(Math.min(r, g), b); + var chroma = (max - min); + var grayscale; + var hue; if (chroma < 1) { grayscale = min / (1 - chroma); @@ -665,7 +693,7 @@ convert.rgb.hcg = function (rgb) { if (max === g) { hue = 2 + (b - r) / chroma; } else { - hue = 4 + (r - g) / chroma; + hue = 4 + (r - g) / chroma + 4; } hue /= 6; @@ -675,12 +703,17 @@ convert.rgb.hcg = function (rgb) { }; convert.hsl.hcg = function (hsl) { - const s = hsl[1] / 100; - const l = hsl[2] / 100; + var s = hsl[1] / 100; + var l = hsl[2] / 100; + var c = 1; + var f = 0; - const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); + if (l < 0.5) { + c = 2.0 * s * l; + } else { + c = 2.0 * s * (1.0 - l); + } - let f = 0; if (c < 1.0) { f = (l - 0.5 * c) / (1.0 - c); } @@ -689,11 +722,11 @@ convert.hsl.hcg = function (hsl) { }; convert.hsv.hcg = function (hsv) { - const s = hsv[1] / 100; - const v = hsv[2] / 100; + var s = hsv[1] / 100; + var v = hsv[2] / 100; - const c = s * v; - let f = 0; + var c = s * v; + var f = 0; if (c < 1.0) { f = (v - c) / (1 - c); @@ -703,21 +736,20 @@ convert.hsv.hcg = function (hsv) { }; convert.hcg.rgb = function (hcg) { - const h = hcg[0] / 360; - const c = hcg[1] / 100; - const g = hcg[2] / 100; + var h = hcg[0] / 360; + var c = hcg[1] / 100; + var g = hcg[2] / 100; if (c === 0.0) { return [g * 255, g * 255, g * 255]; } - const pure = [0, 0, 0]; - const hi = (h % 1) * 6; - const v = hi % 1; - const w = 1 - v; - let mg = 0; + var pure = [0, 0, 0]; + var hi = (h % 1) * 6; + var v = hi % 1; + var w = 1 - v; + var mg = 0; - /* eslint-disable max-statements-per-line */ switch (Math.floor(hi)) { case 0: pure[0] = 1; pure[1] = v; pure[2] = 0; break; @@ -732,7 +764,6 @@ convert.hcg.rgb = function (hcg) { default: pure[0] = 1; pure[1] = 0; pure[2] = w; } - /* eslint-enable max-statements-per-line */ mg = (1.0 - c) * g; @@ -744,11 +775,11 @@ convert.hcg.rgb = function (hcg) { }; convert.hcg.hsv = function (hcg) { - const c = hcg[1] / 100; - const g = hcg[2] / 100; + var c = hcg[1] / 100; + var g = hcg[2] / 100; - const v = c + g * (1.0 - c); - let f = 0; + var v = c + g * (1.0 - c); + var f = 0; if (v > 0.0) { f = c / v; @@ -758,11 +789,11 @@ convert.hcg.hsv = function (hcg) { }; convert.hcg.hsl = function (hcg) { - const c = hcg[1] / 100; - const g = hcg[2] / 100; + var c = hcg[1] / 100; + var g = hcg[2] / 100; - const l = g * (1.0 - c) + 0.5 * c; - let s = 0; + var l = g * (1.0 - c) + 0.5 * c; + var s = 0; if (l > 0.0 && l < 0.5) { s = c / (2 * l); @@ -775,18 +806,18 @@ convert.hcg.hsl = function (hcg) { }; convert.hcg.hwb = function (hcg) { - const c = hcg[1] / 100; - const g = hcg[2] / 100; - const v = c + g * (1.0 - c); + var c = hcg[1] / 100; + var g = hcg[2] / 100; + var v = c + g * (1.0 - c); return [hcg[0], (v - c) * 100, (1 - v) * 100]; }; convert.hwb.hcg = function (hwb) { - const w = hwb[1] / 100; - const b = hwb[2] / 100; - const v = 1 - b; - const c = v - w; - let g = 0; + var w = hwb[1] / 100; + var b = hwb[2] / 100; + var v = 1 - b; + var c = v - w; + var g = 0; if (c < 1) { g = (v - c) / (1 - c); @@ -807,12 +838,10 @@ convert.gray.rgb = function (args) { return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; }; -convert.gray.hsl = function (args) { +convert.gray.hsl = convert.gray.hsv = function (args) { return [0, 0, args[0]]; }; -convert.gray.hsv = convert.gray.hsl; - convert.gray.hwb = function (gray) { return [0, 100, gray[0]]; }; @@ -826,14 +855,14 @@ convert.gray.lab = function (gray) { }; convert.gray.hex = function (gray) { - const val = Math.round(gray[0] / 100 * 255) & 0xFF; - const integer = (val << 16) + (val << 8) + val; + var val = Math.round(gray[0] / 100 * 255) & 0xFF; + var integer = (val << 16) + (val << 8) + val; - const string = integer.toString(16).toUpperCase(); + var string = integer.toString(16).toUpperCase(); return '000000'.substring(string.length) + string; }; convert.rgb.gray = function (rgb) { - const val = (rgb[0] + rgb[1] + rgb[2]) / 3; + var val = (rgb[0] + rgb[1] + rgb[2]) / 3; return [val / 255 * 100]; }; diff --git a/tools/node_modules/eslint/node_modules/color-convert/index.js b/tools/node_modules/eslint/node_modules/color-convert/index.js index b648e5737be616..e65b5d775da353 100644 --- a/tools/node_modules/eslint/node_modules/color-convert/index.js +++ b/tools/node_modules/eslint/node_modules/color-convert/index.js @@ -1,25 +1,24 @@ -const conversions = require('./conversions'); -const route = require('./route'); +var conversions = require('./conversions'); +var route = require('./route'); -const convert = {}; +var convert = {}; -const models = Object.keys(conversions); +var models = Object.keys(conversions); function wrapRaw(fn) { - const wrappedFn = function (...args) { - const arg0 = args[0]; - if (arg0 === undefined || arg0 === null) { - return arg0; + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; } - if (arg0.length > 1) { - args = arg0; + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); } return fn(args); }; - // Preserve .conversion property if there is one + // preserve .conversion property if there is one if ('conversion' in fn) { wrappedFn.conversion = fn.conversion; } @@ -28,24 +27,22 @@ function wrapRaw(fn) { } function wrapRounded(fn) { - const wrappedFn = function (...args) { - const arg0 = args[0]; - - if (arg0 === undefined || arg0 === null) { - return arg0; + var wrappedFn = function (args) { + if (args === undefined || args === null) { + return args; } - if (arg0.length > 1) { - args = arg0; + if (arguments.length > 1) { + args = Array.prototype.slice.call(arguments); } - const result = fn(args); + var result = fn(args); - // We're assuming the result is an array here. + // we're assuming the result is an array here. // see notice in conversions.js; don't use box types // in conversion functions. if (typeof result === 'object') { - for (let len = result.length, i = 0; i < len; i++) { + for (var len = result.length, i = 0; i < len; i++) { result[i] = Math.round(result[i]); } } @@ -53,7 +50,7 @@ function wrapRounded(fn) { return result; }; - // Preserve .conversion property if there is one + // preserve .conversion property if there is one if ('conversion' in fn) { wrappedFn.conversion = fn.conversion; } @@ -61,17 +58,17 @@ function wrapRounded(fn) { return wrappedFn; } -models.forEach(fromModel => { +models.forEach(function (fromModel) { convert[fromModel] = {}; Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); - const routes = route(fromModel); - const routeModels = Object.keys(routes); + var routes = route(fromModel); + var routeModels = Object.keys(routes); - routeModels.forEach(toModel => { - const fn = routes[toModel]; + routeModels.forEach(function (toModel) { + var fn = routes[toModel]; convert[fromModel][toModel] = wrapRounded(fn); convert[fromModel][toModel].raw = wrapRaw(fn); diff --git a/tools/node_modules/eslint/node_modules/color-convert/package.json b/tools/node_modules/eslint/node_modules/color-convert/package.json index 6e48000c7c98fd..991f69308c29f4 100644 --- a/tools/node_modules/eslint/node_modules/color-convert/package.json +++ b/tools/node_modules/eslint/node_modules/color-convert/package.json @@ -1,17 +1,28 @@ { - "name": "color-convert", - "description": "Plain color conversion functions", - "version": "2.0.1", - "author": "Heather Arthur <fayearthur@gmail.com>", - "license": "MIT", - "repository": "Qix-/color-convert", - "scripts": { - "pretest": "xo", - "test": "node test/basic.js" + "author": { + "name": "Heather Arthur", + "email": "fayearthur@gmail.com" + }, + "bugs": { + "url": "https://github.com/Qix-/color-convert/issues" + }, + "bundleDependencies": false, + "dependencies": { + "color-name": "1.1.3" }, - "engines": { - "node": ">=7.0.0" + "deprecated": false, + "description": "Plain color conversion functions", + "devDependencies": { + "chalk": "1.1.1", + "xo": "0.11.2" }, + "files": [ + "index.js", + "conversions.js", + "css-keywords.js", + "route.js" + ], + "homepage": "https://github.com/Qix-/color-convert#readme", "keywords": [ "color", "colour", @@ -26,23 +37,22 @@ "ansi", "ansi16" ], - "files": [ - "index.js", - "conversions.js", - "route.js" - ], + "license": "MIT", + "name": "color-convert", + "repository": { + "type": "git", + "url": "git+https://github.com/Qix-/color-convert.git" + }, + "scripts": { + "pretest": "xo", + "test": "node test/basic.js" + }, + "version": "1.9.3", "xo": { "rules": { "default-case": 0, "no-inline-comments": 0, "operator-linebreak": 0 } - }, - "devDependencies": { - "chalk": "^2.4.2", - "xo": "^0.24.0" - }, - "dependencies": { - "color-name": "~1.1.4" } -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/color-convert/route.js b/tools/node_modules/eslint/node_modules/color-convert/route.js index 1a08521b5a0017..0a1fdea689e2a7 100644 --- a/tools/node_modules/eslint/node_modules/color-convert/route.js +++ b/tools/node_modules/eslint/node_modules/color-convert/route.js @@ -1,7 +1,7 @@ -const conversions = require('./conversions'); +var conversions = require('./conversions'); /* - This function routes a model to all other models. + this function routes a model to all other models. all functions that are routed have a property `.conversion` attached to the returned synthetic function. This property is an array @@ -12,11 +12,11 @@ const conversions = require('./conversions'); */ function buildGraph() { - const graph = {}; + var graph = {}; // https://jsperf.com/object-keys-vs-for-in-with-closure/3 - const models = Object.keys(conversions); + var models = Object.keys(conversions); - for (let len = models.length, i = 0; i < len; i++) { + for (var len = models.length, i = 0; i < len; i++) { graph[models[i]] = { // http://jsperf.com/1-vs-infinity // micro-opt, but this is simple. @@ -30,18 +30,18 @@ function buildGraph() { // https://en.wikipedia.org/wiki/Breadth-first_search function deriveBFS(fromModel) { - const graph = buildGraph(); - const queue = [fromModel]; // Unshift -> queue -> pop + var graph = buildGraph(); + var queue = [fromModel]; // unshift -> queue -> pop graph[fromModel].distance = 0; while (queue.length) { - const current = queue.pop(); - const adjacents = Object.keys(conversions[current]); + var current = queue.pop(); + var adjacents = Object.keys(conversions[current]); - for (let len = adjacents.length, i = 0; i < len; i++) { - const adjacent = adjacents[i]; - const node = graph[adjacent]; + for (var len = adjacents.length, i = 0; i < len; i++) { + var adjacent = adjacents[i]; + var node = graph[adjacent]; if (node.distance === -1) { node.distance = graph[current].distance + 1; @@ -61,10 +61,10 @@ function link(from, to) { } function wrapConversion(toModel, graph) { - const path = [graph[toModel].parent, toModel]; - let fn = conversions[graph[toModel].parent][toModel]; + var path = [graph[toModel].parent, toModel]; + var fn = conversions[graph[toModel].parent][toModel]; - let cur = graph[toModel].parent; + var cur = graph[toModel].parent; while (graph[cur].parent) { path.unshift(graph[cur].parent); fn = link(conversions[graph[cur].parent][cur], fn); @@ -76,16 +76,16 @@ function wrapConversion(toModel, graph) { } module.exports = function (fromModel) { - const graph = deriveBFS(fromModel); - const conversion = {}; + var graph = deriveBFS(fromModel); + var conversion = {}; - const models = Object.keys(graph); - for (let len = models.length, i = 0; i < len; i++) { - const toModel = models[i]; - const node = graph[toModel]; + var models = Object.keys(graph); + for (var len = models.length, i = 0; i < len; i++) { + var toModel = models[i]; + var node = graph[toModel]; if (node.parent === null) { - // No possible conversion, or this node is the source model. + // no possible conversion, or this node is the source model. continue; } diff --git a/tools/node_modules/eslint/node_modules/color-name/package.json b/tools/node_modules/eslint/node_modules/color-name/package.json index 782dd82878030a..87d0e72ec56239 100644 --- a/tools/node_modules/eslint/node_modules/color-name/package.json +++ b/tools/node_modules/eslint/node_modules/color-name/package.json @@ -1,28 +1,30 @@ -{ - "name": "color-name", - "version": "1.1.4", - "description": "A list of color names and its values", - "main": "index.js", - "files": [ - "index.js" - ], - "scripts": { - "test": "node test.js" - }, - "repository": { - "type": "git", - "url": "git@github.com:colorjs/color-name.git" - }, - "keywords": [ - "color-name", - "color", - "color-keyword", - "keyword" - ], - "author": "DY <dfcreative@gmail.com>", - "license": "MIT", - "bugs": { - "url": "https://github.com/colorjs/color-name/issues" - }, - "homepage": "https://github.com/colorjs/color-name" -} +{ + "author": { + "name": "DY", + "email": "dfcreative@gmail.com" + }, + "bugs": { + "url": "https://github.com/dfcreative/color-name/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "A list of color names and its values", + "homepage": "https://github.com/dfcreative/color-name", + "keywords": [ + "color-name", + "color", + "color-keyword", + "keyword" + ], + "license": "MIT", + "main": "index.js", + "name": "color-name", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/dfcreative/color-name.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.1.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/concat-map/package.json b/tools/node_modules/eslint/node_modules/concat-map/package.json index d3640e6b027b9e..4abe42193c4cc4 100644 --- a/tools/node_modules/eslint/node_modules/concat-map/package.json +++ b/tools/node_modules/eslint/node_modules/concat-map/package.json @@ -1,43 +1,65 @@ { - "name" : "concat-map", - "description" : "concatenative mapdashery", - "version" : "0.0.1", - "repository" : { - "type" : "git", - "url" : "git://github.com/substack/node-concat-map.git" - }, - "main" : "index.js", - "keywords" : [ - "concat", - "concatMap", - "map", - "functional", - "higher-order" - ], - "directories" : { - "example" : "example", - "test" : "test" - }, - "scripts" : { - "test" : "tape test/*.js" - }, - "devDependencies" : { - "tape" : "~2.4.0" - }, - "license" : "MIT", - "author" : { - "name" : "James Halliday", - "email" : "mail@substack.net", - "url" : "http://substack.net" - }, - "testling" : { - "files" : "test/*.js", - "browsers" : { - "ie" : [ 6, 7, 8, 9 ], - "ff" : [ 3.5, 10, 15.0 ], - "chrome" : [ 10, 22 ], - "safari" : [ 5.1 ], - "opera" : [ 12 ] - } + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/substack/node-concat-map/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "concatenative mapdashery", + "devDependencies": { + "tape": "~2.4.0" + }, + "directories": { + "example": "example", + "test": "test" + }, + "homepage": "https://github.com/substack/node-concat-map#readme", + "keywords": [ + "concat", + "concatMap", + "map", + "functional", + "higher-order" + ], + "license": "MIT", + "main": "index.js", + "name": "concat-map", + "repository": { + "type": "git", + "url": "git://github.com/substack/node-concat-map.git" + }, + "scripts": { + "test": "tape test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": { + "ie": [ + 6, + 7, + 8, + 9 + ], + "ff": [ + 3.5, + 10, + 15 + ], + "chrome": [ + 10, + 22 + ], + "safari": [ + 5.1 + ], + "opera": [ + 12 + ] } -} + }, + "version": "0.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/cross-spawn/package.json b/tools/node_modules/eslint/node_modules/cross-spawn/package.json index 232ff97e04b213..5ed16e25e312a4 100644 --- a/tools/node_modules/eslint/node_modules/cross-spawn/package.json +++ b/tools/node_modules/eslint/node_modules/cross-spawn/package.json @@ -1,47 +1,12 @@ { - "name": "cross-spawn", - "version": "7.0.3", - "description": "Cross platform child_process#spawn and child_process#spawnSync", - "keywords": [ - "spawn", - "spawnSync", - "windows", - "cross-platform", - "path-ext", - "shebang", - "cmd", - "execute" - ], - "author": "André Cruz <andre@moxy.studio>", - "homepage": "https://github.com/moxystudio/node-cross-spawn", - "repository": { - "type": "git", - "url": "git@github.com:moxystudio/node-cross-spawn.git" - }, - "license": "MIT", - "main": "index.js", - "files": [ - "lib" - ], - "scripts": { - "lint": "eslint .", - "test": "jest --env node --coverage", - "prerelease": "npm t && npm run lint", - "release": "standard-version", - "postrelease": "git push --follow-tags origin HEAD && npm publish" + "author": { + "name": "André Cruz", + "email": "andre@moxy.studio" }, - "husky": { - "hooks": { - "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "*.js": [ - "eslint --fix", - "git add" - ] + "bugs": { + "url": "https://github.com/moxystudio/node-cross-spawn/issues" }, + "bundleDependencies": false, "commitlint": { "extends": [ "@commitlint/config-conventional" @@ -52,6 +17,8 @@ "shebang-command": "^2.0.0", "which": "^2.0.1" }, + "deprecated": false, + "description": "Cross platform child_process#spawn and child_process#spawnSync", "devDependencies": { "@commitlint/cli": "^8.1.0", "@commitlint/config-conventional": "^8.1.0", @@ -69,5 +36,46 @@ }, "engines": { "node": ">= 8" - } -} + }, + "files": [ + "lib" + ], + "homepage": "https://github.com/moxystudio/node-cross-spawn", + "husky": { + "hooks": { + "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", + "pre-commit": "lint-staged" + } + }, + "keywords": [ + "spawn", + "spawnSync", + "windows", + "cross-platform", + "path-ext", + "shebang", + "cmd", + "execute" + ], + "license": "MIT", + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ] + }, + "main": "index.js", + "name": "cross-spawn", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/moxystudio/node-cross-spawn.git" + }, + "scripts": { + "lint": "eslint .", + "postrelease": "git push --follow-tags origin HEAD && npm publish", + "prerelease": "npm t && npm run lint", + "release": "standard-version", + "test": "jest --env node --coverage" + }, + "version": "7.0.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/debug/package.json b/tools/node_modules/eslint/node_modules/debug/package.json index da809d2b8d28b2..2b1dece47b8c9d 100644 --- a/tools/node_modules/eslint/node_modules/debug/package.json +++ b/tools/node_modules/eslint/node_modules/debug/package.json @@ -1,38 +1,33 @@ { - "name": "debug", - "version": "4.3.1", - "repository": { - "type": "git", - "url": "git://github.com/visionmedia/debug.git" + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" }, - "description": "small debugging utility", - "keywords": [ - "debug", - "log", - "debugger" - ], - "files": [ - "src", - "LICENSE", - "README.md" - ], - "author": "TJ Holowaychuk <tj@vision-media.ca>", + "browser": "./src/browser.js", + "bugs": { + "url": "https://github.com/visionmedia/debug/issues" + }, + "bundleDependencies": false, "contributors": [ - "Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)", - "Andrew Rhyne <rhyneandrew@gmail.com>", - "Josh Junon <josh@junon.me>" + { + "name": "Nathan Rajlich", + "email": "nathan@tootallnate.net", + "url": "http://n8.io" + }, + { + "name": "Andrew Rhyne", + "email": "rhyneandrew@gmail.com" + }, + { + "name": "Josh Junon", + "email": "josh@junon.me" + } ], - "license": "MIT", - "scripts": { - "lint": "xo", - "test": "npm run test:node && npm run test:browser && npm run lint", - "test:node": "istanbul cover _mocha -- test.js", - "test:browser": "karma start --single-run", - "test:coverage": "cat ./coverage/lcov.info | coveralls" - }, "dependencies": { "ms": "2.1.2" }, + "deprecated": false, + "description": "small debugging utility", "devDependencies": { "brfs": "^2.0.1", "browserify": "^16.2.3", @@ -46,14 +41,38 @@ "mocha-lcov-reporter": "^1.2.0", "xo": "^0.23.0" }, + "engines": { + "node": ">=6.0" + }, + "files": [ + "src", + "LICENSE", + "README.md" + ], + "homepage": "https://github.com/visionmedia/debug#readme", + "keywords": [ + "debug", + "log", + "debugger" + ], + "license": "MIT", + "main": "./src/index.js", + "name": "debug", "peerDependenciesMeta": { "supports-color": { "optional": true } }, - "main": "./src/index.js", - "browser": "./src/browser.js", - "engines": { - "node": ">=6.0" - } -} + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/debug.git" + }, + "scripts": { + "lint": "xo", + "test": "npm run test:node && npm run test:browser && npm run lint", + "test:browser": "karma start --single-run", + "test:coverage": "cat ./coverage/lcov.info | coveralls", + "test:node": "istanbul cover _mocha -- test.js" + }, + "version": "4.3.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/deep-is/package.json b/tools/node_modules/eslint/node_modules/deep-is/package.json index 63628f0d015c9e..c921e8a0e8b105 100644 --- a/tools/node_modules/eslint/node_modules/deep-is/package.json +++ b/tools/node_modules/eslint/node_modules/deep-is/package.json @@ -1,37 +1,42 @@ { - "name": "deep-is", - "version": "0.1.3", - "description": "node's assert.deepEqual algorithm except for NaN being equal to NaN", - "main": "index.js", - "directories": { - "lib": ".", - "example": "example", - "test": "test" + "author": { + "name": "Thorsten Lorenz", + "email": "thlorenz@gmx.de", + "url": "http://thlorenz.com" }, - "scripts": { - "test": "tape test/*.js" + "bugs": { + "url": "https://github.com/thlorenz/deep-is/issues" }, + "bundleDependencies": false, + "deprecated": false, + "description": "node's assert.deepEqual algorithm except for NaN being equal to NaN", "devDependencies": { "tape": "~1.0.2" }, - "repository": { - "type": "git", - "url": "http://github.com/thlorenz/deep-is.git" + "directories": { + "lib": ".", + "example": "example", + "test": "test" }, + "homepage": "https://github.com/thlorenz/deep-is#readme", "keywords": [ "equality", "equal", "compare" ], - "author": { - "name": "Thorsten Lorenz", - "email": "thlorenz@gmx.de", - "url": "http://thlorenz.com" - }, "license": { "type": "MIT", "url": "https://github.com/thlorenz/deep-is/blob/master/LICENSE" }, + "main": "index.js", + "name": "deep-is", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/thlorenz/deep-is.git" + }, + "scripts": { + "test": "tape test/*.js" + }, "testling": { "files": "test/*.js", "browsers": { @@ -57,5 +62,6 @@ 12 ] } - } -} + }, + "version": "0.1.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/doctrine/package.json b/tools/node_modules/eslint/node_modules/doctrine/package.json index b66099863d4479..b9a378a090734c 100644 --- a/tools/node_modules/eslint/node_modules/doctrine/package.json +++ b/tools/node_modules/eslint/node_modules/doctrine/package.json @@ -1,58 +1,66 @@ { - "name": "doctrine", + "bugs": { + "url": "https://github.com/eslint/doctrine/issues" + }, + "bundleDependencies": false, + "dependencies": { + "esutils": "^2.0.2" + }, + "deprecated": false, "description": "JSDoc parser", - "homepage": "https://github.com/eslint/doctrine", - "main": "lib/doctrine.js", - "version": "3.0.0", - "engines": { - "node": ">=6.0.0" + "devDependencies": { + "coveralls": "^3.0.1", + "dateformat": "^1.0.11", + "eslint": "^1.10.3", + "eslint-release": "^1.0.0", + "linefix": "^0.1.1", + "mocha": "^3.4.2", + "npm-license": "^0.3.1", + "nyc": "^10.3.2", + "semver": "^5.0.3", + "shelljs": "^0.5.3", + "shelljs-nodecli": "^0.1.1", + "should": "^5.0.1" }, "directories": { "lib": "./lib" }, + "engines": { + "node": ">=6.0.0" + }, "files": [ "lib" ], + "homepage": "https://github.com/eslint/doctrine", + "license": "Apache-2.0", + "main": "lib/doctrine.js", "maintainers": [ { "name": "Nicholas C. Zakas", "email": "nicholas+npm@nczconsulting.com", - "web": "https://www.nczonline.net" + "url": "https://www.nczonline.net" }, { "name": "Yusuke Suzuki", "email": "utatane.tea@gmail.com", - "web": "https://github.com/Constellation" + "url": "https://github.com/Constellation" } ], - "repository": "eslint/doctrine", - "devDependencies": { - "coveralls": "^3.0.1", - "dateformat": "^1.0.11", - "eslint": "^1.10.3", - "eslint-release": "^1.0.0", - "linefix": "^0.1.1", - "mocha": "^3.4.2", - "npm-license": "^0.3.1", - "nyc": "^10.3.2", - "semver": "^5.0.3", - "shelljs": "^0.5.3", - "shelljs-nodecli": "^0.1.1", - "should": "^5.0.1" + "name": "doctrine", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/doctrine.git" }, - "license": "Apache-2.0", "scripts": { - "pretest": "npm run lint", - "test": "nyc mocha", "coveralls": "nyc report --reporter=text-lcov | coveralls", - "lint": "eslint lib/", - "generate-release": "eslint-generate-release", "generate-alpharelease": "eslint-generate-prerelease alpha", "generate-betarelease": "eslint-generate-prerelease beta", "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" + "generate-release": "eslint-generate-release", + "lint": "eslint lib/", + "pretest": "npm run lint", + "publish-release": "eslint-publish-release", + "test": "nyc mocha" }, - "dependencies": { - "esutils": "^2.0.2" - } -} + "version": "3.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/emoji-regex/package.json b/tools/node_modules/eslint/node_modules/emoji-regex/package.json index 6d323528292b00..eecc5eb318cd0a 100644 --- a/tools/node_modules/eslint/node_modules/emoji-regex/package.json +++ b/tools/node_modules/eslint/node_modules/emoji-regex/package.json @@ -1,10 +1,32 @@ { - "name": "emoji-regex", - "version": "8.0.0", + "author": { + "name": "Mathias Bynens", + "url": "https://mathiasbynens.be/" + }, + "bugs": { + "url": "https://github.com/mathiasbynens/emoji-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "A regular expression to match all Emoji-only symbols as per the Unicode Standard.", + "devDependencies": { + "@babel/cli": "^7.2.3", + "@babel/core": "^7.3.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", + "@babel/preset-env": "^7.3.4", + "mocha": "^6.0.2", + "regexgen": "^1.3.0", + "unicode-12.0.0": "^0.7.9" + }, + "files": [ + "LICENSE-MIT.txt", + "index.js", + "index.d.ts", + "text.js", + "es2015/index.js", + "es2015/text.js" + ], "homepage": "https://mths.be/emoji-regex", - "main": "index.js", - "types": "index.d.ts", "keywords": [ "unicode", "regex", @@ -16,35 +38,17 @@ "emoji" ], "license": "MIT", - "author": { - "name": "Mathias Bynens", - "url": "https://mathiasbynens.be/" - }, + "main": "index.js", + "name": "emoji-regex", "repository": { "type": "git", - "url": "https://github.com/mathiasbynens/emoji-regex.git" + "url": "git+https://github.com/mathiasbynens/emoji-regex.git" }, - "bugs": "https://github.com/mathiasbynens/emoji-regex/issues", - "files": [ - "LICENSE-MIT.txt", - "index.js", - "index.d.ts", - "text.js", - "es2015/index.js", - "es2015/text.js" - ], "scripts": { "build": "rm -rf -- es2015; babel src -d .; NODE_ENV=es2015 babel src -d ./es2015; node script/inject-sequences.js", "test": "mocha", "test:watch": "npm run test -- --watch" }, - "devDependencies": { - "@babel/cli": "^7.2.3", - "@babel/core": "^7.3.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.2.0", - "@babel/preset-env": "^7.3.4", - "mocha": "^6.0.2", - "regexgen": "^1.3.0", - "unicode-12.0.0": "^0.7.9" - } -} + "types": "index.d.ts", + "version": "8.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/enquirer/package.json b/tools/node_modules/eslint/node_modules/enquirer/package.json index 1d986c735661ab..c6d3a5ae0b8d7c 100644 --- a/tools/node_modules/eslint/node_modules/enquirer/package.json +++ b/tools/node_modules/eslint/node_modules/enquirer/package.json @@ -1,34 +1,27 @@ { - "name": "enquirer", - "description": "Stylish, intuitive and user-friendly prompt system. Fast and lightweight enough for small projects, powerful and extensible enough for the most advanced use cases.", - "version": "2.3.6", - "homepage": "https://github.com/enquirer/enquirer", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "contributors": [ - "Brian Woodward (https://twitter.com/doowb)", - "Jon Schlinkert (http://twitter.com/jonschlinkert)" - ], - "repository": "enquirer/enquirer", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, "bugs": { "url": "https://github.com/enquirer/enquirer/issues" }, - "license": "MIT", - "files": [ - "index.js", - "index.d.ts", - "lib" + "bundleDependencies": false, + "contributors": [ + { + "name": "Brian Woodward", + "url": "https://twitter.com/doowb" + }, + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + } ], - "main": "index.js", - "engines": { - "node": ">=8.6" - }, - "scripts": { - "test": "mocha && tsc -p ./test/types", - "cover": "nyc --reporter=text --reporter=html mocha" - }, "dependencies": { "ansi-colors": "^4.1.1" }, + "deprecated": false, + "description": "Stylish, intuitive and user-friendly prompt system. Fast and lightweight enough for small projects, powerful and extensible enough for the most advanced use cases.", "devDependencies": { "@types/node": "^8", "gulp-format-md": "^2.0.0", @@ -39,6 +32,15 @@ "time-require": "github:jonschlinkert/time-require", "typescript": "^3.1.6" }, + "engines": { + "node": ">=8.6" + }, + "files": [ + "index.js", + "index.d.ts", + "lib" + ], + "homepage": "https://github.com/enquirer/enquirer", "keywords": [ "answer", "answers", @@ -77,6 +79,7 @@ "yo", "zsh" ], + "license": "MIT", "lintDeps": { "devDependencies": { "files": { @@ -88,6 +91,16 @@ } } }, + "main": "index.js", + "name": "enquirer", + "repository": { + "type": "git", + "url": "git+https://github.com/enquirer/enquirer.git" + }, + "scripts": { + "cover": "nyc --reporter=text --reporter=html mocha", + "test": "mocha && tsc -p ./test/types" + }, "verb": { "toc": false, "layout": false, @@ -107,5 +120,6 @@ "inquirer", "prompt-skeleton" ] - } -} + }, + "version": "2.3.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/escape-string-regexp/package.json b/tools/node_modules/eslint/node_modules/escape-string-regexp/package.json index f307df34a232c0..ced6973347e903 100644 --- a/tools/node_modules/eslint/node_modules/escape-string-regexp/package.json +++ b/tools/node_modules/eslint/node_modules/escape-string-regexp/package.json @@ -1,27 +1,26 @@ { - "name": "escape-string-regexp", - "version": "1.0.5", - "description": "Escape RegExp special characters", - "license": "MIT", - "repository": "sindresorhus/escape-string-regexp", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "sindresorhus.com" }, - "maintainers": [ - "Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)", - "Joshua Boy Nicolai Appelman <joshua@jbna.nl> (jbna.nl)" - ], + "bugs": { + "url": "https://github.com/sindresorhus/escape-string-regexp/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Escape RegExp special characters", + "devDependencies": { + "ava": "*", + "xo": "*" + }, "engines": { "node": ">=0.8.0" }, - "scripts": { - "test": "xo && ava" - }, "files": [ "index.js" ], + "homepage": "https://github.com/sindresorhus/escape-string-regexp#readme", "keywords": [ "escape", "regex", @@ -34,8 +33,26 @@ "special", "characters" ], - "devDependencies": { - "ava": "*", - "xo": "*" - } -} + "license": "MIT", + "maintainers": [ + { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + { + "name": "Joshua Boy Nicolai Appelman", + "email": "joshua@jbna.nl", + "url": "jbna.nl" + } + ], + "name": "escape-string-regexp", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/escape-string-regexp.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.5" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/eslint-scope/package.json b/tools/node_modules/eslint/node_modules/eslint-scope/package.json index b700b92afbea29..bc425ebbd0bea4 100644 --- a/tools/node_modules/eslint/node_modules/eslint-scope/package.json +++ b/tools/node_modules/eslint/node_modules/eslint-scope/package.json @@ -1,35 +1,14 @@ { - "name": "eslint-scope", - "description": "ECMAScript scope analyzer for ESLint", - "homepage": "http://github.com/eslint/eslint-scope", - "main": "lib/index.js", - "version": "5.1.1", - "engines": { - "node": ">=8.0.0" - }, - "repository": "eslint/eslint-scope", "bugs": { "url": "https://github.com/eslint/eslint-scope/issues" }, - "license": "BSD-2-Clause", - "scripts": { - "test": "node Makefile.js test", - "lint": "node Makefile.js lint", - "generate-release": "eslint-generate-release", - "generate-alpharelease": "eslint-generate-prerelease alpha", - "generate-betarelease": "eslint-generate-prerelease beta", - "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" - }, - "files": [ - "LICENSE", - "README.md", - "lib" - ], + "bundleDependencies": false, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" }, + "deprecated": false, + "description": "ECMAScript scope analyzer for ESLint", "devDependencies": { "@typescript-eslint/parser": "^1.11.0", "chai": "^4.2.0", @@ -44,5 +23,31 @@ "npm-license": "^0.3.3", "shelljs": "^0.8.3", "typescript": "^3.5.2" - } -} + }, + "engines": { + "node": ">=8.0.0" + }, + "files": [ + "LICENSE", + "README.md", + "lib" + ], + "homepage": "http://github.com/eslint/eslint-scope", + "license": "BSD-2-Clause", + "main": "lib/index.js", + "name": "eslint-scope", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslint-scope.git" + }, + "scripts": { + "generate-alpharelease": "eslint-generate-prerelease alpha", + "generate-betarelease": "eslint-generate-prerelease beta", + "generate-rcrelease": "eslint-generate-prerelease rc", + "generate-release": "eslint-generate-release", + "lint": "node Makefile.js lint", + "publish-release": "eslint-publish-release", + "test": "node Makefile.js test" + }, + "version": "5.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys/package.json b/tools/node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys/package.json index 63267be6437e03..2765f6d5537237 100644 --- a/tools/node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys/package.json +++ b/tools/node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys/package.json @@ -1,15 +1,15 @@ { - "name": "eslint-visitor-keys", - "version": "1.3.0", - "description": "Constants and utilities about visitor keys to traverse AST.", - "main": "lib/index.js", - "files": [ - "lib" - ], - "engines": { - "node": ">=4" + "author": { + "name": "Toru Nagashima", + "url": "https://github.com/mysticatea" }, + "bugs": { + "url": "https://github.com/eslint/eslint-visitor-keys/issues" + }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Constants and utilities about visitor keys to traverse AST.", "devDependencies": { "eslint": "^4.7.2", "eslint-config-eslint": "^4.0.0", @@ -18,23 +18,31 @@ "nyc": "^11.2.1", "opener": "^1.4.3" }, + "engines": { + "node": ">=4" + }, + "files": [ + "lib" + ], + "homepage": "https://github.com/eslint/eslint-visitor-keys#readme", + "keywords": [], + "license": "Apache-2.0", + "main": "lib/index.js", + "name": "eslint-visitor-keys", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslint-visitor-keys.git" + }, "scripts": { - "lint": "eslint lib tests/lib", - "pretest": "npm run -s lint", - "test": "nyc mocha tests/lib", "coverage": "nyc report --reporter lcov && opener coverage/lcov-report/index.html", - "generate-release": "eslint-generate-release", "generate-alpharelease": "eslint-generate-prerelease alpha", "generate-betarelease": "eslint-generate-prerelease beta", "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" - }, - "repository": "eslint/eslint-visitor-keys", - "keywords": [], - "author": "Toru Nagashima (https://github.com/mysticatea)", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/eslint/eslint-visitor-keys/issues" + "generate-release": "eslint-generate-release", + "lint": "eslint lib tests/lib", + "pretest": "npm run -s lint", + "publish-release": "eslint-publish-release", + "test": "nyc mocha tests/lib" }, - "homepage": "https://github.com/eslint/eslint-visitor-keys#readme" -} + "version": "1.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/eslint-utils/package.json b/tools/node_modules/eslint/node_modules/eslint-utils/package.json index 661a97fb097865..38ea2a071cdf83 100644 --- a/tools/node_modules/eslint/node_modules/eslint-utils/package.json +++ b/tools/node_modules/eslint/node_modules/eslint-utils/package.json @@ -1,19 +1,16 @@ { - "name": "eslint-utils", - "version": "2.1.0", - "description": "Utilities for ESLint plugins.", - "engines": { - "node": ">=6" + "author": { + "name": "Toru Nagashima" }, - "sideEffects": false, - "main": "index", - "module": "index.mjs", - "files": [ - "index.*" - ], + "bugs": { + "url": "https://github.com/mysticatea/eslint-utils/issues" + }, + "bundleDependencies": false, "dependencies": { "eslint-visitor-keys": "^1.1.0" }, + "deprecated": false, + "description": "Utilities for ESLint plugins.", "devDependencies": { "@mysticatea/eslint-plugin": "^12.0.0", "codecov": "^3.6.1", @@ -32,8 +29,26 @@ "vuepress": "^1.2.0", "warun": "^1.0.0" }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.*" + ], + "funding": "https://github.com/sponsors/mysticatea", + "homepage": "https://github.com/mysticatea/eslint-utils#readme", + "keywords": [ + "eslint" + ], + "license": "MIT", + "main": "index", + "module": "index.mjs", + "name": "eslint-utils", + "repository": { + "type": "git", + "url": "git+https://github.com/mysticatea/eslint-utils.git" + }, "scripts": { - "prebuild": "npm run -s clean", "build": "rollup -c", "clean": "rimraf .nyc_output coverage index.*", "codecov": "nyc report -r lcovonly && codecov", @@ -41,25 +56,14 @@ "docs:build": "vuepress build docs", "docs:watch": "vuepress dev docs", "lint": "eslint src test", - "test": "run-s lint build test:mocha", - "test:mocha": "nyc mocha --reporter dot \"test/*.js\"", - "preversion": "npm test && npm run -s build", "postversion": "git push && git push --tags", + "prebuild": "npm run -s clean", + "preversion": "npm test && npm run -s build", "prewatch": "npm run -s clean", + "test": "run-s lint build test:mocha", + "test:mocha": "nyc mocha --reporter dot \"test/*.js\"", "watch": "warun \"{src,test}/**/*.js\" -- npm run -s test:mocha" }, - "repository": { - "type": "git", - "url": "git+https://github.com/mysticatea/eslint-utils.git" - }, - "keywords": [ - "eslint" - ], - "author": "Toru Nagashima", - "license": "MIT", - "bugs": { - "url": "https://github.com/mysticatea/eslint-utils/issues" - }, - "homepage": "https://github.com/mysticatea/eslint-utils#readme", - "funding": "https://github.com/sponsors/mysticatea" -} + "sideEffects": false, + "version": "2.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/eslint-visitor-keys/package.json b/tools/node_modules/eslint/node_modules/eslint-visitor-keys/package.json index a3b8dd684ab145..5845760935ce9b 100644 --- a/tools/node_modules/eslint/node_modules/eslint-visitor-keys/package.json +++ b/tools/node_modules/eslint/node_modules/eslint-visitor-keys/package.json @@ -1,15 +1,15 @@ { - "name": "eslint-visitor-keys", - "version": "2.0.0", - "description": "Constants and utilities about visitor keys to traverse AST.", - "main": "lib/index.js", - "files": [ - "lib" - ], - "engines": { - "node": ">=10" + "author": { + "name": "Toru Nagashima", + "url": "https://github.com/mysticatea" }, + "bugs": { + "url": "https://github.com/eslint/eslint-visitor-keys/issues" + }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Constants and utilities about visitor keys to traverse AST.", "devDependencies": { "eslint": "^4.7.2", "eslint-config-eslint": "^4.0.0", @@ -18,22 +18,30 @@ "nyc": "^11.2.1", "opener": "^1.4.3" }, + "engines": { + "node": ">=10" + }, + "files": [ + "lib" + ], + "homepage": "https://github.com/eslint/eslint-visitor-keys#readme", + "keywords": [], + "license": "Apache-2.0", + "main": "lib/index.js", + "name": "eslint-visitor-keys", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslint-visitor-keys.git" + }, "scripts": { - "lint": "eslint lib tests/lib", - "test": "nyc mocha tests/lib", "coverage": "nyc report --reporter lcov && opener coverage/lcov-report/index.html", - "generate-release": "eslint-generate-release", "generate-alpharelease": "eslint-generate-prerelease alpha", "generate-betarelease": "eslint-generate-prerelease beta", "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" - }, - "repository": "eslint/eslint-visitor-keys", - "keywords": [], - "author": "Toru Nagashima (https://github.com/mysticatea)", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/eslint/eslint-visitor-keys/issues" + "generate-release": "eslint-generate-release", + "lint": "eslint lib tests/lib", + "publish-release": "eslint-publish-release", + "test": "nyc mocha tests/lib" }, - "homepage": "https://github.com/eslint/eslint-visitor-keys#readme" -} + "version": "2.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/espree/node_modules/eslint-visitor-keys/package.json b/tools/node_modules/eslint/node_modules/espree/node_modules/eslint-visitor-keys/package.json index 63267be6437e03..2765f6d5537237 100644 --- a/tools/node_modules/eslint/node_modules/espree/node_modules/eslint-visitor-keys/package.json +++ b/tools/node_modules/eslint/node_modules/espree/node_modules/eslint-visitor-keys/package.json @@ -1,15 +1,15 @@ { - "name": "eslint-visitor-keys", - "version": "1.3.0", - "description": "Constants and utilities about visitor keys to traverse AST.", - "main": "lib/index.js", - "files": [ - "lib" - ], - "engines": { - "node": ">=4" + "author": { + "name": "Toru Nagashima", + "url": "https://github.com/mysticatea" }, + "bugs": { + "url": "https://github.com/eslint/eslint-visitor-keys/issues" + }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Constants and utilities about visitor keys to traverse AST.", "devDependencies": { "eslint": "^4.7.2", "eslint-config-eslint": "^4.0.0", @@ -18,23 +18,31 @@ "nyc": "^11.2.1", "opener": "^1.4.3" }, + "engines": { + "node": ">=4" + }, + "files": [ + "lib" + ], + "homepage": "https://github.com/eslint/eslint-visitor-keys#readme", + "keywords": [], + "license": "Apache-2.0", + "main": "lib/index.js", + "name": "eslint-visitor-keys", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslint-visitor-keys.git" + }, "scripts": { - "lint": "eslint lib tests/lib", - "pretest": "npm run -s lint", - "test": "nyc mocha tests/lib", "coverage": "nyc report --reporter lcov && opener coverage/lcov-report/index.html", - "generate-release": "eslint-generate-release", "generate-alpharelease": "eslint-generate-prerelease alpha", "generate-betarelease": "eslint-generate-prerelease beta", "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" - }, - "repository": "eslint/eslint-visitor-keys", - "keywords": [], - "author": "Toru Nagashima (https://github.com/mysticatea)", - "license": "Apache-2.0", - "bugs": { - "url": "https://github.com/eslint/eslint-visitor-keys/issues" + "generate-release": "eslint-generate-release", + "lint": "eslint lib tests/lib", + "pretest": "npm run -s lint", + "publish-release": "eslint-publish-release", + "test": "nyc mocha tests/lib" }, - "homepage": "https://github.com/eslint/eslint-visitor-keys#readme" -} + "version": "1.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/espree/package.json b/tools/node_modules/eslint/node_modules/espree/package.json index 724e6fcee72c3f..9fd6ee14a34c8f 100644 --- a/tools/node_modules/eslint/node_modules/espree/package.json +++ b/tools/node_modules/eslint/node_modules/espree/package.json @@ -1,27 +1,19 @@ { - "name": "espree", - "description": "An Esprima-compatible JavaScript parser built on Acorn", - "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", - "homepage": "https://github.com/eslint/espree", - "main": "espree.js", - "version": "7.3.1", - "files": [ - "lib", - "espree.js" - ], - "engines": { - "node": "^10.12.0 || >=12.0.0" + "author": { + "name": "Nicholas C. Zakas", + "email": "nicholas+npm@nczconsulting.com" }, - "repository": "eslint/espree", "bugs": { "url": "http://github.com/eslint/espree.git" }, - "license": "BSD-2-Clause", + "bundleDependencies": false, "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", "eslint-visitor-keys": "^1.3.0" }, + "deprecated": false, + "description": "An Esprima-compatible JavaScript parser built on Acorn", "devDependencies": { "browserify": "^16.5.0", "chai": "^4.2.0", @@ -40,6 +32,14 @@ "shelljs-nodecli": "^0.1.1", "unicode-6.3.0": "^0.7.5" }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "files": [ + "lib", + "espree.js" + ], + "homepage": "https://github.com/eslint/espree", "keywords": [ "ast", "ecmascript", @@ -48,17 +48,25 @@ "syntax", "acorn" ], + "license": "BSD-2-Clause", + "main": "espree.js", + "name": "espree", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/espree.git" + }, "scripts": { - "generate-regex": "node tools/generate-identifier-regex.js", - "test": "npm run-script lint && node Makefile.js test", - "lint": "node Makefile.js lint", - "fixlint": "node Makefile.js lint --fix", - "sync-docs": "node Makefile.js docs", "browserify": "node Makefile.js browserify", - "generate-release": "eslint-generate-release", + "fixlint": "node Makefile.js lint --fix", "generate-alpharelease": "eslint-generate-prerelease alpha", "generate-betarelease": "eslint-generate-prerelease beta", "generate-rcrelease": "eslint-generate-prerelease rc", - "publish-release": "eslint-publish-release" - } -} + "generate-regex": "node tools/generate-identifier-regex.js", + "generate-release": "eslint-generate-release", + "lint": "node Makefile.js lint", + "publish-release": "eslint-publish-release", + "sync-docs": "node Makefile.js docs", + "test": "npm run-script lint && node Makefile.js test" + }, + "version": "7.3.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/esprima/package.json b/tools/node_modules/eslint/node_modules/esprima/package.json index 4148b8ce4f4fa7..03d154ee722a3c 100644 --- a/tools/node_modules/eslint/node_modules/esprima/package.json +++ b/tools/node_modules/eslint/node_modules/esprima/package.json @@ -1,39 +1,18 @@ { - "name": "esprima", - "description": "ECMAScript parsing infrastructure for multipurpose analysis", - "homepage": "http://esprima.org", - "main": "dist/esprima.js", - "bin": { - "esparse": "./bin/esparse.js", - "esvalidate": "./bin/esvalidate.js" - }, - "version": "4.0.1", - "files": [ - "bin", - "dist/esprima.js" - ], - "engines": { - "node": ">=4" - }, "author": { "name": "Ariya Hidayat", "email": "ariya.hidayat@gmail.com" }, - "maintainers": [ - { - "name": "Ariya Hidayat", - "email": "ariya.hidayat@gmail.com", - "web": "http://ariya.ofilabs.com" - } - ], - "repository": { - "type": "git", - "url": "https://github.com/jquery/esprima.git" + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" }, "bugs": { "url": "https://github.com/jquery/esprima/issues" }, - "license": "BSD-2-Clause", + "bundleDependencies": false, + "deprecated": false, + "description": "ECMAScript parsing infrastructure for multipurpose analysis", "devDependencies": { "codecov.io": "~0.1.6", "escomplex-js": "1.2.0", @@ -62,6 +41,14 @@ "unicode-8.0.0": "~0.7.0", "webpack": "~1.14.0" }, + "engines": { + "node": ">=4" + }, + "files": [ + "bin", + "dist/esprima.js" + ], + "homepage": "http://esprima.org", "keywords": [ "ast", "ecmascript", @@ -70,43 +57,58 @@ "parser", "syntax" ], + "license": "BSD-2-Clause", + "main": "dist/esprima.js", + "maintainers": [ + { + "name": "Ariya Hidayat", + "email": "ariya.hidayat@gmail.com", + "url": "http://ariya.ofilabs.com" + } + ], + "name": "esprima", + "repository": { + "type": "git", + "url": "git+https://github.com/jquery/esprima.git" + }, "scripts": { + "all-tests": "npm run verify-line-ending && npm run generate-fixtures && npm run unit-tests && npm run api-tests && npm run grammar-tests && npm run regression-tests && npm run hostile-env-tests", + "analyze-coverage": "istanbul cover test/unit-tests.js", + "api-tests": "mocha -R dot test/api-tests.js", + "appveyor": "npm run compile && npm run all-tests && npm run browser-tests", + "benchmark": "npm run benchmark-parser && npm run benchmark-tokenizer", + "benchmark-parser": "node -expose_gc test/benchmark-parser.js", + "benchmark-tokenizer": "node --expose_gc test/benchmark-tokenizer.js", + "browser-tests": "npm run compile && npm run generate-fixtures && cd test && karma start --single-run", + "check-coverage": "istanbul check-coverage --statement 100 --branch 100 --function 100", "check-version": "node test/check-version.js", - "tslint": "tslint src/*.ts", + "circleci": "npm test && npm run codecov && npm run downstream", "code-style": "tsfmt --verify src/*.ts && tsfmt --verify test/*.js", - "format-code": "tsfmt -r src/*.ts && tsfmt -r test/*.js", + "codecov": "istanbul report cobertura && codecov < ./coverage/cobertura-coverage.xml", + "compile": "tsc -p src/ && webpack && node tools/fixupbundle.js", "complexity": "node test/check-complexity.js", - "static-analysis": "npm run check-version && npm run tslint && npm run code-style && npm run complexity", - "hostile-env-tests": "node test/hostile-environment-tests.js", - "unit-tests": "node test/unit-tests.js", - "api-tests": "mocha -R dot test/api-tests.js", + "downstream": "node test/downstream.js", + "droneio": "npm run compile && npm run all-tests && npm run saucelabs", + "dynamic-analysis": "npm run analyze-coverage && npm run check-coverage", + "format-code": "tsfmt -r src/*.ts && tsfmt -r test/*.js", + "generate-fixtures": "node tools/generate-fixtures.js", + "generate-regex": "node tools/generate-identifier-regex.js", + "generate-xhtml-entities": "node tools/generate-xhtml-entities.js", "grammar-tests": "node test/grammar-tests.js", + "hostile-env-tests": "node test/hostile-environment-tests.js", + "prepublish": "npm run compile", + "profile": "node --prof test/profile.js && mv isolate*.log v8.log && node-tick-processor", "regression-tests": "node test/regression-tests.js", - "all-tests": "npm run verify-line-ending && npm run generate-fixtures && npm run unit-tests && npm run api-tests && npm run grammar-tests && npm run regression-tests && npm run hostile-env-tests", - "verify-line-ending": "node test/verify-line-ending.js", - "generate-fixtures": "node tools/generate-fixtures.js", - "browser-tests": "npm run compile && npm run generate-fixtures && cd test && karma start --single-run", + "saucelabs": "npm run saucelabs-evergreen && npm run saucelabs-ie && npm run saucelabs-safari", "saucelabs-evergreen": "cd test && karma start saucelabs-evergreen.conf.js", - "saucelabs-safari": "cd test && karma start saucelabs-safari.conf.js", "saucelabs-ie": "cd test && karma start saucelabs-ie.conf.js", - "saucelabs": "npm run saucelabs-evergreen && npm run saucelabs-ie && npm run saucelabs-safari", - "analyze-coverage": "istanbul cover test/unit-tests.js", - "check-coverage": "istanbul check-coverage --statement 100 --branch 100 --function 100", - "dynamic-analysis": "npm run analyze-coverage && npm run check-coverage", - "compile": "tsc -p src/ && webpack && node tools/fixupbundle.js", + "saucelabs-safari": "cd test && karma start saucelabs-safari.conf.js", + "static-analysis": "npm run check-version && npm run tslint && npm run code-style && npm run complexity", "test": "npm run compile && npm run all-tests && npm run static-analysis && npm run dynamic-analysis", - "prepublish": "npm run compile", - "profile": "node --prof test/profile.js && mv isolate*.log v8.log && node-tick-processor", - "benchmark-parser": "node -expose_gc test/benchmark-parser.js", - "benchmark-tokenizer": "node --expose_gc test/benchmark-tokenizer.js", - "benchmark": "npm run benchmark-parser && npm run benchmark-tokenizer", - "codecov": "istanbul report cobertura && codecov < ./coverage/cobertura-coverage.xml", - "downstream": "node test/downstream.js", "travis": "npm test", - "circleci": "npm test && npm run codecov && npm run downstream", - "appveyor": "npm run compile && npm run all-tests && npm run browser-tests", - "droneio": "npm run compile && npm run all-tests && npm run saucelabs", - "generate-regex": "node tools/generate-identifier-regex.js", - "generate-xhtml-entities": "node tools/generate-xhtml-entities.js" - } -} + "tslint": "tslint src/*.ts", + "unit-tests": "node test/unit-tests.js", + "verify-line-ending": "node test/verify-line-ending.js" + }, + "version": "4.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/esquery/node_modules/estraverse/package.json b/tools/node_modules/eslint/node_modules/esquery/node_modules/estraverse/package.json index bc99e7c4a64674..4d336a00e8b526 100644 --- a/tools/node_modules/eslint/node_modules/esquery/node_modules/estraverse/package.json +++ b/tools/node_modules/eslint/node_modules/esquery/node_modules/estraverse/package.json @@ -1,23 +1,10 @@ { - "name": "estraverse", - "description": "ECMAScript JS AST traversal functions", - "homepage": "https://github.com/estools/estraverse", - "main": "estraverse.js", - "version": "5.2.0", - "engines": { - "node": ">=4.0" - }, - "maintainers": [ - { - "name": "Yusuke Suzuki", - "email": "utatane.tea@gmail.com", - "web": "http://github.com/Constellation" - } - ], - "repository": { - "type": "git", - "url": "http://github.com/estools/estraverse.git" + "bugs": { + "url": "https://github.com/estools/estraverse/issues" }, + "bundleDependencies": false, + "deprecated": false, + "description": "ECMAScript JS AST traversal functions", "devDependencies": { "babel-preset-env": "^1.6.1", "babel-register": "^6.3.13", @@ -31,10 +18,28 @@ "jshint": "^2.5.6", "mocha": "^2.1.0" }, + "engines": { + "node": ">=4.0" + }, + "homepage": "https://github.com/estools/estraverse", "license": "BSD-2-Clause", + "main": "estraverse.js", + "maintainers": [ + { + "name": "Yusuke Suzuki", + "email": "utatane.tea@gmail.com", + "url": "http://github.com/Constellation" + } + ], + "name": "estraverse", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/estools/estraverse.git" + }, "scripts": { - "test": "npm run-script lint && npm run-script unit-test", "lint": "jshint estraverse.js", + "test": "npm run-script lint && npm run-script unit-test", "unit-test": "mocha --compilers js:babel-register" - } -} + }, + "version": "5.2.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/esquery/package.json b/tools/node_modules/eslint/node_modules/esquery/package.json index 3d818750d38a6d..6f9a3e44bf0794 100644 --- a/tools/node_modules/eslint/node_modules/esquery/package.json +++ b/tools/node_modules/eslint/node_modules/esquery/package.json @@ -1,11 +1,39 @@ { - "name": "esquery", - "version": "1.4.0", - "author": "Joel Feenstra <jrfeenst+esquery@gmail.com>", + "author": { + "name": "Joel Feenstra", + "email": "jrfeenst+esquery@gmail.com" + }, + "bugs": { + "url": "https://github.com/estools/esquery/issues" + }, + "bundleDependencies": false, "contributors": [], + "dependencies": { + "estraverse": "^5.1.0" + }, + "deprecated": false, "description": "A query library for ECMAScript AST using a CSS selector like query language.", - "main": "dist/esquery.min.js", - "module": "dist/esquery.esm.min.js", + "devDependencies": { + "@babel/core": "^7.9.0", + "@babel/preset-env": "^7.9.5", + "@babel/register": "^7.9.0", + "@rollup/plugin-commonjs": "^11.1.0", + "@rollup/plugin-json": "^4.0.2", + "@rollup/plugin-node-resolve": "^7.1.3", + "babel-plugin-transform-es2017-object-entries": "0.0.5", + "chai": "^4.2.0", + "eslint": "^6.8.0", + "esprima": "~4.0.1", + "mocha": "^7.1.1", + "nyc": "^15.0.1", + "pegjs": "~0.10.0", + "rollup": "^1.32.1", + "rollup-plugin-babel": "^4.4.0", + "rollup-plugin-terser": "^5.3.0" + }, + "engines": { + "node": ">=0.10" + }, "files": [ "dist/*.js", "dist/*.map", @@ -13,6 +41,17 @@ "license.txt", "README.md" ], + "homepage": "https://github.com/estools/esquery/", + "keywords": [ + "ast", + "ecmascript", + "javascript", + "query" + ], + "license": "BSD-3-Clause", + "main": "dist/esquery.min.js", + "module": "dist/esquery.esm.min.js", + "name": "esquery", "nyc": { "branches": 100, "lines": 100, @@ -28,51 +67,19 @@ "tests" ] }, + "repository": { + "type": "git", + "url": "git+https://github.com/estools/esquery.git" + }, "scripts": { - "prepublishOnly": "npm run build && npm test", - "build:parser": "rm parser.js && pegjs --cache --format umd -o \"parser.js\" \"grammar.pegjs\"", - "build:browser": "rollup -c", "build": "npm run build:parser && npm run build:browser", + "build:browser": "rollup -c", + "build:parser": "rm parser.js && pegjs --cache --format umd -o \"parser.js\" \"grammar.pegjs\"", + "lint": "eslint .", "mocha": "mocha --require chai/register-assert --require @babel/register tests", + "prepublishOnly": "npm run build && npm test", "test": "nyc npm run mocha && npm run lint", - "test:ci": "npm run mocha", - "lint": "eslint ." + "test:ci": "npm run mocha" }, - "repository": { - "type": "git", - "url": "https://github.com/estools/esquery.git" - }, - "bugs": "https://github.com/estools/esquery/issues", - "homepage": "https://github.com/estools/esquery/", - "keywords": [ - "ast", - "ecmascript", - "javascript", - "query" - ], - "devDependencies": { - "@babel/core": "^7.9.0", - "@babel/preset-env": "^7.9.5", - "@babel/register": "^7.9.0", - "@rollup/plugin-commonjs": "^11.1.0", - "@rollup/plugin-json": "^4.0.2", - "@rollup/plugin-node-resolve": "^7.1.3", - "babel-plugin-transform-es2017-object-entries": "0.0.5", - "chai": "^4.2.0", - "eslint": "^6.8.0", - "esprima": "~4.0.1", - "mocha": "^7.1.1", - "nyc": "^15.0.1", - "pegjs": "~0.10.0", - "rollup": "^1.32.1", - "rollup-plugin-babel": "^4.4.0", - "rollup-plugin-terser": "^5.3.0" - }, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10" - }, - "dependencies": { - "estraverse": "^5.1.0" - } -} + "version": "1.4.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/esrecurse/node_modules/estraverse/package.json b/tools/node_modules/eslint/node_modules/esrecurse/node_modules/estraverse/package.json index bc99e7c4a64674..4d336a00e8b526 100644 --- a/tools/node_modules/eslint/node_modules/esrecurse/node_modules/estraverse/package.json +++ b/tools/node_modules/eslint/node_modules/esrecurse/node_modules/estraverse/package.json @@ -1,23 +1,10 @@ { - "name": "estraverse", - "description": "ECMAScript JS AST traversal functions", - "homepage": "https://github.com/estools/estraverse", - "main": "estraverse.js", - "version": "5.2.0", - "engines": { - "node": ">=4.0" - }, - "maintainers": [ - { - "name": "Yusuke Suzuki", - "email": "utatane.tea@gmail.com", - "web": "http://github.com/Constellation" - } - ], - "repository": { - "type": "git", - "url": "http://github.com/estools/estraverse.git" + "bugs": { + "url": "https://github.com/estools/estraverse/issues" }, + "bundleDependencies": false, + "deprecated": false, + "description": "ECMAScript JS AST traversal functions", "devDependencies": { "babel-preset-env": "^1.6.1", "babel-register": "^6.3.13", @@ -31,10 +18,28 @@ "jshint": "^2.5.6", "mocha": "^2.1.0" }, + "engines": { + "node": ">=4.0" + }, + "homepage": "https://github.com/estools/estraverse", "license": "BSD-2-Clause", + "main": "estraverse.js", + "maintainers": [ + { + "name": "Yusuke Suzuki", + "email": "utatane.tea@gmail.com", + "url": "http://github.com/Constellation" + } + ], + "name": "estraverse", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/estools/estraverse.git" + }, "scripts": { - "test": "npm run-script lint && npm run-script unit-test", "lint": "jshint estraverse.js", + "test": "npm run-script lint && npm run-script unit-test", "unit-test": "mocha --compilers js:babel-register" - } -} + }, + "version": "5.2.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/esrecurse/package.json b/tools/node_modules/eslint/node_modules/esrecurse/package.json index dec5b1bc1fd3ac..8e2661c63262b1 100755 --- a/tools/node_modules/eslint/node_modules/esrecurse/package.json +++ b/tools/node_modules/eslint/node_modules/esrecurse/package.json @@ -1,26 +1,18 @@ { - "name": "esrecurse", - "description": "ECMAScript AST recursive visitor", - "homepage": "https://github.com/estools/esrecurse", - "main": "esrecurse.js", - "version": "4.3.0", - "engines": { - "node": ">=4.0" + "babel": { + "presets": [ + "es2015" + ] }, - "maintainers": [ - { - "name": "Yusuke Suzuki", - "email": "utatane.tea@gmail.com", - "web": "https://github.com/Constellation" - } - ], - "repository": { - "type": "git", - "url": "https://github.com/estools/esrecurse.git" + "bugs": { + "url": "https://github.com/estools/esrecurse/issues" }, + "bundleDependencies": false, "dependencies": { "estraverse": "^5.2.0" }, + "deprecated": false, + "description": "ECMAScript AST recursive visitor", "devDependencies": { "babel-cli": "^6.24.1", "babel-eslint": "^7.2.3", @@ -38,15 +30,28 @@ "jsdoc": "^3.3.0-alpha10", "minimist": "^1.1.0" }, + "engines": { + "node": ">=4.0" + }, + "homepage": "https://github.com/estools/esrecurse", "license": "BSD-2-Clause", + "main": "esrecurse.js", + "maintainers": [ + { + "name": "Yusuke Suzuki", + "email": "utatane.tea@gmail.com", + "url": "https://github.com/Constellation" + } + ], + "name": "esrecurse", + "repository": { + "type": "git", + "url": "git+https://github.com/estools/esrecurse.git" + }, "scripts": { + "lint": "gulp lint", "test": "gulp travis", - "unit-test": "gulp test", - "lint": "gulp lint" + "unit-test": "gulp test" }, - "babel": { - "presets": [ - "es2015" - ] - } -} + "version": "4.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/estraverse/package.json b/tools/node_modules/eslint/node_modules/estraverse/package.json index 1138238672309b..8a9a242d0fd34a 100644 --- a/tools/node_modules/eslint/node_modules/estraverse/package.json +++ b/tools/node_modules/eslint/node_modules/estraverse/package.json @@ -1,23 +1,10 @@ { - "name": "estraverse", - "description": "ECMAScript JS AST traversal functions", - "homepage": "https://github.com/estools/estraverse", - "main": "estraverse.js", - "version": "4.3.0", - "engines": { - "node": ">=4.0" - }, - "maintainers": [ - { - "name": "Yusuke Suzuki", - "email": "utatane.tea@gmail.com", - "web": "http://github.com/Constellation" - } - ], - "repository": { - "type": "git", - "url": "http://github.com/estools/estraverse.git" + "bugs": { + "url": "https://github.com/estools/estraverse/issues" }, + "bundleDependencies": false, + "deprecated": false, + "description": "ECMAScript JS AST traversal functions", "devDependencies": { "babel-preset-env": "^1.6.1", "babel-register": "^6.3.13", @@ -31,10 +18,28 @@ "jshint": "^2.5.6", "mocha": "^2.1.0" }, + "engines": { + "node": ">=4.0" + }, + "homepage": "https://github.com/estools/estraverse", "license": "BSD-2-Clause", + "main": "estraverse.js", + "maintainers": [ + { + "name": "Yusuke Suzuki", + "email": "utatane.tea@gmail.com", + "url": "http://github.com/Constellation" + } + ], + "name": "estraverse", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/estools/estraverse.git" + }, "scripts": { - "test": "npm run-script lint && npm run-script unit-test", "lint": "jshint estraverse.js", + "test": "npm run-script lint && npm run-script unit-test", "unit-test": "mocha --compilers js:babel-register" - } -} + }, + "version": "4.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/esutils/package.json b/tools/node_modules/eslint/node_modules/esutils/package.json index 8396f4cee3f7e8..cffa439c8dafce 100644 --- a/tools/node_modules/eslint/node_modules/esutils/package.json +++ b/tools/node_modules/eslint/node_modules/esutils/package.json @@ -1,44 +1,49 @@ { - "name": "esutils", + "bugs": { + "url": "https://github.com/estools/esutils/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "utility box for ECMAScript language tools", - "homepage": "https://github.com/estools/esutils", - "main": "lib/utils.js", - "version": "2.0.3", - "engines": { - "node": ">=0.10.0" + "devDependencies": { + "chai": "~1.7.2", + "coffee-script": "~1.6.3", + "jshint": "2.6.3", + "mocha": "~2.2.1", + "regenerate": "~1.3.1", + "unicode-9.0.0": "~0.7.0" }, "directories": { "lib": "./lib" }, + "engines": { + "node": ">=0.10.0" + }, "files": [ "LICENSE.BSD", "README.md", "lib" ], + "homepage": "https://github.com/estools/esutils", + "license": "BSD-2-Clause", + "main": "lib/utils.js", "maintainers": [ { "name": "Yusuke Suzuki", "email": "utatane.tea@gmail.com", - "web": "http://github.com/Constellation" + "url": "http://github.com/Constellation" } ], + "name": "esutils", "repository": { "type": "git", - "url": "http://github.com/estools/esutils.git" - }, - "devDependencies": { - "chai": "~1.7.2", - "coffee-script": "~1.6.3", - "jshint": "2.6.3", - "mocha": "~2.2.1", - "regenerate": "~1.3.1", - "unicode-9.0.0": "~0.7.0" + "url": "git+ssh://git@github.com/estools/esutils.git" }, - "license": "BSD-2-Clause", "scripts": { - "test": "npm run-script lint && npm run-script unit-test", + "generate-regex": "node tools/generate-identifier-regex.js", "lint": "jshint lib/*.js", - "unit-test": "mocha --compilers coffee:coffee-script -R spec", - "generate-regex": "node tools/generate-identifier-regex.js" - } -} + "test": "npm run-script lint && npm run-script unit-test", + "unit-test": "mocha --compilers coffee:coffee-script -R spec" + }, + "version": "2.0.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/fast-deep-equal/package.json b/tools/node_modules/eslint/node_modules/fast-deep-equal/package.json index 3cfe66c68e832b..6377dca7e45e79 100644 --- a/tools/node_modules/eslint/node_modules/fast-deep-equal/package.json +++ b/tools/node_modules/eslint/node_modules/fast-deep-equal/package.json @@ -1,33 +1,13 @@ { - "name": "fast-deep-equal", - "version": "3.1.3", - "description": "Fast deep equal", - "main": "index.js", - "scripts": { - "eslint": "eslint *.js benchmark/*.js spec/*.js", - "build": "node build", - "benchmark": "npm i && npm run build && cd ./benchmark && npm i && node ./", - "test-spec": "mocha spec/*.spec.js -R spec", - "test-cov": "nyc npm run test-spec", - "test-ts": "tsc --target ES5 --noImplicitAny index.d.ts", - "test": "npm run build && npm run eslint && npm run test-ts && npm run test-cov", - "prepublish": "npm run build" + "author": { + "name": "Evgeny Poberezkin" }, - "repository": { - "type": "git", - "url": "git+https://github.com/epoberezkin/fast-deep-equal.git" - }, - "keywords": [ - "fast", - "equal", - "deep-equal" - ], - "author": "Evgeny Poberezkin", - "license": "MIT", "bugs": { "url": "https://github.com/epoberezkin/fast-deep-equal/issues" }, - "homepage": "https://github.com/epoberezkin/fast-deep-equal#readme", + "bundleDependencies": false, + "deprecated": false, + "description": "Fast deep equal", "devDependencies": { "coveralls": "^3.1.0", "dot": "^1.1.2", @@ -40,6 +20,22 @@ "sinon": "^9.0.2", "typescript": "^3.9.5" }, + "files": [ + "index.js", + "index.d.ts", + "react.js", + "react.d.ts", + "es6/" + ], + "homepage": "https://github.com/epoberezkin/fast-deep-equal#readme", + "keywords": [ + "fast", + "equal", + "deep-equal" + ], + "license": "MIT", + "main": "index.js", + "name": "fast-deep-equal", "nyc": { "exclude": [ "**/spec/**", @@ -50,12 +46,20 @@ "text-summary" ] }, - "files": [ - "index.js", - "index.d.ts", - "react.js", - "react.d.ts", - "es6/" - ], - "types": "index.d.ts" -} + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/fast-deep-equal.git" + }, + "scripts": { + "benchmark": "npm i && npm run build && cd ./benchmark && npm i && node ./", + "build": "node build", + "eslint": "eslint *.js benchmark/*.js spec/*.js", + "prepublish": "npm run build", + "test": "npm run build && npm run eslint && npm run test-ts && npm run test-cov", + "test-cov": "nyc npm run test-spec", + "test-spec": "mocha spec/*.spec.js -R spec", + "test-ts": "tsc --target ES5 --noImplicitAny index.d.ts" + }, + "types": "index.d.ts", + "version": "3.1.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/fast-json-stable-stringify/package.json b/tools/node_modules/eslint/node_modules/fast-json-stable-stringify/package.json index ad2c8bff7c94e2..46f5c8e221cc45 100644 --- a/tools/node_modules/eslint/node_modules/fast-json-stable-stringify/package.json +++ b/tools/node_modules/eslint/node_modules/fast-json-stable-stringify/package.json @@ -1,10 +1,16 @@ { - "name": "fast-json-stable-stringify", - "version": "2.1.0", - "description": "deterministic `JSON.stringify()` - a faster version of substack's json-stable-strigify without jsonify", - "main": "index.js", - "types": "index.d.ts", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/epoberezkin/fast-json-stable-stringify/issues" + }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "deterministic `JSON.stringify()` - a faster version of substack's json-stable-strigify without jsonify", "devDependencies": { "benchmark": "^2.1.4", "coveralls": "^3.0.0", @@ -16,15 +22,6 @@ "pre-commit": "^1.2.2", "tape": "^4.11.0" }, - "scripts": { - "eslint": "eslint index.js test", - "test-spec": "tape test/*.js", - "test": "npm run eslint && nyc npm run test-spec" - }, - "repository": { - "type": "git", - "url": "git://github.com/epoberezkin/fast-json-stable-stringify.git" - }, "homepage": "https://github.com/epoberezkin/fast-json-stable-stringify", "keywords": [ "json", @@ -33,12 +30,9 @@ "hash", "stable" ], - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net" - }, "license": "MIT", + "main": "index.js", + "name": "fast-json-stable-stringify", "nyc": { "exclude": [ "test", @@ -48,5 +42,16 @@ "lcov", "text-summary" ] - } -} + }, + "repository": { + "type": "git", + "url": "git://github.com/epoberezkin/fast-json-stable-stringify.git" + }, + "scripts": { + "eslint": "eslint index.js test", + "test": "npm run eslint && nyc npm run test-spec", + "test-spec": "tape test/*.js" + }, + "types": "index.d.ts", + "version": "2.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/fast-levenshtein/package.json b/tools/node_modules/eslint/node_modules/fast-levenshtein/package.json index 5b4736d4537a5e..85c3001e0bff5e 100644 --- a/tools/node_modules/eslint/node_modules/fast-levenshtein/package.json +++ b/tools/node_modules/eslint/node_modules/fast-levenshtein/package.json @@ -1,17 +1,15 @@ { - "name": "fast-levenshtein", - "version": "2.0.6", - "description": "Efficient implementation of Levenshtein algorithm with locale-specific collator support.", - "main": "levenshtein.js", - "files": [ - "levenshtein.js" - ], - "scripts": { - "build": "grunt build", - "prepublish": "npm run build", - "benchmark": "grunt benchmark", - "test": "mocha" + "author": { + "name": "Ramesh Nair", + "email": "ram@hiddentao.com", + "url": "http://www.hiddentao.com/" + }, + "bugs": { + "url": "https://github.com/hiddentao/fast-levenshtein/issues" }, + "bundleDependencies": false, + "deprecated": false, + "description": "Efficient implementation of Levenshtein algorithm with locale-specific collator support.", "devDependencies": { "chai": "~1.5.0", "grunt": "~0.4.1", @@ -25,15 +23,27 @@ "lodash": "^4.0.1", "mocha": "~1.9.0" }, - "repository": { - "type": "git", - "url": "https://github.com/hiddentao/fast-levenshtein.git" - }, + "files": [ + "levenshtein.js" + ], + "homepage": "https://github.com/hiddentao/fast-levenshtein#readme", "keywords": [ "levenshtein", "distance", "string" ], - "author": "Ramesh Nair <ram@hiddentao.com> (http://www.hiddentao.com/)", - "license": "MIT" -} + "license": "MIT", + "main": "levenshtein.js", + "name": "fast-levenshtein", + "repository": { + "type": "git", + "url": "git+https://github.com/hiddentao/fast-levenshtein.git" + }, + "scripts": { + "benchmark": "grunt benchmark", + "build": "grunt build", + "prepublish": "npm run build", + "test": "mocha" + }, + "version": "2.0.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/file-entry-cache/package.json b/tools/node_modules/eslint/node_modules/file-entry-cache/package.json index f03ef48cc6f523..b9010e8a4993f6 100644 --- a/tools/node_modules/eslint/node_modules/file-entry-cache/package.json +++ b/tools/node_modules/eslint/node_modules/file-entry-cache/package.json @@ -1,50 +1,12 @@ { - "name": "file-entry-cache", - "version": "6.0.1", - "description": "Super simple cache for file metadata, useful for process that work o a given series of files and that only need to repeat the job on the changed ones since the previous run of the process", - "repository": "royriojas/file-entry-cache", - "license": "MIT", "author": { "name": "Roy Riojas", "url": "http://royriojas.com" }, - "main": "cache.js", - "files": [ - "cache.js" - ], - "engines": { - "node": "^10.12.0 || >=12.0.0" + "bugs": { + "url": "https://github.com/royriojas/file-entry-cache/issues" }, - "scripts": { - "eslint": "eslint --cache --cache-location=node_modules/.cache/ 'cache.js' 'test/**/*.js' 'perf.js'", - "autofix": "npm run eslint -- --fix", - "install-hooks": "prepush install && changelogx install-hook && precommit install", - "changelog": "changelogx -f markdown -o ./changelog.md", - "do-changelog": "npm run changelog && git add ./changelog.md && git commit -m 'DOC: Generate changelog' --no-verify", - "pre-v": "npm run test", - "post-v": "npm run do-changelog && git push --no-verify && git push --tags --no-verify", - "bump-major": "npm run pre-v && npm version major -m 'BLD: Release v%s' && npm run post-v", - "bump-minor": "npm run pre-v && npm version minor -m 'BLD: Release v%s' && npm run post-v", - "bump-patch": "npm run pre-v && npm version patch -m 'BLD: Release v%s' && npm run post-v", - "test": "npm run eslint --silent && mocha -R spec test/specs", - "perf": "node perf.js", - "cover": "istanbul cover test/runner.js html text-summary", - "watch": "watch-run -i -p 'test/specs/**/*.js' istanbul cover test/runner.js html text-summary" - }, - "prepush": [ - "npm run eslint --silent" - ], - "precommit": [ - "npm run eslint --silent" - ], - "keywords": [ - "file cache", - "task cache files", - "file cache", - "key par", - "key value", - "cache" - ], + "bundleDependencies": false, "changelogx": { "ignoreRegExp": [ "BLD: Release", @@ -57,6 +19,11 @@ "issueIDURL": "https://github.com/royriojas/file-entry-cache/issues/{0}", "projectName": "file-entry-cache" }, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "deprecated": false, + "description": "Super simple cache for file metadata, useful for process that work o a given series of files and that only need to repeat the job on the changed ones since the previous run of the process", "devDependencies": { "chai": "^4.2.0", "changelogx": "^5.0.6", @@ -74,7 +41,49 @@ "watch-run": "^1.2.5", "write": "^2.0.0" }, - "dependencies": { - "flat-cache": "^3.0.4" - } -} + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "files": [ + "cache.js" + ], + "homepage": "https://github.com/royriojas/file-entry-cache#readme", + "keywords": [ + "file cache", + "task cache files", + "file cache", + "key par", + "key value", + "cache" + ], + "license": "MIT", + "main": "cache.js", + "name": "file-entry-cache", + "precommit": [ + "npm run eslint --silent" + ], + "prepush": [ + "npm run eslint --silent" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/royriojas/file-entry-cache.git" + }, + "scripts": { + "autofix": "npm run eslint -- --fix", + "bump-major": "npm run pre-v && npm version major -m 'BLD: Release v%s' && npm run post-v", + "bump-minor": "npm run pre-v && npm version minor -m 'BLD: Release v%s' && npm run post-v", + "bump-patch": "npm run pre-v && npm version patch -m 'BLD: Release v%s' && npm run post-v", + "changelog": "changelogx -f markdown -o ./changelog.md", + "cover": "istanbul cover test/runner.js html text-summary", + "do-changelog": "npm run changelog && git add ./changelog.md && git commit -m 'DOC: Generate changelog' --no-verify", + "eslint": "eslint --cache --cache-location=node_modules/.cache/ 'cache.js' 'test/**/*.js' 'perf.js'", + "install-hooks": "prepush install && changelogx install-hook && precommit install", + "perf": "node perf.js", + "post-v": "npm run do-changelog && git push --no-verify && git push --tags --no-verify", + "pre-v": "npm run test", + "test": "npm run eslint --silent && mocha -R spec test/specs", + "watch": "watch-run -i -p 'test/specs/**/*.js' istanbul cover test/runner.js html text-summary" + }, + "version": "6.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/flat-cache/package.json b/tools/node_modules/eslint/node_modules/flat-cache/package.json index 8bc6f87c51083b..a08d68db3dd4f1 100644 --- a/tools/node_modules/eslint/node_modules/flat-cache/package.json +++ b/tools/node_modules/eslint/node_modules/flat-cache/package.json @@ -1,55 +1,12 @@ { - "name": "flat-cache", - "version": "3.0.4", - "description": "A stupidly simple key/value storage using files to persist some data", - "repository": "royriojas/flat-cache", - "license": "MIT", "author": { "name": "Roy Riojas", "url": "http://royriojas.com" }, - "main": "src/cache.js", - "files": [ - "src/cache.js", - "src/del.js", - "src/utils.js" - ], - "engines": { - "node": "^10.12.0 || >=12.0.0" + "bugs": { + "url": "https://github.com/royriojas/flat-cache/issues" }, - "precommit": [ - "npm run verify --silent" - ], - "prepush": [ - "npm run verify --silent" - ], - "scripts": { - "eslint": "eslint --cache --cache-location=node_modules/.cache/ ./src/**/*.js ./test/**/*.js", - "eslint-fix": "npm run eslint -- --fix", - "autofix": "npm run eslint-fix", - "check": "npm run eslint", - "verify": "npm run eslint && npm run test:cache", - "install-hooks": "prepush install && changelogx install-hook && precommit install", - "changelog": "changelogx -f markdown -o ./changelog.md", - "do-changelog": "npm run changelog && git add ./changelog.md && git commit -m 'DOC: Generate changelog' --no-verify", - "pre-v": "npm run verify", - "post-v": "npm run do-changelog && git push --no-verify && git push --tags --no-verify", - "bump-major": "npm run pre-v && npm version major -m 'BLD: Release v%s' && npm run post-v", - "bump-minor": "npm run pre-v && npm version minor -m 'BLD: Release v%s' && npm run post-v", - "bump-patch": "npm run pre-v && npm version patch -m 'BLD: Release v%s' && npm run post-v", - "test:cache": "mocha -R spec test/specs", - "test": "npm run verify --silent", - "cover": "istanbul cover test/runner.js html text-summary", - "watch": "watch-run -i -p 'test/specs/**/*.js' istanbul cover test/runner.js html text-summary" - }, - "keywords": [ - "json cache", - "simple cache", - "file cache", - "key par", - "key value", - "cache" - ], + "bundleDependencies": false, "changelogx": { "ignoreRegExp": [ "BLD: Release", @@ -62,6 +19,12 @@ "issueIDURL": "https://github.com/royriojas/flat-cache/issues/{0}", "projectName": "flat-cache" }, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "deprecated": false, + "description": "A stupidly simple key/value storage using files to persist some data", "devDependencies": { "chai": "^4.2.0", "changelogx": "^5.0.6", @@ -77,8 +40,54 @@ "prettier": "^2.1.2", "watch-run": "^1.2.5" }, - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } -} + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "files": [ + "src/cache.js", + "src/del.js", + "src/utils.js" + ], + "homepage": "https://github.com/royriojas/flat-cache#readme", + "keywords": [ + "json cache", + "simple cache", + "file cache", + "key par", + "key value", + "cache" + ], + "license": "MIT", + "main": "src/cache.js", + "name": "flat-cache", + "precommit": [ + "npm run verify --silent" + ], + "prepush": [ + "npm run verify --silent" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/royriojas/flat-cache.git" + }, + "scripts": { + "autofix": "npm run eslint-fix", + "bump-major": "npm run pre-v && npm version major -m 'BLD: Release v%s' && npm run post-v", + "bump-minor": "npm run pre-v && npm version minor -m 'BLD: Release v%s' && npm run post-v", + "bump-patch": "npm run pre-v && npm version patch -m 'BLD: Release v%s' && npm run post-v", + "changelog": "changelogx -f markdown -o ./changelog.md", + "check": "npm run eslint", + "cover": "istanbul cover test/runner.js html text-summary", + "do-changelog": "npm run changelog && git add ./changelog.md && git commit -m 'DOC: Generate changelog' --no-verify", + "eslint": "eslint --cache --cache-location=node_modules/.cache/ ./src/**/*.js ./test/**/*.js", + "eslint-fix": "npm run eslint -- --fix", + "install-hooks": "prepush install && changelogx install-hook && precommit install", + "post-v": "npm run do-changelog && git push --no-verify && git push --tags --no-verify", + "pre-v": "npm run verify", + "test": "npm run verify --silent", + "test:cache": "mocha -R spec test/specs", + "verify": "npm run eslint && npm run test:cache", + "watch": "watch-run -i -p 'test/specs/**/*.js' istanbul cover test/runner.js html text-summary" + }, + "version": "3.0.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/flatted/package.json b/tools/node_modules/eslint/node_modules/flatted/package.json index 7b106e797c1c8d..41a5b5defc6d20 100644 --- a/tools/node_modules/eslint/node_modules/flatted/package.json +++ b/tools/node_modules/eslint/node_modules/flatted/package.json @@ -1,37 +1,13 @@ { - "name": "flatted", - "version": "3.1.1", - "description": "A super light and fast circular JSON parser.", - "unpkg": "min.js", - "types": "types.d.ts", - "main": "./cjs/index.js", - "scripts": { - "build": "npm run cjs && npm run rollup:es && npm run rollup:babel && npm run min && npm run test && npm run size", - "cjs": "ascjs esm cjs", - "rollup:es": "rollup --config rollup/es.config.js && sed -i.bck 's/^var /self./' es.js && rm -rf es.js.bck", - "rollup:babel": "rollup --config rollup/babel.config.js && sed -i.bck 's/^var /self./' index.js && rm -rf index.js.bck && drop-babel-typeof index.js", - "min": "terser index.js -c -m -o min.js", - "size": "cat index.js | wc -c;cat min.js | wc -c;gzip -c9 min.js | wc -c;cat min.js | brotli | wc -c; cat es.js | brotli | wc -c", - "coveralls": "nyc report --reporter=text-lcov | coveralls", - "test": "nyc node test/index.js" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/WebReflection/flatted.git" + "author": { + "name": "Andrea Giammarchi" }, - "keywords": [ - "circular", - "JSON", - "fast", - "parser", - "minimal" - ], - "author": "Andrea Giammarchi", - "license": "ISC", "bugs": { "url": "https://github.com/WebReflection/flatted/issues" }, - "homepage": "https://github.com/WebReflection/flatted#readme", + "bundleDependencies": false, + "deprecated": false, + "description": "A super light and fast circular JSON parser.", "devDependencies": { "@babel/core": "^7.11.6", "@babel/preset-env": "^7.11.5", @@ -48,10 +24,38 @@ "rollup-plugin-terser": "^7.0.2", "terser": "^5.3.0" }, - "module": "./esm/index.js", - "type": "module", "exports": { "import": "./esm/index.js", "default": "./cjs/index.js" - } -} + }, + "homepage": "https://github.com/WebReflection/flatted#readme", + "keywords": [ + "circular", + "JSON", + "fast", + "parser", + "minimal" + ], + "license": "ISC", + "main": "./cjs/index.js", + "module": "./esm/index.js", + "name": "flatted", + "repository": { + "type": "git", + "url": "git+https://github.com/WebReflection/flatted.git" + }, + "scripts": { + "build": "npm run cjs && npm run rollup:es && npm run rollup:babel && npm run min && npm run test && npm run size", + "cjs": "ascjs esm cjs", + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "min": "terser index.js -c -m -o min.js", + "rollup:babel": "rollup --config rollup/babel.config.js && sed -i.bck 's/^var /self./' index.js && rm -rf index.js.bck && drop-babel-typeof index.js", + "rollup:es": "rollup --config rollup/es.config.js && sed -i.bck 's/^var /self./' es.js && rm -rf es.js.bck", + "size": "cat index.js | wc -c;cat min.js | wc -c;gzip -c9 min.js | wc -c;cat min.js | brotli | wc -c; cat es.js | brotli | wc -c", + "test": "nyc node test/index.js" + }, + "type": "module", + "types": "types.d.ts", + "unpkg": "min.js", + "version": "3.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/fs.realpath/package.json b/tools/node_modules/eslint/node_modules/fs.realpath/package.json index 3edc57d21c7137..9007f26d64f78d 100644 --- a/tools/node_modules/eslint/node_modules/fs.realpath/package.json +++ b/tools/node_modules/eslint/node_modules/fs.realpath/package.json @@ -1,26 +1,36 @@ { - "name": "fs.realpath", - "version": "1.0.0", - "description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails", - "main": "index.js", - "dependencies": {}, - "devDependencies": {}, - "scripts": { - "test": "tap test/*.js --cov" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" }, - "repository": { - "type": "git", - "url": "git+https://github.com/isaacs/fs.realpath.git" + "bugs": { + "url": "https://github.com/isaacs/fs.realpath/issues" }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails", + "devDependencies": {}, + "files": [ + "old.js", + "index.js" + ], + "homepage": "https://github.com/isaacs/fs.realpath#readme", "keywords": [ "realpath", "fs", "polyfill" ], - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", "license": "ISC", - "files": [ - "old.js", - "index.js" - ] -} + "main": "index.js", + "name": "fs.realpath", + "repository": { + "type": "git", + "url": "git+https://github.com/isaacs/fs.realpath.git" + }, + "scripts": { + "test": "tap test/*.js --cov" + }, + "version": "1.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/functional-red-black-tree/package.json b/tools/node_modules/eslint/node_modules/functional-red-black-tree/package.json index 13d6f270cb95a6..27edb36786b7e7 100644 --- a/tools/node_modules/eslint/node_modules/functional-red-black-tree/package.json +++ b/tools/node_modules/eslint/node_modules/functional-red-black-tree/package.json @@ -1,23 +1,22 @@ { - "name": "functional-red-black-tree", - "version": "1.0.1", - "description": "A fully persistent balanced binary search tree", - "main": "rbtree.js", - "directories": { - "test": "test" + "author": { + "name": "Mikola Lysenko" + }, + "bugs": { + "url": "https://github.com/mikolalysenko/functional-red-black-tree/issues" }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "A fully persistent balanced binary search tree", "devDependencies": { "iota-array": "^0.0.1", "tape": "^2.12.0" }, - "scripts": { - "test": "tape test/*.js" - }, - "repository": { - "type": "git", - "url": "git://github.com/mikolalysenko/functional-red-black-tree.git" + "directories": { + "test": "test" }, + "homepage": "https://github.com/mikolalysenko/functional-red-black-tree#readme", "keywords": [ "functional", "red", @@ -32,9 +31,15 @@ "data", "structure" ], - "author": "Mikola Lysenko", "license": "MIT", - "bugs": { - "url": "https://github.com/mikolalysenko/functional-red-black-tree/issues" - } -} + "main": "rbtree.js", + "name": "functional-red-black-tree", + "repository": { + "type": "git", + "url": "git://github.com/mikolalysenko/functional-red-black-tree.git" + }, + "scripts": { + "test": "tape test/*.js" + }, + "version": "1.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/glob-parent/index.js b/tools/node_modules/eslint/node_modules/glob-parent/index.js index 789dbbf2ff09ef..09e257ea306cd4 100644 --- a/tools/node_modules/eslint/node_modules/glob-parent/index.js +++ b/tools/node_modules/eslint/node_modules/glob-parent/index.js @@ -6,7 +6,7 @@ var isWin32 = require('os').platform() === 'win32'; var slash = '/'; var backslash = /\\/g; -var enclosure = /[\{\[].*[\/]*.*[\}\]]$/; +var enclosure = /[\{\[].*[\}\]]$/; var globby = /(^|[^\\])([\{\[]|\([^\)]+$)/; var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; @@ -14,6 +14,7 @@ var escaped = /\\([\!\*\?\|\[\]\(\)\{\}])/g; * @param {string} str * @param {Object} opts * @param {boolean} [opts.flipBackslashes=true] + * @returns {string} */ module.exports = function globParent(str, opts) { var options = Object.assign({ flipBackslashes: true }, opts); diff --git a/tools/node_modules/eslint/node_modules/glob-parent/package.json b/tools/node_modules/eslint/node_modules/glob-parent/package.json index 1dfd6bc39000f9..13ca103caf238f 100644 --- a/tools/node_modules/eslint/node_modules/glob-parent/package.json +++ b/tools/node_modules/eslint/node_modules/glob-parent/package.json @@ -1,32 +1,28 @@ { - "name": "glob-parent", - "version": "5.1.1", - "description": "Extract the non-magic parent path from a glob string.", - "author": "Gulp Team <team@gulpjs.com> (https://gulpjs.com/)", - "contributors": [ - "Elan Shanker (https://github.com/es128)", - "Blaine Bublitz <blaine.bublitz@gmail.com>" - ], - "repository": "gulpjs/glob-parent", - "license": "ISC", - "engines": { - "node": ">= 6" + "author": { + "name": "Gulp Team", + "email": "team@gulpjs.com", + "url": "https://gulpjs.com/" }, - "main": "index.js", - "files": [ - "LICENSE", - "index.js" - ], - "scripts": { - "lint": "eslint .", - "pretest": "npm run lint", - "test": "nyc mocha --async-only", - "azure-pipelines": "nyc mocha --async-only --reporter xunit -O output=test.xunit", - "coveralls": "nyc report --reporter=text-lcov | coveralls" + "bugs": { + "url": "https://github.com/gulpjs/glob-parent/issues" }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Elan Shanker", + "url": "https://github.com/es128" + }, + { + "name": "Blaine Bublitz", + "email": "blaine.bublitz@gmail.com" + } + ], "dependencies": { "is-glob": "^4.0.1" }, + "deprecated": false, + "description": "Extract the non-magic parent path from a glob string.", "devDependencies": { "coveralls": "^3.0.11", "eslint": "^2.13.1", @@ -35,6 +31,14 @@ "mocha": "^6.0.2", "nyc": "^13.3.0" }, + "engines": { + "node": ">= 6" + }, + "files": [ + "LICENSE", + "index.js" + ], + "homepage": "https://github.com/gulpjs/glob-parent#readme", "keywords": [ "glob", "parent", @@ -44,5 +48,20 @@ "directory", "base", "wildcard" - ] -} + ], + "license": "ISC", + "main": "index.js", + "name": "glob-parent", + "repository": { + "type": "git", + "url": "git+https://github.com/gulpjs/glob-parent.git" + }, + "scripts": { + "azure-pipelines": "nyc mocha --async-only --reporter xunit -O output=test.xunit", + "coveralls": "nyc report --reporter=text-lcov | coveralls", + "lint": "eslint .", + "pretest": "npm run lint", + "test": "nyc mocha --async-only" + }, + "version": "5.1.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/glob/package.json b/tools/node_modules/eslint/node_modules/glob/package.json index 6477c3070cb14e..bf5a63608ca133 100644 --- a/tools/node_modules/eslint/node_modules/glob/package.json +++ b/tools/node_modules/eslint/node_modules/glob/package.json @@ -1,21 +1,13 @@ { - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", - "name": "glob", - "description": "a little globber", - "version": "7.1.6", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/node-glob.git" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" }, - "main": "glob.js", - "files": [ - "glob.js", - "sync.js", - "common.js" - ], - "engines": { - "node": "*" + "bugs": { + "url": "https://github.com/isaacs/node-glob/issues" }, + "bundleDependencies": false, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -24,23 +16,41 @@ "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, + "deprecated": false, + "description": "a little globber", "devDependencies": { "mkdirp": "0", "rimraf": "^2.2.8", "tap": "^12.0.1", "tick": "0.0.6" }, + "engines": { + "node": "*" + }, + "files": [ + "glob.js", + "sync.js", + "common.js" + ], + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "homepage": "https://github.com/isaacs/node-glob#readme", + "license": "ISC", + "main": "glob.js", + "name": "glob", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-glob.git" + }, "scripts": { + "bench": "bash benchmark.sh", + "benchclean": "node benchclean.js", "prepublish": "npm run benchclean", + "prof": "bash prof.sh && cat profile.txt", "profclean": "rm -f v8.log profile.txt", "test": "tap test/*.js --cov", - "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js", - "bench": "bash benchmark.sh", - "prof": "bash prof.sh && cat profile.txt", - "benchclean": "node benchclean.js" + "test-regen": "npm run profclean && TEST_REGEN=1 node test/00-setup.js" }, - "license": "ISC", - "funding": { - "url": "https://github.com/sponsors/isaacs" - } -} + "version": "7.1.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/globals/globals.json b/tools/node_modules/eslint/node_modules/globals/globals.json index b85dc3f80d5148..d518aa24140cf1 100644 --- a/tools/node_modules/eslint/node_modules/globals/globals.json +++ b/tools/node_modules/eslint/node_modules/globals/globals.json @@ -1,5 +1,6 @@ { "builtin": { + "AggregateError": false, "Array": false, "ArrayBuffer": false, "Atomics": false, @@ -18,6 +19,7 @@ "escape": false, "eval": false, "EvalError": false, + "FinalizationRegistry": false, "Float32Array": false, "Float64Array": false, "Function": false, @@ -62,6 +64,7 @@ "URIError": false, "valueOf": false, "WeakMap": false, + "WeakRef": false, "WeakSet": false }, "es5": { @@ -224,6 +227,139 @@ "WeakMap": false, "WeakSet": false }, + "es2020": { + "Array": false, + "ArrayBuffer": false, + "Atomics": false, + "BigInt": false, + "BigInt64Array": false, + "BigUint64Array": false, + "Boolean": false, + "constructor": false, + "DataView": false, + "Date": false, + "decodeURI": false, + "decodeURIComponent": false, + "encodeURI": false, + "encodeURIComponent": false, + "Error": false, + "escape": false, + "eval": false, + "EvalError": false, + "Float32Array": false, + "Float64Array": false, + "Function": false, + "globalThis": false, + "hasOwnProperty": false, + "Infinity": false, + "Int16Array": false, + "Int32Array": false, + "Int8Array": false, + "isFinite": false, + "isNaN": false, + "isPrototypeOf": false, + "JSON": false, + "Map": false, + "Math": false, + "NaN": false, + "Number": false, + "Object": false, + "parseFloat": false, + "parseInt": false, + "Promise": false, + "propertyIsEnumerable": false, + "Proxy": false, + "RangeError": false, + "ReferenceError": false, + "Reflect": false, + "RegExp": false, + "Set": false, + "SharedArrayBuffer": false, + "String": false, + "Symbol": false, + "SyntaxError": false, + "toLocaleString": false, + "toString": false, + "TypeError": false, + "Uint16Array": false, + "Uint32Array": false, + "Uint8Array": false, + "Uint8ClampedArray": false, + "undefined": false, + "unescape": false, + "URIError": false, + "valueOf": false, + "WeakMap": false, + "WeakSet": false + }, + "es2021": { + "AggregateError": false, + "Array": false, + "ArrayBuffer": false, + "Atomics": false, + "BigInt": false, + "BigInt64Array": false, + "BigUint64Array": false, + "Boolean": false, + "constructor": false, + "DataView": false, + "Date": false, + "decodeURI": false, + "decodeURIComponent": false, + "encodeURI": false, + "encodeURIComponent": false, + "Error": false, + "escape": false, + "eval": false, + "EvalError": false, + "FinalizationRegistry": false, + "Float32Array": false, + "Float64Array": false, + "Function": false, + "globalThis": false, + "hasOwnProperty": false, + "Infinity": false, + "Int16Array": false, + "Int32Array": false, + "Int8Array": false, + "isFinite": false, + "isNaN": false, + "isPrototypeOf": false, + "JSON": false, + "Map": false, + "Math": false, + "NaN": false, + "Number": false, + "Object": false, + "parseFloat": false, + "parseInt": false, + "Promise": false, + "propertyIsEnumerable": false, + "Proxy": false, + "RangeError": false, + "ReferenceError": false, + "Reflect": false, + "RegExp": false, + "Set": false, + "SharedArrayBuffer": false, + "String": false, + "Symbol": false, + "SyntaxError": false, + "toLocaleString": false, + "toString": false, + "TypeError": false, + "Uint16Array": false, + "Uint32Array": false, + "Uint8Array": false, + "Uint8ClampedArray": false, + "undefined": false, + "unescape": false, + "URIError": false, + "valueOf": false, + "WeakMap": false, + "WeakRef": false, + "WeakSet": false + }, "browser": { "AbortController": false, "AbortSignal": false, @@ -546,6 +682,7 @@ "OfflineAudioContext": false, "offscreenBuffering": false, "OffscreenCanvas": true, + "OffscreenCanvasRenderingContext2D": false, "onabort": true, "onafterprint": true, "onanimationend": true, @@ -1203,20 +1340,19 @@ "quit": false }, "wsh": { - "ActiveXObject": true, - "CollectGarbage": true, - "Debug": true, - "Enumerator": true, - "GetObject": true, - "RuntimeObject": true, - "ScriptEngine": true, - "ScriptEngineBuildVersion": true, - "ScriptEngineMajorVersion": true, - "ScriptEngineMinorVersion": true, - "VBArray": true, - "WScript": true, - "WSH": true, - "XDomainRequest": true + "ActiveXObject": false, + "CollectGarbage": false, + "Debug": false, + "Enumerator": false, + "GetObject": false, + "RuntimeObject": false, + "ScriptEngine": false, + "ScriptEngineBuildVersion": false, + "ScriptEngineMajorVersion": false, + "ScriptEngineMinorVersion": false, + "VBArray": false, + "WScript": false, + "WSH": false }, "jquery": { "$": false, @@ -1299,7 +1435,6 @@ "Try": false }, "meteor": { - "_": false, "$": false, "Accounts": false, "AccountsClient": false, @@ -1485,6 +1620,7 @@ }, "atomtest": { "advanceClock": false, + "atom": false, "fakeClearInterval": false, "fakeClearTimeout": false, "fakeSetInterval": false, @@ -1539,17 +1675,25 @@ "exportFunction": false, "GM": false, "GM_addStyle": false, + "GM_addValueChangeListener": false, "GM_deleteValue": false, + "GM_download": false, "GM_getResourceText": false, "GM_getResourceURL": false, + "GM_getTab": false, + "GM_getTabs": false, "GM_getValue": false, "GM_info": false, "GM_listValues": false, "GM_log": false, + "GM_notification": false, "GM_openInTab": false, "GM_registerMenuCommand": false, + "GM_removeValueChangeListener": false, + "GM_saveTab": false, "GM_setClipboard": false, "GM_setValue": false, + "GM_unregisterMenuCommand": false, "GM_xmlhttpRequest": false, "unsafeWindow": false }, diff --git a/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/license b/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/license new file mode 100644 index 00000000000000..3e4c85ab7effdc --- /dev/null +++ b/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https:/sindresorhus.com) + +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. diff --git a/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/package.json b/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/package.json new file mode 100644 index 00000000000000..607b879b96506a --- /dev/null +++ b/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/package.json @@ -0,0 +1,67 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/type-fest/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "A collection of essential TypeScript types", + "devDependencies": { + "@sindresorhus/tsconfig": "~0.7.0", + "tsd": "^0.13.1", + "typescript": "^4.1.2", + "xo": "^0.35.0" + }, + "engines": { + "node": ">=10" + }, + "files": [ + "index.d.ts", + "base.d.ts", + "source", + "ts41" + ], + "funding": "https://github.com/sponsors/sindresorhus", + "homepage": "https://github.com/sindresorhus/type-fest#readme", + "keywords": [ + "typescript", + "ts", + "types", + "utility", + "util", + "utilities", + "omit", + "merge", + "json" + ], + "license": "(MIT OR CC0-1.0)", + "name": "type-fest", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/type-fest.git" + }, + "scripts": { + "//test": "xo && tsd && tsc", + "test": "xo && tsc" + }, + "types": "./index.d.ts", + "typesVersions": { + ">=4.1": { + "*": [ + "ts41/*" + ] + } + }, + "version": "0.20.2", + "xo": { + "rules": { + "@typescript-eslint/ban-types": "off", + "@typescript-eslint/indent": "off", + "node/no-unsupported-features/es-builtins": "off" + } + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/readme.md b/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/readme.md new file mode 100644 index 00000000000000..714df7868e1372 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/globals/node_modules/type-fest/readme.md @@ -0,0 +1,658 @@ +<div align="center"> + <br> + <br> + <img src="media/logo.svg" alt="type-fest" height="300"> + <br> + <br> + <b>A collection of essential TypeScript types</b> + <br> + <hr> +</div> +<br> +<br> + +[](https://giphy.com/gifs/illustration-rainbow-unicorn-26AHG5KGFxSkUWw1i) +<!-- Commented out until they actually show anything +[](https://www.npmjs.com/package/type-fest?activeTab=dependents) [](https://www.npmjs.com/package/type-fest) +--> + +Many of the types here should have been built-in. You can help by suggesting some of them to the [TypeScript project](https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md). + +Either add this package as a dependency or copy-paste the needed types. No credit required. 👌 + +PR welcome for additional commonly needed types and docs improvements. Read the [contributing guidelines](.github/contributing.md) first. + +## Install + +``` +$ npm install type-fest +``` + +*Requires TypeScript >=3.4* + +## Usage + +```ts +import {Except} from 'type-fest'; + +type Foo = { + unicorn: string; + rainbow: boolean; +}; + +type FooWithoutRainbow = Except<Foo, 'rainbow'>; +//=> {unicorn: string} +``` + +## API + +Click the type names for complete docs. + +### Basic + +- [`Primitive`](source/basic.d.ts) - Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive). +- [`Class`](source/basic.d.ts) - Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes). +- [`TypedArray`](source/basic.d.ts) - Matches any [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), like `Uint8Array` or `Float64Array`. +- [`JsonObject`](source/basic.d.ts) - Matches a JSON object. +- [`JsonArray`](source/basic.d.ts) - Matches a JSON array. +- [`JsonValue`](source/basic.d.ts) - Matches any valid JSON value. +- [`ObservableLike`](source/basic.d.ts) - Matches a value that is like an [Observable](https://github.com/tc39/proposal-observable). + +### Utilities + +- [`Except`](source/except.d.ts) - Create a type from an object type without certain keys. This is a stricter version of [`Omit`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#the-omit-helper-type). +- [`Mutable`](source/mutable.d.ts) - Convert an object with `readonly` keys into a mutable object. The inverse of `Readonly<T>`. +- [`Merge`](source/merge.d.ts) - Merge two types into a new type. Keys of the second type overrides keys of the first type. +- [`MergeExclusive`](source/merge-exclusive.d.ts) - Create a type that has mutually exclusive keys. +- [`RequireAtLeastOne`](source/require-at-least-one.d.ts) - Create a type that requires at least one of the given keys. +- [`RequireExactlyOne`](source/require-exactly-one.d.ts) - Create a type that requires exactly a single key of the given keys and disallows more. +- [`PartialDeep`](source/partial-deep.d.ts) - Create a deeply optional version of another type. Use [`Partial<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) if you only need one level deep. +- [`ReadonlyDeep`](source/readonly-deep.d.ts) - Create a deeply immutable version of an `object`/`Map`/`Set`/`Array` type. Use [`Readonly<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) if you only need one level deep. +- [`LiteralUnion`](source/literal-union.d.ts) - Create a union type by combining primitive types and literal types without sacrificing auto-completion in IDEs for the literal type part of the union. Workaround for [Microsoft/TypeScript#29729](https://github.com/Microsoft/TypeScript/issues/29729). +- [`Promisable`](source/promisable.d.ts) - Create a type that represents either the value or the value wrapped in `PromiseLike`. +- [`Opaque`](source/opaque.d.ts) - Create an [opaque type](https://codemix.com/opaque-types-in-javascript/). +- [`SetOptional`](source/set-optional.d.ts) - Create a type that makes the given keys optional. +- [`SetRequired`](source/set-required.d.ts) - Create a type that makes the given keys required. +- [`ValueOf`](source/value-of.d.ts) - Create a union of the given object's values, and optionally specify which keys to get the values from. +- [`PromiseValue`](source/promise-value.d.ts) - Returns the type that is wrapped inside a `Promise`. +- [`AsyncReturnType`](source/async-return-type.d.ts) - Unwrap the return type of a function that returns a `Promise`. +- [`ConditionalKeys`](source/conditional-keys.d.ts) - Extract keys from a shape where values extend the given `Condition` type. +- [`ConditionalPick`](source/conditional-pick.d.ts) - Like `Pick` except it selects properties from a shape where the values extend the given `Condition` type. +- [`ConditionalExcept`](source/conditional-except.d.ts) - Like `Omit` except it removes properties from a shape where the values extend the given `Condition` type. +- [`UnionToIntersection`](source/union-to-intersection.d.ts) - Convert a union type to an intersection type. +- [`Stringified`](source/stringified.d.ts) - Create a type with the keys of the given type changed to `string` type. +- [`FixedLengthArray`](source/fixed-length-array.d.ts) - Create a type that represents an array of the given type and length. +- [`IterableElement`](source/iterable-element.d.ts) - Get the element type of an `Iterable`/`AsyncIterable`. For example, an array or a generator. +- [`Entry`](source/entry.d.ts) - Create a type that represents the type of an entry of a collection. +- [`Entries`](source/entries.d.ts) - Create a type that represents the type of the entries of a collection. +- [`SetReturnType`](source/set-return-type.d.ts) - Create a function type with a return type of your choice and the same parameters as the given function type. +- [`Asyncify`](source/asyncify.d.ts) - Create an async version of the given function type. + +### Template literal types + +*Note:* These require [TypeScript 4.1 or newer](https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#template-literal-types). + +- [`CamelCase`](ts41/camel-case.d.ts) – Convert a string literal to camel-case (`fooBar`). +- [`KebabCase`](ts41/kebab-case.d.ts) – Convert a string literal to kebab-case (`foo-bar`). +- [`PascalCase`](ts41/pascal-case.d.ts) – Converts a string literal to pascal-case (`FooBar`) +- [`SnakeCase`](ts41/snake-case.d.ts) – Convert a string literal to snake-case (`foo_bar`). +- [`DelimiterCase`](ts41/delimiter-case.d.ts) – Convert a string literal to a custom string delimiter casing. + +### Miscellaneous + +- [`PackageJson`](source/package-json.d.ts) - Type for [npm's `package.json` file](https://docs.npmjs.com/creating-a-package-json-file). +- [`TsConfigJson`](source/tsconfig-json.d.ts) - Type for [TypeScript's `tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) (TypeScript 3.7). + +## Declined types + +*If we decline a type addition, we will make sure to document the better solution here.* + +- [`Diff` and `Spread`](https://github.com/sindresorhus/type-fest/pull/7) - The PR author didn't provide any real-world use-cases and the PR went stale. If you think this type is useful, provide some real-world use-cases and we might reconsider. +- [`Dictionary`](https://github.com/sindresorhus/type-fest/issues/33) - You only save a few characters (`Dictionary<number>` vs `Record<string, number>`) from [`Record`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434), which is more flexible and well-known. Also, you shouldn't use an object as a dictionary. We have `Map` in JavaScript now. +- [`SubType`](https://github.com/sindresorhus/type-fest/issues/22) - The type is powerful, but lacks good use-cases and is prone to misuse. +- [`ExtractProperties` and `ExtractMethods`](https://github.com/sindresorhus/type-fest/pull/4) - The types violate the single responsibility principle. Instead, refine your types into more granular type hierarchies. + +## Tips + +### Built-in types + +There are many advanced types most users don't know about. + +- [`Partial<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1401-L1406) - Make all properties in `T` optional. + <details> + <summary> + Example + </summary> + + [Playground](https://www.typescriptlang.org/play/#code/JYOwLgpgTgZghgYwgAgHIHsAmEDC6QzADmyA3gLABQyycADnanALYQBcyAzmFKEQNxUaddFDAcQAV2YAjaIMoBfKlQQAbOJ05osEAIIMAQpOBrsUMkOR1eANziRkCfISKSoD4Pg4ZseAsTIALyW1DS0DEysHADkvvoMMQA0VsKi4sgAzAAMuVaKClY2wPaOknSYDrguADwA0sgQAB6QIJjaANYQAJ7oMDp+LsQAfAAUXd0cdUnI9mo+uv6uANp1ALoAlKHhyGAAFsCcAHTOAW4eYF4gyxNrwbNwago0ypRWp66jH8QcAApwYmAjxq8SWIy2FDCNDA3ToKFBQyIdR69wmfQG1TOhShyBgomQX3w3GQE2Q6IA8jIAFYQBBgI4TTiEs5bTQYsFInrLTbbHZOIlgZDlSqQABqj0kKBC3yINx6a2xfOQwH6o2FVXFaklwSCIUkbQghBAEEwENSfNOlykEGefNe5uhB2O6sgS3GPRmLogmslG1tLxUOKgEDA7hAuydtteryAA) + + ```ts + interface NodeConfig { + appName: string; + port: number; + } + + class NodeAppBuilder { + private configuration: NodeConfig = { + appName: 'NodeApp', + port: 3000 + }; + + private updateConfig<Key extends keyof NodeConfig>(key: Key, value: NodeConfig[Key]) { + this.configuration[key] = value; + } + + config(config: Partial<NodeConfig>) { + type NodeConfigKey = keyof NodeConfig; + + for (const key of Object.keys(config) as NodeConfigKey[]) { + const updateValue = config[key]; + + if (updateValue === undefined) { + continue; + } + + this.updateConfig(key, updateValue); + } + + return this; + } + } + + // `Partial<NodeConfig>`` allows us to provide only a part of the + // NodeConfig interface. + new NodeAppBuilder().config({appName: 'ToDoApp'}); + ``` + </details> + +- [`Required<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1408-L1413) - Make all properties in `T` required. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgGED21VQGJZwC2wA3gFCjXAzFJgA2A-AFzADOUckA5gNxUaIYjA4ckvGG07c+g6gF8KQkAgCuEFFDA5O6gEbEwUbLm2ESwABQIixACJIoSdgCUYAR3Vg4MACYAPGYuFvYAfACU5Ko0APRxwADKMBD+wFAAFuh2Vv7OSBlYGdmc8ABu8LHKsRyGxqY4oQT21pTCIHQMjOwA5DAAHgACxAAOjDAAdChYxL0ANLHUouKSMH0AEmAAhJhY6ozpAJ77GTCMjMCiV0ToSAb7UJPPC9WRgrEJwAAqR6MwSRQPFGUFocDgRHYxnEfGAowh-zgUCOwF6KwkUl6tXqJhCeEsxDaS1AXSYfUGI3GUxmc0WSneQA) + + ```ts + interface ContactForm { + email?: string; + message?: string; + } + + function submitContactForm(formData: Required<ContactForm>) { + // Send the form data to the server. + } + + submitContactForm({ + email: 'ex@mple.com', + message: 'Hi! Could you tell me more about…', + }); + + // TypeScript error: missing property 'message' + submitContactForm({ + email: 'ex@mple.com', + }); + ``` + </details> + +- [`Readonly<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1415-L1420) - Make all properties in `T` readonly. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4UwOwVwW2AZA9gc3mAbmANsA3gKFCOAHkAzMgGkOJABEwAjKZa2kAUQCcvEu32AMQCGAF2FYBIAL4BufDRABLCKLBcywgMZgEKZOoDCiCGSXI8i4hGEwwALmABnUVxXJ57YFgzZHSVF8sT1BpBSItLGEnJz1kAy5LLy0TM2RHACUwYQATEywATwAeAITjU3MAPnkrCJMXLigtUT4AClxgGztKbyDgaX99I1TzAEokr1BRAAslJwA6FIqLAF48TtswHp9MHDla9hJGACswZvmyLjAwAC8wVpm5xZHkUZDaMKIwqyWXYCW0oN4sNlsA1h0ug5gAByACyBQAggAHJHQ7ZBIFoXbzBjMCz7OoQP5YIaJNYQMAAdziCVaALGNSIAHomcAACoFJFgADKWjcSNEwG4vC4ji0wggEEQguiTnMEGALWAV1yAFp8gVgEjeFyuKICvMrCTgVxnst5jtsGC4ljsPNhXxGaAWcAAOq6YRXYDCRg+RWIcA5JSC+kWdCepQ+v3RYCU3RInzRMCGwlpC19NYBW1Ye08R1AA) + + ```ts + enum LogLevel { + Off, + Debug, + Error, + Fatal + }; + + interface LoggerConfig { + name: string; + level: LogLevel; + } + + class Logger { + config: Readonly<LoggerConfig>; + + constructor({name, level}: LoggerConfig) { + this.config = {name, level}; + Object.freeze(this.config); + } + } + + const config: LoggerConfig = { + name: 'MyApp', + level: LogLevel.Debug + }; + + const logger = new Logger(config); + + // TypeScript Error: cannot assign to read-only property. + logger.config.level = LogLevel.Error; + + // We are able to edit config variable as we please. + config.level = LogLevel.Error; + ``` + </details> + +- [`Pick<T, K>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1422-L1427) - From `T`, pick a set of properties whose keys are in the union `K`. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4SwOwFwUwJwGYEMDGNgEE5TCgNugN4BQoZwOUBAXMAM5RyQDmA3KeSFABYCuAtgCMISMHloMmENh04oA9tBjQJjFuzIBfYrOAB6PcADCcGElh1gEGAHcKATwAO6ebyjB5CTNlwFwSxFR0BX5HeToYABNgBDh5fm8cfBg6AHIKG3ldA2BHOOcfFNpUygJ0pAhokr4hETFUgDpswywkggAFUwA3MFtgAF5gQgowKhhVKTYKGuFRcXo1aVZgbTIoJ3RW3xhOmB6+wfbcAGsAHi3kgBpgEtGy4AAfG54BWfqAPnZm4AAlZUj4MAkMA8GAGB4vEgfMlLLw6CwPBA8PYRmMgZVgAC6CgmI4cIommQELwICh8RBgKZKvALh1ur0bHQABR5PYMui0Wk7em2ADaAF0AJS0AASABUALIAGQAogR+Mp3CROCAFBBwVC2ikBpj5CgBIqGjizLA5TAFdAmalImAuqlBRoVQh5HBgEy1eDWfs7J5cjzGYKhroVfpDEhHM4MV6GRR5NN0JrtnRg6BVirTFBeHAKYmYY6QNpdB73LmCJZBlSAXAubtvczeSmQMNSuMbmKNgBlHFgPEUNwusBIPAAQlS1xetTmxT0SDoESgdD0C4aACtHMwxytLrohawgA) + + ```ts + interface Article { + title: string; + thumbnail: string; + content: string; + } + + // Creates new type out of the `Article` interface composed + // from the Articles' two properties: `title` and `thumbnail`. + // `ArticlePreview = {title: string; thumbnail: string}` + type ArticlePreview = Pick<Article, 'title' | 'thumbnail'>; + + // Render a list of articles using only title and description. + function renderArticlePreviews(previews: ArticlePreview[]): HTMLElement { + const articles = document.createElement('div'); + + for (const preview of previews) { + // Append preview to the articles. + } + + return articles; + } + + const articles = renderArticlePreviews([ + { + title: 'TypeScript tutorial!', + thumbnail: '/assets/ts.jpg' + } + ]); + ``` + </details> + +- [`Record<K, T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1429-L1434) - Construct a type with a set of properties `K` of type `T`. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/AQ4ejYAUHsGcCWAXBMB2dgwGbAKYC2ADgDYwCeeemCaWArgE7ADGMxAhmuQHQBQoYEnJE8wALKEARnkaxEKdMAC8wAOS0kstGuAAfdQBM8ANzxlRjXQbVaWACwC0JPB0NqA3HwGgIwAJJoWozYHCxixnAsjAhStADmwESMMJYo1Fi4HMCIaPEu+MRklHj8gpqyoeHAAKJFFFTAAN4+giDYCIxwSAByHAR4AFw5SDF5Xm2gJBzdfQPD3WPxE5PAlBxdAPLYNQAelgh4aOHDaPQEMowrIAC+3oJ+AMKMrlrAXFhSAFZ4LEhC9g4-0BmA4JBISXgiCkBQABpILrJ5MhUGhYcATGD6Bk4Hh-jNgABrPDkOBlXyQAAq9ngYmJpOAAHcEOCRjAXqwYODfoo6DhakUSph+Uh7GI4P0xER4Cj0OSQGwMP8tP1hgAlX7swwAHgRl2RvIANALSA08ABtAC6AD4VM1Wm0Kow0MMrYaHYJjGYLLJXZb3at1HYnC43Go-QHQDcvA6-JsmEJXARgCDgMYWAhjIYhDAU+YiMAAFIwex0ZmilMITCGF79TLAGRsAgJYAAZRwSEZGzEABFTOZUrJ5Yn+jwnWgeER6HB7AAKJrADpdXqS4ZqYultTG6azVfqHswPBbtauLY7fayQ7HIbAAAMwBuAEoYw9IBq2Ixs9h2eFMOQYPQObALQKJgggABeYhghCIpikkKRpOQRIknAsZUiIeCttECBEP8NSMCkjDDAARMGziuIYxHwYOjDCMBmDNnAuTxA6irdCOBB1Lh5Dqpqn66tISIykawBnOCtqqC0gbjqc9DgpGkxegOliyfJDrRkAA) + + ```ts + // Positions of employees in our company. + type MemberPosition = 'intern' | 'developer' | 'tech-lead'; + + // Interface describing properties of a single employee. + interface Employee { + firstName: string; + lastName: string; + yearsOfExperience: number; + } + + // Create an object that has all possible `MemberPosition` values set as keys. + // Those keys will store a collection of Employees of the same position. + const team: Record<MemberPosition, Employee[]> = { + intern: [], + developer: [], + 'tech-lead': [], + }; + + // Our team has decided to help John with his dream of becoming Software Developer. + team.intern.push({ + firstName: 'John', + lastName: 'Doe', + yearsOfExperience: 0 + }); + + // `Record` forces you to initialize all of the property keys. + // TypeScript Error: "tech-lead" property is missing + const teamEmpty: Record<MemberPosition, null> = { + intern: null, + developer: null, + }; + ``` + </details> + +- [`Exclude<T, U>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1436-L1439) - Exclude from `T` those types that are assignable to `U`. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgMrQG7QMIHsQzADmyA3gFDLIAOuUYAXMiAK4A2byAPsgM5hRQJHqwC2AI2gBucgF9y5MAE9qKAEoQAjiwj8AEnBAATNtGQBeZAAooWphu26wAGmS3e93bRC8IASgsAPmRDJRlyAHoI5ABRAA8ENhYjFFYOZGVVZBgoXFFkAAM0zh5+QRBhZhYJaAKAOkjogEkQZAQ4X2QAdwALCFbaemRgXmQtFjhOMFwq9K6ULuB0lk6U+HYwZAxJnQaYFhAEMGB8ZCIIMAAFOjAANR2IK0HGWISklIAedCgsKDwCYgAbQA5M9gQBdVzFQJ+JhiSRQMiUYYwayZCC4VHPCzmSzAspCYEBWxgFhQAZwKC+FpgJ43VwARgADH4ZFQSWSBjcZPJyPtDsdTvxKWBvr8rD1DCZoJ5HPopaYoK4EPhCEQmGKcKriLCtrhgEYkVQVT5Nr4fmZLLZtMBbFZgT0wGBqES6ghbHBIJqoBKFdBWQpjfh+DQbhY2tqiHVsbjLMVkAB+ZAAZiZaeQTHOVxu9ySjxNaujNwDVHNvzqbBGkBAdPoAfkQA) + + ```ts + interface ServerConfig { + port: null | string | number; + } + + type RequestHandler = (request: Request, response: Response) => void; + + // Exclude `null` type from `null | string | number`. + // In case the port is equal to `null`, we will use default value. + function getPortValue(port: Exclude<ServerConfig['port'], null>): number { + if (typeof port === 'string') { + return parseInt(port, 10); + } + + return port; + } + + function startServer(handler: RequestHandler, config: ServerConfig): void { + const server = require('http').createServer(handler); + + const port = config.port === null ? 3000 : getPortValue(config.port); + server.listen(port); + } + ``` + </details> + +- [`Extract<T, U>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1441-L1444) - Extract from `T` those types that are assignable to `U`. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXzSwEdkQBJYACgEoAueVZAWwCMQYBuAKDDwGcM8MgBF4AXngBlAJ6scESgHIRi6ty5ZUGdoihgEABXZ888AN5d48ANoiAuvUat23K6ihMQ9ATE0BzV3goPy8GZjZOLgBfLi4Aejj4AEEICBwAdz54MAALKFQQ+BxEeAAHY1NgKAwoIKy0grr4DByEUpgccpgMaXgAaxBerCzi+B9-ZulygDouFHRsU1z8kKMYE1RhaqgAHkt4AHkWACt4EAAPbVRgLLWNgBp9gGlBs8uQa6yAUUuYPQwdgNpKM7nh7mMML4CgA+R5WABqUAgpDeVxuhxO1he0jsXGh8EoOBO9COx3BQPo2PBADckaR6IjkSA6PBqTgsMBzPsicdrEC7OJWXSQNwYvFEgAVTS9JLXODpeDpKBZFg4GCoWa8VACIJykAKiQWKy2YQOAioYikCg0OEMDyhRSy4DyxS24KhAAMjyi6gS8AAwjh5OD0iBFHAkJoEOksC1mnkMJq8gUQKDNttKPlnfrwYp3J5XfBHXqoKpfYkAOI4ansTxaeDADmoRSCCBYAbxhC6TDx6rwYHIRX5bScjA4bLJwoDmDwDkfbA9JMrVMVdM1TN69LgkTgwgkchUahqIA) + + ```ts + declare function uniqueId(): number; + + const ID = Symbol('ID'); + + interface Person { + [ID]: number; + name: string; + age: number; + } + + // Allows changing the person data as long as the property key is of string type. + function changePersonData< + Obj extends Person, + Key extends Extract<keyof Person, string>, + Value extends Obj[Key] + > (obj: Obj, key: Key, value: Value): void { + obj[key] = value; + } + + // Tiny Andrew was born. + const andrew = { + [ID]: uniqueId(), + name: 'Andrew', + age: 0, + }; + + // Cool, we're fine with that. + changePersonData(andrew, 'name', 'Pony'); + + // Goverment didn't like the fact that you wanted to change your identity. + changePersonData(andrew, ID, uniqueId()); + ``` + </details> + +- [`NonNullable<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1446-L1449) - Exclude `null` and `undefined` from `T`. + <details> + <summary> + Example + </summary> + Works with <code>strictNullChecks</code> set to <code>true</code>. (Read more <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html">here</a>) + + [Playground](https://typescript-play.js.org/?target=6#code/C4TwDgpgBACg9gJ2AOQK4FsBGEFQLxQDOwCAlgHYDmUAPlORtrnQwDasDcAUFwPQBU-WAEMkUOADMowqAGNWwwoSgATCBIqlgpOOSjAAFsOBRSy1IQgr9cKJlSlW1mZYQA3HFH68u8xcoBlHA8EACEHJ08Aby4oKDBUTFZSWXjEFEYcAEIALihkXTR2YSSIAB54JDQsHAA+blj4xOTUsHSACkMzPKD3HHDHNQQAGjSkPMqMmoQASh7g-oihqBi4uNIpdraxPAI2VhmVxrX9AzMAOm2ppnwoAA4ABifuE4BfKAhWSyOTuK7CS7pao3AhXF5rV48E4ICDAVAIPT-cGQyG+XTEIgLMJLTx7CAAdygvRCA0iCHaMwarhJOIQjUBSHaACJHk8mYdeLwxtdcVAAOSsh58+lXdr7Dlcq7A3n3J4PEUdADMcspUE53OluAIUGVTx46oAKuAIAFZGQwCYAKIIBCILjUxaDHAMnla+iodjcIA) + + ```ts + type PortNumber = string | number | null; + + /** Part of a class definition that is used to build a server */ + class ServerBuilder { + portNumber!: NonNullable<PortNumber>; + + port(this: ServerBuilder, port: PortNumber): ServerBuilder { + if (port == null) { + this.portNumber = 8000; + } else { + this.portNumber = port; + } + + return this; + } + } + + const serverBuilder = new ServerBuilder(); + + serverBuilder + .port('8000') // portNumber = '8000' + .port(null) // portNumber = 8000 + .port(3000); // portNumber = 3000 + + // TypeScript error + serverBuilder.portNumber = null; + ``` + </details> + +- [`Parameters<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1451-L1454) - Obtain the parameters of a function type in a tuple. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/GYVwdgxgLglg9mABAZwBYmMANgUwBQxgAOIUAXIgIZgCeA2gLoCUFAbnDACaIDeAUIkQB6IYgCypSlBxUATrMo1ECsJzgBbLEoipqAc0J7EMKMgDkiHLnU4wp46pwAPHMgB0fAL58+oSLARECEosLAA5ABUYG2QAHgAxJGdpVWREPDdMylk9ZApqemZEAF4APipacrw-CApEgBogkKwAYThwckQwEHUAIxxZJl4BYVEImiIZKF0oZRwiWVdbeygJmThgOYgcGFYcbhqApCJsyhtpWXcR1cnEePBoeDAABVPzgbTixFeFd8uEsClADcIxGiygIFkSEOT3SmTc2VydQeRx+ZxwF2QQ34gkEwDgsnSuFmMBKiAADEDjIhYk1Qm0OlSYABqZnYka4xA1DJZHJYkGc7yCbyeRA+CAIZCzNAYbA4CIAdxg2zJwVCkWirjwMswuEaACYmCCgA) + + ```ts + function shuffle(input: any[]): void { + // Mutate array randomly changing its' elements indexes. + } + + function callNTimes<Fn extends (...args: any[]) => any> (func: Fn, callCount: number) { + // Type that represents the type of the received function parameters. + type FunctionParameters = Parameters<Fn>; + + return function (...args: FunctionParameters) { + for (let i = 0; i < callCount; i++) { + func(...args); + } + } + } + + const shuffleTwice = callNTimes(shuffle, 2); + ``` + </details> + +- [`ConstructorParameters<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1456-L1459) - Obtain the parameters of a constructor function type in a tuple. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECCBOAXAlqApgWQPYBM0mgG8AoaaFRENALmgkXmQDsBzAblOmCycTV4D8teo1YdO3JiICuwRFngAKClWENmLAJRFOZRAAtkEAHQq00ALzlklNBzIBfYk+KhIMAJJTEYJsDQAwmDA+mgAPAAq0GgAHnxMODCKTGgA7tCKxllg8CwQtL4AngDaALraFgB80EWa1SRkAA6MAG5gfNAB4FABPDJyCrQR9tDNyG0dwMGhtBhgjWEiGgA00F70vv4RhY3hEZXVVinpc42KmuJkkv3y8Bly8EPaDWTkhiZd7r3e8LK3llwGCMXGQWGhEOsfH5zJlsrl8p0+gw-goAAo5MAAW3BaHgEEilU0tEhmzQ212BJ0ry4SOg+kg+gBBiMximIGA0nAfAQLGk2N4EAAEgzYcYcnkLsRdDTvNEYkYUKwSdCme9WdM0MYwYhFPSIPpJdTkAAzDKxBUaZX+aAAQgsVmkCTQxuYaBw2ng4Ok8CYcotSu8pMur09iG9vuObxZnx6SN+AyUWTF8MN0CcZE4Ywm5jZHK5aB5fP4iCFIqT4oRRTKRLo6lYVNeAHpG50wOzOe1zHr9NLQ+HoABybsD4HOKXXRA1JCoKhBELmI5pNaB6Fz0KKBAodDYPAgSUTmqYsAALx4m5nC6nW9nGq14KtaEUA9gR9PvuNCjQ9BgACNvcwNBtAcLiAA) + + ```ts + class ArticleModel { + title: string; + content?: string; + + constructor(title: string) { + this.title = title; + } + } + + class InstanceCache<T extends (new (...args: any[]) => any)> { + private ClassConstructor: T; + private cache: Map<string, InstanceType<T>> = new Map(); + + constructor (ctr: T) { + this.ClassConstructor = ctr; + } + + getInstance (...args: ConstructorParameters<T>): InstanceType<T> { + const hash = this.calculateArgumentsHash(...args); + + const existingInstance = this.cache.get(hash); + if (existingInstance !== undefined) { + return existingInstance; + } + + return new this.ClassConstructor(...args); + } + + private calculateArgumentsHash(...args: any[]): string { + // Calculate hash. + return 'hash'; + } + } + + const articleCache = new InstanceCache(ArticleModel); + const amazonArticle = articleCache.getInstance('Amazon forests burining!'); + ``` + </details> + +- [`ReturnType<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1461-L1464) – Obtain the return type of a function type. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + /** Provides every element of the iterable `iter` into the `callback` function and stores the results in an array. */ + function mapIter< + Elem, + Func extends (elem: Elem) => any, + Ret extends ReturnType<Func> + >(iter: Iterable<Elem>, callback: Func): Ret[] { + const mapped: Ret[] = []; + + for (const elem of iter) { + mapped.push(callback(elem)); + } + + return mapped; + } + + const setObject: Set<string> = new Set(); + const mapObject: Map<number, string> = new Map(); + + mapIter(setObject, (value: string) => value.indexOf('Foo')); // number[] + + mapIter(mapObject, ([key, value]: [number, string]) => { + return key % 2 === 0 ? value : 'Odd'; + }); // string[] + ``` + </details> + +- [`InstanceType<T>`](https://github.com/Microsoft/TypeScript/blob/2961bc3fc0ea1117d4e53bc8e97fa76119bc33e3/src/lib/es5.d.ts#L1466-L1469) – Obtain the instance type of a constructor function type. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/MYGwhgzhAECSAmICmBlJAnAbgS2E6A3gFDTTwD2AcuQC4AW2AdgOYAUAlAFzSbnbyEAvkWFFQkGJSQB3GMVI1sNZNwg10TZgG4S0YOUY0kh1es07d+xmvQBXYDXLpWi5UlMaWAGj0GjJ6BtNdkJdBQYIADpXZGgAXmgYpB1ScOwoq38aeN9DYxoU6GFRKzVoJjUwRjwAYXJbPPRuAFkwAAcAHgAxBodsAx9GWwBbACMMAD4cxhloVraOCyYjdAAzMDxoOut1e0d0UNIZ6WhWSPOwdGYIbiqATwBtAF0uaHudUQB6ACpv6ABpJBINqJdAbADW0Do5BOw3u5R2VTwMHIq2gAANtjZ0bkbHsnFCwJh8ONjHp0EgwEZ4JFoN9PkRVr1FAZoMwkDRYIjqkgOrosepoEgAB7+eAwAV2BxOLy6ACCVxgIrFEoMeOl6AACpcwMMORgIB1JRMiBNWKVdhruJKfOdIpdrtwFddXlzKjyACp3Nq842HaDIbL6BrZBIVGhIpB1EMYSLsmjmtWW-YhAA+qegAAYLKQLQj3ZsEsdccmnGcLor2Dn8xGedHGpEIBzEzspfsfMHDNAANTQACMVaIljV5GQkRA5DYmIpVKQAgAJARO9le33BDXIyi0YuLW2nJFGLqkOvxFB0YPdBSaLZ0IwNzyPkO8-xkGgsLh8Al427a3hWAhXwwHA8EHT5PmgAB1bAQBAANJ24adKWpft72RaBUTgRBUCAj89HAM8xCTaBjggABRQx0DuHJv25P9dCkWRZVIAAiBjoFImpmjlFBgA0NpsjadByDacgIDAEAIAAQmYpjoGYgAZSBsmGPw6DtZiiFA8CoJguDmAQmoZ2QvtUKQLdoAYmBTwgdEiCAA) + + ```ts + class IdleService { + doNothing (): void {} + } + + class News { + title: string; + content: string; + + constructor(title: string, content: string) { + this.title = title; + this.content = content; + } + } + + const instanceCounter: Map<Function, number> = new Map(); + + interface Constructor { + new(...args: any[]): any; + } + + // Keep track how many instances of `Constr` constructor have been created. + function getInstance< + Constr extends Constructor, + Args extends ConstructorParameters<Constr> + >(constructor: Constr, ...args: Args): InstanceType<Constr> { + let count = instanceCounter.get(constructor) || 0; + + const instance = new constructor(...args); + + instanceCounter.set(constructor, count + 1); + + console.log(`Created ${count + 1} instances of ${Constr.name} class`); + + return instance; + } + + + const idleService = getInstance(IdleService); + // Will log: `Created 1 instances of IdleService class` + const newsEntry = getInstance(News, 'New ECMAScript proposals!', 'Last month...'); + // Will log: `Created 1 instances of News class` + ``` + </details> + +- [`Omit<T, K>`](https://github.com/microsoft/TypeScript/blob/71af02f7459dc812e85ac31365bfe23daf14b4e4/src/lib/es5.d.ts#L1446) – Constructs a type by picking all properties from T and then removing K. + <details> + <summary> + Example + </summary> + + [Playground](https://typescript-play.js.org/?target=6#code/JYOwLgpgTgZghgYwgAgIImAWzgG2QbwChlks4BzCAVShwC5kBnMKUcgbmKYAcIFgIjBs1YgOXMpSFMWbANoBdTiW5woFddwAW0kfKWEAvoUIB6U8gDCUCHEiNkICAHdkYAJ69kz4GC3JcPG4oAHteKDABBxCYNAxsPFBIWEQUCAAPJG4wZABySUFcgJAAEzMLXNV1ck0dIuCw6EjBADpy5AB1FAQ4EGQAV0YUP2AHDy8wEOQbUugmBLwtEIA3OcmQnEjuZBgQqE7gAGtgZAhwKHdkHFGwNvGUdDIcAGUliIBJEF3kAF5kAHlML4ADyPBIAGjyBUYRQAPnkqho4NoYQA+TiEGD9EAISIhPozErQMG4AASK2gn2+AApek9pCSXm8wFSQooAJQMUkAFQAsgAZACiOAgmDOOSIJAQ+OYyGl4DgoDmf2QJRCCH6YvALQQNjsEGFovF1NyJWAy1y7OUyHMyE+yRAuFImG4Iq1YDswHxbRINjA-SgfXlHqVUE4xiAA) + + ```ts + interface Animal { + imageUrl: string; + species: string; + images: string[]; + paragraphs: string[]; + } + + // Creates new type with all properties of the `Animal` interface + // except 'images' and 'paragraphs' properties. We can use this + // type to render small hover tooltip for a wiki entry list. + type AnimalShortInfo = Omit<Animal, 'images' | 'paragraphs'>; + + function renderAnimalHoverInfo (animals: AnimalShortInfo[]): HTMLElement { + const container = document.createElement('div'); + // Internal implementation. + return container; + } + ``` + </details> + +You can find some examples in the [TypeScript docs](https://www.typescriptlang.org/docs/handbook/advanced-types.html#predefined-conditional-types). + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Jarek Radosz](https://github.com/CvX) +- [Dimitri Benin](https://github.com/BendingBender) +- [Pelle Wessman](https://github.com/voxpelli) + +## License + +(MIT OR CC0-1.0) + +--- + +<div align="center"> + <b> + <a href="https://tidelift.com/subscription/pkg/npm-type-fest?utm_source=npm-type-fest&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> + </b> + <br> + <sub> + Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. + </sub> +</div> diff --git a/tools/node_modules/eslint/node_modules/globals/package.json b/tools/node_modules/eslint/node_modules/globals/package.json index 1569748ef90c16..40f6e173930794 100644 --- a/tools/node_modules/eslint/node_modules/globals/package.json +++ b/tools/node_modules/eslint/node_modules/globals/package.json @@ -1,52 +1,64 @@ { - "name": "globals", - "version": "12.4.0", - "description": "Global identifiers from different JavaScript environments", - "license": "MIT", - "repository": "sindresorhus/globals", - "funding": "https://github.com/sponsors/sindresorhus", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "https://sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js", - "index.d.ts", - "globals.json" - ], - "keywords": [ - "globals", - "global", - "identifiers", - "variables", - "vars", - "jshint", - "eslint", - "environments" - ], - "dependencies": { - "type-fest": "^0.8.1" - }, - "devDependencies": { - "ava": "^2.2.0", - "tsd": "^0.9.0", - "xo": "^0.25.3" - }, - "xo": { - "ignores": [ - "get-browser-globals.js" - ] - }, - "tsd": { - "compilerOptions": { - "resolveJsonModule": true - } - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/globals/issues" + }, + "bundleDependencies": false, + "dependencies": { + "type-fest": "^0.20.2" + }, + "deprecated": false, + "description": "Global identifiers from different JavaScript environments", + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.14.0", + "xo": "^0.36.1" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts", + "globals.json" + ], + "funding": "https://github.com/sponsors/sindresorhus", + "homepage": "https://github.com/sindresorhus/globals#readme", + "keywords": [ + "globals", + "global", + "identifiers", + "variables", + "vars", + "jshint", + "eslint", + "environments" + ], + "license": "MIT", + "name": "globals", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/globals.git" + }, + "scripts": { + "test": "xo && ava" + }, + "tsd": { + "compilerOptions": { + "resolveJsonModule": true + } + }, + "version": "13.6.0", + "xo": { + "ignores": [ + "get-browser-globals.js" + ], + "rules": { + "node/no-unsupported-features/es-syntax": "off" + } + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/globals/readme.md b/tools/node_modules/eslint/node_modules/globals/readme.md index fdcfa087ab1107..0ef22c30451630 100644 --- a/tools/node_modules/eslint/node_modules/globals/readme.md +++ b/tools/node_modules/eslint/node_modules/globals/readme.md @@ -1,12 +1,12 @@ -# globals [](https://travis-ci.org/sindresorhus/globals) +# globals > Global identifiers from different JavaScript environments -Extracted from [JSHint](https://github.com/jshint/jshint/blob/3a8efa979dbb157bfb5c10b5826603a55a33b9ad/src/vars.js) and [ESLint](https://github.com/eslint/eslint/blob/b648406218f8a2d7302b98f5565e23199f44eb31/conf/environments.json) and merged. +It's just a [JSON file](globals.json), so use it in any environment. -It's just a [JSON file](globals.json), so use it in whatever environment you like. +This package is used by ESLint. -**This module [no longer accepts](https://github.com/sindresorhus/globals/issues/82) new environments. If you need it for ESLint, just [create a plugin](http://eslint.org/docs/developer-guide/working-with-plugins#environments-in-plugins).** +**This package [no longer accepts](https://github.com/sindresorhus/globals/issues/82) new environments. If you need it for ESLint, just [create a plugin](http://eslint.org/docs/developer-guide/working-with-plugins#environments-in-plugins).** ## Install diff --git a/tools/node_modules/eslint/node_modules/has-flag/index.js b/tools/node_modules/eslint/node_modules/has-flag/index.js index b6f80b1f8ffd76..5139728fba6a26 100644 --- a/tools/node_modules/eslint/node_modules/has-flag/index.js +++ b/tools/node_modules/eslint/node_modules/has-flag/index.js @@ -1,8 +1,8 @@ 'use strict'; - -module.exports = (flag, argv = process.argv) => { +module.exports = (flag, argv) => { + argv = argv || process.argv; const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--'); - const position = argv.indexOf(prefix + flag); - const terminatorPosition = argv.indexOf('--'); - return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); + const pos = argv.indexOf(prefix + flag); + const terminatorPos = argv.indexOf('--'); + return pos !== -1 && (terminatorPos === -1 ? true : pos < terminatorPos); }; diff --git a/tools/node_modules/eslint/node_modules/has-flag/package.json b/tools/node_modules/eslint/node_modules/has-flag/package.json index a9cba4b856d046..1903ff01cf6766 100644 --- a/tools/node_modules/eslint/node_modules/has-flag/package.json +++ b/tools/node_modules/eslint/node_modules/has-flag/package.json @@ -1,46 +1,53 @@ { - "name": "has-flag", - "version": "4.0.0", - "description": "Check if argv has a specific flag", - "license": "MIT", - "repository": "sindresorhus/has-flag", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "has", - "check", - "detect", - "contains", - "find", - "flag", - "cli", - "command-line", - "argv", - "process", - "arg", - "args", - "argument", - "arguments", - "getopt", - "minimist", - "optimist" - ], - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/has-flag/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Check if argv has a specific flag", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/has-flag#readme", + "keywords": [ + "has", + "check", + "detect", + "contains", + "find", + "flag", + "cli", + "command-line", + "argv", + "process", + "arg", + "args", + "argument", + "arguments", + "getopt", + "minimist", + "optimist" + ], + "license": "MIT", + "name": "has-flag", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/has-flag.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "3.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/has-flag/readme.md b/tools/node_modules/eslint/node_modules/has-flag/readme.md index 3f72dff29a6961..677893c278a2e3 100644 --- a/tools/node_modules/eslint/node_modules/has-flag/readme.md +++ b/tools/node_modules/eslint/node_modules/has-flag/readme.md @@ -4,20 +4,6 @@ Correctly stops looking after an `--` argument terminator. ---- - -<div align="center"> - <b> - <a href="https://tidelift.com/subscription/pkg/npm-has-flag?utm_source=npm-has-flag&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> - </b> - <br> - <sub> - Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. - </sub> -</div> - ---- - ## Install @@ -79,11 +65,6 @@ Default: `process.argv` CLI arguments. -## Security - -To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. - - ## License MIT © [Sindre Sorhus](https://sindresorhus.com) diff --git a/tools/node_modules/eslint/node_modules/ignore/package.json b/tools/node_modules/eslint/node_modules/ignore/package.json index 8cb1b7873257b4..1c14f37a316960 100644 --- a/tools/node_modules/eslint/node_modules/ignore/package.json +++ b/tools/node_modules/eslint/node_modules/ignore/package.json @@ -1,28 +1,38 @@ { - "name": "ignore", - "version": "4.0.6", + "author": { + "name": "kael" + }, + "bugs": { + "url": "https://github.com/kaelzhang/node-ignore/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "Ignore is a manager and filter for .gitignore rules.", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-preset-env": "^1.7.0", + "codecov": "^3.0.4", + "eslint": "^5.3.0", + "eslint-config-ostai": "^1.3.2", + "eslint-plugin-import": "^2.13.0", + "mkdirp": "^0.5.1", + "pre-suf": "^1.1.0", + "rimraf": "^2.6.2", + "spawn-sync": "^2.0.0", + "tap": "^12.0.1", + "tmp": "0.0.33", + "typescript": "^3.0.1" + }, + "engines": { + "node": ">= 4" + }, "files": [ "legacy.js", "index.js", "index.d.ts", "LICENSE-MIT" ], - "scripts": { - "prepublish": "npm run build", - "build": "babel -o legacy.js index.js", - "test:lint": "eslint .", - "test:tsc": "tsc ./test/ts/simple.ts", - "test:git": "tap test/git-check-ignore.js", - "test:ignore": "tap test/ignore.js --coverage", - "test-no-cov": "npm run test:lint && npm run test:tsc && tap test/*.js --coverage", - "test": "npm run test-no-cov", - "posttest": "tap --coverage-report=html && codecov" - }, - "repository": { - "type": "git", - "url": "git@github.com:kaelzhang/node-ignore.git" - }, + "homepage": "https://github.com/kaelzhang/node-ignore#readme", "keywords": [ "ignore", ".gitignore", @@ -38,27 +48,22 @@ "asterisks", "regular-expression" ], - "author": "kael", "license": "MIT", - "bugs": { - "url": "https://github.com/kaelzhang/node-ignore/issues" + "name": "ignore", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/kaelzhang/node-ignore.git" }, - "devDependencies": { - "babel-cli": "^6.26.0", - "babel-preset-env": "^1.7.0", - "codecov": "^3.0.4", - "eslint": "^5.3.0", - "eslint-config-ostai": "^1.3.2", - "eslint-plugin-import": "^2.13.0", - "mkdirp": "^0.5.1", - "pre-suf": "^1.1.0", - "rimraf": "^2.6.2", - "spawn-sync": "^2.0.0", - "tap": "^12.0.1", - "tmp": "0.0.33", - "typescript": "^3.0.1" + "scripts": { + "build": "babel -o legacy.js index.js", + "posttest": "tap --coverage-report=html && codecov", + "prepublish": "npm run build", + "test": "npm run test-no-cov", + "test-no-cov": "npm run test:lint && npm run test:tsc && tap test/*.js --coverage", + "test:git": "tap test/git-check-ignore.js", + "test:ignore": "tap test/ignore.js --coverage", + "test:lint": "eslint .", + "test:tsc": "tsc ./test/ts/simple.ts" }, - "engines": { - "node": ">= 4" - } -} + "version": "4.0.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/import-fresh/package.json b/tools/node_modules/eslint/node_modules/import-fresh/package.json index 0c093620687612..5a30f8061505fc 100644 --- a/tools/node_modules/eslint/node_modules/import-fresh/package.json +++ b/tools/node_modules/eslint/node_modules/import-fresh/package.json @@ -1,43 +1,52 @@ { - "name": "import-fresh", - "version": "3.3.0", - "description": "Import a module while bypassing the cache", - "license": "MIT", - "repository": "sindresorhus/import-fresh", - "funding": "https://github.com/sponsors/sindresorhus", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "https://sindresorhus.com" - }, - "engines": { - "node": ">=6" - }, - "scripts": { - "test": "xo && ava && tsd", - "heapdump": "node heapdump.js" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "require", - "cache", - "uncache", - "uncached", - "module", - "fresh", - "bypass" - ], - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "devDependencies": { - "ava": "^1.0.1", - "heapdump": "^0.3.12", - "tsd": "^0.7.3", - "xo": "^0.23.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/import-fresh/issues" + }, + "bundleDependencies": false, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "deprecated": false, + "description": "Import a module while bypassing the cache", + "devDependencies": { + "ava": "^1.0.1", + "heapdump": "^0.3.12", + "tsd": "^0.7.3", + "xo": "^0.23.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "funding": "https://github.com/sponsors/sindresorhus", + "homepage": "https://github.com/sindresorhus/import-fresh#readme", + "keywords": [ + "require", + "cache", + "uncache", + "uncached", + "module", + "fresh", + "bypass" + ], + "license": "MIT", + "name": "import-fresh", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/import-fresh.git" + }, + "scripts": { + "heapdump": "node heapdump.js", + "test": "xo && ava && tsd" + }, + "version": "3.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/imurmurhash/package.json b/tools/node_modules/eslint/node_modules/imurmurhash/package.json index 8a93edb55a2245..b07b2935ed7a79 100644 --- a/tools/node_modules/eslint/node_modules/imurmurhash/package.json +++ b/tools/node_modules/eslint/node_modules/imurmurhash/package.json @@ -1,22 +1,27 @@ { - "name": "imurmurhash", - "version": "0.1.4", + "author": { + "name": "Jens Taylor", + "email": "jensyt@gmail.com", + "url": "https://github.com/homebrewing" + }, + "bugs": { + "url": "https://github.com/jensyt/imurmurhash-js/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, "description": "An incremental implementation of MurmurHash3", - "homepage": "https://github.com/jensyt/imurmurhash-js", - "main": "imurmurhash.js", + "devDependencies": {}, + "engines": { + "node": ">=0.8.19" + }, "files": [ "imurmurhash.js", "imurmurhash.min.js", "package.json", "README.md" ], - "repository": { - "type": "git", - "url": "https://github.com/jensyt/imurmurhash-js" - }, - "bugs": { - "url": "https://github.com/jensyt/imurmurhash-js/issues" - }, + "homepage": "https://github.com/jensyt/imurmurhash-js", "keywords": [ "murmur", "murmurhash", @@ -24,17 +29,12 @@ "hash", "incremental" ], - "author": { - "name": "Jens Taylor", - "email": "jensyt@gmail.com", - "url": "https://github.com/homebrewing" - }, "license": "MIT", - "dependencies": { - }, - "devDependencies": { + "main": "imurmurhash.js", + "name": "imurmurhash", + "repository": { + "type": "git", + "url": "git+https://github.com/jensyt/imurmurhash-js.git" }, - "engines": { - "node": ">=0.8.19" - } -} + "version": "0.1.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/inflight/package.json b/tools/node_modules/eslint/node_modules/inflight/package.json index 6084d3509a5d6d..c5576ffbad6ee6 100644 --- a/tools/node_modules/eslint/node_modules/inflight/package.json +++ b/tools/node_modules/eslint/node_modules/inflight/package.json @@ -1,29 +1,35 @@ { - "name": "inflight", - "version": "1.0.6", - "description": "Add callbacks to requests in flight to avoid async duplication", - "main": "inflight.js", - "files": [ - "inflight.js" - ], + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/inflight/issues" + }, + "bundleDependencies": false, "dependencies": { "once": "^1.3.0", "wrappy": "1" }, + "deprecated": false, + "description": "Add callbacks to requests in flight to avoid async duplication", "devDependencies": { "tap": "^7.1.2" }, - "scripts": { - "test": "tap test.js --100" - }, + "files": [ + "inflight.js" + ], + "homepage": "https://github.com/isaacs/inflight", + "license": "ISC", + "main": "inflight.js", + "name": "inflight", "repository": { "type": "git", - "url": "https://github.com/npm/inflight.git" + "url": "git+https://github.com/npm/inflight.git" }, - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", - "bugs": { - "url": "https://github.com/isaacs/inflight/issues" + "scripts": { + "test": "tap test.js --100" }, - "homepage": "https://github.com/isaacs/inflight", - "license": "ISC" -} + "version": "1.0.6" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/inherits/package.json b/tools/node_modules/eslint/node_modules/inherits/package.json index 37b4366b83e63e..9642125b71d65f 100644 --- a/tools/node_modules/eslint/node_modules/inherits/package.json +++ b/tools/node_modules/eslint/node_modules/inherits/package.json @@ -1,7 +1,19 @@ { - "name": "inherits", + "browser": "./inherits_browser.js", + "bugs": { + "url": "https://github.com/isaacs/inherits/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "Browser-friendly inheritance fully compatible with standard node.js inherits()", - "version": "2.0.4", + "devDependencies": { + "tap": "^14.2.4" + }, + "files": [ + "inherits.js", + "inherits_browser.js" + ], + "homepage": "https://github.com/isaacs/inherits#readme", "keywords": [ "inheritance", "class", @@ -12,18 +24,15 @@ "browser", "browserify" ], - "main": "./inherits.js", - "browser": "./inherits_browser.js", - "repository": "git://github.com/isaacs/inherits", "license": "ISC", + "main": "./inherits.js", + "name": "inherits", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/inherits.git" + }, "scripts": { "test": "tap" }, - "devDependencies": { - "tap": "^14.2.4" - }, - "files": [ - "inherits.js", - "inherits_browser.js" - ] -} + "version": "2.0.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/is-extglob/package.json b/tools/node_modules/eslint/node_modules/is-extglob/package.json index 7a908369d39f55..3ae794888e29c7 100644 --- a/tools/node_modules/eslint/node_modules/is-extglob/package.json +++ b/tools/node_modules/eslint/node_modules/is-extglob/package.json @@ -1,28 +1,25 @@ { - "name": "is-extglob", - "description": "Returns true if a string has an extglob.", - "version": "2.1.1", - "homepage": "https://github.com/jonschlinkert/is-extglob", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "repository": "jonschlinkert/is-extglob", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, "bugs": { "url": "https://github.com/jonschlinkert/is-extglob/issues" }, - "license": "MIT", - "files": [ - "index.js" - ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, + "bundleDependencies": false, + "deprecated": false, + "description": "Returns true if a string has an extglob.", "devDependencies": { "gulp-format-md": "^0.1.10", "mocha": "^3.0.2" }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/jonschlinkert/is-extglob", "keywords": [ "bash", "braces", @@ -42,6 +39,16 @@ "string", "test" ], + "license": "MIT", + "main": "index.js", + "name": "is-extglob", + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/is-extglob.git" + }, + "scripts": { + "test": "mocha" + }, "verb": { "toc": false, "layout": "default", @@ -65,5 +72,6 @@ "lint": { "reflinks": true } - } -} + }, + "version": "2.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/is-fullwidth-code-point/package.json b/tools/node_modules/eslint/node_modules/is-fullwidth-code-point/package.json index 2137e888fa503d..c556c1c78dd459 100644 --- a/tools/node_modules/eslint/node_modules/is-fullwidth-code-point/package.json +++ b/tools/node_modules/eslint/node_modules/is-fullwidth-code-point/package.json @@ -1,42 +1,51 @@ { - "name": "is-fullwidth-code-point", - "version": "3.0.0", - "description": "Check if the character represented by a given Unicode code point is fullwidth", - "license": "MIT", - "repository": "sindresorhus/is-fullwidth-code-point", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd-check" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "fullwidth", - "full-width", - "full", - "width", - "unicode", - "character", - "string", - "codepoint", - "code", - "point", - "is", - "detect", - "check" - ], - "devDependencies": { - "ava": "^1.3.1", - "tsd-check": "^0.5.0", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/is-fullwidth-code-point/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Check if the character represented by a given Unicode code point is fullwidth", + "devDependencies": { + "ava": "^1.3.1", + "tsd-check": "^0.5.0", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/is-fullwidth-code-point#readme", + "keywords": [ + "fullwidth", + "full-width", + "full", + "width", + "unicode", + "character", + "string", + "codepoint", + "code", + "point", + "is", + "detect", + "check" + ], + "license": "MIT", + "name": "is-fullwidth-code-point", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/is-fullwidth-code-point.git" + }, + "scripts": { + "test": "xo && ava && tsd-check" + }, + "version": "3.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/is-glob/package.json b/tools/node_modules/eslint/node_modules/is-glob/package.json index 806000dbdafcc7..d8973094f63003 100644 --- a/tools/node_modules/eslint/node_modules/is-glob/package.json +++ b/tools/node_modules/eslint/node_modules/is-glob/package.json @@ -1,36 +1,42 @@ { - "name": "is-glob", - "description": "Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a better user experience.", - "version": "4.0.1", - "homepage": "https://github.com/micromatch/is-glob", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "contributors": [ - "Brian Woodward (https://twitter.com/doowb)", - "Daniel Perez (https://tuvistavie.com)", - "Jon Schlinkert (http://twitter.com/jonschlinkert)" - ], - "repository": "micromatch/is-glob", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, "bugs": { "url": "https://github.com/micromatch/is-glob/issues" }, - "license": "MIT", - "files": [ - "index.js" + "bundleDependencies": false, + "contributors": [ + { + "name": "Brian Woodward", + "url": "https://twitter.com/doowb" + }, + { + "name": "Daniel Perez", + "url": "https://tuvistavie.com" + }, + { + "name": "Jon Schlinkert", + "url": "http://twitter.com/jonschlinkert" + } ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, "dependencies": { "is-extglob": "^2.1.1" }, + "deprecated": false, + "description": "Returns `true` if the given string looks like a glob pattern or an extglob pattern. This makes it easy to create code that only uses external modules like node-glob when necessary, resulting in much faster code execution and initialization time, and a better user experience.", "devDependencies": { "gulp-format-md": "^0.1.10", "mocha": "^3.0.2" }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/micromatch/is-glob", "keywords": [ "bash", "braces", @@ -50,6 +56,16 @@ "string", "test" ], + "license": "MIT", + "main": "index.js", + "name": "is-glob", + "repository": { + "type": "git", + "url": "git+https://github.com/micromatch/is-glob.git" + }, + "scripts": { + "test": "mocha" + }, "verb": { "layout": "default", "plugins": [ @@ -77,5 +93,6 @@ "verb", "vinyl" ] - } -} + }, + "version": "4.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/isexe/package.json b/tools/node_modules/eslint/node_modules/isexe/package.json index e452689442f201..6403682bbe2c80 100644 --- a/tools/node_modules/eslint/node_modules/isexe/package.json +++ b/tools/node_modules/eslint/node_modules/isexe/package.json @@ -1,31 +1,37 @@ { - "name": "isexe", - "version": "2.0.0", - "description": "Minimal module to check if a file is executable.", - "main": "index.js", - "directories": { - "test": "test" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/isexe/issues" }, + "bundleDependencies": false, + "deprecated": false, + "description": "Minimal module to check if a file is executable.", "devDependencies": { "mkdirp": "^0.5.1", "rimraf": "^2.5.0", "tap": "^10.3.0" }, - "scripts": { - "test": "tap test/*.js --100", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --all; git push origin --tags" + "directories": { + "test": "test" }, - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", + "homepage": "https://github.com/isaacs/isexe#readme", + "keywords": [], "license": "ISC", + "main": "index.js", + "name": "isexe", "repository": { "type": "git", "url": "git+https://github.com/isaacs/isexe.git" }, - "keywords": [], - "bugs": { - "url": "https://github.com/isaacs/isexe/issues" + "scripts": { + "postpublish": "git push origin --all; git push origin --tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap test/*.js --100" }, - "homepage": "https://github.com/isaacs/isexe#readme" -} + "version": "2.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/js-tokens/package.json b/tools/node_modules/eslint/node_modules/js-tokens/package.json index 66752fab275101..ccfb4ef3dc3100 100644 --- a/tools/node_modules/eslint/node_modules/js-tokens/package.json +++ b/tools/node_modules/eslint/node_modules/js-tokens/package.json @@ -1,9 +1,23 @@ { - "name": "js-tokens", - "version": "4.0.0", - "author": "Simon Lydell", - "license": "MIT", + "author": { + "name": "Simon Lydell" + }, + "bugs": { + "url": "https://github.com/lydell/js-tokens/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "A regex that tokenizes JavaScript.", + "devDependencies": { + "coffeescript": "2.1.1", + "esprima": "4.0.0", + "everything.js": "1.0.3", + "mocha": "5.0.0" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/lydell/js-tokens#readme", "keywords": [ "JavaScript", "js", @@ -11,20 +25,17 @@ "tokenize", "regex" ], - "files": [ - "index.js" - ], - "repository": "lydell/js-tokens", + "license": "MIT", + "name": "js-tokens", + "repository": { + "type": "git", + "url": "git+https://github.com/lydell/js-tokens.git" + }, "scripts": { - "test": "mocha --ui tdd", - "esprima-compare": "node esprima-compare ./index.js everything.js/es5.js", "build": "node generate-index.js", - "dev": "npm run build && npm test" + "dev": "npm run build && npm test", + "esprima-compare": "node esprima-compare ./index.js everything.js/es5.js", + "test": "mocha --ui tdd" }, - "devDependencies": { - "coffeescript": "2.1.1", - "esprima": "4.0.0", - "everything.js": "1.0.3", - "mocha": "5.0.0" - } -} + "version": "4.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/js-yaml/package.json b/tools/node_modules/eslint/node_modules/js-yaml/package.json index 0d2366762c5448..6f63d1aed92082 100644 --- a/tools/node_modules/eslint/node_modules/js-yaml/package.json +++ b/tools/node_modules/eslint/node_modules/js-yaml/package.json @@ -1,37 +1,38 @@ { - "name": "js-yaml", - "version": "3.14.1", - "description": "YAML 1.2 parser and serializer", - "keywords": [ - "yaml", - "parser", - "serializer", - "pyyaml" - ], - "homepage": "https://github.com/nodeca/js-yaml", - "author": "Vladimir Zapparov <dervus.grim@gmail.com>", - "contributors": [ - "Aleksey V Zapparov <ixti@member.fsf.org> (http://www.ixti.net/)", - "Vitaly Puzrin <vitaly@rcdesign.ru> (https://github.com/puzrin)", - "Martin Grenfell <martin.grenfell@gmail.com> (http://got-ravings.blogspot.com)" - ], - "license": "MIT", - "repository": "nodeca/js-yaml", - "files": [ - "index.js", - "lib/", - "bin/", - "dist/" - ], + "author": { + "name": "Vladimir Zapparov", + "email": "dervus.grim@gmail.com" + }, "bin": { "js-yaml": "bin/js-yaml.js" }, - "unpkg": "dist/js-yaml.min.js", - "jsdelivr": "dist/js-yaml.min.js", + "bugs": { + "url": "https://github.com/nodeca/js-yaml/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Aleksey V Zapparov", + "email": "ixti@member.fsf.org", + "url": "http://www.ixti.net/" + }, + { + "name": "Vitaly Puzrin", + "email": "vitaly@rcdesign.ru", + "url": "https://github.com/puzrin" + }, + { + "name": "Martin Grenfell", + "email": "martin.grenfell@gmail.com", + "url": "http://got-ravings.blogspot.com" + } + ], "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, + "deprecated": false, + "description": "YAML 1.2 parser and serializer", "devDependencies": { "ansi": "^0.3.1", "benchmark": "^2.1.4", @@ -43,7 +44,29 @@ "mocha": "^7.1.2", "uglify-js": "^3.0.1" }, + "files": [ + "index.js", + "lib/", + "bin/", + "dist/" + ], + "homepage": "https://github.com/nodeca/js-yaml", + "jsdelivr": "dist/js-yaml.min.js", + "keywords": [ + "yaml", + "parser", + "serializer", + "pyyaml" + ], + "license": "MIT", + "name": "js-yaml", + "repository": { + "type": "git", + "url": "git+https://github.com/nodeca/js-yaml.git" + }, "scripts": { "test": "make test" - } -} + }, + "unpkg": "dist/js-yaml.min.js", + "version": "3.14.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/json-schema-traverse/package.json b/tools/node_modules/eslint/node_modules/json-schema-traverse/package.json index 156606327babdd..d5060aff01fbf6 100644 --- a/tools/node_modules/eslint/node_modules/json-schema-traverse/package.json +++ b/tools/node_modules/eslint/node_modules/json-schema-traverse/package.json @@ -1,28 +1,13 @@ { - "name": "json-schema-traverse", - "version": "0.4.1", - "description": "Traverse JSON Schema passing each schema object to callback", - "main": "index.js", - "scripts": { - "eslint": "eslint index.js spec", - "test-spec": "mocha spec -R spec", - "test": "npm run eslint && nyc npm run test-spec" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/epoberezkin/json-schema-traverse.git" + "author": { + "name": "Evgeny Poberezkin" }, - "keywords": [ - "JSON-Schema", - "traverse", - "iterate" - ], - "author": "Evgeny Poberezkin", - "license": "MIT", "bugs": { "url": "https://github.com/epoberezkin/json-schema-traverse/issues" }, - "homepage": "https://github.com/epoberezkin/json-schema-traverse#readme", + "bundleDependencies": false, + "deprecated": false, + "description": "Traverse JSON Schema passing each schema object to callback", "devDependencies": { "coveralls": "^2.13.1", "eslint": "^3.19.0", @@ -30,6 +15,15 @@ "nyc": "^11.0.2", "pre-commit": "^1.2.2" }, + "homepage": "https://github.com/epoberezkin/json-schema-traverse#readme", + "keywords": [ + "JSON-Schema", + "traverse", + "iterate" + ], + "license": "MIT", + "main": "index.js", + "name": "json-schema-traverse", "nyc": { "exclude": [ "**/spec/**", @@ -39,5 +33,15 @@ "lcov", "text-summary" ] - } -} + }, + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/json-schema-traverse.git" + }, + "scripts": { + "eslint": "eslint index.js spec", + "test": "npm run eslint && nyc npm run test-spec", + "test-spec": "mocha spec -R spec" + }, + "version": "0.4.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/json-stable-stringify-without-jsonify/package.json b/tools/node_modules/eslint/node_modules/json-stable-stringify-without-jsonify/package.json index c59c87edbe0b5c..8cc2f1397506e7 100644 --- a/tools/node_modules/eslint/node_modules/json-stable-stringify-without-jsonify/package.json +++ b/tools/node_modules/eslint/node_modules/json-stable-stringify-without-jsonify/package.json @@ -1,13 +1,35 @@ { - "name": "json-stable-stringify-without-jsonify", - "version": "1.0.1", - "description": "deterministic JSON.stringify() with custom sorting to get deterministic hashes from stringified results, with no public domain dependencies", - "main": "index.js", - "dependencies": { + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/samn/json-stable-stringify/issues" }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "deterministic JSON.stringify() with custom sorting to get deterministic hashes from stringified results, with no public domain dependencies", "devDependencies": { "tape": "~1.0.4" }, + "homepage": "https://github.com/samn/json-stable-stringify", + "keywords": [ + "json", + "stringify", + "deterministic", + "hash", + "sort", + "stable" + ], + "license": "MIT", + "main": "index.js", + "name": "json-stable-stringify-without-jsonify", + "repository": { + "type": "git", + "url": "git://github.com/samn/json-stable-stringify.git" + }, "scripts": { "test": "tape test/*.js" }, @@ -15,29 +37,13 @@ "files": "test/*.js", "browsers": [ "ie/8..latest", - "ff/5", "ff/latest", - "chrome/15", "chrome/latest", + "ff/5", + "ff/latest", + "chrome/15", + "chrome/latest", "safari/latest", "opera/latest" ] }, - "repository": { - "type": "git", - "url": "git://github.com/samn/json-stable-stringify.git" - }, - "homepage": "https://github.com/samn/json-stable-stringify", - "keywords": [ - "json", - "stringify", - "deterministic", - "hash", - "sort", - "stable" - ], - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net" - }, - "license": "MIT" -} + "version": "1.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/levn/package.json b/tools/node_modules/eslint/node_modules/levn/package.json index 0c356d6978eef0..a9868ae5880948 100644 --- a/tools/node_modules/eslint/node_modules/levn/package.json +++ b/tools/node_modules/eslint/node_modules/levn/package.json @@ -1,8 +1,30 @@ { - "name": "levn", - "version": "0.4.1", - "author": "George Zahariev <z@georgezahariev.com>", + "author": { + "name": "George Zahariev", + "email": "z@georgezahariev.com" + }, + "bugs": { + "url": "https://github.com/gkz/levn/issues" + }, + "bundleDependencies": false, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "deprecated": false, "description": "Light ECMAScript (JavaScript) Value Notation - human written, concise, typed, flexible", + "devDependencies": { + "livescript": "^1.6.0", + "mocha": "^7.1.1" + }, + "engines": { + "node": ">= 0.8.0" + }, + "files": [ + "lib", + "README.md", + "LICENSE" + ], "homepage": "https://github.com/gkz/levn", "keywords": [ "levn", @@ -17,17 +39,9 @@ "typed", "flexible" ], - "files": [ - "lib", - "README.md", - "LICENSE" - ], - "main": "./lib/", - "bugs": "https://github.com/gkz/levn/issues", "license": "MIT", - "engines": { - "node": ">= 0.8.0" - }, + "main": "./lib/", + "name": "levn", "repository": { "type": "git", "url": "git://github.com/gkz/levn.git" @@ -35,12 +49,5 @@ "scripts": { "test": "make test" }, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "devDependencies": { - "livescript": "^1.6.0", - "mocha": "^7.1.1" - } -} + "version": "0.4.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/lodash/package.json b/tools/node_modules/eslint/node_modules/lodash/package.json index b35fd95cc7aa28..e5fe5bf3f4e308 100644 --- a/tools/node_modules/eslint/node_modules/lodash/package.json +++ b/tools/node_modules/eslint/node_modules/lodash/package.json @@ -1,17 +1,40 @@ { - "name": "lodash", - "version": "4.17.21", + "author": { + "name": "John-David Dalton", + "email": "john.david.dalton@gmail.com" + }, + "bugs": { + "url": "https://github.com/lodash/lodash/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "John-David Dalton", + "email": "john.david.dalton@gmail.com" + }, + { + "name": "Mathias Bynens", + "email": "mathias@qiwi.be" + } + ], + "deprecated": false, "description": "Lodash modular utilities.", - "keywords": "modules, stdlib, util", "homepage": "https://lodash.com/", - "repository": "lodash/lodash", "icon": "https://lodash.com/icon.svg", + "keywords": [ + "modules", + "stdlib", + "util" + ], "license": "MIT", "main": "lodash.js", - "author": "John-David Dalton <john.david.dalton@gmail.com>", - "contributors": [ - "John-David Dalton <john.david.dalton@gmail.com>", - "Mathias Bynens <mathias@qiwi.be>" - ], - "scripts": { "test": "echo \"See https://travis-ci.org/lodash-archive/lodash-cli for testing details.\"" } -} + "name": "lodash", + "repository": { + "type": "git", + "url": "git+https://github.com/lodash/lodash.git" + }, + "scripts": { + "test": "echo \"See https://travis-ci.org/lodash-archive/lodash-cli for testing details.\"" + }, + "version": "4.17.21" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/lru-cache/package.json b/tools/node_modules/eslint/node_modules/lru-cache/package.json index 43b7502c3e7c79..a7a6da707b62fe 100644 --- a/tools/node_modules/eslint/node_modules/lru-cache/package.json +++ b/tools/node_modules/eslint/node_modules/lru-cache/package.json @@ -1,34 +1,46 @@ { - "name": "lru-cache", - "description": "A cache object that deletes the least-recently-used items.", - "version": "6.0.0", - "author": "Isaac Z. Schlueter <i@izs.me>", - "keywords": [ - "mru", - "lru", - "cache" - ], - "scripts": { - "test": "tap", - "snap": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me" }, - "main": "index.js", - "repository": "git://github.com/isaacs/node-lru-cache.git", + "bugs": { + "url": "https://github.com/isaacs/node-lru-cache/issues" + }, + "bundleDependencies": false, + "dependencies": { + "yallist": "^4.0.0" + }, + "deprecated": false, + "description": "A cache object that deletes the least-recently-used items.", "devDependencies": { "benchmark": "^2.1.4", "tap": "^14.10.7" }, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" + "engines": { + "node": ">=10" }, "files": [ "index.js" ], - "engines": { - "node": ">=10" - } -} + "homepage": "https://github.com/isaacs/node-lru-cache#readme", + "keywords": [ + "mru", + "lru", + "cache" + ], + "license": "ISC", + "main": "index.js", + "name": "lru-cache", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-lru-cache.git" + }, + "scripts": { + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preversion": "npm test", + "snap": "tap", + "test": "tap" + }, + "version": "6.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/minimatch/package.json b/tools/node_modules/eslint/node_modules/minimatch/package.json index c4514c807776de..3cd0d037a03d23 100644 --- a/tools/node_modules/eslint/node_modules/minimatch/package.json +++ b/tools/node_modules/eslint/node_modules/minimatch/package.json @@ -1,30 +1,40 @@ { - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)", - "name": "minimatch", - "description": "a glob matcher in javascript", - "version": "3.0.4", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/minimatch.git" - }, - "main": "minimatch.js", - "scripts": { - "test": "tap test/*.js --cov", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --all; git push origin --tags" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" }, - "engines": { - "node": "*" + "bugs": { + "url": "https://github.com/isaacs/minimatch/issues" }, + "bundleDependencies": false, "dependencies": { "brace-expansion": "^1.1.7" }, + "deprecated": false, + "description": "a glob matcher in javascript", "devDependencies": { "tap": "^10.3.2" }, - "license": "ISC", + "engines": { + "node": "*" + }, "files": [ "minimatch.js" - ] -} + ], + "homepage": "https://github.com/isaacs/minimatch#readme", + "license": "ISC", + "main": "minimatch.js", + "name": "minimatch", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/minimatch.git" + }, + "scripts": { + "postpublish": "git push origin --all; git push origin --tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap test/*.js --cov" + }, + "version": "3.0.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/ms/package.json b/tools/node_modules/eslint/node_modules/ms/package.json index eea666e1fb03d6..7192a30c57c910 100644 --- a/tools/node_modules/eslint/node_modules/ms/package.json +++ b/tools/node_modules/eslint/node_modules/ms/package.json @@ -1,16 +1,16 @@ { - "name": "ms", - "version": "2.1.2", + "bugs": { + "url": "https://github.com/zeit/ms/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "Tiny millisecond conversion utility", - "repository": "zeit/ms", - "main": "./index", - "files": [ - "index.js" - ], - "scripts": { - "precommit": "lint-staged", - "lint": "eslint lib/* bin/*", - "test": "mocha tests.js" + "devDependencies": { + "eslint": "4.12.1", + "expect.js": "0.3.1", + "husky": "0.14.3", + "lint-staged": "5.0.0", + "mocha": "4.0.1" }, "eslintConfig": { "extends": "eslint:recommended", @@ -19,6 +19,11 @@ "es6": true } }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/zeit/ms#readme", + "license": "MIT", "lint-staged": { "*.js": [ "npm run lint", @@ -26,12 +31,16 @@ "git add" ] }, - "license": "MIT", - "devDependencies": { - "eslint": "4.12.1", - "expect.js": "0.3.1", - "husky": "0.14.3", - "lint-staged": "5.0.0", - "mocha": "4.0.1" - } -} + "main": "./index", + "name": "ms", + "repository": { + "type": "git", + "url": "git+https://github.com/zeit/ms.git" + }, + "scripts": { + "lint": "eslint lib/* bin/*", + "precommit": "lint-staged", + "test": "mocha tests.js" + }, + "version": "2.1.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/natural-compare/package.json b/tools/node_modules/eslint/node_modules/natural-compare/package.json index 1a71362eea81a2..af8b2be0162cc8 100644 --- a/tools/node_modules/eslint/node_modules/natural-compare/package.json +++ b/tools/node_modules/eslint/node_modules/natural-compare/package.json @@ -1,10 +1,28 @@ { - "name": "natural-compare", - "version": "1.4.0", - "stability": 3, - "author": "Lauri Rooden (https://github.com/litejs/natural-compare-lite)", - "license": "MIT", + "author": { + "name": "Lauri Rooden", + "url": "https://github.com/litejs/natural-compare-lite" + }, + "bugs": { + "url": "https://github.com/litejs/natural-compare-lite/issues" + }, + "buildman": { + "dist/index-min.js": { + "banner": "/*! litejs.com/MIT-LICENSE.txt */", + "input": "index.js" + } + }, + "bundleDependencies": false, + "deprecated": false, "description": "Compare strings containing a mix of letters and numbers in the way a human being would in sort order.", + "devDependencies": { + "buildman": "*", + "testman": "*" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/litejs/natural-compare-lite#readme", "keywords": [ "string", "natural", @@ -16,27 +34,17 @@ "alphanum", "litejs" ], + "license": "MIT", "main": "index.js", - "readmeFilename": "README.md", - "files": [ - "index.js" - ], + "name": "natural-compare", + "repository": { + "type": "git", + "url": "git://github.com/litejs/natural-compare-lite.git" + }, "scripts": { "build": "node node_modules/buildman/index.js --all", "test": "node tests/index.js" }, - "repository": "git://github.com/litejs/natural-compare-lite.git", - "bugs": { - "url": "https://github.com/litejs/natural-compare-lite/issues" - }, - "devDependencies": { - "buildman": "*", - "testman": "*" - }, - "buildman": { - "dist/index-min.js": { - "banner": "/*! litejs.com/MIT-LICENSE.txt */", - "input": "index.js" - } - } -} + "stability": 3, + "version": "1.4.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/once/package.json b/tools/node_modules/eslint/node_modules/once/package.json index 16815b2fa11193..8635367075655a 100644 --- a/tools/node_modules/eslint/node_modules/once/package.json +++ b/tools/node_modules/eslint/node_modules/once/package.json @@ -1,33 +1,43 @@ { - "name": "once", - "version": "1.4.0", - "description": "Run a function exactly one time", - "main": "once.js", - "directories": { - "test": "test" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/once/issues" }, + "bundleDependencies": false, "dependencies": { "wrappy": "1" }, + "deprecated": false, + "description": "Run a function exactly one time", "devDependencies": { "tap": "^7.0.1" }, - "scripts": { - "test": "tap test/*.js" + "directories": { + "test": "test" }, "files": [ "once.js" ], - "repository": { - "type": "git", - "url": "git://github.com/isaacs/once" - }, + "homepage": "https://github.com/isaacs/once#readme", "keywords": [ "once", "function", "one", "single" ], - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", - "license": "ISC" -} + "license": "ISC", + "main": "once.js", + "name": "once", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/once.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "version": "1.4.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/optionator/package.json b/tools/node_modules/eslint/node_modules/optionator/package.json index 83fb1c2c930a47..b76e0b4f4c532d 100644 --- a/tools/node_modules/eslint/node_modules/optionator/package.json +++ b/tools/node_modules/eslint/node_modules/optionator/package.json @@ -1,8 +1,34 @@ { - "name": "optionator", - "version": "0.9.1", - "author": "George Zahariev <z@georgezahariev.com>", + "author": { + "name": "George Zahariev", + "email": "z@georgezahariev.com" + }, + "bugs": { + "url": "https://github.com/gkz/optionator/issues" + }, + "bundleDependencies": false, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "deprecated": false, "description": "option parsing and help generation", + "devDependencies": { + "livescript": "^1.6.0", + "mocha": "^7.1.1" + }, + "engines": { + "node": ">= 0.8.0" + }, + "files": [ + "lib", + "README.md", + "LICENSE" + ], "homepage": "https://github.com/gkz/optionator", "keywords": [ "options", @@ -10,17 +36,9 @@ "option parsing", "cli" ], - "files": [ - "lib", - "README.md", - "LICENSE" - ], - "main": "./lib/", - "bugs": "https://github.com/gkz/optionator/issues", "license": "MIT", - "engines": { - "node": ">= 0.8.0" - }, + "main": "./lib/", + "name": "optionator", "repository": { "type": "git", "url": "git://github.com/gkz/optionator.git" @@ -28,16 +46,5 @@ "scripts": { "test": "make test" }, - "dependencies": { - "prelude-ls": "^1.2.1", - "deep-is": "^0.1.3", - "word-wrap": "^1.2.3", - "type-check": "^0.4.0", - "levn": "^0.4.1", - "fast-levenshtein": "^2.0.6" - }, - "devDependencies": { - "livescript": "^1.6.0", - "mocha": "^7.1.1" - } -} + "version": "0.9.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/parent-module/package.json b/tools/node_modules/eslint/node_modules/parent-module/package.json index 790333d0b1a659..1f5b6cb1757a63 100644 --- a/tools/node_modules/eslint/node_modules/parent-module/package.json +++ b/tools/node_modules/eslint/node_modules/parent-module/package.json @@ -1,46 +1,55 @@ { - "name": "parent-module", - "version": "1.0.1", - "description": "Get the path of the parent module", - "license": "MIT", - "repository": "sindresorhus/parent-module", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=6" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js" - ], - "keywords": [ - "parent", - "module", - "package", - "pkg", - "caller", - "calling", - "module", - "path", - "callsites", - "callsite", - "stacktrace", - "stack", - "trace", - "function", - "file" - ], - "dependencies": { - "callsites": "^3.0.0" - }, - "devDependencies": { - "ava": "^1.4.1", - "execa": "^1.0.0", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/parent-module/issues" + }, + "bundleDependencies": false, + "dependencies": { + "callsites": "^3.0.0" + }, + "deprecated": false, + "description": "Get the path of the parent module", + "devDependencies": { + "ava": "^1.4.1", + "execa": "^1.0.0", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=6" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/parent-module#readme", + "keywords": [ + "parent", + "module", + "package", + "pkg", + "caller", + "calling", + "module", + "path", + "callsites", + "callsite", + "stacktrace", + "stack", + "trace", + "function", + "file" + ], + "license": "MIT", + "name": "parent-module", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/parent-module.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "1.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/path-is-absolute/package.json b/tools/node_modules/eslint/node_modules/path-is-absolute/package.json index 91196d5e9c1336..9280158131fa5a 100644 --- a/tools/node_modules/eslint/node_modules/path-is-absolute/package.json +++ b/tools/node_modules/eslint/node_modules/path-is-absolute/package.json @@ -1,23 +1,25 @@ { - "name": "path-is-absolute", - "version": "1.0.1", - "description": "Node.js 0.12 path.isAbsolute() ponyfill", - "license": "MIT", - "repository": "sindresorhus/path-is-absolute", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", "url": "sindresorhus.com" }, + "bugs": { + "url": "https://github.com/sindresorhus/path-is-absolute/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Node.js 0.12 path.isAbsolute() ponyfill", + "devDependencies": { + "xo": "^0.16.0" + }, "engines": { "node": ">=0.10.0" }, - "scripts": { - "test": "xo && node test.js" - }, "files": [ "index.js" ], + "homepage": "https://github.com/sindresorhus/path-is-absolute#readme", "keywords": [ "path", "paths", @@ -37,7 +39,14 @@ "detect", "check" ], - "devDependencies": { - "xo": "^0.16.0" - } -} + "license": "MIT", + "name": "path-is-absolute", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/path-is-absolute.git" + }, + "scripts": { + "test": "xo && node test.js" + }, + "version": "1.0.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/path-key/package.json b/tools/node_modules/eslint/node_modules/path-key/package.json index c8cbd383afc955..7c2ea096485043 100644 --- a/tools/node_modules/eslint/node_modules/path-key/package.json +++ b/tools/node_modules/eslint/node_modules/path-key/package.json @@ -1,39 +1,48 @@ { - "name": "path-key", - "version": "3.1.1", - "description": "Get the PATH environment variable key cross-platform", - "license": "MIT", - "repository": "sindresorhus/path-key", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "path", - "key", - "environment", - "env", - "variable", - "var", - "get", - "cross-platform", - "windows" - ], - "devDependencies": { - "@types/node": "^11.13.0", - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/path-key/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Get the PATH environment variable key cross-platform", + "devDependencies": { + "@types/node": "^11.13.0", + "ava": "^1.4.1", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/path-key#readme", + "keywords": [ + "path", + "key", + "environment", + "env", + "variable", + "var", + "get", + "cross-platform", + "windows" + ], + "license": "MIT", + "name": "path-key", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/path-key.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "3.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/prelude-ls/package.json b/tools/node_modules/eslint/node_modules/prelude-ls/package.json index c313c3da34a9dc..ff1fcd6e1a8b50 100644 --- a/tools/node_modules/eslint/node_modules/prelude-ls/package.json +++ b/tools/node_modules/eslint/node_modules/prelude-ls/package.json @@ -1,8 +1,30 @@ { - "name": "prelude-ls", - "version": "1.2.1", - "author": "George Zahariev <z@georgezahariev.com>", + "author": { + "name": "George Zahariev", + "email": "z@georgezahariev.com" + }, + "bugs": { + "url": "https://github.com/gkz/prelude-ls/issues" + }, + "bundleDependencies": false, + "deprecated": false, "description": "prelude.ls is a functionally oriented utility library. It is powerful and flexible. Almost all of its functions are curried. It is written in, and is the recommended base library for, LiveScript.", + "devDependencies": { + "browserify": "^16.5.1", + "livescript": "^1.6.0", + "mocha": "^7.1.1", + "sinon": "~8.0.1", + "uglify-js": "^3.8.1" + }, + "engines": { + "node": ">= 0.8.0" + }, + "files": [ + "lib/", + "README.md", + "LICENSE" + ], + "homepage": "http://preludels.com", "keywords": [ "prelude", "livescript", @@ -17,18 +39,9 @@ "object", "string" ], - "main": "lib/", - "files": [ - "lib/", - "README.md", - "LICENSE" - ], - "homepage": "http://preludels.com", - "bugs": "https://github.com/gkz/prelude-ls/issues", "license": "MIT", - "engines": { - "node": ">= 0.8.0" - }, + "main": "lib/", + "name": "prelude-ls", "repository": { "type": "git", "url": "git://github.com/gkz/prelude-ls.git" @@ -36,11 +49,5 @@ "scripts": { "test": "make test" }, - "devDependencies": { - "livescript": "^1.6.0", - "uglify-js": "^3.8.1", - "mocha": "^7.1.1", - "browserify": "^16.5.1", - "sinon": "~8.0.1" - } -} + "version": "1.2.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/progress/package.json b/tools/node_modules/eslint/node_modules/progress/package.json index bb81fa0bcd2b1d..64511f8344114f 100644 --- a/tools/node_modules/eslint/node_modules/progress/package.json +++ b/tools/node_modules/eslint/node_modules/progress/package.json @@ -1,26 +1,47 @@ { - "name": "progress", - "version": "2.0.3", + "author": { + "name": "TJ Holowaychuk", + "email": "tj@vision-media.ca" + }, + "bugs": { + "url": "https://github.com/visionmedia/node-progress/issues" + }, + "bundleDependencies": false, + "contributors": [ + { + "name": "Christoffer Hallas", + "email": "christoffer.hallas@gmail.com" + }, + { + "name": "Jordan Scales", + "email": "scalesjordan@gmail.com" + }, + { + "name": "Andrew Rhyne", + "email": "rhyneandrew@gmail.com" + }, + { + "name": "Marco Brack", + "email": "PapstDonB@Googlemail.com" + } + ], + "dependencies": {}, + "deprecated": false, "description": "Flexible ascii progress bar", - "repository": { - "type": "git", - "url": "git://github.com/visionmedia/node-progress" + "engines": { + "node": ">=0.4.0" }, + "homepage": "https://github.com/visionmedia/node-progress#readme", "keywords": [ "cli", "progress" ], - "author": "TJ Holowaychuk <tj@vision-media.ca>", - "contributors": [ - "Christoffer Hallas <christoffer.hallas@gmail.com>", - "Jordan Scales <scalesjordan@gmail.com>", - "Andrew Rhyne <rhyneandrew@gmail.com>", - "Marco Brack <PapstDonB@Googlemail.com>" - ], - "dependencies": {}, + "license": "MIT", "main": "./index.js", - "engines": { - "node": ">=0.4.0" + "name": "progress", + "repository": { + "type": "git", + "url": "git://github.com/visionmedia/node-progress.git" }, - "license": "MIT" -} + "version": "2.0.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/punycode/package.json b/tools/node_modules/eslint/node_modules/punycode/package.json index 9202ccf8c0c8f7..36ee74b2ef460e 100644 --- a/tools/node_modules/eslint/node_modules/punycode/package.json +++ b/tools/node_modules/eslint/node_modules/punycode/package.json @@ -1,58 +1,62 @@ { - "name": "punycode", - "version": "2.1.1", - "description": "A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.", - "homepage": "https://mths.be/punycode", - "main": "punycode.js", - "jsnext:main": "punycode.es6.js", - "module": "punycode.es6.js", - "engines": { - "node": ">=6" - }, - "keywords": [ - "punycode", - "unicode", - "idn", - "idna", - "dns", - "url", - "domain" - ], - "license": "MIT", "author": { "name": "Mathias Bynens", "url": "https://mathiasbynens.be/" }, + "bugs": { + "url": "https://github.com/bestiejs/punycode.js/issues" + }, + "bundleDependencies": false, "contributors": [ { "name": "Mathias Bynens", "url": "https://mathiasbynens.be/" } ], - "repository": { - "type": "git", - "url": "https://github.com/bestiejs/punycode.js.git" + "deprecated": false, + "description": "A robust Punycode converter that fully complies to RFC 3492 and RFC 5891, and works on nearly all JavaScript platforms.", + "devDependencies": { + "codecov": "^1.0.1", + "istanbul": "^0.4.1", + "mocha": "^2.5.3" + }, + "engines": { + "node": ">=6" }, - "bugs": "https://github.com/bestiejs/punycode.js/issues", "files": [ "LICENSE-MIT.txt", "punycode.js", "punycode.es6.js" ], - "scripts": { - "test": "mocha tests", - "prepublish": "node scripts/prepublish.js" - }, - "devDependencies": { - "codecov": "^1.0.1", - "istanbul": "^0.4.1", - "mocha": "^2.5.3" - }, + "homepage": "https://mths.be/punycode", + "jsnext:main": "punycode.es6.js", "jspm": { "map": { "./punycode.js": { "node": "@node/punycode" } } - } -} + }, + "keywords": [ + "punycode", + "unicode", + "idn", + "idna", + "dns", + "url", + "domain" + ], + "license": "MIT", + "main": "punycode.js", + "module": "punycode.es6.js", + "name": "punycode", + "repository": { + "type": "git", + "url": "git+https://github.com/bestiejs/punycode.js.git" + }, + "scripts": { + "prepublish": "node scripts/prepublish.js", + "test": "mocha tests" + }, + "version": "2.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/regexpp/package.json b/tools/node_modules/eslint/node_modules/regexpp/package.json index 029cbe74e37f60..1c2b0eb82ba809 100644 --- a/tools/node_modules/eslint/node_modules/regexpp/package.json +++ b/tools/node_modules/eslint/node_modules/regexpp/package.json @@ -1,15 +1,15 @@ { - "name": "regexpp", - "version": "3.1.0", - "description": "Regular expression parser for ECMAScript.", - "engines": { - "node": ">=8" + "author": { + "name": "Toru Nagashima", + "url": "https://github.com/mysticatea" }, - "main": "index", - "files": [ - "index.*" - ], + "bugs": { + "url": "https://github.com/mysticatea/regexpp/issues" + }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Regular expression parser for ECMAScript.", "devDependencies": { "@mysticatea/eslint-plugin": "^11.0.0", "@types/eslint": "^4.16.2", @@ -30,31 +30,14 @@ "ts-node": "^8.3.0", "typescript": "^3.5.3" }, - "scripts": { - "prebuild": "npm run -s clean", - "build": "run-s build:*", - "build:tsc": "tsc --module es2015", - "build:rollup": "rollup -c", - "build:dts": "dts-bundle --name regexpp --main .temp/index.d.ts --out ../index.d.ts", - "clean": "rimraf .temp index.*", - "codecov": "nyc report -r lcovonly && codecov -t ${CODECOV_TOKEN} --disable=gcov", - "lint": "eslint scripts src test --ext .ts", - "pretest": "run-s build lint", - "test": "nyc _mocha \"test/*.ts\" --reporter dot --timeout 10000", - "update:test": "ts-node scripts/update-fixtures.ts", - "update:unicode": "run-s update:unicode:*", - "update:unicode:ids": "ts-node scripts/update-unicode-ids.ts", - "update:unicode:props": "ts-node scripts/update-unicode-properties.ts", - "preversion": "npm test", - "version": "npm run -s build", - "postversion": "git push && git push --tags", - "prewatch": "npm run -s clean", - "watch": "_mocha \"test/*.ts\" --require ts-node/register --reporter dot --timeout 10000 --watch-extensions ts --watch --growl" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/mysticatea/regexpp.git" + "engines": { + "node": ">=8" }, + "files": [ + "index.*" + ], + "funding": "https://github.com/sponsors/mysticatea", + "homepage": "https://github.com/mysticatea/regexpp#readme", "keywords": [ "regexp", "regular", @@ -74,11 +57,33 @@ "es2020", "annexB" ], - "author": "Toru Nagashima (https://github.com/mysticatea)", "license": "MIT", - "bugs": { - "url": "https://github.com/mysticatea/regexpp/issues" + "main": "index", + "name": "regexpp", + "repository": { + "type": "git", + "url": "git+https://github.com/mysticatea/regexpp.git" }, - "homepage": "https://github.com/mysticatea/regexpp#readme", - "funding": "https://github.com/sponsors/mysticatea" -} + "scripts": { + "build": "run-s build:*", + "build:dts": "dts-bundle --name regexpp --main .temp/index.d.ts --out ../index.d.ts", + "build:rollup": "rollup -c", + "build:tsc": "tsc --module es2015", + "clean": "rimraf .temp index.*", + "codecov": "nyc report -r lcovonly && codecov -t ${CODECOV_TOKEN} --disable=gcov", + "lint": "eslint scripts src test --ext .ts", + "postversion": "git push && git push --tags", + "prebuild": "npm run -s clean", + "pretest": "run-s build lint", + "preversion": "npm test", + "prewatch": "npm run -s clean", + "test": "nyc _mocha \"test/*.ts\" --reporter dot --timeout 10000", + "update:test": "ts-node scripts/update-fixtures.ts", + "update:unicode": "run-s update:unicode:*", + "update:unicode:ids": "ts-node scripts/update-unicode-ids.ts", + "update:unicode:props": "ts-node scripts/update-unicode-properties.ts", + "version": "npm run -s build", + "watch": "_mocha \"test/*.ts\" --require ts-node/register --reporter dot --timeout 10000 --watch-extensions ts --watch --growl" + }, + "version": "3.1.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/require-from-string/package.json b/tools/node_modules/eslint/node_modules/require-from-string/package.json index 800d46efcb00b3..4ca96a63470da5 100644 --- a/tools/node_modules/eslint/node_modules/require-from-string/package.json +++ b/tools/node_modules/eslint/node_modules/require-from-string/package.json @@ -1,28 +1,35 @@ { - "name": "require-from-string", - "version": "2.0.2", - "description": "Require module from string", - "license": "MIT", - "repository": "floatdrop/require-from-string", "author": { "name": "Vsevolod Strukchinsky", "email": "floatdrop@gmail.com", "url": "github.com/floatdrop" }, + "bugs": { + "url": "https://github.com/floatdrop/require-from-string/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, + "description": "Require module from string", + "devDependencies": { + "mocha": "*" + }, "engines": { "node": ">=0.10.0" }, - "scripts": { - "test": "mocha" - }, "files": [ "index.js" ], - "keywords": [ - "" - ], - "dependencies": {}, - "devDependencies": { - "mocha": "*" - } -} + "homepage": "https://github.com/floatdrop/require-from-string#readme", + "keywords": [], + "license": "MIT", + "name": "require-from-string", + "repository": { + "type": "git", + "url": "git+https://github.com/floatdrop/require-from-string.git" + }, + "scripts": { + "test": "mocha" + }, + "version": "2.0.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/resolve-from/package.json b/tools/node_modules/eslint/node_modules/resolve-from/package.json index 96bade58824569..dde5eec0bc6102 100644 --- a/tools/node_modules/eslint/node_modules/resolve-from/package.json +++ b/tools/node_modules/eslint/node_modules/resolve-from/package.json @@ -1,34 +1,43 @@ { - "name": "resolve-from", - "version": "4.0.0", - "description": "Resolve the path of a module like `require.resolve()` but from a given path", - "license": "MIT", - "repository": "sindresorhus/resolve-from", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=4" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js" - ], - "keywords": [ - "require", - "resolve", - "path", - "module", - "from", - "like", - "import" - ], - "devDependencies": { - "ava": "*", - "xo": "*" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/resolve-from/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Resolve the path of a module like `require.resolve()` but from a given path", + "devDependencies": { + "ava": "*", + "xo": "*" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/sindresorhus/resolve-from#readme", + "keywords": [ + "require", + "resolve", + "path", + "module", + "from", + "like", + "import" + ], + "license": "MIT", + "name": "resolve-from", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/resolve-from.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "4.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/rimraf/package.json b/tools/node_modules/eslint/node_modules/rimraf/package.json index 1bf8d5e38775d7..036517586b227f 100644 --- a/tools/node_modules/eslint/node_modules/rimraf/package.json +++ b/tools/node_modules/eslint/node_modules/rimraf/package.json @@ -1,32 +1,47 @@ { - "name": "rimraf", - "version": "3.0.2", - "main": "rimraf.js", - "description": "A deep deletion module for node (like `rm -rf`)", - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", - "license": "ISC", - "repository": "git://github.com/isaacs/rimraf.git", - "scripts": { - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --follow-tags", - "test": "tap test/*.js" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bin": { + "rimraf": "bin.js" }, - "bin": "./bin.js", + "bugs": { + "url": "https://github.com/isaacs/rimraf/issues" + }, + "bundleDependencies": false, "dependencies": { "glob": "^7.1.3" }, + "deprecated": false, + "description": "A deep deletion module for node (like `rm -rf`)", + "devDependencies": { + "mkdirp": "^0.5.1", + "tap": "^12.1.1" + }, "files": [ "LICENSE", "README.md", "bin.js", "rimraf.js" ], - "devDependencies": { - "mkdirp": "^0.5.1", - "tap": "^12.1.1" - }, "funding": { "url": "https://github.com/sponsors/isaacs" - } -} + }, + "homepage": "https://github.com/isaacs/rimraf#readme", + "license": "ISC", + "main": "rimraf.js", + "name": "rimraf", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/rimraf.git" + }, + "scripts": { + "postpublish": "git push origin --follow-tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap test/*.js" + }, + "version": "3.0.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/semver/package.json b/tools/node_modules/eslint/node_modules/semver/package.json index d4043d38a13529..2aa14e5a6dd7e9 100644 --- a/tools/node_modules/eslint/node_modules/semver/package.json +++ b/tools/node_modules/eslint/node_modules/semver/package.json @@ -1,22 +1,21 @@ { - "name": "semver", - "version": "7.3.4", - "description": "The semantic version parser used by npm.", - "main": "index.js", - "scripts": { - "test": "tap", - "snap": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --follow-tags" + "bin": { + "semver": "bin/semver.js" }, + "bugs": { + "url": "https://github.com/npm/node-semver/issues" + }, + "bundleDependencies": false, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "deprecated": false, + "description": "The semantic version parser used by npm.", "devDependencies": { "tap": "^14.10.7" }, - "license": "ISC", - "repository": "https://github.com/npm/node-semver", - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=10" }, "files": [ "bin/**/*.js", @@ -28,14 +27,24 @@ "index.js", "preload.js" ], + "homepage": "https://github.com/npm/node-semver#readme", + "license": "ISC", + "main": "index.js", + "name": "semver", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/node-semver.git" + }, + "scripts": { + "postpublish": "git push origin --follow-tags", + "postversion": "npm publish", + "preversion": "npm test", + "snap": "tap", + "test": "tap" + }, "tap": { "check-coverage": true, "coverage-map": "map.js" }, - "engines": { - "node": ">=10" - }, - "dependencies": { - "lru-cache": "^6.0.0" - } -} + "version": "7.3.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/shebang-command/package.json b/tools/node_modules/eslint/node_modules/shebang-command/package.json index 18e3c04638cb68..3dc3563bceda96 100644 --- a/tools/node_modules/eslint/node_modules/shebang-command/package.json +++ b/tools/node_modules/eslint/node_modules/shebang-command/package.json @@ -1,34 +1,43 @@ { - "name": "shebang-command", - "version": "2.0.0", - "description": "Get the command from a shebang", - "license": "MIT", - "repository": "kevva/shebang-command", - "author": { - "name": "Kevin Mårtensson", - "email": "kevinmartensson@gmail.com", - "url": "github.com/kevva" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js" - ], - "keywords": [ - "cmd", - "command", - "parse", - "shebang" - ], - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "devDependencies": { - "ava": "^2.3.0", - "xo": "^0.24.0" - } -} + "author": { + "name": "Kevin Mårtensson", + "email": "kevinmartensson@gmail.com", + "url": "github.com/kevva" + }, + "bugs": { + "url": "https://github.com/kevva/shebang-command/issues" + }, + "bundleDependencies": false, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "deprecated": false, + "description": "Get the command from a shebang", + "devDependencies": { + "ava": "^2.3.0", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js" + ], + "homepage": "https://github.com/kevva/shebang-command#readme", + "keywords": [ + "cmd", + "command", + "parse", + "shebang" + ], + "license": "MIT", + "name": "shebang-command", + "repository": { + "type": "git", + "url": "git+https://github.com/kevva/shebang-command.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "2.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/shebang-regex/package.json b/tools/node_modules/eslint/node_modules/shebang-regex/package.json index 00ab30feeefe89..22a96573aadd2d 100644 --- a/tools/node_modules/eslint/node_modules/shebang-regex/package.json +++ b/tools/node_modules/eslint/node_modules/shebang-regex/package.json @@ -1,35 +1,44 @@ { - "name": "shebang-regex", - "version": "3.0.0", - "description": "Regular expression for matching a shebang line", - "license": "MIT", - "repository": "sindresorhus/shebang-regex", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "regex", - "regexp", - "shebang", - "match", - "test", - "line" - ], - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/shebang-regex/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Regular expression for matching a shebang line", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/shebang-regex#readme", + "keywords": [ + "regex", + "regexp", + "shebang", + "match", + "test", + "line" + ], + "license": "MIT", + "name": "shebang-regex", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/shebang-regex.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "3.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/index.js b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/index.js new file mode 100644 index 00000000000000..5d82581a13f990 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/index.js @@ -0,0 +1,163 @@ +'use strict'; + +const wrapAnsi16 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${code + offset}m`; +}; + +const wrapAnsi256 = (fn, offset) => (...args) => { + const code = fn(...args); + return `\u001B[${38 + offset};5;${code}m`; +}; + +const wrapAnsi16m = (fn, offset) => (...args) => { + const rgb = fn(...args); + return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`; +}; + +const ansi2ansi = n => n; +const rgb2rgb = (r, g, b) => [r, g, b]; + +const setLazyProperty = (object, property, get) => { + Object.defineProperty(object, property, { + get: () => { + const value = get(); + + Object.defineProperty(object, property, { + value, + enumerable: true, + configurable: true + }); + + return value; + }, + enumerable: true, + configurable: true + }); +}; + +/** @type {typeof import('color-convert')} */ +let colorConvert; +const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => { + if (colorConvert === undefined) { + colorConvert = require('color-convert'); + } + + const offset = isBackground ? 10 : 0; + const styles = {}; + + for (const [sourceSpace, suite] of Object.entries(colorConvert)) { + const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace; + if (sourceSpace === targetSpace) { + styles[name] = wrap(identity, offset); + } else if (typeof suite === 'object') { + styles[name] = wrap(suite[targetSpace], offset); + } + } + + return styles; +}; + +function assembleStyles() { + const codes = new Map(); + const styles = { + modifier: { + reset: [0, 0], + // 21 isn't widely supported and 22 does the same thing + bold: [1, 22], + dim: [2, 22], + italic: [3, 23], + underline: [4, 24], + inverse: [7, 27], + hidden: [8, 28], + strikethrough: [9, 29] + }, + color: { + black: [30, 39], + red: [31, 39], + green: [32, 39], + yellow: [33, 39], + blue: [34, 39], + magenta: [35, 39], + cyan: [36, 39], + white: [37, 39], + + // Bright color + blackBright: [90, 39], + redBright: [91, 39], + greenBright: [92, 39], + yellowBright: [93, 39], + blueBright: [94, 39], + magentaBright: [95, 39], + cyanBright: [96, 39], + whiteBright: [97, 39] + }, + bgColor: { + bgBlack: [40, 49], + bgRed: [41, 49], + bgGreen: [42, 49], + bgYellow: [43, 49], + bgBlue: [44, 49], + bgMagenta: [45, 49], + bgCyan: [46, 49], + bgWhite: [47, 49], + + // Bright color + bgBlackBright: [100, 49], + bgRedBright: [101, 49], + bgGreenBright: [102, 49], + bgYellowBright: [103, 49], + bgBlueBright: [104, 49], + bgMagentaBright: [105, 49], + bgCyanBright: [106, 49], + bgWhiteBright: [107, 49] + } + }; + + // Alias bright black as gray (and grey) + styles.color.gray = styles.color.blackBright; + styles.bgColor.bgGray = styles.bgColor.bgBlackBright; + styles.color.grey = styles.color.blackBright; + styles.bgColor.bgGrey = styles.bgColor.bgBlackBright; + + for (const [groupName, group] of Object.entries(styles)) { + for (const [styleName, style] of Object.entries(group)) { + styles[styleName] = { + open: `\u001B[${style[0]}m`, + close: `\u001B[${style[1]}m` + }; + + group[styleName] = styles[styleName]; + + codes.set(style[0], style[1]); + } + + Object.defineProperty(styles, groupName, { + value: group, + enumerable: false + }); + } + + Object.defineProperty(styles, 'codes', { + value: codes, + enumerable: false + }); + + styles.color.close = '\u001B[39m'; + styles.bgColor.close = '\u001B[49m'; + + setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false)); + setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false)); + setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true)); + setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true)); + + return styles; +} + +// Make the export immutable +Object.defineProperty(module, 'exports', { + enumerable: true, + get: assembleStyles +}); diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/license b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/license new file mode 100644 index 00000000000000..e7af2f77107d73 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com) + +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. diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/package.json b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/package.json new file mode 100644 index 00000000000000..d276e03d2549c8 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/package.json @@ -0,0 +1,65 @@ +{ + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/ansi-styles/issues" + }, + "bundleDependencies": false, + "dependencies": { + "color-convert": "^2.0.1" + }, + "deprecated": false, + "description": "ANSI escape codes for styling strings in the terminal", + "devDependencies": { + "@types/color-convert": "^1.9.0", + "ava": "^2.3.0", + "svg-term-cli": "^2.1.1", + "tsd": "^0.11.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "funding": "https://github.com/chalk/ansi-styles?sponsor=1", + "homepage": "https://github.com/chalk/ansi-styles#readme", + "keywords": [ + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "ansi-styles", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/ansi-styles.git" + }, + "scripts": { + "screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor", + "test": "xo && ava && tsd" + }, + "version": "4.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/readme.md b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/readme.md new file mode 100644 index 00000000000000..24883de808be6a --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/ansi-styles/readme.md @@ -0,0 +1,152 @@ +# ansi-styles [](https://travis-ci.org/chalk/ansi-styles) + +> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal + +You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings. + +<img src="screenshot.svg" width="900"> + +## Install + +``` +$ npm install ansi-styles +``` + +## Usage + +```js +const style = require('ansi-styles'); + +console.log(`${style.green.open}Hello world!${style.green.close}`); + + +// Color conversion between 16/256/truecolor +// NOTE: If conversion goes to 16 colors or 256 colors, the original color +// may be degraded to fit that color palette. This means terminals +// that do not support 16 million colors will best-match the +// original color. +console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close); +console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close); +console.log(style.color.ansi16m.hex('#abcdef') + 'Hello world!' + style.color.close); +``` + +## API + +Each style has an `open` and `close` property. + +## Styles + +### Modifiers + +- `reset` +- `bold` +- `dim` +- `italic` *(Not widely supported)* +- `underline` +- `inverse` +- `hidden` +- `strikethrough` *(Not widely supported)* + +### Colors + +- `black` +- `red` +- `green` +- `yellow` +- `blue` +- `magenta` +- `cyan` +- `white` +- `blackBright` (alias: `gray`, `grey`) +- `redBright` +- `greenBright` +- `yellowBright` +- `blueBright` +- `magentaBright` +- `cyanBright` +- `whiteBright` + +### Background colors + +- `bgBlack` +- `bgRed` +- `bgGreen` +- `bgYellow` +- `bgBlue` +- `bgMagenta` +- `bgCyan` +- `bgWhite` +- `bgBlackBright` (alias: `bgGray`, `bgGrey`) +- `bgRedBright` +- `bgGreenBright` +- `bgYellowBright` +- `bgBlueBright` +- `bgMagentaBright` +- `bgCyanBright` +- `bgWhiteBright` + +## Advanced usage + +By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module. + +- `style.modifier` +- `style.color` +- `style.bgColor` + +###### Example + +```js +console.log(style.color.green.open); +``` + +Raw escape codes (i.e. without the CSI escape prefix `\u001B[` and render mode postfix `m`) are available under `style.codes`, which returns a `Map` with the open codes as keys and close codes as values. + +###### Example + +```js +console.log(style.codes.get(36)); +//=> 39 +``` + +## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728) + +`ansi-styles` uses the [`color-convert`](https://github.com/Qix-/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors. + +The following color spaces from `color-convert` are supported: + +- `rgb` +- `hex` +- `keyword` +- `hsl` +- `hsv` +- `hwb` +- `ansi` +- `ansi256` + +To use these, call the associated conversion function with the intended output, for example: + +```js +style.color.ansi.rgb(100, 200, 15); // RGB to 16 color ansi foreground code +style.bgColor.ansi.rgb(100, 200, 15); // RGB to 16 color ansi background code + +style.color.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code +style.bgColor.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code + +style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground code +style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code +``` + +## Related + +- [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal + +## Maintainers + +- [Sindre Sorhus](https://github.com/sindresorhus) +- [Josh Junon](https://github.com/qix-) + +## For enterprise + +Available as part of the Tidelift Subscription. + +The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/LICENSE b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/LICENSE new file mode 100644 index 00000000000000..5b4c386f9269b3 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2011-2016 Heather Arthur <fayearthur@gmail.com> + +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. + diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/README.md b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/README.md new file mode 100644 index 00000000000000..d4b08fc369948d --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/README.md @@ -0,0 +1,68 @@ +# color-convert + +[](https://travis-ci.org/Qix-/color-convert) + +Color-convert is a color conversion library for JavaScript and node. +It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s (will round to closest): + +```js +var convert = require('color-convert'); + +convert.rgb.hsl(140, 200, 100); // [96, 48, 59] +convert.keyword.rgb('blue'); // [0, 0, 255] + +var rgbChannels = convert.rgb.channels; // 3 +var cmykChannels = convert.cmyk.channels; // 4 +var ansiChannels = convert.ansi16.channels; // 1 +``` + +# Install + +```console +$ npm install color-convert +``` + +# API + +Simply get the property of the _from_ and _to_ conversion that you're looking for. + +All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function. + +All 'from' functions have a hidden property called `.channels` that indicates the number of channels the function expects (not including alpha). + +```js +var convert = require('color-convert'); + +// Hex to LAB +convert.hex.lab('DEADBF'); // [ 76, 21, -2 ] +convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ] + +// RGB to CMYK +convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ] +convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ] +``` + +### Arrays +All functions that accept multiple arguments also support passing an array. + +Note that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.) + +```js +var convert = require('color-convert'); + +convert.rgb.hex(123, 45, 67); // '7B2D43' +convert.rgb.hex([123, 45, 67]); // '7B2D43' +``` + +## Routing + +Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex). + +Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js). + +# Contribute + +If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request. + +# License +Copyright © 2011-2016, Heather Arthur and Josh Junon. Licensed under the [MIT License](LICENSE). diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/conversions.js b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/conversions.js new file mode 100644 index 00000000000000..2657f265c9e102 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/conversions.js @@ -0,0 +1,839 @@ +/* MIT license */ +/* eslint-disable no-mixed-operators */ +const cssKeywords = require('color-name'); + +// NOTE: conversions should only return primitive values (i.e. arrays, or +// values that give correct `typeof` results). +// do not use box values types (i.e. Number(), String(), etc.) + +const reverseKeywords = {}; +for (const key of Object.keys(cssKeywords)) { + reverseKeywords[cssKeywords[key]] = key; +} + +const convert = { + rgb: {channels: 3, labels: 'rgb'}, + hsl: {channels: 3, labels: 'hsl'}, + hsv: {channels: 3, labels: 'hsv'}, + hwb: {channels: 3, labels: 'hwb'}, + cmyk: {channels: 4, labels: 'cmyk'}, + xyz: {channels: 3, labels: 'xyz'}, + lab: {channels: 3, labels: 'lab'}, + lch: {channels: 3, labels: 'lch'}, + hex: {channels: 1, labels: ['hex']}, + keyword: {channels: 1, labels: ['keyword']}, + ansi16: {channels: 1, labels: ['ansi16']}, + ansi256: {channels: 1, labels: ['ansi256']}, + hcg: {channels: 3, labels: ['h', 'c', 'g']}, + apple: {channels: 3, labels: ['r16', 'g16', 'b16']}, + gray: {channels: 1, labels: ['gray']} +}; + +module.exports = convert; + +// Hide .channels and .labels properties +for (const model of Object.keys(convert)) { + if (!('channels' in convert[model])) { + throw new Error('missing channels property: ' + model); + } + + if (!('labels' in convert[model])) { + throw new Error('missing channel labels property: ' + model); + } + + if (convert[model].labels.length !== convert[model].channels) { + throw new Error('channel and label counts mismatch: ' + model); + } + + const {channels, labels} = convert[model]; + delete convert[model].channels; + delete convert[model].labels; + Object.defineProperty(convert[model], 'channels', {value: channels}); + Object.defineProperty(convert[model], 'labels', {value: labels}); +} + +convert.rgb.hsl = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const min = Math.min(r, g, b); + const max = Math.max(r, g, b); + const delta = max - min; + let h; + let s; + + if (max === min) { + h = 0; + } else if (r === max) { + h = (g - b) / delta; + } else if (g === max) { + h = 2 + (b - r) / delta; + } else if (b === max) { + h = 4 + (r - g) / delta; + } + + h = Math.min(h * 60, 360); + + if (h < 0) { + h += 360; + } + + const l = (min + max) / 2; + + if (max === min) { + s = 0; + } else if (l <= 0.5) { + s = delta / (max + min); + } else { + s = delta / (2 - max - min); + } + + return [h, s * 100, l * 100]; +}; + +convert.rgb.hsv = function (rgb) { + let rdif; + let gdif; + let bdif; + let h; + let s; + + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const v = Math.max(r, g, b); + const diff = v - Math.min(r, g, b); + const diffc = function (c) { + return (v - c) / 6 / diff + 1 / 2; + }; + + if (diff === 0) { + h = 0; + s = 0; + } else { + s = diff / v; + rdif = diffc(r); + gdif = diffc(g); + bdif = diffc(b); + + if (r === v) { + h = bdif - gdif; + } else if (g === v) { + h = (1 / 3) + rdif - bdif; + } else if (b === v) { + h = (2 / 3) + gdif - rdif; + } + + if (h < 0) { + h += 1; + } else if (h > 1) { + h -= 1; + } + } + + return [ + h * 360, + s * 100, + v * 100 + ]; +}; + +convert.rgb.hwb = function (rgb) { + const r = rgb[0]; + const g = rgb[1]; + let b = rgb[2]; + const h = convert.rgb.hsl(rgb)[0]; + const w = 1 / 255 * Math.min(r, Math.min(g, b)); + + b = 1 - 1 / 255 * Math.max(r, Math.max(g, b)); + + return [h, w * 100, b * 100]; +}; + +convert.rgb.cmyk = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + + const k = Math.min(1 - r, 1 - g, 1 - b); + const c = (1 - r - k) / (1 - k) || 0; + const m = (1 - g - k) / (1 - k) || 0; + const y = (1 - b - k) / (1 - k) || 0; + + return [c * 100, m * 100, y * 100, k * 100]; +}; + +function comparativeDistance(x, y) { + /* + See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance + */ + return ( + ((x[0] - y[0]) ** 2) + + ((x[1] - y[1]) ** 2) + + ((x[2] - y[2]) ** 2) + ); +} + +convert.rgb.keyword = function (rgb) { + const reversed = reverseKeywords[rgb]; + if (reversed) { + return reversed; + } + + let currentClosestDistance = Infinity; + let currentClosestKeyword; + + for (const keyword of Object.keys(cssKeywords)) { + const value = cssKeywords[keyword]; + + // Compute comparative distance + const distance = comparativeDistance(rgb, value); + + // Check if its less, if so set as closest + if (distance < currentClosestDistance) { + currentClosestDistance = distance; + currentClosestKeyword = keyword; + } + } + + return currentClosestKeyword; +}; + +convert.keyword.rgb = function (keyword) { + return cssKeywords[keyword]; +}; + +convert.rgb.xyz = function (rgb) { + let r = rgb[0] / 255; + let g = rgb[1] / 255; + let b = rgb[2] / 255; + + // Assume sRGB + r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92); + g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92); + b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92); + + const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + return [x * 100, y * 100, z * 100]; +}; + +convert.rgb.lab = function (rgb) { + const xyz = convert.rgb.xyz(rgb); + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.hsl.rgb = function (hsl) { + const h = hsl[0] / 360; + const s = hsl[1] / 100; + const l = hsl[2] / 100; + let t2; + let t3; + let val; + + if (s === 0) { + val = l * 255; + return [val, val, val]; + } + + if (l < 0.5) { + t2 = l * (1 + s); + } else { + t2 = l + s - l * s; + } + + const t1 = 2 * l - t2; + + const rgb = [0, 0, 0]; + for (let i = 0; i < 3; i++) { + t3 = h + 1 / 3 * -(i - 1); + if (t3 < 0) { + t3++; + } + + if (t3 > 1) { + t3--; + } + + if (6 * t3 < 1) { + val = t1 + (t2 - t1) * 6 * t3; + } else if (2 * t3 < 1) { + val = t2; + } else if (3 * t3 < 2) { + val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; + } else { + val = t1; + } + + rgb[i] = val * 255; + } + + return rgb; +}; + +convert.hsl.hsv = function (hsl) { + const h = hsl[0]; + let s = hsl[1] / 100; + let l = hsl[2] / 100; + let smin = s; + const lmin = Math.max(l, 0.01); + + l *= 2; + s *= (l <= 1) ? l : 2 - l; + smin *= lmin <= 1 ? lmin : 2 - lmin; + const v = (l + s) / 2; + const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s); + + return [h, sv * 100, v * 100]; +}; + +convert.hsv.rgb = function (hsv) { + const h = hsv[0] / 60; + const s = hsv[1] / 100; + let v = hsv[2] / 100; + const hi = Math.floor(h) % 6; + + const f = h - Math.floor(h); + const p = 255 * v * (1 - s); + const q = 255 * v * (1 - (s * f)); + const t = 255 * v * (1 - (s * (1 - f))); + v *= 255; + + switch (hi) { + case 0: + return [v, t, p]; + case 1: + return [q, v, p]; + case 2: + return [p, v, t]; + case 3: + return [p, q, v]; + case 4: + return [t, p, v]; + case 5: + return [v, p, q]; + } +}; + +convert.hsv.hsl = function (hsv) { + const h = hsv[0]; + const s = hsv[1] / 100; + const v = hsv[2] / 100; + const vmin = Math.max(v, 0.01); + let sl; + let l; + + l = (2 - s) * v; + const lmin = (2 - s) * vmin; + sl = s * vmin; + sl /= (lmin <= 1) ? lmin : 2 - lmin; + sl = sl || 0; + l /= 2; + + return [h, sl * 100, l * 100]; +}; + +// http://dev.w3.org/csswg/css-color/#hwb-to-rgb +convert.hwb.rgb = function (hwb) { + const h = hwb[0] / 360; + let wh = hwb[1] / 100; + let bl = hwb[2] / 100; + const ratio = wh + bl; + let f; + + // Wh + bl cant be > 1 + if (ratio > 1) { + wh /= ratio; + bl /= ratio; + } + + const i = Math.floor(6 * h); + const v = 1 - bl; + f = 6 * h - i; + + if ((i & 0x01) !== 0) { + f = 1 - f; + } + + const n = wh + f * (v - wh); // Linear interpolation + + let r; + let g; + let b; + /* eslint-disable max-statements-per-line,no-multi-spaces */ + switch (i) { + default: + case 6: + case 0: r = v; g = n; b = wh; break; + case 1: r = n; g = v; b = wh; break; + case 2: r = wh; g = v; b = n; break; + case 3: r = wh; g = n; b = v; break; + case 4: r = n; g = wh; b = v; break; + case 5: r = v; g = wh; b = n; break; + } + /* eslint-enable max-statements-per-line,no-multi-spaces */ + + return [r * 255, g * 255, b * 255]; +}; + +convert.cmyk.rgb = function (cmyk) { + const c = cmyk[0] / 100; + const m = cmyk[1] / 100; + const y = cmyk[2] / 100; + const k = cmyk[3] / 100; + + const r = 1 - Math.min(1, c * (1 - k) + k); + const g = 1 - Math.min(1, m * (1 - k) + k); + const b = 1 - Math.min(1, y * (1 - k) + k); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.rgb = function (xyz) { + const x = xyz[0] / 100; + const y = xyz[1] / 100; + const z = xyz[2] / 100; + let r; + let g; + let b; + + r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + // Assume sRGB + r = r > 0.0031308 + ? ((1.055 * (r ** (1.0 / 2.4))) - 0.055) + : r * 12.92; + + g = g > 0.0031308 + ? ((1.055 * (g ** (1.0 / 2.4))) - 0.055) + : g * 12.92; + + b = b > 0.0031308 + ? ((1.055 * (b ** (1.0 / 2.4))) - 0.055) + : b * 12.92; + + r = Math.min(Math.max(0, r), 1); + g = Math.min(Math.max(0, g), 1); + b = Math.min(Math.max(0, b), 1); + + return [r * 255, g * 255, b * 255]; +}; + +convert.xyz.lab = function (xyz) { + let x = xyz[0]; + let y = xyz[1]; + let z = xyz[2]; + + x /= 95.047; + y /= 100; + z /= 108.883; + + x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116); + y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116); + z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116); + + const l = (116 * y) - 16; + const a = 500 * (x - y); + const b = 200 * (y - z); + + return [l, a, b]; +}; + +convert.lab.xyz = function (lab) { + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let x; + let y; + let z; + + y = (l + 16) / 116; + x = a / 500 + y; + z = y - b / 200; + + const y2 = y ** 3; + const x2 = x ** 3; + const z2 = z ** 3; + y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787; + x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787; + z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787; + + x *= 95.047; + y *= 100; + z *= 108.883; + + return [x, y, z]; +}; + +convert.lab.lch = function (lab) { + const l = lab[0]; + const a = lab[1]; + const b = lab[2]; + let h; + + const hr = Math.atan2(b, a); + h = hr * 360 / 2 / Math.PI; + + if (h < 0) { + h += 360; + } + + const c = Math.sqrt(a * a + b * b); + + return [l, c, h]; +}; + +convert.lch.lab = function (lch) { + const l = lch[0]; + const c = lch[1]; + const h = lch[2]; + + const hr = h / 360 * 2 * Math.PI; + const a = c * Math.cos(hr); + const b = c * Math.sin(hr); + + return [l, a, b]; +}; + +convert.rgb.ansi16 = function (args, saturation = null) { + const [r, g, b] = args; + let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization + + value = Math.round(value / 50); + + if (value === 0) { + return 30; + } + + let ansi = 30 + + ((Math.round(b / 255) << 2) + | (Math.round(g / 255) << 1) + | Math.round(r / 255)); + + if (value === 2) { + ansi += 60; + } + + return ansi; +}; + +convert.hsv.ansi16 = function (args) { + // Optimization here; we already know the value and don't need to get + // it converted for us. + return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]); +}; + +convert.rgb.ansi256 = function (args) { + const r = args[0]; + const g = args[1]; + const b = args[2]; + + // We use the extended greyscale palette here, with the exception of + // black and white. normal palette only has 4 greyscale shades. + if (r === g && g === b) { + if (r < 8) { + return 16; + } + + if (r > 248) { + return 231; + } + + return Math.round(((r - 8) / 247) * 24) + 232; + } + + const ansi = 16 + + (36 * Math.round(r / 255 * 5)) + + (6 * Math.round(g / 255 * 5)) + + Math.round(b / 255 * 5); + + return ansi; +}; + +convert.ansi16.rgb = function (args) { + let color = args % 10; + + // Handle greyscale + if (color === 0 || color === 7) { + if (args > 50) { + color += 3.5; + } + + color = color / 10.5 * 255; + + return [color, color, color]; + } + + const mult = (~~(args > 50) + 1) * 0.5; + const r = ((color & 1) * mult) * 255; + const g = (((color >> 1) & 1) * mult) * 255; + const b = (((color >> 2) & 1) * mult) * 255; + + return [r, g, b]; +}; + +convert.ansi256.rgb = function (args) { + // Handle greyscale + if (args >= 232) { + const c = (args - 232) * 10 + 8; + return [c, c, c]; + } + + args -= 16; + + let rem; + const r = Math.floor(args / 36) / 5 * 255; + const g = Math.floor((rem = args % 36) / 6) / 5 * 255; + const b = (rem % 6) / 5 * 255; + + return [r, g, b]; +}; + +convert.rgb.hex = function (args) { + const integer = ((Math.round(args[0]) & 0xFF) << 16) + + ((Math.round(args[1]) & 0xFF) << 8) + + (Math.round(args[2]) & 0xFF); + + const string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.hex.rgb = function (args) { + const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i); + if (!match) { + return [0, 0, 0]; + } + + let colorString = match[0]; + + if (match[0].length === 3) { + colorString = colorString.split('').map(char => { + return char + char; + }).join(''); + } + + const integer = parseInt(colorString, 16); + const r = (integer >> 16) & 0xFF; + const g = (integer >> 8) & 0xFF; + const b = integer & 0xFF; + + return [r, g, b]; +}; + +convert.rgb.hcg = function (rgb) { + const r = rgb[0] / 255; + const g = rgb[1] / 255; + const b = rgb[2] / 255; + const max = Math.max(Math.max(r, g), b); + const min = Math.min(Math.min(r, g), b); + const chroma = (max - min); + let grayscale; + let hue; + + if (chroma < 1) { + grayscale = min / (1 - chroma); + } else { + grayscale = 0; + } + + if (chroma <= 0) { + hue = 0; + } else + if (max === r) { + hue = ((g - b) / chroma) % 6; + } else + if (max === g) { + hue = 2 + (b - r) / chroma; + } else { + hue = 4 + (r - g) / chroma; + } + + hue /= 6; + hue %= 1; + + return [hue * 360, chroma * 100, grayscale * 100]; +}; + +convert.hsl.hcg = function (hsl) { + const s = hsl[1] / 100; + const l = hsl[2] / 100; + + const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l)); + + let f = 0; + if (c < 1.0) { + f = (l - 0.5 * c) / (1.0 - c); + } + + return [hsl[0], c * 100, f * 100]; +}; + +convert.hsv.hcg = function (hsv) { + const s = hsv[1] / 100; + const v = hsv[2] / 100; + + const c = s * v; + let f = 0; + + if (c < 1.0) { + f = (v - c) / (1 - c); + } + + return [hsv[0], c * 100, f * 100]; +}; + +convert.hcg.rgb = function (hcg) { + const h = hcg[0] / 360; + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + if (c === 0.0) { + return [g * 255, g * 255, g * 255]; + } + + const pure = [0, 0, 0]; + const hi = (h % 1) * 6; + const v = hi % 1; + const w = 1 - v; + let mg = 0; + + /* eslint-disable max-statements-per-line */ + switch (Math.floor(hi)) { + case 0: + pure[0] = 1; pure[1] = v; pure[2] = 0; break; + case 1: + pure[0] = w; pure[1] = 1; pure[2] = 0; break; + case 2: + pure[0] = 0; pure[1] = 1; pure[2] = v; break; + case 3: + pure[0] = 0; pure[1] = w; pure[2] = 1; break; + case 4: + pure[0] = v; pure[1] = 0; pure[2] = 1; break; + default: + pure[0] = 1; pure[1] = 0; pure[2] = w; + } + /* eslint-enable max-statements-per-line */ + + mg = (1.0 - c) * g; + + return [ + (c * pure[0] + mg) * 255, + (c * pure[1] + mg) * 255, + (c * pure[2] + mg) * 255 + ]; +}; + +convert.hcg.hsv = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + const v = c + g * (1.0 - c); + let f = 0; + + if (v > 0.0) { + f = c / v; + } + + return [hcg[0], f * 100, v * 100]; +}; + +convert.hcg.hsl = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + + const l = g * (1.0 - c) + 0.5 * c; + let s = 0; + + if (l > 0.0 && l < 0.5) { + s = c / (2 * l); + } else + if (l >= 0.5 && l < 1.0) { + s = c / (2 * (1 - l)); + } + + return [hcg[0], s * 100, l * 100]; +}; + +convert.hcg.hwb = function (hcg) { + const c = hcg[1] / 100; + const g = hcg[2] / 100; + const v = c + g * (1.0 - c); + return [hcg[0], (v - c) * 100, (1 - v) * 100]; +}; + +convert.hwb.hcg = function (hwb) { + const w = hwb[1] / 100; + const b = hwb[2] / 100; + const v = 1 - b; + const c = v - w; + let g = 0; + + if (c < 1) { + g = (v - c) / (1 - c); + } + + return [hwb[0], c * 100, g * 100]; +}; + +convert.apple.rgb = function (apple) { + return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255]; +}; + +convert.rgb.apple = function (rgb) { + return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535]; +}; + +convert.gray.rgb = function (args) { + return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255]; +}; + +convert.gray.hsl = function (args) { + return [0, 0, args[0]]; +}; + +convert.gray.hsv = convert.gray.hsl; + +convert.gray.hwb = function (gray) { + return [0, 100, gray[0]]; +}; + +convert.gray.cmyk = function (gray) { + return [0, 0, 0, gray[0]]; +}; + +convert.gray.lab = function (gray) { + return [gray[0], 0, 0]; +}; + +convert.gray.hex = function (gray) { + const val = Math.round(gray[0] / 100 * 255) & 0xFF; + const integer = (val << 16) + (val << 8) + val; + + const string = integer.toString(16).toUpperCase(); + return '000000'.substring(string.length) + string; +}; + +convert.rgb.gray = function (rgb) { + const val = (rgb[0] + rgb[1] + rgb[2]) / 3; + return [val / 255 * 100]; +}; diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/index.js b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/index.js new file mode 100644 index 00000000000000..b648e5737be616 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/index.js @@ -0,0 +1,81 @@ +const conversions = require('./conversions'); +const route = require('./route'); + +const convert = {}; + +const models = Object.keys(conversions); + +function wrapRaw(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + if (arg0 === undefined || arg0 === null) { + return arg0; + } + + if (arg0.length > 1) { + args = arg0; + } + + return fn(args); + }; + + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +function wrapRounded(fn) { + const wrappedFn = function (...args) { + const arg0 = args[0]; + + if (arg0 === undefined || arg0 === null) { + return arg0; + } + + if (arg0.length > 1) { + args = arg0; + } + + const result = fn(args); + + // We're assuming the result is an array here. + // see notice in conversions.js; don't use box types + // in conversion functions. + if (typeof result === 'object') { + for (let len = result.length, i = 0; i < len; i++) { + result[i] = Math.round(result[i]); + } + } + + return result; + }; + + // Preserve .conversion property if there is one + if ('conversion' in fn) { + wrappedFn.conversion = fn.conversion; + } + + return wrappedFn; +} + +models.forEach(fromModel => { + convert[fromModel] = {}; + + Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels}); + Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels}); + + const routes = route(fromModel); + const routeModels = Object.keys(routes); + + routeModels.forEach(toModel => { + const fn = routes[toModel]; + + convert[fromModel][toModel] = wrapRounded(fn); + convert[fromModel][toModel].raw = wrapRaw(fn); + }); +}); + +module.exports = convert; diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/package.json b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/package.json new file mode 100644 index 00000000000000..427616bd0587d8 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/package.json @@ -0,0 +1,60 @@ +{ + "author": { + "name": "Heather Arthur", + "email": "fayearthur@gmail.com" + }, + "bugs": { + "url": "https://github.com/Qix-/color-convert/issues" + }, + "bundleDependencies": false, + "dependencies": { + "color-name": "~1.1.4" + }, + "deprecated": false, + "description": "Plain color conversion functions", + "devDependencies": { + "chalk": "^2.4.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=7.0.0" + }, + "files": [ + "index.js", + "conversions.js", + "route.js" + ], + "homepage": "https://github.com/Qix-/color-convert#readme", + "keywords": [ + "color", + "colour", + "convert", + "converter", + "conversion", + "rgb", + "hsl", + "hsv", + "hwb", + "cmyk", + "ansi", + "ansi16" + ], + "license": "MIT", + "name": "color-convert", + "repository": { + "type": "git", + "url": "git+https://github.com/Qix-/color-convert.git" + }, + "scripts": { + "pretest": "xo", + "test": "node test/basic.js" + }, + "version": "2.0.1", + "xo": { + "rules": { + "default-case": 0, + "no-inline-comments": 0, + "operator-linebreak": 0 + } + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/route.js b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/route.js new file mode 100644 index 00000000000000..1a08521b5a0017 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-convert/route.js @@ -0,0 +1,97 @@ +const conversions = require('./conversions'); + +/* + This function routes a model to all other models. + + all functions that are routed have a property `.conversion` attached + to the returned synthetic function. This property is an array + of strings, each with the steps in between the 'from' and 'to' + color models (inclusive). + + conversions that are not possible simply are not included. +*/ + +function buildGraph() { + const graph = {}; + // https://jsperf.com/object-keys-vs-for-in-with-closure/3 + const models = Object.keys(conversions); + + for (let len = models.length, i = 0; i < len; i++) { + graph[models[i]] = { + // http://jsperf.com/1-vs-infinity + // micro-opt, but this is simple. + distance: -1, + parent: null + }; + } + + return graph; +} + +// https://en.wikipedia.org/wiki/Breadth-first_search +function deriveBFS(fromModel) { + const graph = buildGraph(); + const queue = [fromModel]; // Unshift -> queue -> pop + + graph[fromModel].distance = 0; + + while (queue.length) { + const current = queue.pop(); + const adjacents = Object.keys(conversions[current]); + + for (let len = adjacents.length, i = 0; i < len; i++) { + const adjacent = adjacents[i]; + const node = graph[adjacent]; + + if (node.distance === -1) { + node.distance = graph[current].distance + 1; + node.parent = current; + queue.unshift(adjacent); + } + } + } + + return graph; +} + +function link(from, to) { + return function (args) { + return to(from(args)); + }; +} + +function wrapConversion(toModel, graph) { + const path = [graph[toModel].parent, toModel]; + let fn = conversions[graph[toModel].parent][toModel]; + + let cur = graph[toModel].parent; + while (graph[cur].parent) { + path.unshift(graph[cur].parent); + fn = link(conversions[graph[cur].parent][cur], fn); + cur = graph[cur].parent; + } + + fn.conversion = path; + return fn; +} + +module.exports = function (fromModel) { + const graph = deriveBFS(fromModel); + const conversion = {}; + + const models = Object.keys(graph); + for (let len = models.length, i = 0; i < len; i++) { + const toModel = models[i]; + const node = graph[toModel]; + + if (node.parent === null) { + // No possible conversion, or this node is the source model. + continue; + } + + conversion[toModel] = wrapConversion(toModel, graph); + } + + return conversion; +}; + diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/LICENSE b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/LICENSE new file mode 100644 index 00000000000000..c6b10012540c24 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/LICENSE @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2015 Dmitry Ivanov + +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. \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/README.md b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/README.md new file mode 100644 index 00000000000000..932b979176f33b --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/README.md @@ -0,0 +1,11 @@ +A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors. + +[](https://nodei.co/npm/color-name/) + + +```js +var colors = require('color-name'); +colors.red //[255,0,0] +``` + +<a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a> diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/index.js b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/index.js new file mode 100644 index 00000000000000..b7c198a6f3d7c5 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/index.js @@ -0,0 +1,152 @@ +'use strict' + +module.exports = { + "aliceblue": [240, 248, 255], + "antiquewhite": [250, 235, 215], + "aqua": [0, 255, 255], + "aquamarine": [127, 255, 212], + "azure": [240, 255, 255], + "beige": [245, 245, 220], + "bisque": [255, 228, 196], + "black": [0, 0, 0], + "blanchedalmond": [255, 235, 205], + "blue": [0, 0, 255], + "blueviolet": [138, 43, 226], + "brown": [165, 42, 42], + "burlywood": [222, 184, 135], + "cadetblue": [95, 158, 160], + "chartreuse": [127, 255, 0], + "chocolate": [210, 105, 30], + "coral": [255, 127, 80], + "cornflowerblue": [100, 149, 237], + "cornsilk": [255, 248, 220], + "crimson": [220, 20, 60], + "cyan": [0, 255, 255], + "darkblue": [0, 0, 139], + "darkcyan": [0, 139, 139], + "darkgoldenrod": [184, 134, 11], + "darkgray": [169, 169, 169], + "darkgreen": [0, 100, 0], + "darkgrey": [169, 169, 169], + "darkkhaki": [189, 183, 107], + "darkmagenta": [139, 0, 139], + "darkolivegreen": [85, 107, 47], + "darkorange": [255, 140, 0], + "darkorchid": [153, 50, 204], + "darkred": [139, 0, 0], + "darksalmon": [233, 150, 122], + "darkseagreen": [143, 188, 143], + "darkslateblue": [72, 61, 139], + "darkslategray": [47, 79, 79], + "darkslategrey": [47, 79, 79], + "darkturquoise": [0, 206, 209], + "darkviolet": [148, 0, 211], + "deeppink": [255, 20, 147], + "deepskyblue": [0, 191, 255], + "dimgray": [105, 105, 105], + "dimgrey": [105, 105, 105], + "dodgerblue": [30, 144, 255], + "firebrick": [178, 34, 34], + "floralwhite": [255, 250, 240], + "forestgreen": [34, 139, 34], + "fuchsia": [255, 0, 255], + "gainsboro": [220, 220, 220], + "ghostwhite": [248, 248, 255], + "gold": [255, 215, 0], + "goldenrod": [218, 165, 32], + "gray": [128, 128, 128], + "green": [0, 128, 0], + "greenyellow": [173, 255, 47], + "grey": [128, 128, 128], + "honeydew": [240, 255, 240], + "hotpink": [255, 105, 180], + "indianred": [205, 92, 92], + "indigo": [75, 0, 130], + "ivory": [255, 255, 240], + "khaki": [240, 230, 140], + "lavender": [230, 230, 250], + "lavenderblush": [255, 240, 245], + "lawngreen": [124, 252, 0], + "lemonchiffon": [255, 250, 205], + "lightblue": [173, 216, 230], + "lightcoral": [240, 128, 128], + "lightcyan": [224, 255, 255], + "lightgoldenrodyellow": [250, 250, 210], + "lightgray": [211, 211, 211], + "lightgreen": [144, 238, 144], + "lightgrey": [211, 211, 211], + "lightpink": [255, 182, 193], + "lightsalmon": [255, 160, 122], + "lightseagreen": [32, 178, 170], + "lightskyblue": [135, 206, 250], + "lightslategray": [119, 136, 153], + "lightslategrey": [119, 136, 153], + "lightsteelblue": [176, 196, 222], + "lightyellow": [255, 255, 224], + "lime": [0, 255, 0], + "limegreen": [50, 205, 50], + "linen": [250, 240, 230], + "magenta": [255, 0, 255], + "maroon": [128, 0, 0], + "mediumaquamarine": [102, 205, 170], + "mediumblue": [0, 0, 205], + "mediumorchid": [186, 85, 211], + "mediumpurple": [147, 112, 219], + "mediumseagreen": [60, 179, 113], + "mediumslateblue": [123, 104, 238], + "mediumspringgreen": [0, 250, 154], + "mediumturquoise": [72, 209, 204], + "mediumvioletred": [199, 21, 133], + "midnightblue": [25, 25, 112], + "mintcream": [245, 255, 250], + "mistyrose": [255, 228, 225], + "moccasin": [255, 228, 181], + "navajowhite": [255, 222, 173], + "navy": [0, 0, 128], + "oldlace": [253, 245, 230], + "olive": [128, 128, 0], + "olivedrab": [107, 142, 35], + "orange": [255, 165, 0], + "orangered": [255, 69, 0], + "orchid": [218, 112, 214], + "palegoldenrod": [238, 232, 170], + "palegreen": [152, 251, 152], + "paleturquoise": [175, 238, 238], + "palevioletred": [219, 112, 147], + "papayawhip": [255, 239, 213], + "peachpuff": [255, 218, 185], + "peru": [205, 133, 63], + "pink": [255, 192, 203], + "plum": [221, 160, 221], + "powderblue": [176, 224, 230], + "purple": [128, 0, 128], + "rebeccapurple": [102, 51, 153], + "red": [255, 0, 0], + "rosybrown": [188, 143, 143], + "royalblue": [65, 105, 225], + "saddlebrown": [139, 69, 19], + "salmon": [250, 128, 114], + "sandybrown": [244, 164, 96], + "seagreen": [46, 139, 87], + "seashell": [255, 245, 238], + "sienna": [160, 82, 45], + "silver": [192, 192, 192], + "skyblue": [135, 206, 235], + "slateblue": [106, 90, 205], + "slategray": [112, 128, 144], + "slategrey": [112, 128, 144], + "snow": [255, 250, 250], + "springgreen": [0, 255, 127], + "steelblue": [70, 130, 180], + "tan": [210, 180, 140], + "teal": [0, 128, 128], + "thistle": [216, 191, 216], + "tomato": [255, 99, 71], + "turquoise": [64, 224, 208], + "violet": [238, 130, 238], + "wheat": [245, 222, 179], + "white": [255, 255, 255], + "whitesmoke": [245, 245, 245], + "yellow": [255, 255, 0], + "yellowgreen": [154, 205, 50] +}; diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/package.json b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/package.json new file mode 100644 index 00000000000000..07b8f6ece8170a --- /dev/null +++ b/tools/node_modules/eslint/node_modules/slice-ansi/node_modules/color-name/package.json @@ -0,0 +1,33 @@ +{ + "author": { + "name": "DY", + "email": "dfcreative@gmail.com" + }, + "bugs": { + "url": "https://github.com/colorjs/color-name/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "A list of color names and its values", + "files": [ + "index.js" + ], + "homepage": "https://github.com/colorjs/color-name", + "keywords": [ + "color-name", + "color", + "color-keyword", + "keyword" + ], + "license": "MIT", + "main": "index.js", + "name": "color-name", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/colorjs/color-name.git" + }, + "scripts": { + "test": "node test.js" + }, + "version": "1.1.4" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/slice-ansi/package.json b/tools/node_modules/eslint/node_modules/slice-ansi/package.json index 7d6ea6956dcfb1..1e32ac1c4a9650 100644 --- a/tools/node_modules/eslint/node_modules/slice-ansi/package.json +++ b/tools/node_modules/eslint/node_modules/slice-ansi/package.json @@ -1,52 +1,61 @@ { - "name": "slice-ansi", - "version": "4.0.0", - "description": "Slice a string with ANSI escape codes", - "license": "MIT", - "repository": "chalk/slice-ansi", - "funding": "https://github.com/chalk/slice-ansi?sponsor=1", - "engines": { - "node": ">=10" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js" - ], - "keywords": [ - "slice", - "string", - "ansi", - "styles", - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "tty", - "escape", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "log", - "logging", - "command-line", - "text" - ], - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "devDependencies": { - "ava": "^2.1.0", - "chalk": "^3.0.0", - "random-item": "^3.0.0", - "strip-ansi": "^6.0.0", - "xo": "^0.26.1" - } -} + "bugs": { + "url": "https://github.com/chalk/slice-ansi/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "deprecated": false, + "description": "Slice a string with ANSI escape codes", + "devDependencies": { + "ava": "^2.1.0", + "chalk": "^3.0.0", + "random-item": "^3.0.0", + "strip-ansi": "^6.0.0", + "xo": "^0.26.1" + }, + "engines": { + "node": ">=10" + }, + "files": [ + "index.js" + ], + "funding": "https://github.com/chalk/slice-ansi?sponsor=1", + "homepage": "https://github.com/chalk/slice-ansi#readme", + "keywords": [ + "slice", + "string", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "slice-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/slice-ansi.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "4.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/sprintf-js/package.json b/tools/node_modules/eslint/node_modules/sprintf-js/package.json index 75f7eca71ea37f..da639c61582bbe 100644 --- a/tools/node_modules/eslint/node_modules/sprintf-js/package.json +++ b/tools/node_modules/eslint/node_modules/sprintf-js/package.json @@ -1,22 +1,31 @@ { - "name": "sprintf-js", - "version": "1.0.3", - "description": "JavaScript sprintf implementation", - "author": "Alexandru Marasteanu <hello@alexei.ro> (http://alexei.ro/)", - "main": "src/sprintf.js", - "scripts": { - "test": "mocha test/test.js" - }, - "repository": { - "type": "git", - "url": "https://github.com/alexei/sprintf.js.git" - }, - "license": "BSD-3-Clause", - "readmeFilename": "README.md", - "devDependencies": { - "mocha": "*", - "grunt": "*", - "grunt-contrib-watch": "*", - "grunt-contrib-uglify": "*" - } -} + "author": { + "name": "Alexandru Marasteanu", + "email": "hello@alexei.ro", + "url": "http://alexei.ro/" + }, + "bugs": { + "url": "https://github.com/alexei/sprintf.js/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "JavaScript sprintf implementation", + "devDependencies": { + "grunt": "*", + "grunt-contrib-uglify": "*", + "grunt-contrib-watch": "*", + "mocha": "*" + }, + "homepage": "https://github.com/alexei/sprintf.js#readme", + "license": "BSD-3-Clause", + "main": "src/sprintf.js", + "name": "sprintf-js", + "repository": { + "type": "git", + "url": "git+https://github.com/alexei/sprintf.js.git" + }, + "scripts": { + "test": "mocha test/test.js" + }, + "version": "1.0.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/string-width/package.json b/tools/node_modules/eslint/node_modules/string-width/package.json index b9b20caaf6f1cd..e2b884cb399042 100644 --- a/tools/node_modules/eslint/node_modules/string-width/package.json +++ b/tools/node_modules/eslint/node_modules/string-width/package.json @@ -1,56 +1,65 @@ { - "name": "string-width", - "version": "4.2.2", - "description": "Get the visual width of a string - the number of columns required to display it", - "license": "MIT", - "repository": "sindresorhus/string-width", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "string", - "character", - "unicode", - "width", - "visual", - "column", - "columns", - "fullwidth", - "full-width", - "full", - "ansi", - "escape", - "codes", - "cli", - "command-line", - "terminal", - "console", - "cjk", - "chinese", - "japanese", - "korean", - "fixed-width" - ], - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "devDependencies": { - "ava": "^1.4.1", - "tsd": "^0.7.1", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/string-width/issues" + }, + "bundleDependencies": false, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "deprecated": false, + "description": "Get the visual width of a string - the number of columns required to display it", + "devDependencies": { + "ava": "^1.4.1", + "tsd": "^0.7.1", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/sindresorhus/string-width#readme", + "keywords": [ + "string", + "character", + "unicode", + "width", + "visual", + "column", + "columns", + "fullwidth", + "full-width", + "full", + "ansi", + "escape", + "codes", + "cli", + "command-line", + "terminal", + "console", + "cjk", + "chinese", + "japanese", + "korean", + "fixed-width" + ], + "license": "MIT", + "name": "string-width", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/string-width.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "4.2.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/strip-ansi/package.json b/tools/node_modules/eslint/node_modules/strip-ansi/package.json index 65a6c95161f747..5db6f68dc0aef7 100644 --- a/tools/node_modules/eslint/node_modules/strip-ansi/package.json +++ b/tools/node_modules/eslint/node_modules/strip-ansi/package.json @@ -1,54 +1,63 @@ { - "name": "strip-ansi", - "version": "6.0.0", - "description": "Strip ANSI escape codes from a string", - "license": "MIT", - "repository": "chalk/strip-ansi", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "strip", - "trim", - "remove", - "ansi", - "styles", - "color", - "colour", - "colors", - "terminal", - "console", - "string", - "tty", - "escape", - "formatting", - "rgb", - "256", - "shell", - "xterm", - "log", - "logging", - "command-line", - "text" - ], - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.10.0", - "xo": "^0.25.3" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/chalk/strip-ansi/issues" + }, + "bundleDependencies": false, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "deprecated": false, + "description": "Strip ANSI escape codes from a string", + "devDependencies": { + "ava": "^2.4.0", + "tsd": "^0.10.0", + "xo": "^0.25.3" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/chalk/strip-ansi#readme", + "keywords": [ + "strip", + "trim", + "remove", + "ansi", + "styles", + "color", + "colour", + "colors", + "terminal", + "console", + "string", + "tty", + "escape", + "formatting", + "rgb", + "256", + "shell", + "xterm", + "log", + "logging", + "command-line", + "text" + ], + "license": "MIT", + "name": "strip-ansi", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/strip-ansi.git" + }, + "scripts": { + "test": "xo && ava && tsd" + }, + "version": "6.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/strip-json-comments/package.json b/tools/node_modules/eslint/node_modules/strip-json-comments/package.json index ce7875aa0d1963..d28e657025b6e9 100644 --- a/tools/node_modules/eslint/node_modules/strip-json-comments/package.json +++ b/tools/node_modules/eslint/node_modules/strip-json-comments/package.json @@ -1,47 +1,56 @@ { - "name": "strip-json-comments", - "version": "3.1.1", - "description": "Strip comments from JSON. Lets you use comments in your JSON files!", - "license": "MIT", - "repository": "sindresorhus/strip-json-comments", - "funding": "https://github.com/sponsors/sindresorhus", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "https://sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava && tsd", - "bench": "matcha benchmark.js" - }, - "files": [ - "index.js", - "index.d.ts" - ], - "keywords": [ - "json", - "strip", - "comments", - "remove", - "delete", - "trim", - "multiline", - "parse", - "config", - "configuration", - "settings", - "util", - "env", - "environment", - "jsonc" - ], - "devDependencies": { - "ava": "^1.4.1", - "matcha": "^0.7.0", - "tsd": "^0.7.2", - "xo": "^0.24.0" - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/strip-json-comments/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "Strip comments from JSON. Lets you use comments in your JSON files!", + "devDependencies": { + "ava": "^1.4.1", + "matcha": "^0.7.0", + "tsd": "^0.7.2", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "funding": "https://github.com/sponsors/sindresorhus", + "homepage": "https://github.com/sindresorhus/strip-json-comments#readme", + "keywords": [ + "json", + "strip", + "comments", + "remove", + "delete", + "trim", + "multiline", + "parse", + "config", + "configuration", + "settings", + "util", + "env", + "environment", + "jsonc" + ], + "license": "MIT", + "name": "strip-json-comments", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/strip-json-comments.git" + }, + "scripts": { + "bench": "matcha benchmark.js", + "test": "xo && ava && tsd" + }, + "version": "3.1.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/supports-color/index.js b/tools/node_modules/eslint/node_modules/supports-color/index.js index 6fada390fb88d8..1704131bdf6c8f 100644 --- a/tools/node_modules/eslint/node_modules/supports-color/index.js +++ b/tools/node_modules/eslint/node_modules/supports-color/index.js @@ -1,31 +1,22 @@ 'use strict'; const os = require('os'); -const tty = require('tty'); const hasFlag = require('has-flag'); -const {env} = process; +const env = process.env; let forceColor; if (hasFlag('no-color') || hasFlag('no-colors') || - hasFlag('color=false') || - hasFlag('color=never')) { - forceColor = 0; + hasFlag('color=false')) { + forceColor = false; } else if (hasFlag('color') || hasFlag('colors') || hasFlag('color=true') || hasFlag('color=always')) { - forceColor = 1; + forceColor = true; } - if ('FORCE_COLOR' in env) { - if (env.FORCE_COLOR === 'true') { - forceColor = 1; - } else if (env.FORCE_COLOR === 'false') { - forceColor = 0; - } else { - forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3); - } + forceColor = env.FORCE_COLOR.length === 0 || parseInt(env.FORCE_COLOR, 10) !== 0; } function translateLevel(level) { @@ -41,8 +32,8 @@ function translateLevel(level) { }; } -function supportsColor(haveStream, streamIsTTY) { - if (forceColor === 0) { +function supportsColor(stream) { + if (forceColor === false) { return 0; } @@ -56,21 +47,22 @@ function supportsColor(haveStream, streamIsTTY) { return 2; } - if (haveStream && !streamIsTTY && forceColor === undefined) { + if (stream && !stream.isTTY && forceColor !== true) { return 0; } - const min = forceColor || 0; - - if (env.TERM === 'dumb') { - return min; - } + const min = forceColor ? 1 : 0; if (process.platform === 'win32') { - // Windows 10 build 10586 is the first Windows release that supports 256 colors. - // Windows 10 build 14931 is the first release that supports 16m/TrueColor. + // Node.js 7.5.0 is the first version of Node.js to include a patch to + // libuv that enables 256 color output on Windows. Anything earlier and it + // won't work. However, here we target Node.js 8 at minimum as it is an LTS + // release, and Node.js 7 is not. Windows 10 build 10586 is the first Windows + // release that supports 256 colors. Windows 10 build 14931 is the first release + // that supports 16m/TrueColor. const osRelease = os.release().split('.'); if ( + Number(process.versions.node.split('.')[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586 ) { @@ -81,7 +73,7 @@ function supportsColor(haveStream, streamIsTTY) { } if ('CI' in env) { - if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'GITHUB_ACTIONS', 'BUILDKITE'].some(sign => sign in env) || env.CI_NAME === 'codeship') { + if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI'].some(sign => sign in env) || env.CI_NAME === 'codeship') { return 1; } @@ -120,16 +112,20 @@ function supportsColor(haveStream, streamIsTTY) { return 1; } + if (env.TERM === 'dumb') { + return min; + } + return min; } function getSupportLevel(stream) { - const level = supportsColor(stream, stream && stream.isTTY); + const level = supportsColor(stream); return translateLevel(level); } module.exports = { supportsColor: getSupportLevel, - stdout: translateLevel(supportsColor(true, tty.isatty(1))), - stderr: translateLevel(supportsColor(true, tty.isatty(2))) + stdout: getSupportLevel(process.stdout), + stderr: getSupportLevel(process.stderr) }; diff --git a/tools/node_modules/eslint/node_modules/supports-color/package.json b/tools/node_modules/eslint/node_modules/supports-color/package.json index f7182edcea2baa..9e4eafa8573232 100644 --- a/tools/node_modules/eslint/node_modules/supports-color/package.json +++ b/tools/node_modules/eslint/node_modules/supports-color/package.json @@ -1,53 +1,62 @@ { - "name": "supports-color", - "version": "7.2.0", - "description": "Detect whether a terminal supports color", - "license": "MIT", - "repository": "chalk/supports-color", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && ava" - }, - "files": [ - "index.js", - "browser.js" - ], - "keywords": [ - "color", - "colour", - "colors", - "terminal", - "console", - "cli", - "ansi", - "styles", - "tty", - "rgb", - "256", - "shell", - "xterm", - "command-line", - "support", - "supports", - "capability", - "detect", - "truecolor", - "16m" - ], - "dependencies": { - "has-flag": "^4.0.0" - }, - "devDependencies": { - "ava": "^1.4.1", - "import-fresh": "^3.0.0", - "xo": "^0.24.0" - }, - "browser": "browser.js" -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "browser": "browser.js", + "bugs": { + "url": "https://github.com/chalk/supports-color/issues" + }, + "bundleDependencies": false, + "dependencies": { + "has-flag": "^3.0.0" + }, + "deprecated": false, + "description": "Detect whether a terminal supports color", + "devDependencies": { + "ava": "^0.25.0", + "import-fresh": "^2.0.0", + "xo": "^0.20.0" + }, + "engines": { + "node": ">=4" + }, + "files": [ + "index.js", + "browser.js" + ], + "homepage": "https://github.com/chalk/supports-color#readme", + "keywords": [ + "color", + "colour", + "colors", + "terminal", + "console", + "cli", + "ansi", + "styles", + "tty", + "rgb", + "256", + "shell", + "xterm", + "command-line", + "support", + "supports", + "capability", + "detect", + "truecolor", + "16m" + ], + "license": "MIT", + "name": "supports-color", + "repository": { + "type": "git", + "url": "git+https://github.com/chalk/supports-color.git" + }, + "scripts": { + "test": "xo && ava" + }, + "version": "5.5.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/supports-color/readme.md b/tools/node_modules/eslint/node_modules/supports-color/readme.md index 36542285863330..f6e40195730ae8 100644 --- a/tools/node_modules/eslint/node_modules/supports-color/readme.md +++ b/tools/node_modules/eslint/node_modules/supports-color/readme.md @@ -44,7 +44,7 @@ The `stdout`/`stderr` objects specifies a level of support for color through a ` It obeys the `--color` and `--no-color` CLI flags. -For situations where using `--color` is not possible, use the environment variable `FORCE_COLOR=1` (level 1), `FORCE_COLOR=2` (level 2), or `FORCE_COLOR=3` (level 3) to forcefully enable color, or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks. +Can be overridden by the user with the flags `--color` and `--no-color`. For situations where using `--color` is not possible, add the environment variable `FORCE_COLOR=1` to forcefully enable color or `FORCE_COLOR=0` to forcefully disable. The use of `FORCE_COLOR` overrides all other color support checks. Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color=16m` flags, respectively. @@ -61,16 +61,6 @@ Explicit 256/Truecolor mode can be enabled using the `--color=256` and `--color= - [Josh Junon](https://github.com/qix-) ---- +## License -<div align="center"> - <b> - <a href="https://tidelift.com/subscription/pkg/npm-supports-color?utm_source=npm-supports-color&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> - </b> - <br> - <sub> - Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. - </sub> -</div> - ---- +MIT diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/.tonic_example.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/.tonic_example.js index 2b0d6683eefe29..7579d21c5bafcf 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/.tonic_example.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/.tonic_example.js @@ -1,20 +1,20 @@ -var Ajv = require("ajv") -var ajv = new Ajv({allErrors: true}) +const Ajv = require("ajv").default +const ajv = new Ajv({allErrors: true}) -var schema = { +const schema = { properties: { foo: {type: "string"}, bar: {type: "number", maximum: 3}, }, } -var validate = ajv.compile(schema) +const validate = ajv.compile(schema) test({foo: "abc", bar: 2}) test({foo: 2, bar: 4}) function test(data) { - var valid = validate(data) + const valid = validate(data) if (valid) console.log("Valid!") else console.log("Invalid: " + ajv.errorsText(validate.errors)) } diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/LICENSE b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/LICENSE index 96ee719987f778..139162ad2c389a 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/LICENSE +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2017 Evgeny Poberezkin +Copyright (c) 2015-2021 Evgeny Poberezkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/README.md b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/README.md index ec11b9be7c503a..2641e8b5d97310 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/README.md +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/README.md @@ -1,8 +1,10 @@ -<img align="right" alt="Ajv logo" width="160" src="https://ajv.js.org/images/ajv_logo.png"> +<img align="right" alt="Ajv logo" width="160" src="https://ajv.js.org/img/ajv.svg"> + + # Ajv: Another JSON schema validator -The fastest JSON schema validator for Node.js and browser. +Super fast JSON schema validator for Node.js and browser. Supports JSON Schema draft-06/07/2019-09 (draft-04 is supported in [version 6](https://github.com/ajv-validator/ajv/tree/v6)) and JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/). @@ -15,14 +17,14 @@ Supports JSON Schema draft-06/07/2019-09 (draft-04 is supported in [version 6](h ## Platinum sponsors -[<img src="https://www.poberezkin.com/images/mozilla.svg" width="45%">](https://www.mozilla.org)[<img src="https://ajv.js.org/images/gap.svg" width="9%">](https://opencollective.com/ajv)[<img src="https://ajv.js.org/images/reserved.svg" width="45%">](https://opencollective.com/ajv) +[<img src="https://ajv.js.org/img/mozilla.svg" width="45%">](https://www.mozilla.org)<img src="https://ajv.js.org/img/gap.svg" width="8%">[<img src="https://ajv.js.org/img/reserved.svg" width="45%">](https://opencollective.com/ajv) ## Using version 7 Ajv version 7 has these new features: -- support of JSON Schema draft-2019-09 features: [`unevaluatedProperties`](./docs/json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./docs/json-schema.md#unevaluateditems), [dynamic recursive references](./docs/validation.md#extending-recursive-schemas) and other [additional keywords](./docs/json-schema.md#json-schema-draft-2019-09). -- NEW: support of JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) (from [v7.1.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v7.1.0)) +- NEW: support of JSON Type Definition [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) (from [v7.1.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v7.1.0)), including generation of [serializers](./docs/api.md#jtd-serialize) and [parsers](./docs/api.md#jtd-parse) from JTD schemas that are more efficient than native JSON serialization/parsing, combining JSON string parsing and validation in one function. +- support of JSON Schema draft-2019-09 features: [`unevaluatedProperties`](./docs/json-schema.md#unevaluatedproperties) and [`unevaluatedItems`](./docs/json-schema.md#unevaluateditems), [dynamic recursive references](./docs/guide/combining-schemas.md#extending-recursive-schemas) and other [additional keywords](./docs/json-schema.md#json-schema-draft-2019-09). - to reduce the mistakes in JSON schemas and unexpected validation results, [strict mode](./docs/strict-mode.md) is added - it prohibits ignored or ambiguous JSON Schema elements. - to make code injection from untrusted schemas impossible, [code generation](./docs/codegen.md) is fully re-written to be safe and to allow code optimization (compiled schema code size is reduced by more than 10%). - to simplify Ajv extensions, the new keyword API that is used by pre-defined keywords is available to user-defined keywords - it is much easier to define any keywords now, especially with subschemas. [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package was updated to use the new API (in [v4.0.0](https://github.com/ajv-validator/ajv-keywords/releases/tag/v4.0.0)) @@ -46,9 +48,25 @@ See [Getting started](#usage) for code example. ## Contributing -100+ people contributed to Ajv. You are very welcome to join by implementing new features that are valuable to many users and by improving documentation. +More than 100 people contributed to Ajv, and we would love to have you join the development. We welcome implementing new features that will benefit many users and ideas to improve our documentation. + +At Ajv, we are committed to creating more equitable and inclusive spaces for our community and team members to contribute to discussions that affect both this project and our ongoing work in the open source ecosystem. + +We strive to create an environment of respect and healthy discourse by setting standards for our interactions and we expect it from all members of our community - from long term project member to first time visitor. For more information, review our [code of conduct](./CODE_OF_CONDUCT.md) and values. + +### How we make decisions + +We value conscious curation of our library size, and balancing performance and functionality. To that end, we cannot accept every suggestion. When evaluating pull requests we consider: -Please do not be disappointed if your suggestion is not accepted - it is important to maintain the balance between the library size, performance and functionality. If it seems that a feature would benefit only a small number of users, its addition may be delayed until there is more support from the users community - so please submit the issue first to explain why this feature is important. +- Will this benefit many users or a niche use case? +- How will this impact the performance of Ajv? +- How will this expand our library size? + +To help us evaluate and understand, when you submit an issue and pull request: + +- Explain why this feature is important to the user base +- Include documentation +- Include test coverage with any new feature implementations Please include documentation and test coverage with any new feature implementations. @@ -62,7 +80,7 @@ npm test `npm run build` - compiles typescript to `dist` folder. -Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components](./docs/components.md). +Please also review [Contributing guidelines](./CONTRIBUTING.md) and [Code components](./docs/components.md). ## Contents @@ -74,14 +92,12 @@ Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components] - [Performance](#performance) - [Features](#features) - [Getting started](#usage) -- [Choosing schema language](#choosing-schema-language) - - [JSON Schema](#json-schema) - - [JSON Type Definition](#json-type-definition) +- [Choosing schema language: JSON Schema vs JSON Type Definition](./docs/guide/schema-language.md#comparison) - [Frequently Asked Questions](./docs/faq.md) -- [Using in browser](#using-in-browser) +- [Using in browser](./docs/guide/environments.md#browsers) - [Content Security Policy](./docs/security.md#content-security-policy) -- [Using in ES5 environment](#using-in-es5-environment) -- [Command line interface](#command-line-interface) +- [Using in ES5 environment](./docs/guide/environments.md#es5-environments) +- [Command line interface](./docs/guide/environments.md#command-line-interface) - [API reference](./docs/api.md) - [Methods](./docs/api.md#ajv-constructor-and-methods) - [Options](./docs/api.md#options) @@ -91,16 +107,17 @@ Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components] - [Prevent unexpected validation](./docs/strict-mode.md#prevent-unexpected-validation) - [Strict types](./docs/strict-mode.md#strict-types) - [Strict number validation](./docs/strict-mode.md#strict-number-validation) -- [Data validation](./docs/validation.md) - - [Validation basics](./docs/validation.md#validation-basics): [JSON Schema keywords](./docs/validation.md#validation-keywords), [annotations](./docs/validation.md#annotation-keywords), [formats](./docs/validation.md#formats) - - [Modular schemas](./docs/validation.md#modular-schemas): [combining with \$ref](./docs/validation.md#ref), [\$data reference](./docs/validation.md#data-reference), [$merge and $patch](./docs/validation.md#merge-and-patch-keywords) - - [Asynchronous schema compilation](./docs/validation.md#asynchronous-schema-compilation) +- [Validation guide](./docs/guide/getting-started.md) + - [Getting started](./docs/guide/getting-started.md) + - [Validating formats](./docs/guide/formats.md) + - [Modular schemas](./docs/guide/combining-schemas.md): [combining with \$ref](./docs/guide/combining-schemas#ref), [\$data reference](./docs/guide/combining-schemas.md#data-reference), [$merge and $patch](./docs/guide/combining-schemas#merge-and-patch-keywords) + - [Asynchronous schema compilation](./docs/guide/managing-schemas.md#asynchronous-schema-compilation) - [Standalone validation code](./docs/standalone.md) - - [Asynchronous validation](./docs/validation.md#asynchronous-validation) - - [Modifying data](./docs/validation.md#modifying-data-during-validation): [additional properties](./docs/validation.md#removing-additional-properties), [defaults](./docs/validation.md#assigning-defaults), [type coercion](./docs/validation.md#coercing-data-types) + - [Asynchronous validation](./docs/guide/async-validation.md) + - [Modifying data](./docs/guide/modifying-data.md): [additional properties](./docs/guide/modifying-data.md#removing-additional-properties), [defaults](./docs/guide/modifying-data.md#assigning-defaults), [type coercion](./docs/guide/modifying-data.md#coercing-data-types) - [Extending Ajv](#extending-ajv) - User-defined keywords: - - [basics](./docs/validation.md#user-defined-keywords) + - [basics](./docs/guide/user-keywords.md) - [guide](./docs/keywords.md) - [Plugins](#plugins) - [Related packages](#related-packages) @@ -117,7 +134,7 @@ Please review [Contributing guidelines](./CONTRIBUTING.md) and [Code components] ## Mozilla MOSS grant and OpenJS Foundation -[<img src="https://www.poberezkin.com/images/mozilla.png" width="240" height="68">](https://www.mozilla.org/en-US/moss/) [<img src="https://www.poberezkin.com/images/openjs.png" width="220" height="68">](https://openjsf.org/blog/2020/08/14/ajv-joins-openjs-foundation-as-an-incubation-project/) +[<img src="https://ajv.js.org/img/mozilla.svg" width="240" height="68">](https://www.mozilla.org/en-US/moss/)<img src="https://ajv.js.org/img/gap.svg" width="5%">[<img src="https://ajv.js.org/img/openjs.png" width="220" height="68">](https://openjsf.org/blog/2020/08/14/ajv-joins-openjs-foundation-as-an-incubation-project/) Ajv has been awarded a grant from Mozilla’s [Open Source Support (MOSS) program](https://www.mozilla.org/en-US/moss/) in the “Foundational Technology” track! It will sponsor the development of Ajv support of [JSON Schema version 2019-09](https://tools.ietf.org/html/draft-handrews-json-schema-02) and of [JSON Type Definition (RFC8927)](https://datatracker.ietf.org/doc/rfc8927/). @@ -185,18 +202,18 @@ Performance of different validators by [json-schema-benchmark](https://github.co - meta-schema for JTD schemas - "union" keyword and user-defined keywords (can be used inside "metadata" member of the schema) - supports [browsers](#using-in-browser) and Node.js 0.10-14.x -- [asynchronous loading](./docs/validation.md#asynchronous-schema-compilation) of referenced schemas during compilation +- [asynchronous loading](./docs/guide/managing-schemas.md#asynchronous-schema-compilation) of referenced schemas during compilation - "All errors" validation mode with [option allErrors](./docs/api.md#options) - [error messages with parameters](./docs/api.md#validation-errors) describing error reasons to allow error message generation - i18n error messages support with [ajv-i18n](https://github.com/ajv-validator/ajv-i18n) package -- [removing-additional-properties](./docs/validation.md#removing-additional-properties) -- [assigning defaults](./docs/validation.md#assigning-defaults) to missing properties and items -- [coercing data](./docs/validation.md#coercing-data-types) to the types specified in `type` keywords +- [removing-additional-properties](./docs/guide/modifying-data.md#removing-additional-properties) +- [assigning defaults](./docs/guide/modifying-data.md#assigning-defaults) to missing properties and items +- [coercing data](./docs/guide/modifying-data.md#coercing-data-types) to the types specified in `type` keywords - [user-defined keywords](#user-defined-keywords) - draft-06/07 keywords `const`, `contains`, `propertyNames` and `if/then/else` - draft-06 boolean schemas (`true`/`false` as a schema to always pass/fail). - additional extension keywords with [ajv-keywords](https://github.com/ajv-validator/ajv-keywords) package -- [\$data reference](./docs/validation.md#data-reference) to use values from the validated data as values for the schema keywords +- [\$data reference](./docs/guide/combining-schemas.md#data-reference) to use values from the validated data as values for the schema keywords - [asynchronous validation](./docs/api.md#asynchronous-validation) of user-defined formats and keywords ## Install @@ -225,215 +242,13 @@ const valid = validate(data) if (!valid) console.log(validate.errors) ``` -In TypeScript: - -```typescript -import Ajv, {JSONSchemaType, DefinedError} from "ajv" - -const ajv = new Ajv() - -type MyData = {foo: number} - -// Optional schema type annotation for schema to match MyData type. -// To use JSONSchemaType set `strictNullChecks: true` in tsconfig `compilerOptions`. -const schema: JSONSchemaType<MyData> = { - type: "object", - properties: { - foo: {type: "number", minimum: 0}, - }, - required: ["foo"], - additionalProperties: false, -} - -// validate is a type guard for MyData - type is inferred from schema type -const validate = ajv.compile(schema) - -// or, if you did not use type annotation for the schema, -// type parameter can be used to make it type guard: -// const validate = ajv.compile<MyData>(schema) - -const data: any = {foo: 1} - -if (validate(data)) { - // data is MyData here - console.log(data.foo) -} else { - // The type cast is needed to allow user-defined keywords and errors - // You can extend this type to include your error types as needed. - for (const err of validate.errors as DefinedError[]) { - switch (err.keyword) { - case "minimum": - // err type is narrowed here to have "minimum" error params properties - console.log(err.params.limit) - break - // ... - } - } -} -``` - -With JSON Type Definition schema: - -```javascript -const Ajv = require("ajv").default - -const ajv = new Ajv() // options can be passed, e.g. {allErrors: true} -const schema = { - properties: { - foo: {type: "float64"}, - }, -} -const validate = ajv.compile(schema) -const valid = validate({foo: 1}) // true -if (!valid) console.log(validate.errors) -// Unlike JSON Schema: -const valid1 = validate(1) // false, bot an object -const valid2 = validate({}) // false, foo is required -const valid3 = validate({foo: 1, bar: 2}) // false, bar is additional -``` - -See [this test](./spec/types/json-schema.spec.ts) for an advanced example, [API reference](./docs/api.md) and [Options](./docs/api.md#options) for more details. - -Ajv compiles schemas to functions and caches them in all cases (using schema itself as a key for Map) or another function passed via options), so that the next time the same schema is used (not necessarily the same object instance) it won't be compiled again. - -The best performance is achieved when using compiled functions returned by `compile` or `getSchema` methods (there is no additional function call). - -**Please note**: every time a validation function or `ajv.validate` are called `errors` property is overwritten. You need to copy `errors` array reference to another variable if you want to use it later (e.g., in the callback). See [Validation errors](./docs/api.md#validation-errors) - -## Using in browser - -See [Content Security Policy](./docs/security.md#content-security-policy) to decide the best approach how to use Ajv in the browser. - -Whether you use Ajv or compiled schemas, it is recommended that you bundle them together with your code. - -If you need to use Ajv in several bundles you can create a separate UMD bundles using `npm run bundle` script. - -Then you need to load Ajv with support of JSON Schema draft-07 in the browser: - -```html -<script src="bundle/ajv7.min.js"></script> -<script> - ;(function () { - const Ajv = window.ajv7.default - const ajv = new Ajv() - })() -</script> -``` - -To load the bundle that supports JSON Schema draft-2019-09: - -```html -<script src="bundle/ajv2019.min.js"></script> -<script> - ;(function () { - const Ajv = window.ajv2019.default - const ajv = new Ajv() - })() -</script> -``` - -To load the bundle that supports JSON Type Definition: - -```html -<script src="bundle/ajvJTD.min.js"></script> -<script> - ;(function () { - const Ajv = window.ajvJTD.default - const ajv = new Ajv() - })() -</script> -``` - -This bundle can be used with different module systems; it creates global `ajv` (or `ajv2019`) if no module system is found. - -The browser bundle is available on [cdnjs](https://cdnjs.com/libraries/ajv). - -**Please note**: some frameworks, e.g. Dojo, may redefine global require in a way that is not compatible with CommonJS module format. In this case Ajv bundle has to be loaded before the framework and then you can use global `ajv` (see issue [#234](https://github.com/ajv-validator/ajv/issues/234)). - -## Choosing schema language - -Both JSON Schema and JSON Type Definition are cross-platform specifications with implementations in multiple programming languages that help you define the shape and requirements to your JSON data. - -This section compares their pros/cons to help decide which specification fits your application better. - -### JSON Schema - -- Pros - - Wide specification adoption. - - Used as part of OpenAPI specification. - - Support of complex validation scenarios: - - untagged unions and boolean logic - - conditional schemas and dependencies - - restrictions on the number ranges and the size of strings, arrays and objects - - semantic validation with formats, patterns and content keywords - - distribute strict record definitions across multiple schemas (with unevaluatedProperties) - - Can be effectively used for validation of any JavaScript objects and configuration files. -- Cons - - Defines the collection of restrictions on the data, rather than the shape of the data. - - No standard support for tagged unions. - - Complex and error prone for the new users (Ajv has [strict mode](./docs/strict-mode) to compensate for it, but it is not cross-platform). - - Some parts of specification are difficult to implement, creating the risk of implementations divergence: - - reference resolution model - - unevaluatedProperties/unevaluatedItems - - dynamic recursive references - - Internet draft status (rather than RFC) - -See [JSON Schema](./docs/json-schema.md) for the list of defined keywords. - -### JSON Type Definition - -- Pros: - - Aligned with type systems of many languages - can be used to generate type definitions and efficient parsers and serializers to/from these types. - - Very simple, enforcing the best practices for cross-platform JSON API modelling. - - Simple to implement, ensuring consistency across implementations. - - Defines the shape of JSON data via strictly defined schema forms (rather than the collection of restrictions). - - Effective support for tagged unions. - - Designed to protect against user mistakes. - - Approved as [RFC8927](https://datatracker.ietf.org/doc/rfc8927/) -- Cons: - - Limited, compared with JSON Schema - no support for untagged unions<sup>\*</sup>, conditionals, references between different schema files<sup>\*\*</sup>, etc. - - No meta-schema in the specification<sup>\*</sup>. - - Brand new - limited industry adoption (as of January 2021). - -<sup>\*</sup> Ajv defines meta-schema for JTD schemas and non-standard keyword "union" that can be used inside "metadata" object. - -<sup>\*\*</sup> You can still combine schemas from multiple files in the application code. - -See [JSON Type Definition](./docs/json-type-definition.md) for the list of defined schema forms. - -## Using in ES5 environment - -You need to: - -- recompile Typescript to ES5 target - it is set to 2018 in the bundled compiled code. -- generate ES5 validation code: - -```javascript -const ajv = new Ajv({code: {es5: true}}) -``` - -See [Advanced options](https://github.com/ajv-validator/ajv/blob/master/docs/api.md#advanced-options). - -## Command line interface - -CLI is available as a separate npm package [ajv-cli](https://github.com/ajv-validator/ajv-cli). It supports: - -- compiling JSON Schemas to test their validity -- generating [standalone validation code](./docs/standalone.md) that exports validation function(s) to be used without Ajv -- migrating schemas to draft-07 and draft-2019-09 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate)) -- validating data file(s) against JSON Schema -- testing expected validity of data against JSON Schema -- referenced schemas -- user-defined meta-schemas, validation keywords and formats -- files in JSON, JSON5, YAML, and JavaScript format -- all Ajv options -- reporting changes in data after validation in [JSON-patch](https://datatracker.ietf.org/doc/rfc6902/) format +See more examples in [Guide: getting started](./docs/guide/getting-started) ## Extending Ajv ### User defined keywords -See section in [data validation](./docs/validation.md#user-defined-keywords) and the [detailed guide](./docs/keywords.md). +See section in [data validation](./docs/guide/user-keywords.md) and the [detailed guide](./docs/keywords.md). ### Plugins @@ -486,7 +301,7 @@ If you have published a useful plugin please submit a PR to add it to the next s ## Changes history -See https://github.com/ajv-validator/ajv/releases +See [https://github.com/ajv-validator/ajv/releases](https://github.com/ajv-validator/ajv/releases) **Please note**: [Changes in version 7.0.0](https://github.com/ajv-validator/ajv/releases/tag/v7.0.0) diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/codegen/index.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/codegen/index.js index 6695ba041c45d6..d2ff4643de6330 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/codegen/index.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/codegen/index.js @@ -26,6 +26,7 @@ exports.operators = { NOT: new code_1._Code("!"), OR: new code_1._Code("||"), AND: new code_1._Code("&&"), + ADD: new code_1._Code("+"), }; class Node { optimizeNodes() { @@ -79,6 +80,15 @@ class Assign extends Node { return addExprNames(names, this.rhs); } } +class AssignOp extends Assign { + constructor(lhs, op, rhs, sideEffects) { + super(lhs, rhs, sideEffects); + this.op = op; + } + render({ _n }) { + return `${this.lhs} ${this.op}= ${this.rhs};` + _n; + } +} class Label extends Node { constructor(label) { super(); @@ -418,6 +428,10 @@ class CodeGen { assign(lhs, rhs, sideEffects) { return this._leafNode(new Assign(lhs, rhs, sideEffects)); } + // `+=` code + add(lhs, rhs) { + return this._leafNode(new AssignOp(lhs, exports.operators.ADD, rhs)); + } // appends passed SafeExpr to code or executes Block code(c) { if (typeof c == "function") diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/index.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/index.js index 05a7a27df28242..1e3d4d967ebc43 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/index.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/index.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.resolveSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0; +exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0; const codegen_1 = require("./codegen"); const error_classes_1 = require("./error_classes"); const names_1 = require("./names"); @@ -57,6 +57,7 @@ function compileSchema(sch) { dataPathArr: [codegen_1.nil], dataLevel: 0, dataTypes: [], + definedProperties: new Set(), topSchemaRef: gen.scopeValue("schema", this.opts.code.source === true ? { ref: sch.schema, code: codegen_1.stringify(sch.schema) } : { ref: sch.schema }), @@ -151,6 +152,7 @@ function getCompilingSchema(schEnv) { return sch; } } +exports.getCompilingSchema = getCompilingSchema; function sameSchemaEnv(s1, s2) { return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId; } diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/parse.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/parse.js new file mode 100644 index 00000000000000..f1427d66ddda12 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/parse.js @@ -0,0 +1,341 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types_1 = require("./types"); +const __1 = require(".."); +const codegen_1 = require("../codegen"); +const error_classes_1 = require("../error_classes"); +const names_1 = require("../names"); +const code_1 = require("../../vocabularies/code"); +const ref_1 = require("../../vocabularies/jtd/ref"); +const type_1 = require("../../vocabularies/jtd/type"); +const parseJson_1 = require("../../runtime/parseJson"); +const util_1 = require("../util"); +const timestamp_1 = require("../timestamp"); +const genParse = { + elements: parseElements, + values: parseValues, + discriminator: parseDiscriminator, + properties: parseProperties, + optionalProperties: parseProperties, + enum: parseEnum, + type: parseType, + ref: parseRef, +}; +function compileParser(sch, definitions) { + const _sch = __1.getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + const parseName = gen.scopeName("parse"); + const cxt = { + self: this, + gen, + schema: sch.schema, + schemaEnv: sch, + definitions, + data: names_1.default.data, + parseName, + char: gen.name("c"), + }; + let sourceCode; + try { + this._compilations.add(sch); + sch.parseName = parseName; + parserFunction(cxt); + gen.optimize(this.opts.code.optimize); + const parseFuncCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${parseFuncCode}`; + const makeParse = new Function(`${names_1.default.scope}`, sourceCode); + const parse = makeParse(this.scope.get()); + this.scope.value(parseName, { ref: parse }); + sch.parse = parse; + } + catch (e) { + if (sourceCode) + this.logger.error("Error compiling parser, function code:", sourceCode); + delete sch.parse; + delete sch.parseName; + throw e; + } + finally { + this._compilations.delete(sch); + } + return sch; +} +exports.default = compileParser; +const undef = codegen_1._ `undefined`; +function parserFunction(cxt) { + const { gen, parseName, char } = cxt; + gen.func(parseName, codegen_1._ `${names_1.default.json}, ${names_1.default.jsonPos}, ${names_1.default.jsonPart}`, false, () => { + gen.let(names_1.default.data); + gen.let(char); + gen.assign(codegen_1._ `${parseName}.message`, undef); + gen.assign(codegen_1._ `${parseName}.position`, undef); + gen.assign(names_1.default.jsonPos, codegen_1._ `${names_1.default.jsonPos} || 0`); + gen.const(names_1.default.jsonLen, codegen_1._ `${names_1.default.json}.length`); + parseCode(cxt); + skipWhitespace(cxt); + gen.if(names_1.default.jsonPart, () => { + gen.assign(codegen_1._ `${parseName}.position`, names_1.default.jsonPos); + gen.return(names_1.default.data); + }); + gen.if(codegen_1._ `${names_1.default.jsonPos} === ${names_1.default.jsonLen}`, () => gen.return(names_1.default.data)); + jsonSyntaxError(cxt); + }); +} +function parseCode(cxt) { + let form; + for (const key of types_1.jtdForms) { + if (key in cxt.schema) { + form = key; + break; + } + } + if (form) + parseNullable(cxt, genParse[form]); + else + parseEmpty(cxt); +} +const parseBoolean = parseBooleanToken(true, parseBooleanToken(false, jsonSyntaxError)); +function parseNullable(cxt, parseForm) { + const { gen, schema, data } = cxt; + if (!schema.nullable) + return parseForm(cxt); + tryParseToken(cxt, "null", parseForm, () => gen.assign(data, null)); +} +function parseElements(cxt) { + const { gen, schema, data } = cxt; + parseToken(cxt, "["); + const ix = gen.let("i", 0); + gen.assign(data, codegen_1._ `[]`); + parseItems(cxt, "]", () => { + const el = gen.let("el"); + parseCode({ ...cxt, schema: schema.elements, data: el }); + gen.assign(codegen_1._ `${data}[${ix}++]`, el); + }); +} +function parseValues(cxt) { + const { gen, schema, data } = cxt; + parseToken(cxt, "{"); + gen.assign(data, codegen_1._ `{}`); + parseItems(cxt, "}", () => parseKeyValue(cxt, schema.values)); +} +function parseItems(cxt, endToken, block) { + tryParseItems(cxt, endToken, block); + parseToken(cxt, endToken); +} +function tryParseItems(cxt, endToken, block) { + const { gen } = cxt; + gen.for(codegen_1._ `;${names_1.default.jsonPos}<${names_1.default.jsonLen} && ${jsonSlice(1)}!==${endToken};`, () => { + block(); + tryParseToken(cxt, ",", () => gen.break(), hasItem); + }); + function hasItem() { + tryParseToken(cxt, endToken, () => { }, jsonSyntaxError); + } +} +function parseKeyValue(cxt, schema) { + const { gen } = cxt; + const key = gen.let("key"); + parseString({ ...cxt, data: key }); + parseToken(cxt, ":"); + parsePropertyValue(cxt, key, schema); +} +function parseDiscriminator(cxt) { + const { gen, data, schema } = cxt; + const { discriminator, mapping } = schema; + parseToken(cxt, "{"); + gen.assign(data, codegen_1._ `{}`); + const startPos = gen.const("pos", names_1.default.jsonPos); + const value = gen.let("value"); + const tag = gen.let("tag"); + tryParseItems(cxt, "}", () => { + const key = gen.let("key"); + parseString({ ...cxt, data: key }); + parseToken(cxt, ":"); + gen.if(codegen_1._ `${key} === ${discriminator}`, () => { + parseString({ ...cxt, data: tag }); + gen.assign(codegen_1._ `${data}[${key}]`, tag); + gen.break(); + }, () => parseEmpty({ ...cxt, data: value }) // can be discarded/skipped + ); + }); + gen.assign(names_1.default.jsonPos, startPos); + gen.if(codegen_1._ `${tag} === undefined`); + parsingError(cxt, codegen_1.str `discriminator tag not found`); + for (const tagValue in mapping) { + gen.elseIf(codegen_1._ `${tag} === ${tagValue}`); + parseSchemaProperties({ ...cxt, schema: mapping[tagValue] }, discriminator); + } + gen.else(); + parsingError(cxt, codegen_1.str `discriminator value not in schema`); + gen.endIf(); +} +function parseProperties(cxt) { + const { gen, data } = cxt; + parseToken(cxt, "{"); + gen.assign(data, codegen_1._ `{}`); + parseSchemaProperties(cxt); +} +function parseSchemaProperties(cxt, discriminator) { + const { gen, schema, data } = cxt; + const { properties, optionalProperties, additionalProperties } = schema; + parseItems(cxt, "}", () => { + const key = gen.let("key"); + parseString({ ...cxt, data: key }); + parseToken(cxt, ":"); + gen.if(false); + parseDefinedProperty(cxt, key, properties); + parseDefinedProperty(cxt, key, optionalProperties); + if (discriminator) { + gen.elseIf(codegen_1._ `${key} === ${discriminator}`); + const tag = gen.let("tag"); + parseString({ ...cxt, data: tag }); // can be discarded, it is already assigned + } + gen.else(); + if (additionalProperties) { + parseEmpty({ ...cxt, data: codegen_1._ `${data}[${key}]` }); + } + else { + parsingError(cxt, codegen_1.str `property ${key} not allowed`); + } + gen.endIf(); + }); + if (properties) { + const hasProp = code_1.hasPropFunc(gen); + const allProps = codegen_1.and(...Object.keys(properties).map((p) => codegen_1._ `${hasProp}.call(${data}, ${p})`)); + gen.if(codegen_1.not(allProps), () => parsingError(cxt, codegen_1.str `missing required properties`)); + } +} +function parseDefinedProperty(cxt, key, schemas = {}) { + const { gen } = cxt; + for (const prop in schemas) { + gen.elseIf(codegen_1._ `${key} === ${prop}`); + parsePropertyValue(cxt, key, schemas[prop]); + } +} +function parsePropertyValue(cxt, key, schema) { + parseCode({ ...cxt, schema, data: codegen_1._ `${cxt.data}[${key}]` }); +} +function parseType(cxt) { + const { gen, schema, data } = cxt; + switch (schema.type) { + case "boolean": + parseBoolean(cxt); + break; + case "string": + parseString(cxt); + break; + case "timestamp": { + // TODO parse timestamp? + parseString(cxt); + const vts = util_1.func(gen, timestamp_1.default); + gen.if(codegen_1._ `!${vts}(${data})`, () => parsingError(cxt, codegen_1.str `invalid timestamp`)); + break; + } + case "float32": + case "float64": + parseNumber(cxt); + break; + default: { + const [min, max, maxDigits] = type_1.intRange[schema.type]; + parseNumber(cxt, maxDigits); + gen.if(codegen_1._ `${data} < ${min} || ${data} > ${max}`, () => parsingError(cxt, codegen_1.str `integer out of range`)); + } + } +} +function parseString(cxt) { + parseToken(cxt, '"'); + parseWith(cxt, parseJson_1.parseJsonString); +} +function parseEnum(cxt) { + const { gen, data, schema } = cxt; + const enumSch = schema.enum; + parseToken(cxt, '"'); + // TODO loopEnum + gen.if(false); + for (const value of enumSch) { + const valueStr = JSON.stringify(value).slice(1); // remove starting quote + gen.elseIf(codegen_1._ `${jsonSlice(valueStr.length)} === ${valueStr}`); + gen.assign(data, codegen_1.str `${value}`); + gen.add(names_1.default.jsonPos, valueStr.length); + } + gen.else(); + jsonSyntaxError(cxt); + gen.endIf(); +} +function parseNumber(cxt, maxDigits) { + const { gen } = cxt; + skipWhitespace(cxt); + gen.if(codegen_1._ `"-0123456789".indexOf(${jsonSlice(1)}) < 0`, () => jsonSyntaxError(cxt), () => parseWith(cxt, parseJson_1.parseJsonNumber, maxDigits)); +} +function parseBooleanToken(bool, fail) { + return (cxt) => { + const { gen, data } = cxt; + tryParseToken(cxt, `${bool}`, () => fail(cxt), () => gen.assign(data, bool)); + }; +} +function parseRef(cxt) { + const { gen, self, definitions, schema, schemaEnv } = cxt; + const { ref } = schema; + const refSchema = definitions[ref]; + if (!refSchema) + throw new error_classes_1.MissingRefError("", ref, `No definition ${ref}`); + if (!ref_1.hasRef(refSchema)) + return parseCode({ ...cxt, schema: refSchema }); + const { root } = schemaEnv; + const sch = compileParser.call(self, new __1.SchemaEnv({ schema: refSchema, root }), definitions); + partialParse(cxt, getParser(gen, sch), true); +} +function getParser(gen, sch) { + return sch.parse + ? gen.scopeValue("parse", { ref: sch.parse }) + : codegen_1._ `${gen.scopeValue("wrapper", { ref: sch })}.parse`; +} +function parseEmpty(cxt) { + parseWith(cxt, parseJson_1.parseJson); +} +function parseWith(cxt, parseFunc, args) { + const f = cxt.gen.scopeValue("func", { + ref: parseFunc, + code: parseFunc.code, + }); + partialParse(cxt, f, args); +} +function partialParse(cxt, parseFunc, args) { + const { gen, data } = cxt; + gen.assign(data, codegen_1._ `${parseFunc}(${names_1.default.json}, ${names_1.default.jsonPos}${args ? codegen_1._ `, ${args}` : codegen_1.nil})`); + gen.assign(names_1.default.jsonPos, codegen_1._ `${parseFunc}.position`); + gen.if(codegen_1._ `${data} === undefined`, () => parsingError(cxt, codegen_1._ `${parseFunc}.message`)); +} +function parseToken(cxt, tok) { + tryParseToken(cxt, tok, jsonSyntaxError); +} +function tryParseToken(cxt, tok, fail, success) { + const { gen } = cxt; + const n = tok.length; + skipWhitespace(cxt); + gen.if(codegen_1._ `${jsonSlice(n)} === ${tok}`, () => { + gen.add(names_1.default.jsonPos, n); + success === null || success === void 0 ? void 0 : success(cxt); + }, () => fail(cxt)); +} +function skipWhitespace({ gen, char: c }) { + gen.code(codegen_1._ `while((${c}=${names_1.default.json}[${names_1.default.jsonPos}],${c}===" "||${c}==="\\n"||${c}==="\\r"||${c}==="\\t"))${names_1.default.jsonPos}++;`); +} +function jsonSlice(len) { + return len === 1 + ? codegen_1._ `${names_1.default.json}[${names_1.default.jsonPos}]` + : codegen_1._ `${names_1.default.json}.slice(${names_1.default.jsonPos}, ${names_1.default.jsonPos}+${len})`; +} +function jsonSyntaxError(cxt) { + parsingError(cxt, codegen_1._ `"unexpected token " + ${names_1.default.json}[${names_1.default.jsonPos}]`); +} +function parsingError({ gen, parseName }, msg) { + gen.assign(codegen_1._ `${parseName}.message`, msg); + gen.assign(codegen_1._ `${parseName}.position`, names_1.default.jsonPos); + gen.return(undef); +} +//# sourceMappingURL=parse.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/serialize.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/serialize.js new file mode 100644 index 00000000000000..26148fef47c6f1 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/serialize.js @@ -0,0 +1,223 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const types_1 = require("./types"); +const __1 = require(".."); +const codegen_1 = require("../codegen"); +const error_classes_1 = require("../error_classes"); +const names_1 = require("../names"); +const code_1 = require("../../vocabularies/code"); +const ref_1 = require("../../vocabularies/jtd/ref"); +const quote_1 = require("../../runtime/quote"); +const genSerialize = { + elements: serializeElements, + values: serializeValues, + discriminator: serializeDiscriminator, + properties: serializeProperties, + optionalProperties: serializeProperties, + enum: serializeString, + type: serializeType, + ref: serializeRef, +}; +function compileSerializer(sch, definitions) { + const _sch = __1.getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + const serializeName = gen.scopeName("serialize"); + const cxt = { + self: this, + gen, + schema: sch.schema, + schemaEnv: sch, + definitions, + data: names_1.default.data, + }; + let sourceCode; + try { + this._compilations.add(sch); + sch.serializeName = serializeName; + gen.func(serializeName, names_1.default.data, false, () => { + gen.let(names_1.default.json, codegen_1.str ``); + serializeCode(cxt); + gen.return(names_1.default.json); + }); + gen.optimize(this.opts.code.optimize); + const serializeFuncCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${serializeFuncCode}`; + const makeSerialize = new Function(`${names_1.default.scope}`, sourceCode); + const serialize = makeSerialize(this.scope.get()); + this.scope.value(serializeName, { ref: serialize }); + sch.serialize = serialize; + } + catch (e) { + if (sourceCode) + this.logger.error("Error compiling serializer, function code:", sourceCode); + delete sch.serialize; + delete sch.serializeName; + throw e; + } + finally { + this._compilations.delete(sch); + } + return sch; +} +exports.default = compileSerializer; +function serializeCode(cxt) { + let form; + for (const key of types_1.jtdForms) { + if (key in cxt.schema) { + form = key; + break; + } + } + serializeNullable(cxt, form ? genSerialize[form] : serializeEmpty); +} +function serializeNullable(cxt, serializeForm) { + const { gen, schema, data } = cxt; + if (!schema.nullable) + return serializeForm(cxt); + gen.if(codegen_1._ `${data} === undefined || ${data} === null`, () => gen.add(names_1.default.json, codegen_1._ `"null"`), () => serializeForm(cxt)); +} +function serializeElements(cxt) { + const { gen, schema, data } = cxt; + gen.add(names_1.default.json, codegen_1.str `[`); + const first = gen.let("first", true); + gen.forOf("el", data, (el) => { + addComma(cxt, first); + serializeCode({ ...cxt, schema: schema.elements, data: el }); + }); + gen.add(names_1.default.json, codegen_1.str `]`); +} +function serializeValues(cxt) { + const { gen, schema, data } = cxt; + gen.add(names_1.default.json, codegen_1.str `{`); + const first = gen.let("first", true); + gen.forIn("key", data, (key) => serializeKeyValue(cxt, key, schema.values, first)); + gen.add(names_1.default.json, codegen_1.str `}`); +} +function serializeKeyValue(cxt, key, schema, first) { + const { gen, data } = cxt; + addComma(cxt, first); + serializeString({ ...cxt, data: key }); + gen.add(names_1.default.json, codegen_1.str `:`); + const value = gen.const("value", codegen_1._ `${data}${codegen_1.getProperty(key)}`); + serializeCode({ ...cxt, schema, data: value }); +} +function serializeDiscriminator(cxt) { + const { gen, schema, data } = cxt; + const { discriminator } = schema; + gen.add(names_1.default.json, codegen_1.str `{${JSON.stringify(discriminator)}:`); + const tag = gen.const("tag", codegen_1._ `${data}${codegen_1.getProperty(discriminator)}`); + serializeString({ ...cxt, data: tag }); + gen.if(false); + for (const tagValue in schema.mapping) { + gen.elseIf(codegen_1._ `${tag} === ${tagValue}`); + const sch = schema.mapping[tagValue]; + serializeSchemaProperties({ ...cxt, schema: sch }, discriminator); + } + gen.endIf(); + gen.add(names_1.default.json, codegen_1.str `}`); +} +function serializeProperties(cxt) { + const { gen } = cxt; + gen.add(names_1.default.json, codegen_1.str `{`); + serializeSchemaProperties(cxt); + gen.add(names_1.default.json, codegen_1.str `}`); +} +function serializeSchemaProperties(cxt, discriminator) { + const { gen, schema, data } = cxt; + const { properties, optionalProperties } = schema; + const props = keys(properties); + const optProps = keys(optionalProperties); + const allProps = allProperties(props.concat(optProps)); + let first = !discriminator; + for (const key of props) { + serializeProperty(key, properties[key], keyValue(key)); + } + for (const key of optProps) { + const value = keyValue(key); + gen.if(codegen_1.and(codegen_1._ `${value} !== undefined`, code_1.isOwnProperty(gen, data, key)), () => serializeProperty(key, optionalProperties[key], value)); + } + if (schema.additionalProperties) { + gen.forIn("key", data, (key) => gen.if(isAdditional(key, allProps), () => serializeKeyValue(cxt, key, {}, gen.let("first", first)))); + } + function keys(ps) { + return ps ? Object.keys(ps) : []; + } + function allProperties(ps) { + if (discriminator) + ps.push(discriminator); + if (new Set(ps).size !== ps.length) { + throw new Error("JTD: properties/optionalProperties/disciminator overlap"); + } + return ps; + } + function keyValue(key) { + return gen.const("value", codegen_1._ `${data}${codegen_1.getProperty(key)}`); + } + function serializeProperty(key, propSchema, value) { + if (first) + first = false; + else + gen.add(names_1.default.json, codegen_1.str `,`); + gen.add(names_1.default.json, codegen_1.str `${JSON.stringify(key)}:`); + serializeCode({ ...cxt, schema: propSchema, data: value }); + } + function isAdditional(key, ps) { + return ps.length ? codegen_1.and(...ps.map((p) => codegen_1._ `${key} !== ${p}`)) : true; + } +} +function serializeType(cxt) { + const { gen, schema, data } = cxt; + switch (schema.type) { + case "boolean": + gen.add(names_1.default.json, codegen_1._ `${data} ? "true" : "false"`); + break; + case "string": + serializeString(cxt); + break; + case "timestamp": + gen.if(codegen_1._ `${data} instanceof Date`, () => gen.add(names_1.default.json, codegen_1._ `${data}.toISOString()`), () => serializeString(cxt)); + break; + default: + serializeNumber(cxt); + } +} +function serializeString({ gen, data }) { + gen.add(names_1.default.json, codegen_1._ `${quoteFunc(gen)}(${data})`); +} +function serializeNumber({ gen, data }) { + gen.add(names_1.default.json, codegen_1._ `"" + ${data}`); +} +function serializeRef(cxt) { + const { gen, self, data, definitions, schema, schemaEnv } = cxt; + const { ref } = schema; + const refSchema = definitions[ref]; + if (!refSchema) + throw new error_classes_1.MissingRefError("", ref, `No definition ${ref}`); + if (!ref_1.hasRef(refSchema)) + return serializeCode({ ...cxt, schema: refSchema }); + const { root } = schemaEnv; + const sch = compileSerializer.call(self, new __1.SchemaEnv({ schema: refSchema, root }), definitions); + gen.add(names_1.default.json, codegen_1._ `${getSerialize(gen, sch)}(${data})`); +} +function getSerialize(gen, sch) { + return sch.serialize + ? gen.scopeValue("serialize", { ref: sch.serialize }) + : codegen_1._ `${gen.scopeValue("wrapper", { ref: sch })}.serialize`; +} +function serializeEmpty({ gen, data }) { + gen.add(names_1.default.json, codegen_1._ `JSON.stringify(${data})`); +} +function addComma({ gen }, first) { + gen.if(first, () => gen.assign(first, false), () => gen.add(names_1.default.json, codegen_1.str `,`)); +} +function quoteFunc(gen) { + return gen.scopeValue("func", { + ref: quote_1.default, + code: codegen_1._ `require("ajv/dist/runtime/quote").default`, + }); +} +//# sourceMappingURL=serialize.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/types.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/types.js new file mode 100644 index 00000000000000..b9c60a90fdd7ff --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/jtd/types.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.jtdForms = void 0; +exports.jtdForms = [ + "elements", + "values", + "discriminator", + "properties", + "optionalProperties", + "enum", + "type", + "ref", +]; +//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/names.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/names.js index d40037194ae3d9..02e80b8e35d348 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/names.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/names.js @@ -18,6 +18,11 @@ const names = { // "globals" self: new codegen_1.Name("self"), scope: new codegen_1.Name("scope"), + // JTD serialize/parse name for JSON string and position + json: new codegen_1.Name("json"), + jsonPos: new codegen_1.Name("jsonPos"), + jsonLen: new codegen_1.Name("jsonLen"), + jsonPart: new codegen_1.Name("jsonPart"), }; exports.default = names; //# sourceMappingURL=names.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/subschema.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/subschema.js index 1d6de6358606b8..30044843893193 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/subschema.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/subschema.js @@ -75,6 +75,7 @@ function extendSubschemaData(subschema, it, { dataProp, dataPropType: dpType, da subschema.data = _nextData; subschema.dataLevel = it.dataLevel + 1; subschema.dataTypes = []; + it.definedProperties = new Set(); subschema.parentData = it.data; subschema.dataNames = [...it.dataNames, _nextData]; } diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/timestamp.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/timestamp.js index fcb61fe51c793b..f280d4fdd5be86 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/timestamp.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/timestamp.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +const codegen_1 = require("./codegen"); const DATE_TIME = /^(\d\d\d\d)-(\d\d)-(\d\d)(?:t|\s)(\d\d):(\d\d):(\d\d)(?:\.\d+)?(?:z|([+-]\d\d)(?::?(\d\d))?)$/i; const DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; function validTimestamp(str) { @@ -26,4 +27,5 @@ function validTimestamp(str) { (hr - tzH === 23 && min - tzM === 59 && sec === 60))); } exports.default = validTimestamp; +validTimestamp.code = codegen_1._ `require("ajv/dist/compile/timestamp").default`; //# sourceMappingURL=timestamp.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/util.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/util.js index e0c90c7e1475ab..40ac8fa2037890 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/util.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/util.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = void 0; +exports.func = exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = void 0; const codegen_1 = require("./codegen"); const validate_1 = require("./validate"); // TODO refactor to use Set @@ -138,4 +138,11 @@ function setEvaluated(gen, props, ps) { Object.keys(ps).forEach((p) => gen.assign(codegen_1._ `${props}${codegen_1.getProperty(p)}`, true)); } exports.setEvaluated = setEvaluated; +function func(gen, f) { + return gen.scopeValue("func", { + ref: f, + code: f.code, + }); +} +exports.func = func; //# sourceMappingURL=util.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/validate/applicability.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/validate/applicability.js index 7a3ebde278aecc..6187dbbeeebb3f 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/validate/applicability.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/compile/validate/applicability.js @@ -12,7 +12,8 @@ function shouldUseGroup(schema, group) { exports.shouldUseGroup = shouldUseGroup; function shouldUseRule(schema, rule) { var _a; - return (schema[rule.keyword] !== undefined || ((_a = rule.definition.implements) === null || _a === void 0 ? void 0 : _a.some((kwd) => schema[kwd] !== undefined))); + return (schema[rule.keyword] !== undefined || + ((_a = rule.definition.implements) === null || _a === void 0 ? void 0 : _a.some((kwd) => schema[kwd] !== undefined))); } exports.shouldUseRule = shouldUseRule; //# sourceMappingURL=applicability.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/core.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/core.js index 8f868c6771ddb0..d11577134fc51c 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/core.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/core.js @@ -21,6 +21,8 @@ const $dataRefSchema = require("./refs/data.json"); const META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"]; const EXT_SCOPE_NAMES = new Set([ "validate", + "serialize", + "parse", "wrapper", "root", "schema", diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/jtd.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/jtd.js index 6d7924b97ae931..32f2581b1f01e0 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/jtd.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/jtd.js @@ -14,6 +14,8 @@ Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function () { const core_1 = require("./core"); const jtd_1 = require("./vocabularies/jtd"); const jtd_schema_1 = require("./refs/jtd-schema"); +const serialize_1 = require("./compile/jtd/serialize"); +const parse_1 = require("./compile/jtd/parse"); // const META_SUPPORT_DATA = ["/properties"] const META_SCHEMA_ID = "JTD-meta-schema"; class Ajv extends core_1.default { @@ -39,6 +41,28 @@ class Ajv extends core_1.default { return (this.opts.defaultMeta = super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : undefined)); } + compileSerializer(schema) { + const sch = this._addSchema(schema); + return sch.serialize || this._compileSerializer(sch); + } + compileParser(schema) { + const sch = this._addSchema(schema); + return (sch.parse || this._compileParser(sch)); + } + _compileSerializer(sch) { + serialize_1.default.call(this, sch, sch.schema.definitions || {}); + /* istanbul ignore if */ + if (!sch.serialize) + throw new Error("ajv implementation error"); + return sch.serialize; + } + _compileParser(sch) { + parse_1.default.call(this, sch, sch.schema.definitions || {}); + /* istanbul ignore if */ + if (!sch.parse) + throw new Error("ajv implementation error"); + return sch.parse; + } } exports.default = Ajv; //# sourceMappingURL=jtd.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/parseJson.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/parseJson.js new file mode 100644 index 00000000000000..cd4b8c1c9127ea --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/parseJson.js @@ -0,0 +1,183 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseJsonString = exports.parseJsonNumber = exports.parseJson = void 0; +const codegen_1 = require("../compile/codegen"); +const rxParseJson = /position\s(\d+)$/; +function parseJson(s, pos) { + let endPos; + parseJson.message = undefined; + let matches; + if (pos) + s = s.slice(pos); + try { + parseJson.position = pos + s.length; + return JSON.parse(s); + } + catch (e) { + matches = rxParseJson.exec(e.message); + if (!matches) { + parseJson.message = "unexpected end"; + return undefined; + } + endPos = +matches[1]; + const c = s[endPos]; + s = s.slice(0, endPos); + parseJson.position = pos + endPos; + try { + return JSON.parse(s); + } + catch (e1) { + parseJson.message = `unexpected token ${c}`; + return undefined; + } + } +} +exports.parseJson = parseJson; +parseJson.message = undefined; +parseJson.position = 0; +parseJson.code = codegen_1._ `require("ajv/dist/runtime/parseJson").parseJson`; +function parseJsonNumber(s, pos, maxDigits) { + let numStr = ""; + let c; + parseJsonNumber.message = undefined; + if (s[pos] === "-") { + numStr += "-"; + pos++; + } + if (s[pos] === "0") { + numStr += "0"; + pos++; + } + else { + if (!parseDigits(maxDigits)) { + errorMessage(); + return undefined; + } + } + if (maxDigits) { + parseJsonNumber.position = pos; + return +numStr; + } + if (s[pos] === ".") { + numStr += "."; + pos++; + if (!parseDigits()) { + errorMessage(); + return undefined; + } + } + if (((c = s[pos]), c === "e" || c === "E")) { + numStr += "e"; + pos++; + if (((c = s[pos]), c === "+" || c === "-")) { + numStr += c; + pos++; + } + if (!parseDigits()) { + errorMessage(); + return undefined; + } + } + parseJsonNumber.position = pos; + return +numStr; + function parseDigits(maxLen) { + let digit = false; + while (((c = s[pos]), c >= "0" && c <= "9" && (maxLen === undefined || maxLen-- > 0))) { + digit = true; + numStr += c; + pos++; + } + return digit; + } + function errorMessage() { + parseJsonNumber.position = pos; + parseJsonNumber.message = pos < s.length ? `unexpected token ${s[pos]}` : "unexpected end"; + } +} +exports.parseJsonNumber = parseJsonNumber; +parseJsonNumber.message = undefined; +parseJsonNumber.position = 0; +parseJsonNumber.code = codegen_1._ `require("ajv/dist/runtime/parseJson").parseJsonNumber`; +const escapedChars = { + b: "\b", + f: "\f", + n: "\n", + r: "\r", + t: "\t", + '"': '"', + "/": "/", + "\\": "\\", +}; +const CODE_A = "a".charCodeAt(0); +const CODE_0 = "0".charCodeAt(0); +function parseJsonString(s, pos) { + let str = ""; + let c; + parseJsonString.message = undefined; + // eslint-disable-next-line no-constant-condition, @typescript-eslint/no-unnecessary-condition + while (true) { + c = s[pos++]; + if (c === '"') + break; + if (c === "\\") { + c = s[pos]; + if (c in escapedChars) { + str += escapedChars[c]; + pos++; + } + else if (c === "u") { + pos++; + let count = 4; + let code = 0; + while (count--) { + code <<= 4; + c = s[pos].toLowerCase(); + if (c >= "a" && c <= "f") { + code += c.charCodeAt(0) - CODE_A + 10; + } + else if (c >= "0" && c <= "9") { + code += c.charCodeAt(0) - CODE_0; + } + else if (c === undefined) { + errorMessage("unexpected end"); + return undefined; + } + else { + errorMessage(`unexpected token ${c}`); + return undefined; + } + pos++; + } + str += String.fromCharCode(code); + } + else { + errorMessage(`unexpected token ${c}`); + return undefined; + } + } + else if (c === undefined) { + errorMessage("unexpected end"); + return undefined; + } + else { + if (c.charCodeAt(0) >= 0x20) { + str += c; + } + else { + errorMessage(`unexpected token ${c}`); + return undefined; + } + } + } + parseJsonString.position = pos; + return str; + function errorMessage(msg) { + parseJsonString.position = pos; + parseJsonString.message = msg; + } +} +exports.parseJsonString = parseJsonString; +parseJsonString.message = undefined; +parseJsonString.position = 0; +parseJsonString.code = codegen_1._ `require("ajv/dist/runtime/parseJson").parseJsonString`; +//# sourceMappingURL=parseJson.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/quote.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/quote.js new file mode 100644 index 00000000000000..361280542bf95b --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/runtime/quote.js @@ -0,0 +1,28 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// eslint-disable-next-line no-control-regex, no-misleading-character-class +const rxEscapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; +const escaped = { + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + '"': '\\"', + "\\": "\\\\", +}; +function quote(s) { + rxEscapable.lastIndex = 0; + return ('"' + + (rxEscapable.test(s) + ? s.replace(rxEscapable, (a) => { + const c = escaped[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + : s) + + '"'); +} +exports.default = quote; +//# sourceMappingURL=quote.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/types/jtd-schema.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/types/jtd-schema.js new file mode 100644 index 00000000000000..11338aa8a8f306 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/types/jtd-schema.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=jtd-schema.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js index 998b9666926b7b..bd8264b44d0bd4 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js @@ -41,13 +41,8 @@ const def = { let definedProp; if (props.length > 8) { // TODO maybe an option instead of hard-coded 8? - const hasProp = gen.scopeValue("func", { - // eslint-disable-next-line @typescript-eslint/unbound-method - ref: Object.prototype.hasOwnProperty, - code: codegen_1._ `Object.prototype.hasOwnProperty`, - }); const propsSchema = util_1.schemaRefOrVal(it, parentSchema.properties, "properties"); - definedProp = codegen_1._ `${hasProp}.call(${propsSchema}, ${key})`; + definedProp = code_1.isOwnProperty(gen, propsSchema, key); } else if (props.length) { definedProp = codegen_1.or(...props.map((p) => codegen_1._ `${key} === ${p}`)); diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/dependencies.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/dependencies.js index 9ec12ab16cf4d9..af1479f1e1315e 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/dependencies.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/dependencies.js @@ -12,7 +12,7 @@ exports.error = { params: ({ params: { property, depsCount, deps, missingProperty } }) => codegen_1._ `{property: ${property}, missingProperty: ${missingProperty}, depsCount: ${depsCount}, - deps: ${deps}}`, + deps: ${deps}}`, // TODO change to reference }; const def = { keyword: "dependencies", @@ -45,7 +45,7 @@ function validatePropertyDeps(cxt, propertyDeps = cxt.schema) { const deps = propertyDeps[prop]; if (deps.length === 0) continue; - const hasProperty = code_1.propertyInData(data, prop, it.opts.ownProperties); + const hasProperty = code_1.propertyInData(gen, data, prop, it.opts.ownProperties); cxt.setParams({ property: prop, depsCount: deps.length, @@ -72,7 +72,7 @@ function validateSchemaDeps(cxt, schemaDeps = cxt.schema) { for (const prop in schemaDeps) { if (util_1.alwaysValidSchema(it, schemaDeps[prop])) continue; - gen.if(code_1.propertyInData(data, prop, it.opts.ownProperties), () => { + gen.if(code_1.propertyInData(gen, data, prop, it.opts.ownProperties), () => { const schCxt = cxt.subschema({ keyword, schemaProp: prop }, valid); cxt.mergeValidEvaluated(schCxt, valid); }, () => gen.var(valid, true) // TODO var diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/properties.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/properties.js index 12fab3ce281970..7d127ae72ab762 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/properties.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/applicator/properties.js @@ -14,6 +14,9 @@ const def = { additionalProperties_1.default.code(new context_1.default(it, additionalProperties_1.default, "additionalProperties")); } const allProps = code_1.allSchemaProperties(schema); + for (const prop of allProps) { + it.definedProperties.add(prop); + } if (it.opts.unevaluated && allProps.length && it.props !== true) { it.props = util_1.mergeEvaluated.props(gen, util_1.toHash(allProps), it.props); } @@ -26,12 +29,13 @@ const def = { applyPropertySchema(prop); } else { - gen.if(code_1.propertyInData(data, prop, it.opts.ownProperties)); + gen.if(code_1.propertyInData(gen, data, prop, it.opts.ownProperties)); applyPropertySchema(prop); if (!it.allErrors) gen.else().var(valid, true); gen.endIf(); } + cxt.it.definedProperties.add(prop); cxt.ok(valid); } function hasDefault(prop) { diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/code.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/code.js index a019e59b2115b7..9e8c5d92aab73b 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/code.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/code.js @@ -1,20 +1,20 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0; +exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.isOwnProperty = exports.hasPropFunc = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0; const codegen_1 = require("../compile/codegen"); const util_1 = require("../compile/util"); const subschema_1 = require("../compile/subschema"); const names_1 = require("../compile/names"); function checkReportMissingProp(cxt, prop) { const { gen, data, it } = cxt; - gen.if(noPropertyInData(data, prop, it.opts.ownProperties), () => { + gen.if(noPropertyInData(gen, data, prop, it.opts.ownProperties), () => { cxt.setParams({ missingProperty: codegen_1._ `${prop}` }, true); cxt.error(); }); } exports.checkReportMissingProp = checkReportMissingProp; -function checkMissingProp({ data, it: { opts } }, properties, missing) { - return codegen_1.or(...properties.map((prop) => codegen_1._ `${noPropertyInData(data, prop, opts.ownProperties)} && (${missing} = ${prop})`)); +function checkMissingProp({ gen, data, it: { opts } }, properties, missing) { + return codegen_1.or(...properties.map((prop) => codegen_1._ `${noPropertyInData(gen, data, prop, opts.ownProperties)} && (${missing} = ${prop})`)); } exports.checkMissingProp = checkMissingProp; function reportMissingProp(cxt, missing) { @@ -22,17 +22,26 @@ function reportMissingProp(cxt, missing) { cxt.error(); } exports.reportMissingProp = reportMissingProp; -function isOwnProperty(data, property) { - return codegen_1._ `Object.prototype.hasOwnProperty.call(${data}, ${property})`; +function hasPropFunc(gen) { + return gen.scopeValue("func", { + // eslint-disable-next-line @typescript-eslint/unbound-method + ref: Object.prototype.hasOwnProperty, + code: codegen_1._ `Object.prototype.hasOwnProperty`, + }); +} +exports.hasPropFunc = hasPropFunc; +function isOwnProperty(gen, data, property) { + return codegen_1._ `${hasPropFunc(gen)}.call(${data}, ${property})`; } -function propertyInData(data, property, ownProperties) { +exports.isOwnProperty = isOwnProperty; +function propertyInData(gen, data, property, ownProperties) { const cond = codegen_1._ `${data}${codegen_1.getProperty(property)} !== undefined`; - return ownProperties ? codegen_1._ `${cond} && ${isOwnProperty(data, property)}` : cond; + return ownProperties ? codegen_1._ `${cond} && ${isOwnProperty(gen, data, property)}` : cond; } exports.propertyInData = propertyInData; -function noPropertyInData(data, property, ownProperties) { +function noPropertyInData(gen, data, property, ownProperties) { const cond = codegen_1._ `${data}${codegen_1.getProperty(property)} === undefined`; - return ownProperties ? codegen_1._ `${cond} || !${isOwnProperty(data, property)}` : cond; + return ownProperties ? codegen_1._ `${cond} || !${isOwnProperty(gen, data, property)}` : cond; } exports.noPropertyInData = noPropertyInData; function allSchemaProperties(schemaMap) { diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/properties.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/properties.js index c9a29a01c5fec2..654a35b3caf591 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/properties.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/properties.js @@ -58,7 +58,7 @@ function validateProperties(cxt) { function validateProps(props, keyword, required) { const _valid = gen.var("valid"); for (const prop of props) { - gen.if(code_1.propertyInData(data, prop, it.opts.ownProperties), () => applyPropertySchema(prop, keyword, _valid), missingProperty); + gen.if(code_1.propertyInData(gen, data, prop, it.opts.ownProperties), () => applyPropertySchema(prop, keyword, _valid), missingProperty); cxt.ok(_valid); } function missingProperty() { @@ -102,12 +102,7 @@ function validateProperties(cxt) { if (props.length > 8) { // TODO maybe an option instead of hard-coded 8? const propsSchema = util_1.schemaRefOrVal(it, parentSchema[keyword], keyword); - const hasProp = gen.scopeValue("func", { - // eslint-disable-next-line @typescript-eslint/unbound-method - ref: Object.prototype.hasOwnProperty, - code: codegen_1._ `Object.prototype.hasOwnProperty`, - }); - additional = codegen_1._ `!${hasProp}.call(${propsSchema}, ${key})`; + additional = code_1.isOwnProperty(gen, propsSchema, key); } else if (props.length) { additional = codegen_1.and(...props.map((p) => codegen_1._ `${key} !== ${p}`)); diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/ref.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/ref.js index acfd31748dae6f..a3b341ab039044 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/ref.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/ref.js @@ -1,5 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.hasRef = void 0; const compile_1 = require("../../compile"); const codegen_1 = require("../../compile/codegen"); const error_classes_1 = require("../../compile/error_classes"); @@ -50,15 +51,16 @@ const def = { errSchemaPath: `/definitions/${ref}`, }, valid); } - function hasRef(schema) { - for (const key in schema) { - let sch; - if (key === "ref" || (typeof (sch = schema[key]) == "object" && hasRef(sch))) - return true; - } - return false; - } }, }; +function hasRef(schema) { + for (const key in schema) { + let sch; + if (key === "ref" || (typeof (sch = schema[key]) == "object" && hasRef(sch))) + return true; + } + return false; +} +exports.hasRef = hasRef; exports.default = def; //# sourceMappingURL=ref.js.map \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/type.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/type.js index 1eedbb86480e3c..96ff3a7e608069 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/type.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/jtd/type.js @@ -1,15 +1,17 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); +exports.intRange = void 0; const codegen_1 = require("../../compile/codegen"); const timestamp_1 = require("../../compile/timestamp"); +const util_1 = require("../../compile/util"); const metadata_1 = require("./metadata"); -const intRange = { - int8: [-128, 127], - uint8: [0, 255], - int16: [-32768, 32767], - uint16: [0, 65535], - int32: [-2147483648, 2147483647], - uint32: [0, 4294967295], +exports.intRange = { + int8: [-128, 127, 3], + uint8: [0, 255, 3], + int16: [-32768, 32767, 5], + uint16: [0, 65535, 5], + int32: [-2147483648, 2147483647, 10], + uint32: [0, 4294967295, 10], }; const def = { keyword: "type", @@ -24,10 +26,7 @@ const def = { cond = codegen_1._ `typeof ${data} == ${schema}`; break; case "timestamp": { - const vts = gen.scopeValue("func", { - ref: timestamp_1.default, - code: codegen_1._ `require("ajv/dist/compile/timestamp").default`, - }); + const vts = util_1.func(gen, timestamp_1.default); cond = codegen_1._ `${data} instanceof Date || (typeof ${data} == "string" && ${vts}(${data}))`; break; } @@ -36,7 +35,7 @@ const def = { cond = codegen_1._ `typeof ${data} == "number"`; break; default: { - const [min, max] = intRange[schema]; + const [min, max] = exports.intRange[schema]; cond = codegen_1._ `typeof ${data} == "number" && isFinite(${data}) && ${data} >= ${min} && ${data} <= ${max} && !(${data} % 1)`; } } diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/validation/required.js b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/validation/required.js index 2d385229fbcd17..687783fd8abba9 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/validation/required.js +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/dist/vocabularies/validation/required.js @@ -2,6 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const code_1 = require("../code"); const codegen_1 = require("../../compile/codegen"); +const validate_1 = require("../../compile/validate"); const error = { message: ({ params: { missingProperty } }) => codegen_1.str `should have required property '${missingProperty}'`, params: ({ params: { missingProperty } }) => codegen_1._ `{missingProperty: ${missingProperty}}`, @@ -22,6 +23,17 @@ const def = { allErrorsMode(); else exitOnErrorMode(); + if (opts.strictRequired) { + const props = cxt.parentSchema.properties; + const { definedProperties } = cxt.it; + for (const requiredKey of schema) { + if ((props === null || props === void 0 ? void 0 : props[requiredKey]) === undefined && !definedProperties.has(requiredKey)) { + const schemaPath = it.schemaEnv.baseId + it.errSchemaPath; + const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)`; + validate_1.checkStrictMode(it, msg, it.opts.strictRequired); + } + } + } function allErrorsMode() { if (useLoop || $data) { cxt.block$data(codegen_1.nil, loopAllRequired); @@ -48,13 +60,13 @@ const def = { function loopAllRequired() { gen.forOf("prop", schemaCode, (prop) => { cxt.setParams({ missingProperty: prop }); - gen.if(code_1.noPropertyInData(data, prop, opts.ownProperties), () => cxt.error()); + gen.if(code_1.noPropertyInData(gen, data, prop, opts.ownProperties), () => cxt.error()); }); } function loopUntilMissing(missing, valid) { cxt.setParams({ missingProperty: missing }); gen.forOf(missing, schemaCode, () => { - gen.assign(valid, code_1.propertyInData(data, missing, opts.ownProperties)); + gen.assign(valid, code_1.propertyInData(gen, data, missing, opts.ownProperties)); gen.if(codegen_1.not(valid), () => { cxt.error(); gen.break(); diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/package.json b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/package.json index 8ecc53dd8a97ec..2f1973e528c153 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/package.json +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/package.json @@ -1,71 +1,23 @@ { - "name": "ajv", - "version": "7.1.1", - "description": "Another JSON Schema Validator", - "main": "dist/ajv.js", - "types": "dist/ajv.d.ts", - "files": [ - "lib/", - "docs/", - "dist/", - "scripts/", - ".tonic_example.js" - ], - "scripts": { - "eslint": "eslint \"lib/**/*.ts\" \"spec/**/*.*s\" scripts --ignore-pattern spec/JSON-Schema-Test-Suite", - "prettier:write": "prettier --write \"./**/*.{md,json,yaml,js,ts}\"", - "prettier:check": "prettier --list-different \"./**/*.{md,json,yaml,js,ts}\"", - "test-spec": "cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register \"spec/**/*.spec.{ts,js}\" -R dot", - "test-codegen": "nyc cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register 'spec/codegen.spec.ts' -R spec", - "test-debug": "npm run test-spec -- --inspect-brk", - "test-cov": "nyc npm run test-spec", - "bundle": "rm -rf bundle && node ./scripts/bundle.js ajv ajv7 ajv7 && node ./scripts/bundle.js 2019 ajv2019 ajv2019 && node ./scripts/bundle.js jtd ajvJTD ajvJTD", - "build": "rm -rf dist && tsc && cp -r lib/refs dist && rm dist/refs/json-schema-2019-09/index.ts && rm dist/refs/jtd-schema.ts", - "json-tests": "rm -rf spec/_json/*.js && node scripts/jsontests", - "test-karma": "karma start", - "test-browser": "rm -rf .browser && npm run bundle && scripts/prepare-tests && karma start", - "test-all": "npm run test-cov && if-node-version 12 npm run test-browser", - "test": "npm link && npm link ajv && npm run json-tests && npm run eslint && npm run test-cov", - "test-ci": "AJV_FULL_TEST=true npm test", - "prepublish": "npm run build" - }, - "nyc": { - "exclude": [ - "**/spec/**", - "node_modules" - ], - "reporter": [ - "lcov", - "text-summary" - ] - }, - "repository": { - "type": "git", - "url": "https://github.com/ajv-validator/ajv.git" + "author": { + "name": "Evgeny Poberezkin" }, - "keywords": [ - "JSON", - "schema", - "validator", - "validation", - "jsonschema", - "json-schema", - "json-schema-validator", - "json-schema-validation" - ], - "author": "Evgeny Poberezkin", - "license": "MIT", "bugs": { "url": "https://github.com/ajv-validator/ajv/issues" }, - "homepage": "https://github.com/ajv-validator/ajv", - "tonicExampleFilename": ".tonic_example.js", + "bundleDependencies": false, + "collective": { + "type": "opencollective", + "url": "https://opencollective.com/ajv" + }, "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", "uri-js": "^4.2.2" }, + "deprecated": false, + "description": "Another JSON Schema Validator", "devDependencies": { "@ajv-validator/config": "^0.3.0", "@types/chai": "^4.2.12", @@ -74,6 +26,7 @@ "@types/require-from-string": "^1.2.0", "@typescript-eslint/eslint-plugin": "^3.8.0", "@typescript-eslint/parser": "^3.8.0", + "@vuepress/shared-utils": "^1.8.2", "ajv-formats": "^1.5.0", "browserify": "^17.0.0", "chai": "^4.0.1", @@ -95,23 +48,79 @@ "terser": "^5.2.1", "ts-node": "^9.0.0", "tsify": "^5.0.2", - "typescript": "^4.0.0" - }, - "collective": { - "type": "opencollective", - "url": "https://opencollective.com/ajv" + "typescript": "^4.2.0", + "vuepress": "^1.8.2" }, + "files": [ + "lib/", + "docs/", + "dist/", + "scripts/", + ".tonic_example.js" + ], "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" }, - "prettier": "@ajv-validator/config/prettierrc.json", + "homepage": "https://github.com/ajv-validator/ajv", "husky": { "hooks": { "pre-commit": "lint-staged && npm test" } }, + "keywords": [ + "JSON", + "schema", + "validator", + "validation", + "jsonschema", + "json-schema", + "json-schema-validator", + "json-schema-validation" + ], + "license": "MIT", "lint-staged": { - "*.{md,json,yaml,js,ts}": "prettier --write" - } -} + "*.{json,yaml,js,ts}": "prettier --write" + }, + "main": "dist/ajv.js", + "name": "ajv", + "nyc": { + "exclude": [ + "**/spec/**", + "node_modules" + ], + "reporter": [ + "lcov", + "text-summary" + ] + }, + "prettier": "@ajv-validator/config/prettierrc.json", + "repository": { + "type": "git", + "url": "git+https://github.com/ajv-validator/ajv.git" + }, + "scripts": { + "benchmark": "npm i && npm run build && npm link && cd ./benchmark && npm link ajv && npm i && node ./jtd", + "build": "rm -rf dist && tsc && cp -r lib/refs dist && rm dist/refs/json-schema-2019-09/index.ts && rm dist/refs/jtd-schema.ts", + "bundle": "rm -rf bundle && node ./scripts/bundle.js ajv ajv7 ajv7 && node ./scripts/bundle.js 2019 ajv2019 ajv2019 && node ./scripts/bundle.js jtd ajvJTD ajvJTD", + "docs:build": "vuepress build docs", + "docs:dev": "vuepress dev docs", + "eslint": "eslint \"lib/**/*.ts\" \"spec/**/*.*s\" scripts --ignore-pattern spec/JSON-Schema-Test-Suite", + "json-tests": "rm -rf spec/_json/*.js && node scripts/jsontests", + "prepublish": "npm run build", + "prettier:check": "prettier --list-different \"./**/*.{json,yaml,js,ts}\"", + "prettier:write": "prettier --write \"./**/*.{json,yaml,js,ts}\"", + "test": "npm link && npm link ajv && npm run json-tests && npm run eslint && npm run test-cov", + "test-all": "npm run test-cov && if-node-version 12 npm run test-browser", + "test-browser": "rm -rf .browser && npm run bundle && scripts/prepare-tests && karma start", + "test-ci": "AJV_FULL_TEST=true npm test", + "test-codegen": "nyc cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register 'spec/codegen.spec.ts' -R spec", + "test-cov": "nyc npm run test-spec", + "test-debug": "npm run test-spec -- --inspect-brk", + "test-karma": "karma start", + "test-spec": "cross-env TS_NODE_PROJECT=spec/tsconfig.json mocha -r ts-node/register \"spec/**/*.spec.{ts,js}\" -R dot" + }, + "tonicExampleFilename": ".tonic_example.js", + "types": "dist/ajv.d.ts", + "version": "7.2.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-bundles b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-bundles index d91d01c034a813..311c21608d2bec 100755 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-bundles +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-bundles @@ -10,7 +10,7 @@ if [[ $GITHUB_REF == refs/tags/v* ]]; then git config --global user.name "$GIT_USER_NAME" git config --global user.email "$GIT_USER_EMAIL" - git clone https://${GH_TOKEN_PUBLIC}@github.com/ajv-validator/ajv-dist.git ../ajv-dist + git clone https://"${GH_TOKEN_PUBLIC}"@github.com/ajv-validator/ajv-dist.git ../ajv-dist rm -rf ../ajv-dist/dist mkdir ../ajv-dist/dist @@ -22,7 +22,7 @@ if [[ $GITHUB_REF == refs/tags/v* ]]; then sed -E "s/\"version\": \"([^\"]*)\"/\"version\": \"$VERSION\"/" package.json > new_package.json mv new_package.json package.json - if [[ `git status --porcelain` ]]; then + if [[ $(git status --porcelain) ]]; then echo "Changes detected. Updating master branch..." git add -A git commit -m "$VERSION: updated by ajv workflow https://github.com/ajv-validator/ajv/actions/runs/$GITHUB_RUN_ID" diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-gh-pages b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-gh-pages deleted file mode 100755 index 34e488a948aa94..00000000000000 --- a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-gh-pages +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -echo "About to publish $GITHUB_REF to gh-pages..." - -rm -rf ../gh-pages - -git config --global user.name "$GIT_USER_NAME" -git config --global user.email "$GIT_USER_EMAIL" -git clone -b gh-pages --single-branch https://${GH_TOKEN_PUBLIC}@github.com/ajv-validator/ajv.git ../gh-pages -SOURCE=../gh-pages/_source -mkdir -p $SOURCE -cp *.md $SOURCE -cp -R docs $SOURCE -cp LICENSE $SOURCE -cd ../gh-pages -node ./generate - -# remove logo from README -sed -E "s/<img[^>]+ajv_logo[^>]+>//" index.md > new-index.md -mv new-index.md index.md - -if [[ `git status --porcelain` ]]; then - echo "Changes detected. Updating gh-pages branch..." - git add -A - git commit -m "updated by ajv workflow https://github.com/ajv-validator/ajv/actions/runs/$GITHUB_RUN_ID" - git push --quiet origin gh-pages > /dev/null 2>&1 -fi - -echo "Done" diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-site b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-site new file mode 100755 index 00000000000000..c09758432d5ba6 --- /dev/null +++ b/tools/node_modules/eslint/node_modules/table/node_modules/ajv/scripts/publish-site @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +set -ex + +echo "About to publish $GITHUB_REF to gh-pages..." +rm -rf ../gh-pages + +function copyReplace { + sed "s/](.\/docs\//](.\//g" $1 > $2 +} + +copyReplace README.md docs/README.md +copyReplace CODE_OF_CONDUCT.md docs/code_of_conduct.md +copyReplace CONTRIBUTING.md docs/contributing.md +copyReplace LICENSE docs/license.md + +npm run docs:build + +git config --global user.name "$GIT_USER_NAME" +git config --global user.email "$GIT_USER_EMAIL" +git clone -b gh-pages --single-branch https://"${GH_TOKEN_PUBLIC}"@github.com/ajv-validator/ajv.git ../gh-pages + +rsync -a ./docs/.vuepress/dist/ ../gh-pages/docs + +cd ../gh-pages + +if [[ $(git status --porcelain) ]]; then + echo "Changes detected. Updating gh-pages branch..." + git add -A + git commit -m "updated by ajv workflow https://github.com/ajv-validator/ajv/actions/runs/$GITHUB_RUN_ID" + git push --quiet origin gh-pages > /dev/null 2>&1 +fi + +echo "Done" diff --git a/tools/node_modules/eslint/node_modules/table/node_modules/json-schema-traverse/package.json b/tools/node_modules/eslint/node_modules/table/node_modules/json-schema-traverse/package.json index e32dfbaeec58fb..98fee959b778b7 100644 --- a/tools/node_modules/eslint/node_modules/table/node_modules/json-schema-traverse/package.json +++ b/tools/node_modules/eslint/node_modules/table/node_modules/json-schema-traverse/package.json @@ -1,35 +1,28 @@ { - "name": "json-schema-traverse", - "version": "1.0.0", - "description": "Traverse JSON Schema passing each schema object to callback", - "main": "index.js", - "types": "index.d.ts", - "scripts": { - "eslint": "eslint index.js spec", - "test-spec": "mocha spec -R spec", - "test": "npm run eslint && nyc npm run test-spec" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/epoberezkin/json-schema-traverse.git" + "author": { + "name": "Evgeny Poberezkin" }, - "keywords": [ - "JSON-Schema", - "traverse", - "iterate" - ], - "author": "Evgeny Poberezkin", - "license": "MIT", "bugs": { "url": "https://github.com/epoberezkin/json-schema-traverse/issues" }, - "homepage": "https://github.com/epoberezkin/json-schema-traverse#readme", + "bundleDependencies": false, + "deprecated": false, + "description": "Traverse JSON Schema passing each schema object to callback", "devDependencies": { "eslint": "^7.3.1", "mocha": "^8.0.1", "nyc": "^15.0.0", "pre-commit": "^1.2.2" }, + "homepage": "https://github.com/epoberezkin/json-schema-traverse#readme", + "keywords": [ + "JSON-Schema", + "traverse", + "iterate" + ], + "license": "MIT", + "main": "index.js", + "name": "json-schema-traverse", "nyc": { "exclude": [ "**/spec/**", @@ -39,5 +32,16 @@ "lcov", "text-summary" ] - } -} + }, + "repository": { + "type": "git", + "url": "git+https://github.com/epoberezkin/json-schema-traverse.git" + }, + "scripts": { + "eslint": "eslint index.js spec", + "test": "npm run eslint && nyc npm run test-spec", + "test-spec": "mocha spec -R spec" + }, + "types": "index.d.ts", + "version": "1.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/table/package.json b/tools/node_modules/eslint/node_modules/table/package.json index 85036e7f2e5bb9..892f772a2f312a 100644 --- a/tools/node_modules/eslint/node_modules/table/package.json +++ b/tools/node_modules/eslint/node_modules/table/package.json @@ -1,15 +1,20 @@ { "author": { - "email": "gajus@gajus.com", "name": "Gajus Kuizinas", + "email": "gajus@gajus.com", "url": "http://gajus.com" }, + "bugs": { + "url": "https://github.com/gajus/table/issues" + }, + "bundleDependencies": false, "dependencies": { "ajv": "^7.0.2", "lodash": "^4.17.20", "slice-ansi": "^4.0.0", "string-width": "^4.2.0" }, + "deprecated": false, "description": "Formats data into a string table.", "devDependencies": { "@babel/cli": "^7.12.10", @@ -41,6 +46,7 @@ "engines": { "node": ">=10.0.0" }, + "homepage": "https://github.com/gajus/table#readme", "husky": { "hooks": { "post-commit": "npm run create-readme && git add README.md && git commit -m 'docs: generate docs' --no-verify", @@ -72,7 +78,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/gajus/table" + "url": "git+https://github.com/gajus/table.git" }, "scripts": { "build": "rm -fr ./dist && NODE_ENV=production babel ./src --out-dir ./dist --copy-files --source-maps && npm run create-validators && flow-copy-source src dist", @@ -82,4 +88,4 @@ "test": "mocha --require @babel/register" }, "version": "6.0.7" -} +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/text-table/package.json b/tools/node_modules/eslint/node_modules/text-table/package.json index b4d17a4ff81316..f5c43b5d934ee1 100644 --- a/tools/node_modules/eslint/node_modules/text-table/package.json +++ b/tools/node_modules/eslint/node_modules/text-table/package.json @@ -1,44 +1,50 @@ { - "name": "text-table", - "version": "0.2.0", - "description": "borderless text tables with alignment", - "main": "index.js", - "devDependencies": { - "tap": "~0.4.0", - "tape": "~1.0.2", - "cli-color": "~0.2.3" - }, - "scripts": { - "test": "tap test/*.js" - }, - "testling" : { - "files" : "test/*.js", - "browsers" : [ - "ie/6..latest", - "chrome/20..latest", - "firefox/10..latest", - "safari/latest", - "opera/11.0..latest", - "iphone/6", "ipad/6" - ] - }, - "repository": { - "type": "git", - "url": "git://github.com/substack/text-table.git" - }, - "homepage": "https://github.com/substack/text-table", - "keywords": [ - "text", - "table", - "align", - "ascii", - "rows", - "tabular" - ], - "author": { - "name": "James Halliday", - "email": "mail@substack.net", - "url": "http://substack.net" - }, - "license": "MIT" -} + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "bugs": { + "url": "https://github.com/substack/text-table/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "borderless text tables with alignment", + "devDependencies": { + "cli-color": "~0.2.3", + "tap": "~0.4.0", + "tape": "~1.0.2" + }, + "homepage": "https://github.com/substack/text-table", + "keywords": [ + "text", + "table", + "align", + "ascii", + "rows", + "tabular" + ], + "license": "MIT", + "main": "index.js", + "name": "text-table", + "repository": { + "type": "git", + "url": "git://github.com/substack/text-table.git" + }, + "scripts": { + "test": "tap test/*.js" + }, + "testling": { + "files": "test/*.js", + "browsers": [ + "ie/6..latest", + "chrome/20..latest", + "firefox/10..latest", + "safari/latest", + "opera/11.0..latest", + "iphone/6", + "ipad/6" + ] + }, + "version": "0.2.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/type-check/package.json b/tools/node_modules/eslint/node_modules/type-check/package.json index 2a57ea0643dc58..a056438a760bad 100644 --- a/tools/node_modules/eslint/node_modules/type-check/package.json +++ b/tools/node_modules/eslint/node_modules/type-check/package.json @@ -1,8 +1,30 @@ { - "name": "type-check", - "version": "0.4.0", - "author": "George Zahariev <z@georgezahariev.com>", + "author": { + "name": "George Zahariev", + "email": "z@georgezahariev.com" + }, + "bugs": { + "url": "https://github.com/gkz/type-check/issues" + }, + "bundleDependencies": false, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "deprecated": false, "description": "type-check allows you to check the types of JavaScript values at runtime with a Haskell like type syntax.", + "devDependencies": { + "browserify": "^16.5.1", + "livescript": "^1.6.0", + "mocha": "^7.1.1" + }, + "engines": { + "node": ">= 0.8.0" + }, + "files": [ + "lib", + "README.md", + "LICENSE" + ], "homepage": "https://github.com/gkz/type-check", "keywords": [ "type", @@ -10,17 +32,9 @@ "checking", "library" ], - "files": [ - "lib", - "README.md", - "LICENSE" - ], - "main": "./lib/", - "bugs": "https://github.com/gkz/type-check/issues", "license": "MIT", - "engines": { - "node": ">= 0.8.0" - }, + "main": "./lib/", + "name": "type-check", "repository": { "type": "git", "url": "git://github.com/gkz/type-check.git" @@ -28,12 +42,5 @@ "scripts": { "test": "make test" }, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "devDependencies": { - "livescript": "^1.6.0", - "mocha": "^7.1.1", - "browserify": "^16.5.1" - } -} + "version": "0.4.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/type-fest/package.json b/tools/node_modules/eslint/node_modules/type-fest/package.json index ea6621129dc660..0164025631ef29 100644 --- a/tools/node_modules/eslint/node_modules/type-fest/package.json +++ b/tools/node_modules/eslint/node_modules/type-fest/package.json @@ -1,51 +1,60 @@ { - "name": "type-fest", - "version": "0.8.1", - "description": "A collection of essential TypeScript types", - "license": "(MIT OR CC0-1.0)", - "repository": "sindresorhus/type-fest", - "author": { - "name": "Sindre Sorhus", - "email": "sindresorhus@gmail.com", - "url": "sindresorhus.com" - }, - "engines": { - "node": ">=8" - }, - "scripts": { - "test": "xo && tsd" - }, - "files": [ - "index.d.ts", - "source" - ], - "keywords": [ - "typescript", - "ts", - "types", - "utility", - "util", - "utilities", - "omit", - "merge", - "json" - ], - "devDependencies": { - "@sindresorhus/tsconfig": "^0.4.0", - "@typescript-eslint/eslint-plugin": "^2.2.0", - "@typescript-eslint/parser": "^2.2.0", - "eslint-config-xo-typescript": "^0.18.0", - "tsd": "^0.7.3", - "xo": "^0.24.0" - }, - "xo": { - "extends": "xo-typescript", - "extensions": [ - "ts" - ], - "rules": { - "import/no-unresolved": "off", - "@typescript-eslint/indent": "off" - } - } -} + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "sindresorhus.com" + }, + "bugs": { + "url": "https://github.com/sindresorhus/type-fest/issues" + }, + "bundleDependencies": false, + "deprecated": false, + "description": "A collection of essential TypeScript types", + "devDependencies": { + "@sindresorhus/tsconfig": "^0.4.0", + "@typescript-eslint/eslint-plugin": "^2.2.0", + "@typescript-eslint/parser": "^2.2.0", + "eslint-config-xo-typescript": "^0.18.0", + "tsd": "^0.7.3", + "xo": "^0.24.0" + }, + "engines": { + "node": ">=8" + }, + "files": [ + "index.d.ts", + "source" + ], + "homepage": "https://github.com/sindresorhus/type-fest#readme", + "keywords": [ + "typescript", + "ts", + "types", + "utility", + "util", + "utilities", + "omit", + "merge", + "json" + ], + "license": "(MIT OR CC0-1.0)", + "name": "type-fest", + "repository": { + "type": "git", + "url": "git+https://github.com/sindresorhus/type-fest.git" + }, + "scripts": { + "test": "xo && tsd" + }, + "version": "0.8.1", + "xo": { + "extends": "xo-typescript", + "extensions": [ + "ts" + ], + "rules": { + "import/no-unresolved": "off", + "@typescript-eslint/indent": "off" + } + } +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/uri-js/package.json b/tools/node_modules/eslint/node_modules/uri-js/package.json index de95d91aa1a410..b97f667a234ea6 100755 --- a/tools/node_modules/eslint/node_modules/uri-js/package.json +++ b/tools/node_modules/eslint/node_modules/uri-js/package.json @@ -1,9 +1,30 @@ { - "name": "uri-js", - "version": "4.4.1", + "author": { + "name": "Gary Court", + "email": "gary.court@gmail.com" + }, + "bugs": { + "url": "https://github.com/garycourt/uri-js/issues" + }, + "bundleDependencies": false, + "dependencies": { + "punycode": "^2.1.0" + }, + "deprecated": false, "description": "An RFC 3986/3987 compliant, scheme extendable URI/IRI parsing/validating/resolving library for JavaScript.", - "main": "dist/es5/uri.all.js", - "types": "dist/es5/uri.all.d.ts", + "devDependencies": { + "babel-cli": "^6.26.0", + "babel-plugin-external-helpers": "^6.22.0", + "babel-preset-latest": "^6.24.1", + "mocha": "^8.2.1", + "mocha-qunit-ui": "^0.1.3", + "rollup": "^0.41.6", + "rollup-plugin-babel": "^2.7.1", + "rollup-plugin-node-resolve": "^2.0.0", + "sorcery": "^0.10.0", + "typescript": "^2.8.1", + "uglify-js": "^2.8.14" + }, "directories": { "test": "tests" }, @@ -15,19 +36,7 @@ "CHANGELOG", "LICENSE" ], - "scripts": { - "build:esnext": "tsc", - "build:es5": "rollup -c && cp dist/esnext/uri.d.ts dist/es5/uri.all.d.ts && npm run build:es5:fix-sourcemap", - "build:es5:fix-sourcemap": "sorcery -i dist/es5/uri.all.js", - "build:es5:min": "uglifyjs dist/es5/uri.all.js --support-ie8 --output dist/es5/uri.all.min.js --in-source-map dist/es5/uri.all.js.map --source-map uri.all.min.js.map --comments --compress --mangle --pure-funcs merge subexp && mv uri.all.min.js.map dist/es5/ && cp dist/es5/uri.all.d.ts dist/es5/uri.all.min.d.ts", - "build": "npm run build:esnext && npm run build:es5 && npm run build:es5:min", - "clean": "rm -rf dist", - "test": "mocha -u mocha-qunit-ui dist/es5/uri.all.js tests/tests.js" - }, - "repository": { - "type": "git", - "url": "http://github.com/garycourt/uri-js" - }, + "homepage": "https://github.com/garycourt/uri-js", "keywords": [ "URI", "IRI", @@ -52,26 +61,22 @@ "RFC6455", "RFC6874" ], - "author": "Gary Court <gary.court@gmail.com>", "license": "BSD-2-Clause", - "bugs": { - "url": "https://github.com/garycourt/uri-js/issues" + "main": "dist/es5/uri.all.js", + "name": "uri-js", + "repository": { + "type": "git", + "url": "git+ssh://git@github.com/garycourt/uri-js.git" }, - "homepage": "https://github.com/garycourt/uri-js", - "devDependencies": { - "babel-cli": "^6.26.0", - "babel-plugin-external-helpers": "^6.22.0", - "babel-preset-latest": "^6.24.1", - "mocha": "^8.2.1", - "mocha-qunit-ui": "^0.1.3", - "rollup": "^0.41.6", - "rollup-plugin-babel": "^2.7.1", - "rollup-plugin-node-resolve": "^2.0.0", - "sorcery": "^0.10.0", - "typescript": "^2.8.1", - "uglify-js": "^2.8.14" + "scripts": { + "build": "npm run build:esnext && npm run build:es5 && npm run build:es5:min", + "build:es5": "rollup -c && cp dist/esnext/uri.d.ts dist/es5/uri.all.d.ts && npm run build:es5:fix-sourcemap", + "build:es5:fix-sourcemap": "sorcery -i dist/es5/uri.all.js", + "build:es5:min": "uglifyjs dist/es5/uri.all.js --support-ie8 --output dist/es5/uri.all.min.js --in-source-map dist/es5/uri.all.js.map --source-map uri.all.min.js.map --comments --compress --mangle --pure-funcs merge subexp && mv uri.all.min.js.map dist/es5/ && cp dist/es5/uri.all.d.ts dist/es5/uri.all.min.d.ts", + "build:esnext": "tsc", + "clean": "rm -rf dist", + "test": "mocha -u mocha-qunit-ui dist/es5/uri.all.js tests/tests.js" }, - "dependencies": { - "punycode": "^2.1.0" - } -} + "types": "dist/es5/uri.all.d.ts", + "version": "4.4.1" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/v8-compile-cache/package.json b/tools/node_modules/eslint/node_modules/v8-compile-cache/package.json index cfc665f109976b..13108425be015f 100644 --- a/tools/node_modules/eslint/node_modules/v8-compile-cache/package.json +++ b/tools/node_modules/eslint/node_modules/v8-compile-cache/package.json @@ -1,25 +1,15 @@ { - "name": "v8-compile-cache", - "version": "2.2.0", - "description": "Require hook for automatic V8 compile cache persistence", - "main": "v8-compile-cache.js", - "scripts": { - "bench": "bench/run.sh", - "eslint": "eslint --max-warnings=0 .", - "tap": "tap test/*-test.js", - "test": "npm run tap", - "posttest": "npm run eslint" + "author": { + "name": "Andres Suarez", + "email": "zertosh@gmail.com" }, - "author": "Andres Suarez <zertosh@gmail.com>", - "repository": { - "type": "git", - "url": "https://github.com/zertosh/v8-compile-cache.git" + "bugs": { + "url": "https://github.com/zertosh/v8-compile-cache/issues" }, - "files": [ - "v8-compile-cache.js" - ], - "license": "MIT", + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Require hook for automatic V8 compile cache persistence", "devDependencies": { "babel-core": "6.26.3", "eslint": "^7.12.1", @@ -27,8 +17,27 @@ "rimraf": "^2.5.4", "rxjs": "6.6.3", "semver": "^5.3.0", - "tap": "^10.1.1", + "tap": "^9.0.0", "temp": "^0.8.3", "yarn": "1.22.10" - } -} + }, + "files": [ + "v8-compile-cache.js" + ], + "homepage": "https://github.com/zertosh/v8-compile-cache#readme", + "license": "MIT", + "main": "v8-compile-cache.js", + "name": "v8-compile-cache", + "repository": { + "type": "git", + "url": "git+https://github.com/zertosh/v8-compile-cache.git" + }, + "scripts": { + "bench": "bench/run.sh", + "eslint": "eslint --max-warnings=0 .", + "posttest": "npm run eslint", + "tap": "tap test/*-test.js", + "test": "npm run tap" + }, + "version": "2.3.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/v8-compile-cache/v8-compile-cache.js b/tools/node_modules/eslint/node_modules/v8-compile-cache/v8-compile-cache.js index b9c09288cb310b..f094da40f8a1d7 100644 --- a/tools/node_modules/eslint/node_modules/v8-compile-cache/v8-compile-cache.js +++ b/tools/node_modules/eslint/node_modules/v8-compile-cache/v8-compile-cache.js @@ -328,15 +328,15 @@ function getCacheDir() { return cacheDir; } -function getParentName() { - // `module.parent.filename` is undefined or null when: +function getMainName() { + // `require.main.filename` is undefined or null when: // * node -e 'require("v8-compile-cache")' // * node -r 'v8-compile-cache' // * Or, requiring from the REPL. - const parentName = module.parent && typeof module.parent.filename === 'string' - ? module.parent.filename + const mainName = require.main && typeof require.main.filename === 'string' + ? require.main.filename : process.cwd(); - return parentName; + return mainName; } //------------------------------------------------------------------------------ @@ -345,7 +345,7 @@ function getParentName() { if (!process.env.DISABLE_V8_COMPILE_CACHE && supportsCachedData()) { const cacheDir = getCacheDir(); - const prefix = getParentName(); + const prefix = getMainName(); const blobStore = new FileSystemBlobStore(cacheDir, prefix); const nativeCompileCache = new NativeCompileCache(); @@ -367,5 +367,5 @@ module.exports.__TEST__ = { slashEscape, supportsCachedData, getCacheDir, - getParentName, + getMainName, }; diff --git a/tools/node_modules/eslint/node_modules/which/package.json b/tools/node_modules/eslint/node_modules/which/package.json index 97ad7fbabc52b5..32eaa57575d680 100644 --- a/tools/node_modules/eslint/node_modules/which/package.json +++ b/tools/node_modules/eslint/node_modules/which/package.json @@ -1,43 +1,53 @@ { - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me)", - "name": "which", - "description": "Like which(1) unix command. Find the first instance of an executable in the PATH.", - "version": "2.0.2", - "repository": { - "type": "git", - "url": "git://github.com/isaacs/node-which.git" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me" }, - "main": "which.js", "bin": { - "node-which": "./bin/node-which" + "node-which": "bin/node-which" }, - "license": "ISC", + "bugs": { + "url": "https://github.com/isaacs/node-which/issues" + }, + "bundleDependencies": false, "dependencies": { "isexe": "^2.0.0" }, + "deprecated": false, + "description": "Like which(1) unix command. Find the first instance of an executable in the PATH.", "devDependencies": { "mkdirp": "^0.5.0", "rimraf": "^2.6.2", "tap": "^14.6.9" }, - "scripts": { - "test": "tap", - "preversion": "npm test", - "postversion": "npm publish", - "prepublish": "npm run changelog", - "prechangelog": "bash gen-changelog.sh", - "changelog": "git add CHANGELOG.md", - "postchangelog": "git commit -m 'update changelog - '${npm_package_version}", - "postpublish": "git push origin --follow-tags" + "engines": { + "node": ">= 8" }, "files": [ "which.js", "bin/node-which" ], + "homepage": "https://github.com/isaacs/node-which#readme", + "license": "ISC", + "main": "which.js", + "name": "which", + "repository": { + "type": "git", + "url": "git://github.com/isaacs/node-which.git" + }, + "scripts": { + "changelog": "git add CHANGELOG.md", + "postchangelog": "git commit -m 'update changelog - '${npm_package_version}", + "postpublish": "git push origin --follow-tags", + "postversion": "npm publish", + "prechangelog": "bash gen-changelog.sh", + "prepublish": "npm run changelog", + "preversion": "npm test", + "test": "tap" + }, "tap": { "check-coverage": true }, - "engines": { - "node": ">= 8" - } -} + "version": "2.0.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/word-wrap/package.json b/tools/node_modules/eslint/node_modules/word-wrap/package.json index 6f8f633c475f82..b53b03260e2797 100644 --- a/tools/node_modules/eslint/node_modules/word-wrap/package.json +++ b/tools/node_modules/eslint/node_modules/word-wrap/package.json @@ -1,39 +1,65 @@ { - "name": "word-wrap", - "description": "Wrap words to a specified length.", - "version": "1.2.3", - "homepage": "https://github.com/jonschlinkert/word-wrap", - "author": "Jon Schlinkert (https://github.com/jonschlinkert)", - "contributors": [ - "Danilo Sampaio <danilo.sampaio@gmail.com> (localhost:8080)", - "Fede Ramirez <i@2fd.me> (https://2fd.github.io)", - "Joe Hildebrand <joe-github@cursive.net> (https://twitter.com/hildjj)", - "Jon Schlinkert <jon.schlinkert@sellside.com> (http://twitter.com/jonschlinkert)", - "Todd Kennedy (https://tck.io)", - "Waldemar Reusch (https://github.com/lordvlad)", - "Wolfgang Faust (http://www.linestarve.com)", - "Zach Hale <zachhale@gmail.com> (http://zachhale.com)" - ], - "repository": "jonschlinkert/word-wrap", + "author": { + "name": "Jon Schlinkert", + "url": "https://github.com/jonschlinkert" + }, "bugs": { "url": "https://github.com/jonschlinkert/word-wrap/issues" }, - "license": "MIT", - "files": [ - "index.js", - "index.d.ts" + "bundleDependencies": false, + "contributors": [ + { + "name": "Danilo Sampaio", + "email": "danilo.sampaio@gmail.com", + "url": "localhost:8080" + }, + { + "name": "Fede Ramirez", + "email": "i@2fd.me", + "url": "https://2fd.github.io" + }, + { + "name": "Joe Hildebrand", + "email": "joe-github@cursive.net", + "url": "https://twitter.com/hildjj" + }, + { + "name": "Jon Schlinkert", + "email": "jon.schlinkert@sellside.com", + "url": "http://twitter.com/jonschlinkert" + }, + { + "name": "Todd Kennedy", + "url": "https://tck.io" + }, + { + "name": "Waldemar Reusch", + "url": "https://github.com/lordvlad" + }, + { + "name": "Wolfgang Faust", + "url": "http://www.linestarve.com" + }, + { + "name": "Zach Hale", + "email": "zachhale@gmail.com", + "url": "http://zachhale.com" + } ], - "main": "index.js", - "engines": { - "node": ">=0.10.0" - }, - "scripts": { - "test": "mocha" - }, + "deprecated": false, + "description": "Wrap words to a specified length.", "devDependencies": { "gulp-format-md": "^0.1.11", "mocha": "^3.2.0" }, + "engines": { + "node": ">=0.10.0" + }, + "files": [ + "index.js", + "index.d.ts" + ], + "homepage": "https://github.com/jonschlinkert/word-wrap", "keywords": [ "break", "carriage", @@ -48,6 +74,16 @@ "words", "wrap" ], + "license": "MIT", + "main": "index.js", + "name": "word-wrap", + "repository": { + "type": "git", + "url": "git+https://github.com/jonschlinkert/word-wrap.git" + }, + "scripts": { + "test": "mocha" + }, "typings": "index.d.ts", "verb": { "toc": false, @@ -73,5 +109,6 @@ "verb", "verb-generate-readme" ] - } -} + }, + "version": "1.2.3" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/wrappy/package.json b/tools/node_modules/eslint/node_modules/wrappy/package.json index 130752046714d6..e80b47520f24ec 100644 --- a/tools/node_modules/eslint/node_modules/wrappy/package.json +++ b/tools/node_modules/eslint/node_modules/wrappy/package.json @@ -1,29 +1,35 @@ { - "name": "wrappy", - "version": "1.0.2", - "description": "Callback wrapping utility", - "main": "wrappy.js", - "files": [ - "wrappy.js" - ], - "directories": { - "test": "test" + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/npm/wrappy/issues" }, + "bundleDependencies": false, "dependencies": {}, + "deprecated": false, + "description": "Callback wrapping utility", "devDependencies": { "tap": "^2.3.1" }, - "scripts": { - "test": "tap --coverage test/*.js" + "directories": { + "test": "test" }, + "files": [ + "wrappy.js" + ], + "homepage": "https://github.com/npm/wrappy", + "license": "ISC", + "main": "wrappy.js", + "name": "wrappy", "repository": { "type": "git", - "url": "https://github.com/npm/wrappy" + "url": "git+https://github.com/npm/wrappy.git" }, - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", - "license": "ISC", - "bugs": { - "url": "https://github.com/npm/wrappy/issues" + "scripts": { + "test": "tap --coverage test/*.js" }, - "homepage": "https://github.com/npm/wrappy" -} + "version": "1.0.2" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/node_modules/yallist/package.json b/tools/node_modules/eslint/node_modules/yallist/package.json index 8a083867d72e00..2d0ad3cb6e2f0c 100644 --- a/tools/node_modules/eslint/node_modules/yallist/package.json +++ b/tools/node_modules/eslint/node_modules/yallist/package.json @@ -1,8 +1,19 @@ { - "name": "yallist", - "version": "4.0.0", + "author": { + "name": "Isaac Z. Schlueter", + "email": "i@izs.me", + "url": "http://blog.izs.me/" + }, + "bugs": { + "url": "https://github.com/isaacs/yallist/issues" + }, + "bundleDependencies": false, + "dependencies": {}, + "deprecated": false, "description": "Yet Another Linked List", - "main": "yallist.js", + "devDependencies": { + "tap": "^12.1.0" + }, "directories": { "test": "test" }, @@ -10,20 +21,19 @@ "yallist.js", "iterator.js" ], - "dependencies": {}, - "devDependencies": { - "tap": "^12.1.0" - }, - "scripts": { - "test": "tap test/*.js --100", - "preversion": "npm test", - "postversion": "npm publish", - "postpublish": "git push origin --all; git push origin --tags" - }, + "homepage": "https://github.com/isaacs/yallist#readme", + "license": "ISC", + "main": "yallist.js", + "name": "yallist", "repository": { "type": "git", "url": "git+https://github.com/isaacs/yallist.git" }, - "author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)", - "license": "ISC" -} + "scripts": { + "postpublish": "git push origin --all; git push origin --tags", + "postversion": "npm publish", + "preversion": "npm test", + "test": "tap test/*.js --100" + }, + "version": "4.0.0" +} \ No newline at end of file diff --git a/tools/node_modules/eslint/package.json b/tools/node_modules/eslint/package.json index cdf5a4b180e1d7..e88d1b76c2f4fc 100644 --- a/tools/node_modules/eslint/package.json +++ b/tools/node_modules/eslint/package.json @@ -1,50 +1,15 @@ { - "name": "eslint", - "version": "7.21.0", - "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>", - "description": "An AST-based pattern checker for JavaScript.", - "bin": { - "eslint": "./bin/eslint.js" - }, - "main": "./lib/api.js", - "scripts": { - "test": "node Makefile.js test", - "test:cli": "mocha", - "lint": "node Makefile.js lint", - "fix": "node Makefile.js lint -- fix", - "fuzz": "node Makefile.js fuzz", - "generate-release": "node Makefile.js generateRelease", - "generate-alpharelease": "node Makefile.js generatePrerelease -- alpha", - "generate-betarelease": "node Makefile.js generatePrerelease -- beta", - "generate-rcrelease": "node Makefile.js generatePrerelease -- rc", - "publish-release": "node Makefile.js publishRelease", - "docs": "node Makefile.js docs", - "gensite": "node Makefile.js gensite", - "webpack": "node Makefile.js webpack", - "perf": "node Makefile.js perf" + "author": { + "name": "Nicholas C. Zakas", + "email": "nicholas+npm@nczconsulting.com" }, - "gitHooks": { - "pre-commit": "lint-staged" + "bin": { + "eslint": "bin/eslint.js" }, - "lint-staged": { - "*.js": [ - "eslint --fix", - "git add" - ], - "*.md": "markdownlint" + "bugs": { + "url": "https://github.com/eslint/eslint/issues/" }, - "files": [ - "LICENSE", - "README.md", - "bin", - "conf", - "lib", - "messages" - ], - "repository": "eslint/eslint", - "funding": "https://opencollective.com/eslint", - "homepage": "https://eslint.org", - "bugs": "https://github.com/eslint/eslint/issues/", + "bundleDependencies": false, "dependencies": { "@babel/code-frame": "7.12.11", "@eslint/eslintrc": "^0.4.0", @@ -63,7 +28,7 @@ "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^12.1.0", + "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -71,7 +36,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.20", + "lodash": "^4.17.21", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -84,6 +49,8 @@ "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, + "deprecated": false, + "description": "An AST-based pattern checker for JavaScript.", "devDependencies": { "@babel/core": "^7.4.3", "@babel/preset-env": "^7.4.3", @@ -108,11 +75,11 @@ "fs-teardown": "^0.1.0", "glob": "^7.1.6", "jsdoc": "^3.5.5", - "karma": "^4.0.1", + "karma": "^6.1.1", "karma-chrome-launcher": "^3.1.0", - "karma-mocha": "^1.3.0", - "karma-mocha-reporter": "^2.2.3", - "karma-webpack": "^4.0.0-rc.6", + "karma-mocha": "^2.0.1", + "karma-mocha-reporter": "^2.2.5", + "karma-webpack": "^5.0.0", "lint-staged": "^10.1.2", "load-perf": "^0.2.0", "markdownlint": "^0.19.0", @@ -120,6 +87,7 @@ "memfs": "^3.0.1", "mocha": "^7.1.1", "mocha-junit-reporter": "^1.23.0", + "node-polyfill-webpack-plugin": "^1.0.3", "npm-license": "^0.3.3", "nyc": "^15.0.1", "proxyquire": "^2.0.1", @@ -129,10 +97,26 @@ "shelljs": "^0.8.2", "sinon": "^9.0.1", "temp": "^0.9.0", - "webpack": "^4.35.0", - "webpack-cli": "^3.3.5", + "webpack": "^5.23.0", + "webpack-cli": "^4.5.0", "yorkie": "^2.0.0" }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "files": [ + "LICENSE", + "README.md", + "bin", + "conf", + "lib", + "messages" + ], + "funding": "https://opencollective.com/eslint", + "gitHooks": { + "pre-commit": "lint-staged" + }, + "homepage": "https://eslint.org", "keywords": [ "ast", "lint", @@ -141,7 +125,34 @@ "espree" ], "license": "MIT", - "engines": { - "node": "^10.12.0 || >=12.0.0" - } -} + "lint-staged": { + "*.js": [ + "eslint --fix", + "git add" + ], + "*.md": "markdownlint" + }, + "main": "./lib/api.js", + "name": "eslint", + "repository": { + "type": "git", + "url": "git+https://github.com/eslint/eslint.git" + }, + "scripts": { + "docs": "node Makefile.js docs", + "fix": "node Makefile.js lint -- fix", + "fuzz": "node Makefile.js fuzz", + "generate-alpharelease": "node Makefile.js generatePrerelease -- alpha", + "generate-betarelease": "node Makefile.js generatePrerelease -- beta", + "generate-rcrelease": "node Makefile.js generatePrerelease -- rc", + "generate-release": "node Makefile.js generateRelease", + "gensite": "node Makefile.js gensite", + "lint": "node Makefile.js lint", + "perf": "node Makefile.js perf", + "publish-release": "node Makefile.js publishRelease", + "test": "node Makefile.js test", + "test:cli": "mocha", + "webpack": "node Makefile.js webpack" + }, + "version": "7.22.0" +} \ No newline at end of file From 836cb679457b869bcc20f94cc17837238083dfac Mon Sep 17 00:00:00 2001 From: James Addison <jay@jp-hosting.net> Date: Tue, 9 Mar 2021 13:08:47 +0000 Subject: [PATCH 02/85] src: add .note.GNU-stack section This indicates to GNU binutils that it can unset the executable stack flag on the binary that it is building. PR-URL: https://github.com/nodejs/node/pull/37688 Refs: https://github.com/nodejs/node/issues/17933 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> --- src/large_pages/node_text_start.S | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/large_pages/node_text_start.S b/src/large_pages/node_text_start.S index 1609b254f0495a..3227b62464932c 100644 --- a/src/large_pages/node_text_start.S +++ b/src/large_pages/node_text_start.S @@ -1,3 +1,6 @@ +#if defined(__ELF__) +.section .note.GNU-stack,"",@progbits +#endif .text .align 0x2000 .global __node_text_start From 4487483d9dd679f52857e9c23be67f88517a5f2a Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Wed, 10 Mar 2021 19:17:44 -0800 Subject: [PATCH 03/85] test: fix test-fs-utimes on non-Y2K38 file systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move Y2K38-specific parts of test-fs-utimes to test-fs-utimes-y2K38.js. On non-Windows, check for Y2K38 support and skip if it is unsupported. Fixes: https://github.com/nodejs/node/issues/36591 PR-URL: https://github.com/nodejs/node/pull/37707 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> --- test/parallel/test-fs-utimes-y2K38.js | 63 +++++++++++++++++++++++++++ test/parallel/test-fs-utimes.js | 31 ------------- 2 files changed, 63 insertions(+), 31 deletions(-) create mode 100644 test/parallel/test-fs-utimes-y2K38.js diff --git a/test/parallel/test-fs-utimes-y2K38.js b/test/parallel/test-fs-utimes-y2K38.js new file mode 100644 index 00000000000000..06c5060c65d2fc --- /dev/null +++ b/test/parallel/test-fs-utimes-y2K38.js @@ -0,0 +1,63 @@ +'use strict'; +const common = require('../common'); + +if (common.isIBMi) { + common.skip('fs.utimesSync() currently fails on IBM i with Y2K38 values'); +} + +const tmpdir = require('../common/tmpdir'); +tmpdir.refresh(); + +const assert = require('assert'); +const fs = require('fs'); + +// Check for Y2K38 support. For Windows and AIX, assume it's there. Windows +// doesn't have `touch` and `date -r` which are used in the check for support. +// AIX lacks `date -r`. +if (!common.isWindows && !common.isAIX) { + const testFilePath = `${tmpdir.path}/y2k38-test`; + const testFileDate = '204001020304'; + const { spawnSync } = require('child_process'); + const touchResult = spawnSync('touch', + ['-t', testFileDate, testFilePath], + { encoding: 'utf8' }); + if (touchResult.status !== 0) { + common.skip('File system appears to lack Y2K38 support (touch failed)'); + } + + const dateResult = spawnSync('date', + ['-r', testFilePath, '+%Y%m%d%H%M'], + { encoding: 'utf8' }); + + assert.strictEqual(dateResult.status, 0); + if (dateResult.stdout.trim() !== testFileDate) { + common.skip('File system appears to lack Y2k38 support (date failed)'); + } +} + +// Ref: https://github.com/nodejs/node/issues/13255 +const path = `${tmpdir.path}/test-utimes-precision`; +fs.writeFileSync(path, ''); + +const Y2K38_mtime = 2 ** 31; +fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime); +const Y2K38_stats = fs.statSync(path); +assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime); + +if (common.isWindows) { + // This value would get converted to (double)1713037251359.9998 + const truncate_mtime = 1713037251360; + fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000); + const truncate_stats = fs.statSync(path); + assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime); + + // test Y2K38 for windows + // This value if treaded as a `signed long` gets converted to -2135622133469. + // POSIX systems stores timestamps in {long t_sec, long t_usec}. + // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv + // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement. + const overflow_mtime = 2159345162531; + fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000); + const overflow_stats = fs.statSync(path); + assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime); +} diff --git a/test/parallel/test-fs-utimes.js b/test/parallel/test-fs-utimes.js index b81c5b6bf62940..8f8286f8a3343f 100644 --- a/test/parallel/test-fs-utimes.js +++ b/test/parallel/test-fs-utimes.js @@ -156,37 +156,6 @@ function runTests(iter) { } } -// Ref: https://github.com/nodejs/node/issues/13255 -const path = `${tmpdir.path}/test-utimes-precision`; -fs.writeFileSync(path, ''); - -// Test Y2K38 for all platforms [except 'arm', 'OpenBSD', 'SunOS' and 'IBMi'] -if (!process.arch.includes('arm') && - !common.isOpenBSD && !common.isSunOS && !common.isIBMi) { - const Y2K38_mtime = 2 ** 31; - fs.utimesSync(path, Y2K38_mtime, Y2K38_mtime); - const Y2K38_stats = fs.statSync(path); - assert.strictEqual(Y2K38_stats.mtime.getTime() / 1000, Y2K38_mtime); -} - -if (common.isWindows) { - // This value would get converted to (double)1713037251359.9998 - const truncate_mtime = 1713037251360; - fs.utimesSync(path, truncate_mtime / 1000, truncate_mtime / 1000); - const truncate_stats = fs.statSync(path); - assert.strictEqual(truncate_stats.mtime.getTime(), truncate_mtime); - - // test Y2K38 for windows - // This value if treaded as a `signed long` gets converted to -2135622133469. - // POSIX systems stores timestamps in {long t_sec, long t_usec}. - // NTFS stores times in nanoseconds in a single `uint64_t`, so when libuv - // calculates (long)`uv_timespec_t.tv_sec` we get 2's complement. - const overflow_mtime = 2159345162531; - fs.utimesSync(path, overflow_mtime / 1000, overflow_mtime / 1000); - const overflow_stats = fs.statSync(path); - assert.strictEqual(overflow_stats.mtime.getTime(), overflow_mtime); -} - const expectTypeError = { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError' From a8edf1aafe24f3f943f61061ab978db35ffb9c81 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 13 Mar 2021 11:50:52 -0800 Subject: [PATCH 04/85] test: add known_issues test for #13683 Add a known_issues test for a known Windows issue. Refs: https://github.com/nodejs/node/issues/13683 PR-URL: https://github.com/nodejs/node/pull/37744 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> --- test/known_issues/known_issues.status | 16 ++++++++++++++++ .../test-path-posix-relative-on-windows.js | 10 ++++++++++ 2 files changed, 26 insertions(+) create mode 100644 test/known_issues/test-path-posix-relative-on-windows.js diff --git a/test/known_issues/known_issues.status b/test/known_issues/known_issues.status index e0f0a456089bf2..690e6e9840b77c 100644 --- a/test/known_issues/known_issues.status +++ b/test/known_issues/known_issues.status @@ -15,16 +15,32 @@ test-vm-timeout-escape-queuemicrotask: SKIP [$system==win32] [$system==linux] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==macos] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==solaris] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==freebsd] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$system==aix] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP [$arch==arm] # The Raspberry Pis are too slow to run this test. # See https://github.com/nodejs/build/issues/2227#issuecomment-608334574 test-crypto-authenticated-stream: SKIP +# Windows-specific test +test-path-posix-relative-on-windows: SKIP + +[$system==ibmi] +# Windows-specific test +test-path-posix-relative-on-windows: SKIP diff --git a/test/known_issues/test-path-posix-relative-on-windows.js b/test/known_issues/test-path-posix-relative-on-windows.js new file mode 100644 index 00000000000000..bcaaca8b18a1ef --- /dev/null +++ b/test/known_issues/test-path-posix-relative-on-windows.js @@ -0,0 +1,10 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); +const path = require('path'); + +// Refs: https://github.com/nodejs/node/issues/13683 + +const relativePath = path.posix.relative('a/b/c', '../../x'); +assert.match(relativePath, /^(\.\.\/){3,5}x$/); From 2e82a97520b264c3b5417506f2462025615d63c8 Mon Sep 17 00:00:00 2001 From: Jiawen Geng <technicalcute@gmail.com> Date: Tue, 16 Mar 2021 07:10:49 +0000 Subject: [PATCH 05/85] doc: add gyp maintain info PR-URL: https://github.com/nodejs/node/pull/37765 Reviewed-By: Ujjwal Sharma <ryzokuken@disroot.org> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Mary Marchini <oss@mmarchini.me> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- doc/api/n-api.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/api/n-api.md b/doc/api/n-api.md index 376af133b357d7..cb741363c300ee 100644 --- a/doc/api/n-api.md +++ b/doc/api/n-api.md @@ -160,9 +160,9 @@ the native addon. #### node-gyp -[node-gyp][] is a build system based on Google's [GYP][] tool and comes -bundled with npm. GYP, and therefore node-gyp, requires that Python be -installed. +[node-gyp][] is a build system based on the [gyp-next][] fork of +Google's [GYP][] tool and comes bundled with npm. GYP, and therefore node-gyp, +requires that Python be installed. Historically, node-gyp has been the tool of choice for building native addons. It has widespread adoption and documentation. However, some @@ -6087,6 +6087,7 @@ the add-on's file name during loading. [context-aware addons]: addons.md#addons_context_aware_addons [docs]: https://github.com/nodejs/node-addon-api#api-documentation [global scope]: globals.md +[gyp-next]: https://github.com/nodejs/gyp-next [module scope]: modules.md#modules_the_module_scope [node-gyp]: https://github.com/nodejs/node-gyp [node-pre-gyp]: https://github.com/mapbox/node-pre-gyp From dc9cd43d8f9663f435b7e776723114e82e970200 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Wed, 17 Mar 2021 11:00:57 -0700 Subject: [PATCH 06/85] buffer: implement btoa and atob Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37529 Fixes: https://github.com/nodejs/node/issues/3462 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> --- doc/api/buffer.md | 38 ++++++++++++++++++++++++++++++++++++++ lib/buffer.js | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 764762c986fa09..f31b8b298b02eb 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -3277,6 +3277,44 @@ While, the `Buffer` object is available as a global, there are additional `Buffer`-related APIs that are available only via the `buffer` module accessed using `require('buffer')`. +### `buffer.atob(data)` +<!-- YAML +added: REPLACEME +--> + +* `data` {any} The Base64-encoded input string. + +Decodes a string of Base64-encoded data into bytes, and encodes those bytes +into a string using Latin-1 (ISO-8859-1). + +The `data` may be any JavaScript-value that can be coerced into a string. + +**This function is only provided for compatibility with legacy web platform APIs +and should never be used in new code, because they use strings to represent +binary data and predate the introduction of typed arrays in JavaScript. +For code running using Node.js APIs, converting between base64-encoded strings +and binary data should be performed using `Buffer.from(str, 'base64')` and +`buf.toString('base64')`.** + +### `buffer.btoa(data)` +<!-- YAML +added: REPLACEME +--> + +* `data` {any} An ASCII (Latin1) string. + +Decodes a string into bytes using Latin-1 (ISO-8859), and encodes those bytes +into a string using Base64. + +The `data` may be any JavaScript-value that can be coerced into a string. + +**This function is only provided for compatibility with legacy web platform APIs +and should never be used in new code, because they use strings to represent +binary data and predate the introduction of typed arrays in JavaScript. +For code running using Node.js APIs, converting between base64-encoded strings +and binary data should be performed using `Buffer.from(str, 'base64')` and +`buf.toString('base64')`.** + ### `buffer.INSPECT_MAX_BYTES` <!-- YAML added: v0.5.4 diff --git a/lib/buffer.js b/lib/buffer.js index 893f5b1915c4d3..951805870c5870 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -1211,6 +1211,40 @@ if (internalBinding('config').hasIntl) { }; } +let DOMException; + +const lazyInvalidCharError = hideStackFrames((message, name) => { + if (DOMException === undefined) + DOMException = internalBinding('messaging').DOMException; + throw new DOMException('Invalid character', 'InvalidCharacterError'); +}); + +function btoa(input) { + // TODO(@jasnell): The implementation here has not been performance + // optimized in any way. + input = `${input}`; + for (let n = 0; n < input.length; n++) { + if (input[n].charCodeAt(0) > 0xff) + lazyInvalidCharError(); + } + const buf = Buffer.from(input, 'latin1'); + return buf.toString('base64'); +} + +const kBase64Digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + +function atob(input) { + // TODO(@jasnell): The implementation here has not been performance + // optimized in any way. + input = `${input}`; + for (let n = 0; n < input.length; n++) { + if (!kBase64Digits.includes(input[n])) + lazyInvalidCharError(); + } + return Buffer.from(input, 'base64').toString('latin1'); +} + module.exports = { Blob, Buffer, @@ -1218,7 +1252,9 @@ module.exports = { transcode, // Legacy kMaxLength, - kStringMaxLength + kStringMaxLength, + btoa, + atob, }; ObjectDefineProperties(module.exports, { From 505f9c95d119895bbc11baba3132508ed1db9a77 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Wed, 17 Mar 2021 11:27:27 -0700 Subject: [PATCH 07/85] test: app atob web platform tests Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37529 Fixes: https://github.com/nodejs/node/issues/3462 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> --- test/fixtures/wpt/README.md | 1 + .../wpt/html/webappapis/atob/base64.any.js | 163 ++++++++++++++++++ test/fixtures/wpt/versions.json | 4 + test/wpt/status/html/webappapis/atob.json | 5 + test/wpt/test-atob.js | 19 ++ 5 files changed, 192 insertions(+) create mode 100644 test/fixtures/wpt/html/webappapis/atob/base64.any.js create mode 100644 test/wpt/status/html/webappapis/atob.json create mode 100644 test/wpt/test-atob.js diff --git a/test/fixtures/wpt/README.md b/test/fixtures/wpt/README.md index 52d4ccc8294a6c..9474d8a1963b7e 100644 --- a/test/fixtures/wpt/README.md +++ b/test/fixtures/wpt/README.md @@ -16,6 +16,7 @@ Last update: - encoding: https://github.com/web-platform-tests/wpt/tree/35f70910d3/encoding - FileAPI: https://github.com/web-platform-tests/wpt/tree/3b279420d4/FileAPI - hr-time: https://github.com/web-platform-tests/wpt/tree/9910784394/hr-time +- html/webappapis/atob: https://github.com/web-platform-tests/wpt/tree/f267e1dca6/html/webappapis/atob - html/webappapis/microtask-queuing: https://github.com/web-platform-tests/wpt/tree/2c5c3c4c27/html/webappapis/microtask-queuing - html/webappapis/timers: https://github.com/web-platform-tests/wpt/tree/5873f2d8f1/html/webappapis/timers - interfaces: https://github.com/web-platform-tests/wpt/tree/79fa4cf76e/interfaces diff --git a/test/fixtures/wpt/html/webappapis/atob/base64.any.js b/test/fixtures/wpt/html/webappapis/atob/base64.any.js new file mode 100644 index 00000000000000..7f433f4d8a9ee2 --- /dev/null +++ b/test/fixtures/wpt/html/webappapis/atob/base64.any.js @@ -0,0 +1,163 @@ +/** + * btoa() as defined by the HTML5 spec, which mostly just references RFC4648. + */ +function mybtoa(s) { + // String conversion as required by WebIDL. + s = String(s); + + // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the + // method's first argument contains any character whose code point is + // greater than U+00FF." + for (var i = 0; i < s.length; i++) { + if (s.charCodeAt(i) > 255) { + return "INVALID_CHARACTER_ERR"; + } + } + + var out = ""; + for (var i = 0; i < s.length; i += 3) { + var groupsOfSix = [undefined, undefined, undefined, undefined]; + groupsOfSix[0] = s.charCodeAt(i) >> 2; + groupsOfSix[1] = (s.charCodeAt(i) & 0x03) << 4; + if (s.length > i + 1) { + groupsOfSix[1] |= s.charCodeAt(i + 1) >> 4; + groupsOfSix[2] = (s.charCodeAt(i + 1) & 0x0f) << 2; + } + if (s.length > i + 2) { + groupsOfSix[2] |= s.charCodeAt(i + 2) >> 6; + groupsOfSix[3] = s.charCodeAt(i + 2) & 0x3f; + } + for (var j = 0; j < groupsOfSix.length; j++) { + if (typeof groupsOfSix[j] == "undefined") { + out += "="; + } else { + out += btoaLookup(groupsOfSix[j]); + } + } + } + return out; +} + +/** + * Lookup table for mybtoa(), which converts a six-bit number into the + * corresponding ASCII character. + */ +function btoaLookup(idx) { + if (idx < 26) { + return String.fromCharCode(idx + 'A'.charCodeAt(0)); + } + if (idx < 52) { + return String.fromCharCode(idx - 26 + 'a'.charCodeAt(0)); + } + if (idx < 62) { + return String.fromCharCode(idx - 52 + '0'.charCodeAt(0)); + } + if (idx == 62) { + return '+'; + } + if (idx == 63) { + return '/'; + } + // Throw INVALID_CHARACTER_ERR exception here -- won't be hit in the tests. +} + +function btoaException(input) { + input = String(input); + for (var i = 0; i < input.length; i++) { + if (input.charCodeAt(i) > 255) { + return true; + } + } + return false; +} + +function testBtoa(input) { + // "The btoa() method must throw an INVALID_CHARACTER_ERR exception if the + // method's first argument contains any character whose code point is + // greater than U+00FF." + var normalizedInput = String(input); + for (var i = 0; i < normalizedInput.length; i++) { + if (normalizedInput.charCodeAt(i) > 255) { + assert_throws_dom("InvalidCharacterError", function() { btoa(input); }, + "Code unit " + i + " has value " + normalizedInput.charCodeAt(i) + ", which is greater than 255"); + return; + } + } + assert_equals(btoa(input), mybtoa(input)); + assert_equals(atob(btoa(input)), String(input), "atob(btoa(input)) must be the same as String(input)"); +} + +var tests = ["עברית", "", "ab", "abc", "abcd", "abcde", + // This one is thrown in because IE9 seems to fail atob(btoa()) on it. Or + // possibly to fail btoa(). I actually can't tell what's happening here, + // but it doesn't hurt. + "\xff\xff\xc0", + // Is your DOM implementation binary-safe? + "\0a", "a\0b", + // WebIDL tests. + undefined, null, 7, 12, 1.5, true, false, NaN, +Infinity, -Infinity, 0, -0, + {toString: function() { return "foo" }}, +]; +for (var i = 0; i < 258; i++) { + tests.push(String.fromCharCode(i)); +} +tests.push(String.fromCharCode(10000)); +tests.push(String.fromCharCode(65534)); +tests.push(String.fromCharCode(65535)); + +// This is supposed to be U+10000. +tests.push(String.fromCharCode(0xd800, 0xdc00)); +tests = tests.map( + function(elem) { + var expected = mybtoa(elem); + if (expected === "INVALID_CHARACTER_ERR") { + return ["btoa(" + format_value(elem) + ") must raise INVALID_CHARACTER_ERR", elem]; + } + return ["btoa(" + format_value(elem) + ") == " + format_value(mybtoa(elem)), elem]; + } +); + +var everything = ""; +for (var i = 0; i < 256; i++) { + everything += String.fromCharCode(i); +} +tests.push(["btoa(first 256 code points concatenated)", everything]); + +generate_tests(testBtoa, tests); + +promise_test(() => fetch("../../../fetch/data-urls/resources/base64.json").then(res => res.json()).then(runAtobTests), "atob() setup."); + +const idlTests = [ + [undefined, null], + [null, [158, 233, 101]], + [7, null], + [12, [215]], + [1.5, null], + [true, [182, 187]], + [false, null], + [NaN, [53, 163]], + [+Infinity, [34, 119, 226, 158, 43, 114]], + [-Infinity, null], + [0, null], + [-0, null], + [{toString: function() { return "foo" }}, [126, 138]], + [{toString: function() { return "abcd" }}, [105, 183, 29]] +]; + +function runAtobTests(tests) { + const allTests = tests.concat(idlTests); + for(let i = 0; i < allTests.length; i++) { + const input = allTests[i][0], + output = allTests[i][1]; + test(() => { + if(output === null) { + assert_throws_dom("InvalidCharacterError", () => globalThis.atob(input)); + } else { + const result = globalThis.atob(input); + for(let ii = 0; ii < output.length; ii++) { + assert_equals(result.charCodeAt(ii), output[ii]); + } + } + }, "atob(" + format_value(input) + ")"); + } +} diff --git a/test/fixtures/wpt/versions.json b/test/fixtures/wpt/versions.json index c533088b4693b8..800f07c607bf4b 100644 --- a/test/fixtures/wpt/versions.json +++ b/test/fixtures/wpt/versions.json @@ -23,6 +23,10 @@ "commit": "9910784394858a8e34d9eb4e5d00788765abf837", "path": "hr-time" }, + "html/webappapis/atob": { + "commit": "f267e1dca6f57a9f4d69f32a6920adfdb3268656", + "path": "html/webappapis/atob" + }, "html/webappapis/microtask-queuing": { "commit": "2c5c3c4c27d27a419c1fdba3e9879c2d22037074", "path": "html/webappapis/microtask-queuing" diff --git a/test/wpt/status/html/webappapis/atob.json b/test/wpt/status/html/webappapis/atob.json new file mode 100644 index 00000000000000..65deda7312d426 --- /dev/null +++ b/test/wpt/status/html/webappapis/atob.json @@ -0,0 +1,5 @@ +{ + "base64.any.js": { + "fail": "promise_test: Unhandled rejection with value: object \"Error: ENOENT: no such file or directory, open '/root/node/node/fetch/data-urls/resources/base64.json'\"" + } +} diff --git a/test/wpt/test-atob.js b/test/wpt/test-atob.js new file mode 100644 index 00000000000000..51fa27e36d3f2f --- /dev/null +++ b/test/wpt/test-atob.js @@ -0,0 +1,19 @@ +'use strict'; + +require('../common'); +const { WPTRunner } = require('../common/wpt'); + +const runner = new WPTRunner('html/webappapis/atob'); + +// Needed to access to DOMException. +runner.setFlags(['--expose-internals']); + +// Set a script that will be executed in the worker before running the tests. +runner.setInitScript(` + const { internalBinding } = require('internal/test/binding'); + const { atob, btoa } = require('buffer'); + const { DOMException } = internalBinding('messaging'); + global.DOMException = DOMException; +`); + +runner.runJsTests(); From bfe3f21ee086b11a645f05fbde6ddb9dd1a7a7f9 Mon Sep 17 00:00:00 2001 From: Filip Skokan <panva.ip@gmail.com> Date: Thu, 18 Mar 2021 11:05:25 +0100 Subject: [PATCH 08/85] crypto: fix crypto.verify callback invocation with a private keyobject fixes https://github.com/nodejs/node/issues/37794 PR-URL: https://github.com/nodejs/node/pull/37795 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/crypto/crypto_sig.cc | 2 +- test/parallel/test-crypto-async-sign-verify.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index cb1ce31be73700..24b1a7f4315f76 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -847,7 +847,7 @@ bool SignTraits::DeriveBits( } break; case SignConfiguration::kVerify: - CHECK_EQ(params.key->GetKeyType(), kKeyTypePublic); + CHECK_NE(params.key->GetKeyType(), kKeyTypeSecret); if (!EVP_DigestVerifyInit( context.get(), &ctx, diff --git a/test/parallel/test-crypto-async-sign-verify.js b/test/parallel/test-crypto-async-sign-verify.js index 4980c87673bdb5..5cfce87ca2e7ce 100644 --- a/test/parallel/test-crypto-async-sign-verify.js +++ b/test/parallel/test-crypto-async-sign-verify.js @@ -52,7 +52,9 @@ function test( })); } - for (const key of [publicPem, publicKey, publicDer]) { + const verifyInputs = [ + publicPem, publicKey, publicDer, privatePem, privateKey, privateDer]; + for (const key of verifyInputs) { crypto.verify(algorithm, data, key, expected, common.mustSucceed( (verified) => assert.strictEqual(verified, true))); From d1a3e0efb65d4a4fbe15b3d255df535e22048dbf Mon Sep 17 00:00:00 2001 From: Gabriel Schulhof <gabrielschulhof@gmail.com> Date: Wed, 3 Mar 2021 20:37:27 -0800 Subject: [PATCH 09/85] node-api: stop ref gc during environment teardown A gc may happen during environment teardown. Thus, during finalization initiated by environment teardown we must remove the V8 finalizer before calling the Node-API finalizer. Fixes: https://github.com/nodejs/node/issues/37236 PR-URL: https://github.com/nodejs/node/pull/37616 Reviewed-By: Chengzhong Wu <legendecas@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> --- src/js_native_api_v8.cc | 28 +++++++++++++- test/node-api/test_env_teardown_gc/binding.c | 37 +++++++++++++++++++ .../node-api/test_env_teardown_gc/binding.gyp | 8 ++++ test/node-api/test_env_teardown_gc/test.js | 14 +++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 test/node-api/test_env_teardown_gc/binding.c create mode 100644 test/node-api/test_env_teardown_gc/binding.gyp create mode 100644 test/node-api/test_env_teardown_gc/test.js diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 8275f37677d1a9..602ded21569cd7 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -270,6 +270,20 @@ class RefBase : protected Finalizer, RefTracker { protected: inline void Finalize(bool is_env_teardown = false) override { + // In addition to being called during environment teardown, this method is + // also the entry point for the garbage collector. During environment + // teardown we have to remove the garbage collector's reference to this + // method so that, if, as part of the user's callback, JS gets executed, + // resulting in a garbage collection pass, this method is not re-entered as + // part of that pass, because that'll cause a double free (as seen in + // https://github.com/nodejs/node/issues/37236). + // + // Since this class does not have access to the V8 persistent reference, + // this method is overridden in the `Reference` class below. Therein the + // weak callback is removed, ensuring that the garbage collector does not + // re-enter this method, and the method chains up to continue the process of + // environment-teardown-induced finalization. + // During environment teardown we have to convert a strong reference to // a weak reference to force the deferring behavior if the user's finalizer // happens to delete this reference so that the code in this function that @@ -278,9 +292,10 @@ class RefBase : protected Finalizer, RefTracker { if (is_env_teardown && RefCount() > 0) _refcount = 0; if (_finalize_callback != nullptr) { - _env->CallFinalizer(_finalize_callback, _finalize_data, _finalize_hint); // This ensures that we never call the finalizer twice. + napi_finalize fini = _finalize_callback; _finalize_callback = nullptr; + _env->CallFinalizer(fini, _finalize_data, _finalize_hint); } // this is safe because if a request to delete the reference @@ -355,6 +370,17 @@ class Reference : public RefBase { } } + protected: + inline void Finalize(bool is_env_teardown = false) override { + // During env teardown, `~napi_env()` alone is responsible for finalizing. + // Thus, we don't want any stray gc passes to trigger a second call to + // `Finalize()`, so let's reset the persistent here. + if (is_env_teardown) _persistent.ClearWeak(); + + // Chain up to perform the rest of the finalization. + RefBase::Finalize(is_env_teardown); + } + private: // The N-API finalizer callback may make calls into the engine. V8's heap is // not in a consistent state during the weak callback, and therefore it does diff --git a/test/node-api/test_env_teardown_gc/binding.c b/test/node-api/test_env_teardown_gc/binding.c new file mode 100644 index 00000000000000..e3ba1cee3edc37 --- /dev/null +++ b/test/node-api/test_env_teardown_gc/binding.c @@ -0,0 +1,37 @@ +#include <stdlib.h> +#include <node_api.h> +#include "../../js-native-api/common.h" + +static void MyObject_fini(napi_env env, void* data, void* hint) { + napi_ref* ref = data; + napi_value global; + napi_value cleanup; + NODE_API_CALL_RETURN_VOID(env, napi_get_global(env, &global)); + NODE_API_CALL_RETURN_VOID( + env, napi_get_named_property(env, global, "cleanup", &cleanup)); + napi_status status = napi_call_function(env, global, cleanup, 0, NULL, NULL); + // We may not be allowed to call into JS, in which case a pending exception + // will be returned. + NODE_API_ASSERT_RETURN_VOID(env, + status == napi_ok || status == napi_pending_exception, + "Unexpected status for napi_call_function"); + NODE_API_CALL_RETURN_VOID(env, napi_delete_reference(env, *ref)); + free(ref); +} + +static napi_value MyObject(napi_env env, napi_callback_info info) { + napi_value js_this; + napi_ref* ref = malloc(sizeof(*ref)); + NODE_API_CALL(env, napi_get_cb_info(env, info, NULL, NULL, &js_this, NULL)); + NODE_API_CALL(env, napi_wrap(env, js_this, ref, MyObject_fini, NULL, ref)); + return NULL; +} + +NAPI_MODULE_INIT() { + napi_value ctor; + NODE_API_CALL( + env, napi_define_class( + env, "MyObject", NAPI_AUTO_LENGTH, MyObject, NULL, 0, NULL, &ctor)); + NODE_API_CALL(env, napi_set_named_property(env, exports, "MyObject", ctor)); + return exports; +} diff --git a/test/node-api/test_env_teardown_gc/binding.gyp b/test/node-api/test_env_teardown_gc/binding.gyp new file mode 100644 index 00000000000000..413621ade335a1 --- /dev/null +++ b/test/node-api/test_env_teardown_gc/binding.gyp @@ -0,0 +1,8 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.c' ] + } + ] +} diff --git a/test/node-api/test_env_teardown_gc/test.js b/test/node-api/test_env_teardown_gc/test.js new file mode 100644 index 00000000000000..8f1c4f4038fbab --- /dev/null +++ b/test/node-api/test_env_teardown_gc/test.js @@ -0,0 +1,14 @@ +'use strict'; +// Flags: --expose-gc + +process.env.NODE_TEST_KNOWN_GLOBALS = 0; + +const common = require('../../common'); +const binding = require(`./build/${common.buildType}/binding`); + +global.it = new binding.MyObject(); + +global.cleanup = () => { + delete global.it; + global.gc(); +}; From 95aa032413800a660c0afffb205114e808619722 Mon Sep 17 00:00:00 2001 From: Nitzan Uziely <nitzan@testim.io> Date: Sun, 7 Feb 2021 01:39:54 +0200 Subject: [PATCH 10/85] child_process: add timeout to spawn and fork Add support for timeout to spawn and fork. Fixes: https://github.com/nodejs/node/issues/27639 PR-URL: https://github.com/nodejs/node/pull/37256 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> --- doc/api/child_process.md | 18 +++++-- lib/child_process.js | 29 +++++++++-- ...-child-process-fork-timeout-kill-signal.js | 50 +++++++++++++++++++ ...child-process-spawn-timeout-kill-signal.js | 50 +++++++++++++++++++ 4 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 test/parallel/test-child-process-fork-timeout-kill-signal.js create mode 100644 test/parallel/test-child-process-spawn-timeout-kill-signal.js diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 677f3a5c190554..77467cc6573064 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -374,6 +374,9 @@ controller.abort(); <!-- YAML added: v0.5.0 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37256 + description: timeout was added. - version: v15.11.0 pr-url: https://github.com/nodejs/node/pull/37325 description: killSignal for AbortSignal was added. @@ -410,8 +413,8 @@ changes: See [Advanced serialization][] for more details. **Default:** `'json'`. * `signal` {AbortSignal} Allows closing the child process using an AbortSignal. - * `killSignal` {string} The signal value to be used when the spawned - process will be killed by the abort signal. **Default:** `'SIGTERM'`. + * `killSignal` {string|integer} The signal value to be used when the spawned + process will be killed by timeout or abort signal. **Default:** `'SIGTERM'`. * `silent` {boolean} If `true`, stdin, stdout, and stderr of the child will be piped to the parent, otherwise they will be inherited from the parent, see the `'pipe'` and `'inherit'` options for [`child_process.spawn()`][]'s @@ -423,6 +426,8 @@ changes: * `uid` {number} Sets the user identity of the process (see setuid(2)). * `windowsVerbatimArguments` {boolean} No quoting or escaping of arguments is done on Windows. Ignored on Unix. **Default:** `false`. + * `timeout` {number} In milliseconds the maximum amount of time the process + is allowed to run. **Default:** `undefined`. * Returns: {ChildProcess} The `child_process.fork()` method is a special case of @@ -478,6 +483,9 @@ if (process.argv[2] === 'child') { <!-- YAML added: v0.1.90 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37256 + description: timeout was added. - version: v15.11.0 pr-url: https://github.com/nodejs/node/pull/37325 description: killSignal for AbortSignal was added. @@ -528,8 +536,10 @@ changes: normally be created on Windows systems. **Default:** `false`. * `signal` {AbortSignal} allows aborting the child process using an AbortSignal. - * `killSignal` {string} The signal value to be used when the spawned - process will be killed by the abort signal. **Default:** `'SIGTERM'`. + * `timeout` {number} In milliseconds the maximum amount of time the process + is allowed to run. **Default:** `undefined`. + * `killSignal` {string|integer} The signal value to be used when the spawned + process will be killed by timeout or abort signal. **Default:** `'SIGTERM'`. * Returns: {ChildProcess} diff --git a/lib/child_process.js b/lib/child_process.js index 57b5eaa71b1bb5..18996f8a5643ce 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -599,15 +599,14 @@ function abortChildProcess(child, killSignal) { function spawn(file, args, options) { - const child = new ChildProcess(); options = normalizeSpawnArguments(file, args, options); + validateTimeout(options.timeout, 'options.timeout'); + validateAbortSignal(options.signal, 'options.signal'); + const killSignal = sanitizeKillSignal(options.killSignal); + const child = new ChildProcess(); if (options.signal) { const signal = options.signal; - // Validate signal, if present - validateAbortSignal(signal, 'options.signal'); - const killSignal = sanitizeKillSignal(options.killSignal); - // Do nothing and throw if already aborted if (signal.aborted) { onAbortListener(); } else { @@ -626,6 +625,26 @@ function spawn(file, args, options) { debug('spawn', options); child.spawn(options); + if (options.timeout > 0) { + let timeoutId = setTimeout(() => { + if (timeoutId) { + try { + child.kill(killSignal); + } catch (err) { + child.emit('error', err); + } + timeoutId = null; + } + }, options.timeout); + + child.once('exit', () => { + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + }); + } + return child; } diff --git a/test/parallel/test-child-process-fork-timeout-kill-signal.js b/test/parallel/test-child-process-fork-timeout-kill-signal.js new file mode 100644 index 00000000000000..ef08d4b12ab94a --- /dev/null +++ b/test/parallel/test-child-process-fork-timeout-kill-signal.js @@ -0,0 +1,50 @@ +'use strict'; + +const { mustCall } = require('../common'); +const { strictEqual, throws } = require('assert'); +const fixtures = require('../common/fixtures'); +const { fork } = require('child_process'); +const { getEventListeners } = require('events'); + +{ + // Verify default signal + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + timeout: 5, + }); + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGTERM'))); +} + +{ + // Verify correct signal + closes after at least 4 ms. + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + timeout: 5, + killSignal: 'SIGKILL', + }); + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGKILL'))); +} + +{ + // Verify timeout verification + throws(() => fork(fixtures.path('child-process-stay-alive-forever.js'), { + timeout: 'badValue', + }), /ERR_OUT_OF_RANGE/); + + throws(() => fork(fixtures.path('child-process-stay-alive-forever.js'), { + timeout: {}, + }), /ERR_OUT_OF_RANGE/); +} + +{ + // Verify abort signal gets unregistered + const signal = new EventTarget(); + signal.aborted = false; + + const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { + timeout: 6, + signal, + }); + strictEqual(getEventListeners(signal, 'abort').length, 1); + cp.on('exit', mustCall(() => { + strictEqual(getEventListeners(signal, 'abort').length, 0); + })); +} diff --git a/test/parallel/test-child-process-spawn-timeout-kill-signal.js b/test/parallel/test-child-process-spawn-timeout-kill-signal.js new file mode 100644 index 00000000000000..59a974d0e0b448 --- /dev/null +++ b/test/parallel/test-child-process-spawn-timeout-kill-signal.js @@ -0,0 +1,50 @@ +'use strict'; + +const { mustCall } = require('../common'); +const { strictEqual, throws } = require('assert'); +const fixtures = require('../common/fixtures'); +const { spawn } = require('child_process'); +const { getEventListeners } = require('events'); + +const aliveForeverFile = 'child-process-stay-alive-forever.js'; +{ + // Verify default signal + closes + const cp = spawn(process.execPath, [fixtures.path(aliveForeverFile)], { + timeout: 5, + }); + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGTERM'))); +} + +{ + // Verify SIGKILL signal + closes + const cp = spawn(process.execPath, [fixtures.path(aliveForeverFile)], { + timeout: 6, + killSignal: 'SIGKILL', + }); + cp.on('exit', mustCall((code, ks) => strictEqual(ks, 'SIGKILL'))); +} + +{ + // Verify timeout verification + throws(() => spawn(process.execPath, [fixtures.path(aliveForeverFile)], { + timeout: 'badValue', + }), /ERR_OUT_OF_RANGE/); + + throws(() => spawn(process.execPath, [fixtures.path(aliveForeverFile)], { + timeout: {}, + }), /ERR_OUT_OF_RANGE/); +} + +{ + // Verify abort signal gets unregistered + const controller = new AbortController(); + const { signal } = controller; + const cp = spawn(process.execPath, [fixtures.path(aliveForeverFile)], { + timeout: 6, + signal, + }); + strictEqual(getEventListeners(signal, 'abort').length, 1); + cp.on('exit', mustCall(() => { + strictEqual(getEventListeners(signal, 'abort').length, 0); + })); +} From 6ed9e0bd81bf0cfd4931462cc4a26df91b443b98 Mon Sep 17 00:00:00 2001 From: marsonya <akhil.marsonya27@gmail.com> Date: Mon, 15 Mar 2021 15:53:53 +0530 Subject: [PATCH 11/85] test: improve test-arm-math-illegal-instruction Instead of writing each Math function and keeping track, loop over Math functions and test each one of them. PR-URL: https://github.com/nodejs/node/pull/37670 Reviewed-By: Darshan Sen <raisinten@gmail.com> --- .../test-arm-math-illegal-instruction.js | 34 ++++--------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/test/parallel/test-arm-math-illegal-instruction.js b/test/parallel/test-arm-math-illegal-instruction.js index 2c4cdd263018a0..4bf881d1b385a3 100644 --- a/test/parallel/test-arm-math-illegal-instruction.js +++ b/test/parallel/test-arm-math-illegal-instruction.js @@ -6,30 +6,10 @@ require('../common'); // See https://github.com/nodejs/node/issues/1376 // and https://code.google.com/p/v8/issues/detail?id=4019 -Math.abs(-0.5); -Math.acos(-0.5); -Math.acosh(-0.5); -Math.asin(-0.5); -Math.asinh(-0.5); -Math.atan(-0.5); -Math.atanh(-0.5); -Math.cbrt(-0.5); -Math.ceil(-0.5); -Math.cos(-0.5); -Math.cosh(-0.5); -Math.exp(-0.5); -Math.expm1(-0.5); -Math.floor(-0.5); -Math.fround(-0.5); -Math.log(-0.5); -Math.log10(-0.5); -Math.log1p(-0.5); -Math.log2(-0.5); -Math.round(-0.5); -Math.sign(-0.5); -Math.sin(-0.5); -Math.sinh(-0.5); -Math.sqrt(-0.5); -Math.tan(-0.5); -Math.tanh(-0.5); -Math.trunc(-0.5); +// Iterate over all Math functions +Object.getOwnPropertyNames(Math).forEach((functionName) => { + if (!/[A-Z]/.test(functionName)) { + // The function names don't have capital letters. + Math[functionName](-0.5); + } +}); From 50f3ad19461c40ba4f88ce56d4d854cb60f61324 Mon Sep 17 00:00:00 2001 From: Ian Kerins <ianskerins@gmail.com> Date: Thu, 11 Mar 2021 14:20:38 -0500 Subject: [PATCH 12/85] doc: fix typo in stream docs PR-URL: https://github.com/nodejs/node/pull/37716 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> --- doc/api/stream.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/stream.md b/doc/api/stream.md index b49110af53e2e9..26a04be24c6f35 100644 --- a/doc/api/stream.md +++ b/doc/api/stream.md @@ -2197,7 +2197,7 @@ user programs. #### `writable._writev(chunks, callback)` * `chunks` {Object[]} The data to be written. The value is an array of {Object} - that each represent a discreet chunk of data to write. The properties of + that each represent a discrete chunk of data to write. The properties of these objects are: * `chunk` {Buffer|string} A buffer instance or string containing the data to be written. The `chunk` will be a string if the `Writable` was created with From c4183bbea4b67c5c9408701f863f69ee18163acd Mon Sep 17 00:00:00 2001 From: dbachko <dbachko@gmail.com> Date: Sat, 13 Mar 2021 10:54:10 +0200 Subject: [PATCH 13/85] doc: fix AbortError example for timers PR-URL: https://github.com/nodejs/node/pull/37738 Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> --- doc/api/timers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/timers.md b/doc/api/timers.md index 55a948ecb58603..77501fd9fab149 100644 --- a/doc/api/timers.md +++ b/doc/api/timers.md @@ -266,7 +266,7 @@ const signal = ac.signal; setImmediatePromise('foobar', { signal }) .then(console.log) .catch((err) => { - if (err.message === 'AbortError') + if (err.name === 'AbortError') console.log('The immediate was aborted'); }); @@ -285,7 +285,7 @@ const signal = ac.signal; setTimeoutPromise(1000, 'foobar', { signal }) .then(console.log) .catch((err) => { - if (err.message === 'AbortError') + if (err.name === 'AbortError') console.log('The timeout was aborted'); }); From 3a440ecdf8af4b4e8f21fb3e55e522cb2e540113 Mon Sep 17 00:00:00 2001 From: marsonya <akhil.marsonya27@gmail.com> Date: Sat, 13 Mar 2021 21:46:08 +0530 Subject: [PATCH 14/85] lib: fix typo in lib/internal/crypto/certificate.js 'referred' was spelled as 'refered' PR-URL: https://github.com/nodejs/node/pull/37741 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> --- lib/internal/crypto/certificate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/crypto/certificate.js b/lib/internal/crypto/certificate.js index 000122f345b4fe..825153a4ac325a 100644 --- a/lib/internal/crypto/certificate.js +++ b/lib/internal/crypto/certificate.js @@ -11,7 +11,7 @@ const { } = require('internal/crypto/util'); // The functions contained in this file cover the SPKAC format -// (also refered to as Netscape SPKI). A general description of +// (also referred to as Netscape SPKI). A general description of // the format can be found at https://en.wikipedia.org/wiki/SPKAC function verifySpkac(spkac, encoding) { From 6e2b60931ce416e1939c40928bfc3bf1f2972986 Mon Sep 17 00:00:00 2001 From: marsonya <akhil.marsonya27@gmail.com> Date: Tue, 16 Mar 2021 23:07:18 +0530 Subject: [PATCH 15/85] lib: fix typo in internal/modules/esm/module_job.js PR-URL: https://github.com/nodejs/node/pull/37773 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Anto Aravinth <anto.aravinth.cse@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> --- lib/internal/modules/esm/module_job.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index 6cf968553bae6d..ebae1940502b4b 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -120,7 +120,7 @@ class ModuleJob { const importStatement = splitStack[1]; // TODO(@ctavan): The original error stack only provides the single // line which causes the error. For multi-line import statements we - // cannot generate an equivalent object descructuring assignment by + // cannot generate an equivalent object destructuring assignment by // just parsing the error stack. const oneLineNamedImports = StringPrototypeMatch(importStatement, /{.*}/); const destructuringAssignment = oneLineNamedImports && From 2227aa61ead2dadedce2e899f066550d792e646a Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Tue, 9 Mar 2021 13:29:20 -0800 Subject: [PATCH 16/85] tools: partially detect quic support in shared_openssl If the shared openssl does not have the `OPENSSL_INFO_QUIC` define, then it definitely does not have the QUIC apis. This is only a partially accurate check because it does not detect if the shared openssl was actually *built* without the OPENSSL_NO_QUIC define set. Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37682 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> --- configure.py | 5 +++++ tools/getsharedopensslhasquic.py | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tools/getsharedopensslhasquic.py diff --git a/configure.py b/configure.py index 096d40a13abe91..bb0f8019da69c1 100755 --- a/configure.py +++ b/configure.py @@ -38,6 +38,7 @@ sys.path.insert(0, 'tools') import getmoduleversion import getnapibuildversion +import getsharedopensslhasquic from gyp_node import run_gyp # parse our options @@ -1348,6 +1349,7 @@ def configure_openssl(o): variables['node_shared_openssl'] = b(options.shared_openssl) variables['openssl_is_fips'] = b(options.openssl_is_fips) variables['openssl_fips'] = '' + variables['openssl_quic'] = b(True) if options.openssl_no_asm: variables['openssl_no_asm'] = 1 @@ -1403,6 +1405,9 @@ def without_ssl_error(option): if options.openssl_fips or options.openssl_fips == '': error('FIPS is not supported in this version of Node.js') + if options.shared_openssl: + variables['openssl_quic'] = b(getsharedopensslhasquic.get_has_quic(options.__dict__['shared_openssl_includes'])) + configure_library('openssl', o) diff --git a/tools/getsharedopensslhasquic.py b/tools/getsharedopensslhasquic.py new file mode 100644 index 00000000000000..549a0eacdaee94 --- /dev/null +++ b/tools/getsharedopensslhasquic.py @@ -0,0 +1,19 @@ +from __future__ import print_function +import os +import re + +def get_has_quic(include_path): + openssl_crypto_h = os.path.join( + include_path, + 'openssl', + 'crypto.h') + + f = open(openssl_crypto_h) + + regex = '^#\s*define OPENSSL_INFO_QUIC' + + for line in f: + if (re.match(regex, line)): + return True + + return False From 9e6aa190e39163bdc89b2ce98d28eeb36b122e23 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Tue, 9 Mar 2021 13:50:08 -0800 Subject: [PATCH 17/85] deps: add ngtcp2 and nghttp3 Reintroduces the ngtcp2 and nghttp3 dependencies, building those by default if the vendored-in openssl (with QUIC support) is used or the shared openssl defines `OPENSSL_INFO_QUIC`. Upates the version metadata to reflect whether ngtcp2 and nghttp3 are present. ngtcp2 as of https://github.com/ngtcp2/ngtcp2/commit/2381f7f7b633602d83ad3de997153df730b1f649 nghttp3 as of https://github.com/ngtcp2/nghttp3/commit/66ad30f0a8f5164f87fbc83b37628f8f6d9ba608 Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37682 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> --- LICENSE | 52 + configure.py | 48 + deps/ngtcp2/.gitignore | 8 + deps/ngtcp2/LICENSE_nghttp3 | 22 + deps/ngtcp2/LICENSE_ngtcp2 | 22 + deps/ngtcp2/README.md | 45 + deps/ngtcp2/config.h | 38 + .../nghttp3/lib/includes/nghttp3/nghttp3.h | 2666 ++++ .../nghttp3/lib/includes/nghttp3/version.h | 46 + deps/ngtcp2/nghttp3/lib/nghttp3_buf.c | 90 + deps/ngtcp2/nghttp3/lib/nghttp3_buf.h | 74 + deps/ngtcp2/nghttp3/lib/nghttp3_conn.c | 3523 +++++ deps/ngtcp2/nghttp3/lib/nghttp3_conn.h | 289 + deps/ngtcp2/nghttp3/lib/nghttp3_conv.c | 134 + deps/ngtcp2/nghttp3/lib/nghttp3_conv.h | 221 + deps/ngtcp2/nghttp3/lib/nghttp3_debug.c | 61 + deps/ngtcp2/nghttp3/lib/nghttp3_debug.h | 44 + deps/ngtcp2/nghttp3/lib/nghttp3_err.c | 126 + deps/ngtcp2/nghttp3/lib/nghttp3_err.h | 34 + deps/ngtcp2/nghttp3/lib/nghttp3_frame.c | 218 + deps/ngtcp2/nghttp3/lib/nghttp3_frame.h | 243 + deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c | 130 + deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h | 104 + deps/ngtcp2/nghttp3/lib/nghttp3_http.c | 844 ++ deps/ngtcp2/nghttp3/lib/nghttp3_http.h | 146 + deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c | 88 + deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h | 99 + deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c | 749 + deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h | 331 + deps/ngtcp2/nghttp3/lib/nghttp3_macro.h | 47 + deps/ngtcp2/nghttp3/lib/nghttp3_map.c | 336 + deps/ngtcp2/nghttp3/lib/nghttp3_map.h | 154 + deps/ngtcp2/nghttp3/lib/nghttp3_mem.c | 77 + deps/ngtcp2/nghttp3/lib/nghttp3_mem.h | 45 + deps/ngtcp2/nghttp3/lib/nghttp3_pq.c | 168 + deps/ngtcp2/nghttp3/lib/nghttp3_pq.h | 129 + deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c | 4095 ++++++ deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h | 961 ++ .../nghttp3/lib/nghttp3_qpack_huffman.c | 122 + .../nghttp3/lib/nghttp3_qpack_huffman.h | 108 + .../nghttp3/lib/nghttp3_qpack_huffman_data.c | 4981 +++++++ deps/ngtcp2/nghttp3/lib/nghttp3_range.c | 62 + deps/ngtcp2/nghttp3/lib/nghttp3_range.h | 81 + deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c | 109 + deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h | 82 + deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c | 159 + deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h | 113 + deps/ngtcp2/nghttp3/lib/nghttp3_str.c | 110 + deps/ngtcp2/nghttp3/lib/nghttp3_str.h | 40 + deps/ngtcp2/nghttp3/lib/nghttp3_stream.c | 1287 ++ deps/ngtcp2/nghttp3/lib/nghttp3_stream.h | 407 + deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c | 110 + deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h | 82 + deps/ngtcp2/nghttp3/lib/nghttp3_vec.c | 38 + deps/ngtcp2/nghttp3/lib/nghttp3_vec.h | 35 + deps/ngtcp2/nghttp3/lib/nghttp3_version.c | 39 + deps/ngtcp2/ngtcp2.gyp | 174 + .../ngtcp2/crypto/boringssl/boringssl.c | 540 + .../crypto/includes/ngtcp2/ngtcp2_crypto.h | 688 + .../includes/ngtcp2/ngtcp2_crypto_boringssl.h | 62 + .../includes/ngtcp2/ngtcp2_crypto_openssl.h | 90 + deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c | 526 + deps/ngtcp2/ngtcp2/crypto/shared.c | 831 ++ deps/ngtcp2/ngtcp2/crypto/shared.h | 211 + .../ngtcp2/lib/includes/ngtcp2/ngtcp2.h | 4831 +++++++ .../ngtcp2/lib/includes/ngtcp2/version.h | 51 + deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c | 318 + deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h | 212 + deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c | 131 + deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h | 78 + deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c | 44 + deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h | 79 + deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c | 536 + deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h | 135 + deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c | 121 + deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h | 153 + deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c | 11236 ++++++++++++++++ deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h | 822 ++ deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c | 257 + deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h | 285 + deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c | 749 + deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h | 103 + deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c | 144 + deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h | 34 + deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c | 129 + deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h | 103 + deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c | 86 + deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h | 95 + deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c | 741 + deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h | 329 + deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c | 767 ++ deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h | 80 + deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h | 53 + deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c | 332 + deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h | 152 + deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c | 75 + deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h | 43 + deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c | 74 + deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h | 49 + deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c | 2432 ++++ deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h | 1195 ++ deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c | 230 + deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h | 153 + deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c | 164 + deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h | 126 + deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c | 180 + deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h | 193 + deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c | 1108 ++ deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h | 144 + deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c | 61 + deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h | 80 + deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h | 42 + deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c | 114 + deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h | 110 + deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c | 327 + deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h | 197 + deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c | 107 + deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h | 74 + deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c | 1301 ++ deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h | 402 + deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c | 242 + deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h | 106 + deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c | 675 + deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h | 268 + deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c | 232 + deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h | 114 + deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c | 39 + node.gypi | 20 +- src/node_metadata.cc | 10 + src/node_metadata.h | 15 +- test/common/index.js | 3 + test/parallel/test-process-versions.js | 19 +- tools/getsharedopensslhasquic.py | 19 +- tools/license-builder.sh | 2 + 134 files changed, 61505 insertions(+), 20 deletions(-) create mode 100644 deps/ngtcp2/.gitignore create mode 100644 deps/ngtcp2/LICENSE_nghttp3 create mode 100644 deps/ngtcp2/LICENSE_ngtcp2 create mode 100644 deps/ngtcp2/README.md create mode 100644 deps/ngtcp2/config.h create mode 100644 deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h create mode 100644 deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_buf.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_buf.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_conn.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_conn.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_conv.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_conv.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_debug.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_debug.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_err.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_err.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_frame.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_frame.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_http.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_http.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_macro.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_map.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_map.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_mem.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_mem.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_pq.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_pq.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_range.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_range.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_str.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_str.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_stream.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_stream.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_vec.c create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_vec.h create mode 100644 deps/ngtcp2/nghttp3/lib/nghttp3_version.c create mode 100644 deps/ngtcp2/ngtcp2.gyp create mode 100644 deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c create mode 100644 deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h create mode 100644 deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h create mode 100644 deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h create mode 100644 deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c create mode 100644 deps/ngtcp2/ngtcp2/crypto/shared.c create mode 100644 deps/ngtcp2/ngtcp2/crypto/shared.h create mode 100644 deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h create mode 100644 deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h create mode 100644 deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c diff --git a/LICENSE b/LICENSE index a62f3ad825834e..c6c4f14e2dc193 100644 --- a/LICENSE +++ b/LICENSE @@ -1578,3 +1578,55 @@ The externally maintained libraries used by Node.js are: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ + +- ngtcp2, located at deps/ngtcp2/ngtcp2/, is licensed as follows: + """ + The MIT License + + Copyright (c) 2016 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. + """ + +- nghttp3, located at deps/ngtcp2/nghttp3/, is licensed as follows: + """ + The MIT License + + Copyright (c) 2019 nghttp3 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. + """ diff --git a/configure.py b/configure.py index bb0f8019da69c1..b526507fee9fbb 100755 --- a/configure.py +++ b/configure.py @@ -284,6 +284,50 @@ dest='shared_nghttp2_libpath', help='a directory to search for the shared nghttp2 DLLs') +shared_optgroup.add_argument('--shared-nghttp3', + action='store_true', + dest='shared_nghttp3', + default=None, + help='link to a shared nghttp3 DLL instead of static linking') + +shared_optgroup.add_argument('--shared-nghttp3-includes', + action='store', + dest='shared_nghttp3_includes', + help='directory containing nghttp3 header files') + +shared_optgroup.add_argument('--shared-nghttp3-libname', + action='store', + dest='shared_nghttp3_libname', + default='nghttp3', + help='alternative lib name to link to [default: %(default)s]') + +shared_optgroup.add_argument('--shared-nghttp3-libpath', + action='store', + dest='shared_nghttp3_libpath', + help='a directory to search for the shared nghttp3 DLLs') + +shared_optgroup.add_argument('--shared-ngtcp2', + action='store_true', + dest='shared_ngtcp2', + default=None, + help='link to a shared ngtcp2 DLL instead of static linking') + +shared_optgroup.add_argument('--shared-ngtcp2-includes', + action='store', + dest='shared_ngtcp2_includes', + help='directory containing ngtcp2 header files') + +shared_optgroup.add_argument('--shared-ngtcp2-libname', + action='store', + dest='shared_ngtcp2_libname', + default='ngtcp2', + help='alternative lib name to link to [default: %(default)s]') + +shared_optgroup.add_argument('--shared-ngtcp2-libpath', + action='store', + dest='shared_ngtcp2_libpath', + help='a directory to search for the shared tcp2 DLLs') + shared_optgroup.add_argument('--shared-openssl', action='store_true', dest='shared_openssl', @@ -1347,6 +1391,8 @@ def configure_openssl(o): variables = o['variables'] variables['node_use_openssl'] = b(not options.without_ssl) variables['node_shared_openssl'] = b(options.shared_openssl) + 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['openssl_fips'] = '' variables['openssl_quic'] = b(True) @@ -1836,6 +1882,8 @@ def make_bin_override(): configure_library('brotli', output, pkgname=['libbrotlidec', 'libbrotlienc']) configure_library('cares', output, pkgname='libcares') configure_library('nghttp2', output, pkgname='libnghttp2') +configure_library('nghttp3', output, pkgname='libnghttp3') +configure_library('ngtcp2', output, pkgname='libngtcp2') configure_v8(output) configure_openssl(output) configure_intl(output) diff --git a/deps/ngtcp2/.gitignore b/deps/ngtcp2/.gitignore new file mode 100644 index 00000000000000..40909a37dd7c69 --- /dev/null +++ b/deps/ngtcp2/.gitignore @@ -0,0 +1,8 @@ +*.in +*.am +*.txt +*.pc +Makefile +*gnutls* +ngtcp2/**/.gitignore +ngtcp2/**/.deps diff --git a/deps/ngtcp2/LICENSE_nghttp3 b/deps/ngtcp2/LICENSE_nghttp3 new file mode 100644 index 00000000000000..37562ea58cd74c --- /dev/null +++ b/deps/ngtcp2/LICENSE_nghttp3 @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2019 nghttp3 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. diff --git a/deps/ngtcp2/LICENSE_ngtcp2 b/deps/ngtcp2/LICENSE_ngtcp2 new file mode 100644 index 00000000000000..9b367cdce71384 --- /dev/null +++ b/deps/ngtcp2/LICENSE_ngtcp2 @@ -0,0 +1,22 @@ +The MIT License + +Copyright (c) 2016 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. diff --git a/deps/ngtcp2/README.md b/deps/ngtcp2/README.md new file mode 100644 index 00000000000000..57043dcc133067 --- /dev/null +++ b/deps/ngtcp2/README.md @@ -0,0 +1,45 @@ +# ngtcp2 and nghttp3 + +The ngtcp2 and nghttp3 dependencies provide the core functionality for +QUIC and HTTP/3. + +The sources are pulled from: + +* ngtcp2: https://github.com/ngtcp2/ngtcp2 +* nghttp3: https://github.com/ngtcp2/nghttp3 + +In both the `ngtcp2` and `nghttp3` git repos, the active development occurs +in the default branch (currently named `master` in each). + +We only use a subset of the sources for each. + +## Updating + +The `nghttp3` library depends on `ngtcp2`. Both should always be updated +together. From `ngtcp2` we only want the contents of the `lib` and `crypto` +directories; from `nghttp3` we only want the contents o the `lib`. + +### Updating ngtcp2 + +To update ngtcp2: + +```sh +$ git clone https://github.com/ngtcp2/ngtcp2 +$ cd ngtcp2 +$ autoreconf -i +$ ./configure --prefix=$PWD/build --enable-lib-only +$ cp -R lib/* ../node/deps/ngtcp2/ngtcp2/lib/ +$ cp -R crypto/* ../node/deps/ngtcp2/ngtcp2/crypto/ +``` + +### Updating nghttp3 + +To update ngtcp2: + +```sh +$ git clone https://github.com/ngtcp2/nghttp3 +$ cd nghttp3 +$ autoreconf -i +$ ./configure --prefix=$PWD/build --enable-lib-only +$ cp -R lib/* ../node/deps/ngtcp2/nghttp3/lib/ +``` diff --git a/deps/ngtcp2/config.h b/deps/ngtcp2/config.h new file mode 100644 index 00000000000000..f57b84208c9b1e --- /dev/null +++ b/deps/ngtcp2/config.h @@ -0,0 +1,38 @@ +/* Edited to match src/node.h. */ +#include <stdint.h> + +#ifdef _WIN32 +#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) +typedef intptr_t ssize_t; +# define _SSIZE_T_ +# define _SSIZE_T_DEFINED +#endif +#else // !_WIN32 +# include <sys/types.h> // size_t, ssize_t +#endif // _WIN32 + +#ifdef _MSC_VER +# include <intrin.h> +# define __builtin_popcount __popcnt +#endif + +/* Define to 1 to enable debug output. */ +/* #undef DEBUGBUILD */ + +/* Define to 1 if you have the <arpa/inet.h> header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define to 1 if you have the <stddef.h> header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +/* #undef HAVE_UNISTD_H */ diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h new file mode 100644 index 00000000000000..bc66c40ce211e4 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/nghttp3.h @@ -0,0 +1,2666 @@ +/* + * nghttp3 + * + * Copyright (c) 2018 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2017 nghttp2 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 NGHTTP3_H +#define NGHTTP3_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include <stdint.h> +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include <inttypes.h> +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include <sys/types.h> +#include <stdarg.h> +#include <stddef.h> + +#include <nghttp3/version.h> + +#ifdef NGHTTP3_STATICLIB +# define NGHTTP3_EXTERN +#elif defined(WIN32) +# ifdef BUILDING_NGHTTP3 +# define NGHTTP3_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGHTTP3 */ +# define NGHTTP3_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGHTTP3 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGHTTP3 +# define NGHTTP3_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGHTTP3 */ +# define NGHTTP3_EXTERN +# endif /* !BUILDING_NGHTTP3 */ +#endif /* !defined(WIN32) */ + +/** + * @typedef + * + * :type:`nghttp3_ssize` is signed counterpart of size_t. + */ +typedef ptrdiff_t nghttp3_ssize; + +/** + * @macro + * + * :macro:`NGHTTP3_ALPN_H3` is a serialized form of HTTP/3 ALPN + * protocol identifier this library supports. Notice that the first + * byte is the length of the following protocol identifier. + */ +#define NGHTTP3_ALPN_H3 "\x2h3" + +/** + * @macrosection + * + * nghttp3 library error codes + */ + +/** + * @macro + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` indicates that a passed + * argument is invalid. + */ +#define NGHTTP3_ERR_INVALID_ARGUMENT -101 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_NOBUF` indicates that a provided buffer does + * not have enough space to store data. + */ +#define NGHTTP3_ERR_NOBUF -102 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_INVALID_STATE` indicates that a requested + * operation is not allowed at the current connection state. + */ +#define NGHTTP3_ERR_INVALID_STATE -103 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_WOULDBLOCK` indicates that an operation might + * block. + */ +#define NGHTTP3_ERR_WOULDBLOCK -104 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_STREAM_IN_USE` indicates that a stream ID is + * already in use. + */ +#define NGHTTP3_ERR_STREAM_IN_USE -105 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_PUSH_ID_BLOCKED` indicates that there are no + * spare push ID available. + */ +#define NGHTTP3_ERR_PUSH_ID_BLOCKED -106 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_HEADER` indicates that an HTTP + * header field is malformed. + */ +#define NGHTTP3_ERR_MALFORMED_HTTP_HEADER -107 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_REMOVE_HTTP_HEADER` indicates that an HTTP + * header field is discarded. + */ +#define NGHTTP3_ERR_REMOVE_HTTP_HEADER -108 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING` indicates that HTTP + * messaging is malformed. + */ +#define NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING -109 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_FATAL` indicates that a fatal error is + * occurred during QPACK processing and it cannot be recoverable. + */ +#define NGHTTP3_ERR_QPACK_FATAL -111 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` indicates that a header + * field is too large to process. + */ +#define NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE -112 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_IGNORE_STREAM` indicates that a stream should + * be ignored. + */ +#define NGHTTP3_ERR_IGNORE_STREAM -113 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` indicates that a stream is + * not found. + */ +#define NGHTTP3_ERR_STREAM_NOT_FOUND -114 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_IGNORE_PUSH_PROMISE` indicates that a push + * promise should be ignored. + */ +#define NGHTTP3_ERR_IGNORE_PUSH_PROMISE -115 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_CONN_CLOSING` indicates that a connection is + * closing state. + */ +#define NGHTTP3_ERR_CONN_CLOSING -116 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` indicates that a + * QPACK decompression failed. + */ +#define NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED -402 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR` indicates that an + * error occurred while reading QPACK encoder stream. + */ +#define NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR -403 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` indicates that an + * error occurred while reading QPACK decoder stream. + */ +#define NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR -404 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_FRAME_UNEXPECTED` indicates that an + * unexpected HTTP/3 frame is received. + */ +#define NGHTTP3_ERR_H3_FRAME_UNEXPECTED -408 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_FRAME_ERROR` indicates that an HTTP/3 frame + * is malformed. + */ +#define NGHTTP3_ERR_H3_FRAME_ERROR -409 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_MISSING_SETTINGS` indicates that an HTTP/3 + * SETTINGS frame is missing. + */ +#define NGHTTP3_ERR_H3_MISSING_SETTINGS -665 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_INTERNAL_ERROR` indicates an internal error. + */ +#define NGHTTP3_ERR_H3_INTERNAL_ERROR -667 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM` indicates that a + * critical stream is closed. + */ +#define NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM -668 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR` indicates a general + * protocol error. This is typically a catch-all error. + */ +#define NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR -669 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_ID_ERROR` indicates that an ID related error + * occurred. + */ +#define NGHTTP3_ERR_H3_ID_ERROR -670 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_SETTINGS_ERROR` indicates that an HTTP/3 + * SETTINGS frame is malformed. + */ +#define NGHTTP3_ERR_H3_SETTINGS_ERROR -671 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_H3_STREAM_CREATION_ERROR` indicates that a + * remote endpoint attempts to create a new stream which is not + * allowed. + */ +#define NGHTTP3_ERR_H3_STREAM_CREATION_ERROR -672 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_FATAL` indicates that error codes less than + * this value is fatal error. When this error is returned, an + * endpoint should drop connection immediately. + */ +#define NGHTTP3_ERR_FATAL -900 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_NOMEM` indicates out of memory. + */ +#define NGHTTP3_ERR_NOMEM -901 +/** + * @macro + * + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` indicates that user defined + * callback function failed. + */ +#define NGHTTP3_ERR_CALLBACK_FAILURE -902 + +/** + * @macrosection + * + * HTTP/3 application error code + */ + +/** + * @macro + * + * :macro:`NGHTTP3_H3_NO_ERROR` is HTTP/3 application error code + * ``H3_NO_ERROR``. + */ +#define NGHTTP3_H3_NO_ERROR 0x0100 +/** + * @macro + * + * :macro:`NGHTTP3_H3_GENERAL_PROTOCOL_ERROR` is HTTP/3 application + * error code ``H3_GENERAL_PROTOCOL_ERROR``. + */ +#define NGHTTP3_H3_GENERAL_PROTOCOL_ERROR 0x0101 +/** + * @macro + * + * :macro:`NGHTTP3_H3_INTERNAL_ERROR` is HTTP/3 application error code + * ``H3_INTERNAL_ERROR``. + */ +#define NGHTTP3_H3_INTERNAL_ERROR 0x0102 +/** + * @macro + * + * :macro:`NGHTTP3_H3_STREAM_CREATION_ERROR` is HTTP/3 application + * error code ``H3_STREAM_CREATION_ERROR``. + */ +#define NGHTTP3_H3_STREAM_CREATION_ERROR 0x0103 +/** + * @macro + * + * :macro:`NGHTTP3_H3_CLOSED_CRITICAL_STREAM` is HTTP/3 application + * error code ``H3_CLOSED_CRITICAL_STREAM``. + */ +#define NGHTTP3_H3_CLOSED_CRITICAL_STREAM 0x0104 +/** + * @macro + * + * :macro:`NGHTTP3_H3_FRAME_UNEXPECTED` is HTTP/3 application error + * code ``H3_FRAME_UNEXPECTED``. + */ +#define NGHTTP3_H3_FRAME_UNEXPECTED 0x0105 +/** + * @macro + * + * :macro:`NGHTTP3_H3_FRAME_ERROR` is HTTP/3 application error code + * ``H3_FRAME_ERROR``. + */ +#define NGHTTP3_H3_FRAME_ERROR 0x0106 +/** + * @macro + * + * :macro:`NGHTTP3_H3_EXCESSIVE_LOAD` is HTTP/3 application error code + * ``H3_EXCESSIVE_LOAD``. + */ +#define NGHTTP3_H3_EXCESSIVE_LOAD 0x0107 +/** + * @macro + * + * :macro:`NGHTTP3_H3_ID_ERROR` is HTTP/3 application error code + * ``H3_ID_ERROR``. + */ +#define NGHTTP3_H3_ID_ERROR 0x0108 +/** + * @macro + * + * :macro:`NGHTTP3_H3_SETTINGS_ERROR` is HTTP/3 application error code + * ``H3_SETTINGS_ERROR``. + */ +#define NGHTTP3_H3_SETTINGS_ERROR 0x0109 +/** + * @macro + * + * :macro:`NGHTTP3_H3_MISSING_SETTINGS` is HTTP/3 application error + * code ``H3_MISSING_SETTINGS``. + */ +#define NGHTTP3_H3_MISSING_SETTINGS 0x010a +/** + * @macro + * + * :macro:`NGHTTP3_H3_REQUEST_REJECTED` is HTTP/3 application error + * code ``H3_REQUEST_REJECTED``. + */ +#define NGHTTP3_H3_REQUEST_REJECTED 0x010b +/** + * @macro + * + * :macro:`NGHTTP3_H3_REQUEST_CANCELLED` is HTTP/3 application error + * code ``H3_REQUEST_CANCELLED``. + */ +#define NGHTTP3_H3_REQUEST_CANCELLED 0x010c +/** + * @macro + * + * :macro:`NGHTTP3_H3_REQUEST_INCOMPLETE` is HTTP/3 application error + * code ``H3_REQUEST_INCOMPLETE``. + */ +#define NGHTTP3_H3_REQUEST_INCOMPLETE 0x010d +/** + * @macro + * + * :macro:`NGHTTP3_H3_MESSAGE_ERROR` is HTTP/3 application error code + * ``H3_MESSAGE_ERROR``. + */ +#define NGHTTP3_H3_MESSAGE_ERROR 0x010e +/** + * @macro + * + * :macro:`NGHTTP3_H3_CONNECT_ERROR` is HTTP/3 application error code + * ``H3_CONNECT_ERROR``. + */ +#define NGHTTP3_H3_CONNECT_ERROR 0x010f +/** + * @macro + * + * :macro:`NGHTTP3_H3_VERSION_FALLBACK` is HTTP/3 application error + * code ``H3_VERSION_FALLBACK``. + */ +#define NGHTTP3_H3_VERSION_FALLBACK 0x0110 +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECOMPRESSION_FAILED` is HTTP/3 application + * error code ``QPACK_DECOMPRESSION_FAILED``. + */ +#define NGHTTP3_QPACK_DECOMPRESSION_FAILED 0x0200 +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_ENCODER_STREAM_ERROR` is HTTP/3 application + * error code ``QPACK_ENCODER_STREAM_ERROR``. + */ +#define NGHTTP3_QPACK_ENCODER_STREAM_ERROR 0x0201 +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODER_STREAM_ERROR` is HTTP/3 application + * error code ``QPACK_DECODER_STREAM_ERROR``. + */ +#define NGHTTP3_QPACK_DECODER_STREAM_ERROR 0x0202 + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void *(*nghttp3_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void (*nghttp3_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void *(*nghttp3_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`nghttp3_mem` structure. + */ +typedef void *(*nghttp3_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * :type:`nghttp3_mem` is a custom memory allocator functions and user + * defined pointer. The |mem_user_data| member is passed to each + * allocator function. This can be used, for example, to achieve + * per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void conn_new() { + * nghttp3_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * } + */ +typedef struct nghttp3_mem { + /** + * :member:`mem_user_data` is an arbitrary user supplied data. This + * is passed to each allocator function. + */ + void *mem_user_data; + /** + * :member:`malloc` is a custom allocator function to replace + * malloc(). + */ + nghttp3_malloc malloc; + /** + * :member:`free` is a custom allocator function to replace free(). + */ + nghttp3_free free; + /** + * :member:`calloc` is a custom allocator function to replace + * calloc(). + */ + nghttp3_calloc calloc; + /** + * :member:`realloc` is a custom allocator function to replace + * realloc(). + */ + nghttp3_realloc realloc; +} nghttp3_mem; + +/** + * @function + * + * `nghttp3_mem_default` returns the default memory allocator which + * uses malloc/calloc/realloc/free. + */ +NGHTTP3_EXTERN const nghttp3_mem *nghttp3_mem_default(void); + +/** + * @struct + * + * :type:`nghttp3_vec` is ``struct iovec`` compatible structure to + * reference arbitrary array of bytes. + */ +typedef struct nghttp3_vec { + /** + * :member:`base` points to the data. + */ + uint8_t *base; + /** + * :member:`len` is the number of bytes which the buffer pointed by + * base contains. + */ + size_t len; +} nghttp3_vec; + +/** + * @struct + * + * :type:`nghttp3_rcbuf` is the object representing reference counted + * buffer. The details of this structure are intentionally hidden + * from the public API. + */ +typedef struct nghttp3_rcbuf nghttp3_rcbuf; + +/** + * @function + * + * `nghttp3_rcbuf_incref` increments the reference count of |rcbuf| by + * 1. + */ +NGHTTP3_EXTERN void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf); + +/** + * @function + * + * `nghttp3_rcbuf_decref` decrements the reference count of |rcbuf| by + * 1. If the reference count becomes zero, the object pointed by + * |rcbuf| will be freed. In this case, application must not use + * |rcbuf| again. + */ +NGHTTP3_EXTERN void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf); + +/** + * @function + * + * `nghttp3_rcbuf_get_buf` returns the underlying buffer managed by + * |rcbuf|. + */ +NGHTTP3_EXTERN nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf); + +/** + * @function + * + * `nghttp3_rcbuf_is_static` returns nonzero if the underlying buffer + * is statically allocated, and 0 otherwise. This can be useful for + * language bindings that wish to avoid creating duplicate strings for + * these buffers. + */ +NGHTTP3_EXTERN int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf); + +/** + * @struct + * + * :type:`nghttp3_buf` is the variable size buffer. + */ +typedef struct nghttp3_buf { + /** + * :member:`begin` points to the beginning of the buffer. + */ + uint8_t *begin; + /** + * :member:`end` points to the one beyond of the last byte of the + * buffer + */ + uint8_t *end; + /** + * :member:`pos` pointers to the start of data. Typically, this + * points to the point that next data should be read. Initially, it + * points to :member:`begin`. + */ + uint8_t *pos; + /** + * :member:`last` points to the one beyond of the last data of the + * buffer. Typically, new data is written at this point. + * Initially, it points to :member:`begin`. + */ + uint8_t *last; +} nghttp3_buf; + +/** + * @function + * + * `nghttp3_buf_init` initializes empty |buf|. + */ +NGHTTP3_EXTERN void nghttp3_buf_init(nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_free` frees resources allocated for |buf| using |mem| + * as memory allocator. buf->begin must be a heap buffer allocated by + * |mem|. + */ +NGHTTP3_EXTERN void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_buf_left` returns the number of additional bytes which can + * be written to the underlying buffer. In other words, it returns + * buf->end - buf->last. + */ +NGHTTP3_EXTERN size_t nghttp3_buf_left(const nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_len` returns the number of bytes left to read. In + * other words, it returns buf->last - buf->pos. + */ +NGHTTP3_EXTERN size_t nghttp3_buf_len(const nghttp3_buf *buf); + +/** + * @function + * + * `nghttp3_buf_reset` sets buf->pos and buf->last to buf->begin. + */ +NGHTTP3_EXTERN void nghttp3_buf_reset(nghttp3_buf *buf); + +/** + * @macrosection + * + * Flags for header field name/value pair + */ + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NONE` indicates no flag set. + */ +#define NGHTTP3_NV_FLAG_NONE 0 + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NEVER_INDEX` indicates that this name/value + * pair must not be indexed. Other implementation calls this bit as + * "sensitive". + */ +#define NGHTTP3_NV_FLAG_NEVER_INDEX 0x01 + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NO_COPY_NAME` is set solely by application. + * If this flag is set, the library does not make a copy of header + * field name. This could improve performance. + */ +#define NGHTTP3_NV_FLAG_NO_COPY_NAME 0x02 + +/** + * @macro + * + * :macro:`NGHTTP3_NV_FLAG_NO_COPY_VALUE` is set solely by + * application. If this flag is set, the library does not make a copy + * of header field value. This could improve performance. + */ +#define NGHTTP3_NV_FLAG_NO_COPY_VALUE 0x04 + +/** + * @struct + * + * :type:`nghttp3_nv` is the name/value pair, which mainly used to + * represent header fields. + */ +typedef struct nghttp3_nv { + /** + * :member:`name` is the header field name. + */ + uint8_t *name; + /** + * :member:`value` is the header field value. + */ + uint8_t *value; + /** + * :member:`namelen` is the length of the |name|, excluding + * terminating NULL. + */ + size_t namelen; + /** + * :member:`valuelen` is the length of the |value|, excluding + * terminating NULL. + */ + size_t valuelen; + /** + * :member:`flags` is bitwise OR of one or more of + * NGHTTP3_NV_FLAG_*. + */ + uint8_t flags; +} nghttp3_nv; + +/* Generated by mkstatichdtbl.py */ +/** + * @enum + * + * :type:`nghttp3_qpack_token` defines HTTP header field name tokens + * to identify field name quickly. It appears in + * :member:`nghttp3_qpack_nv.token`. + */ +typedef enum nghttp3_qpack_token { + /** + * :enum:`NGHTTP3_QPACK_TOKEN__AUTHORITY` is a token for + * ``:authority``. + */ + NGHTTP3_QPACK_TOKEN__AUTHORITY = 0, + /** + * :enum:`NGHTTP3_QPACK_TOKEN__PATH` is a token for ``:path``. + */ + NGHTTP3_QPACK_TOKEN__PATH = 8, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_AGE` is a token for ``age``. + */ + NGHTTP3_QPACK_TOKEN_AGE = 43, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION` is a token for + * ``content-disposition``. + */ + NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION = 52, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH` is a token for + * ``content-length``. + */ + NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH = 55, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_COOKIE` is a token for ``cookie``. + */ + NGHTTP3_QPACK_TOKEN_COOKIE = 68, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_DATE` is a token for ``date``. + */ + NGHTTP3_QPACK_TOKEN_DATE = 69, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ETAG` is a token for ``etag``. + */ + NGHTTP3_QPACK_TOKEN_ETAG = 71, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE` is a token for + * ``if-modified-since``. + */ + NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE = 74, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH` is a token for + * ``if-none-match``. + */ + NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH = 75, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_LAST_MODIFIED` is a token for + * ``last-modified``. + */ + NGHTTP3_QPACK_TOKEN_LAST_MODIFIED = 77, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_LINK` is a token for ``link``. + */ + NGHTTP3_QPACK_TOKEN_LINK = 78, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_LOCATION` is a token for ``location``. + */ + NGHTTP3_QPACK_TOKEN_LOCATION = 79, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_REFERER` is a token for ``referer``. + */ + NGHTTP3_QPACK_TOKEN_REFERER = 83, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_SET_COOKIE` is a token for + * ``set-cookie``. + */ + NGHTTP3_QPACK_TOKEN_SET_COOKIE = 85, + /** + * :enum:`NGHTTP3_QPACK_TOKEN__METHOD` is a token for ``:method``. + */ + NGHTTP3_QPACK_TOKEN__METHOD = 1, + /** + * :enum:`NGHTTP3_QPACK_TOKEN__SCHEME` is a token for ``:scheme``. + */ + NGHTTP3_QPACK_TOKEN__SCHEME = 9, + /** + * :enum:`NGHTTP3_QPACK_TOKEN__STATUS` is a token for ``:status``. + */ + NGHTTP3_QPACK_TOKEN__STATUS = 11, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT` is a token for ``accept``. + */ + NGHTTP3_QPACK_TOKEN_ACCEPT = 25, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING` is a token for + * ``accept-encoding``. + */ + NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING = 27, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES` is a token for + * ``accept-ranges``. + */ + NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES = 29, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS` is a + * token for ``access-control-allow-headers``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS = 32, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN` is a + * token for ``access-control-allow-origin``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN = 38, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CACHE_CONTROL` is a token for + * ``cache-control``. + */ + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL = 46, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING` is a token for + * ``content-encoding``. + */ + NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING = 53, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_TYPE` is a token for + * ``content-type``. + */ + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE = 57, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_RANGE` is a token for ``range``. + */ + NGHTTP3_QPACK_TOKEN_RANGE = 82, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY` is a token + * for ``strict-transport-security``. + */ + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY = 86, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_VARY` is a token for ``vary``. + */ + NGHTTP3_QPACK_TOKEN_VARY = 92, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS` is a token for + * ``x-content-type-options``. + */ + NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS = 94, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION` is a token for + * ``x-xss-protection``. + */ + NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION = 98, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE` is a token for + * ``accept-language``. + */ + NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE = 28, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS` is a + * token for ``access-control-allow-credentials``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS = 30, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS` is a + * token for ``access-control-allow-methods``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS = 35, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS` is a + * token for ``access-control-expose-headers``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS = 39, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS` is a + * token for ``access-control-request-headers``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS = 40, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD` is a + * token for ``access-control-request-method``. + */ + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD = 41, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ALT_SVC` is a token for ``alt-svc``. + */ + NGHTTP3_QPACK_TOKEN_ALT_SVC = 44, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_AUTHORIZATION` is a token for + * ``authorization``. + */ + NGHTTP3_QPACK_TOKEN_AUTHORIZATION = 45, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY` is a token + * for ``content-security-policy``. + */ + NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY = 56, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_EARLY_DATA` is a token for + * ``early-data``. + */ + NGHTTP3_QPACK_TOKEN_EARLY_DATA = 70, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_EXPECT_CT` is a token for + * ``expect-ct``. + */ + NGHTTP3_QPACK_TOKEN_EXPECT_CT = 72, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_FORWARDED` is a token for + * ``forwarded``. + */ + NGHTTP3_QPACK_TOKEN_FORWARDED = 73, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_IF_RANGE` is a token for ``if-range``. + */ + NGHTTP3_QPACK_TOKEN_IF_RANGE = 76, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_ORIGIN` is a token for ``origin``. + */ + NGHTTP3_QPACK_TOKEN_ORIGIN = 80, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_PURPOSE` is a token for ``purpose``. + */ + NGHTTP3_QPACK_TOKEN_PURPOSE = 81, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_SERVER` is a token for ``server``. + */ + NGHTTP3_QPACK_TOKEN_SERVER = 84, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN` is a token for + * ``timing-allow-origin``. + */ + NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN = 89, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS` is a token + * for ``upgrade-insecure-requests``. + */ + NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS = 90, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_USER_AGENT` is a token for + * ``user-agent``. + */ + NGHTTP3_QPACK_TOKEN_USER_AGENT = 91, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR` is a token for + * ``x-forwarded-for``. + */ + NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR = 95, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS` is a token for + * ``x-frame-options``. + */ + NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS = 96, + + /* Additional header fields for HTTP messaging validation */ + + /** + * :enum:`NGHTTP3_QPACK_TOKEN_HOST` is a token for ``host``. + */ + NGHTTP3_QPACK_TOKEN_HOST = 1000, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_CONNECTION` is a token for + * ``connection``. + */ + NGHTTP3_QPACK_TOKEN_CONNECTION, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_KEEP_ALIVE` is a token for + * ``keep-alive``. + */ + NGHTTP3_QPACK_TOKEN_KEEP_ALIVE, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION` is a token for + * ``proxy-connection``. + */ + NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING` is a token for + * ``transfer-encoding``. + */ + NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_UPGRADE` is a token for ``upgrade``. + */ + NGHTTP3_QPACK_TOKEN_UPGRADE, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_TE` is a token for ``te``. + */ + NGHTTP3_QPACK_TOKEN_TE, + /** + * :enum:`NGHTTP3_QPACK_TOKEN__PROTOCOL` is a token for + * ``:protocol``. + */ + NGHTTP3_QPACK_TOKEN__PROTOCOL, + /** + * :enum:`NGHTTP3_QPACK_TOKEN_PRIORITY` is a token for ``priority``. + */ + NGHTTP3_QPACK_TOKEN_PRIORITY +} nghttp3_qpack_token; + +/** + * @struct + * + * :type:`nghttp3_qpack_nv` represents header field name/value pair + * just like :type:`nghttp3_nv`. It is an extended version of + * :type:`nghttp3_nv` and has reference counted buffers and tokens + * which might be useful for applications. + */ +typedef struct { + /** + * :member:`name` is the buffer containing header field name. + * NULL-termination is guaranteed. + */ + nghttp3_rcbuf *name; + /** + * :member:`value` is the buffer containing header field value. + * NULL-termination is guaranteed. + */ + nghttp3_rcbuf *value; + /** + * :member:`token` is :type:`nghttp3_qpack_token` value of + * :member:`name`. It could be -1 if we have no token for that + * header field name. + */ + int32_t token; + /** + * :member:`flags` is a bitwise OR of one or more of + * NGHTTP3_NV_FLAG_*. See :macro:`NGHTTP3_NV_FLAG_NONE`. + */ + uint8_t flags; +} nghttp3_qpack_nv; + +/** + * @struct + * + * :type:`nghttp3_qpack_encoder` is QPACK encoder. + */ +typedef struct nghttp3_qpack_encoder nghttp3_qpack_encoder; + +/** + * @function + * + * `nghttp3_qpack_encoder_new` initializes QPACK encoder. |pencoder| + * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic + * table size. |max_blocked| is the maximum number of streams which + * can be blocked. |mem| is a memory allocator. This function + * allocates memory for :type:`nghttp3_qpack_encoder` itself and + * assigns its pointer to |*pencoder| if it succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, + size_t max_dtable_size, + size_t max_blocked, + const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_encoder_del` frees memory allocated for |encoder|. + * This function frees memory pointed by |encoder| itself. + */ +NGHTTP3_EXTERN void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder); + +/** + * @function + * + * `nghttp3_qpack_encoder_encode` encodes the list of header fields + * |nva|. |nvlen| is the length of |nva|. |stream_id| is the + * identifier of the stream which this header fields belong to. This + * function writes header block prefix, encoded header fields, and + * encoder stream to |pbuf|, |rbuf|, and |ebuf| respectively. The + * :member:`nghttp3_buf.last` will be adjusted when data is written. + * An application should write |pbuf| and |rbuf| to the request stream + * in this order. + * + * The buffer pointed by |pbuf|, |rbuf|, and |ebuf| can be empty + * buffer. It is fine to pass a buffer initialized by + * `nghttp3_buf_init(buf) <nghttp3_buf_init>`. This function + * allocates memory for these buffers as necessary. In particular, it + * frees and expands buffer if the current capacity of buffer is not + * enough. If :member:`nghttp3_buf.begin` of any buffer is not NULL, + * it must be allocated by the same memory allocator passed to + * `nghttp3_qpack_encoder_new()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + * |encoder| is in unrecoverable error state and cannot be used + * anymore. + */ +NGHTTP3_EXTERN int nghttp3_qpack_encoder_encode( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, nghttp3_buf *rbuf, + nghttp3_buf *ebuf, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen); + +/** + * @function + * + * `nghttp3_qpack_encoder_read_decoder` reads decoder stream. The + * buffer pointed by |src| of length |srclen| contains decoder stream. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + * |encoder| is in unrecoverable error state and cannot be used + * anymore. + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM` + * |encoder| is unable to process input because it is malformed. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_encoder_read_decoder( + nghttp3_qpack_encoder *encoder, const uint8_t *src, size_t srclen); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_max_dtable_size` sets max dynamic table + * size to |max_dtable_size|. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + * |max_dtable_size| exceeds the hard limit that decoder specifies. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_hard_max_dtable_size` sets hard maximum + * dynamic table size to |hard_max_dtable_size|. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * TBD + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_set_hard_max_dtable_size(nghttp3_qpack_encoder *encoder, + size_t hard_max_dtable_size); + +/** + * @function + * + * `nghttp3_qpack_encoder_set_max_blocked` sets the number of streams + * which can be blocked to |max_blocked|. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * TBD + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, + size_t max_blocked); + +/** + * @function + * + * `nghttp3_qpack_encoder_ack_header` tells |encoder| that header + * block for a stream denoted by |stream_id| was acknowledged by + * decoder. This function is provided for debugging purpose only. In + * HTTP/3, |encoder| knows acknowledgement of header block by reading + * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_qpack_encoder_add_insert_count` increments known received + * count of |encoder| by |n|. This function is provided for debugging + * purpose only. In HTTP/3, |encoder| increments known received count + * by reading decoder stream with + * `nghttp3_qpack_encoder_read_decoder()`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR` + * |n| is too large. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, + uint64_t n); + +/** + * @function + * + * `nghttp3_qpack_encoder_ack_everything` tells |encoder| that all + * encoded header blocks are acknowledged. This function is provided + * for debugging purpose only. In HTTP/3, |encoder| knows this by + * reading decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder); + +/** + * @function + * + * `nghttp3_qpack_encoder_cancel_stream` tells |encoder| that stream + * denoted by |stream_id| is cancelled. This function is provided for + * debugging purpose only. In HTTP/3, |encoder| knows this by reading + * decoder stream with `nghttp3_qpack_encoder_read_decoder()`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_qpack_encoder_get_num_blocked` returns the number of + * streams which are potentially blocked at decoder side. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder); + +/** + * @struct + * + * :type:`nghttp3_qpack_stream_context` is a decoder context for an + * individual stream. Its state is per header block. In order to + * reuse this object for another header block, call + * `nghttp3_qpack_stream_context_reset`. + */ +typedef struct nghttp3_qpack_stream_context nghttp3_qpack_stream_context; + +/** + * @function + * + * `nghttp3_qpack_stream_context_new` initializes stream context. + * |psctx| must be non-NULL pointer. |stream_id| is stream ID. |mem| + * is a memory allocator. This function allocates memory for + * :type:`nghttp3_qpack_stream_context` itself and assigns its pointer + * to |*psctx| if it succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, + int64_t stream_id, const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_stream_context_del` frees memory allocated for + * |sctx|. This function frees memory pointed by |sctx| itself. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx); + +/** + * @function + * + * `nghttp3_qpack_stream_context_get_ricnt` returns required insert + * count. + */ +NGHTTP3_EXTERN uint64_t +nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx); + +/** + * @function + * + * `nghttp3_qpack_stream_context_reset` resets the state of |sctx|. + * Then it can be reused for an another header block in the same + * stream. + */ +NGHTTP3_EXTERN +void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx); + +/** + * @struct + * + * :type:`nghttp3_qpack_decoder` is QPACK decoder. + */ +typedef struct nghttp3_qpack_decoder nghttp3_qpack_decoder; + +/** + * @function + * + * `nghttp3_qpack_decoder_new` initializes QPACK decoder. |pdecoder| + * must be non-NULL pointer. |max_dtable_size| is the maximum dynamic + * table size. |max_blocked| is the maximum number of streams which + * can be blocked. |mem| is a memory allocator. This function + * allocates memory for :type:`nghttp3_qpack_decoder` itself and + * assigns its pointer to |*pdecoder| if it succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, + size_t max_dtable_size, + size_t max_blocked, + const nghttp3_mem *mem); + +/** + * @function + * + * `nghttp3_qpack_decoder_del` frees memory allocated for |decoder|. + * This function frees memory pointed by |decoder| itself. + */ +NGHTTP3_EXTERN void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder); + +/** + * @function + * + * `nghttp3_qpack_decoder_read_encoder` reads encoder stream. The + * buffer pointed by |src| of length |srclen| contains encoder stream. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + * |decoder| is in unrecoverable error state and cannot be used + * anymore. + * :macro:`NGHTTP3_ERR_QPACK_ENCODER_STREAM` + * Could not interpret encoder stream instruction. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_encoder( + nghttp3_qpack_decoder *decoder, const uint8_t *src, size_t srclen); + +/** + * @function + * + * `nghttp3_qpack_decoder_get_icnt` returns insert count. + */ +NGHTTP3_EXTERN uint64_t +nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder); + +/** + * @macrosection + * + * Flags for QPACK decoder + */ + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_NONE` indicates that no flag set. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_NONE 0 + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` indicates that a header + * field is successfully decoded. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_EMIT 0x01 + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` indicates that all header + * fields have been decoded. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_FINAL 0x02 + +/** + * @macro + * + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` indicates that decoding + * has been blocked. + */ +#define NGHTTP3_QPACK_DECODE_FLAG_BLOCKED 0x04 + +/** + * @function + * + * `nghttp3_qpack_decoder_read_request` reads request stream. The + * request stream is given as the buffer pointed by |src| of length + * |srclen|. |sctx| is the stream context and it must be created by + * `nghttp3_qpack_stream_context_new()`. |*pflags| must be non-NULL + * pointer. |nv| must be non-NULL pointer. + * + * If this function succeeds, it assigns flags to |*pflags|. If + * |*pflags| has :macro:`NGHTTP3_QPACK_DECODE_FLAG_EMIT` set, a + * decoded header field is assigned to |nv|. If |*pflags| has + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_FINAL` set, all header fields + * have been successfully decoded. If |*pflags| has + * :macro:`NGHTTP3_QPACK_DECODE_FLAG_BLOCKED` set, decoding is blocked + * due to required insert count. + * + * When a header field is decoded, an application receives it in |nv|. + * nv->name and nv->value are reference counted buffer, and their + * reference counts are already incremented for application use. + * Therefore, when application finishes processing the header field, + * it must call `nghttp3_rcbuf_decref(nv->name) + * <nghttp3_rcbuf_decref>` and `nghttp3_rcbuf_decref(nv->value) + * <nghttp3_rcbuf_decref>` or memory leak might occur. + * + * This function returns the number of bytes read, or one of the + * following negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + * |decoder| is in unrecoverable error state and cannot be used + * anymore. + * :macro:`NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED` + * Could not interpret header block instruction. + * :macro:`NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE` + * Header field is too large. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_qpack_decoder_read_request( + nghttp3_qpack_decoder *decoder, nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv, uint8_t *pflags, const uint8_t *src, size_t srclen, + int fin); + +/** + * @function + * + * `nghttp3_qpack_decoder_write_decoder` writes decoder stream into + * |dbuf|. + * + * The caller must ensure that `nghttp3_buf_left(dbuf) + * <nghttp3_buf_left>` >= + * `nghttp3_qpack_decoder_get_decoder_streamlen(decoder) + * <nghttp3_qpack_decoder_get_decoder_streamlen>`. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, + nghttp3_buf *dbuf); + +/** + * @function + * + * `nghttp3_qpack_decoder_get_decoder_streamlen` returns the length of + * decoder stream. + */ +NGHTTP3_EXTERN size_t +nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder); + +/** + * @function + * + * `nghttp3_qpack_decoder_cancel_stream` cancels header decoding for + * stream denoted by |stream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + * :macro:`NGHTTP3_ERR_QPACK_FATAL` + * Decoder stream overflow. + */ +NGHTTP3_EXTERN int +nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_qpack_decoder_set_dtable_cap` sets |cap| as maximum + * dynamic table size. Normally, the maximum capacity is communicated + * in encoder stream. This function is provided for debugging and + * testing purpose. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, + size_t cap); + +/** + * @function + * + * `nghttp3_qpack_decoder_set_max_concurrent_streams` tells |decoder| + * the maximum number of concurrent streams that a remote endpoint can + * open, including both bidirectional and unidirectional streams which + * potentially receive QPACK encoded HEADERS frame. This value is + * used as a hint to limit the length of decoder stream. + */ +NGHTTP3_EXTERN void +nghttp3_qpack_decoder_set_max_concurrent_streams(nghttp3_qpack_decoder *decoder, + size_t max_concurrent_streams); + +/** + * @function + * + * `nghttp3_strerror` returns textual representation of |liberr|. + */ +NGHTTP3_EXTERN const char *nghttp3_strerror(int liberr); + +/** + * @function + * + * `nghttp3_err_infer_quic_app_error_code` returns a QUIC application + * error code which corresponds to |liberr|. + */ +NGHTTP3_EXTERN uint64_t nghttp3_err_infer_quic_app_error_code(int liberr); + +/** + * @functypedef + * + * :type:`nghttp3_debug_vprintf_callback` is a callback function + * invoked when the library outputs debug logging. The function is + * called with arguments suitable for ``vfprintf(3)`` + * + * The debug output is only enabled if the library is built with + * ``DEBUGBUILD`` macro defined. + */ +typedef void (*nghttp3_debug_vprintf_callback)(const char *format, + va_list args); + +/** + * @function + * + * `nghttp3_set_debug_vprintf_callback` sets a debug output callback + * called by the library when built with ``DEBUGBUILD`` macro defined. + * If this option is not used, debug log is written into standard + * error output. + * + * For builds without ``DEBUGBUILD`` macro defined, this function is + * noop. + * + * Note that building with ``DEBUGBUILD`` may cause significant + * performance penalty to libnghttp3 because of extra processing. It + * should be used for debugging purpose only. + * + * .. Warning:: + * + * Building with ``DEBUGBUILD`` may cause significant performance + * penalty to libnghttp3 because of extra processing. It should be + * used for debugging purpose only. We write this two times because + * this is important. + */ +NGHTTP3_EXTERN void nghttp3_set_debug_vprintf_callback( + nghttp3_debug_vprintf_callback debug_vprintf_callback); + +/** + * @struct + * + * :type:`nghttp3_conn` represents a single HTTP/3 connection. + */ +typedef struct nghttp3_conn nghttp3_conn; + +/** + * @functypedef + * + * :type:`nghttp3_acked_stream_data` is a callback function which is + * invoked when data sent on stream denoted by |stream_id| supplied + * from application is acknowledged by remote endpoint. The number of + * bytes acknowledged is given in |datalen|. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_acked_stream_data)(nghttp3_conn *conn, int64_t stream_id, + size_t datalen, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_conn_stream_close` is a callback function which is + * invoked when a stream identified by |stream_id| is closed. + * |app_error_code| indicates the reason of this closure. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_stream_close)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_recv_data` is a callback function which is invoked + * when a part of request or response body on stream identified by + * |stream_id| is received. |data| points to the received data and + * its length is |datalen|. + * + * The application is responsible for increasing flow control credit + * by |datalen| bytes. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_data)(nghttp3_conn *conn, int64_t stream_id, + const uint8_t *data, size_t datalen, + void *conn_user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_deferred_consume` is a callback function which is + * invoked when the library consumed |consumed| bytes for a stream + * identified by |stream_id|. This callback is used to notify the + * consumed bytes for stream blocked by QPACK decoder. The + * application is responsible for increasing flow control credit by + * |consumed| bytes. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_deferred_consume)(nghttp3_conn *conn, int64_t stream_id, + size_t consumed, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_begin_headers` is a callback function which is + * invoked when an incoming header block section is started on a + * stream denoted by |stream_id|. Each header field is passed to + * application by :type:`nghttp3_recv_header` callback. And then + * :type:`nghttp3_end_headers` is called when a whole header block is + * processed. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_begin_headers)(nghttp3_conn *conn, int64_t stream_id, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_recv_header` is a callback function which is invoked + * when a header field is received on a stream denoted by |stream_id|. + * |name| contains a field name and |value| contains a field value. + * |token| is one of token defined in :type:`nghttp3_qpack_token` or + * -1 if no token is defined for |name|. |flags| is bitwise OR of + * zero or more of NGHTTP3_NV_FLAG_*. + * + * The buffers for |name| and |value| are reference counted. If + * application needs to keep them, increment the reference count with + * `nghttp3_rcbuf_incref`. When they are no longer used, call + * `nghttp3_rcbuf_decref`. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_header)(nghttp3_conn *conn, int64_t stream_id, + int32_t token, nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_end_headers` is a callback function which is invoked + * when an incoming header block has ended. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_headers)(nghttp3_conn *conn, int64_t stream_id, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_begin_push_promise` is a callback function which is + * invoked when an incoming header block section in PUSH_PROMISE is + * started on a stream denoted by |stream_id|. |push_id| identifies a + * push promise. Each header field is passed to application by + * :type:`nghttp3_recv_push_promise` callback. And then + * :type:`nghttp3_end_push_promise` is called when a whole header + * block is processed. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_begin_push_promise)(nghttp3_conn *conn, int64_t stream_id, + int64_t push_id, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_recv_push_promise` is a callback function which is + * invoked when a header field in PUSH_PROMISE is received on a stream + * denoted by |stream_id|. |push_id| identifies a push promise. + * |name| contains a field name and |value| contains a field value. + * |token| is one of token defined in :type:`nghttp3_qpack_token` or + * -1 if no token is defined for |name|. |flags| is bitwise OR of + * zero or more of NGHTTP3_NV_FLAG_*. + * + * The buffers for |name| and |value| are reference counted. If + * application needs to keep them, increment the reference count with + * `nghttp3_rcbuf_incref`. When they are no longer used, call + * `nghttp3_rcbuf_decref`. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_recv_push_promise)(nghttp3_conn *conn, int64_t stream_id, + int64_t push_id, int32_t token, + nghttp3_rcbuf *name, + nghttp3_rcbuf *value, uint8_t flags, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_end_push_promise` is a callback function which is + * invoked when an incoming header block in PUSH_PROMISE has ended. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_push_promise)(nghttp3_conn *conn, int64_t stream_id, + int64_t push_id, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_end_stream` is a callback function which is invoked + * when the receiving side of stream is closed. For server, this + * callback function is invoked when HTTP request is received + * completely. For client, this callback function is invoked when + * HTTP response is received completely. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_end_stream)(nghttp3_conn *conn, int64_t stream_id, + void *conn_user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_cancel_push` is a callback function which is invoked + * when the push identified by |push_id| is cancelled by remote + * endpoint. If a stream has been bound to the push ID, |stream_id| + * contains the stream ID and |stream_user_data| points to the stream + * user data. Otherwise, |stream_id| is -1 and |stream_user_data| is + * NULL. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_cancel_push)(nghttp3_conn *conn, int64_t push_id, + int64_t stream_id, void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_send_stop_sending` is a callback function which is + * invoked when the library asks application to send STOP_SENDING to + * the stream identified by |stream_id|. |app_error_code| indicates + * the reason for this action. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_send_stop_sending)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_push_stream` is a callback function which is invoked + * when a push stream identified by |stream_id| is opened with + * |push_id|. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_push_stream)(nghttp3_conn *conn, int64_t push_id, + int64_t stream_id, void *conn_user_data); + +/** + * @functypedef + * + * :type:`nghttp3_reset_stream` is a callback function which is + * invoked when the library asks application to reset stream + * identified by |stream_id|. |app_error_code| indicates the reason + * for this action. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning :macro:`NGHTTP3_ERR_CALLBACK_FAILURE` will return to the + * caller immediately. Any values other than 0 is treated as + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + */ +typedef int (*nghttp3_reset_stream)(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code, + void *conn_user_data, + void *stream_user_data); + +/** + * @struct + * + * :type:`nghttp3_callbacks` holds a set of callback functions. + */ +typedef struct nghttp3_callbacks { + /** + * :member:`acked_stream_data` is a callback function which is + * invoked when data sent on a particular stream have been + * acknowledged by a remote endpoint. + */ + nghttp3_acked_stream_data acked_stream_data; + /** + * :member:`stream_close` is a callback function which is invoked + * when a particular stream has closed. + */ + nghttp3_stream_close stream_close; + /** + * :member:`recv_data` is a callback function which is invoked when + * stream data is received. + */ + nghttp3_recv_data recv_data; + /** + * :member:`deferred_consume` is a callback function which is + * invoked when the library consumed data for a particular stream + * which had been blocked for synchronization between streams. + */ + nghttp3_deferred_consume deferred_consume; + /** + * :member:`begin_headers` is a callback function which is invoked + * when a header block has started on a particular stream. + */ + nghttp3_begin_headers begin_headers; + /** + * :member:`recv_header` is a callback function which is invoked + * when a single header field is received on a particular stream. + */ + nghttp3_recv_header recv_header; + /** + * :member:`end_headers` is a callback function which is invoked + * when a header block has ended on a particular stream. + */ + nghttp3_end_headers end_headers; + /** + * :member:`begin_trailers` is a callback function which is invoked + * when a trailer block has started on a particular stream. + */ + nghttp3_begin_headers begin_trailers; + /** + * :member:`recv_trailer` is a callback function which is invoked + * when a single trailer field is received on a particular stream. + */ + nghttp3_recv_header recv_trailer; + /** + * :member:`end_trailers` is a callback function which is invoked + * when a trailer block has ended on a particular stream. + */ + nghttp3_end_headers end_trailers; + /** + * :member:`begin_push_promise` is a callback function which is + * invoked when a push promise has started on a particular stream. + */ + nghttp3_begin_push_promise begin_push_promise; + /** + * :member:`recv_push_promise` is a callback function which is + * invoked when a single header field in a push promise is received + * on a particular stream. + */ + nghttp3_recv_push_promise recv_push_promise; + /** + * :member:`end_push_promise` is a callback function which is + * invoked when a push promise has ended on a particular stream. + */ + nghttp3_end_push_promise end_push_promise; + /** + * :member:`cancel_push` is a callback function which is invoked + * when a push promise has been cancelled by a remote endpoint. + */ + nghttp3_cancel_push cancel_push; + /** + * :member:`send_stop_sending` is a callback function which is + * invoked when the library asks application to send STOP_SENDING to + * a particular stream. + */ + nghttp3_send_stop_sending send_stop_sending; + /** + * :member:`push_stream` is a callback function which is invoked + * when a push stream has opened. + */ + nghttp3_push_stream push_stream; + /** + * :member:`end_stream` is a callback function which is invoked when + * a receiving side of stream has been closed. + */ + nghttp3_end_stream end_stream; + /** + * :member:`reset_stream` is a callback function which is invoked + * when the library asks application to reset stream (by sending + * RESET_STREAM). + */ + nghttp3_reset_stream reset_stream; +} nghttp3_callbacks; + +/** + * @struct + * + * :type:`nghttp3_settings` defines HTTP/3 settings. + */ +typedef struct { + /** + * :member:`max_field_section_size` specifies the maximum header + * section (block) size. + */ + uint64_t max_field_section_size; + /** + * :member:`max_pushes` specifies the maximum number of concurrent + * pushes it accepts from a remote endpoint. + */ + uint64_t max_pushes; + /** + * :member:`qpack_max_table_capacity` is the maximum size of QPACK + * dynamic table. + */ + size_t qpack_max_table_capacity; + /** + * :member:`qpack_blocked_streams` is the maximum number of streams + * which can be blocked while they are being decoded. + */ + size_t qpack_blocked_streams; +} nghttp3_settings; + +/** + * @function + * + * `nghttp3_settings_default` fills |settings| with the default + * values. + */ +NGHTTP3_EXTERN void nghttp3_settings_default(nghttp3_settings *settings); + +/** + * @function + * + * `nghttp3_conn_client_new` creates :type:`nghttp3_conn` and + * initializes it for client use. The pointer to the object is stored + * in |*pconn|. + */ +NGHTTP3_EXTERN int nghttp3_conn_client_new(nghttp3_conn **pconn, + const nghttp3_callbacks *callbacks, + const nghttp3_settings *settings, + const nghttp3_mem *mem, + void *conn_user_data); + +/** + * @function + * + * `nghttp3_conn_server_new` creates :type:`nghttp3_conn` and + * initializes it for server use. The pointer to the object is stored + * in |*pconn|. + */ +NGHTTP3_EXTERN int nghttp3_conn_server_new(nghttp3_conn **pconn, + const nghttp3_callbacks *callbacks, + const nghttp3_settings *settings, + const nghttp3_mem *mem, + void *conn_user_data); + +/** + * @function + * + * `nghttp3_conn_del` frees resources allocated for |conn|. + */ +NGHTTP3_EXTERN void nghttp3_conn_del(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_conn_bind_control_stream` binds stream denoted by + * |stream_id| to outgoing unidirectional control stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_STATE` + * Control stream has already corresponding stream ID. + * + * TBD + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_bind_qpack_streams` binds stream denoted by + * |qenc_stream_id| to outgoing QPACK encoder stream and stream + * denoted by |qdec_stream_id| to outgoing QPACK encoder stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_STATE` + * QPACK encoder/decoder stream have already corresponding stream + * IDs. + * + * TBD + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, + int64_t qenc_stream_id, + int64_t qdec_stream_id); + +/** + * @function + * + * `nghttp3_conn_read_stream` reads data |src| of length |srclen| on + * stream identified by |stream_id|. It returns the number of bytes + * consumed. The "consumed" means that application can increase flow + * control credit (both stream and connection) of underlying QUIC + * connection by that amount. It does not include the amount of data + * carried by DATA frame which contains application data (excluding + * any control or QPACK unidirectional streams) . See + * :type:`nghttp3_recv_data` to handle those bytes. If |fin| is + * nonzero, this is the last data from remote endpoint in this stream. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, + int64_t stream_id, + const uint8_t *src, + size_t srclen, int fin); + +/** + * @function + * + * `nghttp3_conn_writev_stream` stores stream data to send to |vec| of + * length |veccnt| and returns the number of nghttp3_vec object in + * which it stored data. It stores stream ID to |*pstream_id|. An + * application has to call `nghttp3_conn_add_write_offset` to inform + * |conn| of the actual number of bytes that underlying QUIC stack + * accepted. |*pfin| will be nonzero if this is the last data to + * send. If there is no stream to write data or send fin, this + * function returns 0, and -1 is assigned to |*pstream_id|. This + * function may return 0 and |*pstream_id| is not -1 and |*pfin| is + * nonzero. It means 0 length data to |*pstream_id| and it is the + * last data to the stream. They must be passed to QUIC stack, and + * they are accepted, the application has to call + * `nghttp3_conn_add_write_offset`. + */ +NGHTTP3_EXTERN nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, + int64_t *pstream_id, + int *pfin, + nghttp3_vec *vec, + size_t veccnt); + +/** + * @function + * + * `nghttp3_conn_add_write_offset` tells |conn| the number of bytes + * |n| for stream denoted by |stream_id| QUIC stack accepted. + * + * If stream has no data to send but just sends fin (closing the write + * side of a stream), the number of bytes sent is 0. It is important + * to call this function even if |n| is 0 in this case. It is safe to + * call this function if |n| is 0. + * + * `nghttp3_conn_writev_stream` must be called before calling this + * function to get data to send, and those data must be fed into QUIC + * stack. + */ +NGHTTP3_EXTERN int nghttp3_conn_add_write_offset(nghttp3_conn *conn, + int64_t stream_id, size_t n); + +/** + * @function + * + * `nghttp3_conn_add_ack_offset` tells |conn| the number of bytes |n| + * for stream denoted by |stream_id| QUIC stack has acknowledged. + */ +NGHTTP3_EXTERN int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, + int64_t stream_id, uint64_t n); + +/** + * @function + * + * `nghttp3_conn_block_stream` tells the library that stream + * identified by |stream_id| is blocked due to QUIC flow control. + */ +NGHTTP3_EXTERN int nghttp3_conn_block_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_unblock_stream` tells the library that stream + * identified by |stream_id| which was blocked by QUIC flow control is + * unblocked. + */ +NGHTTP3_EXTERN int nghttp3_conn_unblock_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_shutdown_stream_write` tells the library that any + * further write operation to stream identified by |stream_id| is + * prohibited. This works like `nghttp3_conn_block_stream`, but it + * cannot be unblocked by `nghttp3_conn_unblock_stream`. + */ +NGHTTP3_EXTERN int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_resume_stream` resumes stream identified by + * |stream_id| which was previously unable to provide data. + */ +NGHTTP3_EXTERN int nghttp3_conn_resume_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_close_stream` closes stream identified by + * |stream_id|. |app_error_code| is the reason of the closure. + */ +NGHTTP3_EXTERN int nghttp3_conn_close_stream(nghttp3_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @function + * + * `nghttp3_conn_reset_stream` must be called if stream identified by + * |stream_id| is reset by a remote endpoint. This is required in + * order to cancel QPACK stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_reset_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @macrosection + * + * Data flags + */ + +/** + * :macro:`NGHTTP3_DATA_FLAG_NONE` indicates no flag set. + */ +#define NGHTTP3_DATA_FLAG_NONE 0x00 + +/** + * :macro:`NGHTTP3_DATA_FLAG_EOF` indicates that all request or + * response body has been provided to the library. It also indicates + * that sending side of stream is closed unless + * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` is given at the same time. + */ +#define NGHTTP3_DATA_FLAG_EOF 0x01 + +/** + * :macro:`NGHTTP3_DATA_FLAG_NO_END_STREAM` indicates that sending + * side of stream is not closed even if :macro:`NGHTTP3_DATA_FLAG_EOF` + * is set. Usually this flag is used to send trailer fields with + * `nghttp3_conn_submit_trailers()`. If + * `nghttp3_conn_submit_trailers()` has been called, regardless of + * this flag, the submitted trailer fields are sent. + */ +#define NGHTTP3_DATA_FLAG_NO_END_STREAM 0x02 + +/** + * @function + * + * `nghttp3_conn_set_max_client_streams_bidi` tells |conn| the + * cumulative number of bidirectional streams that client can open. + */ +NGHTTP3_EXTERN void +nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn, + uint64_t max_streams); + +/** + * @function + * + * `nghttp3_conn_set_max_concurrent_streams` tells |conn| the maximum + * number of concurrent streams that a remote endpoint can open, + * including both bidirectional and unidirectional streams which + * potentially receive QPACK encoded HEADERS frame. This value is + * used as a hint to limit the internal resource consumption. + */ +NGHTTP3_EXTERN void +nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, + size_t max_concurrent_streams); + +/** + * @functypedef + * + * :type:`nghttp3_read_data_callback` is a callback function invoked + * when the library asks an application to provide stream data for a + * stream denoted by |stream_id|. + * + * The library provides |vec| of length |veccnt| to the application. + * The application should fill data and its length to |vec|. It has + * to return the number of the filled objects. The application must + * retain data until they are safe to free. It is notified by + * :type:`nghttp3_acked_stream_data` callback. + * + * If this is the last data to send (or there is no data to send + * because all data have been sent already), set + * :macro:`NGHTTP3_DATA_FLAG_EOF` to |*pflags|. + * + * If the application is unable to provide data temporarily, return + * :macro:`NGHTTP3_ERR_WOULDBLOCK`. When it is ready to provide + * data, call `nghttp3_conn_resume_stream()`. + * + * The callback should return the number of objects in |vec| that the + * application filled if it succeeds, or + * :macro:`NGHTTP3_ERR_CALLBACK_FAILURE`. + * + * TODO Add NGHTTP3_ERR_TEMPORAL_CALLBACK_FAILURE to reset just this + * stream. + */ +typedef nghttp3_ssize (*nghttp3_read_data_callback)( + nghttp3_conn *conn, int64_t stream_id, nghttp3_vec *vec, size_t veccnt, + uint32_t *pflags, void *conn_user_data, void *stream_user_data); + +/** + * @struct + * + * :type:`nghttp3_data_reader` specifies the way how to generate + * request or response body. + */ +typedef struct { + /** + * :member:`read_data` is a callback function to generate body. + */ + nghttp3_read_data_callback read_data; +} nghttp3_data_reader; + +/** + * @function + * + * `nghttp3_conn_submit_request` submits HTTP request header fields + * and body on the stream identified by |stream_id|. |stream_id| must + * be a client initiated bidirectional stream. Only client can submit + * HTTP request. |nva| of length |nvlen| specifies HTTP request + * header fields. |dr| specifies a request body. If there is no + * request body, specify NULL. If |dr| is NULL, it implies the end of + * stream. |stream_user_data| is an opaque pointer attached to the + * stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_request( + nghttp3_conn *conn, int64_t stream_id, const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr, void *stream_user_data); + +/** + * @function + * + * `nghttp3_conn_submit_push_promise` submits push promise on the + * stream identified by |stream_id|. |stream_id| must be a client + * initiated bidirectional stream. Only server can submit push + * promise. On success, a push ID is assigned to |*ppush_id|. |nva| + * of length |nvlen| specifies HTTP request header fields. In order + * to submit HTTP response, first call + * `nghttp3_conn_bind_push_stream()` and then + * `nghttp3_conn_submit_response()`. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, + int64_t *ppush_id, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_submit_info` submits HTTP non-final response header + * fields on the stream identified by |stream_id|. |nva| of length + * |nvlen| specifies HTTP response header fields. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_info(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_submit_response` submits HTTP response header fields + * and body on the stream identified by |stream_id|. |nva| of length + * |nvlen| specifies HTTP response header fields. |dr| specifies a + * response body. If there is no response body, specify NULL. If + * |dr| is NULL, it implies the end of stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_response(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen, + const nghttp3_data_reader *dr); + +/** + * @function + * + * `nghttp3_conn_submit_trailers` submits HTTP trailer fields on the + * stream identified by |stream_id|. |nva| of length |nvlen| + * specifies HTTP trailer fields. Calling this function implies the + * end of stream. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_trailers(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_nv *nva, + size_t nvlen); + +/** + * @function + * + * `nghttp3_conn_bind_push_stream` binds the stream identified by + * |stream_id| to the push identified by |push_id|. |stream_id| must + * be a server initiated unidirectional stream. |push_id| must be + * obtained from `nghttp3_conn_submit_push_promise()`. To send + * response to this push, call `nghttp3_conn_submit_response()`. + */ +NGHTTP3_EXTERN int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, + int64_t push_id, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_cancel_push` cancels the push identified by + * |push_id|. + */ +NGHTTP3_EXTERN int nghttp3_conn_cancel_push(nghttp3_conn *conn, + int64_t push_id); + +/** + * @function + * + * `nghttp3_conn_submit_shutdown_notice` notifies the other endpoint + * to stop creating new stream (for server) or push (for client). + * After a couple of RTTs later, call `nghttp3_conn_shutdown` to start + * graceful shutdown. + */ +NGHTTP3_EXTERN int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_conn_shutdown` starts graceful shutdown. It should be + * called after `nghttp3_conn_submit_shutdown_notice` and a couple of + * RTT. After calling this function, the local endpoint starts + * rejecting new incoming streams (for server) or pushes (for client). + * The existing streams or pushes are processed normally. + */ +NGHTTP3_EXTERN int nghttp3_conn_shutdown(nghttp3_conn *conn); + +/** + * @function + * + * `nghttp3_conn_set_stream_user_data` sets |stream_user_data| to the + * stream identified by |stream_id|. + */ +NGHTTP3_EXTERN int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, + int64_t stream_id, + void *stream_user_data); + +/** + * @function + * + * `nghttp3_conn_get_frame_payload_left` returns the number of bytes + * left to read current frame payload for a stream denoted by + * |stream_id|. If no such stream is found, it returns + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND`. + */ +NGHTTP3_EXTERN int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @macro + * + * :macro:`NGHTTP3_DEFAULT_URGENCY` is the default urgency level. + */ +#define NGHTTP3_DEFAULT_URGENCY 1 + +/** + * @macro + * + * :macro:`NGHTTP3_URGENCY_HIGH` is the highest urgency level. + */ +#define NGHTTP3_URGENCY_HIGH 0 + +/** + * @macro + * + * :macro:`NGHTTP3_URGENCY_LOW` is the lowest urgency level. + */ +#define NGHTTP3_URGENCY_LOW 7 + +/** + * @macro + * + * :macro:`NGHTTP3_URGENCY_LEVELS` is the number of urgency levels. + */ +#define NGHTTP3_URGENCY_LEVELS (NGHTTP3_URGENCY_LOW + 1) + +/** + * @struct + * + * :type:`nghttp3_pri` represents HTTP priority. + */ +typedef struct nghttp3_pri { + /** + * :member:`urgency` is the urgency of a stream, it must be in + * [:macro:`NGHTTP3_URGENCY_HIGH`, :macro:`NGHTTP3_URGENCY_LOW`], + * inclusive, and 0 is the highest urgency. + */ + uint32_t urgency; + /** + * :member:`inc` indicates that a content can be processed + * incrementally or not. If inc is 0, it cannot be processed + * incrementally. If inc is 1, it can be processed incrementally. + * Other value is not permitted. + */ + int inc; +} nghttp3_pri; + +/** + * @function + * + * `nghttp3_conn_get_stream_priority` stores stream priority of a + * stream denoted by |stream_id| into |*dest|. Only server can use + * this function. + * + * This function must not be called if |conn| is initialized as + * client. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + * Stream not found. + */ +NGHTTP3_EXTERN int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, + nghttp3_pri *dest, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_conn_set_stream_priority` updates stream priority of a + * stream denoted by |stream_id| by the value pointed by |pri|. + * + * This function must not be called if |conn| is initialized as + * client. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_STREAM_NOT_FOUND` + * Stream not found. + * :macro:`NGHTTP3_ERR_NOMEM` + * Out of memory. + */ +NGHTTP3_EXTERN int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, + int64_t stream_id, + const nghttp3_pri *pri); + +/** + * @function + * + * `nghttp3_conn_is_remote_qpack_encoder_stream` returns nonzero if a + * stream denoted by |stream_id| is QPACK encoder stream of a remote + * endpoint. + */ +NGHTTP3_EXTERN int +nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `nghttp3_vec_len` returns the sum of length in |vec| of |cnt| + * elements. + */ +NGHTTP3_EXTERN size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t cnt); + +/** + * @function + * + * `nghttp3_check_header_name` returns nonzero if HTTP header field + * name |name| of length |len| is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + * + * Because this is a header field name in HTTP/3, the upper cased + * alphabet is treated as error. + */ +NGHTTP3_EXTERN int nghttp3_check_header_name(const uint8_t *name, size_t len); + +/** + * @function + * + * `nghttp3_check_header_value` returns nonzero if HTTP header field + * value |value| of length |len| is valid according to + * http://tools.ietf.org/html/rfc7230#section-3.2 + */ +NGHTTP3_EXTERN int nghttp3_check_header_value(const uint8_t *value, size_t len); + +/** + * @function + * + * `nghttp3_http_parse_priority` parses priority HTTP header field + * stored in the buffer pointed by |value| of length |len|. If it + * successfully processed header field value, it stores the result + * into |*dest|. This function just overwrites what it sees in the + * header field value and does not initialize any field in |*dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGHTTP3_ERR_INVALID_ARGUMENT` + * The function could not parse the provided value. + */ +NGHTTP3_EXTERN int nghttp3_http_parse_priority(nghttp3_pri *dest, + const uint8_t *value, + size_t len); + +/** + * @macro + * + * :macro:`NGHTTP3_VERSION_AGE` is the age of :type:`nghttp3_info`. + */ +#define NGHTTP3_VERSION_AGE 1 + +/** + * @struct + * + * :type:`nghttp3_info` is what `nghttp3_version()` returns. It holds + * information about the particular nghttp3 version. + */ +typedef struct { + /** + * :member:`age` is the age of this struct. This instance of + * nghttp3 sets it to :macro:`NGHTTP3_VERSION_AGE` but a future + * version may bump it and add more struct fields at the bottom + */ + int age; + /** + * :member:`version_num` is the :macro:`NGHTTP3_VERSION_NUM` number + * (since age ==1) + */ + int version_num; + /** + * :member:`version_str` points to the :macro:`NGHTTP3_VERSION` + * string (since age ==1) + */ + const char *version_str; + /* -------- the above fields all exist when age == 1 */ +} nghttp3_info; + +/** + * @function + * + * `nghttp3_version` returns a pointer to a :type:`nghttp3_info` + * struct with version information about the run-time library in use. + * The |least_version| argument can be set to a 24 bit numerical value + * for the least accepted version number and if the condition is not + * met, this function will return a ``NULL``. Pass in 0 to skip the + * version checking. + */ +NGHTTP3_EXTERN nghttp3_info *nghttp3_version(int least_version); + +#ifdef __cplusplus +} +#endif + +#endif /* NGHTTP3_H */ diff --git a/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h new file mode 100644 index 00000000000000..69d29e9f140c33 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h @@ -0,0 +1,46 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 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 NGHTTP3_VERSION_H +#define NGHTTP3_VERSION_H + +/** + * @macro + * + * Version number of the nghttp3 library release. + */ +#define NGHTTP3_VERSION "0.1.0-DEV" + +/** + * @macro + * + * Numerical representation of the version number of the nghttp3 + * library release. This is a 24 bit number with 8 bits for major + * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 + * becomes 0x010203. + */ +#define NGHTTP3_VERSION_NUM 0x000100 + +#endif /* NGHTTP3_VERSION_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c new file mode 100644 index 00000000000000..aae075a73cc4be --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.c @@ -0,0 +1,90 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 "nghttp3_buf.h" + +void nghttp3_buf_init(nghttp3_buf *buf) { + buf->begin = buf->end = buf->pos = buf->last = NULL; +} + +void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len) { + buf->begin = buf->pos = buf->last = src; + buf->end = buf->begin + len; +} + +void nghttp3_buf_free(nghttp3_buf *buf, const nghttp3_mem *mem) { + nghttp3_mem_free(mem, buf->begin); +} + +size_t nghttp3_buf_left(const nghttp3_buf *buf) { + return (size_t)(buf->end - buf->last); +} + +size_t nghttp3_buf_len(const nghttp3_buf *buf) { + return (size_t)(buf->last - buf->pos); +} + +size_t nghttp3_buf_cap(const nghttp3_buf *buf) { + return (size_t)(buf->end - buf->begin); +} + +void nghttp3_buf_reset(nghttp3_buf *buf) { buf->pos = buf->last = buf->begin; } + +int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem) { + uint8_t *p; + nghttp3_ssize pos_offset, last_offset; + + if ((size_t)(buf->end - buf->begin) >= size) { + return 0; + } + + pos_offset = buf->pos - buf->begin; + last_offset = buf->last - buf->begin; + + p = nghttp3_mem_realloc(mem, buf->begin, size); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + buf->begin = p; + buf->end = p + size; + buf->pos = p + pos_offset; + buf->last = p + last_offset; + + return 0; +} + +void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b) { + nghttp3_buf c = *a; + + *a = *b; + *b = c; +} + +void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, + nghttp3_buf_type type) { + tbuf->buf = *buf; + tbuf->type = type; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h new file mode 100644 index 00000000000000..472a4b7b14a80e --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_buf.h @@ -0,0 +1,74 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_BUF_H +#define NGHTTP3_BUF_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +void nghttp3_buf_wrap_init(nghttp3_buf *buf, uint8_t *src, size_t len); + +/* + * nghttp3_buf_cap returns the capacity of the buffer. In other + * words, it returns buf->end - buf->begin. + */ +size_t nghttp3_buf_cap(const nghttp3_buf *buf); + +int nghttp3_buf_reserve(nghttp3_buf *buf, size_t size, const nghttp3_mem *mem); + +/* + * nghttp3_buf_swap swaps |a| and |b|. + */ +void nghttp3_buf_swap(nghttp3_buf *a, nghttp3_buf *b); + +typedef enum nghttp3_buf_type { + /* NGHTTP3_BUF_TYPE_PRIVATE indicates that memory is allocated for + this buffer only and should be freed after its use. */ + NGHTTP3_BUF_TYPE_PRIVATE, + /* NGHTTP3_BUF_TYPE_SHARED indicates that buffer points to shared + memory. */ + NGHTTP3_BUF_TYPE_SHARED, + /* NGHTTP3_BUF_TYPE_ALIEN indicates that the buffer points to a + memory which comes from outside of the library. */ + NGHTTP3_BUF_TYPE_ALIEN, +} nghttp3_buf_type; + +typedef struct nghttp3_typed_buf { + nghttp3_buf buf; + nghttp3_buf_type type; +} nghttp3_typed_buf; + +void nghttp3_typed_buf_init(nghttp3_typed_buf *tbuf, const nghttp3_buf *buf, + nghttp3_buf_type type); + +void nghttp3_typed_buf_free(nghttp3_typed_buf *tbuf); + +#endif /* NGHTTP3_BUF_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c new file mode 100644 index 00000000000000..2f9ce7b10ca89e --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.c @@ -0,0 +1,3523 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 "nghttp3_conn.h" + +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include "nghttp3_mem.h" +#include "nghttp3_macro.h" +#include "nghttp3_err.h" +#include "nghttp3_conv.h" +#include "nghttp3_http.h" + +/* + * conn_remote_stream_uni returns nonzero if |stream_id| is remote + * unidirectional stream ID. + */ +static int conn_remote_stream_uni(nghttp3_conn *conn, int64_t stream_id) { + if (conn->server) { + return (stream_id & 0x03) == 0x02; + } + return (stream_id & 0x03) == 0x03; +} + +static int conn_call_begin_headers(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.begin_headers) { + return 0; + } + + rv = conn->callbacks.begin_headers(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_headers(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.end_headers) { + return 0; + } + + rv = conn->callbacks.end_headers(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_begin_trailers(nghttp3_conn *conn, + nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.begin_trailers) { + return 0; + } + + rv = conn->callbacks.begin_trailers(conn, stream->node.nid.id, + conn->user_data, stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_trailers(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.end_trailers) { + return 0; + } + + rv = conn->callbacks.end_trailers(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_begin_push_promise(nghttp3_conn *conn, + nghttp3_stream *stream, + int64_t push_id) { + int rv; + + if (!conn->callbacks.begin_push_promise) { + return 0; + } + + rv = conn->callbacks.begin_push_promise(conn, stream->node.nid.id, push_id, + conn->user_data, stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_push_promise(nghttp3_conn *conn, + nghttp3_stream *stream, int64_t push_id) { + int rv; + + if (!conn->callbacks.end_push_promise) { + return 0; + } + + rv = conn->callbacks.end_push_promise(conn, stream->node.nid.id, push_id, + conn->user_data, stream->user_data); + if (rv != 0) { + /* TODO Allow ignore headers */ + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_end_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.end_stream) { + return 0; + } + + rv = conn->callbacks.end_stream(conn, stream->node.nid.id, conn->user_data, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_cancel_push(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.cancel_push) { + return 0; + } + + rv = conn->callbacks.cancel_push( + conn, push_id, stream ? stream->node.nid.id : -1, conn->user_data, + stream ? stream->user_data : NULL); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_send_stop_sending(nghttp3_conn *conn, + nghttp3_stream *stream, + uint64_t app_error_code) { + int rv; + + if (!conn->callbacks.send_stop_sending) { + return 0; + } + + rv = conn->callbacks.send_stop_sending(conn, stream->node.nid.id, + app_error_code, conn->user_data, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_reset_stream(nghttp3_conn *conn, nghttp3_stream *stream, + uint64_t app_error_code) { + int rv; + + if (!conn->callbacks.reset_stream) { + return 0; + } + + rv = conn->callbacks.reset_stream(conn, stream->node.nid.id, app_error_code, + conn->user_data, stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_push_stream(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream) { + int rv; + + if (!conn->callbacks.push_stream) { + return 0; + } + + rv = conn->callbacks.push_stream(conn, push_id, stream->node.nid.id, + conn->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_deferred_consume(nghttp3_conn *conn, + nghttp3_stream *stream, + size_t nconsumed) { + int rv; + + if (nconsumed == 0 || !conn->callbacks.deferred_consume) { + return 0; + } + + rv = conn->callbacks.deferred_consume(conn, stream->node.nid.id, nconsumed, + conn->user_data, stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int ricnt_less(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + nghttp3_stream *lhs = + nghttp3_struct_of(lhsx, nghttp3_stream, qpack_blocked_pe); + nghttp3_stream *rhs = + nghttp3_struct_of(rhsx, nghttp3_stream, qpack_blocked_pe); + + return lhs->qpack_sctx.ricnt < rhs->qpack_sctx.ricnt; +} + +static int cycle_less(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + const nghttp3_tnode *lhs = nghttp3_struct_of(lhsx, nghttp3_tnode, pe); + const nghttp3_tnode *rhs = nghttp3_struct_of(rhsx, nghttp3_tnode, pe); + + if (lhs->cycle == rhs->cycle) { + return lhs->seq < rhs->seq; + } + + return rhs->cycle - lhs->cycle <= NGHTTP3_TNODE_MAX_CYCLE_GAP; +} + +static int conn_new(nghttp3_conn **pconn, int server, + const nghttp3_callbacks *callbacks, + const nghttp3_settings *settings, const nghttp3_mem *mem, + void *user_data) { + int rv; + nghttp3_conn *conn; + size_t i; + + conn = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_conn)); + if (conn == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_map_init(&conn->streams, mem); + if (rv != 0) { + goto streams_init_fail; + } + + rv = nghttp3_map_init(&conn->pushes, mem); + if (rv != 0) { + goto pushes_init_fail; + } + + rv = nghttp3_qpack_decoder_init(&conn->qdec, + settings->qpack_max_table_capacity, + settings->qpack_blocked_streams, mem); + if (rv != 0) { + goto qdec_init_fail; + } + + rv = nghttp3_qpack_encoder_init(&conn->qenc, 0, 0, mem); + if (rv != 0) { + goto qenc_init_fail; + } + + nghttp3_pq_init(&conn->qpack_blocked_streams, ricnt_less, mem); + + for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { + nghttp3_pq_init(&conn->sched[i].spq, cycle_less, mem); + } + + rv = nghttp3_idtr_init(&conn->remote.bidi.idtr, server, mem); + if (rv != 0) { + goto remote_bidi_idtr_init_fail; + } + + rv = nghttp3_gaptr_init(&conn->remote.uni.push_idtr, mem); + if (rv != 0) { + goto push_idtr_init_fail; + } + + conn->callbacks = *callbacks; + conn->local.settings = *settings; + nghttp3_settings_default(&conn->remote.settings); + conn->mem = mem; + conn->user_data = user_data; + conn->next_seq = 0; + conn->server = server; + conn->rx.goaway_id = NGHTTP3_VARINT_MAX + 1; + conn->tx.goaway_id = NGHTTP3_VARINT_MAX + 1; + conn->rx.max_stream_id_bidi = 0; + conn->rx.max_push_id = 0; + + *pconn = conn; + + return 0; + +push_idtr_init_fail: + nghttp3_idtr_free(&conn->remote.bidi.idtr); +remote_bidi_idtr_init_fail: + nghttp3_qpack_encoder_free(&conn->qenc); +qenc_init_fail: + nghttp3_qpack_decoder_free(&conn->qdec); +qdec_init_fail: + nghttp3_map_free(&conn->pushes); +pushes_init_fail: + nghttp3_map_free(&conn->streams); +streams_init_fail: + nghttp3_mem_free(mem, conn); + + return rv; +} + +int nghttp3_conn_client_new(nghttp3_conn **pconn, + const nghttp3_callbacks *callbacks, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *user_data) { + int rv; + + rv = conn_new(pconn, /* server = */ 0, callbacks, settings, mem, user_data); + if (rv != 0) { + return rv; + } + + (*pconn)->remote.uni.unsent_max_pushes = settings->max_pushes; + + return 0; +} + +int nghttp3_conn_server_new(nghttp3_conn **pconn, + const nghttp3_callbacks *callbacks, + const nghttp3_settings *settings, + const nghttp3_mem *mem, void *user_data) { + int rv; + + rv = conn_new(pconn, /* server = */ 1, callbacks, settings, mem, user_data); + if (rv != 0) { + return rv; + } + + return 0; +} + +static int free_push_promise(nghttp3_map_entry *ent, void *ptr) { + nghttp3_push_promise *pp = nghttp3_struct_of(ent, nghttp3_push_promise, me); + const nghttp3_mem *mem = ptr; + + nghttp3_push_promise_del(pp, mem); + + return 0; +} + +static int free_stream(nghttp3_map_entry *ent, void *ptr) { + nghttp3_stream *stream = nghttp3_struct_of(ent, nghttp3_stream, me); + + (void)ptr; + + nghttp3_stream_del(stream); + + return 0; +} + +void nghttp3_conn_del(nghttp3_conn *conn) { + size_t i; + + if (conn == NULL) { + return; + } + + nghttp3_buf_free(&conn->tx.qpack.ebuf, conn->mem); + nghttp3_buf_free(&conn->tx.qpack.rbuf, conn->mem); + + nghttp3_gaptr_free(&conn->remote.uni.push_idtr); + + nghttp3_idtr_free(&conn->remote.bidi.idtr); + + for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { + nghttp3_pq_free(&conn->sched[i].spq); + } + + nghttp3_pq_free(&conn->qpack_blocked_streams); + + nghttp3_qpack_encoder_free(&conn->qenc); + nghttp3_qpack_decoder_free(&conn->qdec); + + nghttp3_map_each_free(&conn->pushes, free_push_promise, (void *)conn->mem); + nghttp3_map_free(&conn->pushes); + + nghttp3_map_each_free(&conn->streams, free_stream, NULL); + nghttp3_map_free(&conn->streams); + + nghttp3_mem_free(conn->mem, conn); +} + +nghttp3_ssize nghttp3_conn_read_stream(nghttp3_conn *conn, int64_t stream_id, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_stream *stream; + size_t bidi_nproc; + int rv; + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + /* TODO Assert idtr */ + /* QUIC transport ensures that this is new stream. */ + if (conn->server) { + if (nghttp3_client_stream_bidi(stream_id)) { + rv = nghttp3_idtr_open(&conn->remote.bidi.idtr, stream_id); + assert(rv == 0); + + conn->rx.max_stream_id_bidi = + nghttp3_max(conn->rx.max_stream_id_bidi, stream_id); + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + + if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->tx.goaway_id <= stream_id) { + stream->rstate.state = NGHTTP3_REQ_STREAM_STATE_IGN_REST; + + rv = nghttp3_conn_reject_stream(conn, stream); + if (rv != 0) { + return rv; + } + } + } else { + /* unidirectional stream */ + if (srclen == 0 && fin) { + return 0; + } + + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + } + + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + } else if (nghttp3_stream_uni(stream_id)) { + if (srclen == 0 && fin) { + return 0; + } + + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + } else { + /* client doesn't expect to receive new bidirectional stream + from server. */ + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + } else if (conn->server) { + if (nghttp3_client_stream_bidi(stream_id)) { + if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_INITIAL; + } + } + } else if (nghttp3_stream_uni(stream_id) && + stream->type == NGHTTP3_STREAM_TYPE_PUSH) { + if (stream->rx.hstate == NGHTTP3_HTTP_STATE_NONE) { + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + } + } + + if (srclen == 0 && !fin) { + return 0; + } + + if (nghttp3_stream_uni(stream_id)) { + return nghttp3_conn_read_uni(conn, stream, src, srclen, fin); + } + + if (fin) { + stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; + } + return nghttp3_conn_read_bidi(conn, &bidi_nproc, stream, src, srclen, fin); +} + +static nghttp3_ssize conn_read_type(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + int64_t stream_type; + + assert(srclen); + + nread = nghttp3_read_varint(rvint, src, srclen, fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + if (rvint->left) { + return nread; + } + + stream_type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + switch (stream_type) { + case NGHTTP3_STREAM_TYPE_CONTROL: + if (conn->flags & NGHTTP3_CONN_FLAG_CONTROL_OPENED) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + conn->flags |= NGHTTP3_CONN_FLAG_CONTROL_OPENED; + stream->type = NGHTTP3_STREAM_TYPE_CONTROL; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE; + break; + case NGHTTP3_STREAM_TYPE_PUSH: + if (conn->server) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + stream->type = NGHTTP3_STREAM_TYPE_PUSH; + rstate->state = NGHTTP3_PUSH_STREAM_STATE_PUSH_ID; + break; + case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: + if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + conn->flags |= NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED; + stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; + break; + case NGHTTP3_STREAM_TYPE_QPACK_DECODER: + if (conn->flags & NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED) { + return NGHTTP3_ERR_H3_STREAM_CREATION_ERROR; + } + conn->flags |= NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED; + stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; + break; + default: + stream->type = NGHTTP3_STREAM_TYPE_UNKNOWN; + break; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED; + + return nread; +} + +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_ssize nread = 0; + nghttp3_ssize nconsumed = 0; + size_t push_nproc; + int rv; + + assert(srclen || fin); + + if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) { + if (srclen == 0 && fin) { + /* Ignore stream if it is closed before reading stream header. + If it is closed while reading it, return error, making it + consistent in our code base. */ + if (stream->rstate.rvint.left) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + rv = conn_delete_stream(conn, stream); + assert(0 == rv); + + return 0; + } + nread = conn_read_type(conn, stream, src, srclen, fin); + if (nread < 0) { + return (int)nread; + } + if (!(stream->flags & NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED)) { + assert((size_t)nread == srclen); + return (nghttp3_ssize)srclen; + } + + src += nread; + srclen -= (size_t)nread; + + if (srclen == 0) { + return nread; + } + } + + switch (stream->type) { + case NGHTTP3_STREAM_TYPE_CONTROL: + if (fin) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + nconsumed = nghttp3_conn_read_control(conn, stream, src, srclen); + break; + case NGHTTP3_STREAM_TYPE_PUSH: + if (fin) { + stream->flags |= NGHTTP3_STREAM_FLAG_READ_EOF; + } + nconsumed = + nghttp3_conn_read_push(conn, &push_nproc, stream, src, srclen, fin); + break; + case NGHTTP3_STREAM_TYPE_QPACK_ENCODER: + if (fin) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + nconsumed = nghttp3_conn_read_qpack_encoder(conn, src, srclen); + break; + case NGHTTP3_STREAM_TYPE_QPACK_DECODER: + if (fin) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + nconsumed = nghttp3_conn_read_qpack_decoder(conn, src, srclen); + break; + case NGHTTP3_STREAM_TYPE_UNKNOWN: + nconsumed = (nghttp3_ssize)srclen; + + rv = conn_call_send_stop_sending(conn, stream, + NGHTTP3_H3_STREAM_CREATION_ERROR); + if (rv != 0) { + return rv; + } + break; + default: + /* unreachable */ + assert(0); + } + + if (nconsumed < 0) { + return nconsumed; + } + + return nread + nconsumed; +} + +static int frame_fin(nghttp3_stream_read_state *rstate, size_t len) { + return (int64_t)len >= rstate->left; +} + +nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, + nghttp3_stream *stream, + const uint8_t *src, size_t srclen) { + const uint8_t *p = src, *end = src + srclen; + int rv; + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + size_t nconsumed = 0; + int busy = 0; + size_t len; + + assert(srclen); + + for (; p != end || busy;) { + busy = 0; + switch (rstate->state) { + case NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->fr.hd.type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + rstate->state = NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH; + if (p == end) { + break; + } + /* Fall through */ + case NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), /* fin = */ 0); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + rstate->left = rstate->fr.hd.length = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (!(conn->flags & NGHTTP3_CONN_FLAG_SETTINGS_RECVED)) { + if (rstate->fr.hd.type != NGHTTP3_FRAME_SETTINGS) { + return NGHTTP3_ERR_H3_MISSING_SETTINGS; + } + conn->flags |= NGHTTP3_CONN_FLAG_SETTINGS_RECVED; + } else if (rstate->fr.hd.type == NGHTTP3_FRAME_SETTINGS) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + + switch (rstate->fr.hd.type) { + case NGHTTP3_FRAME_CANCEL_PUSH: + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH; + break; + case NGHTTP3_FRAME_SETTINGS: + /* SETTINGS frame might be empty. */ + if (rstate->left == 0) { + nghttp3_stream_read_state_reset(rstate); + break; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS; + break; + case NGHTTP3_FRAME_GOAWAY: + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_GOAWAY; + break; + case NGHTTP3_FRAME_MAX_PUSH_ID: + if (!conn->server) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + rstate->state = NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID; + break; + case NGHTTP3_FRAME_DATA: + case NGHTTP3_FRAME_HEADERS: + case NGHTTP3_FRAME_PUSH_PROMISE: + case NGHTTP3_H2_FRAME_PRIORITY: + case NGHTTP3_H2_FRAME_PING: + case NGHTTP3_H2_FRAME_WINDOW_UPDATE: + case NGHTTP3_H2_FRAME_CONTINUATION: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + default: + /* TODO Handle reserved frame type */ + busy = 1; + rstate->state = NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME; + break; + } + break; + case NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + rstate->fr.cancel_push.push_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (conn->server) { + rv = nghttp3_conn_on_server_cancel_push(conn, &rstate->fr.cancel_push); + } else { + rv = nghttp3_conn_on_client_cancel_push(conn, &rstate->fr.cancel_push); + } + if (rv != 0) { + return rv; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_SETTINGS: + for (; p != end;) { + if (rstate->left == 0) { + nghttp3_stream_read_state_reset(rstate); + break; + } + /* Read Identifier */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID; + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + /* Read Value */ + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + len -= (size_t)nread; + if (len == 0) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; + break; + } + + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + rv = + nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings); + if (rv != 0) { + return rv; + } + } + break; + case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].id = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE; + + if (p == end) { + return (nghttp3_ssize)nconsumed; + } + /* Fall through */ + case NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + rstate->fr.settings.iv[0].value = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + rv = nghttp3_conn_on_settings_entry_received(conn, &rstate->fr.settings); + if (rv != 0) { + return rv; + } + + if (rstate->left) { + rstate->state = NGHTTP3_CTRL_STREAM_STATE_SETTINGS; + break; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_GOAWAY: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + if (conn->server && !nghttp3_client_stream_bidi(rvint->acc)) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + if (conn->rx.goaway_id < rvint->acc) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_RECVED; + conn->rx.goaway_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID: + /* server side only */ + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + assert(len > 0); + nread = nghttp3_read_varint(rvint, p, len, frame_fin(rstate, len)); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + return (nghttp3_ssize)nconsumed; + } + + if (conn->local.uni.max_pushes > (uint64_t)rvint->acc) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + conn->local.uni.max_pushes = (uint64_t)rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + return (nghttp3_ssize)nconsumed; + } + + nghttp3_stream_read_state_reset(rstate); + break; + default: + /* unreachable */ + assert(0); + } + } + + return (nghttp3_ssize)nconsumed; +} + +nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin) { + const uint8_t *p = src, *end = src ? src + srclen : src; + int rv; + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + size_t nconsumed = 0; + int busy = 0; + size_t len; + int64_t push_id; + + if (stream->flags & (NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED | + NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED)) { + *pnproc = 0; + + if (srclen == 0) { + return 0; + } + + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + return 0; + } + + for (; p != end || busy;) { + busy = 0; + switch (rstate->state) { + case NGHTTP3_PUSH_STREAM_STATE_PUSH_ID: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + push_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + rv = nghttp3_conn_on_stream_push_id(conn, stream, push_id); + if (rv != 0) { + if (rv == NGHTTP3_ERR_IGNORE_STREAM) { + rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_REST; + break; + } + return (nghttp3_ssize)rv; + } + + rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE; + + if (stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED) { + if (p != end) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (end == p) { + goto almost_done; + } + + /* Fall through */ + case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->fr.hd.type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + rstate->state = NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH; + if (p == end) { + goto almost_done; + } + /* Fall through */ + case NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->left = rstate->fr.hd.length = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + switch (rstate->fr.hd.type) { + case NGHTTP3_FRAME_DATA: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); + if (rv != 0) { + return rv; + } + /* DATA frame might be empty. */ + if (rstate->left == 0) { + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + rstate->state = NGHTTP3_PUSH_STREAM_STATE_DATA; + break; + case NGHTTP3_FRAME_HEADERS: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); + if (rv != 0) { + return rv; + } + if (rstate->left == 0) { + rv = nghttp3_stream_empty_headers_allowed(stream); + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = conn_call_begin_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_begin_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rstate->state = NGHTTP3_PUSH_STREAM_STATE_HEADERS; + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + case NGHTTP3_FRAME_CANCEL_PUSH: + case NGHTTP3_FRAME_SETTINGS: + case NGHTTP3_FRAME_GOAWAY: + case NGHTTP3_FRAME_MAX_PUSH_ID: + case NGHTTP3_H2_FRAME_PRIORITY: + case NGHTTP3_H2_FRAME_PING: + case NGHTTP3_H2_FRAME_WINDOW_UPDATE: + case NGHTTP3_H2_FRAME_CONTINUATION: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + default: + /* TODO Handle reserved frame type */ + busy = 1; + rstate->state = NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME; + break; + } + break; + case NGHTTP3_PUSH_STREAM_STATE_DATA: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + rv = nghttp3_conn_on_data(conn, stream, p, len); + if (rv != 0) { + return rv; + } + + p += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_PUSH_STREAM_STATE_HEADERS: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = nghttp3_http_on_response_headers(&stream->rx.http); + if (rv != 0) { + return rv; + } + + rv = conn_call_end_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_end_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: + nconsumed += (size_t)(end - p); + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + } + +almost_done: + if (fin) { + switch (rstate->state) { + case NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE: + if (rvint->left) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_MSG_END); + if (rv != 0) { + return rv; + } + rv = conn_call_end_stream(conn, stream); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_PUSH_STREAM_STATE_IGN_REST: + break; + default: + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + } + + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; +} + +static void conn_delete_push_promise(nghttp3_conn *conn, + nghttp3_push_promise *pp) { + int rv; + + rv = nghttp3_map_remove(&conn->pushes, (key_type)pp->node.nid.id); + assert(0 == rv); + + if (!conn->server && + !(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED)) { + ++conn->remote.uni.unsent_max_pushes; + } + + nghttp3_push_promise_del(pp, conn->mem); +} + +static int conn_delete_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int bidi_or_push = nghttp3_stream_bidi_or_push(stream); + int rv; + + if (bidi_or_push) { + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + } + + rv = conn_call_deferred_consume(conn, stream, + nghttp3_stream_get_buffered_datalen(stream)); + if (rv != 0) { + return rv; + } + + if (bidi_or_push && conn->callbacks.stream_close) { + rv = conn->callbacks.stream_close(conn, stream->node.nid.id, + stream->error_code, conn->user_data, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + + rv = nghttp3_map_remove(&conn->streams, (key_type)stream->node.nid.id); + + assert(0 == rv); + + if (stream->pp) { + assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); + + conn_delete_push_promise(conn, stream->pp); + } + + nghttp3_stream_del(stream); + + return 0; +} + +static int conn_process_blocked_stream_data(nghttp3_conn *conn, + nghttp3_stream *stream) { + nghttp3_buf *buf; + size_t nproc; + nghttp3_ssize nconsumed; + int rv; + size_t len; + + for (;;) { + len = nghttp3_ringbuf_len(&stream->inq); + if (len == 0) { + break; + } + buf = nghttp3_ringbuf_get(&stream->inq, 0); + if (nghttp3_stream_uni(stream->node.nid.id)) { + nconsumed = nghttp3_conn_read_push( + conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), + len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); + } else { + nconsumed = nghttp3_conn_read_bidi( + conn, &nproc, stream, buf->pos, nghttp3_buf_len(buf), + len == 1 && (stream->flags & NGHTTP3_STREAM_FLAG_READ_EOF)); + } + if (nconsumed < 0) { + return (int)nconsumed; + } + + buf->pos += nproc; + + rv = conn_call_deferred_consume(conn, stream, (size_t)nconsumed); + if (rv != 0) { + return 0; + } + + if (nghttp3_buf_len(buf) == 0) { + nghttp3_buf_free(buf, stream->mem); + nghttp3_ringbuf_pop_front(&stream->inq); + } + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + break; + } + } + + if (!(stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) && + (stream->flags & NGHTTP3_STREAM_FLAG_CLOSED)) { + assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); + + rv = conn_delete_stream(conn, stream); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen) { + nghttp3_ssize nconsumed = + nghttp3_qpack_decoder_read_encoder(&conn->qdec, src, srclen); + nghttp3_stream *stream; + int rv; + + if (nconsumed < 0) { + return nconsumed; + } + + for (; !nghttp3_pq_empty(&conn->qpack_blocked_streams);) { + stream = nghttp3_struct_of(nghttp3_pq_top(&conn->qpack_blocked_streams), + nghttp3_stream, qpack_blocked_pe); + if (nghttp3_qpack_stream_context_get_ricnt(&stream->qpack_sctx) > + nghttp3_qpack_decoder_get_icnt(&conn->qdec)) { + break; + } + + nghttp3_conn_qpack_blocked_streams_pop(conn); + stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; + stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; + + rv = conn_process_blocked_stream_data(conn, stream); + if (rv != 0) { + return rv; + } + } + + return nconsumed; +} + +nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen) { + return nghttp3_qpack_encoder_read_decoder(&conn->qenc, src, srclen); +} + +static int conn_update_stream_priority(nghttp3_conn *conn, + nghttp3_stream *stream, uint8_t pri) { + if (stream->node.pri == pri) { + return 0; + } + + nghttp3_conn_unschedule_stream(conn, stream); + + stream->node.pri = pri; + + assert(nghttp3_stream_bidi_or_push(stream)); + + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_conn_schedule_stream(conn, stream); + } + + return 0; +} + +nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin) { + const uint8_t *p = src, *end = src ? src + srclen : src; + int rv; + nghttp3_stream_read_state *rstate = &stream->rstate; + nghttp3_varint_read_state *rvint = &rstate->rvint; + nghttp3_ssize nread; + size_t nconsumed = 0; + int busy = 0; + size_t len; + nghttp3_push_promise *pp; + nghttp3_push_promise fake_pp = {{0}, {{0}, 0, {0}, 0, 0, 0}, {0}, NULL, -1, + 0}; + nghttp3_frame_entry frent; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + *pnproc = 0; + + if (srclen == 0) { + return 0; + } + + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + return 0; + } + + for (; p != end || busy;) { + busy = 0; + switch (rstate->state) { + case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->fr.hd.type = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + rstate->state = NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH; + if (p == end) { + goto almost_done; + } + /* Fall through */ + case NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH: + assert(end - p > 0); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), fin); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + if (rvint->left) { + goto almost_done; + } + + rstate->left = rstate->fr.hd.length = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + switch (rstate->fr.hd.type) { + case NGHTTP3_FRAME_DATA: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_BEGIN); + if (rv != 0) { + return rv; + } + /* DATA frame might be empty. */ + if (rstate->left == 0) { + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + rstate->state = NGHTTP3_REQ_STREAM_STATE_DATA; + break; + case NGHTTP3_FRAME_HEADERS: + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_BEGIN); + if (rv != 0) { + return rv; + } + if (rstate->left == 0) { + rv = nghttp3_stream_empty_headers_allowed(stream); + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state( + stream, NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = conn_call_begin_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_begin_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rstate->state = NGHTTP3_REQ_STREAM_STATE_HEADERS; + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + if (conn->server) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + /* No stream->rx.hstate change with PUSH_PROMISE */ + + rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID; + break; + case NGHTTP3_FRAME_CANCEL_PUSH: + case NGHTTP3_FRAME_SETTINGS: + case NGHTTP3_FRAME_GOAWAY: + case NGHTTP3_FRAME_MAX_PUSH_ID: + case NGHTTP3_H2_FRAME_PRIORITY: + case NGHTTP3_H2_FRAME_PING: + case NGHTTP3_H2_FRAME_WINDOW_UPDATE: + case NGHTTP3_H2_FRAME_CONTINUATION: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + default: + /* TODO Handle reserved frame type */ + busy = 1; + rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_FRAME; + break; + } + break; + case NGHTTP3_REQ_STREAM_STATE_DATA: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + rv = nghttp3_conn_on_data(conn, stream, p, len); + if (rv != 0) { + return rv; + } + p += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_DATA_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_read_varint(rvint, p, (size_t)(end - p), + (int64_t)len == rstate->left); + if (nread < 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + if (rvint->left) { + goto almost_done; + } + + rstate->fr.push_promise.push_id = rvint->acc; + nghttp3_varint_read_state_reset(rvint); + + if (rstate->left == 0) { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + rv = nghttp3_conn_on_push_promise_push_id( + conn, rstate->fr.push_promise.push_id, stream); + if (rv != 0) { + if (rv == NGHTTP3_ERR_IGNORE_PUSH_PROMISE) { + rstate->state = NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE; + if (p == end) { + goto almost_done; + } + break; + } + + return rv; + } + + rstate->state = NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE; + + if (p == end) { + goto almost_done; + } + /* Fall through */ + case NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE: + pp = + nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); + + assert(pp); + + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, pp, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + rv = nghttp3_http_on_request_headers(&pp->http); + if (rv != 0) { + return rv; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; + + rv = conn_call_end_push_promise(conn, stream, pp->node.nid.id); + if (rv != 0) { + return rv; + } + + /* Find pp again because application might call + nghttp3_conn_cancel_push and it may delete pp. */ + pp = + nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); + if (!pp) { + nghttp3_stream_read_state_reset(rstate); + break; + } + + if (!pp->stream && (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED)) { + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL) { + rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); + if (rv != 0) { + return rv; + } + } + + conn_delete_push_promise(conn, pp); + + nghttp3_stream_read_state_reset(rstate); + break; + } + + if (pp->stream) { + ++conn->remote.uni.unsent_max_pushes; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; + + if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { + assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); + + rv = conn_call_push_stream(conn, pp->node.nid.id, pp->stream); + if (rv != 0) { + return rv; + } + + pp->stream->flags &= + (uint16_t)~NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; + + rv = conn_process_blocked_stream_data(conn, pp->stream); + if (rv != 0) { + return rv; + } + } + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, &fake_pp, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + pp = + nghttp3_conn_find_push_promise(conn, rstate->fr.push_promise.push_id); + if (pp) { + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECVED; + + if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->tx.goaway_id <= pp->node.nid.id) { + if (pp->stream) { + rv = nghttp3_conn_reject_push_stream(conn, pp->stream); + if (rv != 0) { + return rv; + } + } else { + frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; + frent.fr.cancel_push.push_id = pp->node.nid.id; + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + conn_delete_push_promise(conn, pp); + } + } + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_HEADERS: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + nread = nghttp3_conn_on_headers(conn, stream, NULL, p, len, + (int64_t)len == rstate->left); + if (nread < 0) { + return nread; + } + + p += nread; + nconsumed += (size_t)nread; + rstate->left -= nread; + + if (stream->flags & NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED) { + if (p != end && nghttp3_stream_get_buffered_datalen(stream) == 0) { + rv = nghttp3_stream_buffer_data(stream, p, (size_t)(end - p)); + if (rv != 0) { + return rv; + } + } + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + + if (rstate->left) { + goto almost_done; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + rv = nghttp3_http_on_request_headers(&stream->rx.http); + break; + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = nghttp3_http_on_response_headers(&stream->rx.http); + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = 0; + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + /* Only server utilizes priority information to schedule + streams. */ + if (conn->server) { + rv = conn_update_stream_priority(conn, stream, stream->rx.http.pri); + if (rv != 0) { + return rv; + } + } + /* fall through */ + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + rv = conn_call_end_headers(conn, stream); + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + rv = conn_call_end_trailers(conn, stream); + break; + default: + /* Unreachable */ + assert(0); + } + + if (rv != 0) { + return rv; + } + + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_HEADERS_END); + assert(0 == rv); + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_IGN_FRAME: + len = (size_t)nghttp3_min(rstate->left, (int64_t)(end - p)); + p += len; + nconsumed += len; + rstate->left -= (int64_t)len; + + if (rstate->left) { + goto almost_done; + } + + nghttp3_stream_read_state_reset(rstate); + break; + case NGHTTP3_REQ_STREAM_STATE_IGN_REST: + nconsumed += (size_t)(end - p); + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; + } + } + +almost_done: + if (fin) { + switch (rstate->state) { + case NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE: + if (rvint->left) { + return NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR; + } + rv = nghttp3_stream_transit_rx_http_state(stream, + NGHTTP3_HTTP_EVENT_MSG_END); + if (rv != 0) { + return rv; + } + rv = conn_call_end_stream(conn, stream); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_REQ_STREAM_STATE_IGN_REST: + break; + default: + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + } + + *pnproc = (size_t)(p - src); + return (nghttp3_ssize)nconsumed; +} + +int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *data, size_t datalen) { + int rv; + + rv = nghttp3_http_on_data_chunk(stream, datalen); + if (rv != 0) { + return rv; + } + + if (!conn->callbacks.recv_data) { + return 0; + } + + rv = conn->callbacks.recv_data(conn, stream->node.nid.id, data, datalen, + conn->user_data, stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int push_idtr_push(nghttp3_gaptr *push_idtr, int64_t push_id) { + int rv; + + rv = nghttp3_gaptr_push(push_idtr, (uint64_t)push_id, 1); + if (rv != 0) { + return rv; + } + + /* Server SHOULD use push ID sequentially, but even if it does, we + might see gaps in push IDs because we might stop reading stream + which ignores PUSH_PROMISE. In order to limit the number of + gaps, drop earlier gaps if certain limit is reached. This makes + otherwise valid push ignored.*/ + if (nghttp3_ksl_len(&push_idtr->gap) > 100) { + nghttp3_gaptr_drop_first_gap(push_idtr); + } + + return 0; +} + +int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream) { + int rv; + nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; + nghttp3_push_promise *pp; + + if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp) { + if ((pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_BOUND) || + (pp->stream_id != -1 && pp->stream_id != stream->node.nid.id)) { + return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; + } + if (pp->stream) { + assert(pp->stream->flags & NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED); + /* Push unidirectional stream has already been received and + blocked */ + } else if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED) { + /* We will call begin_push_promise callback even if push is + cancelled */ + } else { + return NGHTTP3_ERR_H3_FRAME_ERROR; + } + + assert(pp->stream_id == -1); + + pp->stream_id = stream->node.nid.id; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; + } else if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)push_id, 1)) { + return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; + } else { + rv = push_idtr_push(push_idtr, push_id); + if (rv != 0) { + return rv; + } + + rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); + if (rv != 0) { + return rv; + } + } + + conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, push_id); + + if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->tx.goaway_id <= push_id) { + return NGHTTP3_ERR_IGNORE_PUSH_PROMISE; + } + + rv = conn_call_begin_push_promise(conn, stream, push_id); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr) { + nghttp3_push_promise *pp; + nghttp3_gaptr *push_idtr = &conn->remote.uni.push_idtr; + int rv; + + if (conn->remote.uni.max_pushes <= (uint64_t)fr->push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + pp = nghttp3_conn_find_push_promise(conn, fr->push_id); + if (pp == NULL) { + if (nghttp3_gaptr_is_pushed(push_idtr, (uint64_t)fr->push_id, 1)) { + /* push is already cancelled or server is misbehaving */ + return 0; + } + + /* We have not received PUSH_PROMISE yet */ + rv = push_idtr_push(push_idtr, fr->push_id); + if (rv != 0) { + return rv; + } + + conn->rx.max_push_id = nghttp3_max(conn->rx.max_push_id, fr->push_id); + + rv = nghttp3_conn_create_push_promise(conn, &pp, fr->push_id, NULL); + if (rv != 0) { + return rv; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; + + /* cancel_push callback will be called after PUSH_PROMISE frame is + completely received. */ + + return 0; + } + + if (pp->stream) { + return 0; + } + + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { + rv = conn_call_cancel_push(conn, pp->node.nid.id, pp->stream); + if (rv != 0) { + return rv; + } + + conn_delete_push_promise(conn, pp); + + return 0; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL; + + return 0; +} + +static nghttp3_pq *conn_get_sched_pq(nghttp3_conn *conn, nghttp3_tnode *tnode) { + uint32_t urgency = nghttp3_pri_uint8_urgency(tnode->pri); + + assert(urgency < NGHTTP3_URGENCY_LEVELS); + + return &conn->sched[urgency].spq; +} + +int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr) { + nghttp3_push_promise *pp; + nghttp3_stream *stream; + int rv; + + if (conn->local.uni.next_push_id <= fr->push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + pp = nghttp3_conn_find_push_promise(conn, fr->push_id); + if (pp == NULL) { + return 0; + } + + stream = pp->stream; + + rv = conn_call_cancel_push(conn, fr->push_id, stream); + if (rv != 0) { + return rv; + } + + if (stream) { + rv = nghttp3_conn_close_stream(conn, stream->node.nid.id, + NGHTTP3_H3_REQUEST_CANCELLED); + if (rv != 0) { + assert(NGHTTP3_ERR_STREAM_NOT_FOUND != rv); + return rv; + } + return 0; + } + + nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); + + conn_delete_push_promise(conn, pp); + + return 0; +} + +int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, + int64_t push_id) { + nghttp3_push_promise *pp; + int rv; + + if (nghttp3_gaptr_is_pushed(&conn->remote.uni.push_idtr, (uint64_t)push_id, + 1)) { + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp) { + if (pp->stream) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + pp->stream = stream; + stream->pp = pp; + + assert(!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)); + + if ((conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->tx.goaway_id <= push_id) { + rv = nghttp3_conn_reject_push_stream(conn, stream); + if (rv != 0) { + return rv; + } + return NGHTTP3_ERR_IGNORE_STREAM; + } + + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED) { + ++conn->remote.uni.unsent_max_pushes; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED; + + return conn_call_push_stream(conn, push_id, stream); + } + + stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; + + return 0; + } + + /* Push ID has been received, but pp is gone. This means that + push is cancelled or server is misbehaving. We have no + information to distinguish the two, so just cancel QPACK stream + just in case, and ask application to send STOP_SENDING and + ignore all frames in this stream. */ + rv = nghttp3_conn_cancel_push_stream(conn, stream); + if (rv != 0) { + return rv; + } + return NGHTTP3_ERR_IGNORE_STREAM; + } + + if (conn->remote.uni.max_pushes <= (uint64_t)push_id) { + return NGHTTP3_ERR_H3_ID_ERROR; + } + + rv = push_idtr_push(&conn->remote.uni.push_idtr, push_id); + if (rv != 0) { + return rv; + } + + /* Don't know the associated stream of PUSH_PROMISE. It doesn't + matter because client sends nothing to this stream. */ + rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, NULL); + if (rv != 0) { + return rv; + } + + pp->stream = stream; + stream->pp = pp; + stream->flags |= NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED; + + return 0; +} + +static nghttp3_ssize conn_decode_headers(nghttp3_conn *conn, + nghttp3_stream *stream, + nghttp3_push_promise *pp, + const uint8_t *src, size_t srclen, + int fin) { + nghttp3_ssize nread; + int rv; + nghttp3_qpack_decoder *qdec = &conn->qdec; + nghttp3_qpack_nv nv; + uint8_t flags; + nghttp3_buf buf; + nghttp3_recv_header recv_header = NULL; + nghttp3_http_state *http; + int request = 0; + int trailers = 0; + int ignore_pp = 0; + + if (pp) { + request = 1; + ignore_pp = pp->stream_id != stream->node.nid.id; + if (ignore_pp) { + http = NULL; + } else { + http = &pp->http; + } + } else { + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + request = 1; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + recv_header = conn->callbacks.recv_header; + break; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + request = 1; + /* Fall through */ + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + trailers = 1; + recv_header = conn->callbacks.recv_trailer; + break; + default: + /* Unreachable */ + assert(0); + } + http = &stream->rx.http; + } + + nghttp3_buf_wrap_init(&buf, (uint8_t *)src, srclen); + buf.last = buf.end; + + for (;;) { + nread = nghttp3_qpack_decoder_read_request(qdec, &stream->qpack_sctx, &nv, + &flags, buf.pos, + nghttp3_buf_len(&buf), fin); + + if (nread < 0) { + return (int)nread; + } + + buf.pos += nread; + + if (flags & NGHTTP3_QPACK_DECODE_FLAG_BLOCKED) { + if (conn->local.settings.qpack_blocked_streams <= + nghttp3_pq_size(&conn->qpack_blocked_streams)) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED; + rv = nghttp3_conn_qpack_blocked_streams_push(conn, stream); + if (rv != 0) { + return rv; + } + break; + } + + if (flags & NGHTTP3_QPACK_DECODE_FLAG_FINAL) { + nghttp3_qpack_stream_context_reset(&stream->qpack_sctx); + break; + } + + if (nread == 0) { + break; + } + + if (flags & NGHTTP3_QPACK_DECODE_FLAG_EMIT) { + if (ignore_pp) { + nghttp3_rcbuf_decref(nv.name); + nghttp3_rcbuf_decref(nv.value); + + continue; + } + + assert(http); + + rv = nghttp3_http_on_header(http, stream->rstate.fr.hd.type, &nv, request, + trailers); + switch (rv) { + case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: + break; + case NGHTTP3_ERR_REMOVE_HTTP_HEADER: + rv = 0; + break; + case 0: + if (pp) { + if (conn->callbacks.recv_push_promise) { + rv = conn->callbacks.recv_push_promise( + conn, stream->node.nid.id, pp->node.nid.id, nv.token, nv.name, + nv.value, nv.flags, conn->user_data, stream->user_data); + } + break; + } + if (recv_header) { + rv = recv_header(conn, stream->node.nid.id, nv.token, nv.name, + nv.value, nv.flags, conn->user_data, + stream->user_data); + } + break; + default: + /* Unreachable */ + assert(0); + } + + nghttp3_rcbuf_decref(nv.name); + nghttp3_rcbuf_decref(nv.value); + + if (rv != 0) { + return rv; + } + } + } + + return buf.pos - src; +} + +nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, + nghttp3_stream *stream, + nghttp3_push_promise *pp, + const uint8_t *src, size_t srclen, + int fin) { + if (srclen == 0 && !fin) { + return 0; + } + + return conn_decode_headers(conn, stream, pp, src, srclen, fin); +} + +int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, + const nghttp3_frame_settings *fr) { + const nghttp3_settings_entry *ent = &fr->iv[0]; + nghttp3_settings *dest = &conn->remote.settings; + int rv; + size_t max_table_capacity = SIZE_MAX; + size_t max_blocked_streams = SIZE_MAX; + + /* TODO Check for duplicates */ + switch (ent->id) { + case NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE: + dest->max_field_section_size = ent->value; + break; + case NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY: + dest->qpack_max_table_capacity = (size_t)ent->value; + max_table_capacity = + nghttp3_min(max_table_capacity, dest->qpack_max_table_capacity); + rv = nghttp3_qpack_encoder_set_hard_max_dtable_size(&conn->qenc, + max_table_capacity); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_set_max_dtable_size(&conn->qenc, + max_table_capacity); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS: + dest->qpack_blocked_streams = (size_t)ent->value; + max_blocked_streams = + nghttp3_min(max_blocked_streams, dest->qpack_blocked_streams); + rv = + nghttp3_qpack_encoder_set_max_blocked(&conn->qenc, max_blocked_streams); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH: + case NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS: + case NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE: + case NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE: + return NGHTTP3_ERR_H3_SETTINGS_ERROR; + default: + /* Ignore unknown settings ID */ + break; + } + + return 0; +} + +static int conn_stream_acked_data(nghttp3_stream *stream, int64_t stream_id, + size_t datalen, void *user_data) { + nghttp3_conn *conn = stream->conn; + int rv; + + if (!conn->callbacks.acked_stream_data) { + return 0; + } + + rv = conn->callbacks.acked_stream_data(conn, stream_id, datalen, + conn->user_data, user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, + int64_t stream_id) { + nghttp3_stream *stream; + int rv; + nghttp3_stream_callbacks callbacks = { + conn_stream_acked_data, + }; + + rv = nghttp3_stream_new(&stream, stream_id, conn->next_seq, &callbacks, + conn->mem); + if (rv != 0) { + return rv; + } + + stream->conn = conn; + + rv = nghttp3_map_insert(&conn->streams, &stream->me); + if (rv != 0) { + nghttp3_stream_del(stream); + return rv; + } + + ++conn->next_seq; + *pstream = stream; + + return 0; +} + +int nghttp3_conn_create_push_promise(nghttp3_conn *conn, + nghttp3_push_promise **ppp, + int64_t push_id, + nghttp3_tnode *assoc_tnode) { + nghttp3_push_promise *pp; + int rv; + + rv = nghttp3_push_promise_new(&pp, push_id, conn->next_seq, assoc_tnode, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_map_insert(&conn->pushes, &pp->me); + if (rv != 0) { + nghttp3_push_promise_del(pp, conn->mem); + return rv; + } + + ++conn->next_seq; + *ppp = pp; + + return 0; +} + +nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, + int64_t stream_id) { + nghttp3_map_entry *me; + + me = nghttp3_map_find(&conn->streams, (key_type)stream_id); + if (me == NULL) { + return NULL; + } + + return nghttp3_struct_of(me, nghttp3_stream, me); +} + +nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, + int64_t push_id) { + nghttp3_map_entry *me; + + me = nghttp3_map_find(&conn->pushes, (key_type)push_id); + if (me == NULL) { + return NULL; + } + + return nghttp3_struct_of(me, nghttp3_push_promise, me); +} + +int nghttp3_conn_bind_control_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream; + nghttp3_frame_entry frent; + int rv; + + assert(!conn->server || nghttp3_server_stream_uni(stream_id)); + assert(conn->server || nghttp3_client_stream_uni(stream_id)); + + if (conn->tx.ctrl) { + return NGHTTP3_ERR_INVALID_STATE; + } + + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_CONTROL; + + conn->tx.ctrl = stream; + + rv = nghttp3_stream_write_stream_type(stream); + if (rv != 0) { + return rv; + } + + frent.fr.hd.type = NGHTTP3_FRAME_SETTINGS; + frent.aux.settings.local_settings = &conn->local.settings; + + return nghttp3_stream_frq_add(stream, &frent); +} + +int nghttp3_conn_bind_qpack_streams(nghttp3_conn *conn, int64_t qenc_stream_id, + int64_t qdec_stream_id) { + nghttp3_stream *stream; + int rv; + + assert(!conn->server || nghttp3_server_stream_uni(qenc_stream_id)); + assert(!conn->server || nghttp3_server_stream_uni(qdec_stream_id)); + assert(conn->server || nghttp3_client_stream_uni(qenc_stream_id)); + assert(conn->server || nghttp3_client_stream_uni(qdec_stream_id)); + + if (conn->tx.qenc || conn->tx.qdec) { + return NGHTTP3_ERR_INVALID_STATE; + } + + rv = nghttp3_conn_create_stream(conn, &stream, qenc_stream_id); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_QPACK_ENCODER; + + conn->tx.qenc = stream; + + rv = nghttp3_stream_write_stream_type(stream); + if (rv != 0) { + return rv; + } + + rv = nghttp3_conn_create_stream(conn, &stream, qdec_stream_id); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_QPACK_DECODER; + + conn->tx.qdec = stream; + + return nghttp3_stream_write_stream_type(stream); +} + +static nghttp3_ssize conn_writev_stream(nghttp3_conn *conn, int64_t *pstream_id, + int *pfin, nghttp3_vec *vec, + size_t veccnt, nghttp3_stream *stream) { + int rv; + nghttp3_ssize n; + + assert(veccnt > 0); + + /* If stream is blocked by read callback, don't attempt to fill + more. */ + if (!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)) { + rv = nghttp3_stream_fill_outq(stream); + if (rv != 0) { + return rv; + } + } + + if (!nghttp3_stream_uni(stream->node.nid.id) && conn->tx.qenc && + !nghttp3_stream_is_blocked(conn->tx.qenc)) { + n = nghttp3_stream_writev(conn->tx.qenc, pfin, vec, veccnt); + if (n < 0) { + return n; + } + if (n) { + *pstream_id = conn->tx.qenc->node.nid.id; + return n; + } + } + + n = nghttp3_stream_writev(stream, pfin, vec, veccnt); + if (n < 0) { + return n; + } + /* We might just want to write stream fin without sending any stream + data. */ + if (n == 0 && *pfin == 0) { + return 0; + } + + *pstream_id = stream->node.nid.id; + + return n; +} + +nghttp3_ssize nghttp3_conn_writev_stream(nghttp3_conn *conn, + int64_t *pstream_id, int *pfin, + nghttp3_vec *vec, size_t veccnt) { + nghttp3_ssize ncnt; + nghttp3_stream *stream; + int rv; + + *pstream_id = -1; + *pfin = 0; + + if (veccnt == 0) { + return 0; + } + + if (conn->tx.ctrl && !nghttp3_stream_is_blocked(conn->tx.ctrl)) { + if (!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED) && + !(conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) && + conn->remote.uni.unsent_max_pushes > conn->remote.uni.max_pushes) { + rv = nghttp3_conn_submit_max_push_id(conn); + if (rv != 0) { + return rv; + } + } + + ncnt = + conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.ctrl); + if (ncnt) { + return ncnt; + } + } + + if (conn->tx.qdec && !nghttp3_stream_is_blocked(conn->tx.qdec)) { + rv = nghttp3_stream_write_qpack_decoder_stream(conn->tx.qdec); + if (rv != 0) { + return rv; + } + + ncnt = + conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qdec); + if (ncnt) { + return ncnt; + } + } + + if (conn->tx.qenc && !nghttp3_stream_is_blocked(conn->tx.qenc)) { + ncnt = + conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, conn->tx.qenc); + if (ncnt) { + return ncnt; + } + } + + stream = nghttp3_conn_get_next_tx_stream(conn); + if (stream == NULL) { + return 0; + } + + ncnt = conn_writev_stream(conn, pstream_id, pfin, vec, veccnt, stream); + if (ncnt < 0) { + return ncnt; + } + + if (nghttp3_stream_bidi_or_push(stream) && + !nghttp3_stream_require_schedule(stream)) { + nghttp3_conn_unschedule_stream(conn, stream); + } + + return ncnt; +} + +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn) { + size_t i; + nghttp3_tnode *tnode; + nghttp3_pq *pq; + + for (i = 0; i < NGHTTP3_URGENCY_LEVELS; ++i) { + pq = &conn->sched[i].spq; + if (nghttp3_pq_empty(pq)) { + continue; + } + + tnode = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); + + if (tnode->nid.type == NGHTTP3_NODE_ID_TYPE_PUSH) { + return nghttp3_struct_of(tnode, nghttp3_push_promise, node)->stream; + } + + return nghttp3_struct_of(tnode, nghttp3_stream, node); + } + + return NULL; +} + +int nghttp3_conn_add_write_offset(nghttp3_conn *conn, int64_t stream_id, + size_t n) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + int rv; + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + rv = nghttp3_stream_add_outq_offset(stream, n); + if (rv != 0) { + return rv; + } + + stream->unscheduled_nwrite += n; + + if (!nghttp3_stream_bidi_or_push(stream)) { + return 0; + } + + if (!nghttp3_stream_require_schedule(stream)) { + nghttp3_conn_unschedule_stream(conn, stream); + return 0; + } + + if (stream->unscheduled_nwrite < NGHTTP3_STREAM_MIN_WRITELEN) { + return 0; + } + + return nghttp3_conn_schedule_stream(conn, stream); +} + +int nghttp3_conn_add_ack_offset(nghttp3_conn *conn, int64_t stream_id, + uint64_t n) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + return nghttp3_stream_add_ack_offset(stream, n); +} + +static int conn_submit_headers_data(nghttp3_conn *conn, nghttp3_stream *stream, + const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr) { + int rv; + nghttp3_nv *nnva; + nghttp3_frame_entry frent; + + rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); + if (rv != 0) { + return rv; + } + + frent.fr.hd.type = NGHTTP3_FRAME_HEADERS; + frent.fr.headers.nva = nnva; + frent.fr.headers.nvlen = nvlen; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + nghttp3_nva_del(nnva, conn->mem); + return rv; + } + + if (dr) { + frent.fr.hd.type = NGHTTP3_FRAME_DATA; + frent.aux.data.dr = *dr; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + return rv; + } + } + + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_conn_schedule_stream(conn, stream); + } + + return 0; +} + +static nghttp3_tnode *stream_get_dependency_node(nghttp3_stream *stream) { + if (stream->pp) { + assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); + return &stream->pp->node; + } + + return &stream->node; +} + +int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + /* Assume that stream stays on the same urgency level */ + int rv; + + rv = nghttp3_tnode_schedule(stream_get_dependency_node(stream), + conn_get_sched_pq(conn, &stream->node), + stream->unscheduled_nwrite); + if (rv != 0) { + return rv; + } + + stream->unscheduled_nwrite = 0; + + return 0; +} + +int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, + nghttp3_stream *stream) { + if (nghttp3_tnode_is_scheduled(stream_get_dependency_node(stream))) { + return 0; + } + + return nghttp3_conn_schedule_stream(conn, stream); +} + +void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, + nghttp3_stream *stream) { + nghttp3_tnode_unschedule(stream_get_dependency_node(stream), + conn_get_sched_pq(conn, &stream->node)); +} + +int nghttp3_conn_submit_request(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr, + void *stream_user_data) { + nghttp3_stream *stream; + int rv; + + assert(!conn->server); + assert(conn->tx.qenc); + + assert(nghttp3_client_stream_bidi(stream_id)); + + /* TODO Should we check that stream_id is client stream_id? */ + /* TODO Check GOAWAY last stream ID */ + if (nghttp3_stream_uni(stream_id)) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { + return NGHTTP3_ERR_CONN_CLOSING; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream != NULL) { + return NGHTTP3_ERR_STREAM_IN_USE; + } + + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_INITIAL; + stream->tx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + stream->user_data = stream_user_data; + + nghttp3_http_record_request_method(stream, nva, nvlen); + + if (dr == NULL) { + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + } + + return conn_submit_headers_data(conn, stream, nva, nvlen, dr); +} + +int nghttp3_conn_submit_info(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen) { + nghttp3_stream *stream; + + /* TODO Verify that it is allowed to send info (non-final response) + now. */ + assert(conn->server); + assert(conn->tx.qenc); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); +} + +int nghttp3_conn_submit_response(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen, + const nghttp3_data_reader *dr) { + nghttp3_stream *stream; + + /* TODO Verify that it is allowed to send response now. */ + assert(conn->server); + assert(conn->tx.qenc); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (dr == NULL) { + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + } + + return conn_submit_headers_data(conn, stream, nva, nvlen, dr); +} + +int nghttp3_conn_submit_trailers(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen) { + nghttp3_stream *stream; + + /* TODO Verify that it is allowed to send trailer now. */ + assert(conn->tx.qenc); + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM) { + return NGHTTP3_ERR_INVALID_STATE; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + + return conn_submit_headers_data(conn, stream, nva, nvlen, NULL); +} + +int nghttp3_conn_submit_push_promise(nghttp3_conn *conn, int64_t *ppush_id, + int64_t stream_id, const nghttp3_nv *nva, + size_t nvlen) { + nghttp3_stream *stream; + int rv; + nghttp3_nv *nnva; + nghttp3_frame_entry frent; + int64_t push_id; + nghttp3_push_promise *pp; + + assert(conn->server); + assert(conn->tx.qenc); + assert(nghttp3_client_stream_bidi(stream_id)); + + if (conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_RECVED) { + return NGHTTP3_ERR_CONN_CLOSING; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (conn->local.uni.max_pushes <= (uint64_t)conn->local.uni.next_push_id) { + return NGHTTP3_ERR_PUSH_ID_BLOCKED; + } + + push_id = conn->local.uni.next_push_id; + + rv = nghttp3_conn_create_push_promise(conn, &pp, push_id, &stream->node); + if (rv != 0) { + return rv; + } + + ++conn->local.uni.next_push_id; + + rv = nghttp3_nva_copy(&nnva, nva, nvlen, conn->mem); + if (rv != 0) { + return rv; + } + + frent.fr.hd.type = NGHTTP3_FRAME_PUSH_PROMISE; + frent.fr.push_promise.push_id = push_id; + frent.fr.push_promise.nva = nnva; + frent.fr.push_promise.nvlen = nvlen; + + rv = nghttp3_stream_frq_add(stream, &frent); + if (rv != 0) { + nghttp3_nva_del(nnva, conn->mem); + return rv; + } + + *ppush_id = push_id; + + if (nghttp3_stream_require_schedule(stream)) { + return nghttp3_conn_schedule_stream(conn, stream); + } + + return 0; +} + +int nghttp3_conn_bind_push_stream(nghttp3_conn *conn, int64_t push_id, + int64_t stream_id) { + nghttp3_push_promise *pp; + nghttp3_stream *stream; + int rv; + + assert(conn->server); + assert(nghttp3_server_stream_uni(stream_id)); + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp == NULL) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + assert(NULL == nghttp3_conn_find_stream(conn, stream_id)); + + rv = nghttp3_conn_create_stream(conn, &stream, stream_id); + if (rv != 0) { + return rv; + } + + stream->type = NGHTTP3_STREAM_TYPE_PUSH; + stream->pp = pp; + + pp->stream = stream; + + rv = nghttp3_stream_write_stream_type_push_id(stream); + if (rv != 0) { + return rv; + } + + return 0; +} + +int nghttp3_conn_cancel_push(nghttp3_conn *conn, int64_t push_id) { + if (conn->server) { + return nghttp3_conn_server_cancel_push(conn, push_id); + } + return nghttp3_conn_client_cancel_push(conn, push_id); +} + +int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id) { + nghttp3_push_promise *pp; + nghttp3_frame_entry frent; + int rv; + + assert(conn->tx.ctrl); + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp == NULL) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL)) { + frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; + frent.fr.cancel_push.push_id = push_id; + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; + } + + if (pp->stream) { + /* CANCEL_PUSH will be sent, but it does not affect pushed stream. + Stream should be explicitly cancelled. */ + rv = conn_call_reset_stream(conn, pp->stream, NGHTTP3_H3_REQUEST_CANCELLED); + if (rv != 0) { + return rv; + } + } + + nghttp3_tnode_unschedule(&pp->node, conn_get_sched_pq(conn, &pp->node)); + + conn_delete_push_promise(conn, pp); + + return 0; +} + +int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id) { + nghttp3_push_promise *pp; + nghttp3_frame_entry frent; + int rv; + + pp = nghttp3_conn_find_push_promise(conn, push_id); + if (pp == NULL) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) { + return 0; + } + + if (!(pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED)) { + return NGHTTP3_ERR_INVALID_STATE; + } + + if (pp->stream) { + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL; + return nghttp3_conn_cancel_push_stream(conn, pp->stream); + } + + frent.fr.hd.type = NGHTTP3_FRAME_CANCEL_PUSH; + frent.fr.cancel_push.push_id = push_id; + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + conn_delete_push_promise(conn, pp); + + return 0; +} + +int nghttp3_conn_submit_shutdown_notice(nghttp3_conn *conn) { + nghttp3_frame_entry frent; + int rv; + + assert(conn->tx.ctrl); + + frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; + frent.fr.goaway.id = conn->server ? (1ull << 62) - 4 : (1ull << 62) - 1; + + assert(frent.fr.goaway.id <= conn->tx.goaway_id); + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + conn->tx.goaway_id = frent.fr.goaway.id; + conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED; + + return 0; +} + +int nghttp3_conn_shutdown(nghttp3_conn *conn) { + nghttp3_frame_entry frent; + int rv; + + assert(conn->tx.ctrl); + + frent.fr.hd.type = NGHTTP3_FRAME_GOAWAY; + if (conn->server) { + frent.fr.goaway.id = + nghttp3_min((1ll << 62) - 4, conn->rx.max_stream_id_bidi + 4); + } else { + frent.fr.goaway.id = nghttp3_min((1ll << 62) - 1, conn->rx.max_push_id + 1); + } + + assert(frent.fr.goaway.id <= conn->tx.goaway_id); + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + conn->tx.goaway_id = frent.fr.goaway.id; + conn->flags |= NGHTTP3_CONN_FLAG_GOAWAY_QUEUED; + + return 0; +} + +int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream) { + int rv; + + rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); + if (rv != 0) { + return rv; + } + + rv = conn_call_send_stop_sending(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); + if (rv != 0) { + return rv; + } + + return conn_call_reset_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); +} + +static int conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream, + uint64_t app_error_code) { + int rv; + + /* TODO Send Stream Cancellation if we have not processed all + incoming stream data up to fin */ + rv = nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream->node.nid.id); + if (rv != 0) { + return rv; + } + + return conn_call_send_stop_sending(conn, stream, app_error_code); +} + +int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, + nghttp3_stream *stream) { + return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_REJECTED); +} + +int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, + nghttp3_stream *stream) { + return conn_reject_push_stream(conn, stream, NGHTTP3_H3_REQUEST_CANCELLED); +} + +int nghttp3_conn_block_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_FC_BLOCKED; + stream->unscheduled_nwrite = 0; + + if (nghttp3_stream_bidi_or_push(stream)) { + nghttp3_conn_unschedule_stream(conn, stream); + } + + return 0; +} + +int nghttp3_conn_shutdown_stream_write(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags |= NGHTTP3_STREAM_FLAG_SHUT_WR; + stream->unscheduled_nwrite = 0; + + if (nghttp3_stream_bidi_or_push(stream)) { + nghttp3_conn_unschedule_stream(conn, stream); + } + + return 0; +} + +int nghttp3_conn_unblock_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_FC_BLOCKED; + + if (nghttp3_stream_bidi_or_push(stream) && + nghttp3_stream_require_schedule(stream)) { + return nghttp3_conn_ensure_stream_scheduled(conn, stream); + } + + return 0; +} + +int nghttp3_conn_resume_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->flags &= (uint16_t)~NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; + + if (nghttp3_stream_bidi_or_push(stream) && + nghttp3_stream_require_schedule(stream)) { + return nghttp3_conn_ensure_stream_scheduled(conn, stream); + } + + return 0; +} + +int nghttp3_conn_close_stream(nghttp3_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + if (nghttp3_stream_uni(stream_id) && + stream->type != NGHTTP3_STREAM_TYPE_PUSH && + stream->type != NGHTTP3_STREAM_TYPE_UNKNOWN) { + return NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM; + } + + stream->error_code = app_error_code; + + nghttp3_conn_unschedule_stream(conn, stream); + + if (stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX && + (conn->server || !stream->pp || + (stream->pp->flags & NGHTTP3_PUSH_PROMISE_FLAG_RECVED))) { + return conn_delete_stream(conn, stream); + } + + stream->flags |= NGHTTP3_STREAM_FLAG_CLOSED; + return 0; +} + +int nghttp3_conn_reset_stream(nghttp3_conn *conn, int64_t stream_id) { + nghttp3_stream *stream; + + stream = nghttp3_conn_find_stream(conn, stream_id); + if (stream) { + stream->flags |= NGHTTP3_STREAM_FLAG_RESET; + } + return nghttp3_qpack_decoder_cancel_stream(&conn->qdec, stream_id); +} + +int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, + nghttp3_stream *stream) { + assert(stream->qpack_blocked_pe.index == NGHTTP3_PQ_BAD_INDEX); + + return nghttp3_pq_push(&conn->qpack_blocked_streams, + &stream->qpack_blocked_pe); +} + +void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn) { + assert(!nghttp3_pq_empty(&conn->qpack_blocked_streams)); + nghttp3_pq_pop(&conn->qpack_blocked_streams); +} + +void nghttp3_conn_set_max_client_streams_bidi(nghttp3_conn *conn, + uint64_t max_streams) { + assert(conn->server); + assert(conn->remote.bidi.max_client_streams <= max_streams); + + conn->remote.bidi.max_client_streams = max_streams; +} + +void nghttp3_conn_set_max_concurrent_streams(nghttp3_conn *conn, + size_t max_concurrent_streams) { + nghttp3_qpack_decoder_set_max_concurrent_streams(&conn->qdec, + max_concurrent_streams); +} + +int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn) { + nghttp3_frame_entry frent; + int rv; + + assert(conn->tx.ctrl); + assert(!(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED)); + + frent.fr.hd.type = NGHTTP3_FRAME_MAX_PUSH_ID; + /* The acutal push_id is set when frame is serialized */ + + rv = nghttp3_stream_frq_add(conn->tx.ctrl, &frent); + if (rv != 0) { + return rv; + } + + conn->flags |= NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; + + return 0; +} + +int nghttp3_conn_set_stream_user_data(nghttp3_conn *conn, int64_t stream_id, + void *stream_user_data) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->user_data = stream_user_data; + + return 0; +} + +int64_t nghttp3_conn_get_frame_payload_left(nghttp3_conn *conn, + int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + return stream->rstate.left; +} + +int nghttp3_conn_get_stream_priority(nghttp3_conn *conn, nghttp3_pri *dest, + int64_t stream_id) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + assert(conn->server); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + dest->urgency = nghttp3_pri_uint8_urgency(stream->rx.http.pri); + dest->inc = nghttp3_pri_uint8_inc(stream->rx.http.pri); + + return 0; +} + +int nghttp3_conn_set_stream_priority(nghttp3_conn *conn, int64_t stream_id, + const nghttp3_pri *pri) { + nghttp3_stream *stream = nghttp3_conn_find_stream(conn, stream_id); + + assert(conn->server); + assert(pri->urgency < NGHTTP3_URGENCY_LEVELS); + assert(pri->inc == 0 || pri->inc == 1); + + if (stream == NULL) { + return NGHTTP3_ERR_STREAM_NOT_FOUND; + } + + stream->rx.http.pri = nghttp3_pri_to_uint8(pri); + + return conn_update_stream_priority(conn, stream, stream->rx.http.pri); +} + +int nghttp3_conn_is_remote_qpack_encoder_stream(nghttp3_conn *conn, + int64_t stream_id) { + nghttp3_stream *stream; + + if (!conn_remote_stream_uni(conn, stream_id)) { + return 0; + } + + stream = nghttp3_conn_find_stream(conn, stream_id); + return stream && stream->type == NGHTTP3_STREAM_TYPE_QPACK_ENCODER; +} + +void nghttp3_settings_default(nghttp3_settings *settings) { + memset(settings, 0, sizeof(nghttp3_settings)); + settings->max_field_section_size = NGHTTP3_VARINT_MAX; +} + +int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, + uint64_t seq, nghttp3_tnode *assoc_tnode, + const nghttp3_mem *mem) { + nghttp3_push_promise *pp; + nghttp3_node_id nid; + + pp = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_push_promise)); + if (pp == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init( + &pp->node, nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_PUSH, push_id), + seq, NGHTTP3_DEFAULT_URGENCY); + + pp->me.key = (key_type)push_id; + pp->node.nid.id = push_id; + pp->http.status_code = -1; + pp->http.content_length = -1; + + if (assoc_tnode) { + assert(assoc_tnode->nid.type == NGHTTP3_NODE_ID_TYPE_STREAM); + + pp->stream_id = assoc_tnode->nid.id; + pp->flags |= NGHTTP3_PUSH_PROMISE_FLAG_BOUND; + } else { + pp->stream_id = -1; + } + + *ppp = pp; + + return 0; +} + +void nghttp3_push_promise_del(nghttp3_push_promise *pp, + const nghttp3_mem *mem) { + if (pp == NULL) { + return; + } + + nghttp3_tnode_free(&pp->node); + + nghttp3_mem_free(mem, pp); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h new file mode 100644 index 00000000000000..f0f012e177001d --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conn.h @@ -0,0 +1,289 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 NGHTTP3_CONN_H +#define NGHTTP3_CONN_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_stream.h" +#include "nghttp3_map.h" +#include "nghttp3_qpack.h" +#include "nghttp3_tnode.h" +#include "nghttp3_idtr.h" +#include "nghttp3_gaptr.h" + +#define NGHTTP3_VARINT_MAX ((1ull << 62) - 1) + +/* NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY is the maximum dynamic + table size for QPACK encoder. */ +#define NGHTTP3_QPACK_ENCODER_MAX_TABLE_CAPACITY 16384 + +/* NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS is the maximum number of + blocked streams for QPACK encoder. */ +#define NGHTTP3_QPACK_ENCODER_MAX_BLOCK_STREAMS 100 + +/* NGHTTP3_PUSH_PROMISE_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_PUSH_PROMISE_FLAG_NONE 0x00 +/* NGHTTP3_PUSH_PROMISE_FLAG_RECVED is set when PUSH_PROMISE is + completely received. */ +#define NGHTTP3_PUSH_PROMISE_FLAG_RECVED 0x01 +/* NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL is set when push is + cancelled by server before receiving PUSH_PROMISE completely. + This flag should have no effect if push stream has already + opened. */ +#define NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL 0x02 +/* NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL is set when push is + canceled by the local endpoint. */ +#define NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL 0x04 +#define NGHTTP3_PUSH_PROMISE_FLAG_CANCELLED \ + (NGHTTP3_PUSH_PROMISE_FLAG_RECV_CANCEL | \ + NGHTTP3_PUSH_PROMISE_FLAG_SENT_CANCEL) +/* NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED indicates that + unsent_max_pushes has been updated for this push ID. */ +#define NGHTTP3_PUSH_PROMISE_FLAG_PUSH_ID_RECLAIMED 0x08 +/* NGHTTP3_PUSH_PROMISE_FLAG_BOUND is set if nghttp3_push_promise + object is bound to a stream where PUSH_PROMISE frame is received + first. nghttp3_push_promise object might be created before + receiving any PUSH_PROMISE when pushed stream is received before + it.*/ +#define NGHTTP3_PUSH_PROMISE_FLAG_BOUND 0x10 + +typedef struct nghttp3_push_promise { + nghttp3_map_entry me; + nghttp3_tnode node; + nghttp3_http_state http; + /* stream is server initiated unidirectional stream which fulfils + the push promise. */ + nghttp3_stream *stream; + /* stream_id is the stream ID where this PUSH_PROMISE is first + received. PUSH_PROMISE with same push ID is allowed to be sent + in the multiple streams. We ignore those duplicated PUSH_PROMISE + entirely because we don't see any value to add extra cost of + processing for it. Even ignoring those frame is not yet easy + because we have to decode the header blocks. Server push is + overly complex and there is no good use case after all. */ + int64_t stream_id; + /* flags is bitwise OR of zero or more of + NGHTTP3_PUSH_PROMISE_FLAG_*. */ + uint16_t flags; +} nghttp3_push_promise; + +/* NGHTTP3_CONN_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_CONN_FLAG_NONE 0x0000 +/* NGHTTP3_CONN_FLAG_SETTINGS_RECVED is set when SETTINGS frame has + been received. */ +#define NGHTTP3_CONN_FLAG_SETTINGS_RECVED 0x0001 +/* NGHTTP3_CONN_FLAG_CONTROL_OPENED is set when a control stream has + opened. */ +#define NGHTTP3_CONN_FLAG_CONTROL_OPENED 0x0002 +/* NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED is set when a QPACK encoder + stream has opened. */ +#define NGHTTP3_CONN_FLAG_QPACK_ENCODER_OPENED 0x0004 +/* NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED is set when a QPACK decoder + stream has opened. */ +#define NGHTTP3_CONN_FLAG_QPACK_DECODER_OPENED 0x0008 +/* NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED indicates that MAX_PUSH_ID has + been queued to control stream. */ +#define NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED 0x0010 +/* NGHTTP3_CONN_FLAG_GOAWAY_RECVED indicates that GOAWAY frame has + received. */ +#define NGHTTP3_CONN_FLAG_GOAWAY_RECVED 0x0020 +/* NGHTTP3_CONN_FLAG_GOAWAY_QUEUED indicates that GOAWAY frame has + been submitted for transmission. */ +#define NGHTTP3_CONN_FLAG_GOAWAY_QUEUED 0x0040 + +struct nghttp3_conn { + nghttp3_callbacks callbacks; + nghttp3_map streams; + nghttp3_map pushes; + nghttp3_qpack_decoder qdec; + nghttp3_qpack_encoder qenc; + nghttp3_pq qpack_blocked_streams; + struct { + nghttp3_pq spq; + } sched[NGHTTP3_URGENCY_LEVELS]; + const nghttp3_mem *mem; + void *user_data; + int server; + uint16_t flags; + uint64_t next_seq; + + struct { + nghttp3_settings settings; + struct { + /* max_pushes is the number of push IDs that local endpoint can + issue. This field is used by server only. */ + uint64_t max_pushes; + /* next_push_id is the next push ID server uses. This field is + used by server only. */ + int64_t next_push_id; + } uni; + } local; + + struct { + struct { + nghttp3_idtr idtr; + /* max_client_streams is the cumulative number of client + initiated bidirectional stream ID the remote endpoint can + issue. This field is used on server side only. */ + uint64_t max_client_streams; + } bidi; + struct { + /* push_idtr tracks which push ID has been used by remote + server. This field is used by client only. */ + nghttp3_gaptr push_idtr; + /* unsent_max_pushes is the maximum number of push which the local + endpoint can accept. This limit is not yet notified to the + remote endpoint. This field is used by client only. */ + uint64_t unsent_max_pushes; + /* max_push is the maximum number of push which the local + endpoint can accept. This field is used by client only. */ + uint64_t max_pushes; + } uni; + nghttp3_settings settings; + } remote; + + struct { + /* goaway_id is the latest ID received in GOAWAY frame. */ + int64_t goaway_id; + + int64_t max_stream_id_bidi; + int64_t max_push_id; + } rx; + + struct { + struct { + nghttp3_buf rbuf; + nghttp3_buf ebuf; + } qpack; + nghttp3_stream *ctrl; + nghttp3_stream *qenc; + nghttp3_stream *qdec; + /* goaway_id is the latest ID sent in GOAWAY frame. */ + int64_t goaway_id; + } tx; +}; + +nghttp3_stream *nghttp3_conn_find_stream(nghttp3_conn *conn, int64_t stream_id); + +nghttp3_push_promise *nghttp3_conn_find_push_promise(nghttp3_conn *conn, + int64_t push_id); + +int nghttp3_conn_create_stream(nghttp3_conn *conn, nghttp3_stream **pstream, + int64_t stream_id); + +int nghttp3_conn_create_push_promise(nghttp3_conn *conn, + nghttp3_push_promise **ppp, + int64_t push_id, + nghttp3_tnode *assoc_tnode); + +nghttp3_ssize nghttp3_conn_read_bidi(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin); + +nghttp3_ssize nghttp3_conn_read_uni(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *src, size_t srclen, int fin); + +nghttp3_ssize nghttp3_conn_read_control(nghttp3_conn *conn, + nghttp3_stream *stream, + const uint8_t *src, size_t srclen); + +nghttp3_ssize nghttp3_conn_read_push(nghttp3_conn *conn, size_t *pnproc, + nghttp3_stream *stream, const uint8_t *src, + size_t srclen, int fin); + +nghttp3_ssize nghttp3_conn_read_qpack_encoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen); + +nghttp3_ssize nghttp3_conn_read_qpack_decoder(nghttp3_conn *conn, + const uint8_t *src, + size_t srclen); + +int nghttp3_conn_on_push_promise_push_id(nghttp3_conn *conn, int64_t push_id, + nghttp3_stream *stream); + +int nghttp3_conn_on_client_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr); + +int nghttp3_conn_on_server_cancel_push(nghttp3_conn *conn, + const nghttp3_frame_cancel_push *fr); + +int nghttp3_conn_on_stream_push_id(nghttp3_conn *conn, nghttp3_stream *stream, + int64_t push_id); + +int nghttp3_conn_on_data(nghttp3_conn *conn, nghttp3_stream *stream, + const uint8_t *data, size_t datalen); + +nghttp3_ssize nghttp3_conn_on_headers(nghttp3_conn *conn, + nghttp3_stream *stream, + nghttp3_push_promise *pp, + const uint8_t *data, size_t datalen, + int fin); + +int nghttp3_conn_on_settings_entry_received(nghttp3_conn *conn, + const nghttp3_frame_settings *fr); + +int nghttp3_conn_qpack_blocked_streams_push(nghttp3_conn *conn, + nghttp3_stream *stream); + +void nghttp3_conn_qpack_blocked_streams_pop(nghttp3_conn *conn); + +int nghttp3_conn_server_cancel_push(nghttp3_conn *conn, int64_t push_id); + +int nghttp3_conn_client_cancel_push(nghttp3_conn *conn, int64_t push_id); + +int nghttp3_conn_submit_max_push_id(nghttp3_conn *conn); + +int nghttp3_conn_schedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +int nghttp3_conn_ensure_stream_scheduled(nghttp3_conn *conn, + nghttp3_stream *stream); + +void nghttp3_conn_unschedule_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +int nghttp3_conn_reject_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +int nghttp3_conn_reject_push_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +int nghttp3_conn_cancel_push_stream(nghttp3_conn *conn, nghttp3_stream *stream); + +/* + * nghttp3_conn_get_next_tx_stream returns next stream to send. It + * returns NULL if there is no such stream. + */ +nghttp3_stream *nghttp3_conn_get_next_tx_stream(nghttp3_conn *conn); + +int nghttp3_push_promise_new(nghttp3_push_promise **ppp, int64_t push_id, + uint64_t seq, nghttp3_tnode *assoc_tnode, + const nghttp3_mem *mem); + +void nghttp3_push_promise_del(nghttp3_push_promise *pp, const nghttp3_mem *mem); + +#endif /* NGHTTP3_CONN_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c new file mode 100644 index 00000000000000..04bf6a0f298b3b --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.c @@ -0,0 +1,134 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 "nghttp3_conv.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp3_str.h" + +int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p) { + union { + char b[8]; + uint16_t n16; + uint32_t n32; + uint64_t n64; + } n; + + *plen = 1u << (*p >> 6); + + switch (*plen) { + case 1: + return (int64_t)*p; + case 2: + memcpy(&n, p, 2); + n.b[0] &= 0x3f; + return (int64_t)ntohs(n.n16); + case 4: + memcpy(&n, p, 4); + n.b[0] &= 0x3f; + return (int64_t)ntohl(n.n32); + case 8: + memcpy(&n, p, 8); + n.b[0] &= 0x3f; + return (int64_t)nghttp3_ntohl64(n.n64); + } + + assert(0); +} + +int64_t nghttp3_get_varint_fb(const uint8_t *p) { return *p & 0x3f; } + +size_t nghttp3_get_varint_len(const uint8_t *p) { return 1u << (*p >> 6); } + +uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n) { + n = nghttp3_htonl64(n); + return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n) { + n = nghttp3_htonl64(n); + return nghttp3_cpymem(p, ((const uint8_t *)&n) + 2, 6); +} + +uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n) { + n = htonl(n); + return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n) { + n = htonl(n); + return nghttp3_cpymem(p, ((const uint8_t *)&n) + 1, 3); +} + +uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n) { + n = htons(n); + return nghttp3_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n) { + uint8_t *rv; + if (n < 64) { + *p++ = (uint8_t)n; + return p; + } + if (n < 16384) { + rv = nghttp3_put_uint16be(p, (uint16_t)n); + *p |= 0x40; + return rv; + } + if (n < 1073741824) { + rv = nghttp3_put_uint32be(p, (uint32_t)n); + *p |= 0x80; + return rv; + } + assert(n < 4611686018427387904LL); + rv = nghttp3_put_uint64be(p, (uint64_t)n); + *p |= 0xc0; + return rv; +} + +size_t nghttp3_put_varint_len(int64_t n) { + if (n < 64) { + return 1; + } + if (n < 16384) { + return 2; + } + if (n < 1073741824) { + return 4; + } + assert(n < 4611686018427387904LL); + return 8; +} + +uint64_t nghttp3_ord_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2) + 1; +} + +uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri) { + return (uint8_t)((uint32_t)pri->inc << 7 | pri->urgency); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h new file mode 100644 index 00000000000000..860326385ba434 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_conv.h @@ -0,0 +1,221 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_CONV_H +#define NGHTTP3_CONV_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_BYTESWAP_H +# include <byteswap.h> +#endif /* HAVE_BYTESWAP_H */ + +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include <sys/endian.h> +#endif /* HAVE_SYS_ENDIAN_H */ + +#include <nghttp3/nghttp3.h> + +#if defined(HAVE_BSWAP_64) || \ + (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) +# define nghttp3_bswap64 bswap_64 +#else /* !HAVE_BSWAP_64 */ +# define nghttp3_bswap64(N) \ + ((uint64_t)(ntohl((uint32_t)(N))) << 32 | ntohl((uint32_t)((N) >> 32))) +#endif /* !HAVE_BSWAP_64 */ + +#if defined(HAVE_BE64TOH) || \ + (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) +# define nghttp3_ntohl64(N) be64toh(N) +# define nghttp3_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# if defined(WORDS_BIGENDIAN) +# define nghttp3_ntohl64(N) (N) +# define nghttp3_htonl64(N) (N) +# else /* !WORDS_BIGENDIAN */ +# define nghttp3_ntohl64(N) nghttp3_bswap64(N) +# define nghttp3_htonl64(N) nghttp3_bswap64(N) +# endif /* !WORDS_BIGENDIAN */ +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family of functions. We + define inline functions for those functions so that we don't have + dependency on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#endif /* WIN32 */ + +/* + * nghttp3_get_varint reads variable-length integer from |p|, and + * returns it in host byte order. The number of bytes read is stored + * in |*plen|. + */ +int64_t nghttp3_get_varint(size_t *plen, const uint8_t *p); + +/* + * nghttp3_get_varint_fb reads first byte of encoded variable-length + * integer from |p|. + */ +int64_t nghttp3_get_varint_fb(const uint8_t *p); + +/* + * nghttp3_get_varint_len returns the required number of bytes to read + * variable-length integer starting at |p|. + */ +size_t nghttp3_get_varint_len(const uint8_t *p); + +/* + * nghttp3_put_uint64be writes |n| in host byte order in |p| in + * network byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *nghttp3_put_uint64be(uint8_t *p, uint64_t n); + +/* + * nghttp3_put_uint48be writes |n| in host byte order in |p| in + * network byte order. It writes only least significant 48 bits. It + * returns the one beyond of the last written position. + */ +uint8_t *nghttp3_put_uint48be(uint8_t *p, uint64_t n); + +/* + * nghttp3_put_uint32be writes |n| in host byte order in |p| in + * network byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *nghttp3_put_uint32be(uint8_t *p, uint32_t n); + +/* + * nghttp3_put_uint24be writes |n| in host byte order in |p| in + * network byte order. It writes only least significant 24 bits. It + * returns the one beyond of the last written position. + */ +uint8_t *nghttp3_put_uint24be(uint8_t *p, uint32_t n); + +/* + * nghttp3_put_uint16be writes |n| in host byte order in |p| in + * network byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *nghttp3_put_uint16be(uint8_t *p, uint16_t n); + +/* + * nghttp3_put_varint writes |n| in |p| using variable-length integer + * encoding. It returns the one beyond of the last written position. + */ +uint8_t *nghttp3_put_varint(uint8_t *p, int64_t n); + +/* + * nghttp3_put_varint_len returns the required number of bytes to + * encode |n|. + */ +size_t nghttp3_put_varint_len(int64_t n); + +/* + * nghttp3_ord_stream_id returns the ordinal number of |stream_id|. + */ +uint64_t nghttp3_ord_stream_id(int64_t stream_id); + +/* + * NGHTTP3_PRI_INC_MASK is a bit mask to retrieve incremental bit from + * a value produced by nghttp3_pri_to_uint8. + */ +#define NGHTTP3_PRI_INC_MASK (1 << 7) + +/* + * nghttp3_pri_to_uint8 encodes |pri| into uint8_t variable. + */ +uint8_t nghttp3_pri_to_uint8(const nghttp3_pri *pri); + +/* + * nghttp3_pri_uint8_urgency extracts urgency from |PRI| which is + * supposed to be constructed by nghttp3_pri_to_uint8. + */ +#define nghttp3_pri_uint8_urgency(PRI) \ + ((uint32_t)((PRI) & ~NGHTTP3_PRI_INC_MASK)) + +/* + * nghttp3_pri_uint8_inc extracts inc from |PRI| which is supposed to + * be constructed by nghttp3_pri_to_uint8. + */ +#define nghttp3_pri_uint8_inc(PRI) (((PRI)&NGHTTP3_PRI_INC_MASK) != 0) + +#endif /* NGHTTP3_CONV_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_debug.c b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.c new file mode 100644 index 00000000000000..4021b0dc469b66 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.c @@ -0,0 +1,61 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 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 "nghttp3_debug.h" + +#include <stdio.h> + +#ifdef DEBUGBUILD + +static void nghttp3_default_debug_vfprintf_callback(const char *fmt, + va_list args) { + vfprintf(stderr, fmt, args); +} + +static nghttp3_debug_vprintf_callback static_debug_vprintf_callback = + nghttp3_default_debug_vfprintf_callback; + +void nghttp3_debug_vprintf(const char *format, ...) { + if (static_debug_vprintf_callback) { + va_list args; + va_start(args, format); + static_debug_vprintf_callback(format, args); + va_end(args); + } +} + +void nghttp3_set_debug_vprintf_callback( + nghttp3_debug_vprintf_callback debug_vprintf_callback) { + static_debug_vprintf_callback = debug_vprintf_callback; +} + +#else /* !DEBUGBUILD */ + +void nghttp3_set_debug_vprintf_callback( + nghttp3_debug_vprintf_callback debug_vprintf_callback) { + (void)debug_vprintf_callback; +} + +#endif /* !DEBUGBUILD */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_debug.h b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.h new file mode 100644 index 00000000000000..01ed918414cfe5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_debug.h @@ -0,0 +1,44 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 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 NGHTTP3_DEBUG_H +#define NGHTTP3_DEBUG_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#ifdef DEBUGBUILD +# define DEBUGF(...) nghttp3_debug_vprintf(__VA_ARGS__) +void nghttp3_debug_vprintf(const char *format, ...); +#else +# define DEBUGF(...) \ + do { \ + } while (0) +#endif + +#endif /* NGHTTP3_DEBUG_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.c b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c new file mode 100644 index 00000000000000..fb1353ea8a45c5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.c @@ -0,0 +1,126 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 "nghttp3_err.h" + +const char *nghttp3_strerror(int liberr) { + switch (liberr) { + case NGHTTP3_ERR_INVALID_ARGUMENT: + return "ERR_INVALID_ARGUMENT"; + case NGHTTP3_ERR_NOBUF: + return "ERR_NOBUF"; + case NGHTTP3_ERR_INVALID_STATE: + return "ERR_INVALID_STATE"; + case NGHTTP3_ERR_WOULDBLOCK: + return "ERR_WOULDBLOCK"; + case NGHTTP3_ERR_STREAM_IN_USE: + return "ERR_STREAM_IN_USE"; + case NGHTTP3_ERR_PUSH_ID_BLOCKED: + return "ERR_PUSH_ID_BLOCKED"; + case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: + return "ERR_MALFORMED_HTTP_HEADER"; + case NGHTTP3_ERR_REMOVE_HTTP_HEADER: + return "ERR_REMOVE_HTTP_HEADER"; + case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING: + return "ERR_MALFORMED_HTTP_MESSAGING"; + case NGHTTP3_ERR_QPACK_FATAL: + return "ERR_QPACK_FATAL"; + case NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE: + return "ERR_QPACK_HEADER_TOO_LARGE"; + case NGHTTP3_ERR_IGNORE_STREAM: + return "ERR_IGNORE_STREAM"; + case NGHTTP3_ERR_STREAM_NOT_FOUND: + return "ERR_STREAM_NOT_FOUND"; + case NGHTTP3_ERR_IGNORE_PUSH_PROMISE: + return "ERR_IGNORE_PUSH_PROMISE"; + case NGHTTP3_ERR_CONN_CLOSING: + return "ERR_CONN_CLOSING"; + case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: + return "ERR_QPACK_DECOMPRESSION_FAILED"; + case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: + return "ERR_QPACK_ENCODER_STREAM_ERROR"; + case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR: + return "ERR_QPACK_DECODER_STREAM_ERROR"; + case NGHTTP3_ERR_H3_FRAME_UNEXPECTED: + return "ERR_H3_FRAME_UNEXPECTED"; + case NGHTTP3_ERR_H3_FRAME_ERROR: + return "ERR_H3_FRAME_ERROR"; + case NGHTTP3_ERR_H3_MISSING_SETTINGS: + return "ERR_H3_MISSING_SETTINGS"; + case NGHTTP3_ERR_H3_INTERNAL_ERROR: + return "ERR_H3_INTERNAL_ERROR"; + case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: + return "ERR_CLOSED_CRITICAL_STREAM"; + case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR: + return "ERR_H3_GENERAL_PROTOCOL_ERROR"; + case NGHTTP3_ERR_H3_ID_ERROR: + return "ERR_H3_ID_ERROR"; + case NGHTTP3_ERR_H3_SETTINGS_ERROR: + return "ERR_H3_SETTINGS_ERROR"; + case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR: + return "ERR_H3_STREAM_CREATION_ERROR"; + case NGHTTP3_ERR_NOMEM: + return "ERR_NOMEM"; + case NGHTTP3_ERR_CALLBACK_FAILURE: + return "ERR_CALLBACK_FAILURE"; + default: + return "(unknown)"; + } +} + +uint64_t nghttp3_err_infer_quic_app_error_code(int liberr) { + switch (liberr) { + case 0: + return NGHTTP3_H3_NO_ERROR; + case NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED: + return NGHTTP3_QPACK_DECOMPRESSION_FAILED; + case NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR: + return NGHTTP3_QPACK_ENCODER_STREAM_ERROR; + case NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR: + return NGHTTP3_QPACK_DECODER_STREAM_ERROR; + case NGHTTP3_ERR_H3_FRAME_UNEXPECTED: + return NGHTTP3_H3_FRAME_UNEXPECTED; + case NGHTTP3_ERR_H3_FRAME_ERROR: + return NGHTTP3_H3_FRAME_ERROR; + case NGHTTP3_ERR_H3_MISSING_SETTINGS: + return NGHTTP3_H3_MISSING_SETTINGS; + case NGHTTP3_ERR_H3_INTERNAL_ERROR: + return NGHTTP3_H3_INTERNAL_ERROR; + case NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM: + return NGHTTP3_H3_CLOSED_CRITICAL_STREAM; + case NGHTTP3_ERR_H3_GENERAL_PROTOCOL_ERROR: + return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; + case NGHTTP3_ERR_H3_ID_ERROR: + return NGHTTP3_H3_ID_ERROR; + case NGHTTP3_ERR_H3_SETTINGS_ERROR: + return NGHTTP3_H3_SETTINGS_ERROR; + case NGHTTP3_ERR_H3_STREAM_CREATION_ERROR: + return NGHTTP3_H3_STREAM_CREATION_ERROR; + case NGHTTP3_ERR_MALFORMED_HTTP_HEADER: + case NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING: + return NGHTTP3_H3_MESSAGE_ERROR; + default: + return NGHTTP3_H3_GENERAL_PROTOCOL_ERROR; + } +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_err.h b/deps/ngtcp2/nghttp3/lib/nghttp3_err.h new file mode 100644 index 00000000000000..2fa914f86b189e --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_err.h @@ -0,0 +1,34 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 NGHTTP3_ERR_H +#define NGHTTP3_ERR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#endif /* NGHTTP3_ERR_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c new file mode 100644 index 00000000000000..6be82c6edaf492 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.c @@ -0,0 +1,218 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 "nghttp3_frame.h" + +#include <string.h> + +#include "nghttp3_conv.h" +#include "nghttp3_str.h" + +uint8_t *nghttp3_frame_write_hd(uint8_t *p, const nghttp3_frame_hd *hd) { + p = nghttp3_put_varint(p, hd->type); + p = nghttp3_put_varint(p, hd->length); + return p; +} + +size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd) { + return nghttp3_put_varint_len(hd->type) + nghttp3_put_varint_len(hd->length); +} + +uint8_t *nghttp3_frame_write_settings(uint8_t *p, + const nghttp3_frame_settings *fr) { + size_t i; + + p = nghttp3_frame_write_hd(p, &fr->hd); + + for (i = 0; i < fr->niv; ++i) { + p = nghttp3_put_varint(p, (int64_t)fr->iv[i].id); + p = nghttp3_put_varint(p, (int64_t)fr->iv[i].value); + } + + return p; +} + +size_t nghttp3_frame_write_settings_len(int64_t *ppayloadlen, + const nghttp3_frame_settings *fr) { + size_t payloadlen = 0; + size_t i; + + for (i = 0; i < fr->niv; ++i) { + payloadlen += nghttp3_put_varint_len((int64_t)fr->iv[i].id) + + nghttp3_put_varint_len((int64_t)fr->iv[i].value); + } + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_SETTINGS) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_cancel_push(uint8_t *p, + const nghttp3_frame_cancel_push *fr) { + p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_put_varint(p, fr->push_id); + + return p; +} + +size_t +nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, + const nghttp3_frame_cancel_push *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->push_id); + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_CANCEL_PUSH) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_max_push_id(uint8_t *p, + const nghttp3_frame_max_push_id *fr) { + p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_put_varint(p, fr->push_id); + + return p; +} + +size_t +nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, + const nghttp3_frame_max_push_id *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->push_id); + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_MAX_PUSH_ID) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +uint8_t *nghttp3_frame_write_goaway(uint8_t *p, + const nghttp3_frame_goaway *fr) { + p = nghttp3_frame_write_hd(p, &fr->hd); + p = nghttp3_put_varint(p, fr->id); + + return p; +} + +size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, + const nghttp3_frame_goaway *fr) { + size_t payloadlen = nghttp3_put_varint_len(fr->id); + + *ppayloadlen = (int64_t)payloadlen; + + return nghttp3_put_varint_len(NGHTTP3_FRAME_GOAWAY) + + nghttp3_put_varint_len((int64_t)payloadlen) + payloadlen; +} + +int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen, + const nghttp3_mem *mem) { + size_t i; + uint8_t *data = NULL; + size_t buflen = 0; + nghttp3_nv *p; + + if (nvlen == 0) { + *pnva = NULL; + + return 0; + } + + for (i = 0; i < nvlen; ++i) { + /* + 1 for null-termination */ + if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) == 0) { + buflen += nva[i].namelen + 1; + } + if ((nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) == 0) { + buflen += nva[i].valuelen + 1; + } + } + + buflen += sizeof(nghttp3_nv) * nvlen; + + *pnva = nghttp3_mem_malloc(mem, buflen); + + if (*pnva == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + p = *pnva; + data = (uint8_t *)(*pnva) + sizeof(nghttp3_nv) * nvlen; + + for (i = 0; i < nvlen; ++i) { + p->flags = nva[i].flags; + + if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_NAME) { + p->name = nva[i].name; + p->namelen = nva[i].namelen; + } else { + if (nva[i].namelen) { + memcpy(data, nva[i].name, nva[i].namelen); + } + p->name = data; + p->namelen = nva[i].namelen; + data[p->namelen] = '\0'; + nghttp3_downcase(p->name, p->namelen); + data += nva[i].namelen + 1; + } + + if (nva[i].flags & NGHTTP3_NV_FLAG_NO_COPY_VALUE) { + p->value = nva[i].value; + p->valuelen = nva[i].valuelen; + } else { + if (nva[i].valuelen) { + memcpy(data, nva[i].value, nva[i].valuelen); + } + p->value = data; + p->valuelen = nva[i].valuelen; + data[p->valuelen] = '\0'; + data += nva[i].valuelen + 1; + } + + ++p; + } + return 0; +} + +void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem) { + nghttp3_mem_free(mem, nva); +} + +void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, + const nghttp3_mem *mem) { + if (fr == NULL) { + return; + } + + nghttp3_nva_del(fr->nva, mem); +} + +void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, + const nghttp3_mem *mem) { + if (fr == NULL) { + return; + } + + nghttp3_nva_del(fr->nva, mem); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h new file mode 100644 index 00000000000000..9bd59a9660d85b --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_frame.h @@ -0,0 +1,243 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 NGHTTP3_FRAME_H +#define NGHTTP3_FRAME_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_buf.h" + +typedef enum nghttp3_frame_type { + NGHTTP3_FRAME_DATA = 0x00, + NGHTTP3_FRAME_HEADERS = 0x01, + NGHTTP3_FRAME_CANCEL_PUSH = 0x03, + NGHTTP3_FRAME_SETTINGS = 0x04, + NGHTTP3_FRAME_PUSH_PROMISE = 0x05, + NGHTTP3_FRAME_GOAWAY = 0x07, + NGHTTP3_FRAME_MAX_PUSH_ID = 0x0d, +} nghttp3_frame_type; + +typedef enum nghttp3_h2_reserved_type { + NGHTTP3_H2_FRAME_PRIORITY = 0x02, + NGHTTP3_H2_FRAME_PING = 0x06, + NGHTTP3_H2_FRAME_WINDOW_UPDATE = 0x08, + NGHTTP3_H2_FRAME_CONTINUATION = 0x9, +} nghttp3_h2_reserved_type; + +typedef struct nghttp3_frame_hd { + int64_t type; + int64_t length; +} nghttp3_frame_hd; + +typedef struct nghttp3_frame_data { + nghttp3_frame_hd hd; +} nghttp3_frame_data; + +typedef struct nghttp3_frame_headers { + nghttp3_frame_hd hd; + nghttp3_nv *nva; + size_t nvlen; +} nghttp3_frame_headers; + +typedef struct nghttp3_frame_cancel_push { + nghttp3_frame_hd hd; + int64_t push_id; +} nghttp3_frame_cancel_push; + +#define NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE 0x06 +#define NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY 0x01 +#define NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS 0x07 + +#define NGHTTP3_H2_SETTINGS_ID_ENABLE_PUSH 0x2 +#define NGHTTP3_H2_SETTINGS_ID_MAX_CONCURRENT_STREAMS 0x3 +#define NGHTTP3_H2_SETTINGS_ID_INITIAL_WINDOW_SIZE 0x4 +#define NGHTTP3_H2_SETTINGS_ID_MAX_FRAME_SIZE 0x5 + +typedef struct nghttp3_settings_entry { + uint64_t id; + uint64_t value; +} nghttp3_settings_entry; + +typedef struct nghttp3_frame_settings { + nghttp3_frame_hd hd; + size_t niv; + nghttp3_settings_entry iv[1]; +} nghttp3_frame_settings; + +typedef struct nghttp3_frame_push_promise { + nghttp3_frame_hd hd; + nghttp3_nv *nva; + size_t nvlen; + int64_t push_id; +} nghttp3_frame_push_promise; + +typedef struct nghttp3_frame_goaway { + nghttp3_frame_hd hd; + int64_t id; +} nghttp3_frame_goaway; + +typedef struct nghttp3_frame_max_push_id { + nghttp3_frame_hd hd; + int64_t push_id; +} nghttp3_frame_max_push_id; + +typedef union nghttp3_frame { + nghttp3_frame_hd hd; + nghttp3_frame_data data; + nghttp3_frame_headers headers; + nghttp3_frame_cancel_push cancel_push; + nghttp3_frame_settings settings; + nghttp3_frame_push_promise push_promise; + nghttp3_frame_goaway goaway; + nghttp3_frame_max_push_id max_push_id; +} nghttp3_frame; + +/* + * nghttp3_frame_write_hd writes frame header |hd| to |dest|. This + * function assumes that |dest| has enough space to write |hd|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_hd(uint8_t *dest, const nghttp3_frame_hd *hd); + +/* + * nghttp3_frame_write_hd_len returns the number of bytes required to + * write |hd|. hd->length must be set. + */ +size_t nghttp3_frame_write_hd_len(const nghttp3_frame_hd *hd); + +/* + * nghttp3_frame_write_settings writes SETTINGS frame |fr| to |dest|. + * This function assumes that |dest| has enough space to write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_settings(uint8_t *dest, + const nghttp3_frame_settings *fr); + +/* + * nghttp3_frame_write_settings_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_settings_len(int64_t *pppayloadlen, + const nghttp3_frame_settings *fr); + +/* + * nghttp3_frame_write_cancel_push writes CANCEL_PUSH frame |fr| to + * |dest|. This function assumes that |dest| has enough space to + * write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_cancel_push(uint8_t *dest, + const nghttp3_frame_cancel_push *fr); + +/* + * nghttp3_frame_write_cancel_push_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_cancel_push_len(int64_t *ppayloadlen, + const nghttp3_frame_cancel_push *fr); + +/* + * nghttp3_frame_write_max_push_id writes MAX_PUSH_ID frame |fr| to + * |dest|. This function assumes that |dest| has enough space to + * write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_max_push_id(uint8_t *dest, + const nghttp3_frame_max_push_id *fr); + +/* + * nghttp3_frame_write_max_push_id_len returns the number of bytes + * required to write |fr|. fr->hd.length is ignored. This function + * stores payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_max_push_id_len(int64_t *ppayloadlen, + const nghttp3_frame_max_push_id *fr); + +/* + * nghttp3_frame_write_goaway writes GOAWAY frame |fr| to |dest|. + * This function assumes that |dest| has enough space to write |fr|. + * + * This function returns |dest| plus the number of bytes written. + */ +uint8_t *nghttp3_frame_write_goaway(uint8_t *dest, + const nghttp3_frame_goaway *fr); + +/* + * nghttp3_frame_write_goaway_len returns the number of bytes required + * to write |fr|. fr->hd.length is ignored. This function stores + * payload length in |*ppayloadlen|. + */ +size_t nghttp3_frame_write_goaway_len(int64_t *ppayloadlen, + const nghttp3_frame_goaway *fr); + +/* + * nghttp3_nva_copy copies name/value pairs from |nva|, which contains + * |nvlen| pairs, to |*nva_ptr|, which is dynamically allocated so + * that all items can be stored. The resultant name and value in + * nghttp2_nv are guaranteed to be NULL-terminated even if the input + * is not null-terminated. + * + * The |*pnva| must be freed using nghttp3_nva_del(). + * + * This function returns 0 if it succeeds or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_nva_copy(nghttp3_nv **pnva, const nghttp3_nv *nva, size_t nvlen, + const nghttp3_mem *mem); + +/* + * nghttp3_nva_del frees |nva|. + */ +void nghttp3_nva_del(nghttp3_nv *nva, const nghttp3_mem *mem); + +/* + * nghttp3_frame_headers_free frees memory allocated for |fr|. It + * assumes that fr->nva is created by nghttp3_nva_copy() or NULL. + */ +void nghttp3_frame_headers_free(nghttp3_frame_headers *fr, + const nghttp3_mem *mem); + +/* + * nghttp3_frame_push_promise_free frees memory allocated for |fr|. + * It assumes that fr->nva is created by nghttp3_nva_copy() or NULL. + */ +void nghttp3_frame_push_promise_free(nghttp3_frame_push_promise *fr, + const nghttp3_mem *mem); + +#endif /* NGHTTP3_FRAME_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c new file mode 100644 index 00000000000000..c60887d1ec4db7 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.c @@ -0,0 +1,130 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 "nghttp3_gaptr.h" +#include "nghttp3_range.h" + +#include <string.h> +#include <assert.h> + +int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem) { + int rv; + nghttp3_range range = {0, UINT64_MAX}; + + rv = nghttp3_ksl_init(&gaptr->gap, nghttp3_ksl_range_compar, + sizeof(nghttp3_range), mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_ksl_insert(&gaptr->gap, NULL, &range, NULL); + if (rv != 0) { + nghttp3_ksl_free(&gaptr->gap); + return rv; + } + + gaptr->mem = mem; + + return 0; +} + +void nghttp3_gaptr_free(nghttp3_gaptr *gaptr) { + if (gaptr == NULL) { + return; + } + + nghttp3_ksl_free(&gaptr->gap); +} + +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen) { + int rv; + nghttp3_range k, m, l, r, q = {offset, offset + datalen}; + nghttp3_ksl_it it; + + it = nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_compar); + + for (; !nghttp3_ksl_it_end(&it);) { + k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + m = nghttp3_range_intersect(&q, &k); + if (!nghttp3_range_len(&m)) { + break; + } + + if (nghttp3_range_eq(&k, &m)) { + nghttp3_ksl_remove(&gaptr->gap, &it, &k); + continue; + } + nghttp3_range_cut(&l, &r, &k, &m); + if (nghttp3_range_len(&l)) { + nghttp3_ksl_update_key(&gaptr->gap, &k, &l); + + if (nghttp3_range_len(&r)) { + rv = nghttp3_ksl_insert(&gaptr->gap, &it, &r, NULL); + if (rv != 0) { + return rv; + } + } + } else if (nghttp3_range_len(&r)) { + nghttp3_ksl_update_key(&gaptr->gap, &k, &r); + } + nghttp3_ksl_it_next(&it); + } + return 0; +} + +uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr) { + nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); + nghttp3_range r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + return r.begin; +} + +nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset) { + nghttp3_range q = {offset, offset + 1}; + return nghttp3_ksl_lower_bound_compar(&gaptr->gap, &q, + nghttp3_ksl_range_exclusive_compar); +} + +int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, + size_t datalen) { + nghttp3_range q = {offset, offset + datalen}; + nghttp3_ksl_it it = nghttp3_ksl_lower_bound_compar( + &gaptr->gap, &q, nghttp3_ksl_range_exclusive_compar); + nghttp3_range k = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + nghttp3_range m = nghttp3_range_intersect(&q, &k); + return nghttp3_range_len(&m) == 0; +} + +void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr) { + nghttp3_ksl_it it = nghttp3_ksl_begin(&gaptr->gap); + nghttp3_range r; + + assert(!nghttp3_ksl_it_end(&it)); + + r = *(nghttp3_range *)nghttp3_ksl_it_key(&it); + + nghttp3_ksl_remove(&gaptr->gap, NULL, &r); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h new file mode 100644 index 00000000000000..b1713a048bb3c7 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_gaptr.h @@ -0,0 +1,104 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_GAPTR_H +#define NGHTTP3_GAPTR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" +#include "nghttp3_ksl.h" + +/* + * nghttp3_gaptr maintains the gap in the range [0, UINT64_MAX). + */ +typedef struct nghttp3_gaptr { + /* gap maintains the range of offset which is not received + yet. Initially, its range is [0, UINT64_MAX). */ + nghttp3_ksl gap; + /* mem is custom memory allocator */ + const nghttp3_mem *mem; +} nghttp3_gaptr; + +/* + * nghttp3_gaptr_init initializes |gaptr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_gaptr_init(nghttp3_gaptr *gaptr, const nghttp3_mem *mem); + +/* + * nghttp3_gaptr_free frees resources allocated for |gaptr|. + */ +void nghttp3_gaptr_free(nghttp3_gaptr *gaptr); + +/* + * nghttp3_gaptr_push adds new data of length |datalen| at the stream + * offset |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory + */ +int nghttp3_gaptr_push(nghttp3_gaptr *gaptr, uint64_t offset, size_t datalen); + +/* + * nghttp3_gaptr_first_gap_offset returns the offset to the first gap. + * If there is no gap, it returns UINT64_MAX. + */ +uint64_t nghttp3_gaptr_first_gap_offset(nghttp3_gaptr *gaptr); + +/* + * nghttp3_gaptr_get_first_gap_after returns the iterator pointing to + * the first gap which overlaps or comes after |offset|. + */ +nghttp3_ksl_it nghttp3_gaptr_get_first_gap_after(nghttp3_gaptr *gaptr, + uint64_t offset); + +/* + * nghttp3_gaptr_is_pushed returns nonzero if range [offset, offset + + * datalen) is completely pushed into this object. + */ +int nghttp3_gaptr_is_pushed(nghttp3_gaptr *gaptr, uint64_t offset, + size_t datalen); + +/* + * nghttp3_gaptr_drop_first_gap deletes the first gap entirely as if + * the range is pushed. This function assumes that at least one gap + * exists. + */ +void nghttp3_gaptr_drop_first_gap(nghttp3_gaptr *gaptr); + +#endif /* NGHTTP3_GAPTR_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.c b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c new file mode 100644 index 00000000000000..465c36ab2cbdb0 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.c @@ -0,0 +1,844 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 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 "nghttp3_http.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp3_stream.h" +#include "nghttp3_macro.h" +#include "nghttp3_conv.h" + +static uint8_t downcase(uint8_t c) { + return 'A' <= c && c <= 'Z' ? (uint8_t)(c - 'A' + 'a') : c; +} + +static int memieq(const void *a, const void *b, size_t n) { + size_t i; + const uint8_t *aa = a, *bb = b; + + for (i = 0; i < n; ++i) { + if (downcase(aa[i]) != downcase(bb[i])) { + return 0; + } + } + return 1; +} + +#define lstrieq(A, B, N) ((sizeof((A)) - 1) == (N) && memieq((A), (B), (N))) + +static int64_t parse_uint(const uint8_t *s, size_t len) { + int64_t n = 0; + size_t i; + if (len == 0) { + return -1; + } + for (i = 0; i < len; ++i) { + if ('0' <= s[i] && s[i] <= '9') { + if (n > INT64_MAX / 10) { + return -1; + } + n *= 10; + if (n > INT64_MAX - (s[i] - '0')) { + return -1; + } + n += s[i] - '0'; + continue; + } + return -1; + } + return n; +} + +static int lws(const uint8_t *s, size_t n) { + size_t i; + for (i = 0; i < n; ++i) { + if (s[i] != ' ' && s[i] != '\t') { + return 0; + } + } + return 1; +} + +static int check_pseudo_header(nghttp3_http_state *http, + const nghttp3_qpack_nv *nv, int flag) { + if (http->flags & flag) { + return 0; + } + if (lws(nv->value->base, nv->value->len)) { + return 0; + } + http->flags = (uint16_t)(http->flags | flag); + return 1; +} + +static int expect_response_body(nghttp3_http_state *http) { + return (http->flags & NGHTTP3_HTTP_FLAG_METH_HEAD) == 0 && + http->status_code / 100 != 1 && http->status_code != 304 && + http->status_code != 204; +} + +/* For "http" or "https" URIs, OPTIONS request may have "*" in :path + header field to represent system-wide OPTIONS request. Otherwise, + :path header field value must start with "/". This function must + be called after ":method" header field was received. This function + returns nonzero if path is valid.*/ +static int check_path(nghttp3_http_state *http) { + return (http->flags & NGHTTP3_HTTP_FLAG_SCHEME_HTTP) == 0 || + ((http->flags & NGHTTP3_HTTP_FLAG_PATH_REGULAR) || + ((http->flags & NGHTTP3_HTTP_FLAG_METH_OPTIONS) && + (http->flags & NGHTTP3_HTTP_FLAG_PATH_ASTERISK))); +} + +int nghttp3_http_parse_priority(nghttp3_pri *dest, const uint8_t *value, + size_t len) { + nghttp3_pri pri = *dest; + const uint8_t *p = value, *end = value + len; + + for (;;) { + for (; p != end && (*p == ' ' || *p == '\t'); ++p) + ; + + if (p == end) { + break; + } + + switch (*p) { + case 'u': + ++p; + + if (p + 2 > end || *p++ != '=') { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (!('0' <= *p && *p <= '7')) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + pri.urgency = (uint32_t)(*p++ - '0'); + + if (p == end) { + goto fin; + } + + if (*p++ != ',') { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + break; + case 'i': + ++p; + + if (p == end) { + pri.inc = 1; + goto fin; + } + + if (*p == ',') { + pri.inc = 1; + ++p; + break; + } + + if (p + 3 > end || *p != '=' || *(p + 1) != '?' || + (*(p + 2) != '0' && *(p + 2) != '1')) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + pri.inc = *(p + 2) == '1'; + + p += 3; + + if (p == end) { + goto fin; + } + + if (*p++ != ',') { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + break; + default: + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (p == end) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + } + +fin: + + *dest = pri; + + return 0; +} + +static int http_request_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int trailers, + int connect_protocol) { + nghttp3_pri pri; + + if (nv->name->base[0] == ':') { + if (trailers || + (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP3_QPACK_TOKEN__AUTHORITY: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__AUTHORITY)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN__METHOD: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__METHOD)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + switch (nv->value->len) { + case 4: + if (lstreq("HEAD", nv->value->base, nv->value->len)) { + http->flags |= NGHTTP3_HTTP_FLAG_METH_HEAD; + } + break; + case 7: + switch (nv->value->base[6]) { + case 'T': + if (lstreq("CONNECT", nv->value->base, nv->value->len)) { + if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { + /* we won't allow CONNECT for push */ + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; + } + break; + case 'S': + if (lstreq("OPTIONS", nv->value->base, nv->value->len)) { + http->flags |= NGHTTP3_HTTP_FLAG_METH_OPTIONS; + } + break; + } + break; + } + break; + case NGHTTP3_QPACK_TOKEN__PATH: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PATH)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (nv->value->base[0] == '/') { + http->flags |= NGHTTP3_HTTP_FLAG_PATH_REGULAR; + } else if (nv->value->len == 1 && nv->value->base[0] == '*') { + http->flags |= NGHTTP3_HTTP_FLAG_PATH_ASTERISK; + } + break; + case NGHTTP3_QPACK_TOKEN__SCHEME: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__SCHEME)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if ((nv->value->len == 4 && memieq("http", nv->value->base, 4)) || + (nv->value->len == 5 && memieq("https", nv->value->base, 5))) { + http->flags |= NGHTTP3_HTTP_FLAG_SCHEME_HTTP; + } + break; + case NGHTTP3_QPACK_TOKEN__PROTOCOL: + if (!connect_protocol) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__PROTOCOL)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN_HOST: + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG_HOST)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { + /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender + MUST NOT generate a trailer that contains a field necessary for + message framing (e.g., Transfer-Encoding and Content-Length), + ... */ + if (trailers) { + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + if (http->content_length != -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = parse_uint(nv->value->base, nv->value->len); + if (http->content_length == -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP3_QPACK_TOKEN_CONNECTION: + case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: + case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: + case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: + case NGHTTP3_QPACK_TOKEN_UPGRADE: + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + case NGHTTP3_QPACK_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + case NGHTTP3_QPACK_TOKEN_PRIORITY: + pri.urgency = nghttp3_pri_uint8_urgency(http->pri); + pri.inc = nghttp3_pri_uint8_inc(http->pri); + if (nghttp3_http_parse_priority(&pri, nv->value->base, nv->value->len) == + 0) { + http->pri = nghttp3_pri_to_uint8(&pri); + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + return 0; +} + +static int http_response_on_header(nghttp3_http_state *http, + nghttp3_qpack_nv *nv, int trailers) { + if (nv->name->base[0] == ':') { + if (trailers || + (http->flags & NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + switch (nv->token) { + case NGHTTP3_QPACK_TOKEN__STATUS: { + if (!check_pseudo_header(http, nv, NGHTTP3_HTTP_FLAG__STATUS)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (nv->value->len != 3) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->status_code = (int16_t)parse_uint(nv->value->base, nv->value->len); + if (http->status_code < 100 || http->status_code == 101) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + } + case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: { + /* https://tools.ietf.org/html/rfc7230#section-4.1.2: A sender + MUST NOT generate a trailer that contains a field necessary for + message framing (e.g., Transfer-Encoding and Content-Length), + ... */ + if (trailers) { + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + if (http->status_code == 204) { + /* content-length header field in 204 response is prohibited by + RFC 7230. But some widely used servers send content-length: + 0. Until they get fixed, we ignore it. */ + if (http->content_length != -1) { + /* Found multiple content-length field */ + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (!lstrieq("0", nv->value->base, nv->value->len)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = 0; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + if (http->status_code / 100 == 1) { + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + /* https://tools.ietf.org/html/rfc7230#section-3.3.3 */ + if (http->status_code / 100 == 2 && + (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) { + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + if (http->content_length != -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = parse_uint(nv->value->base, nv->value->len); + if (http->content_length == -1) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + } + /* disallowed header fields */ + case NGHTTP3_QPACK_TOKEN_CONNECTION: + case NGHTTP3_QPACK_TOKEN_KEEP_ALIVE: + case NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION: + case NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING: + case NGHTTP3_QPACK_TOKEN_UPGRADE: + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + case NGHTTP3_QPACK_TOKEN_TE: + if (!lstrieq("trailers", nv->value->base, nv->value->len)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + break; + default: + if (nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + return 0; +} + +/* Generated by genauthroitychartbl.py */ +static char VALID_AUTHORITY_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 0 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 0 /* < */, 1 /* = */, 0 /* > */, 0 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 0 /* \ */, 1 /* ] */, 0 /* ^ */, 1 /* _ */, + 0 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 0 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +static int check_authority(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_AUTHORITY_CHARS[*value]) { + return 0; + } + } + return 1; +} + +static int check_scheme(const uint8_t *value, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + + if (!(('A' <= *value && *value <= 'Z') || ('a' <= *value && *value <= 'z'))) { + return 0; + } + + last = value + len; + ++value; + + for (; value != last; ++value) { + if (!(('A' <= *value && *value <= 'Z') || + ('a' <= *value && *value <= 'z') || + ('0' <= *value && *value <= '9') || *value == '+' || *value == '-' || + *value == '.')) { + return 0; + } + } + return 1; +} + +int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int request, int trailers) { + int rv; + size_t i; + uint8_t c; + + if (!nghttp3_check_header_name(nv->name->base, nv->name->len)) { + if (nv->name->len > 0 && nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + /* header field name must be lower-cased without exception */ + for (i = 0; i < nv->name->len; ++i) { + c = nv->name->base[i]; + if ('A' <= c && c <= 'Z') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + /* When ignoring regular header fields, we set this flag so that + we still enforce header field ordering rule for pseudo header + fields. */ + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + + assert(nv->name->len > 0); + + if (nv->token == NGHTTP3_QPACK_TOKEN__AUTHORITY || + nv->token == NGHTTP3_QPACK_TOKEN_HOST) { + rv = check_authority(nv->value->base, nv->value->len); + } else if (nv->token == NGHTTP3_QPACK_TOKEN__SCHEME) { + rv = check_scheme(nv->value->base, nv->value->len); + } else { + rv = nghttp3_check_header_value(nv->value->base, nv->value->len); + } + + if (rv == 0) { + if (nv->name->base[0] == ':') { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + /* When ignoring regular header fields, we set this flag so that + we still enforce header field ordering rule for pseudo header + fields. */ + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + return NGHTTP3_ERR_REMOVE_HTTP_HEADER; + } + + if (request) { + rv = http_request_on_header(http, frame_type, nv, trailers, + /* connect_protocol = */ 0); + } else { + rv = http_response_on_header(http, nv, trailers); + } + + if (nv->name->base[0] != ':') { + switch (rv) { + case 0: + case NGHTTP3_ERR_REMOVE_HTTP_HEADER: + http->flags |= NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED; + break; + } + } + + return rv; +} + +int nghttp3_http_on_request_headers(nghttp3_http_state *http) { + if (!(http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) && + (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT)) { + if ((http->flags & (NGHTTP3_HTTP_FLAG__SCHEME | NGHTTP3_HTTP_FLAG__PATH)) || + (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + http->content_length = -1; + } else { + if ((http->flags & NGHTTP3_HTTP_FLAG_REQ_HEADERS) != + NGHTTP3_HTTP_FLAG_REQ_HEADERS || + (http->flags & + (NGHTTP3_HTTP_FLAG__AUTHORITY | NGHTTP3_HTTP_FLAG_HOST)) == 0) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if ((http->flags & NGHTTP3_HTTP_FLAG__PROTOCOL) && + ((http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) == 0 || + (http->flags & NGHTTP3_HTTP_FLAG__AUTHORITY) == 0)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + if (!check_path(http)) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + } + + return 0; +} + +int nghttp3_http_on_response_headers(nghttp3_http_state *http) { + if ((http->flags & NGHTTP3_HTTP_FLAG__STATUS) == 0) { + return NGHTTP3_ERR_MALFORMED_HTTP_HEADER; + } + + if (http->status_code / 100 == 1) { + /* non-final response */ + http->flags = (uint16_t)((http->flags & NGHTTP3_HTTP_FLAG_METH_ALL) | + NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + http->content_length = -1; + http->status_code = -1; + return 0; + } + + http->flags = + (uint16_t)(http->flags & ~NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE); + + if (!expect_response_body(http)) { + http->content_length = 0; + } else if (http->flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { + http->content_length = -1; + } + + return 0; +} + +int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream) { + if (stream->flags & NGHTTP3_STREAM_FLAG_RESET) { + return 0; + } + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->rx.http.content_length != -1 && + stream->rx.http.content_length != stream->rx.http.recv_content_length)) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + + return 0; +} + +int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n) { + stream->rx.http.recv_content_length += (int64_t)n; + + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) || + (stream->rx.http.content_length != -1 && + stream->rx.http.recv_content_length > stream->rx.http.content_length)) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + + return 0; +} + +void nghttp3_http_record_request_method(nghttp3_stream *stream, + const nghttp3_nv *nva, size_t nvlen) { + size_t i; + const nghttp3_nv *nv; + + /* TODO we should do this strictly. */ + for (i = 0; i < nvlen; ++i) { + nv = &nva[i]; + if (!(nv->namelen == 7 && nv->name[6] == 'd' && + memcmp(":metho", nv->name, nv->namelen - 1) == 0)) { + continue; + } + if (lstreq("CONNECT", nv->value, nv->valuelen)) { + stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_CONNECT; + return; + } + if (lstreq("HEAD", nv->value, nv->valuelen)) { + stream->rx.http.flags |= NGHTTP3_HTTP_FLAG_METH_HEAD; + return; + } + return; + } +} + +/* Generated by gennmchartbl.py */ +static const int VALID_HD_NAME_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 0 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 0 /* SPC */, 1 /* ! */, 0 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 0 /* ( */, 0 /* ) */, 1 /* * */, 1 /* + */, + 0 /* , */, 1 /* - */, 1 /* . */, 0 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 0 /* : */, 0 /* ; */, + 0 /* < */, 0 /* = */, 0 /* > */, 0 /* ? */, + 0 /* @ */, 0 /* A */, 0 /* B */, 0 /* C */, + 0 /* D */, 0 /* E */, 0 /* F */, 0 /* G */, + 0 /* H */, 0 /* I */, 0 /* J */, 0 /* K */, + 0 /* L */, 0 /* M */, 0 /* N */, 0 /* O */, + 0 /* P */, 0 /* Q */, 0 /* R */, 0 /* S */, + 0 /* T */, 0 /* U */, 0 /* V */, 0 /* W */, + 0 /* X */, 0 /* Y */, 0 /* Z */, 0 /* [ */, + 0 /* \ */, 0 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 0 /* { */, + 1 /* | */, 0 /* } */, 1 /* ~ */, 0 /* DEL */, + 0 /* 0x80 */, 0 /* 0x81 */, 0 /* 0x82 */, 0 /* 0x83 */, + 0 /* 0x84 */, 0 /* 0x85 */, 0 /* 0x86 */, 0 /* 0x87 */, + 0 /* 0x88 */, 0 /* 0x89 */, 0 /* 0x8a */, 0 /* 0x8b */, + 0 /* 0x8c */, 0 /* 0x8d */, 0 /* 0x8e */, 0 /* 0x8f */, + 0 /* 0x90 */, 0 /* 0x91 */, 0 /* 0x92 */, 0 /* 0x93 */, + 0 /* 0x94 */, 0 /* 0x95 */, 0 /* 0x96 */, 0 /* 0x97 */, + 0 /* 0x98 */, 0 /* 0x99 */, 0 /* 0x9a */, 0 /* 0x9b */, + 0 /* 0x9c */, 0 /* 0x9d */, 0 /* 0x9e */, 0 /* 0x9f */, + 0 /* 0xa0 */, 0 /* 0xa1 */, 0 /* 0xa2 */, 0 /* 0xa3 */, + 0 /* 0xa4 */, 0 /* 0xa5 */, 0 /* 0xa6 */, 0 /* 0xa7 */, + 0 /* 0xa8 */, 0 /* 0xa9 */, 0 /* 0xaa */, 0 /* 0xab */, + 0 /* 0xac */, 0 /* 0xad */, 0 /* 0xae */, 0 /* 0xaf */, + 0 /* 0xb0 */, 0 /* 0xb1 */, 0 /* 0xb2 */, 0 /* 0xb3 */, + 0 /* 0xb4 */, 0 /* 0xb5 */, 0 /* 0xb6 */, 0 /* 0xb7 */, + 0 /* 0xb8 */, 0 /* 0xb9 */, 0 /* 0xba */, 0 /* 0xbb */, + 0 /* 0xbc */, 0 /* 0xbd */, 0 /* 0xbe */, 0 /* 0xbf */, + 0 /* 0xc0 */, 0 /* 0xc1 */, 0 /* 0xc2 */, 0 /* 0xc3 */, + 0 /* 0xc4 */, 0 /* 0xc5 */, 0 /* 0xc6 */, 0 /* 0xc7 */, + 0 /* 0xc8 */, 0 /* 0xc9 */, 0 /* 0xca */, 0 /* 0xcb */, + 0 /* 0xcc */, 0 /* 0xcd */, 0 /* 0xce */, 0 /* 0xcf */, + 0 /* 0xd0 */, 0 /* 0xd1 */, 0 /* 0xd2 */, 0 /* 0xd3 */, + 0 /* 0xd4 */, 0 /* 0xd5 */, 0 /* 0xd6 */, 0 /* 0xd7 */, + 0 /* 0xd8 */, 0 /* 0xd9 */, 0 /* 0xda */, 0 /* 0xdb */, + 0 /* 0xdc */, 0 /* 0xdd */, 0 /* 0xde */, 0 /* 0xdf */, + 0 /* 0xe0 */, 0 /* 0xe1 */, 0 /* 0xe2 */, 0 /* 0xe3 */, + 0 /* 0xe4 */, 0 /* 0xe5 */, 0 /* 0xe6 */, 0 /* 0xe7 */, + 0 /* 0xe8 */, 0 /* 0xe9 */, 0 /* 0xea */, 0 /* 0xeb */, + 0 /* 0xec */, 0 /* 0xed */, 0 /* 0xee */, 0 /* 0xef */, + 0 /* 0xf0 */, 0 /* 0xf1 */, 0 /* 0xf2 */, 0 /* 0xf3 */, + 0 /* 0xf4 */, 0 /* 0xf5 */, 0 /* 0xf6 */, 0 /* 0xf7 */, + 0 /* 0xf8 */, 0 /* 0xf9 */, 0 /* 0xfa */, 0 /* 0xfb */, + 0 /* 0xfc */, 0 /* 0xfd */, 0 /* 0xfe */, 0 /* 0xff */ +}; + +int nghttp3_check_header_name(const uint8_t *name, size_t len) { + const uint8_t *last; + if (len == 0) { + return 0; + } + if (*name == ':') { + if (len == 1) { + return 0; + } + ++name; + --len; + } + for (last = name + len; name != last; ++name) { + if (!VALID_HD_NAME_CHARS[*name]) { + return 0; + } + } + return 1; +} + +/* Generated by genvchartbl.py */ +static const int VALID_HD_VALUE_CHARS[] = { + 0 /* NUL */, 0 /* SOH */, 0 /* STX */, 0 /* ETX */, + 0 /* EOT */, 0 /* ENQ */, 0 /* ACK */, 0 /* BEL */, + 0 /* BS */, 1 /* HT */, 0 /* LF */, 0 /* VT */, + 0 /* FF */, 0 /* CR */, 0 /* SO */, 0 /* SI */, + 0 /* DLE */, 0 /* DC1 */, 0 /* DC2 */, 0 /* DC3 */, + 0 /* DC4 */, 0 /* NAK */, 0 /* SYN */, 0 /* ETB */, + 0 /* CAN */, 0 /* EM */, 0 /* SUB */, 0 /* ESC */, + 0 /* FS */, 0 /* GS */, 0 /* RS */, 0 /* US */, + 1 /* SPC */, 1 /* ! */, 1 /* " */, 1 /* # */, + 1 /* $ */, 1 /* % */, 1 /* & */, 1 /* ' */, + 1 /* ( */, 1 /* ) */, 1 /* * */, 1 /* + */, + 1 /* , */, 1 /* - */, 1 /* . */, 1 /* / */, + 1 /* 0 */, 1 /* 1 */, 1 /* 2 */, 1 /* 3 */, + 1 /* 4 */, 1 /* 5 */, 1 /* 6 */, 1 /* 7 */, + 1 /* 8 */, 1 /* 9 */, 1 /* : */, 1 /* ; */, + 1 /* < */, 1 /* = */, 1 /* > */, 1 /* ? */, + 1 /* @ */, 1 /* A */, 1 /* B */, 1 /* C */, + 1 /* D */, 1 /* E */, 1 /* F */, 1 /* G */, + 1 /* H */, 1 /* I */, 1 /* J */, 1 /* K */, + 1 /* L */, 1 /* M */, 1 /* N */, 1 /* O */, + 1 /* P */, 1 /* Q */, 1 /* R */, 1 /* S */, + 1 /* T */, 1 /* U */, 1 /* V */, 1 /* W */, + 1 /* X */, 1 /* Y */, 1 /* Z */, 1 /* [ */, + 1 /* \ */, 1 /* ] */, 1 /* ^ */, 1 /* _ */, + 1 /* ` */, 1 /* a */, 1 /* b */, 1 /* c */, + 1 /* d */, 1 /* e */, 1 /* f */, 1 /* g */, + 1 /* h */, 1 /* i */, 1 /* j */, 1 /* k */, + 1 /* l */, 1 /* m */, 1 /* n */, 1 /* o */, + 1 /* p */, 1 /* q */, 1 /* r */, 1 /* s */, + 1 /* t */, 1 /* u */, 1 /* v */, 1 /* w */, + 1 /* x */, 1 /* y */, 1 /* z */, 1 /* { */, + 1 /* | */, 1 /* } */, 1 /* ~ */, 0 /* DEL */, + 1 /* 0x80 */, 1 /* 0x81 */, 1 /* 0x82 */, 1 /* 0x83 */, + 1 /* 0x84 */, 1 /* 0x85 */, 1 /* 0x86 */, 1 /* 0x87 */, + 1 /* 0x88 */, 1 /* 0x89 */, 1 /* 0x8a */, 1 /* 0x8b */, + 1 /* 0x8c */, 1 /* 0x8d */, 1 /* 0x8e */, 1 /* 0x8f */, + 1 /* 0x90 */, 1 /* 0x91 */, 1 /* 0x92 */, 1 /* 0x93 */, + 1 /* 0x94 */, 1 /* 0x95 */, 1 /* 0x96 */, 1 /* 0x97 */, + 1 /* 0x98 */, 1 /* 0x99 */, 1 /* 0x9a */, 1 /* 0x9b */, + 1 /* 0x9c */, 1 /* 0x9d */, 1 /* 0x9e */, 1 /* 0x9f */, + 1 /* 0xa0 */, 1 /* 0xa1 */, 1 /* 0xa2 */, 1 /* 0xa3 */, + 1 /* 0xa4 */, 1 /* 0xa5 */, 1 /* 0xa6 */, 1 /* 0xa7 */, + 1 /* 0xa8 */, 1 /* 0xa9 */, 1 /* 0xaa */, 1 /* 0xab */, + 1 /* 0xac */, 1 /* 0xad */, 1 /* 0xae */, 1 /* 0xaf */, + 1 /* 0xb0 */, 1 /* 0xb1 */, 1 /* 0xb2 */, 1 /* 0xb3 */, + 1 /* 0xb4 */, 1 /* 0xb5 */, 1 /* 0xb6 */, 1 /* 0xb7 */, + 1 /* 0xb8 */, 1 /* 0xb9 */, 1 /* 0xba */, 1 /* 0xbb */, + 1 /* 0xbc */, 1 /* 0xbd */, 1 /* 0xbe */, 1 /* 0xbf */, + 1 /* 0xc0 */, 1 /* 0xc1 */, 1 /* 0xc2 */, 1 /* 0xc3 */, + 1 /* 0xc4 */, 1 /* 0xc5 */, 1 /* 0xc6 */, 1 /* 0xc7 */, + 1 /* 0xc8 */, 1 /* 0xc9 */, 1 /* 0xca */, 1 /* 0xcb */, + 1 /* 0xcc */, 1 /* 0xcd */, 1 /* 0xce */, 1 /* 0xcf */, + 1 /* 0xd0 */, 1 /* 0xd1 */, 1 /* 0xd2 */, 1 /* 0xd3 */, + 1 /* 0xd4 */, 1 /* 0xd5 */, 1 /* 0xd6 */, 1 /* 0xd7 */, + 1 /* 0xd8 */, 1 /* 0xd9 */, 1 /* 0xda */, 1 /* 0xdb */, + 1 /* 0xdc */, 1 /* 0xdd */, 1 /* 0xde */, 1 /* 0xdf */, + 1 /* 0xe0 */, 1 /* 0xe1 */, 1 /* 0xe2 */, 1 /* 0xe3 */, + 1 /* 0xe4 */, 1 /* 0xe5 */, 1 /* 0xe6 */, 1 /* 0xe7 */, + 1 /* 0xe8 */, 1 /* 0xe9 */, 1 /* 0xea */, 1 /* 0xeb */, + 1 /* 0xec */, 1 /* 0xed */, 1 /* 0xee */, 1 /* 0xef */, + 1 /* 0xf0 */, 1 /* 0xf1 */, 1 /* 0xf2 */, 1 /* 0xf3 */, + 1 /* 0xf4 */, 1 /* 0xf5 */, 1 /* 0xf6 */, 1 /* 0xf7 */, + 1 /* 0xf8 */, 1 /* 0xf9 */, 1 /* 0xfa */, 1 /* 0xfb */, + 1 /* 0xfc */, 1 /* 0xfd */, 1 /* 0xfe */, 1 /* 0xff */ +}; + +int nghttp3_check_header_value(const uint8_t *value, size_t len) { + const uint8_t *last; + for (last = value + len; value != last; ++value) { + if (!VALID_HD_VALUE_CHARS[*value]) { + return 0; + } + } + return 1; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_http.h b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h new file mode 100644 index 00000000000000..65ec91759babdc --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_http.h @@ -0,0 +1,146 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2015 nghttp2 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 NGHTTP3_HTTP_H +#define NGHTTP3_HTTP_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +typedef struct nghttp3_stream nghttp3_stream; + +typedef struct nghttp3_http_state nghttp3_http_state; + +/* HTTP related flags to enforce HTTP semantics */ + +/* NGHTTP3_HTTP_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_HTTP_FLAG_NONE 0x00 +/* header field seen so far */ +#define NGHTTP3_HTTP_FLAG__AUTHORITY 0x01 +#define NGHTTP3_HTTP_FLAG__PATH 0x02 +#define NGHTTP3_HTTP_FLAG__METHOD 0x04 +#define NGHTTP3_HTTP_FLAG__SCHEME 0x08 +/* host is not pseudo header, but we require either host or + :authority */ +#define NGHTTP3_HTTP_FLAG_HOST 0x10 +#define NGHTTP3_HTTP_FLAG__STATUS 0x20 +/* required header fields for HTTP request except for CONNECT + method. */ +#define NGHTTP3_HTTP_FLAG_REQ_HEADERS \ + (NGHTTP3_HTTP_FLAG__METHOD | NGHTTP3_HTTP_FLAG__PATH | \ + NGHTTP3_HTTP_FLAG__SCHEME) +#define NGHTTP3_HTTP_FLAG_PSEUDO_HEADER_DISALLOWED 0x40 +/* HTTP method flags */ +#define NGHTTP3_HTTP_FLAG_METH_CONNECT 0x80 +#define NGHTTP3_HTTP_FLAG_METH_HEAD 0x0100 +#define NGHTTP3_HTTP_FLAG_METH_OPTIONS 0x0200 +#define NGHTTP3_HTTP_FLAG_METH_ALL \ + (NGHTTP3_HTTP_FLAG_METH_CONNECT | NGHTTP3_HTTP_FLAG_METH_HEAD | \ + NGHTTP3_HTTP_FLAG_METH_OPTIONS) +/* :path category */ +/* path starts with "/" */ +#define NGHTTP3_HTTP_FLAG_PATH_REGULAR 0x0400 +/* path "*" */ +#define NGHTTP3_HTTP_FLAG_PATH_ASTERISK 0x0800 +/* scheme */ +/* "http" or "https" scheme */ +#define NGHTTP3_HTTP_FLAG_SCHEME_HTTP 0x1000 +/* set if final response is expected */ +#define NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE 0x2000 +/* NGHTTP3_HTTP_FLAG__PROTOCOL is set when :protocol pseudo header + field is seen. */ +#define NGHTTP3_HTTP_FLAG__PROTOCOL 0x4000 + +/* + * This function is called when HTTP header field |nv| in a frame of type + * |frame_type| is received for |http|. This function will validate |nv| + * against the current state of stream. Pass nonzero if this is request + * headers. Pass nonzero to |trailers| if |nv| is included in trailers. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_HEADER + * Invalid HTTP header field was received. + * NGHTTP3_ERR_REMOVE_HTTP_HEADER + * Invalid HTTP header field was received but it can be treated as + * if it was not received because of compatibility reasons. + */ +int nghttp3_http_on_header(nghttp3_http_state *http, int64_t frame_type, + nghttp3_qpack_nv *nv, int request, int trailers); + +/* + * This function is called when request header is received. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_HEADER + * Required HTTP header field was not received; or an invalid + * header field was received. + */ +int nghttp3_http_on_request_headers(nghttp3_http_state *http); + +/* + * This function is called when response header is received. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_HEADER + * Required HTTP header field was not received; or an invalid + * header field was received. + */ +int nghttp3_http_on_response_headers(nghttp3_http_state *http); + +/* + * This function is called when read side stream is closed. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING + * HTTP messaging is violated. + */ +int nghttp3_http_on_remote_end_stream(nghttp3_stream *stream); + +/* + * This function is called when chunk of data is received. This + * function performs validation and returns 0 if it succeeds, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING + * HTTP messaging is violated. + */ +int nghttp3_http_on_data_chunk(nghttp3_stream *stream, size_t n); + +/* + * This function inspects header fields in |nva| of length |nvlen| and + * records its method in stream->http_flags. + */ +void nghttp3_http_record_request_method(nghttp3_stream *stream, + const nghttp3_nv *nva, size_t nvlen); + +#endif /* NGHTTP3_HTTP_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c new file mode 100644 index 00000000000000..cd8fd82e6b2bb5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.c @@ -0,0 +1,88 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 "nghttp3_idtr.h" + +#include <assert.h> + +int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem) { + int rv; + + rv = nghttp3_gaptr_init(&idtr->gap, mem); + if (rv != 0) { + return rv; + } + + idtr->server = server; + idtr->mem = mem; + + return 0; +} + +void nghttp3_idtr_free(nghttp3_idtr *idtr) { + if (idtr == NULL) { + return; + } + + nghttp3_gaptr_free(&idtr->gap); +} + +/* + * id_from_stream_id translates |stream_id| to id space used by + * nghttp3_idtr. + */ +static uint64_t id_from_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2); +} + +int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + if (nghttp3_gaptr_is_pushed(&idtr->gap, q, 1)) { + return NGHTTP3_ERR_STREAM_IN_USE; + } + + return nghttp3_gaptr_push(&idtr->gap, q, 1); +} + +int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + return nghttp3_gaptr_is_pushed(&idtr->gap, q, 1); +} + +uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr) { + return nghttp3_gaptr_first_gap_offset(&idtr->gap); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h new file mode 100644 index 00000000000000..c4d60861ab167c --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_idtr.h @@ -0,0 +1,99 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_IDTR_H +#define NGHTTP3_IDTR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" +#include "nghttp3_gaptr.h" + +/* + * nghttp3_idtr tracks the usage of stream ID. + */ +typedef struct nghttp3_idtr { + /* gap maintains the range of ID which is not used yet. Initially, + its range is [0, UINT64_MAX). */ + nghttp3_gaptr gap; + /* server is nonzero if this object records server initiated stream + ID. */ + int server; + /* mem is custom memory allocator */ + const nghttp3_mem *mem; +} nghttp3_idtr; + +/* + * nghttp3_idtr_init initializes |idtr|. |chunk| is the size of buffer + * per chunk. + * + * If this object records server initiated ID (even number), set + * |server| to nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_idtr_init(nghttp3_idtr *idtr, int server, const nghttp3_mem *mem); + +/* + * nghttp3_idtr_free frees resources allocated for |idtr|. + */ +void nghttp3_idtr_free(nghttp3_idtr *idtr); + +/* + * nghttp3_idtr_open claims that |stream_id| is in used. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_STREAM_IN_USE + * ID has already been used. + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_idtr_open(nghttp3_idtr *idtr, int64_t stream_id); + +/* + * nghttp3_idtr_open tells whether ID |stream_id| is in used or not. + * + * It returns nonzero if |stream_id| is used. + */ +int nghttp3_idtr_is_open(nghttp3_idtr *idtr, int64_t stream_id); + +/* + * nghttp3_idtr_first_gap returns the first id of first gap. If there + * is no gap, it returns UINT64_MAX. The returned id is an id space + * used in this object internally, and not stream ID. + */ +uint64_t nghttp3_idtr_first_gap(nghttp3_idtr *idtr); + +#endif /* NGHTTP3_IDTR_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c new file mode 100644 index 00000000000000..0896cb693f9989 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.c @@ -0,0 +1,749 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 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 "nghttp3_ksl.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_macro.h" +#include "nghttp3_mem.h" +#include "nghttp3_range.h" + +static size_t ksl_nodelen(size_t keylen) { + return (sizeof(nghttp3_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & + (size_t)~0xf; +} + +static size_t ksl_blklen(size_t nodelen) { + return sizeof(nghttp3_ksl_blk) + nodelen * NGHTTP3_KSL_MAX_NBLK - + sizeof(uint64_t); +} + +/* + * ksl_node_set_key sets |key| to |node|. + */ +static void ksl_node_set_key(nghttp3_ksl *ksl, nghttp3_ksl_node *node, + const void *key) { + memcpy(node->key, key, ksl->keylen); +} + +int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, + const nghttp3_mem *mem) { + size_t nodelen = ksl_nodelen(keylen); + size_t blklen = ksl_blklen(nodelen); + nghttp3_ksl_blk *head; + + ksl->head = nghttp3_mem_malloc(mem, blklen); + if (!ksl->head) { + return NGHTTP3_ERR_NOMEM; + } + ksl->front = ksl->back = ksl->head; + ksl->compar = compar; + ksl->keylen = keylen; + ksl->nodelen = nodelen; + ksl->n = 0; + ksl->mem = mem; + + head = ksl->head; + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; + + return 0; +} + +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk); + } + } + + nghttp3_mem_free(ksl->mem, blk); +} + +void nghttp3_ksl_free(nghttp3_ksl *ksl) { + if (!ksl) { + return; + } + + ksl_free_blk(ksl, ksl->head); +} + +/* + * ksl_split_blk splits |blk| into 2 nghttp3_ksl_blk objects. The new + * nghttp3_ksl_blk is always the "right" block. + * + * It returns the pointer to the nghttp3_ksl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static nghttp3_ksl_blk *ksl_split_blk(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk) { + nghttp3_ksl_blk *rblk; + + rblk = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + if (rblk->next) { + rblk->next->prev = rblk; + } else if (ksl->back == blk) { + ksl->back = rblk; + } + rblk->prev = blk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), + ksl->nodelen * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGHTTP3_KSL_MIN_NBLK); + assert(rblk->n >= NGHTTP3_KSL_MIN_NBLK); + + return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + nghttp3_ksl_node *node; + nghttp3_ksl_blk *lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk, *rblk; + + rblk = ksl_split_blk(ksl, lblk); + if (rblk == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + memmove(blk->nodes + (i + 2) * ksl->nodelen, + blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + node = nghttp3_ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, + nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = nghttp3_ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, + nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + + return 0; +} + +/* + * ksl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_head(nghttp3_ksl *ksl) { + nghttp3_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; + nghttp3_ksl_node *node; + + rblk = ksl_split_blk(ksl, ksl->head); + if (rblk == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + lblk = ksl->head; + + nhead = nghttp3_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (nhead == NULL) { + nghttp3_mem_free(ksl->mem, rblk); + return NGHTTP3_ERR_NOMEM; + } + nhead->next = nhead->prev = NULL; + nhead->n = 2; + nhead->leaf = 0; + + node = nghttp3_ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, + nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = nghttp3_ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, + nghttp3_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + node->blk = rblk; + + ksl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose key is |key| with the associated + * |data| at the index of |i|. This function assumes that the number + * of nodes contained by |blk| is strictly less than + * NGHTTP3_KSL_MAX_NBLK. + */ +static void ksl_insert_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i, + const nghttp3_ksl_key *key, void *data) { + nghttp3_ksl_node *node; + + assert(blk->n < NGHTTP3_KSL_MAX_NBLK); + + memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, + ksl->nodelen * (blk->n - i)); + + node = nghttp3_ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key); + node->data = data; + + ++blk->n; +} + +static size_t ksl_bsearch(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + const nghttp3_ksl_key *key, + nghttp3_ksl_compar compar) { + nghttp3_ssize left = -1, right = (nghttp3_ssize)blk->n, mid; + nghttp3_ksl_node *node; + + while (right - left > 1) { + mid = (left + right) >> 1; + node = nghttp3_ksl_nth_node(ksl, blk, (size_t)mid); + if (compar((nghttp3_ksl_key *)node->key, key)) { + left = mid; + } else { + right = mid; + } + } + + return (size_t)right; +} + +int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key, void *data) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_node *node; + size_t i; + int rv; + + if (blk->n == NGHTTP3_KSL_MAX_NBLK) { + rv = ksl_split_head(ksl); + if (rv != 0) { + return rv; + } + blk = ksl->head; + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i < blk->n && + !ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) { + if (it) { + *it = nghttp3_ksl_end(ksl); + } + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + ksl_insert_node(ksl, blk, i, key, data); + ++ksl->n; + if (it) { + nghttp3_ksl_it_init(it, ksl, blk, i); + } + return 0; + } + + if (i == blk->n) { + /* This insertion extends the largest key in this subtree. */ + for (; !blk->leaf;) { + node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1); + if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, blk->n - 1); + if (rv != 0) { + return rv; + } + node = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key); + blk = node->blk; + } + ksl_insert_node(ksl, blk, blk->n, key, data); + ++ksl->n; + if (it) { + nghttp3_ksl_it_init(it, ksl, blk, blk->n - 1); + } + return 0; + } + + node = nghttp3_ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGHTTP3_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, i); + if (rv != 0) { + return rv; + } + if (ksl->compar((nghttp3_ksl_key *)node->key, key)) { + node = nghttp3_ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar((nghttp3_ksl_key *)node->key, key)) { + ksl_node_set_key(ksl, node, key); + } + } + } + + blk = node->blk; + } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |ksl| by 1. + * + * This function returns the pointer to the merged block. + */ +static nghttp3_ksl_blk *ksl_merge_node(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, + size_t i) { + nghttp3_ksl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; + rblk = nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk; + + assert(lblk->n + rblk->n < NGHTTP3_KSL_MAX_NBLK); + + memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, + ksl->nodelen * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + if (lblk->next) { + lblk->next->prev = lblk; + } else if (ksl->back == rblk) { + ksl->back = lblk; + } + + nghttp3_mem_free(ksl->mem, rblk); + + if (ksl->head == blk && blk->n == 2) { + nghttp3_mem_free(ksl->mem, ksl->head); + ksl->head = lblk; + } else { + ksl_remove_node(ksl, blk, i + 1); + ksl_node_set_key(ksl, nghttp3_ksl_nth_node(ksl, blk, i), + nghttp3_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + } + + return lblk; +} + +/* + * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i - 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible. + */ +static void ksl_shift_left(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + nghttp3_ksl_node *lnode, *rnode; + size_t n; + + assert(i > 0); + + lnode = nghttp3_ksl_nth_node(ksl, blk, i - 1); + rnode = nghttp3_ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGHTTP3_KSL_MAX_NBLK); + assert(rnode->blk->n > NGHTTP3_KSL_MIN_NBLK); + + n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n; + + assert(n > 0); + assert(lnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n); + assert(rnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n); + + memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes, + ksl->nodelen * n); + + lnode->blk->n += (uint32_t)n; + rnode->blk->n -= (uint32_t)n; + + ksl_node_set_key( + ksl, lnode, + nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); + + memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n, + ksl->nodelen * rnode->blk->n); +} + +/* + * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i + 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible.. + */ +static void ksl_shift_right(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t i) { + nghttp3_ksl_node *lnode, *rnode; + size_t n; + + assert(i < blk->n - 1); + + lnode = nghttp3_ksl_nth_node(ksl, blk, i); + rnode = nghttp3_ksl_nth_node(ksl, blk, i + 1); + + assert(lnode->blk->n > NGHTTP3_KSL_MIN_NBLK); + assert(rnode->blk->n < NGHTTP3_KSL_MAX_NBLK); + + n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n; + + assert(n > 0); + assert(lnode->blk->n >= NGHTTP3_KSL_MIN_NBLK + n); + assert(rnode->blk->n <= NGHTTP3_KSL_MAX_NBLK - n); + + memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes, + ksl->nodelen * rnode->blk->n); + + rnode->blk->n += (uint32_t)n; + lnode->blk->n -= (uint32_t)n; + + memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n, + ksl->nodelen * n); + + ksl_node_set_key( + ksl, lnode, + nghttp3_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(nghttp3_ksl_compar compar, const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_node *node; + size_t i; + + if (!blk->leaf && blk->n == 2 && + nghttp3_ksl_nth_node(ksl, blk, 0)->blk->n == NGHTTP3_KSL_MIN_NBLK && + nghttp3_ksl_nth_node(ksl, blk, 1)->blk->n == NGHTTP3_KSL_MIN_NBLK) { + blk = ksl_merge_node(ksl, ksl->head, 0); + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (i == blk->n) { + if (it) { + *it = nghttp3_ksl_end(ksl); + } + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (blk->leaf) { + if (ksl->compar(key, nghttp3_ksl_nth_node(ksl, blk, i)->key)) { + if (it) { + *it = nghttp3_ksl_end(ksl); + } + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + ksl_remove_node(ksl, blk, i); + --ksl->n; + if (it) { + if (blk->n == i && blk->next) { + nghttp3_ksl_it_init(it, ksl, blk->next, 0); + } else { + nghttp3_ksl_it_init(it, ksl, blk, i); + } + } + return 0; + } + + node = nghttp3_ksl_nth_node(ksl, blk, i); + + if (node->blk->n > NGHTTP3_KSL_MIN_NBLK) { + blk = node->blk; + continue; + } + + assert(node->blk->n == NGHTTP3_KSL_MIN_NBLK); + + if (i + 1 < blk->n && + nghttp3_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + blk = node->blk; + continue; + } + + if (i > 0 && + nghttp3_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGHTTP3_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + blk = node->blk; + continue; + } + + if (i + 1 < blk->n) { + blk = ksl_merge_node(ksl, blk, i); + continue; + } + + assert(i > 0); + + blk = ksl_merge_node(ksl, blk, i - 1); + } +} + +nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; + } +} + +nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key, + nghttp3_ksl_compar compar) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = nghttp3_ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + nghttp3_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = nghttp3_ksl_nth_node(ksl, blk, i)->blk; + } +} + +void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, + const nghttp3_ksl_key *new_key) { + nghttp3_ksl_blk *blk = ksl->head; + nghttp3_ksl_node *node; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = nghttp3_ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key)); + ksl_node_set_key(ksl, node, new_key); + return; + } + + if (key_equal(ksl->compar, (nghttp3_ksl_key *)node->key, old_key) || + ksl->compar((nghttp3_ksl_key *)node->key, new_key)) { + ksl_node_set_key(ksl, node, new_key); + } + + blk = node->blk; + } +} + +static void ksl_print(nghttp3_ksl *ksl, nghttp3_ksl_blk *blk, size_t level) { + size_t i; + nghttp3_ksl_node *node; + + fprintf(stderr, "LV=%zu n=%u\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = nghttp3_ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, nghttp3_ksl_nth_node(ksl, blk, i)->blk, level + 1); + } +} + +size_t nghttp3_ksl_len(nghttp3_ksl *ksl) { return ksl->n; } + +void nghttp3_ksl_clear(nghttp3_ksl *ksl) { + size_t i; + nghttp3_ksl_blk *head; + + if (!ksl->head->leaf) { + for (i = 0; i < ksl->head->n; ++i) { + ksl_free_blk(ksl, nghttp3_ksl_nth_node(ksl, ksl->head, i)->blk); + } + } + + ksl->front = ksl->back = ksl->head; + ksl->n = 0; + + head = ksl->head; + + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; +} + +void nghttp3_ksl_print(nghttp3_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } + +nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl) { + nghttp3_ksl_it it; + nghttp3_ksl_it_init(&it, ksl, ksl->front, 0); + return it; +} + +nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl) { + nghttp3_ksl_it it; + nghttp3_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + return it; +} + +void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, size_t i) { + it->ksl = ksl; + it->blk = blk; + it->i = i; +} + +void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it) { + assert(it->i < it->blk->n); + return nghttp3_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void nghttp3_ksl_it_prev(nghttp3_ksl_it *it) { + assert(!nghttp3_ksl_it_begin(it)); + + if (it->i == 0) { + it->blk = it->blk->prev; + it->i = it->blk->n - 1; + } else { + --it->i; + } +} + +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} + +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_range *a = lhs, *b = rhs; + return a->begin < b->begin; +} + +int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_range *a = lhs, *b = rhs; + return a->begin < b->begin && + !(nghttp3_max(a->begin, b->begin) < nghttp3_min(a->end, b->end)); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h new file mode 100644 index 00000000000000..7ba36bb9cb107e --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ksl.h @@ -0,0 +1,331 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 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 NGHTTP3_KSL_H +#define NGHTTP3_KSL_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> + +#include <nghttp3/nghttp3.h> + +/* + * Skip List using single key instead of range. + */ + +#define NGHTTP3_KSL_DEGR 16 +/* NGHTTP3_KSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGHTTP3_KSL_MAX_NBLK (2 * NGHTTP3_KSL_DEGR - 1) +/* NGHTTP3_KSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGHTTP3_KSL_MIN_NBLK (NGHTTP3_KSL_DEGR - 1) + +/* + * nghttp3_ksl_key represents key in nghttp3_ksl. + */ +typedef void nghttp3_ksl_key; + +typedef struct nghttp3_ksl_node nghttp3_ksl_node; + +typedef struct nghttp3_ksl_blk nghttp3_ksl_blk; + +/* + * nghttp3_ksl_node is a node which contains either nghttp3_ksl_blk or + * opaque data. If a node is an internal node, it contains + * nghttp3_ksl_blk. Otherwise, it has data. The key is stored at the + * location starting at key. + */ +struct nghttp3_ksl_node { + union { + nghttp3_ksl_blk *blk; + void *data; + }; + union { + uint64_t align; + /* key is a buffer to include key associated to this node. + Because the length of key is unknown until nghttp3_ksl_init is + called, the actual buffer will be allocated after this + field. */ + uint8_t key[1]; + }; +}; + +/* + * nghttp3_ksl_blk contains nghttp3_ksl_node objects. + */ +struct nghttp3_ksl_blk { + /* next points to the next block if leaf field is nonzero. */ + nghttp3_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + nghttp3_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGHTTP3_KSL_MAX_NBLK + nghttp3_ksl_node objects. Because nghttp3_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until nghttp3_ksl_init + is called. */ + uint8_t nodes[1]; + }; +}; + +/* + * nghttp3_ksl_compar is a function type which returns nonzero if key + * |lhs| should be placed before |rhs|. It returns 0 otherwise. + */ +typedef int (*nghttp3_ksl_compar)(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +typedef struct nghttp3_ksl nghttp3_ksl; + +typedef struct nghttp3_ksl_it nghttp3_ksl_it; + +/* + * nghttp3_ksl_it is a forward iterator to iterate nodes. + */ +struct nghttp3_ksl_it { + const nghttp3_ksl *ksl; + nghttp3_ksl_blk *blk; + size_t i; +}; + +/* + * nghttp3_ksl is a deterministic paged skip list. + */ +struct nghttp3_ksl { + /* head points to the root block. */ + nghttp3_ksl_blk *head; + /* front points to the first leaf block. */ + nghttp3_ksl_blk *front; + /* back points to the last leaf block. */ + nghttp3_ksl_blk *back; + nghttp3_ksl_compar compar; + size_t n; + /* keylen is the size of key */ + size_t keylen; + /* nodelen is the actual size of nghttp3_ksl_node including key + storage. */ + size_t nodelen; + const nghttp3_mem *mem; +}; + +/* + * nghttp3_ksl_init initializes |ksl|. |compar| specifies compare + * function. |keylen| is the length of key. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_ksl_init(nghttp3_ksl *ksl, nghttp3_ksl_compar compar, size_t keylen, + const nghttp3_mem *mem); + +/* + * nghttp3_ksl_free frees resources allocated for |ksl|. If |ksl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |ksl| itself. + */ +void nghttp3_ksl_free(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_insert inserts |key| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_INVALID_ARGUMENT + * |key| already exists. + */ +int nghttp3_ksl_insert(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key, void *data); + +/* + * nghttp3_ksl_remove removes the |key| from |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL. If |key| is not found, no deletion takes place and + * the return value of nghttp3_ksl_end(ksl) is assigned to |*it|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_INVALID_ARGUMENT + * |key| does not exist. + */ +int nghttp3_ksl_remove(nghttp3_ksl *ksl, nghttp3_ksl_it *it, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_lower_bound returns the iterator which points to the + * first node which has the key which is equal to |key| or the last + * node which satisfies !compar(&node->key, key). If there is no such + * node, it returns the iterator which satisfies nghttp3_ksl_it_end(it) + * != 0. + */ +nghttp3_ksl_it nghttp3_ksl_lower_bound(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key); + +/* + * nghttp3_ksl_lower_bound_compar works like nghttp3_ksl_lower_bound, + * but it takes custom function |compar| to do lower bound search. + */ +nghttp3_ksl_it nghttp3_ksl_lower_bound_compar(nghttp3_ksl *ksl, + const nghttp3_ksl_key *key, + nghttp3_ksl_compar compar); + +/* + * nghttp3_ksl_update_key replaces the key of nodes which has |old_key| + * with |new_key|. |new_key| must be strictly greater than the + * previous node and strictly smaller than the next node. + */ +void nghttp3_ksl_update_key(nghttp3_ksl *ksl, const nghttp3_ksl_key *old_key, + const nghttp3_ksl_key *new_key); + +/* + * nghttp3_ksl_begin returns the iterator which points to the first + * node. If there is no node in |ksl|, it returns the iterator which + * satisfies nghttp3_ksl_it_end(it) != 0. + */ +nghttp3_ksl_it nghttp3_ksl_begin(const nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_end returns the iterator which points to the node + * following the last node. The returned object satisfies + * nghttp3_ksl_it_end(). If there is no node in |ksl|, it returns the + * iterator which satisfies nghttp3_ksl_it_begin(it) != 0. + */ +nghttp3_ksl_it nghttp3_ksl_end(const nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_len returns the number of elements stored in |ksl|. + */ +size_t nghttp3_ksl_len(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_clear removes all elements stored in |ksl|. + */ +void nghttp3_ksl_clear(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_nth_node returns the |n|th node under |blk|. + */ +#define nghttp3_ksl_nth_node(KSL, BLK, N) \ + ((nghttp3_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) + +/* + * nghttp3_ksl_print prints its internal state in stderr. It assumes + * that the key is of type int64_t. This function should be used for + * the debugging purpose only. + */ +void nghttp3_ksl_print(nghttp3_ksl *ksl); + +/* + * nghttp3_ksl_it_init initializes |it|. + */ +void nghttp3_ksl_it_init(nghttp3_ksl_it *it, const nghttp3_ksl *ksl, + nghttp3_ksl_blk *blk, size_t i); + +/* + * nghttp3_ksl_it_get returns the data associated to the node which + * |it| points to. It is undefined to call this function when + * nghttp3_ksl_it_end(it) returns nonzero. + */ +void *nghttp3_ksl_it_get(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_it_next advances the iterator by one. It is undefined + * if this function is called when nghttp3_ksl_it_end(it) returns + * nonzero. + */ +#define nghttp3_ksl_it_next(IT) \ + (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ + ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ + : 0) + +/* + * nghttp3_ksl_it_prev moves backward the iterator by one. It is + * undefined if this function is called when nghttp3_ksl_it_begin(it) + * returns nonzero. + */ +void nghttp3_ksl_it_prev(nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +#define nghttp3_ksl_it_end(IT) \ + ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) + +/* + * nghttp3_ksl_it_begin returns nonzero if |it| points to the first + * node. |it| might satisfy both nghttp3_ksl_it_begin(&it) and + * nghttp3_ksl_it_end(&it) if the skip list has no node. + */ +int nghttp3_ksl_it_begin(const nghttp3_ksl_it *it); + +/* + * nghttp3_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when nghttp3_ksl_it_end(it) + * returns nonzero. + */ +#define nghttp3_ksl_it_key(IT) \ + ((nghttp3_ksl_key *)nghttp3_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) + +/* + * nghttp3_ksl_range_compar is an implementation of + * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to + * nghttp3_range object and the function returns nonzero if (const + * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range + * *)(rhs->ptr)->begin. + */ +int nghttp3_ksl_range_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +/* + * nghttp3_ksl_range_exclusive_compar is an implementation of + * nghttp3_ksl_compar. lhs->ptr and rhs->ptr must point to + * nghttp3_range object and the function returns nonzero if (const + * nghttp3_range *)(lhs->ptr)->begin < (const nghttp3_range + * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + */ +int nghttp3_ksl_range_exclusive_compar(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs); + +#endif /* NGHTTP3_KSL_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h new file mode 100644 index 00000000000000..6ee704cc47dd98 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_macro.h @@ -0,0 +1,47 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_MACRO_H +#define NGHTTP3_MACRO_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stddef.h> + +#include <nghttp3/nghttp3.h> + +#define nghttp3_min(A, B) ((A) < (B) ? (A) : (B)) +#define nghttp3_max(A, B) ((A) > (B) ? (A) : (B)) + +#define nghttp3_struct_of(ptr, type, member) \ + ((type *)(void *)((char *)(ptr)-offsetof(type, member))) + +#define nghttp3_arraylen(A) (sizeof(A) / sizeof(*(A))) + +#define lstreq(A, B, N) ((sizeof((A)) - 1) == (N) && memcmp((A), (B), (N)) == 0) + +#endif /* NGHTTP3_MACRO_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.c b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c new file mode 100644 index 00000000000000..a80955ad7a4c83 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.c @@ -0,0 +1,336 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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 "nghttp3_map.h" + +#include <string.h> +#include <assert.h> + +#include "nghttp3_conv.h" + +#define INITIAL_TABLE_LENGTH 256 + +int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = + nghttp3_mem_calloc(mem, map->tablelen, sizeof(nghttp3_map_bucket)); + if (map->table == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void nghttp3_map_free(nghttp3_map *map) { + size_t i; + nghttp3_map_bucket *bkt; + + if (!map) { + return; + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + if (bkt->ksl) { + nghttp3_ksl_free(bkt->ksl); + nghttp3_mem_free(map->mem, bkt->ksl); + } + } + + nghttp3_mem_free(map->mem, map->table); +} + +void nghttp3_map_each_free(nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr) { + uint32_t i; + nghttp3_map_bucket *bkt; + nghttp3_ksl_it it; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->ptr) { + func(bkt->ptr, ptr); + bkt->ptr = NULL; + assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); + nghttp3_ksl_it_next(&it)) { + func(nghttp3_ksl_it_get(&it), ptr); + } + + nghttp3_ksl_free(bkt->ksl); + nghttp3_mem_free(map->mem, bkt->ksl); + bkt->ksl = NULL; + } + } +} + +int nghttp3_map_each(nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + nghttp3_map_bucket *bkt; + nghttp3_ksl_it it; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->ptr) { + rv = func(bkt->ptr, ptr); + if (rv != 0) { + return rv; + } + assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); + nghttp3_ksl_it_next(&it)) { + rv = func(nghttp3_ksl_it_get(&it), ptr); + if (rv != 0) { + return rv; + } + } + } + } + return 0; +} + +void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key) { + entry->key = key; +} + +/* FNV1a hash */ +static uint32_t hash(key_type key, uint32_t mod) { + uint8_t *p, *end; + uint32_t h = 0x811C9DC5u; + + key = nghttp3_htonl64(key); + p = (uint8_t *)&key; + end = p + sizeof(key_type); + + for (; p != end;) { + h ^= *p++; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + + return h & (mod - 1); +} + +static int less(const nghttp3_ksl_key *lhs, const nghttp3_ksl_key *rhs) { + return *(key_type *)lhs < *(key_type *)rhs; +} + +static int map_insert(nghttp3_map *map, nghttp3_map_bucket *table, + uint32_t tablelen, nghttp3_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + nghttp3_map_bucket *bkt = &table[h]; + const nghttp3_mem *mem = map->mem; + int rv; + + if (bkt->ptr == NULL && + (bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0)) { + bkt->ptr = entry; + return 0; + } + + if (!bkt->ksl) { + bkt->ksl = nghttp3_mem_malloc(mem, sizeof(*bkt->ksl)); + if (bkt->ksl == NULL) { + return NGHTTP3_ERR_NOMEM; + } + nghttp3_ksl_init(bkt->ksl, less, sizeof(key_type), mem); + } + + if (bkt->ptr) { + rv = nghttp3_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); + if (rv != 0) { + return rv; + } + + bkt->ptr = NULL; + } + + return nghttp3_ksl_insert(bkt->ksl, NULL, &entry->key, entry); +} + +/* new_tablelen must be power of 2 */ +static int map_resize(nghttp3_map *map, uint32_t new_tablelen) { + uint32_t i; + nghttp3_map_bucket *new_table; + nghttp3_map_bucket *bkt; + nghttp3_ksl_it it; + int rv; + + new_table = + nghttp3_mem_calloc(map->mem, new_tablelen, sizeof(nghttp3_map_bucket)); + if (new_table == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->ptr) { + rv = map_insert(map, new_table, new_tablelen, bkt->ptr); + if (rv != 0) { + goto fail; + } + assert(bkt->ksl == NULL || nghttp3_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = nghttp3_ksl_begin(bkt->ksl); !nghttp3_ksl_it_end(&it); + nghttp3_ksl_it_next(&it)) { + rv = map_insert(map, new_table, new_tablelen, nghttp3_ksl_it_get(&it)); + if (rv != 0) { + goto fail; + } + } + } + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + if (bkt->ksl) { + nghttp3_ksl_free(bkt->ksl); + nghttp3_mem_free(map->mem, bkt->ksl); + } + } + + nghttp3_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; + +fail: + for (i = 0; i < new_tablelen; ++i) { + bkt = &new_table[i]; + if (bkt->ksl) { + nghttp3_ksl_free(bkt->ksl); + nghttp3_mem_free(map->mem, bkt->ksl); + } + } + + return rv; +} + +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *new_entry) { + int rv; + + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = map_resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = map_insert(map, map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key) { + nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + nghttp3_ksl_it it; + + if (bkt->ptr) { + if (bkt->ptr->key == key) { + return bkt->ptr; + } + return NULL; + } + + if (bkt->ksl) { + it = nghttp3_ksl_lower_bound(bkt->ksl, &key); + if (nghttp3_ksl_it_end(&it) || + *(key_type *)nghttp3_ksl_it_key(&it) != key) { + return NULL; + } + return nghttp3_ksl_it_get(&it); + } + + return NULL; +} + +int nghttp3_map_remove(nghttp3_map *map, key_type key) { + nghttp3_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + int rv; + + if (bkt->ptr) { + if (bkt->ptr->key == key) { + bkt->ptr = NULL; + --map->size; + return 0; + } + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (bkt->ksl) { + rv = nghttp3_ksl_remove(bkt->ksl, NULL, &key); + if (rv != 0) { + return rv; + } + --map->size; + return 0; + } + + return NGHTTP3_ERR_INVALID_ARGUMENT; +} + +void nghttp3_map_clear(nghttp3_map *map) { + uint32_t i; + nghttp3_map_bucket *bkt; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + bkt->ptr = NULL; + if (bkt->ksl) { + nghttp3_ksl_free(bkt->ksl); + nghttp3_mem_free(map->mem, bkt->ksl); + bkt->ksl = NULL; + } + } + + map->size = 0; +} + +size_t nghttp3_map_size(nghttp3_map *map) { return map->size; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_map.h b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h new file mode 100644 index 00000000000000..2eae2e7cb725c6 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_map.h @@ -0,0 +1,154 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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 NGHTTP3_MAP_H +#define NGHTTP3_MAP_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" +#include "nghttp3_ksl.h" + +/* Implementation of unordered map */ + +typedef uint64_t key_type; + +typedef struct nghttp3_map_entry nghttp3_map_entry; + +struct nghttp3_map_entry { + key_type key; +}; + +typedef struct nghttp3_map_bucket { + nghttp3_map_entry *ptr; + nghttp3_ksl *ksl; +} nghttp3_map_bucket; + +typedef struct nghttp3_map { + nghttp3_map_bucket *table; + const nghttp3_mem *mem; + size_t size; + uint32_t tablelen; +} nghttp3_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory + */ +int nghttp3_map_init(nghttp3_map *map, const nghttp3_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use nghttp3_map_each_free() to free + * each entries. + */ +void nghttp3_map_free(nghttp3_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void nghttp3_map_each_free(nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void nghttp3_map_entry_init(nghttp3_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGHTTP3_ERR_NOMEM + * Out of memory + */ +int nghttp3_map_insert(nghttp3_map *map, nghttp3_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +nghttp3_map_entry *nghttp3_map_find(nghttp3_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int nghttp3_map_remove(nghttp3_map *map, key_type key); + +/* + * Removes all entries from |map|. + */ +void nghttp3_map_clear(nghttp3_map *map); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t nghttp3_map_size(nghttp3_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * nghttp3_map_each_free() instead. + */ +int nghttp3_map_each(nghttp3_map *map, + int (*func)(nghttp3_map_entry *entry, void *ptr), + void *ptr); + +#endif /* NGHTTP3_MAP_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c new file mode 100644 index 00000000000000..e5f93f10bdabf2 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.c @@ -0,0 +1,77 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 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 "nghttp3_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return realloc(ptr, size); +} + +static nghttp3_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +const nghttp3_mem *nghttp3_mem_default(void) { return &mem_default; } + +void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, + void *mem_user_data) { + free_func(ptr, mem_user_data); +} + +void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h new file mode 100644 index 00000000000000..55ef86b4f921c9 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_mem.h @@ -0,0 +1,45 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 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 NGHTTP3_MEM_H +#define NGHTTP3_MEM_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *nghttp3_mem_malloc(const nghttp3_mem *mem, size_t size); +void nghttp3_mem_free(const nghttp3_mem *mem, void *ptr); +void nghttp3_mem_free2(const nghttp3_free free_func, void *ptr, + void *mem_user_data); +void *nghttp3_mem_calloc(const nghttp3_mem *mem, size_t nmemb, size_t size); +void *nghttp3_mem_realloc(const nghttp3_mem *mem, void *ptr, size_t size); + +#endif /* NGHTTP3_MEM_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c new file mode 100644 index 00000000000000..5d09050ae63798 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.c @@ -0,0 +1,168 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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 "nghttp3_pq.h" + +#include <assert.h> + +#include "nghttp3_macro.h" + +void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, + const nghttp3_mem *mem) { + pq->mem = mem; + pq->capacity = 0; + pq->q = NULL; + pq->length = 0; + pq->less = less; +} + +void nghttp3_pq_free(nghttp3_pq *pq) { + nghttp3_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(nghttp3_pq *pq, size_t i, size_t j) { + nghttp3_pq_entry *a = pq->q[i]; + nghttp3_pq_entry *b = pq->q[j]; + + pq->q[i] = b; + b->index = i; + pq->q[j] = a; + a->index = j; +} + +static void bubble_up(nghttp3_pq *pq, size_t index) { + size_t parent; + while (index != 0) { + parent = (index - 1) / 2; + if (!pq->less(pq->q[index], pq->q[parent])) { + return; + } + swap(pq, parent, index); + index = parent; + } +} + +int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item) { + if (pq->capacity <= pq->length) { + void *nq; + size_t ncapacity; + + ncapacity = nghttp3_max(4, (pq->capacity * 2)); + + nq = nghttp3_mem_realloc(pq->mem, pq->q, + ncapacity * sizeof(nghttp3_pq_entry *)); + if (nq == NULL) { + return NGHTTP3_ERR_NOMEM; + } + pq->capacity = ncapacity; + pq->q = nq; + } + pq->q[pq->length] = item; + item->index = pq->length; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq) { + assert(pq->length); + return pq->q[0]; +} + +static void bubble_down(nghttp3_pq *pq, size_t index) { + size_t i, j, minindex; + for (;;) { + j = index * 2 + 1; + minindex = index; + for (i = 0; i < 2; ++i, ++j) { + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex == index) { + return; + } + swap(pq, index, minindex); + index = minindex; + } +} + +void nghttp3_pq_pop(nghttp3_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + pq->q[0]->index = 0; + --pq->length; + bubble_down(pq, 0); + } +} + +void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item) { + assert(pq->q[item->index] == item); + + if (item->index == 0) { + nghttp3_pq_pop(pq); + return; + } + + if (item->index == pq->length - 1) { + --pq->length; + return; + } + + pq->q[item->index] = pq->q[pq->length - 1]; + pq->q[item->index]->index = item->index; + --pq->length; + + if (pq->less(item, pq->q[item->index])) { + bubble_down(pq, item->index); + } else { + bubble_up(pq, item->index); + } +} + +int nghttp3_pq_empty(const nghttp3_pq *pq) { return pq->length == 0; } + +size_t nghttp3_pq_size(const nghttp3_pq *pq) { return pq->length; } + +int nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_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; +} + +void nghttp3_pq_clear(nghttp3_pq *pq) { pq->length = 0; } diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h new file mode 100644 index 00000000000000..f1f369231dfcdc --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_pq.h @@ -0,0 +1,129 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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 NGHTTP3_PQ_H +#define NGHTTP3_PQ_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +/* Implementation of priority queue */ + +/* NGHTTP3_PQ_BAD_INDEX is the priority queue index which indicates + that an entry is not queued. Assigning this value to + nghttp3_pq_entry.index can check that the entry is queued or not. */ +#define NGHTTP3_PQ_BAD_INDEX SIZE_MAX + +typedef struct nghttp3_pq_entry { + size_t index; +} nghttp3_pq_entry; + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*nghttp3_less)(const nghttp3_pq_entry *lhs, + const nghttp3_pq_entry *rhs); + +typedef struct nghttp3_pq { + /* The pointer to the pointer to the item stored */ + nghttp3_pq_entry **q; + /* Memory allocator */ + const nghttp3_mem *mem; + /* The number of items stored */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + nghttp3_less less; +} nghttp3_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + */ +void nghttp3_pq_init(nghttp3_pq *pq, nghttp3_less less, const nghttp3_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void nghttp3_pq_free(nghttp3_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_pq_push(nghttp3_pq *pq, nghttp3_pq_entry *item); + +/* + * Returns item at the top of the queue |pq|. It is undefined if the + * queue is empty. + */ +nghttp3_pq_entry *nghttp3_pq_top(const nghttp3_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void nghttp3_pq_pop(nghttp3_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int nghttp3_pq_empty(const nghttp3_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t nghttp3_pq_size(const nghttp3_pq *pq); + +typedef int (*nghttp3_pq_item_cb)(nghttp3_pq_entry *item, void *arg); + +/* + * Applys |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 nghttp3_pq_each(const nghttp3_pq *pq, nghttp3_pq_item_cb fun, void *arg); + +/* + * Removes |item| from priority queue. + */ +void nghttp3_pq_remove(nghttp3_pq *pq, nghttp3_pq_entry *item); + +void nghttp3_pq_clear(nghttp3_pq *pq); + +#endif /* NGHTTP3_PQ_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c new file mode 100644 index 00000000000000..6837942687aa88 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.c @@ -0,0 +1,4095 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 "nghttp3_qpack.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_str.h" +#include "nghttp3_macro.h" +#include "nghttp3_debug.h" + +/* NGHTTP3_QPACK_MAX_QPACK_STREAMS is the maximum number of concurrent + nghttp3_qpack_stream object to handle a client which never cancel + or acknowledge header block. After this limit, encoder stops using + dynamic table. */ +#define NGHTTP3_QPACK_MAX_QPACK_STREAMS 2000 + +/* Make scalar initialization form of nghttp3_qpack_static_entry */ +#define MAKE_STATIC_ENT(I, T, H) \ + { I, T, H } + +/* Generated by mkstatichdtbl.py */ +static nghttp3_qpack_static_entry token_stable[] = { + MAKE_STATIC_ENT(0, NGHTTP3_QPACK_TOKEN__AUTHORITY, 3153725150u), + MAKE_STATIC_ENT(15, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(16, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(17, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(18, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(19, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(20, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(21, NGHTTP3_QPACK_TOKEN__METHOD, 695666056u), + MAKE_STATIC_ENT(1, NGHTTP3_QPACK_TOKEN__PATH, 3292848686u), + MAKE_STATIC_ENT(22, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u), + MAKE_STATIC_ENT(23, NGHTTP3_QPACK_TOKEN__SCHEME, 2510477674u), + MAKE_STATIC_ENT(24, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(25, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(26, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(27, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(28, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(63, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(64, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(65, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(66, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(67, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(68, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(69, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(70, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(71, NGHTTP3_QPACK_TOKEN__STATUS, 4000288983u), + MAKE_STATIC_ENT(29, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u), + MAKE_STATIC_ENT(30, NGHTTP3_QPACK_TOKEN_ACCEPT, 136609321u), + MAKE_STATIC_ENT(31, NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING, 3379649177u), + MAKE_STATIC_ENT(72, NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE, 1979086614u), + MAKE_STATIC_ENT(32, NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES, 1713753958u), + MAKE_STATIC_ENT(73, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS, + 901040780u), + MAKE_STATIC_ENT(74, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS, + 901040780u), + MAKE_STATIC_ENT(33, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, + 1524311232u), + MAKE_STATIC_ENT(34, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, + 1524311232u), + MAKE_STATIC_ENT(75, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS, + 1524311232u), + MAKE_STATIC_ENT(76, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, + 2175229868u), + MAKE_STATIC_ENT(77, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, + 2175229868u), + MAKE_STATIC_ENT(78, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS, + 2175229868u), + MAKE_STATIC_ENT(35, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN, + 2710797292u), + MAKE_STATIC_ENT(79, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS, + 2449824425u), + MAKE_STATIC_ENT(80, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS, + 3599549072u), + MAKE_STATIC_ENT(81, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD, + 2417078055u), + MAKE_STATIC_ENT(82, NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD, + 2417078055u), + MAKE_STATIC_ENT(2, NGHTTP3_QPACK_TOKEN_AGE, 742476188u), + MAKE_STATIC_ENT(83, NGHTTP3_QPACK_TOKEN_ALT_SVC, 2148877059u), + MAKE_STATIC_ENT(84, NGHTTP3_QPACK_TOKEN_AUTHORIZATION, 2436257726u), + MAKE_STATIC_ENT(36, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(37, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(38, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(39, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(40, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(41, NGHTTP3_QPACK_TOKEN_CACHE_CONTROL, 1355326669u), + MAKE_STATIC_ENT(3, NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION, 3889184348u), + MAKE_STATIC_ENT(42, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u), + MAKE_STATIC_ENT(43, NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING, 65203592u), + MAKE_STATIC_ENT(4, NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH, 1308181789u), + MAKE_STATIC_ENT(85, NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY, + 1569039836u), + MAKE_STATIC_ENT(44, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(45, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(46, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(47, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(48, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(49, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(50, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(51, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(52, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(53, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(54, NGHTTP3_QPACK_TOKEN_CONTENT_TYPE, 4244048277u), + MAKE_STATIC_ENT(5, NGHTTP3_QPACK_TOKEN_COOKIE, 2007449791u), + MAKE_STATIC_ENT(6, NGHTTP3_QPACK_TOKEN_DATE, 3564297305u), + MAKE_STATIC_ENT(86, NGHTTP3_QPACK_TOKEN_EARLY_DATA, 4080895051u), + MAKE_STATIC_ENT(7, NGHTTP3_QPACK_TOKEN_ETAG, 113792960u), + MAKE_STATIC_ENT(87, NGHTTP3_QPACK_TOKEN_EXPECT_CT, 1183214960u), + MAKE_STATIC_ENT(88, NGHTTP3_QPACK_TOKEN_FORWARDED, 1485178027u), + MAKE_STATIC_ENT(8, NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE, 2213050793u), + MAKE_STATIC_ENT(9, NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH, 2536202615u), + MAKE_STATIC_ENT(89, NGHTTP3_QPACK_TOKEN_IF_RANGE, 2340978238u), + MAKE_STATIC_ENT(10, NGHTTP3_QPACK_TOKEN_LAST_MODIFIED, 3226950251u), + MAKE_STATIC_ENT(11, NGHTTP3_QPACK_TOKEN_LINK, 232457833u), + MAKE_STATIC_ENT(12, NGHTTP3_QPACK_TOKEN_LOCATION, 200649126u), + MAKE_STATIC_ENT(90, NGHTTP3_QPACK_TOKEN_ORIGIN, 3649018447u), + MAKE_STATIC_ENT(91, NGHTTP3_QPACK_TOKEN_PURPOSE, 4212263681u), + MAKE_STATIC_ENT(55, NGHTTP3_QPACK_TOKEN_RANGE, 4208725202u), + MAKE_STATIC_ENT(13, NGHTTP3_QPACK_TOKEN_REFERER, 3969579366u), + MAKE_STATIC_ENT(92, NGHTTP3_QPACK_TOKEN_SERVER, 1085029842u), + MAKE_STATIC_ENT(14, NGHTTP3_QPACK_TOKEN_SET_COOKIE, 1848371000u), + MAKE_STATIC_ENT(56, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, + 4138147361u), + MAKE_STATIC_ENT(57, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, + 4138147361u), + MAKE_STATIC_ENT(58, NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY, + 4138147361u), + MAKE_STATIC_ENT(93, NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN, 2432297564u), + MAKE_STATIC_ENT(94, NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS, + 2479169413u), + MAKE_STATIC_ENT(95, NGHTTP3_QPACK_TOKEN_USER_AGENT, 606444526u), + MAKE_STATIC_ENT(59, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u), + MAKE_STATIC_ENT(60, NGHTTP3_QPACK_TOKEN_VARY, 1085005381u), + MAKE_STATIC_ENT(61, NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS, + 3644557769u), + MAKE_STATIC_ENT(96, NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR, 2914187656u), + MAKE_STATIC_ENT(97, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u), + MAKE_STATIC_ENT(98, NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS, 3993834824u), + MAKE_STATIC_ENT(62, NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION, 2501058888u), +}; + +/* Make scalar initialization form of nghttp3_qpack_static_entry */ +#define MAKE_STATIC_HD(N, V, T) \ + { \ + {NULL, NULL, (uint8_t *)(N), sizeof((N)) - 1, -1}, \ + {NULL, NULL, (uint8_t *)(V), sizeof((V)) - 1, -1}, T \ + } + +static nghttp3_qpack_static_header stable[] = { + MAKE_STATIC_HD(":authority", "", NGHTTP3_QPACK_TOKEN__AUTHORITY), + MAKE_STATIC_HD(":path", "/", NGHTTP3_QPACK_TOKEN__PATH), + MAKE_STATIC_HD("age", "0", NGHTTP3_QPACK_TOKEN_AGE), + MAKE_STATIC_HD("content-disposition", "", + NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION), + MAKE_STATIC_HD("content-length", "0", NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH), + MAKE_STATIC_HD("cookie", "", NGHTTP3_QPACK_TOKEN_COOKIE), + MAKE_STATIC_HD("date", "", NGHTTP3_QPACK_TOKEN_DATE), + MAKE_STATIC_HD("etag", "", NGHTTP3_QPACK_TOKEN_ETAG), + MAKE_STATIC_HD("if-modified-since", "", + NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE), + MAKE_STATIC_HD("if-none-match", "", NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH), + MAKE_STATIC_HD("last-modified", "", NGHTTP3_QPACK_TOKEN_LAST_MODIFIED), + MAKE_STATIC_HD("link", "", NGHTTP3_QPACK_TOKEN_LINK), + MAKE_STATIC_HD("location", "", NGHTTP3_QPACK_TOKEN_LOCATION), + MAKE_STATIC_HD("referer", "", NGHTTP3_QPACK_TOKEN_REFERER), + MAKE_STATIC_HD("set-cookie", "", NGHTTP3_QPACK_TOKEN_SET_COOKIE), + MAKE_STATIC_HD(":method", "CONNECT", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "DELETE", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "GET", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "HEAD", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "OPTIONS", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "POST", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":method", "PUT", NGHTTP3_QPACK_TOKEN__METHOD), + MAKE_STATIC_HD(":scheme", "http", NGHTTP3_QPACK_TOKEN__SCHEME), + MAKE_STATIC_HD(":scheme", "https", NGHTTP3_QPACK_TOKEN__SCHEME), + MAKE_STATIC_HD(":status", "103", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "200", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "304", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "404", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "503", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD("accept", "*/*", NGHTTP3_QPACK_TOKEN_ACCEPT), + MAKE_STATIC_HD("accept", "application/dns-message", + NGHTTP3_QPACK_TOKEN_ACCEPT), + MAKE_STATIC_HD("accept-encoding", "gzip, deflate, br", + NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING), + MAKE_STATIC_HD("accept-ranges", "bytes", NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES), + MAKE_STATIC_HD("access-control-allow-headers", "cache-control", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), + MAKE_STATIC_HD("access-control-allow-headers", "content-type", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), + MAKE_STATIC_HD("access-control-allow-origin", "*", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN), + MAKE_STATIC_HD("cache-control", "max-age=0", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "max-age=2592000", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "max-age=604800", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "no-cache", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "no-store", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("cache-control", "public, max-age=31536000", + NGHTTP3_QPACK_TOKEN_CACHE_CONTROL), + MAKE_STATIC_HD("content-encoding", "br", + NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), + MAKE_STATIC_HD("content-encoding", "gzip", + NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING), + MAKE_STATIC_HD("content-type", "application/dns-message", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "application/javascript", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "application/json", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "application/x-www-form-urlencoded", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "image/gif", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "image/jpeg", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "image/png", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/css", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/html; charset=utf-8", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/plain", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("content-type", "text/plain;charset=utf-8", + NGHTTP3_QPACK_TOKEN_CONTENT_TYPE), + MAKE_STATIC_HD("range", "bytes=0-", NGHTTP3_QPACK_TOKEN_RANGE), + MAKE_STATIC_HD("strict-transport-security", "max-age=31536000", + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), + MAKE_STATIC_HD("strict-transport-security", + "max-age=31536000; includesubdomains", + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), + MAKE_STATIC_HD("strict-transport-security", + "max-age=31536000; includesubdomains; preload", + NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY), + MAKE_STATIC_HD("vary", "accept-encoding", NGHTTP3_QPACK_TOKEN_VARY), + MAKE_STATIC_HD("vary", "origin", NGHTTP3_QPACK_TOKEN_VARY), + MAKE_STATIC_HD("x-content-type-options", "nosniff", + NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS), + MAKE_STATIC_HD("x-xss-protection", "1; mode=block", + NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION), + MAKE_STATIC_HD(":status", "100", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "204", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "206", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "302", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "400", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "403", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "421", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "425", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD(":status", "500", NGHTTP3_QPACK_TOKEN__STATUS), + MAKE_STATIC_HD("accept-language", "", NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE), + MAKE_STATIC_HD("access-control-allow-credentials", "FALSE", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), + MAKE_STATIC_HD("access-control-allow-credentials", "TRUE", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS), + MAKE_STATIC_HD("access-control-allow-headers", "*", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS), + MAKE_STATIC_HD("access-control-allow-methods", "get", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), + MAKE_STATIC_HD("access-control-allow-methods", "get, post, options", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), + MAKE_STATIC_HD("access-control-allow-methods", "options", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS), + MAKE_STATIC_HD("access-control-expose-headers", "content-length", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS), + MAKE_STATIC_HD("access-control-request-headers", "content-type", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS), + MAKE_STATIC_HD("access-control-request-method", "get", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), + MAKE_STATIC_HD("access-control-request-method", "post", + NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD), + MAKE_STATIC_HD("alt-svc", "clear", NGHTTP3_QPACK_TOKEN_ALT_SVC), + MAKE_STATIC_HD("authorization", "", NGHTTP3_QPACK_TOKEN_AUTHORIZATION), + MAKE_STATIC_HD("content-security-policy", + "script-src 'none'; object-src 'none'; base-uri 'none'", + NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY), + MAKE_STATIC_HD("early-data", "1", NGHTTP3_QPACK_TOKEN_EARLY_DATA), + MAKE_STATIC_HD("expect-ct", "", NGHTTP3_QPACK_TOKEN_EXPECT_CT), + MAKE_STATIC_HD("forwarded", "", NGHTTP3_QPACK_TOKEN_FORWARDED), + MAKE_STATIC_HD("if-range", "", NGHTTP3_QPACK_TOKEN_IF_RANGE), + MAKE_STATIC_HD("origin", "", NGHTTP3_QPACK_TOKEN_ORIGIN), + MAKE_STATIC_HD("purpose", "prefetch", NGHTTP3_QPACK_TOKEN_PURPOSE), + MAKE_STATIC_HD("server", "", NGHTTP3_QPACK_TOKEN_SERVER), + MAKE_STATIC_HD("timing-allow-origin", "*", + NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN), + MAKE_STATIC_HD("upgrade-insecure-requests", "1", + NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS), + MAKE_STATIC_HD("user-agent", "", NGHTTP3_QPACK_TOKEN_USER_AGENT), + MAKE_STATIC_HD("x-forwarded-for", "", NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR), + MAKE_STATIC_HD("x-frame-options", "deny", + NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), + MAKE_STATIC_HD("x-frame-options", "sameorigin", + NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS), +}; + +static int memeq(const void *s1, const void *s2, size_t n) { + return n == 0 || memcmp(s1, s2, n) == 0; +} + +/* Generated by genlibtokenlookup.py */ +static int32_t qpack_lookup_token(const uint8_t *name, size_t namelen) { + switch (namelen) { + case 2: + switch (name[1]) { + case 'e': + if (memeq("t", name, 1)) { + return NGHTTP3_QPACK_TOKEN_TE; + } + break; + } + break; + case 3: + switch (name[2]) { + case 'e': + if (memeq("ag", name, 2)) { + return NGHTTP3_QPACK_TOKEN_AGE; + } + break; + } + break; + case 4: + switch (name[3]) { + case 'e': + if (memeq("dat", name, 3)) { + return NGHTTP3_QPACK_TOKEN_DATE; + } + break; + case 'g': + if (memeq("eta", name, 3)) { + return NGHTTP3_QPACK_TOKEN_ETAG; + } + break; + case 'k': + if (memeq("lin", name, 3)) { + return NGHTTP3_QPACK_TOKEN_LINK; + } + break; + case 't': + if (memeq("hos", name, 3)) { + return NGHTTP3_QPACK_TOKEN_HOST; + } + break; + case 'y': + if (memeq("var", name, 3)) { + return NGHTTP3_QPACK_TOKEN_VARY; + } + break; + } + break; + case 5: + switch (name[4]) { + case 'e': + if (memeq("rang", name, 4)) { + return NGHTTP3_QPACK_TOKEN_RANGE; + } + break; + case 'h': + if (memeq(":pat", name, 4)) { + return NGHTTP3_QPACK_TOKEN__PATH; + } + break; + } + break; + case 6: + switch (name[5]) { + case 'e': + if (memeq("cooki", name, 5)) { + return NGHTTP3_QPACK_TOKEN_COOKIE; + } + break; + case 'n': + if (memeq("origi", name, 5)) { + return NGHTTP3_QPACK_TOKEN_ORIGIN; + } + break; + case 'r': + if (memeq("serve", name, 5)) { + return NGHTTP3_QPACK_TOKEN_SERVER; + } + break; + case 't': + if (memeq("accep", name, 5)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT; + } + break; + } + break; + case 7: + switch (name[6]) { + case 'c': + if (memeq("alt-sv", name, 6)) { + return NGHTTP3_QPACK_TOKEN_ALT_SVC; + } + break; + case 'd': + if (memeq(":metho", name, 6)) { + return NGHTTP3_QPACK_TOKEN__METHOD; + } + break; + case 'e': + if (memeq(":schem", name, 6)) { + return NGHTTP3_QPACK_TOKEN__SCHEME; + } + if (memeq("purpos", name, 6)) { + return NGHTTP3_QPACK_TOKEN_PURPOSE; + } + if (memeq("upgrad", name, 6)) { + return NGHTTP3_QPACK_TOKEN_UPGRADE; + } + break; + case 'r': + if (memeq("refere", name, 6)) { + return NGHTTP3_QPACK_TOKEN_REFERER; + } + break; + case 's': + if (memeq(":statu", name, 6)) { + return NGHTTP3_QPACK_TOKEN__STATUS; + } + break; + } + break; + case 8: + switch (name[7]) { + case 'e': + if (memeq("if-rang", name, 7)) { + return NGHTTP3_QPACK_TOKEN_IF_RANGE; + } + break; + case 'n': + if (memeq("locatio", name, 7)) { + return NGHTTP3_QPACK_TOKEN_LOCATION; + } + break; + case 'y': + if (memeq("priorit", name, 7)) { + return NGHTTP3_QPACK_TOKEN_PRIORITY; + } + break; + } + break; + case 9: + switch (name[8]) { + case 'd': + if (memeq("forwarde", name, 8)) { + return NGHTTP3_QPACK_TOKEN_FORWARDED; + } + break; + case 'l': + if (memeq(":protoco", name, 8)) { + return NGHTTP3_QPACK_TOKEN__PROTOCOL; + } + break; + case 't': + if (memeq("expect-c", name, 8)) { + return NGHTTP3_QPACK_TOKEN_EXPECT_CT; + } + break; + } + break; + case 10: + switch (name[9]) { + case 'a': + if (memeq("early-dat", name, 9)) { + return NGHTTP3_QPACK_TOKEN_EARLY_DATA; + } + break; + case 'e': + if (memeq("keep-aliv", name, 9)) { + return NGHTTP3_QPACK_TOKEN_KEEP_ALIVE; + } + if (memeq("set-cooki", name, 9)) { + return NGHTTP3_QPACK_TOKEN_SET_COOKIE; + } + break; + case 'n': + if (memeq("connectio", name, 9)) { + return NGHTTP3_QPACK_TOKEN_CONNECTION; + } + break; + case 't': + if (memeq("user-agen", name, 9)) { + return NGHTTP3_QPACK_TOKEN_USER_AGENT; + } + break; + case 'y': + if (memeq(":authorit", name, 9)) { + return NGHTTP3_QPACK_TOKEN__AUTHORITY; + } + break; + } + break; + case 12: + switch (name[11]) { + case 'e': + if (memeq("content-typ", name, 11)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_TYPE; + } + break; + } + break; + case 13: + switch (name[12]) { + case 'd': + if (memeq("last-modifie", name, 12)) { + return NGHTTP3_QPACK_TOKEN_LAST_MODIFIED; + } + break; + case 'h': + if (memeq("if-none-matc", name, 12)) { + return NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH; + } + break; + case 'l': + if (memeq("cache-contro", name, 12)) { + return NGHTTP3_QPACK_TOKEN_CACHE_CONTROL; + } + break; + case 'n': + if (memeq("authorizatio", name, 12)) { + return NGHTTP3_QPACK_TOKEN_AUTHORIZATION; + } + break; + case 's': + if (memeq("accept-range", name, 12)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT_RANGES; + } + break; + } + break; + case 14: + switch (name[13]) { + case 'h': + if (memeq("content-lengt", name, 13)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH; + } + break; + } + break; + case 15: + switch (name[14]) { + case 'e': + if (memeq("accept-languag", name, 14)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT_LANGUAGE; + } + break; + case 'g': + if (memeq("accept-encodin", name, 14)) { + return NGHTTP3_QPACK_TOKEN_ACCEPT_ENCODING; + } + break; + case 'r': + if (memeq("x-forwarded-fo", name, 14)) { + return NGHTTP3_QPACK_TOKEN_X_FORWARDED_FOR; + } + break; + case 's': + if (memeq("x-frame-option", name, 14)) { + return NGHTTP3_QPACK_TOKEN_X_FRAME_OPTIONS; + } + break; + } + break; + case 16: + switch (name[15]) { + case 'g': + if (memeq("content-encodin", name, 15)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_ENCODING; + } + break; + case 'n': + if (memeq("proxy-connectio", name, 15)) { + return NGHTTP3_QPACK_TOKEN_PROXY_CONNECTION; + } + if (memeq("x-xss-protectio", name, 15)) { + return NGHTTP3_QPACK_TOKEN_X_XSS_PROTECTION; + } + break; + } + break; + case 17: + switch (name[16]) { + case 'e': + if (memeq("if-modified-sinc", name, 16)) { + return NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE; + } + break; + case 'g': + if (memeq("transfer-encodin", name, 16)) { + return NGHTTP3_QPACK_TOKEN_TRANSFER_ENCODING; + } + break; + } + break; + case 19: + switch (name[18]) { + case 'n': + if (memeq("content-dispositio", name, 18)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_DISPOSITION; + } + if (memeq("timing-allow-origi", name, 18)) { + return NGHTTP3_QPACK_TOKEN_TIMING_ALLOW_ORIGIN; + } + break; + } + break; + case 22: + switch (name[21]) { + case 's': + if (memeq("x-content-type-option", name, 21)) { + return NGHTTP3_QPACK_TOKEN_X_CONTENT_TYPE_OPTIONS; + } + break; + } + break; + case 23: + switch (name[22]) { + case 'y': + if (memeq("content-security-polic", name, 22)) { + return NGHTTP3_QPACK_TOKEN_CONTENT_SECURITY_POLICY; + } + break; + } + break; + case 25: + switch (name[24]) { + case 's': + if (memeq("upgrade-insecure-request", name, 24)) { + return NGHTTP3_QPACK_TOKEN_UPGRADE_INSECURE_REQUESTS; + } + break; + case 'y': + if (memeq("strict-transport-securit", name, 24)) { + return NGHTTP3_QPACK_TOKEN_STRICT_TRANSPORT_SECURITY; + } + break; + } + break; + case 27: + switch (name[26]) { + case 'n': + if (memeq("access-control-allow-origi", name, 26)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_ORIGIN; + } + break; + } + break; + case 28: + switch (name[27]) { + case 's': + if (memeq("access-control-allow-header", name, 27)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_HEADERS; + } + if (memeq("access-control-allow-method", name, 27)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_METHODS; + } + break; + } + break; + case 29: + switch (name[28]) { + case 'd': + if (memeq("access-control-request-metho", name, 28)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_METHOD; + } + break; + case 's': + if (memeq("access-control-expose-header", name, 28)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_EXPOSE_HEADERS; + } + break; + } + break; + case 30: + switch (name[29]) { + case 's': + if (memeq("access-control-request-header", name, 29)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_REQUEST_HEADERS; + } + break; + } + break; + case 32: + switch (name[31]) { + case 's': + if (memeq("access-control-allow-credential", name, 31)) { + return NGHTTP3_QPACK_TOKEN_ACCESS_CONTROL_ALLOW_CREDENTIALS; + } + break; + } + break; + } + return -1; +} + +static size_t table_space(size_t namelen, size_t valuelen) { + return NGHTTP3_QPACK_ENTRY_OVERHEAD + namelen + valuelen; +} + +static int qpack_nv_name_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { + return a->name->len == b->namelen && + memeq(a->name->base, b->name, b->namelen); +} + +static int qpack_nv_value_eq(const nghttp3_qpack_nv *a, const nghttp3_nv *b) { + return a->value->len == b->valuelen && + memeq(a->value->base, b->value, b->valuelen); +} + +static void qpack_map_init(nghttp3_qpack_map *map) { + memset(map, 0, sizeof(nghttp3_qpack_map)); +} + +static void qpack_map_insert(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { + nghttp3_qpack_entry **bucket; + + bucket = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; + + if (*bucket == NULL) { + *bucket = ent; + return; + } + + /* larger absidx is linked near the root */ + ent->map_next = *bucket; + *bucket = ent; +} + +static void qpack_map_remove(nghttp3_qpack_map *map, nghttp3_qpack_entry *ent) { + nghttp3_qpack_entry **dst; + + dst = &map->table[ent->hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; + + for (; *dst; dst = &(*dst)->map_next) { + if (*dst != ent) { + continue; + } + + *dst = ent->map_next; + ent->map_next = NULL; + return; + } +} + +/* + * qpack_context_can_reference returns nonzero if dynamic table entry + * at |absidx| can be referenced. In other words, it is within + * ctx->max_dtable_size. + */ +static int qpack_context_can_reference(nghttp3_qpack_context *ctx, + uint64_t absidx) { + nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); + return ctx->dtable_sum - ent->sum <= ctx->max_dtable_size; +} + +/* |*ppb_match| (post-base match), if it is not NULL, is always exact + match. */ +static void encoder_qpack_map_find(nghttp3_qpack_encoder *encoder, + int *exact_match, + nghttp3_qpack_entry **pmatch, + nghttp3_qpack_entry **ppb_match, + const nghttp3_nv *nv, int32_t token, + uint32_t hash, uint64_t krcnt, + int allow_blocking, int name_only) { + nghttp3_qpack_entry *p; + + *exact_match = 0; + *pmatch = NULL; + *ppb_match = NULL; + + for (p = encoder->dtable_map.table[hash & (NGHTTP3_QPACK_MAP_SIZE - 1)]; p; + p = p->map_next) { + if (token != p->nv.token || + (token == -1 && (hash != p->hash || !qpack_nv_name_eq(&p->nv, nv))) || + !qpack_context_can_reference(&encoder->ctx, p->absidx)) { + continue; + } + if (allow_blocking || p->absidx + 1 <= krcnt) { + if (!*pmatch) { + *pmatch = p; + if (name_only) { + return; + } + } + if (qpack_nv_value_eq(&p->nv, nv)) { + *pmatch = p; + *exact_match = 1; + return; + } + } else if (!*ppb_match && qpack_nv_value_eq(&p->nv, nv)) { + *ppb_match = p; + } + } +} + +/* + * qpack_context_init initializes |ctx|. |max_dtable_size| is the + * maximum size of dynamic table. |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_context_init(nghttp3_qpack_context *ctx, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + size_t len = 4096 / NGHTTP3_QPACK_ENTRY_OVERHEAD; + size_t len2; + + for (len2 = 1; len2 < len; len2 <<= 1) + ; + + rv = nghttp3_ringbuf_init(&ctx->dtable, len2, sizeof(nghttp3_qpack_entry *), + mem); + if (rv != 0) { + return rv; + } + + ctx->mem = mem; + ctx->dtable_size = 0; + ctx->dtable_sum = 0; + ctx->hard_max_dtable_size = max_dtable_size; + ctx->max_dtable_size = 0; + ctx->max_blocked = max_blocked; + ctx->next_absidx = 0; + ctx->bad = 0; + + return 0; +} + +static void qpack_context_free(nghttp3_qpack_context *ctx) { + nghttp3_qpack_entry *ent; + size_t i, len = nghttp3_ringbuf_len(&ctx->dtable); + + for (i = 0; i < len; ++i) { + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i); + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(ctx->mem, ent); + } + nghttp3_ringbuf_free(&ctx->dtable); +} + +static int ref_min_cnt_less(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + nghttp3_qpack_header_block_ref *lhs = + nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, min_cnts_pe); + nghttp3_qpack_header_block_ref *rhs = + nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, min_cnts_pe); + + return lhs->min_cnt < rhs->min_cnt; +} + +typedef struct nghttp3_blocked_streams_key { + uint64_t max_cnt; + uint64_t id; +} nghttp3_blocked_streams_key; + +static int max_cnt_greater(const nghttp3_ksl_key *lhs, + const nghttp3_ksl_key *rhs) { + const nghttp3_blocked_streams_key *a = lhs; + const nghttp3_blocked_streams_key *b = rhs; + return a->max_cnt > b->max_cnt || (a->max_cnt == b->max_cnt && a->id < b->id); +} + +int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + + rv = qpack_context_init(&encoder->ctx, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_map_init(&encoder->streams, mem); + if (rv != 0) { + goto streams_init_fail; + } + + rv = nghttp3_ksl_init(&encoder->blocked_streams, max_cnt_greater, + sizeof(nghttp3_blocked_streams_key), mem); + if (rv != 0) { + goto blocked_streams_init_fail; + } + + qpack_map_init(&encoder->dtable_map); + nghttp3_pq_init(&encoder->min_cnts, ref_min_cnt_less, mem); + + encoder->krcnt = 0; + encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; + encoder->opcode = 0; + encoder->min_dtable_update = SIZE_MAX; + encoder->last_max_dtable_update = 0; + encoder->flags = NGHTTP3_QPACK_ENCODER_FLAG_NONE; + + nghttp3_qpack_read_state_reset(&encoder->rstate); + + return 0; + +blocked_streams_init_fail: + nghttp3_map_free(&encoder->streams); +streams_init_fail: + qpack_context_free(&encoder->ctx); + + return rv; +} + +static int map_stream_free(nghttp3_map_entry *entry, void *ptr) { + const nghttp3_mem *mem = ptr; + nghttp3_qpack_stream *stream = + nghttp3_struct_of(entry, nghttp3_qpack_stream, me); + nghttp3_qpack_stream_del(stream, mem); + return 0; +} + +void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder) { + nghttp3_pq_free(&encoder->min_cnts); + nghttp3_ksl_free(&encoder->blocked_streams); + nghttp3_map_each_free(&encoder->streams, map_stream_free, + (void *)encoder->ctx.mem); + nghttp3_map_free(&encoder->streams); + qpack_context_free(&encoder->ctx); +} + +int nghttp3_qpack_encoder_set_max_dtable_size(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size) { + if (encoder->ctx.hard_max_dtable_size < max_dtable_size) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + if (encoder->ctx.max_dtable_size == max_dtable_size) { + return 0; + } + + encoder->flags |= NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; + + if (encoder->min_dtable_update > max_dtable_size) { + encoder->min_dtable_update = max_dtable_size; + encoder->ctx.max_dtable_size = max_dtable_size; + } + encoder->last_max_dtable_update = max_dtable_size; + + return 0; +} + +int nghttp3_qpack_encoder_set_hard_max_dtable_size( + nghttp3_qpack_encoder *encoder, size_t hard_max_dtable_size) { + /* TODO This is not ideal. */ + if (encoder->ctx.hard_max_dtable_size) { + return NGHTTP3_ERR_INVALID_STATE; + } + + encoder->ctx.hard_max_dtable_size = hard_max_dtable_size; + + return 0; +} + +int nghttp3_qpack_encoder_set_max_blocked(nghttp3_qpack_encoder *encoder, + size_t max_blocked) { + /* TODO This is not ideal. */ + if (encoder->ctx.max_blocked) { + return NGHTTP3_ERR_INVALID_STATE; + } + + encoder->ctx.max_blocked = max_blocked; + + return 0; +} + +uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder) { + assert(!nghttp3_pq_empty(&encoder->min_cnts)); + + return nghttp3_struct_of(nghttp3_pq_top(&encoder->min_cnts), + nghttp3_qpack_header_block_ref, min_cnts_pe) + ->min_cnt; +} + +void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder) { + nghttp3_ringbuf *dtable = &encoder->ctx.dtable; + const nghttp3_mem *mem = encoder->ctx.mem; + uint64_t min_cnt = UINT64_MAX; + size_t len; + nghttp3_qpack_entry *ent; + + if (encoder->ctx.dtable_size <= encoder->ctx.max_dtable_size) { + return; + } + + if (!nghttp3_pq_empty(&encoder->min_cnts)) { + min_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); + } + + for (; encoder->ctx.dtable_size > encoder->ctx.max_dtable_size;) { + len = nghttp3_ringbuf_len(dtable); + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); + if (ent->absidx + 1 == min_cnt) { + return; + } + + encoder->ctx.dtable_size -= + table_space(ent->nv.name->len, ent->nv.value->len); + + nghttp3_ringbuf_pop_back(dtable); + qpack_map_remove(&encoder->dtable_map, ent); + + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(mem, ent); + } +} + +/* + * qpack_encoder_add_stream_ref adds another dynamic table reference + * to a stream denoted by |stream_id|. |stream| must be NULL if no + * stream object is not found for the given stream ID. |max_cnt| and + * |min_cnt| is the maximum and minimum insert count it references + * respectively. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_encoder_add_stream_ref(nghttp3_qpack_encoder *encoder, + int64_t stream_id, + nghttp3_qpack_stream *stream, + uint64_t max_cnt, uint64_t min_cnt) { + nghttp3_qpack_header_block_ref *ref; + const nghttp3_mem *mem = encoder->ctx.mem; + uint64_t prev_max_cnt = 0; + int rv; + + if (stream == NULL) { + rv = nghttp3_qpack_stream_new(&stream, stream_id, mem); + if (rv != 0) { + assert(rv == NGHTTP3_ERR_NOMEM); + return rv; + } + rv = nghttp3_map_insert(&encoder->streams, &stream->me); + if (rv != 0) { + assert(rv == NGHTTP3_ERR_NOMEM); + nghttp3_qpack_stream_del(stream, mem); + return rv; + } + } else { + prev_max_cnt = nghttp3_qpack_stream_get_max_cnt(stream); + if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream) && + max_cnt > prev_max_cnt) { + nghttp3_qpack_encoder_unblock_stream(encoder, stream); + } + } + + rv = nghttp3_qpack_header_block_ref_new(&ref, max_cnt, min_cnt, mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_qpack_stream_add_ref(stream, ref); + if (rv != 0) { + nghttp3_qpack_header_block_ref_del(ref, mem); + return rv; + } + + if (max_cnt > prev_max_cnt && + nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) { + rv = nghttp3_qpack_encoder_block_stream(encoder, stream); + if (rv != 0) { + return rv; + } + } + + return nghttp3_pq_push(&encoder->min_cnts, &ref->min_cnts_pe); +} + +static void qpack_encoder_remove_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + size_t i, len; + nghttp3_qpack_header_block_ref *ref; + + nghttp3_map_remove(&encoder->streams, stream->me.key); + + len = nghttp3_ringbuf_len(&stream->refs); + for (i = 0; i < len; ++i) { + ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, + i); + + assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); + } +} + +/* + * reserve_buf_internal ensures that |buf| contains at least + * |extra_size| of free space. In other words, if this function + * succeeds, nghttp2_buf_left(buf) >= extra_size holds. |min_size| is + * the minimum size of buffer. The allocated buffer has at least + * |min_size| bytes. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int reserve_buf_internal(nghttp3_buf *buf, size_t extra_size, + size_t min_size, const nghttp3_mem *mem) { + size_t left = nghttp3_buf_left(buf); + size_t n = min_size, need; + + if (left >= extra_size) { + return 0; + } + + need = nghttp3_buf_cap(buf) + extra_size - left; + + for (; n < need; n *= 2) + ; + + return nghttp3_buf_reserve(buf, n, mem); +} + +static int reserve_buf_small(nghttp3_buf *buf, size_t extra_size, + const nghttp3_mem *mem) { + return reserve_buf_internal(buf, extra_size, 32, mem); +} + +static int reserve_buf(nghttp3_buf *buf, size_t extra_size, + const nghttp3_mem *mem) { + return reserve_buf_internal(buf, extra_size, 32, mem); +} + +int nghttp3_qpack_encoder_encode(nghttp3_qpack_encoder *encoder, + nghttp3_buf *pbuf, nghttp3_buf *rbuf, + nghttp3_buf *ebuf, int64_t stream_id, + const nghttp3_nv *nva, size_t nvlen) { + size_t i; + uint64_t max_cnt = 0, min_cnt = UINT64_MAX; + uint64_t base; + int rv = 0; + int allow_blocking; + int blocked_stream; + nghttp3_qpack_stream *stream; + + if (encoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + rv = nghttp3_qpack_encoder_process_dtable_update(encoder, ebuf); + if (rv != 0) { + goto fail; + } + + base = encoder->ctx.next_absidx; + + stream = nghttp3_qpack_encoder_find_stream(encoder, stream_id); + blocked_stream = + stream && nghttp3_qpack_encoder_stream_is_blocked(encoder, stream); + allow_blocking = + blocked_stream || + encoder->ctx.max_blocked > nghttp3_ksl_len(&encoder->blocked_streams); + + DEBUGF("qpack::encode: stream %ld blocked=%d allow_blocking=%d\n", stream_id, + blocked_stream, allow_blocking); + + for (i = 0; i < nvlen; ++i) { + rv = nghttp3_qpack_encoder_encode_nv(encoder, &max_cnt, &min_cnt, rbuf, + ebuf, &nva[i], base, allow_blocking); + if (rv != 0) { + goto fail; + } + } + + nghttp3_qpack_encoder_write_field_section_prefix(encoder, pbuf, max_cnt, + base); + + /* TODO If max_cnt == 0, no reference is made to dtable. */ + if (!max_cnt) { + return 0; + } + + rv = qpack_encoder_add_stream_ref(encoder, stream_id, stream, max_cnt, + min_cnt); + if (rv != 0) { + goto fail; + } + + return 0; + +fail: + encoder->ctx.bad = 1; + return rv; +} + +/* + * qpack_write_number writes variable integer to |rbuf|. |num| is an + * integer to write. |prefix| is a prefix of variable integer + * encoding. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_write_number(nghttp3_buf *rbuf, uint8_t fb, uint64_t num, + size_t prefix, const nghttp3_mem *mem) { + int rv; + size_t len = nghttp3_qpack_put_varint_len(num, prefix); + uint8_t *p; + + rv = reserve_buf(rbuf, len, mem); + if (rv != 0) { + return rv; + } + + p = rbuf->last; + + *p = fb; + p = nghttp3_qpack_put_varint(p, num, prefix); + + assert((size_t)(p - rbuf->last) == len); + + rbuf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf) { + int rv; + + nghttp3_qpack_encoder_shrink_dtable(encoder); + + if (encoder->ctx.max_dtable_size < encoder->ctx.dtable_size || + !(encoder->flags & NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP)) { + return 0; + } + + if (encoder->min_dtable_update < encoder->last_max_dtable_update) { + rv = nghttp3_qpack_encoder_write_set_dtable_cap(encoder, ebuf, + encoder->min_dtable_update); + if (rv != 0) { + return rv; + } + } + + rv = nghttp3_qpack_encoder_write_set_dtable_cap( + encoder, ebuf, encoder->last_max_dtable_update); + if (rv != 0) { + return rv; + } + + encoder->flags &= (uint8_t)~NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP; + encoder->min_dtable_update = SIZE_MAX; + encoder->ctx.max_dtable_size = encoder->last_max_dtable_update; + + return 0; +} + +int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t cap) { + DEBUGF("qpack::encode: Set Dynamic Table Capacity capacity=%zu\n", cap); + return qpack_write_number(ebuf, 0x20, cap, 5, encoder->ctx.mem); +} + +nghttp3_qpack_stream * +nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { + nghttp3_map_entry *me = + nghttp3_map_find(&encoder->streams, (uint64_t)stream_id); + return me == NULL ? NULL : nghttp3_struct_of(me, nghttp3_qpack_stream, me); +} + +int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + return stream && encoder->krcnt < nghttp3_qpack_stream_get_max_cnt(stream); +} + +/* + * qpack_encoder_decide_indexing_mode determines and returns indexing + * mode for header field |nv|. |token| is a token of header field + * name. + */ +static nghttp3_qpack_indexing_mode +qpack_encoder_decide_indexing_mode(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, int32_t token) { + if (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) { + return NGHTTP3_QPACK_INDEXING_MODE_NEVER; + } + + switch (token) { + case NGHTTP3_QPACK_TOKEN_AUTHORIZATION: + return NGHTTP3_QPACK_INDEXING_MODE_NEVER; + case NGHTTP3_QPACK_TOKEN_COOKIE: + if (nv->valuelen < 20) { + return NGHTTP3_QPACK_INDEXING_MODE_NEVER; + } + break; + case -1: + case NGHTTP3_QPACK_TOKEN__PATH: + case NGHTTP3_QPACK_TOKEN_AGE: + case NGHTTP3_QPACK_TOKEN_CONTENT_LENGTH: + case NGHTTP3_QPACK_TOKEN_ETAG: + case NGHTTP3_QPACK_TOKEN_IF_MODIFIED_SINCE: + case NGHTTP3_QPACK_TOKEN_IF_NONE_MATCH: + case NGHTTP3_QPACK_TOKEN_LOCATION: + case NGHTTP3_QPACK_TOKEN_SET_COOKIE: + return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; + case NGHTTP3_QPACK_TOKEN_HOST: + case NGHTTP3_QPACK_TOKEN_TE: + case NGHTTP3_QPACK_TOKEN__PROTOCOL: + case NGHTTP3_QPACK_TOKEN_PRIORITY: + break; + default: + if (token >= 1000) { + return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; + } + } + + if (table_space(nv->namelen, nv->valuelen) > + encoder->ctx.max_dtable_size * 3 / 4) { + return NGHTTP3_QPACK_INDEXING_MODE_LITERAL; + } + + return NGHTTP3_QPACK_INDEXING_MODE_STORE; +} + +/* + * qpack_encoder_can_index returns nonzero if an entry which occupies + * |need| bytes can be inserted into dynamic table. |min_cnt| is the + * minimum insert count which blocked stream requires. + */ +static int qpack_encoder_can_index(nghttp3_qpack_encoder *encoder, size_t need, + uint64_t min_cnt) { + size_t avail = 0; + size_t len; + uint64_t gmin_cnt; + nghttp3_qpack_entry *min_ent, *last_ent; + nghttp3_ringbuf *dtable = &encoder->ctx.dtable; + + if (encoder->ctx.max_dtable_size > encoder->ctx.dtable_size) { + avail = encoder->ctx.max_dtable_size - encoder->ctx.dtable_size; + if (need <= avail) { + return 1; + } + } + + if (!nghttp3_pq_empty(&encoder->min_cnts)) { + gmin_cnt = nghttp3_qpack_encoder_get_min_cnt(encoder); + min_cnt = nghttp3_min(min_cnt, gmin_cnt); + } + + if (min_cnt == UINT64_MAX) { + return encoder->ctx.max_dtable_size >= need; + } + + min_ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, min_cnt - 1); + + len = nghttp3_ringbuf_len(&encoder->ctx.dtable); + assert(len); + last_ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(dtable, len - 1); + + if (min_ent == last_ent) { + return 0; + } + + return avail + min_ent->sum - last_ent->sum >= need; +} + +/* + * qpack_encoder_can_index_nv returns nonzero if header field |nv| can + * be inserted into dynamic table. |min_cnt| is the minimum insert + * count which blocked stream requires. + */ +static int qpack_encoder_can_index_nv(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, uint64_t min_cnt) { + return qpack_encoder_can_index( + encoder, table_space(nv->namelen, nv->valuelen), min_cnt); +} + +/* + * qpack_encoder_can_index_duplicate returns nonzero if an entry at + * |absidx| in dynamic table can be inserted to dynamic table as + * duplicate. |min_cnt| is the minimum insert count which blocked + * stream requires. + */ +static int qpack_encoder_can_index_duplicate(nghttp3_qpack_encoder *encoder, + uint64_t absidx, + uint64_t min_cnt) { + nghttp3_qpack_entry *ent = + nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + + return qpack_encoder_can_index( + encoder, table_space(ent->nv.name->len, ent->nv.value->len), min_cnt); +} + +/* + * qpack_context_check_draining returns nonzero if an entry at + * |absidx| in dynamic table is one of draining entries. + */ +static int qpack_context_check_draining(nghttp3_qpack_context *ctx, + uint64_t absidx) { + const size_t safe = + ctx->max_dtable_size - nghttp3_min(512, ctx->max_dtable_size * 1 / 8); + nghttp3_qpack_entry *ent = nghttp3_qpack_context_dtable_get(ctx, absidx); + + return ctx->dtable_sum - ent->sum > safe; +} + +int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, + uint64_t *pmax_cnt, uint64_t *pmin_cnt, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + const nghttp3_nv *nv, uint64_t base, + int allow_blocking) { + uint32_t hash = 0; + int32_t token; + nghttp3_qpack_indexing_mode indexing_mode; + nghttp3_qpack_lookup_result sres = {-1, 0, -1}, dres = {-1, 0, -1}; + nghttp3_qpack_entry *new_ent = NULL; + int static_entry; + int just_index = 0; + int rv; + + token = qpack_lookup_token(nv->name, nv->namelen); + static_entry = token != -1 && (size_t)token < nghttp3_arraylen(token_stable); + if (static_entry) { + hash = token_stable[token].hash; + } else { + switch (token) { + case NGHTTP3_QPACK_TOKEN_HOST: + hash = 2952701295u; + break; + case NGHTTP3_QPACK_TOKEN_TE: + hash = 1011170994u; + break; + case NGHTTP3_QPACK_TOKEN__PROTOCOL: + hash = 1128642621u; + break; + case NGHTTP3_QPACK_TOKEN_PRIORITY: + hash = 2498028297u; + break; + } + } + + indexing_mode = qpack_encoder_decide_indexing_mode(encoder, nv, token); + + if (static_entry) { + sres = nghttp3_qpack_lookup_stable(nv, token, indexing_mode); + if (sres.index != -1 && sres.name_value_match) { + return nghttp3_qpack_encoder_write_static_indexed(encoder, rbuf, + (size_t)sres.index); + } + } + + if (hash && + nghttp3_map_size(&encoder->streams) < NGHTTP3_QPACK_MAX_QPACK_STREAMS) { + dres = nghttp3_qpack_encoder_lookup_dtable(encoder, nv, token, hash, + indexing_mode, encoder->krcnt, + allow_blocking); + just_index = indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_STORE && + dres.pb_index == -1; + } + + if (dres.index != -1 && dres.name_value_match) { + if (allow_blocking && + qpack_context_check_draining(&encoder->ctx, (size_t)dres.index) && + qpack_encoder_can_index_duplicate(encoder, (size_t)dres.index, + *pmin_cnt)) { + rv = nghttp3_qpack_encoder_write_duplicate_insert(encoder, ebuf, + (size_t)dres.index); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_dtable_duplicate_add(encoder, + (size_t)dres.index); + if (rv != 0) { + return rv; + } + + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + dres.index = (nghttp3_ssize)new_ent->absidx; + } + *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1)); + *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1)); + + return nghttp3_qpack_encoder_write_dynamic_indexed( + encoder, rbuf, (size_t)dres.index, base); + } + + if (sres.index != -1) { + if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) { + rv = nghttp3_qpack_encoder_write_static_insert(encoder, ebuf, + (size_t)sres.index, nv); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_dtable_static_add(encoder, (size_t)sres.index, + nv, hash); + if (rv != 0) { + return rv; + } + if (allow_blocking) { + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); + *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); + + return nghttp3_qpack_encoder_write_dynamic_indexed( + encoder, rbuf, new_ent->absidx, base); + } + } + + return nghttp3_qpack_encoder_write_static_indexed_name( + encoder, rbuf, (size_t)sres.index, nv); + } + + if (dres.index != -1) { + if (just_index && + qpack_encoder_can_index_nv( + encoder, nv, + allow_blocking ? *pmin_cnt + : nghttp3_min((size_t)dres.index + 1, *pmin_cnt))) { + rv = nghttp3_qpack_encoder_write_dynamic_insert(encoder, ebuf, + (size_t)dres.index, nv); + if (rv != 0) { + return rv; + } + + if (!allow_blocking) { + *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)dres.index + 1); + } + + rv = nghttp3_qpack_encoder_dtable_dynamic_add(encoder, (size_t)dres.index, + nv, hash); + if (rv != 0) { + return rv; + } + + if (allow_blocking) { + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); + *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); + + return nghttp3_qpack_encoder_write_dynamic_indexed( + encoder, rbuf, new_ent->absidx, base); + } + } + + *pmax_cnt = nghttp3_max(*pmax_cnt, (size_t)(dres.index + 1)); + *pmin_cnt = nghttp3_min(*pmin_cnt, (size_t)(dres.index + 1)); + + return nghttp3_qpack_encoder_write_dynamic_indexed_name( + encoder, rbuf, (size_t)dres.index, base, nv); + } + + if (just_index && qpack_encoder_can_index_nv(encoder, nv, *pmin_cnt)) { + rv = nghttp3_qpack_encoder_dtable_literal_add(encoder, nv, token, hash); + if (rv != 0) { + return rv; + } + rv = nghttp3_qpack_encoder_write_literal_insert(encoder, ebuf, nv); + if (rv != 0) { + return rv; + } + if (allow_blocking) { + new_ent = nghttp3_qpack_context_dtable_top(&encoder->ctx); + *pmax_cnt = nghttp3_max(*pmax_cnt, new_ent->absidx + 1); + *pmin_cnt = nghttp3_min(*pmin_cnt, new_ent->absidx + 1); + + return nghttp3_qpack_encoder_write_dynamic_indexed(encoder, rbuf, + new_ent->absidx, base); + } + } + + return nghttp3_qpack_encoder_write_literal(encoder, rbuf, nv); +} + +nghttp3_qpack_lookup_result +nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, + nghttp3_qpack_indexing_mode indexing_mode) { + nghttp3_qpack_lookup_result res = {(nghttp3_ssize)token_stable[token].absidx, + 0, -1}; + nghttp3_qpack_static_entry *ent; + nghttp3_qpack_static_header *hdr; + size_t i; + + assert(token >= 0); + + if (indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER) { + return res; + } + + for (i = (size_t)token; + i < nghttp3_arraylen(token_stable) && token_stable[i].token == token; + ++i) { + ent = &token_stable[i]; + hdr = &stable[ent->absidx]; + if (hdr->value.len == nv->valuelen && + memeq(hdr->value.base, nv->value, nv->valuelen)) { + res.index = (nghttp3_ssize)ent->absidx; + res.name_value_match = 1; + return res; + } + } + return res; +} + +nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( + nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, + uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt, + int allow_blocking) { + nghttp3_qpack_lookup_result res = {-1, 0, -1}; + int exact_match = 0; + nghttp3_qpack_entry *match, *pb_match; + + encoder_qpack_map_find(encoder, &exact_match, &match, &pb_match, nv, token, + hash, krcnt, allow_blocking, + indexing_mode == NGHTTP3_QPACK_INDEXING_MODE_NEVER); + if (match) { + res.index = (nghttp3_ssize)match->absidx; + res.name_value_match = exact_match; + } + if (pb_match) { + res.pb_index = (nghttp3_ssize)pb_match->absidx; + } + + return res; +} + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, + uint64_t max_cnt, uint64_t min_cnt, + const nghttp3_mem *mem) { + nghttp3_qpack_header_block_ref *ref = + nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_header_block_ref)); + + if (ref == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + ref->max_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; + ref->min_cnts_pe.index = NGHTTP3_PQ_BAD_INDEX; + ref->max_cnt = max_cnt; + ref->min_cnt = min_cnt; + + *pref = ref; + + return 0; +} + +void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, + const nghttp3_mem *mem) { + nghttp3_mem_free(mem, ref); +} + +static int ref_max_cnt_greater(const nghttp3_pq_entry *lhsx, + const nghttp3_pq_entry *rhsx) { + const nghttp3_qpack_header_block_ref *lhs = + nghttp3_struct_of(lhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); + const nghttp3_qpack_header_block_ref *rhs = + nghttp3_struct_of(rhsx, nghttp3_qpack_header_block_ref, max_cnts_pe); + + return lhs->max_cnt > rhs->max_cnt; +} + +int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, + const nghttp3_mem *mem) { + int rv; + nghttp3_qpack_stream *stream; + + stream = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream)); + if (stream == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_ringbuf_init(&stream->refs, 4, + sizeof(nghttp3_qpack_header_block_ref *), mem); + if (rv != 0) { + nghttp3_mem_free(mem, stream); + return rv; + } + + nghttp3_pq_init(&stream->max_cnts, ref_max_cnt_greater, mem); + + stream->me.key = (uint64_t)stream_id; + + *pstream = stream; + + return 0; +} + +void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, + const nghttp3_mem *mem) { + nghttp3_qpack_header_block_ref *ref; + size_t i, len; + + if (stream == NULL) { + return; + } + + nghttp3_pq_free(&stream->max_cnts); + + len = nghttp3_ringbuf_len(&stream->refs); + for (i = 0; i < len; ++i) { + ref = *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, + i); + nghttp3_qpack_header_block_ref_del(ref, mem); + } + + nghttp3_ringbuf_free(&stream->refs); + + nghttp3_mem_free(mem, stream); +} + +uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream) { + nghttp3_qpack_header_block_ref *ref; + + if (nghttp3_pq_empty(&stream->max_cnts)) { + return 0; + } + + ref = nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe); + return ref->max_cnt; +} + +int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, + nghttp3_qpack_header_block_ref *ref) { + nghttp3_qpack_header_block_ref **dest; + int rv; + + if (nghttp3_ringbuf_full(&stream->refs)) { + rv = nghttp3_ringbuf_reserve(&stream->refs, + nghttp3_ringbuf_len(&stream->refs) * 2); + if (rv != 0) { + return rv; + } + } + + dest = nghttp3_ringbuf_push_back(&stream->refs); + *dest = ref; + + return nghttp3_pq_push(&stream->max_cnts, &ref->max_cnts_pe); +} + +void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream) { + nghttp3_qpack_header_block_ref *ref; + + assert(nghttp3_ringbuf_len(&stream->refs)); + + ref = + *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); + + assert(ref->max_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&stream->max_cnts, &ref->max_cnts_pe); + + nghttp3_ringbuf_pop_front(&stream->refs); +} + +int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + uint64_t absidx) { + DEBUGF("qpack::encode: Indexed Field Line (static) absidx=%" PRIu64 "\n", + absidx); + return qpack_write_number(rbuf, 0xc0, absidx, 6, encoder->ctx.mem); +} + +int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + uint64_t absidx, + uint64_t base) { + DEBUGF("qpack::encode: Indexed Field Line (dynamic) absidx=%" PRIu64 + " base=%" PRIu64 "\n", + absidx, base); + + if (absidx < base) { + return qpack_write_number(rbuf, 0x80, base - absidx - 1, 6, + encoder->ctx.mem); + } + + return qpack_write_number(rbuf, 0x10, absidx - base, 4, encoder->ctx.mem); +} + +/* + * qpack_encoder_write_indexed_name writes generic indexed name. |fb| + * is the first byte. |nameidx| is an index of referenced name. + * |prefix| is a prefix of variable integer encoding. |nv| is a + * header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_encoder_write_indexed_name(nghttp3_qpack_encoder *encoder, + nghttp3_buf *buf, uint8_t fb, + uint64_t nameidx, size_t prefix, + const nghttp3_nv *nv) { + int rv; + size_t len = nghttp3_qpack_put_varint_len(nameidx, prefix); + uint8_t *p; + size_t hlen; + int h = 0; + + hlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); + if (hlen < nv->valuelen) { + h = 1; + len += nghttp3_qpack_put_varint_len(hlen, 7) + hlen; + } else { + len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; + } + + rv = reserve_buf(buf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = buf->last; + + *p = fb; + p = nghttp3_qpack_put_varint(p, nameidx, prefix); + + if (h) { + *p = 0x80; + p = nghttp3_qpack_put_varint(p, hlen, 7); + p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); + } else { + *p = 0; + p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); + if (nv->valuelen) { + p = nghttp3_cpymem(p, nv->value, nv->valuelen); + } + } + + assert((size_t)(p - buf->last) == len); + + buf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_write_static_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, + const nghttp3_nv *nv) { + uint8_t fb = + (uint8_t)(0x50 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); + + DEBUGF("qpack::encode: Literal Field Line With Name Reference (static) " + "absidx=%" PRIu64 " never=%d\n", + absidx, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); + return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx, 4, nv); +} + +int nghttp3_qpack_encoder_write_dynamic_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, + uint64_t base, const nghttp3_nv *nv) { + uint8_t fb; + + DEBUGF("qpack::encode: Literal Field Line With Name Reference (dynamic) " + "absidx=%" PRIu64 " base=%" PRIu64 " never=%d\n", + absidx, base, (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) != 0); + + if (absidx < base) { + fb = (uint8_t)(0x40 | + ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x20 : 0)); + return qpack_encoder_write_indexed_name(encoder, rbuf, fb, + base - absidx - 1, 4, nv); + } + + fb = (nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x08 : 0; + return qpack_encoder_write_indexed_name(encoder, rbuf, fb, absidx - base, 3, + nv); +} + +/* + * qpack_encoder_write_literal writes generic literal header field + * representation. |fb| is a first byte. |prefix| is a prefix of + * variable integer encoding for name length. |nv| is a header field + * to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +static int qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, + nghttp3_buf *buf, uint8_t fb, + size_t prefix, const nghttp3_nv *nv) { + int rv; + size_t len; + uint8_t *p; + size_t nhlen, vhlen; + int nh = 0, vh = 0; + + nhlen = nghttp3_qpack_huffman_encode_count(nv->name, nv->namelen); + if (nhlen < nv->namelen) { + nh = 1; + len = nghttp3_qpack_put_varint_len(nhlen, prefix) + nhlen; + } else { + len = nghttp3_qpack_put_varint_len(nv->namelen, prefix) + nv->namelen; + } + + vhlen = nghttp3_qpack_huffman_encode_count(nv->value, nv->valuelen); + if (vhlen < nv->valuelen) { + vh = 1; + len += nghttp3_qpack_put_varint_len(vhlen, 7) + vhlen; + } else { + len += nghttp3_qpack_put_varint_len(nv->valuelen, 7) + nv->valuelen; + } + + rv = reserve_buf(buf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = buf->last; + + *p = fb; + if (nh) { + *p |= (uint8_t)(1 << prefix); + p = nghttp3_qpack_put_varint(p, nhlen, prefix); + p = nghttp3_qpack_huffman_encode(p, nv->name, nv->namelen); + } else { + p = nghttp3_qpack_put_varint(p, nv->namelen, prefix); + if (nv->namelen) { + p = nghttp3_cpymem(p, nv->name, nv->namelen); + } + } + + *p = 0; + + if (vh) { + *p |= 0x80; + p = nghttp3_qpack_put_varint(p, vhlen, 7); + p = nghttp3_qpack_huffman_encode(p, nv->value, nv->valuelen); + } else { + p = nghttp3_qpack_put_varint(p, nv->valuelen, 7); + if (nv->valuelen) { + p = nghttp3_cpymem(p, nv->value, nv->valuelen); + } + } + + assert((size_t)(p - buf->last) == len); + + buf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + const nghttp3_nv *nv) { + uint8_t fb = + (uint8_t)(0x20 | ((nv->flags & NGHTTP3_NV_FLAG_NEVER_INDEX) ? 0x10 : 0)); + + DEBUGF("qpack::encode: Literal Field Line With Literal Name\n"); + return qpack_encoder_write_literal(encoder, rbuf, fb, 3, nv); +} + +int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + uint64_t absidx, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Name Reference (static) absidx=%" PRIu64 + "\n", + absidx); + return qpack_encoder_write_indexed_name(encoder, ebuf, 0xc0, absidx, 6, nv); +} + +int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + uint64_t absidx, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Name Reference (dynamic) absidx=%" PRIu64 + "\n", + absidx); + return qpack_encoder_write_indexed_name( + encoder, ebuf, 0x80, encoder->ctx.next_absidx - absidx - 1, 6, nv); +} + +int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + uint64_t absidx) { + uint64_t idx = encoder->ctx.next_absidx - absidx - 1; + size_t len = nghttp3_qpack_put_varint_len(idx, 5); + uint8_t *p; + int rv; + + DEBUGF("qpack::encode: Insert duplicate absidx=%" PRIu64 "\n", absidx); + + rv = reserve_buf(ebuf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = ebuf->last; + + *p = 0; + p = nghttp3_qpack_put_varint(p, idx, 5); + + assert((size_t)(p - ebuf->last) == len); + + ebuf->last = p; + + return 0; +} + +int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + const nghttp3_nv *nv) { + DEBUGF("qpack::encode: Insert With Literal Name\n"); + return qpack_encoder_write_literal(encoder, ebuf, 0x40, 5, nv); +} + +int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, + nghttp3_qpack_nv *qnv, + nghttp3_qpack_map *dtable_map, + uint32_t hash) { + nghttp3_qpack_entry *new_ent, **p, *ent; + const nghttp3_mem *mem = ctx->mem; + size_t space; + size_t i; + int rv; + + space = table_space(qnv->name->len, qnv->value->len); + + assert(space <= ctx->max_dtable_size); + + while (ctx->dtable_size + space > ctx->max_dtable_size) { + i = nghttp3_ringbuf_len(&ctx->dtable); + assert(i); + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); + + ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); + + nghttp3_ringbuf_pop_back(&ctx->dtable); + if (dtable_map) { + qpack_map_remove(dtable_map, ent); + } + + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(mem, ent); + } + + new_ent = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_entry)); + if (new_ent == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_qpack_entry_init(new_ent, qnv, ctx->dtable_sum, ctx->next_absidx++, + hash); + + if (nghttp3_ringbuf_full(&ctx->dtable)) { + rv = nghttp3_ringbuf_reserve(&ctx->dtable, + nghttp3_ringbuf_len(&ctx->dtable) * 2); + if (rv != 0) { + goto fail; + } + } + + p = nghttp3_ringbuf_push_front(&ctx->dtable); + *p = new_ent; + + if (dtable_map) { + qpack_map_insert(dtable_map, new_ent); + } + + ctx->dtable_size += space; + ctx->dtable_sum += space; + + return 0; + +fail: + nghttp3_qpack_entry_free(new_ent); + nghttp3_mem_free(mem, new_ent); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, + uint64_t absidx, + const nghttp3_nv *nv, + uint32_t hash) { + const nghttp3_qpack_static_header *shd; + nghttp3_qpack_nv qnv; + const nghttp3_mem *mem = encoder->ctx.mem; + int rv; + + rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); + if (rv != 0) { + return rv; + } + + assert(nghttp3_arraylen(stable) > absidx); + + shd = &stable[absidx]; + + qnv.name = (nghttp3_rcbuf *)&shd->name; + qnv.token = shd->token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, hash); + + nghttp3_rcbuf_decref(qnv.value); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, + uint64_t absidx, + const nghttp3_nv *nv, + uint32_t hash) { + nghttp3_qpack_nv qnv; + nghttp3_qpack_entry *ent; + const nghttp3_mem *mem = encoder->ctx.mem; + int rv; + + rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); + if (rv != 0) { + return rv; + } + + ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + + qnv.name = ent->nv.name; + qnv.token = ent->nv.token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + nghttp3_rcbuf_incref(qnv.name); + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, hash); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, + uint64_t absidx) { + nghttp3_qpack_nv qnv; + nghttp3_qpack_entry *ent; + int rv; + + ent = nghttp3_qpack_context_dtable_get(&encoder->ctx, absidx); + + qnv = ent->nv; + nghttp3_rcbuf_incref(qnv.name); + nghttp3_rcbuf_incref(qnv.value); + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, ent->hash); + + nghttp3_rcbuf_decref(qnv.name); + nghttp3_rcbuf_decref(qnv.value); + + return rv; +} + +int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, + int32_t token, uint32_t hash) { + nghttp3_qpack_nv qnv; + const nghttp3_mem *mem = encoder->ctx.mem; + int rv; + + rv = nghttp3_rcbuf_new2(&qnv.name, nv->name, nv->namelen, mem); + if (rv != 0) { + return rv; + } + + rv = nghttp3_rcbuf_new2(&qnv.value, nv->value, nv->valuelen, mem); + if (rv != 0) { + nghttp3_rcbuf_decref(qnv.name); + return rv; + } + + qnv.token = token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&encoder->ctx, &qnv, + &encoder->dtable_map, hash); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx) { + size_t relidx; + + assert(ctx->next_absidx > absidx); + assert(ctx->next_absidx - absidx - 1 < nghttp3_ringbuf_len(&ctx->dtable)); + + relidx = (size_t)(ctx->next_absidx - absidx - 1); + + return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, relidx); +} + +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx) { + assert(nghttp3_ringbuf_len(&ctx->dtable)); + return *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, 0); +} + +void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, + size_t sum, uint64_t absidx, uint32_t hash) { + ent->nv = *qnv; + ent->map_next = NULL; + ent->sum = sum; + ent->absidx = absidx; + ent->hash = hash; + + nghttp3_rcbuf_incref(ent->nv.name); + nghttp3_rcbuf_incref(ent->nv.value); +} + +void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent) { + nghttp3_rcbuf_decref(ent->nv.value); + nghttp3_rcbuf_decref(ent->nv.name); +} + +int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + nghttp3_blocked_streams_key bsk = { + nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + stream->me.key}; + + return nghttp3_ksl_insert(&encoder->blocked_streams, NULL, &bsk, stream); +} + +void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream) { + nghttp3_blocked_streams_key bsk = { + nghttp3_struct_of(nghttp3_pq_top(&stream->max_cnts), + nghttp3_qpack_header_block_ref, max_cnts_pe) + ->max_cnt, + stream->me.key}; + nghttp3_ksl_it it; + + /* This is purely debugging purpose only */ + it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); + + assert(!nghttp3_ksl_it_end(&it)); + assert(nghttp3_ksl_it_get(&it) == stream); + + nghttp3_ksl_remove(&encoder->blocked_streams, NULL, &bsk); +} + +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, + uint64_t max_cnt) { + nghttp3_blocked_streams_key bsk = {max_cnt, 0}; + nghttp3_ksl_it it; + + it = nghttp3_ksl_lower_bound(&encoder->blocked_streams, &bsk); + + for (; !nghttp3_ksl_it_end(&it);) { + bsk = *(nghttp3_blocked_streams_key *)nghttp3_ksl_it_key(&it); + nghttp3_ksl_remove(&encoder->blocked_streams, &it, &bsk); + } +} + +void nghttp3_qpack_encoder_ack_header(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { + nghttp3_qpack_stream *stream = + nghttp3_qpack_encoder_find_stream(encoder, stream_id); + const nghttp3_mem *mem = encoder->ctx.mem; + nghttp3_qpack_header_block_ref *ref; + + if (stream == NULL) { + /* This might be NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR, but we + don't create stream which does not use dynamic table. */ + return; + } + + assert(nghttp3_ringbuf_len(&stream->refs)); + + ref = + *(nghttp3_qpack_header_block_ref **)nghttp3_ringbuf_get(&stream->refs, 0); + + DEBUGF("qpack::encoder: Header acknowledgement stream=%ld ricnt=%" PRIu64 + " krcnt=%" PRIu64 "\n", + stream_id, ref->max_cnt, encoder->krcnt); + + if (encoder->krcnt < ref->max_cnt) { + encoder->krcnt = ref->max_cnt; + + nghttp3_qpack_encoder_unblock(encoder, ref->max_cnt); + } + + nghttp3_qpack_stream_pop_ref(stream); + + assert(ref->min_cnts_pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(&encoder->min_cnts, &ref->min_cnts_pe); + + nghttp3_qpack_header_block_ref_del(ref, mem); + + if (nghttp3_ringbuf_len(&stream->refs)) { + return; + } + + qpack_encoder_remove_stream(encoder, stream); + + nghttp3_qpack_stream_del(stream, mem); +} + +int nghttp3_qpack_encoder_add_insert_count(nghttp3_qpack_encoder *encoder, + uint64_t n) { + if (n == 0 || encoder->ctx.next_absidx - encoder->krcnt < n) { + return NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; + } + encoder->krcnt += n; + + nghttp3_qpack_encoder_unblock(encoder, encoder->krcnt); + + return 0; +} + +void nghttp3_qpack_encoder_ack_everything(nghttp3_qpack_encoder *encoder) { + encoder->krcnt = encoder->ctx.next_absidx; + + nghttp3_ksl_clear(&encoder->blocked_streams); + nghttp3_pq_clear(&encoder->min_cnts); + nghttp3_map_each_free(&encoder->streams, map_stream_free, + (void *)encoder->ctx.mem); +} + +void nghttp3_qpack_encoder_cancel_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id) { + nghttp3_qpack_stream *stream = + nghttp3_qpack_encoder_find_stream(encoder, stream_id); + const nghttp3_mem *mem = encoder->ctx.mem; + + if (stream == NULL) { + return; + } + + if (nghttp3_qpack_encoder_stream_is_blocked(encoder, stream)) { + nghttp3_qpack_encoder_unblock_stream(encoder, stream); + } + + qpack_encoder_remove_stream(encoder, stream); + + nghttp3_qpack_stream_del(stream, mem); +} + +size_t nghttp3_qpack_encoder_get_num_blocked(nghttp3_qpack_encoder *encoder) { + return nghttp3_ksl_len(&encoder->blocked_streams); +} + +int nghttp3_qpack_encoder_write_field_section_prefix( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, + uint64_t base) { + size_t max_ents = + encoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + uint64_t encricnt = ricnt == 0 ? 0 : (ricnt % (2 * max_ents)) + 1; + int sign = base < ricnt; + uint64_t delta_base = sign ? ricnt - base - 1 : base - ricnt; + size_t len = nghttp3_qpack_put_varint_len(encricnt, 8) + + nghttp3_qpack_put_varint_len(delta_base, 7); + uint8_t *p; + int rv; + + DEBUGF("qpack::encode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n", + ricnt, base, encoder->ctx.next_absidx); + + rv = reserve_buf(pbuf, len, encoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = pbuf->last; + + p = nghttp3_qpack_put_varint(p, encricnt, 8); + if (sign) { + *p = 0x80; + } else { + *p = 0; + } + p = nghttp3_qpack_put_varint(p, delta_base, 7); + + assert((size_t)(p - pbuf->last) == len); + + pbuf->last = p; + + return 0; +} + +/* + * qpack_read_varint reads |rstate->prefix| prefixed integer stored + * from |begin|. The |end| represents the 1 beyond the last of the + * valid contiguous memory region from |begin|. The decoded integer + * must be less than or equal to NGHTTP3_QPACK_INT_MAX. + * + * If the |rstate->left| is nonzero, it is used as an initial value, + * and this function assumes the |begin| starts with intermediate + * data. |rstate->shift| is used as initial integer shift. + * + * If an entire integer is decoded successfully, the |*fin| is set to + * nonzero. + * + * This function stores the decoded integer in |rstate->left| if it + * succeeds, including partial decoding (in this case, number of shift + * to make in the next call will be stored in |rstate->shift|) and + * returns number of bytes processed, or returns negative error code + * NGHTTP3_ERR_QPACK_FATAL, indicating decoding error. + */ +static nghttp3_ssize qpack_read_varint(int *fin, + nghttp3_qpack_read_state *rstate, + const uint8_t *begin, + const uint8_t *end) { + uint64_t k = (uint8_t)((1 << rstate->prefix) - 1); + uint64_t n = rstate->left; + uint64_t add; + const uint8_t *p = begin; + size_t shift = rstate->shift; + + rstate->shift = 0; + *fin = 0; + + if (n == 0) { + if (((*p) & k) != k) { + rstate->left = (*p) & k; + *fin = 1; + return 1; + } + + n = k; + + if (++p == end) { + rstate->left = n; + return (nghttp3_ssize)(p - begin); + } + } + + for (; p != end; ++p, shift += 7) { + add = (*p) & 0x7f; + + if (shift > 62) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + if ((NGHTTP3_QPACK_INT_MAX >> shift) < add) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + add <<= shift; + + if (NGHTTP3_QPACK_INT_MAX - add < n) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + n += add; + + if (((*p) & (1 << 7)) == 0) { + break; + } + } + + rstate->shift = shift; + + if (p == end) { + rstate->left = n; + return (nghttp3_ssize)(p - begin); + } + + rstate->left = n; + *fin = 1; + return (nghttp3_ssize)(p + 1 - begin); +} + +nghttp3_ssize nghttp3_qpack_encoder_read_decoder(nghttp3_qpack_encoder *encoder, + const uint8_t *src, + size_t srclen) { + const uint8_t *p = src, *end; + int rv; + nghttp3_ssize nread; + int rfin; + + if (encoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + if (srclen == 0) { + return 0; + } + + end = src + srclen; + + for (; p != end;) { + switch (encoder->state) { + case NGHTTP3_QPACK_DS_STATE_OPCODE: + if ((*p) & 0x80) { + DEBUGF("qpack::encode: OPCODE_SECTION_ACK\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK; + encoder->rstate.prefix = 7; + } else if ((*p) & 0x40) { + DEBUGF("qpack::encode: OPCODE_STREAM_CANCEL\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL; + encoder->rstate.prefix = 6; + } else { + DEBUGF("qpack::encode: OPCODE_ICNT_INCREMENT\n"); + encoder->opcode = NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT; + encoder->rstate.prefix = 6; + } + encoder->state = NGHTTP3_QPACK_DS_STATE_READ_NUMBER; + /* fall through */ + case NGHTTP3_QPACK_DS_STATE_READ_NUMBER: + nread = qpack_read_varint(&rfin, &encoder->rstate, p, end); + if (nread < 0) { + assert(nread == NGHTTP3_ERR_QPACK_FATAL); + rv = NGHTTP3_ERR_QPACK_DECODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + switch (encoder->opcode) { + case NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT: + rv = nghttp3_qpack_encoder_add_insert_count(encoder, + encoder->rstate.left); + if (rv != 0) { + goto fail; + } + break; + case NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK: + nghttp3_qpack_encoder_ack_header(encoder, + (int64_t)encoder->rstate.left); + break; + case NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL: + nghttp3_qpack_encoder_cancel_stream(encoder, + (int64_t)encoder->rstate.left); + break; + default: + /* unreachable */ + assert(0); + break; + } + + encoder->state = NGHTTP3_QPACK_DS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&encoder->rstate); + break; + default: + /* unreachable */ + assert(0); + break; + } + } + + return p - src; + +fail: + encoder->ctx.bad = 1; + return rv; +} + +size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + size_t len = 0; + + if (n < k) { + return 1; + } + + n -= k; + ++len; + + for (; n >= 128; n >>= 7, ++len) + ; + + return len + 1; +} + +uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix) { + size_t k = (size_t)((1 << prefix) - 1); + + *buf = (uint8_t)(*buf & ~k); + + if (n < k) { + *buf = (uint8_t)(*buf | n); + return buf + 1; + } + + *buf = (uint8_t)(*buf | k); + ++buf; + + n -= k; + + for (; n >= 128; n >>= 7) { + *buf++ = (uint8_t)((1 << 7) | (n & 0x7f)); + } + + *buf++ = (uint8_t)n; + + return buf; +} + +void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate) { + nghttp3_rcbuf_decref(rstate->value); + nghttp3_rcbuf_decref(rstate->name); +} + +void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate) { + rstate->name = NULL; + rstate->value = NULL; + nghttp3_buf_init(&rstate->namebuf); + nghttp3_buf_init(&rstate->valuebuf); + rstate->left = 0; + rstate->prefix = 0; + rstate->shift = 0; + rstate->absidx = 0; + rstate->never = 0; + rstate->dynamic = 0; + rstate->huffman_encoded = 0; +} + +int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + + rv = qpack_context_init(&decoder->ctx, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + decoder->opcode = 0; + decoder->written_icnt = 0; + decoder->max_concurrent_streams = 0; + + nghttp3_qpack_read_state_reset(&decoder->rstate); + nghttp3_buf_init(&decoder->dbuf); + + return 0; +} + +void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder) { + nghttp3_buf_free(&decoder->dbuf, decoder->ctx.mem); + nghttp3_qpack_read_state_free(&decoder->rstate); + qpack_context_free(&decoder->ctx); +} + +/* + * qpack_read_huffman_string decodes huffman string in buffer [begin, + * end) and writes the decoded string to |dest|. This function + * assumes the buffer pointed by |dest| has enough space. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_FATAL + * Could not decode huffman string. + */ +static nghttp3_ssize qpack_read_huffman_string(nghttp3_qpack_read_state *rstate, + nghttp3_buf *dest, + const uint8_t *begin, + const uint8_t *end) { + nghttp3_ssize nwrite; + size_t len = (size_t)(end - begin); + int fin = 0; + + if (len >= rstate->left) { + len = (size_t)rstate->left; + fin = 1; + } + + nwrite = nghttp3_qpack_huffman_decode(&rstate->huffman_ctx, dest->last, begin, + len, fin); + if (nwrite < 0) { + return nwrite; + } + + if (nghttp3_qpack_huffman_decode_failure_state(&rstate->huffman_ctx)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + dest->last += nwrite; + rstate->left -= len; + return (nghttp3_ssize)len; +} + +static nghttp3_ssize qpack_read_string(nghttp3_qpack_read_state *rstate, + nghttp3_buf *dest, const uint8_t *begin, + const uint8_t *end) { + size_t len = (size_t)(end - begin); + size_t n = (size_t)nghttp3_min((uint64_t)len, rstate->left); + + dest->last = nghttp3_cpymem(dest->last, begin, n); + + rstate->left -= n; + return (nghttp3_ssize)n; +} + +/* + * qpack_decoder_validate_index checks rstate->absidx is acceptable. + * + * It returns 0 if it suceeds, or one of the following negative error + * codes: + * + * NGHTTP3_ERR_QPACK_FATAL + * rstate->absidx is invalid. + */ +static int qpack_decoder_validate_index(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_read_state *rstate) { + if (rstate->dynamic) { + return rstate->absidx < decoder->ctx.next_absidx && + decoder->ctx.next_absidx - rstate->absidx - 1 < + nghttp3_ringbuf_len(&decoder->ctx.dtable) + ? 0 + : NGHTTP3_ERR_QPACK_FATAL; + } + return rstate->absidx < nghttp3_arraylen(stable) ? 0 + : NGHTTP3_ERR_QPACK_FATAL; +} + +static void qpack_read_state_check_huffman(nghttp3_qpack_read_state *rstate, + const uint8_t b) { + rstate->huffman_encoded = (b & (1 << rstate->prefix)) != 0; +} + +static void qpack_read_state_terminate_name(nghttp3_qpack_read_state *rstate) { + *rstate->namebuf.last = '\0'; + rstate->name->len = nghttp3_buf_len(&rstate->namebuf); +} + +static void qpack_read_state_terminate_value(nghttp3_qpack_read_state *rstate) { + *rstate->valuebuf.last = '\0'; + rstate->value->len = nghttp3_buf_len(&rstate->valuebuf); +} + +nghttp3_ssize nghttp3_qpack_decoder_read_encoder(nghttp3_qpack_decoder *decoder, + const uint8_t *src, + size_t srclen) { + const uint8_t *p = src, *end; + int rv; + int busy = 0; + const nghttp3_mem *mem = decoder->ctx.mem; + nghttp3_ssize nread; + int rfin; + + if (decoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + if (srclen == 0) { + return 0; + } + + end = src + srclen; + + for (; p != end || busy;) { + busy = 0; + switch (decoder->state) { + case NGHTTP3_QPACK_ES_STATE_OPCODE: + if ((*p) & 0x80) { + DEBUGF("qpack::decode: OPCODE_INSERT_INDEXED\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED; + decoder->rstate.dynamic = !((*p) & 0x40); + decoder->rstate.prefix = 6; + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; + } else if ((*p) & 0x40) { + DEBUGF("qpack::decode: OPCODE_INSERT\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_INSERT; + decoder->rstate.dynamic = 0; + decoder->rstate.prefix = 5; + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN; + } else if ((*p) & 0x20) { + DEBUGF("qpack::decode: OPCODE_SET_DTABLE_TABLE_CAP\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP; + decoder->rstate.prefix = 5; + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; + } else if (!((*p) & 0x20)) { + DEBUGF("qpack::decode: OPCODE_DUPLICATE\n"); + decoder->opcode = NGHTTP3_QPACK_ES_OPCODE_DUPLICATE; + decoder->rstate.dynamic = 1; + decoder->rstate.prefix = 5; + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_INDEX; + } else { + DEBUGF("qpack::decode: unknown opcode %02x\n", *p); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + break; + case NGHTTP3_QPACK_ES_STATE_READ_INDEX: + nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP) { + if (decoder->rstate.left > decoder->ctx.hard_max_dtable_size) { + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + DEBUGF("qpack::decode: Set dtable capacity to %" PRIu64 "\n", + decoder->rstate.left); + nghttp3_qpack_decoder_set_dtable_cap(decoder, + (size_t)decoder->rstate.left); + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + } + + rv = nghttp3_qpack_decoder_rel2abs(decoder, &decoder->rstate); + if (rv < 0) { + goto fail; + } + + if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_DUPLICATE) { + rv = nghttp3_qpack_decoder_dtable_duplicate_add(decoder); + if (rv != 0) { + goto fail; + } + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + } + + if (decoder->opcode == NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED) { + decoder->rstate.prefix = 7; + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + break; + } + + /* Unreachable */ + assert(0); + break; + case NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN: + qpack_read_state_check_huffman(&decoder->rstate, *p); + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAMELEN; + decoder->rstate.left = 0; + decoder->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_ES_STATE_READ_NAMELEN: + nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + if (decoder->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { + rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; + goto fail; + } + + if (decoder->rstate.huffman_encoded) { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&decoder->rstate.name, + (size_t)decoder->rstate.left * 2 + 1, mem); + } else { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_NAME; + rv = nghttp3_rcbuf_new(&decoder->rstate.name, + (size_t)decoder->rstate.left + 1, mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&decoder->rstate.namebuf, + decoder->rstate.name->base, + decoder->rstate.name->len); + break; + case NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN: + nread = qpack_read_huffman_string(&decoder->rstate, + &decoder->rstate.namebuf, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_name(&decoder->rstate); + + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + decoder->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_ES_STATE_READ_NAME: + nread = + qpack_read_string(&decoder->rstate, &decoder->rstate.namebuf, p, end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_name(&decoder->rstate); + + decoder->state = NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN; + decoder->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN: + qpack_read_state_check_huffman(&decoder->rstate, *p); + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUELEN; + decoder->rstate.left = 0; + decoder->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_ES_STATE_READ_VALUELEN: + nread = qpack_read_varint(&rfin, &decoder->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (!rfin) { + return p - src; + } + + if (decoder->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { + rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; + goto fail; + } + + if (decoder->rstate.huffman_encoded) { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&decoder->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&decoder->rstate.value, + (size_t)decoder->rstate.left * 2 + 1, mem); + } else { + decoder->state = NGHTTP3_QPACK_ES_STATE_READ_VALUE; + rv = nghttp3_rcbuf_new(&decoder->rstate.value, + (size_t)decoder->rstate.left + 1, mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&decoder->rstate.valuebuf, + decoder->rstate.value->base, + decoder->rstate.value->len); + + /* value might be 0 length */ + busy = 1; + break; + case NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN: + nread = qpack_read_huffman_string(&decoder->rstate, + &decoder->rstate.valuebuf, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_value(&decoder->rstate); + + switch (decoder->opcode) { + case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: + rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); + break; + case NGHTTP3_QPACK_ES_OPCODE_INSERT: + rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); + break; + default: + /* Unreachable */ + assert(0); + } + if (rv != 0) { + goto fail; + } + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + case NGHTTP3_QPACK_ES_STATE_READ_VALUE: + nread = qpack_read_string(&decoder->rstate, &decoder->rstate.valuebuf, p, + end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (decoder->rstate.left) { + return p - src; + } + + qpack_read_state_terminate_value(&decoder->rstate); + + switch (decoder->opcode) { + case NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED: + rv = nghttp3_qpack_decoder_dtable_indexed_add(decoder); + break; + case NGHTTP3_QPACK_ES_OPCODE_INSERT: + rv = nghttp3_qpack_decoder_dtable_literal_add(decoder); + break; + default: + /* Unreachable */ + assert(0); + } + if (rv != 0) { + goto fail; + } + + decoder->state = NGHTTP3_QPACK_ES_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&decoder->rstate); + break; + } + } + + return p - src; + +fail: + decoder->ctx.bad = 1; + return rv; +} + +void nghttp3_qpack_decoder_set_dtable_cap(nghttp3_qpack_decoder *decoder, + size_t cap) { + nghttp3_qpack_entry *ent; + size_t i; + nghttp3_qpack_context *ctx = &decoder->ctx; + const nghttp3_mem *mem = ctx->mem; + + ctx->max_dtable_size = cap; + + while (ctx->dtable_size > cap) { + i = nghttp3_ringbuf_len(&ctx->dtable); + assert(i); + ent = *(nghttp3_qpack_entry **)nghttp3_ringbuf_get(&ctx->dtable, i - 1); + + ctx->dtable_size -= table_space(ent->nv.name->len, ent->nv.value->len); + + nghttp3_ringbuf_pop_back(&ctx->dtable); + nghttp3_qpack_entry_free(ent); + nghttp3_mem_free(mem, ent); + } +} + +int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder) { + DEBUGF("qpack::decode: Insert With Name Reference (%s) absidx=%" PRIu64 ": " + "value=%*s\n", + decoder->rstate.dynamic ? "dynamic" : "static", decoder->rstate.absidx, + (int)decoder->rstate.value->len, decoder->rstate.value->base); + + if (decoder->rstate.dynamic) { + return nghttp3_qpack_decoder_dtable_dynamic_add(decoder); + } + + return nghttp3_qpack_decoder_dtable_static_add(decoder); +} + +int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder) { + nghttp3_qpack_nv qnv; + int rv; + const nghttp3_qpack_static_header *shd; + + shd = &stable[decoder->rstate.absidx]; + + if (table_space(shd->name.len, decoder->rstate.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv.name = (nghttp3_rcbuf *)&shd->name; + qnv.value = decoder->rstate.value; + qnv.token = shd->token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + + return rv; +} + +int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder) { + nghttp3_qpack_nv qnv; + int rv; + nghttp3_qpack_entry *ent; + + ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); + + if (table_space(ent->nv.name->len, decoder->rstate.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv.name = ent->nv.name; + qnv.value = decoder->rstate.value; + qnv.token = ent->nv.token; + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + nghttp3_rcbuf_incref(qnv.name); + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder) { + int rv; + nghttp3_qpack_entry *ent; + nghttp3_qpack_nv qnv; + + DEBUGF("qpack::decode: Insert duplicate absidx=%" PRIu64 "\n", + decoder->rstate.absidx); + + ent = nghttp3_qpack_context_dtable_get(&decoder->ctx, decoder->rstate.absidx); + + if (table_space(ent->nv.name->len, ent->nv.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv = ent->nv; + nghttp3_rcbuf_incref(qnv.name); + nghttp3_rcbuf_incref(qnv.value); + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder) { + nghttp3_qpack_nv qnv; + int rv; + + DEBUGF("qpack::decode: Insert With Literal Name: name=%*s value=%*s\n", + (int)decoder->rstate.name->len, decoder->rstate.name->base, + (int)decoder->rstate.value->len, decoder->rstate.value->base); + + if (table_space(decoder->rstate.name->len, decoder->rstate.value->len) > + decoder->ctx.max_dtable_size) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + + qnv.name = decoder->rstate.name; + qnv.value = decoder->rstate.value; + qnv.token = qpack_lookup_token(qnv.name->base, qnv.name->len); + qnv.flags = NGHTTP3_NV_FLAG_NONE; + + rv = nghttp3_qpack_context_dtable_add(&decoder->ctx, &qnv, NULL, 0); + + nghttp3_rcbuf_decref(qnv.value); + nghttp3_rcbuf_decref(qnv.name); + + return rv; +} + +void nghttp3_qpack_decoder_set_max_concurrent_streams( + nghttp3_qpack_decoder *decoder, size_t max_concurrent_streams) { + decoder->max_concurrent_streams = + nghttp3_max(decoder->max_concurrent_streams, max_concurrent_streams); +} + +void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, + int64_t stream_id, + const nghttp3_mem *mem) { + nghttp3_qpack_read_state_reset(&sctx->rstate); + + sctx->mem = mem; + sctx->rstate.prefix = 8; + sctx->state = NGHTTP3_QPACK_RS_STATE_RICNT; + sctx->opcode = 0; + sctx->stream_id = stream_id; + sctx->ricnt = 0; + sctx->dbase_sign = 0; + sctx->base = 0; +} + +void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_read_state_free(&sctx->rstate); +} + +void nghttp3_qpack_stream_context_reset(nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_stream_context_init(sctx, sctx->stream_id, sctx->mem); +} + +uint64_t +nghttp3_qpack_stream_context_get_ricnt(nghttp3_qpack_stream_context *sctx) { + return sctx->ricnt; +} + +nghttp3_ssize +nghttp3_qpack_decoder_read_request(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv, uint8_t *pflags, + const uint8_t *src, size_t srclen, int fin) { + const uint8_t *p = src, *end = src ? src + srclen : src; + int rv; + int busy = 0; + nghttp3_ssize nread; + int rfin; + const nghttp3_mem *mem = decoder->ctx.mem; + + if (decoder->ctx.bad) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + *pflags = NGHTTP3_QPACK_DECODE_FLAG_NONE; + + for (; p != end || busy;) { + busy = 0; + switch (sctx->state) { + case NGHTTP3_QPACK_RS_STATE_RICNT: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + rv = nghttp3_qpack_decoder_reconstruct_ricnt(decoder, &sctx->ricnt, + sctx->rstate.left); + if (rv != 0) { + goto fail; + } + + sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE_SIGN; + break; + case NGHTTP3_QPACK_RS_STATE_DBASE_SIGN: + if ((*p) & 0x80) { + sctx->dbase_sign = 1; + } + sctx->state = NGHTTP3_QPACK_RS_STATE_DBASE; + sctx->rstate.left = 0; + sctx->rstate.prefix = 7; + sctx->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_RS_STATE_DBASE: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + if (sctx->dbase_sign) { + if (sctx->ricnt < sctx->rstate.left) { + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + sctx->base = sctx->ricnt - sctx->rstate.left - 1; + } else { + sctx->base = sctx->ricnt + sctx->rstate.left; + } + + DEBUGF("qpack::decode: ricnt=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 + "\n", + sctx->ricnt, sctx->base, decoder->ctx.next_absidx); + + if (sctx->ricnt > decoder->ctx.next_absidx) { + DEBUGF("qpack::decode: stream blocked\n"); + sctx->state = NGHTTP3_QPACK_RS_STATE_BLOCKED; + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; + return p - src; + } + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + sctx->rstate.left = 0; + sctx->rstate.shift = 0; + break; + case NGHTTP3_QPACK_RS_STATE_OPCODE: + assert(sctx->rstate.left == 0); + assert(sctx->rstate.shift == 0); + if ((*p) & 0x80) { + DEBUGF("qpack::decode: OPCODE_INDEXED\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED; + sctx->rstate.dynamic = !((*p) & 0x40); + sctx->rstate.prefix = 6; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } else if ((*p) & 0x40) { + DEBUGF("qpack::decode: OPCODE_INDEXED_NAME\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME; + sctx->rstate.never = (*p) & 0x20; + sctx->rstate.dynamic = !((*p) & 0x10); + sctx->rstate.prefix = 4; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } else if ((*p) & 0x20) { + DEBUGF("qpack::decode: OPCODE_LITERAL\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_LITERAL; + sctx->rstate.never = (*p) & 0x10; + sctx->rstate.dynamic = 0; + sctx->rstate.prefix = 3; + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN; + } else if ((*p) & 0x10) { + DEBUGF("qpack::decode: OPCODE_INDEXED_PB\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB; + sctx->rstate.dynamic = 1; + sctx->rstate.prefix = 4; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } else { + DEBUGF("qpack::decode: OPCODE_INDEXED_NAME_PB\n"); + sctx->opcode = NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB; + sctx->rstate.never = (*p) & 0x08; + sctx->rstate.dynamic = 1; + sctx->rstate.prefix = 3; + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_INDEX; + } + break; + case NGHTTP3_QPACK_RS_STATE_READ_INDEX: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + switch (sctx->opcode) { + case NGHTTP3_QPACK_RS_OPCODE_INDEXED: + rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv); + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB: + rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + nghttp3_qpack_decoder_emit_indexed(decoder, sctx, nv); + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: + rv = nghttp3_qpack_decoder_brel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + sctx->rstate.prefix = 7; + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + break; + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: + rv = nghttp3_qpack_decoder_pbrel2abs(decoder, sctx); + if (rv != 0) { + goto fail; + } + sctx->rstate.prefix = 7; + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + break; + default: + /* Unreachable */ + assert(0); + } + break; + case NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN: + qpack_read_state_check_huffman(&sctx->rstate, *p); + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAMELEN; + sctx->rstate.left = 0; + sctx->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_RS_STATE_READ_NAMELEN: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + if (sctx->rstate.left > NGHTTP3_QPACK_MAX_NAMELEN) { + rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; + goto fail; + } + + if (sctx->rstate.huffman_encoded) { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&sctx->rstate.name, + (size_t)sctx->rstate.left * 2 + 1, mem); + } else { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_NAME; + rv = nghttp3_rcbuf_new(&sctx->rstate.name, + (size_t)sctx->rstate.left + 1, mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&sctx->rstate.namebuf, sctx->rstate.name->base, + sctx->rstate.name->len); + break; + case NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN: + nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.namebuf, p, + end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_name(&sctx->rstate); + + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + sctx->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_RS_STATE_READ_NAME: + nread = qpack_read_string(&sctx->rstate, &sctx->rstate.namebuf, p, end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_name(&sctx->rstate); + + sctx->state = NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN; + sctx->rstate.prefix = 7; + break; + case NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN: + qpack_read_state_check_huffman(&sctx->rstate, *p); + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUELEN; + sctx->rstate.left = 0; + sctx->rstate.shift = 0; + /* Fall through */ + case NGHTTP3_QPACK_RS_STATE_READ_VALUELEN: + nread = qpack_read_varint(&rfin, &sctx->rstate, p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (!rfin) { + goto almost_ok; + } + + if (sctx->rstate.left > NGHTTP3_QPACK_MAX_VALUELEN) { + rv = NGHTTP3_ERR_QPACK_HEADER_TOO_LARGE; + goto fail; + } + + if (sctx->rstate.huffman_encoded) { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN; + nghttp3_qpack_huffman_decode_context_init(&sctx->rstate.huffman_ctx); + rv = nghttp3_rcbuf_new(&sctx->rstate.value, + (size_t)sctx->rstate.left * 2 + 1, mem); + } else { + sctx->state = NGHTTP3_QPACK_RS_STATE_READ_VALUE; + rv = nghttp3_rcbuf_new(&sctx->rstate.value, + (size_t)sctx->rstate.left + 1, mem); + } + if (rv != 0) { + goto fail; + } + + nghttp3_buf_wrap_init(&sctx->rstate.valuebuf, sctx->rstate.value->base, + sctx->rstate.value->len); + + /* value might be 0 length */ + busy = 1; + break; + case NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN: + nread = qpack_read_huffman_string(&sctx->rstate, &sctx->rstate.valuebuf, + p, end); + if (nread < 0) { + assert(NGHTTP3_ERR_QPACK_FATAL == nread); + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_value(&sctx->rstate); + + switch (sctx->opcode) { + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: + nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + break; + case NGHTTP3_QPACK_RS_OPCODE_LITERAL: + nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); + break; + default: + /* Unreachable */ + assert(0); + } + + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_STATE_READ_VALUE: + nread = qpack_read_string(&sctx->rstate, &sctx->rstate.valuebuf, p, end); + if (nread < 0) { + rv = (int)nread; + goto fail; + } + + p += nread; + + if (sctx->rstate.left) { + goto almost_ok; + } + + qpack_read_state_terminate_value(&sctx->rstate); + + switch (sctx->opcode) { + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME: + case NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB: + nghttp3_qpack_decoder_emit_indexed_name(decoder, sctx, nv); + break; + case NGHTTP3_QPACK_RS_OPCODE_LITERAL: + nghttp3_qpack_decoder_emit_literal(decoder, sctx, nv); + break; + default: + /* Unreachable */ + assert(0); + } + + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_EMIT; + + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + + return p - src; + case NGHTTP3_QPACK_RS_STATE_BLOCKED: + if (sctx->ricnt > decoder->ctx.next_absidx) { + DEBUGF("qpack::decode: stream still blocked\n"); + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_BLOCKED; + return p - src; + } + sctx->state = NGHTTP3_QPACK_RS_STATE_OPCODE; + nghttp3_qpack_read_state_reset(&sctx->rstate); + break; + } + } + +almost_ok: + if (fin) { + if (sctx->state != NGHTTP3_QPACK_RS_STATE_OPCODE) { + rv = NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + goto fail; + } + + *pflags |= NGHTTP3_QPACK_DECODE_FLAG_FINAL; + + if (sctx->ricnt) { + rv = nghttp3_qpack_decoder_write_section_ack(decoder, sctx); + if (rv != 0) { + goto fail; + } + } + } + + return p - src; + +fail: + decoder->ctx.bad = 1; + return rv; +} + +static int qpack_decoder_dbuf_overflow(nghttp3_qpack_decoder *decoder) { + size_t limit = nghttp3_max(decoder->max_concurrent_streams, 100); + /* 10 = nghttp3_qpack_put_varint_len((1ULL << 62) - 1, 2)) */ + return nghttp3_buf_len(&decoder->dbuf) > limit * 2 * 10; +} + +int nghttp3_qpack_decoder_write_section_ack( + nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx) { + nghttp3_buf *dbuf = &decoder->dbuf; + uint8_t *p; + int rv; + + if (qpack_decoder_dbuf_overflow(decoder)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + rv = reserve_buf_small( + dbuf, nghttp3_qpack_put_varint_len((uint64_t)sctx->stream_id, 7), + decoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = dbuf->last; + *p = 0x80; + dbuf->last = nghttp3_qpack_put_varint(p, (uint64_t)sctx->stream_id, 7); + + if (decoder->written_icnt < sctx->ricnt) { + decoder->written_icnt = sctx->ricnt; + } + + return 0; +} + +size_t +nghttp3_qpack_decoder_get_decoder_streamlen(nghttp3_qpack_decoder *decoder) { + uint64_t n; + size_t len = 0; + + if (decoder->written_icnt < decoder->ctx.next_absidx) { + n = decoder->ctx.next_absidx - decoder->written_icnt; + len = nghttp3_qpack_put_varint_len(n, 6); + } + + return nghttp3_buf_len(&decoder->dbuf) + len; +} + +void nghttp3_qpack_decoder_write_decoder(nghttp3_qpack_decoder *decoder, + nghttp3_buf *dbuf) { + uint8_t *p; + uint64_t n = 0; + size_t len = 0; + + if (decoder->written_icnt < decoder->ctx.next_absidx) { + n = decoder->ctx.next_absidx - decoder->written_icnt; + len = nghttp3_qpack_put_varint_len(n, 6); + } + + assert(nghttp3_buf_left(dbuf) >= nghttp3_buf_len(&decoder->dbuf) + len); + + if (nghttp3_buf_len(&decoder->dbuf)) { + dbuf->last = nghttp3_cpymem(dbuf->last, decoder->dbuf.pos, + nghttp3_buf_len(&decoder->dbuf)); + } + + if (n) { + p = dbuf->last; + *p = 0; + dbuf->last = nghttp3_qpack_put_varint(p, n, 6); + + decoder->written_icnt = decoder->ctx.next_absidx; + } + + nghttp3_buf_reset(&decoder->dbuf); +} + +int nghttp3_qpack_decoder_cancel_stream(nghttp3_qpack_decoder *decoder, + int64_t stream_id) { + uint8_t *p; + int rv; + + if (qpack_decoder_dbuf_overflow(decoder)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + rv = reserve_buf(&decoder->dbuf, + nghttp3_qpack_put_varint_len((uint64_t)stream_id, 6), + decoder->ctx.mem); + if (rv != 0) { + return rv; + } + + p = decoder->dbuf.last; + *p = 0x40; + decoder->dbuf.last = nghttp3_qpack_put_varint(p, (uint64_t)stream_id, 6); + + return 0; +} + +int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, + uint64_t *dest, uint64_t encricnt) { + uint64_t max_ents, full, max, max_wrapped, ricnt; + + if (encricnt == 0) { + *dest = 0; + return 0; + } + + max_ents = decoder->ctx.hard_max_dtable_size / NGHTTP3_QPACK_ENTRY_OVERHEAD; + full = 2 * max_ents; + + if (encricnt > full) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + max = decoder->ctx.next_absidx + max_ents; + max_wrapped = max / full * full; + ricnt = max_wrapped + encricnt - 1; + + if (ricnt > max) { + if (ricnt <= full) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + ricnt -= full; + } + + if (ricnt == 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + *dest = ricnt; + + return 0; +} + +int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_read_state *rstate) { + DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " icnt=%" PRIu64 "\n", + rstate->dynamic, rstate->left, decoder->ctx.next_absidx); + + if (rstate->dynamic) { + if (decoder->ctx.next_absidx < rstate->left + 1) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + rstate->absidx = decoder->ctx.next_absidx - rstate->left - 1; + } else { + rstate->absidx = rstate->left; + } + if (qpack_decoder_validate_index(decoder, rstate) != 0) { + return NGHTTP3_ERR_QPACK_ENCODER_STREAM_ERROR; + } + return 0; +} + +int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_read_state *rstate = &sctx->rstate; + + DEBUGF("qpack::decode: dynamic=%d relidx=%" PRIu64 " base=%" PRIu64 + " icnt=%" PRIu64 "\n", + rstate->dynamic, rstate->left, sctx->base, decoder->ctx.next_absidx); + + if (rstate->dynamic) { + if (sctx->base < rstate->left + 1) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + rstate->absidx = sctx->base - rstate->left - 1; + + if (rstate->absidx >= sctx->ricnt) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + } else { + rstate->absidx = rstate->left; + } + + if (qpack_decoder_validate_index(decoder, rstate) != 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + return 0; +} + +int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx) { + nghttp3_qpack_read_state *rstate = &sctx->rstate; + + DEBUGF("qpack::decode: pbidx=%" PRIu64 " base=%" PRIu64 " icnt=%" PRIu64 "\n", + rstate->left, sctx->base, decoder->ctx.next_absidx); + + assert(rstate->dynamic); + + rstate->absidx = rstate->left + sctx->base; + + if (rstate->absidx >= sctx->ricnt) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + + if (qpack_decoder_validate_index(decoder, rstate) != 0) { + return NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED; + } + return 0; +} + +static void +qpack_decoder_emit_static_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; + (void)decoder; + + nv->name = (nghttp3_rcbuf *)&shd->name; + nv->value = (nghttp3_rcbuf *)&shd->value; + nv->token = shd->token; + nv->flags = NGHTTP3_NV_FLAG_NONE; +} + +static void +qpack_decoder_emit_dynamic_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + nghttp3_qpack_entry *ent = + nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); + + *nv = ent->nv; + + nghttp3_rcbuf_incref(nv->name); + nghttp3_rcbuf_incref(nv->value); +} + +void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + DEBUGF("qpack::decode: Indexed (%s) absidx=%" PRIu64 "\n", + sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx); + + if (sctx->rstate.dynamic) { + qpack_decoder_emit_dynamic_indexed(decoder, sctx, nv); + } else { + qpack_decoder_emit_static_indexed(decoder, sctx, nv); + } +} + +static void +qpack_decoder_emit_static_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + const nghttp3_qpack_static_header *shd = &stable[sctx->rstate.absidx]; + (void)decoder; + + nv->name = (nghttp3_rcbuf *)&shd->name; + nv->value = sctx->rstate.value; + nv->token = shd->token; + nv->flags = + sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + + sctx->rstate.value = NULL; +} + +static void +qpack_decoder_emit_dynamic_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + nghttp3_qpack_entry *ent = + nghttp3_qpack_context_dtable_get(&decoder->ctx, sctx->rstate.absidx); + (void)decoder; + + nv->name = ent->nv.name; + nv->value = sctx->rstate.value; + nv->token = ent->nv.token; + nv->flags = + sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + + nghttp3_rcbuf_incref(nv->name); + + sctx->rstate.value = NULL; +} + +void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + (void)decoder; + + DEBUGF("qpack::decode: Indexed name (%s) absidx=%" PRIu64 " value=%*s\n", + sctx->rstate.dynamic ? "dynamic" : "static", sctx->rstate.absidx, + (int)sctx->rstate.value->len, sctx->rstate.value->base); + + if (sctx->rstate.dynamic) { + qpack_decoder_emit_dynamic_indexed_name(decoder, sctx, nv); + } else { + qpack_decoder_emit_static_indexed_name(decoder, sctx, nv); + } +} + +void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv) { + (void)decoder; + + DEBUGF("qpack::decode: Emit literal name=%*s value=%*s\n", + (int)sctx->rstate.name->len, sctx->rstate.name->base, + (int)sctx->rstate.value->len, sctx->rstate.value->base); + + nv->name = sctx->rstate.name; + nv->value = sctx->rstate.value; + nv->token = qpack_lookup_token(nv->name->base, nv->name->len); + nv->flags = + sctx->rstate.never ? NGHTTP3_NV_FLAG_NEVER_INDEX : NGHTTP3_NV_FLAG_NONE; + + sctx->rstate.name = NULL; + sctx->rstate.value = NULL; +} + +int nghttp3_qpack_encoder_new(nghttp3_qpack_encoder **pencoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + nghttp3_qpack_encoder *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_encoder)); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_qpack_encoder_init(p, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + *pencoder = p; + + return 0; +} + +void nghttp3_qpack_encoder_del(nghttp3_qpack_encoder *encoder) { + const nghttp3_mem *mem; + + if (encoder == NULL) { + return; + } + + mem = encoder->ctx.mem; + + nghttp3_qpack_encoder_free(encoder); + nghttp3_mem_free(mem, encoder); +} + +int nghttp3_qpack_stream_context_new(nghttp3_qpack_stream_context **psctx, + int64_t stream_id, + const nghttp3_mem *mem) { + nghttp3_qpack_stream_context *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_stream_context)); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_qpack_stream_context_init(p, stream_id, mem); + + *psctx = p; + + return 0; +} + +void nghttp3_qpack_stream_context_del(nghttp3_qpack_stream_context *sctx) { + const nghttp3_mem *mem; + + if (sctx == NULL) { + return; + } + + mem = sctx->mem; + + nghttp3_qpack_stream_context_free(sctx); + nghttp3_mem_free(mem, sctx); +} + +int nghttp3_qpack_decoder_new(nghttp3_qpack_decoder **pdecoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem) { + int rv; + nghttp3_qpack_decoder *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_qpack_decoder)); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + rv = nghttp3_qpack_decoder_init(p, max_dtable_size, max_blocked, mem); + if (rv != 0) { + return rv; + } + + *pdecoder = p; + + return 0; +} + +void nghttp3_qpack_decoder_del(nghttp3_qpack_decoder *decoder) { + const nghttp3_mem *mem; + + if (decoder == NULL) { + return; + } + + mem = decoder->ctx.mem; + + nghttp3_qpack_decoder_free(decoder); + nghttp3_mem_free(mem, decoder); +} + +uint64_t nghttp3_qpack_decoder_get_icnt(const nghttp3_qpack_decoder *decoder) { + return decoder->ctx.next_absidx; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h new file mode 100644 index 00000000000000..429c55a78081f1 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack.h @@ -0,0 +1,961 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 NGHTTP3_QPACK_H +#define NGHTTP3_QPACK_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_rcbuf.h" +#include "nghttp3_map.h" +#include "nghttp3_pq.h" +#include "nghttp3_ringbuf.h" +#include "nghttp3_buf.h" +#include "nghttp3_ksl.h" +#include "nghttp3_qpack_huffman.h" + +#define NGHTTP3_QPACK_INT_MAX ((1ull << 62) - 1) + +/* NGHTTP3_QPACK_MAX_NAMELEN is the maximum (compressed) length of + header name this library can decode. */ +#define NGHTTP3_QPACK_MAX_NAMELEN 256 +/* NGHTTP3_QPACK_MAX_VALUELEN is the maximum (compressed) length of + header value this library can decode. */ +#define NGHTTP3_QPACK_MAX_VALUELEN 65536 + +/* nghttp3_qpack_indexing_mode is a indexing strategy. */ +typedef enum nghttp3_qpack_indexing_mode { + /* NGHTTP3_QPACK_INDEXING_MODE_LITERAL means that header field + should not be inserted into dynamic table. */ + NGHTTP3_QPACK_INDEXING_MODE_LITERAL, + /* NGHTTP3_QPACK_INDEXING_MODE_STORE means that header field can be + inserted into dynamic table. */ + NGHTTP3_QPACK_INDEXING_MODE_STORE, + /* NGHTTP3_QPACK_INDEXING_MODE_NEVER means that header field should + not be inserted into dynamic table and this must be true for all + forwarding paths. */ + NGHTTP3_QPACK_INDEXING_MODE_NEVER, +} nghttp3_qpack_indexing_mode; + +typedef struct nghttp3_qpack_entry nghttp3_qpack_entry; + +struct nghttp3_qpack_entry { + /* The header field name/value pair */ + nghttp3_qpack_nv nv; + /* map_next points to the entry which shares same bucket in hash + table. */ + nghttp3_qpack_entry *map_next; + /* sum is the sum of all entries inserted up to this entry. This + value does not contain the space required for this entry. */ + size_t sum; + /* absidx is the absolute index of this entry. */ + uint64_t absidx; + /* The hash value for header name (nv.name). */ + uint32_t hash; +}; + +/* The entry used for static table. */ +typedef struct nghttp3_qpack_static_entry { + uint64_t absidx; + int32_t token; + uint32_t hash; +} nghttp3_qpack_static_entry; + +typedef struct nghttp3_qpack_static_header { + nghttp3_rcbuf name; + nghttp3_rcbuf value; + int32_t token; +} nghttp3_qpack_static_header; + +/* + * nghttp3_qpack_header_block_ref is created per encoded header block + * and includes the required insert count and the minimum insert count + * of dynamic table entry it refers to. + */ +typedef struct nghttp3_qpack_header_block_ref { + nghttp3_pq_entry max_cnts_pe; + nghttp3_pq_entry min_cnts_pe; + /* max_cnt is the required insert count. */ + uint64_t max_cnt; + /* min_cnt is the minimum insert count of dynamic table entry it + refers to. In other words, this is the minimum absolute index of + dynamic header table entry this encoded block refers to plus + 1. */ + uint64_t min_cnt; +} nghttp3_qpack_header_block_ref; + +int nghttp3_qpack_header_block_ref_new(nghttp3_qpack_header_block_ref **pref, + uint64_t max_cnt, uint64_t min_cnt, + const nghttp3_mem *mem); + +void nghttp3_qpack_header_block_ref_del(nghttp3_qpack_header_block_ref *ref, + const nghttp3_mem *mem); + +typedef struct nghttp3_qpack_stream { + nghttp3_map_entry me; + /* refs is an array of pointer to nghttp3_qpack_header_block_ref in + the order of the time they are encoded. HTTP/3 allows multiple + header blocks (e.g., non-final response headers, final response + headers, trailers, and push promises) per stream. */ + nghttp3_ringbuf refs; + /* max_cnts is a priority queue sorted by descending order of + max_cnt of nghttp3_qpack_header_block_ref. */ + nghttp3_pq max_cnts; +} nghttp3_qpack_stream; + +int nghttp3_qpack_stream_new(nghttp3_qpack_stream **pstream, int64_t stream_id, + const nghttp3_mem *mem); + +void nghttp3_qpack_stream_del(nghttp3_qpack_stream *stream, + const nghttp3_mem *mem); + +uint64_t nghttp3_qpack_stream_get_max_cnt(const nghttp3_qpack_stream *stream); + +int nghttp3_qpack_stream_add_ref(nghttp3_qpack_stream *stream, + nghttp3_qpack_header_block_ref *ref); + +void nghttp3_qpack_stream_pop_ref(nghttp3_qpack_stream *stream); + +#define NGHTTP3_QPACK_ENTRY_OVERHEAD 32 + +typedef struct nghttp3_qpack_context { + /* dtable is a dynamic table */ + nghttp3_ringbuf dtable; + /* mem is memory allocator */ + const nghttp3_mem *mem; + /* dtable_size is abstracted buffer size of dtable as described in + the spec. This is the sum of length of name/value in dtable + + NGHTTP3_QPACK_ENTRY_OVERHEAD bytes overhead per each entry. */ + size_t dtable_size; + size_t dtable_sum; + /* hard_max_dtable_size is the maximum size of dynamic table. In + HTTP/3, it is notified by decoder as + SETTINGS_QPACK_MAX_TABLE_CAPACITY. Any value lower than or equal + to SETTINGS_QPACK_MAX_TABLE_CAPACITY is OK because encoder has + the authority to decide how many entries are inserted into + dynamic table. */ + size_t hard_max_dtable_size; + /* max_dtable_size is the effective maximum size of dynamic table. */ + size_t max_dtable_size; + /* max_blocked is the maximum number of stream which can be + blocked. */ + size_t max_blocked; + /* next_absidx is the next absolute index for nghttp3_qpack_entry. + It is equivalent to insert count. */ + uint64_t next_absidx; + /* If inflate/deflate error occurred, this value is set to 1 and + further invocation of inflate/deflate will fail with + NGHTTP3_ERR_QPACK_FATAL. */ + uint8_t bad; +} nghttp3_qpack_context; + +typedef struct nghttp3_qpack_read_state { + nghttp3_qpack_huffman_decode_context huffman_ctx; + nghttp3_buf namebuf; + nghttp3_buf valuebuf; + nghttp3_rcbuf *name; + nghttp3_rcbuf *value; + uint64_t left; + size_t prefix; + size_t shift; + uint64_t absidx; + int never; + int dynamic; + int huffman_encoded; +} nghttp3_qpack_read_state; + +void nghttp3_qpack_read_state_free(nghttp3_qpack_read_state *rstate); + +void nghttp3_qpack_read_state_reset(nghttp3_qpack_read_state *rstate); + +#define NGHTTP3_QPACK_MAP_SIZE 64 + +typedef struct nghttp3_qpack_map { + nghttp3_qpack_entry *table[NGHTTP3_QPACK_MAP_SIZE]; +} nghttp3_qpack_map; + +/* nghttp3_qpack_decoder_stream_state is a set of states when decoding + decoder stream. */ +typedef enum nghttp3_qpack_decoder_stream_state { + NGHTTP3_QPACK_DS_STATE_OPCODE, + NGHTTP3_QPACK_DS_STATE_READ_NUMBER, +} nghttp3_qpack_decoder_stream_state; + +/* nghttp3_qpack_decoder_stream_opcode is opcode used in decoder + stream. */ +typedef enum nghttp3_qpack_decoder_stream_opcode { + NGHTTP3_QPACK_DS_OPCODE_ICNT_INCREMENT, + NGHTTP3_QPACK_DS_OPCODE_SECTION_ACK, + NGHTTP3_QPACK_DS_OPCODE_STREAM_CANCEL, +} nghttp3_qpack_decoder_stream_opcode; + +/* QPACK encoder flags */ + +/* NGHTTP3_QPACK_ENCODER_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_QPACK_ENCODER_FLAG_NONE 0x00 +/* NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP indicates that + Set Dynamic Table Capacity is required. */ +#define NGHTTP3_QPACK_ENCODER_FLAG_PENDING_SET_DTABLE_CAP 0x01 + +struct nghttp3_qpack_encoder { + nghttp3_qpack_context ctx; + /* dtable_map is a map of hash to nghttp3_qpack_entry to provide + fast access to an entry in dynamic table. */ + nghttp3_qpack_map dtable_map; + /* streams is a map of stream ID to nghttp3_qpack_stream to keep + track of unacknowledged streams. */ + nghttp3_map streams; + /* blocked_streams is an ordered list of nghttp3_qpack_stream, in + descending order of max_cnt, to search the unblocked streams by + received known count. */ + nghttp3_ksl blocked_streams; + /* min_cnts is a priority queue of nghttp3_qpack_header_block_ref + sorted by ascending order of min_cnt to know that an entry can be + evicted from dynamic table. */ + nghttp3_pq min_cnts; + /* krcnt is Known Received Count. */ + uint64_t krcnt; + /* state is a current state of reading decoder stream. */ + nghttp3_qpack_decoder_stream_state state; + /* opcode is a decoder stream opcode being processed. */ + nghttp3_qpack_decoder_stream_opcode opcode; + /* rstate is a set of intermediate state which are used to process + decoder stream. */ + nghttp3_qpack_read_state rstate; + /* min_dtable_update is the minimum dynamic table size required. */ + size_t min_dtable_update; + /* last_max_dtable_update is the dynamic table size last + requested. */ + size_t last_max_dtable_update; + /* flags is bitwise OR of zero or more of + NGHTTP3_QPACK_ENCODER_FLAG_*. */ + uint8_t flags; +}; + +/* + * nghttp3_qpack_encoder_init initializes |encoder|. + * |max_dtable_size| is the maximum size of dynamic table. + * |max_blocked| is the maximum number of stream which can be blocked. + * |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_init(nghttp3_qpack_encoder *encoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem); + +/* + * nghttp3_qpack_encoder_free frees memory allocated for |encoder|. + * This function does not free memory pointed by |encoder|. + */ +void nghttp3_qpack_encoder_free(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_encode_nv encodes |nv|. It writes request + * stream into |rbuf| and writes encoder stream into |ebuf|. |nv| is + * a header field to encode. |base| is base. |allow_blocking| is + * nonzero if this stream can be blocked (or it has been blocked + * already). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_encode_nv(nghttp3_qpack_encoder *encoder, + uint64_t *pmax_cnt, uint64_t *pmin_cnt, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + const nghttp3_nv *nv, uint64_t base, + int allow_blocking); + +/* nghttp3_qpack_lookup_result stores a result of table lookup. */ +typedef struct nghttp3_qpack_lookup_result { + /* index is an index of matched entry. -1 if no match is made. */ + nghttp3_ssize index; + /* name_value_match is nonzero if both name and value are + matched. */ + int name_value_match; + /* pb_index is the absolute index of matched post-based dynamic + table entry. -1 if no such entry exists. */ + nghttp3_ssize pb_index; +} nghttp3_qpack_lookup_result; + +/* + * nghttp3_qpack_lookup_stable searches |nv| in static table. |token| + * is a token of nv->name and it is -1 if there is no corresponding + * token defined. |indexing_mode| provides indexing strategy. + */ +nghttp3_qpack_lookup_result +nghttp3_qpack_lookup_stable(const nghttp3_nv *nv, int32_t token, + nghttp3_qpack_indexing_mode indexing_mode); + +/* + * nghttp3_qpack_encoder_lookup_dtable searches |nv| in dynamic table. + * |token| is a token of nv->name and it is -1 if there is no + * corresponding token defined. |hash| is a hash of nv->name. + * |indexing_mode| provides indexing strategy. |krcnt| is Known + * Received Count. |allow_blocking| is nonzero if this stream can be + * blocked (or it has been blocked already). + */ +nghttp3_qpack_lookup_result nghttp3_qpack_encoder_lookup_dtable( + nghttp3_qpack_encoder *encoder, const nghttp3_nv *nv, int32_t token, + uint32_t hash, nghttp3_qpack_indexing_mode indexing_mode, uint64_t krcnt, + int allow_blocking); + +/* + * nghttp3_qpack_encoder_write_field_section_prefix writes Encoded + * Field Section Prefix into |pbuf|. |ricnt| is Required Insert + * Count. |base| is Base. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_field_section_prefix( + nghttp3_qpack_encoder *encoder, nghttp3_buf *pbuf, uint64_t ricnt, + uint64_t base); + +/* + * nghttp3_qpack_encoder_write_static_indexed writes Indexed Header + * Field to |rbuf|. |absidx| is an absolute index into static table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_static_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + uint64_t absidx); + +/* + * nghttp3_qpack_encoder_write_dynamic_indexed writes Indexed Header + * Field to |rbuf|. |absidx| is an absolute index into dynamic table. + * |base| is base. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_indexed(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + uint64_t absidx, uint64_t base); + +/* + * nghttp3_qpack_encoder_write_static_indexed writes Literal Header + * Field With Name Reference to |rbuf|. |absidx| is an absolute index + * into static table to reference a name. |nv| is a header field to + * encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_static_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_dynamic_indexed writes Literal Header + * Field With Name Reference to |rbuf|. |absidx| is an absolute index + * into dynamic table to reference a name. |base| is a base. |nv| is + * a header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_indexed_name( + nghttp3_qpack_encoder *encoder, nghttp3_buf *rbuf, uint64_t absidx, + uint64_t base, const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_literal writes Literal Header Field + * With Literal Name to |rbuf|. |nv| is a header field to encode. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_literal(nghttp3_qpack_encoder *encoder, + nghttp3_buf *rbuf, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_static_insert writes Insert With Name + * Reference to |ebuf|. |absidx| is an absolute index into static + * table to reference a name. |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_static_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + uint64_t absidx, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_dynamic_insert writes Insert With Name + * Reference to |ebuf|. |absidx| is an absolute index into dynamic + * table to reference a name. |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_dynamic_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + uint64_t absidx, + const nghttp3_nv *nv); + +/* + * nghttp3_qpack_encoder_write_duplicate_insert writes Duplicate to + * |ebuf|. |absidx| is an absolute index into dynamic table to + * reference an entry. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_duplicate_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + uint64_t absidx); + +/* + * nghttp3_qpack_encoder_write_literal_insert writes Insert With + * Literal Name to |ebuf|. |nv| is a header field to insert. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_literal_insert(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, + const nghttp3_nv *nv); + +int nghttp3_qpack_encoder_stream_is_blocked(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_block_stream blocks |stream|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_block_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_unblock_stream unblocks |stream|. + */ +void nghttp3_qpack_encoder_unblock_stream(nghttp3_qpack_encoder *encoder, + nghttp3_qpack_stream *stream); + +/* + * nghttp3_qpack_encoder_unblock unblocks stream whose max_cnt is less + * than or equal to |max_cnt|. + */ +void nghttp3_qpack_encoder_unblock(nghttp3_qpack_encoder *encoder, + uint64_t max_cnt); + +/* + * nghttp3_qpack_encoder_find_stream returns stream whose stream ID is + * |stream_id|. This function returns NULL if there is no such + * stream. + */ +nghttp3_qpack_stream * +nghttp3_qpack_encoder_find_stream(nghttp3_qpack_encoder *encoder, + int64_t stream_id); + +uint64_t nghttp3_qpack_encoder_get_min_cnt(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_shrink_dtable shrinks dynamic table so that + * the dynamic table size is less than or equal to maximum size. + */ +void nghttp3_qpack_encoder_shrink_dtable(nghttp3_qpack_encoder *encoder); + +/* + * nghttp3_qpack_encoder_process_dtable_update processes pending + * dynamic table size update. It might write encoder stream into + * |ebuf|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_process_dtable_update(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf); + +/* + * nghttp3_qpack_encoder_write_set_dtable_cap writes Set Dynamic Table + * Capacity. to |ebuf|. |cap| is the capacity of dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_write_set_dtable_cap(nghttp3_qpack_encoder *encoder, + nghttp3_buf *ebuf, size_t cap); + +/* + * nghttp3_qpack_context_dtable_add adds |qnv| to dynamic table. If + * |ctx| is a part of encoder, |dtable_map| is not NULL. |hash| is a + * hash value of name. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_context_dtable_add(nghttp3_qpack_context *ctx, + nghttp3_qpack_nv *qnv, + nghttp3_qpack_map *dtable_map, + uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_static_add adds |nv| to dynamic table + * by referencing static table entry at an absolute index |absidx|. + * The hash of name is given as |hash|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_dtable_static_add(nghttp3_qpack_encoder *encoder, + uint64_t absidx, + const nghttp3_nv *nv, + uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_dynamic_add adds |nv| to dynamic table + * by referencing dynamic table entry at an absolute index |absidx|. + * The hash of name is given as |hash|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_dtable_dynamic_add(nghttp3_qpack_encoder *encoder, + uint64_t absidx, + const nghttp3_nv *nv, + uint32_t hash); + +/* + * nghttp3_qpack_encoder_dtable_duplicate_add duplicates dynamic table + * entry at an absolute index |absidx|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_encoder_dtable_duplicate_add(nghttp3_qpack_encoder *encoder, + uint64_t absidx); + +/* + * nghttp3_qpack_encoder_dtable_literal_add adds |nv| to dynamic + * table. |token| is a token of name and is -1 if it has no token + * value defined. |hash| is a hash of name. + * + * NGHTTP3_ERR_NOMEM Out of memory. + */ +int nghttp3_qpack_encoder_dtable_literal_add(nghttp3_qpack_encoder *encoder, + const nghttp3_nv *nv, + int32_t token, uint32_t hash); + +/* + * nghttp3_qpack_context_dtable_get returns dynamic table entry whose + * absolute index is |absidx|. This function assumes that such entry + * exists. + */ +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_get(nghttp3_qpack_context *ctx, uint64_t absidx); + +/* + * nghttp3_qpack_context_dtable_top returns latest dynamic table + * entry. This function assumes dynamic table is not empty. + */ +nghttp3_qpack_entry * +nghttp3_qpack_context_dtable_top(nghttp3_qpack_context *ctx); + +/* + * nghttp3_qpack_entry_init initializes |ent|. |qnv| is a header + * field. |sum| is the sum of table space occupied by all entries + * inserted so far. It does not include this entry. |absidx| is an + * absolute index of this entry. |hash| is a hash of header field + * name. This function increases reference count of qnv->nv.name and + * qnv->nv.value. + */ +void nghttp3_qpack_entry_init(nghttp3_qpack_entry *ent, nghttp3_qpack_nv *qnv, + size_t sum, uint64_t absidx, uint32_t hash); + +/* + * nghttp3_qpack_entry_free frees memory allocated for |ent|. + */ +void nghttp3_qpack_entry_free(nghttp3_qpack_entry *ent); + +/* + * nghttp3_qpack_put_varint_len returns the required number of bytes + * to encode |n| with |prefix| bits. + */ +size_t nghttp3_qpack_put_varint_len(uint64_t n, size_t prefix); + +/* + * nghttp3_qpack_put_varint encodes |n| using variable integer + * encoding with |prefix| bits into |buf|. This function assumes the + * buffer pointed by |buf| has enough space. This function returns + * the one byte beyond the last write (buf + + * nghttp3_qpack_put_varint_len(n, prefix)). + */ +uint8_t *nghttp3_qpack_put_varint(uint8_t *buf, uint64_t n, size_t prefix); + +/* nghttp3_qpack_encoder_stream_state is a set of states for encoder + stream decoding. */ +typedef enum nghttp3_qpack_encoder_stream_state { + NGHTTP3_QPACK_ES_STATE_OPCODE, + NGHTTP3_QPACK_ES_STATE_READ_INDEX, + NGHTTP3_QPACK_ES_STATE_CHECK_NAME_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_NAMELEN, + NGHTTP3_QPACK_ES_STATE_READ_NAME_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_NAME, + NGHTTP3_QPACK_ES_STATE_CHECK_VALUE_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_VALUELEN, + NGHTTP3_QPACK_ES_STATE_READ_VALUE_HUFFMAN, + NGHTTP3_QPACK_ES_STATE_READ_VALUE, +} nghttp3_qpack_encoder_stream_state; + +/* nghttp3_qpack_encoder_stream_opcode is a set of opcodes used in + encoder stream. */ +typedef enum nghttp3_qpack_encoder_stream_opcode { + NGHTTP3_QPACK_ES_OPCODE_INSERT_INDEXED, + NGHTTP3_QPACK_ES_OPCODE_INSERT, + NGHTTP3_QPACK_ES_OPCODE_DUPLICATE, + NGHTTP3_QPACK_ES_OPCODE_SET_DTABLE_CAP, +} nghttp3_qpack_encoder_stream_opcode; + +/* nghttp3_qpack_request_stream_state is a set of states for request + stream decoding. */ +typedef enum nghttp3_qpack_request_stream_state { + NGHTTP3_QPACK_RS_STATE_RICNT, + NGHTTP3_QPACK_RS_STATE_DBASE_SIGN, + NGHTTP3_QPACK_RS_STATE_DBASE, + NGHTTP3_QPACK_RS_STATE_OPCODE, + NGHTTP3_QPACK_RS_STATE_READ_INDEX, + NGHTTP3_QPACK_RS_STATE_CHECK_NAME_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_NAMELEN, + NGHTTP3_QPACK_RS_STATE_READ_NAME_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_NAME, + NGHTTP3_QPACK_RS_STATE_CHECK_VALUE_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_VALUELEN, + NGHTTP3_QPACK_RS_STATE_READ_VALUE_HUFFMAN, + NGHTTP3_QPACK_RS_STATE_READ_VALUE, + NGHTTP3_QPACK_RS_STATE_BLOCKED, +} nghttp3_qpack_request_stream_state; + +/* nghttp3_qpack_request_stream_opcode is a set of opcodes used in + request stream. */ +typedef enum nghttp3_qpack_request_stream_opcode { + NGHTTP3_QPACK_RS_OPCODE_INDEXED, + NGHTTP3_QPACK_RS_OPCODE_INDEXED_PB, + NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME, + NGHTTP3_QPACK_RS_OPCODE_INDEXED_NAME_PB, + NGHTTP3_QPACK_RS_OPCODE_LITERAL, +} nghttp3_qpack_request_stream_opcode; + +struct nghttp3_qpack_decoder { + nghttp3_qpack_context ctx; + /* state is a current state of reading encoder stream. */ + nghttp3_qpack_encoder_stream_state state; + /* opcode is an encoder stream opcode being processed. */ + nghttp3_qpack_encoder_stream_opcode opcode; + /* rstate is a set of intermediate state which are used to process + encoder stream. */ + nghttp3_qpack_read_state rstate; + /* dbuf is decoder stream. */ + nghttp3_buf dbuf; + /* written_icnt is Insert Count written to decoder stream so far. */ + uint64_t written_icnt; + /* max_concurrent_streams is the number of concurrent streams that a + remote endpoint can open, including both bidirectional and + unidirectional streams which potentially receives QPACK encoded + HEADER frame. */ + size_t max_concurrent_streams; +}; + +/* + * nghttp3_qpack_decoder_init initializes |decoder|. + * |max_dtable_size| is the maximum size of dynamic table. + * |max_blocked| is the maximum number of stream which can be blocked. + * |mem| is a memory allocator. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_qpack_decoder_init(nghttp3_qpack_decoder *decoder, + size_t max_dtable_size, size_t max_blocked, + const nghttp3_mem *mem); + +/* + * nghttp3_qpack_decoder_free frees memory allocated for |decoder|. + * This function does not free memory pointed by |decoder|. + */ +void nghttp3_qpack_decoder_free(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_indexed_add adds entry received in + * Insert With Name Reference to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_indexed_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_static_add adds entry received in + * Insert With Name Reference (static) to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_static_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_dynamic_add adds entry received in + * Insert With Name Reference (dynamic) to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_dynamic_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_duplicate_add adds entry received in + * Duplicate to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_duplicate_add(nghttp3_qpack_decoder *decoder); + +/* + * nghttp3_qpack_decoder_dtable_literal_add adds entry received in + * Insert With Literal Name to dynamic table. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Space required for a decoded entry exceeds max dynamic table + * size. + */ +int nghttp3_qpack_decoder_dtable_literal_add(nghttp3_qpack_decoder *decoder); + +struct nghttp3_qpack_stream_context { + /* state is a current state of reading request stream. */ + nghttp3_qpack_request_stream_state state; + /* rstate is a set of intermediate state which are used to process + request stream. */ + nghttp3_qpack_read_state rstate; + const nghttp3_mem *mem; + /* opcode is a request stream opcode being processed. */ + nghttp3_qpack_request_stream_opcode opcode; + int64_t stream_id; + /* ricnt is Required Insert Count to decode this header block. */ + uint64_t ricnt; + /* base is Base in Header Block Prefix. */ + uint64_t base; + /* dbase_sign is the delta base sign in Header Block Prefix. */ + int dbase_sign; +}; + +/* + * nghttp3_qpack_stream_context_init initializes |sctx|. + */ +void nghttp3_qpack_stream_context_init(nghttp3_qpack_stream_context *sctx, + int64_t stream_id, + const nghttp3_mem *mem); + +/* + * nghttp3_qpack_stream_context_free frees memory allocated for + * |sctx|. This function does not free memory pointed by |sctx|. + */ +void nghttp3_qpack_stream_context_free(nghttp3_qpack_stream_context *sctx); + +/* + * nghttp3_qpack_decoder_reconstruct_ricnt reconstructs Required + * Insert Count from the encoded form |encricnt| and stores Required + * Insert Count in |*dest|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + * Unable to reconstruct Required Insert Count. + */ +int nghttp3_qpack_decoder_reconstruct_ricnt(nghttp3_qpack_decoder *decoder, + uint64_t *dest, uint64_t encricnt); + +/* + * nghttp3_qpack_decoder_rel2abs converts relative index rstate->left + * received in encoder stream to absolute index and stores it in + * rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_ENCODER_STREAM + * Relative index is invalid. + */ +int nghttp3_qpack_decoder_rel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_read_state *rstate); + +/* + * nghttp3_qpack_decoder_brel2abs converts Base relative index + * rstate->left received in request stream to absolute index and + * stores it in rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + * Base relative index is invalid. + */ +int nghttp3_qpack_decoder_brel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx); + +/* + * nghttp3_qpack_decoder_pbrel2abs converts Post-Base relative index + * rstate->left received in request stream to absolute index and + * stores it in rstate->absidx. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_QPACK_DECOMPRESSION_FAILED + * Post-Base relative index is invalid. + */ +int nghttp3_qpack_decoder_pbrel2abs(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx); + +void nghttp3_qpack_decoder_emit_indexed(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); + +void nghttp3_qpack_decoder_emit_indexed_name(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); + +void nghttp3_qpack_decoder_emit_literal(nghttp3_qpack_decoder *decoder, + nghttp3_qpack_stream_context *sctx, + nghttp3_qpack_nv *nv); + +/* + * nghttp3_qpack_decoder_write_section_ack writes Section + * Acknowledgement to decoder stream. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + * NGHTTP3_ERR_QPACK_FATAL + * Decoder stream overflow. + */ +int nghttp3_qpack_decoder_write_section_ack( + nghttp3_qpack_decoder *decoder, const nghttp3_qpack_stream_context *sctx); + +#endif /* NGHTTP3_QPACK_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c new file mode 100644 index 00000000000000..c36a68ededd1af --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.c @@ -0,0 +1,122 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 "nghttp3_qpack_huffman.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_conv.h" + +size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len) { + size_t i; + size_t nbits = 0; + + for (i = 0; i < len; ++i) { + nbits += huffman_sym_table[src[i]].nbits; + } + /* pad the prefix of EOS (256) */ + return (nbits + 7) / 8; +} + +uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, + size_t srclen) { + const nghttp3_qpack_huffman_sym *sym; + const uint8_t *end = src + srclen; + uint64_t code = 0; + size_t nbits = 0; + uint32_t x; + + for (; src != end;) { + sym = &huffman_sym_table[*src++]; + code |= (uint64_t)sym->code << (32 - nbits); + nbits += sym->nbits; + if (nbits < 32) { + continue; + } + x = htonl((uint32_t)(code >> 32)); + memcpy(dest, &x, 4); + dest += 4; + code <<= 32; + nbits -= 32; + } + + for (; nbits >= 8;) { + *dest++ = (uint8_t)(code >> 56); + code <<= 8; + nbits -= 8; + } + + if (nbits) { + *dest++ = (uint8_t)((uint8_t)(code >> 56) | ((1 << (8 - nbits)) - 1)); + } + + return dest; +} + +void nghttp3_qpack_huffman_decode_context_init( + nghttp3_qpack_huffman_decode_context *ctx) { + ctx->fstate = NGHTTP3_QPACK_HUFFMAN_ACCEPTED; +} + +nghttp3_ssize +nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, + uint8_t *dest, const uint8_t *src, size_t srclen, + int fin) { + uint8_t *p = dest; + const uint8_t *end = src + srclen; + nghttp3_qpack_huffman_decode_node node = {ctx->fstate, 0}; + const nghttp3_qpack_huffman_decode_node *t = &node; + uint8_t c; + + /* We use the decoding algorithm described in + http://graphics.ics.uci.edu/pub/Prefix.pdf */ + for (; src != end;) { + c = *src++; + t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c >> 4]; + if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) { + *p++ = t->sym; + } + + t = &qpack_huffman_decode_table[t->fstate & 0x1ff][c & 0xf]; + if (t->fstate & NGHTTP3_QPACK_HUFFMAN_SYM) { + *p++ = t->sym; + } + } + + ctx->fstate = t->fstate; + + if (fin && !(ctx->fstate & NGHTTP3_QPACK_HUFFMAN_ACCEPTED)) { + return NGHTTP3_ERR_QPACK_FATAL; + } + + return p - dest; +} + +int nghttp3_qpack_huffman_decode_failure_state( + nghttp3_qpack_huffman_decode_context *ctx) { + return ctx->fstate == 0x100; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h new file mode 100644 index 00000000000000..fc3bc7b264a900 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman.h @@ -0,0 +1,108 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 NGHTTP3_QPACK_HUFFMAN_H +#define NGHTTP3_QPACK_HUFFMAN_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +typedef struct nghttp3_qpack_huffman_sym { + /* The number of bits in this code */ + uint32_t nbits; + /* Huffman code aligned to LSB */ + uint32_t code; +} nghttp3_qpack_huffman_sym; + +extern const nghttp3_qpack_huffman_sym huffman_sym_table[]; + +size_t nghttp3_qpack_huffman_encode_count(const uint8_t *src, size_t len); + +uint8_t *nghttp3_qpack_huffman_encode(uint8_t *dest, const uint8_t *src, + size_t srclen); + +typedef enum nghttp3_qpack_huffman_decode_flag { + /* FSA accepts this state as the end of huffman encoding + sequence. */ + NGHTTP3_QPACK_HUFFMAN_ACCEPTED = 1 << 14, + /* This state emits symbol */ + NGHTTP3_QPACK_HUFFMAN_SYM = 1 << 15, +} nghttp3_qpack_huffman_decode_flag; + +typedef struct nghttp3_qpack_huffman_decode_node { + /* fstate is the current huffman decoding state, which is actually + the node ID of internal huffman tree with + nghttp3_qpack_huffman_decode_flag OR-ed. We have 257 leaf nodes, + but they are identical to root node other than emitting a symbol, + so we have 256 internal nodes [1..256], inclusive. The node ID + 256 is a special node and it is a terminal state that means + decoding failed. */ + uint16_t fstate; + /* symbol if NGHTTP3_QPACK_HUFFMAN_SYM flag set */ + uint8_t sym; +} nghttp3_qpack_huffman_decode_node; + +typedef struct nghttp3_qpack_huffman_decode_context { + /* fstate is the current huffman decoding state. */ + uint16_t fstate; +} nghttp3_qpack_huffman_decode_context; + +extern const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16]; + +void nghttp3_qpack_huffman_decode_context_init( + nghttp3_qpack_huffman_decode_context *ctx); + +/* + * nghttp3_qpack_huffman_decode decodes huffman encoded byte string + * stored in |src| of length |srclen|. |ctx| is a decoding context. + * |ctx| remembers the decoding state, and caller can call this + * function multiple times to feed each chunk of huffman encoded + * substring. |fin| must be nonzero if |src| contains the last chunk + * of huffman string. The decoded string is written to the buffer + * pointed by |dest|. This function assumes that the buffer pointed + * by |dest| contains enough memory to store decoded byte string. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGHTTP3_ERR_QPACK_FATAL + * Could not decode huffman string. + */ +nghttp3_ssize +nghttp3_qpack_huffman_decode(nghttp3_qpack_huffman_decode_context *ctx, + uint8_t *dest, const uint8_t *src, size_t srclen, + int fin); + +/* + * nghttp3_qpack_huffman_decode_failure_state returns nonzero if |ctx| + * indicates that huffman decoding context is in failure state. + */ +int nghttp3_qpack_huffman_decode_failure_state( + nghttp3_qpack_huffman_decode_context *ctx); + +#endif /* NGHTTP3_QPACK_HUFFMAN_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c new file mode 100644 index 00000000000000..0c104dbc0a0bd8 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_qpack_huffman_data.c @@ -0,0 +1,4981 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2013 nghttp2 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 "nghttp3_qpack_huffman.h" + +/* Generated by mkhufftbl.py */ + +const nghttp3_qpack_huffman_sym huffman_sym_table[] = { + {13, 0xffc00000u}, {23, 0xffffb000u}, {28, 0xfffffe20u}, {28, 0xfffffe30u}, + {28, 0xfffffe40u}, {28, 0xfffffe50u}, {28, 0xfffffe60u}, {28, 0xfffffe70u}, + {28, 0xfffffe80u}, {24, 0xffffea00u}, {30, 0xfffffff0u}, {28, 0xfffffe90u}, + {28, 0xfffffea0u}, {30, 0xfffffff4u}, {28, 0xfffffeb0u}, {28, 0xfffffec0u}, + {28, 0xfffffed0u}, {28, 0xfffffee0u}, {28, 0xfffffef0u}, {28, 0xffffff00u}, + {28, 0xffffff10u}, {28, 0xffffff20u}, {30, 0xfffffff8u}, {28, 0xffffff30u}, + {28, 0xffffff40u}, {28, 0xffffff50u}, {28, 0xffffff60u}, {28, 0xffffff70u}, + {28, 0xffffff80u}, {28, 0xffffff90u}, {28, 0xffffffa0u}, {28, 0xffffffb0u}, + {6, 0x50000000u}, {10, 0xfe000000u}, {10, 0xfe400000u}, {12, 0xffa00000u}, + {13, 0xffc80000u}, {6, 0x54000000u}, {8, 0xf8000000u}, {11, 0xff400000u}, + {10, 0xfe800000u}, {10, 0xfec00000u}, {8, 0xf9000000u}, {11, 0xff600000u}, + {8, 0xfa000000u}, {6, 0x58000000u}, {6, 0x5c000000u}, {6, 0x60000000u}, + {5, 0x0u}, {5, 0x8000000u}, {5, 0x10000000u}, {6, 0x64000000u}, + {6, 0x68000000u}, {6, 0x6c000000u}, {6, 0x70000000u}, {6, 0x74000000u}, + {6, 0x78000000u}, {6, 0x7c000000u}, {7, 0xb8000000u}, {8, 0xfb000000u}, + {15, 0xfff80000u}, {6, 0x80000000u}, {12, 0xffb00000u}, {10, 0xff000000u}, + {13, 0xffd00000u}, {6, 0x84000000u}, {7, 0xba000000u}, {7, 0xbc000000u}, + {7, 0xbe000000u}, {7, 0xc0000000u}, {7, 0xc2000000u}, {7, 0xc4000000u}, + {7, 0xc6000000u}, {7, 0xc8000000u}, {7, 0xca000000u}, {7, 0xcc000000u}, + {7, 0xce000000u}, {7, 0xd0000000u}, {7, 0xd2000000u}, {7, 0xd4000000u}, + {7, 0xd6000000u}, {7, 0xd8000000u}, {7, 0xda000000u}, {7, 0xdc000000u}, + {7, 0xde000000u}, {7, 0xe0000000u}, {7, 0xe2000000u}, {7, 0xe4000000u}, + {8, 0xfc000000u}, {7, 0xe6000000u}, {8, 0xfd000000u}, {13, 0xffd80000u}, + {19, 0xfffe0000u}, {13, 0xffe00000u}, {14, 0xfff00000u}, {6, 0x88000000u}, + {15, 0xfffa0000u}, {5, 0x18000000u}, {6, 0x8c000000u}, {5, 0x20000000u}, + {6, 0x90000000u}, {5, 0x28000000u}, {6, 0x94000000u}, {6, 0x98000000u}, + {6, 0x9c000000u}, {5, 0x30000000u}, {7, 0xe8000000u}, {7, 0xea000000u}, + {6, 0xa0000000u}, {6, 0xa4000000u}, {6, 0xa8000000u}, {5, 0x38000000u}, + {6, 0xac000000u}, {7, 0xec000000u}, {6, 0xb0000000u}, {5, 0x40000000u}, + {5, 0x48000000u}, {6, 0xb4000000u}, {7, 0xee000000u}, {7, 0xf0000000u}, + {7, 0xf2000000u}, {7, 0xf4000000u}, {7, 0xf6000000u}, {15, 0xfffc0000u}, + {11, 0xff800000u}, {14, 0xfff40000u}, {13, 0xffe80000u}, {28, 0xffffffc0u}, + {20, 0xfffe6000u}, {22, 0xffff4800u}, {20, 0xfffe7000u}, {20, 0xfffe8000u}, + {22, 0xffff4c00u}, {22, 0xffff5000u}, {22, 0xffff5400u}, {23, 0xffffb200u}, + {22, 0xffff5800u}, {23, 0xffffb400u}, {23, 0xffffb600u}, {23, 0xffffb800u}, + {23, 0xffffba00u}, {23, 0xffffbc00u}, {24, 0xffffeb00u}, {23, 0xffffbe00u}, + {24, 0xffffec00u}, {24, 0xffffed00u}, {22, 0xffff5c00u}, {23, 0xffffc000u}, + {24, 0xffffee00u}, {23, 0xffffc200u}, {23, 0xffffc400u}, {23, 0xffffc600u}, + {23, 0xffffc800u}, {21, 0xfffee000u}, {22, 0xffff6000u}, {23, 0xffffca00u}, + {22, 0xffff6400u}, {23, 0xffffcc00u}, {23, 0xffffce00u}, {24, 0xffffef00u}, + {22, 0xffff6800u}, {21, 0xfffee800u}, {20, 0xfffe9000u}, {22, 0xffff6c00u}, + {22, 0xffff7000u}, {23, 0xffffd000u}, {23, 0xffffd200u}, {21, 0xfffef000u}, + {23, 0xffffd400u}, {22, 0xffff7400u}, {22, 0xffff7800u}, {24, 0xfffff000u}, + {21, 0xfffef800u}, {22, 0xffff7c00u}, {23, 0xffffd600u}, {23, 0xffffd800u}, + {21, 0xffff0000u}, {21, 0xffff0800u}, {22, 0xffff8000u}, {21, 0xffff1000u}, + {23, 0xffffda00u}, {22, 0xffff8400u}, {23, 0xffffdc00u}, {23, 0xffffde00u}, + {20, 0xfffea000u}, {22, 0xffff8800u}, {22, 0xffff8c00u}, {22, 0xffff9000u}, + {23, 0xffffe000u}, {22, 0xffff9400u}, {22, 0xffff9800u}, {23, 0xffffe200u}, + {26, 0xfffff800u}, {26, 0xfffff840u}, {20, 0xfffeb000u}, {19, 0xfffe2000u}, + {22, 0xffff9c00u}, {23, 0xffffe400u}, {22, 0xffffa000u}, {25, 0xfffff600u}, + {26, 0xfffff880u}, {26, 0xfffff8c0u}, {26, 0xfffff900u}, {27, 0xfffffbc0u}, + {27, 0xfffffbe0u}, {26, 0xfffff940u}, {24, 0xfffff100u}, {25, 0xfffff680u}, + {19, 0xfffe4000u}, {21, 0xffff1800u}, {26, 0xfffff980u}, {27, 0xfffffc00u}, + {27, 0xfffffc20u}, {26, 0xfffff9c0u}, {27, 0xfffffc40u}, {24, 0xfffff200u}, + {21, 0xffff2000u}, {21, 0xffff2800u}, {26, 0xfffffa00u}, {26, 0xfffffa40u}, + {28, 0xffffffd0u}, {27, 0xfffffc60u}, {27, 0xfffffc80u}, {27, 0xfffffca0u}, + {20, 0xfffec000u}, {24, 0xfffff300u}, {20, 0xfffed000u}, {21, 0xffff3000u}, + {22, 0xffffa400u}, {21, 0xffff3800u}, {21, 0xffff4000u}, {23, 0xffffe600u}, + {22, 0xffffa800u}, {22, 0xffffac00u}, {25, 0xfffff700u}, {25, 0xfffff780u}, + {24, 0xfffff400u}, {24, 0xfffff500u}, {26, 0xfffffa80u}, {23, 0xffffe800u}, + {26, 0xfffffac0u}, {27, 0xfffffcc0u}, {26, 0xfffffb00u}, {26, 0xfffffb40u}, + {27, 0xfffffce0u}, {27, 0xfffffd00u}, {27, 0xfffffd20u}, {27, 0xfffffd40u}, + {27, 0xfffffd60u}, {28, 0xffffffe0u}, {27, 0xfffffd80u}, {27, 0xfffffda0u}, + {27, 0xfffffdc0u}, {27, 0xfffffde0u}, {27, 0xfffffe00u}, {26, 0xfffffb80u}, + {30, 0xfffffffcu}}; + +const nghttp3_qpack_huffman_decode_node qpack_huffman_decode_table[][16] = { + /* 0 */ + { + {0x04, 0}, + {0x05, 0}, + {0x07, 0}, + {0x08, 0}, + {0x0b, 0}, + {0x0c, 0}, + {0x10, 0}, + {0x13, 0}, + {0x19, 0}, + {0x1c, 0}, + {0x20, 0}, + {0x23, 0}, + {0x2a, 0}, + {0x31, 0}, + {0x39, 0}, + {0x4040, 0}, + }, + /* 1 */ + { + {0xc000, 48}, + {0xc000, 49}, + {0xc000, 50}, + {0xc000, 97}, + {0xc000, 99}, + {0xc000, 101}, + {0xc000, 105}, + {0xc000, 111}, + {0xc000, 115}, + {0xc000, 116}, + {0x0d, 0}, + {0x0e, 0}, + {0x11, 0}, + {0x12, 0}, + {0x14, 0}, + {0x15, 0}, + }, + /* 2 */ + { + {0x8001, 48}, + {0xc016, 48}, + {0x8001, 49}, + {0xc016, 49}, + {0x8001, 50}, + {0xc016, 50}, + {0x8001, 97}, + {0xc016, 97}, + {0x8001, 99}, + {0xc016, 99}, + {0x8001, 101}, + {0xc016, 101}, + {0x8001, 105}, + {0xc016, 105}, + {0x8001, 111}, + {0xc016, 111}, + }, + /* 3 */ + { + {0x8002, 48}, + {0x8009, 48}, + {0x8017, 48}, + {0xc028, 48}, + {0x8002, 49}, + {0x8009, 49}, + {0x8017, 49}, + {0xc028, 49}, + {0x8002, 50}, + {0x8009, 50}, + {0x8017, 50}, + {0xc028, 50}, + {0x8002, 97}, + {0x8009, 97}, + {0x8017, 97}, + {0xc028, 97}, + }, + /* 4 */ + { + {0x8003, 48}, + {0x8006, 48}, + {0x800a, 48}, + {0x800f, 48}, + {0x8018, 48}, + {0x801f, 48}, + {0x8029, 48}, + {0xc038, 48}, + {0x8003, 49}, + {0x8006, 49}, + {0x800a, 49}, + {0x800f, 49}, + {0x8018, 49}, + {0x801f, 49}, + {0x8029, 49}, + {0xc038, 49}, + }, + /* 5 */ + { + {0x8003, 50}, + {0x8006, 50}, + {0x800a, 50}, + {0x800f, 50}, + {0x8018, 50}, + {0x801f, 50}, + {0x8029, 50}, + {0xc038, 50}, + {0x8003, 97}, + {0x8006, 97}, + {0x800a, 97}, + {0x800f, 97}, + {0x8018, 97}, + {0x801f, 97}, + {0x8029, 97}, + {0xc038, 97}, + }, + /* 6 */ + { + {0x8002, 99}, + {0x8009, 99}, + {0x8017, 99}, + {0xc028, 99}, + {0x8002, 101}, + {0x8009, 101}, + {0x8017, 101}, + {0xc028, 101}, + {0x8002, 105}, + {0x8009, 105}, + {0x8017, 105}, + {0xc028, 105}, + {0x8002, 111}, + {0x8009, 111}, + {0x8017, 111}, + {0xc028, 111}, + }, + /* 7 */ + { + {0x8003, 99}, + {0x8006, 99}, + {0x800a, 99}, + {0x800f, 99}, + {0x8018, 99}, + {0x801f, 99}, + {0x8029, 99}, + {0xc038, 99}, + {0x8003, 101}, + {0x8006, 101}, + {0x800a, 101}, + {0x800f, 101}, + {0x8018, 101}, + {0x801f, 101}, + {0x8029, 101}, + {0xc038, 101}, + }, + /* 8 */ + { + {0x8003, 105}, + {0x8006, 105}, + {0x800a, 105}, + {0x800f, 105}, + {0x8018, 105}, + {0x801f, 105}, + {0x8029, 105}, + {0xc038, 105}, + {0x8003, 111}, + {0x8006, 111}, + {0x800a, 111}, + {0x800f, 111}, + {0x8018, 111}, + {0x801f, 111}, + {0x8029, 111}, + {0xc038, 111}, + }, + /* 9 */ + { + {0x8001, 115}, + {0xc016, 115}, + {0x8001, 116}, + {0xc016, 116}, + {0xc000, 32}, + {0xc000, 37}, + {0xc000, 45}, + {0xc000, 46}, + {0xc000, 47}, + {0xc000, 51}, + {0xc000, 52}, + {0xc000, 53}, + {0xc000, 54}, + {0xc000, 55}, + {0xc000, 56}, + {0xc000, 57}, + }, + /* 10 */ + { + {0x8002, 115}, + {0x8009, 115}, + {0x8017, 115}, + {0xc028, 115}, + {0x8002, 116}, + {0x8009, 116}, + {0x8017, 116}, + {0xc028, 116}, + {0x8001, 32}, + {0xc016, 32}, + {0x8001, 37}, + {0xc016, 37}, + {0x8001, 45}, + {0xc016, 45}, + {0x8001, 46}, + {0xc016, 46}, + }, + /* 11 */ + { + {0x8003, 115}, + {0x8006, 115}, + {0x800a, 115}, + {0x800f, 115}, + {0x8018, 115}, + {0x801f, 115}, + {0x8029, 115}, + {0xc038, 115}, + {0x8003, 116}, + {0x8006, 116}, + {0x800a, 116}, + {0x800f, 116}, + {0x8018, 116}, + {0x801f, 116}, + {0x8029, 116}, + {0xc038, 116}, + }, + /* 12 */ + { + {0x8002, 32}, + {0x8009, 32}, + {0x8017, 32}, + {0xc028, 32}, + {0x8002, 37}, + {0x8009, 37}, + {0x8017, 37}, + {0xc028, 37}, + {0x8002, 45}, + {0x8009, 45}, + {0x8017, 45}, + {0xc028, 45}, + {0x8002, 46}, + {0x8009, 46}, + {0x8017, 46}, + {0xc028, 46}, + }, + /* 13 */ + { + {0x8003, 32}, + {0x8006, 32}, + {0x800a, 32}, + {0x800f, 32}, + {0x8018, 32}, + {0x801f, 32}, + {0x8029, 32}, + {0xc038, 32}, + {0x8003, 37}, + {0x8006, 37}, + {0x800a, 37}, + {0x800f, 37}, + {0x8018, 37}, + {0x801f, 37}, + {0x8029, 37}, + {0xc038, 37}, + }, + /* 14 */ + { + {0x8003, 45}, + {0x8006, 45}, + {0x800a, 45}, + {0x800f, 45}, + {0x8018, 45}, + {0x801f, 45}, + {0x8029, 45}, + {0xc038, 45}, + {0x8003, 46}, + {0x8006, 46}, + {0x800a, 46}, + {0x800f, 46}, + {0x8018, 46}, + {0x801f, 46}, + {0x8029, 46}, + {0xc038, 46}, + }, + /* 15 */ + { + {0x8001, 47}, + {0xc016, 47}, + {0x8001, 51}, + {0xc016, 51}, + {0x8001, 52}, + {0xc016, 52}, + {0x8001, 53}, + {0xc016, 53}, + {0x8001, 54}, + {0xc016, 54}, + {0x8001, 55}, + {0xc016, 55}, + {0x8001, 56}, + {0xc016, 56}, + {0x8001, 57}, + {0xc016, 57}, + }, + /* 16 */ + { + {0x8002, 47}, + {0x8009, 47}, + {0x8017, 47}, + {0xc028, 47}, + {0x8002, 51}, + {0x8009, 51}, + {0x8017, 51}, + {0xc028, 51}, + {0x8002, 52}, + {0x8009, 52}, + {0x8017, 52}, + {0xc028, 52}, + {0x8002, 53}, + {0x8009, 53}, + {0x8017, 53}, + {0xc028, 53}, + }, + /* 17 */ + { + {0x8003, 47}, + {0x8006, 47}, + {0x800a, 47}, + {0x800f, 47}, + {0x8018, 47}, + {0x801f, 47}, + {0x8029, 47}, + {0xc038, 47}, + {0x8003, 51}, + {0x8006, 51}, + {0x800a, 51}, + {0x800f, 51}, + {0x8018, 51}, + {0x801f, 51}, + {0x8029, 51}, + {0xc038, 51}, + }, + /* 18 */ + { + {0x8003, 52}, + {0x8006, 52}, + {0x800a, 52}, + {0x800f, 52}, + {0x8018, 52}, + {0x801f, 52}, + {0x8029, 52}, + {0xc038, 52}, + {0x8003, 53}, + {0x8006, 53}, + {0x800a, 53}, + {0x800f, 53}, + {0x8018, 53}, + {0x801f, 53}, + {0x8029, 53}, + {0xc038, 53}, + }, + /* 19 */ + { + {0x8002, 54}, + {0x8009, 54}, + {0x8017, 54}, + {0xc028, 54}, + {0x8002, 55}, + {0x8009, 55}, + {0x8017, 55}, + {0xc028, 55}, + {0x8002, 56}, + {0x8009, 56}, + {0x8017, 56}, + {0xc028, 56}, + {0x8002, 57}, + {0x8009, 57}, + {0x8017, 57}, + {0xc028, 57}, + }, + /* 20 */ + { + {0x8003, 54}, + {0x8006, 54}, + {0x800a, 54}, + {0x800f, 54}, + {0x8018, 54}, + {0x801f, 54}, + {0x8029, 54}, + {0xc038, 54}, + {0x8003, 55}, + {0x8006, 55}, + {0x800a, 55}, + {0x800f, 55}, + {0x8018, 55}, + {0x801f, 55}, + {0x8029, 55}, + {0xc038, 55}, + }, + /* 21 */ + { + {0x8003, 56}, + {0x8006, 56}, + {0x800a, 56}, + {0x800f, 56}, + {0x8018, 56}, + {0x801f, 56}, + {0x8029, 56}, + {0xc038, 56}, + {0x8003, 57}, + {0x8006, 57}, + {0x800a, 57}, + {0x800f, 57}, + {0x8018, 57}, + {0x801f, 57}, + {0x8029, 57}, + {0xc038, 57}, + }, + /* 22 */ + { + {0x1a, 0}, + {0x1b, 0}, + {0x1d, 0}, + {0x1e, 0}, + {0x21, 0}, + {0x22, 0}, + {0x24, 0}, + {0x25, 0}, + {0x2b, 0}, + {0x2e, 0}, + {0x32, 0}, + {0x35, 0}, + {0x3a, 0}, + {0x3d, 0}, + {0x41, 0}, + {0x4044, 0}, + }, + /* 23 */ + { + {0xc000, 61}, + {0xc000, 65}, + {0xc000, 95}, + {0xc000, 98}, + {0xc000, 100}, + {0xc000, 102}, + {0xc000, 103}, + {0xc000, 104}, + {0xc000, 108}, + {0xc000, 109}, + {0xc000, 110}, + {0xc000, 112}, + {0xc000, 114}, + {0xc000, 117}, + {0x26, 0}, + {0x27, 0}, + }, + /* 24 */ + { + {0x8001, 61}, + {0xc016, 61}, + {0x8001, 65}, + {0xc016, 65}, + {0x8001, 95}, + {0xc016, 95}, + {0x8001, 98}, + {0xc016, 98}, + {0x8001, 100}, + {0xc016, 100}, + {0x8001, 102}, + {0xc016, 102}, + {0x8001, 103}, + {0xc016, 103}, + {0x8001, 104}, + {0xc016, 104}, + }, + /* 25 */ + { + {0x8002, 61}, + {0x8009, 61}, + {0x8017, 61}, + {0xc028, 61}, + {0x8002, 65}, + {0x8009, 65}, + {0x8017, 65}, + {0xc028, 65}, + {0x8002, 95}, + {0x8009, 95}, + {0x8017, 95}, + {0xc028, 95}, + {0x8002, 98}, + {0x8009, 98}, + {0x8017, 98}, + {0xc028, 98}, + }, + /* 26 */ + { + {0x8003, 61}, + {0x8006, 61}, + {0x800a, 61}, + {0x800f, 61}, + {0x8018, 61}, + {0x801f, 61}, + {0x8029, 61}, + {0xc038, 61}, + {0x8003, 65}, + {0x8006, 65}, + {0x800a, 65}, + {0x800f, 65}, + {0x8018, 65}, + {0x801f, 65}, + {0x8029, 65}, + {0xc038, 65}, + }, + /* 27 */ + { + {0x8003, 95}, + {0x8006, 95}, + {0x800a, 95}, + {0x800f, 95}, + {0x8018, 95}, + {0x801f, 95}, + {0x8029, 95}, + {0xc038, 95}, + {0x8003, 98}, + {0x8006, 98}, + {0x800a, 98}, + {0x800f, 98}, + {0x8018, 98}, + {0x801f, 98}, + {0x8029, 98}, + {0xc038, 98}, + }, + /* 28 */ + { + {0x8002, 100}, + {0x8009, 100}, + {0x8017, 100}, + {0xc028, 100}, + {0x8002, 102}, + {0x8009, 102}, + {0x8017, 102}, + {0xc028, 102}, + {0x8002, 103}, + {0x8009, 103}, + {0x8017, 103}, + {0xc028, 103}, + {0x8002, 104}, + {0x8009, 104}, + {0x8017, 104}, + {0xc028, 104}, + }, + /* 29 */ + { + {0x8003, 100}, + {0x8006, 100}, + {0x800a, 100}, + {0x800f, 100}, + {0x8018, 100}, + {0x801f, 100}, + {0x8029, 100}, + {0xc038, 100}, + {0x8003, 102}, + {0x8006, 102}, + {0x800a, 102}, + {0x800f, 102}, + {0x8018, 102}, + {0x801f, 102}, + {0x8029, 102}, + {0xc038, 102}, + }, + /* 30 */ + { + {0x8003, 103}, + {0x8006, 103}, + {0x800a, 103}, + {0x800f, 103}, + {0x8018, 103}, + {0x801f, 103}, + {0x8029, 103}, + {0xc038, 103}, + {0x8003, 104}, + {0x8006, 104}, + {0x800a, 104}, + {0x800f, 104}, + {0x8018, 104}, + {0x801f, 104}, + {0x8029, 104}, + {0xc038, 104}, + }, + /* 31 */ + { + {0x8001, 108}, + {0xc016, 108}, + {0x8001, 109}, + {0xc016, 109}, + {0x8001, 110}, + {0xc016, 110}, + {0x8001, 112}, + {0xc016, 112}, + {0x8001, 114}, + {0xc016, 114}, + {0x8001, 117}, + {0xc016, 117}, + {0xc000, 58}, + {0xc000, 66}, + {0xc000, 67}, + {0xc000, 68}, + }, + /* 32 */ + { + {0x8002, 108}, + {0x8009, 108}, + {0x8017, 108}, + {0xc028, 108}, + {0x8002, 109}, + {0x8009, 109}, + {0x8017, 109}, + {0xc028, 109}, + {0x8002, 110}, + {0x8009, 110}, + {0x8017, 110}, + {0xc028, 110}, + {0x8002, 112}, + {0x8009, 112}, + {0x8017, 112}, + {0xc028, 112}, + }, + /* 33 */ + { + {0x8003, 108}, + {0x8006, 108}, + {0x800a, 108}, + {0x800f, 108}, + {0x8018, 108}, + {0x801f, 108}, + {0x8029, 108}, + {0xc038, 108}, + {0x8003, 109}, + {0x8006, 109}, + {0x800a, 109}, + {0x800f, 109}, + {0x8018, 109}, + {0x801f, 109}, + {0x8029, 109}, + {0xc038, 109}, + }, + /* 34 */ + { + {0x8003, 110}, + {0x8006, 110}, + {0x800a, 110}, + {0x800f, 110}, + {0x8018, 110}, + {0x801f, 110}, + {0x8029, 110}, + {0xc038, 110}, + {0x8003, 112}, + {0x8006, 112}, + {0x800a, 112}, + {0x800f, 112}, + {0x8018, 112}, + {0x801f, 112}, + {0x8029, 112}, + {0xc038, 112}, + }, + /* 35 */ + { + {0x8002, 114}, + {0x8009, 114}, + {0x8017, 114}, + {0xc028, 114}, + {0x8002, 117}, + {0x8009, 117}, + {0x8017, 117}, + {0xc028, 117}, + {0x8001, 58}, + {0xc016, 58}, + {0x8001, 66}, + {0xc016, 66}, + {0x8001, 67}, + {0xc016, 67}, + {0x8001, 68}, + {0xc016, 68}, + }, + /* 36 */ + { + {0x8003, 114}, + {0x8006, 114}, + {0x800a, 114}, + {0x800f, 114}, + {0x8018, 114}, + {0x801f, 114}, + {0x8029, 114}, + {0xc038, 114}, + {0x8003, 117}, + {0x8006, 117}, + {0x800a, 117}, + {0x800f, 117}, + {0x8018, 117}, + {0x801f, 117}, + {0x8029, 117}, + {0xc038, 117}, + }, + /* 37 */ + { + {0x8002, 58}, + {0x8009, 58}, + {0x8017, 58}, + {0xc028, 58}, + {0x8002, 66}, + {0x8009, 66}, + {0x8017, 66}, + {0xc028, 66}, + {0x8002, 67}, + {0x8009, 67}, + {0x8017, 67}, + {0xc028, 67}, + {0x8002, 68}, + {0x8009, 68}, + {0x8017, 68}, + {0xc028, 68}, + }, + /* 38 */ + { + {0x8003, 58}, + {0x8006, 58}, + {0x800a, 58}, + {0x800f, 58}, + {0x8018, 58}, + {0x801f, 58}, + {0x8029, 58}, + {0xc038, 58}, + {0x8003, 66}, + {0x8006, 66}, + {0x800a, 66}, + {0x800f, 66}, + {0x8018, 66}, + {0x801f, 66}, + {0x8029, 66}, + {0xc038, 66}, + }, + /* 39 */ + { + {0x8003, 67}, + {0x8006, 67}, + {0x800a, 67}, + {0x800f, 67}, + {0x8018, 67}, + {0x801f, 67}, + {0x8029, 67}, + {0xc038, 67}, + {0x8003, 68}, + {0x8006, 68}, + {0x800a, 68}, + {0x800f, 68}, + {0x8018, 68}, + {0x801f, 68}, + {0x8029, 68}, + {0xc038, 68}, + }, + /* 40 */ + { + {0x2c, 0}, + {0x2d, 0}, + {0x2f, 0}, + {0x30, 0}, + {0x33, 0}, + {0x34, 0}, + {0x36, 0}, + {0x37, 0}, + {0x3b, 0}, + {0x3c, 0}, + {0x3e, 0}, + {0x3f, 0}, + {0x42, 0}, + {0x43, 0}, + {0x45, 0}, + {0x4048, 0}, + }, + /* 41 */ + { + {0xc000, 69}, + {0xc000, 70}, + {0xc000, 71}, + {0xc000, 72}, + {0xc000, 73}, + {0xc000, 74}, + {0xc000, 75}, + {0xc000, 76}, + {0xc000, 77}, + {0xc000, 78}, + {0xc000, 79}, + {0xc000, 80}, + {0xc000, 81}, + {0xc000, 82}, + {0xc000, 83}, + {0xc000, 84}, + }, + /* 42 */ + { + {0x8001, 69}, + {0xc016, 69}, + {0x8001, 70}, + {0xc016, 70}, + {0x8001, 71}, + {0xc016, 71}, + {0x8001, 72}, + {0xc016, 72}, + {0x8001, 73}, + {0xc016, 73}, + {0x8001, 74}, + {0xc016, 74}, + {0x8001, 75}, + {0xc016, 75}, + {0x8001, 76}, + {0xc016, 76}, + }, + /* 43 */ + { + {0x8002, 69}, + {0x8009, 69}, + {0x8017, 69}, + {0xc028, 69}, + {0x8002, 70}, + {0x8009, 70}, + {0x8017, 70}, + {0xc028, 70}, + {0x8002, 71}, + {0x8009, 71}, + {0x8017, 71}, + {0xc028, 71}, + {0x8002, 72}, + {0x8009, 72}, + {0x8017, 72}, + {0xc028, 72}, + }, + /* 44 */ + { + {0x8003, 69}, + {0x8006, 69}, + {0x800a, 69}, + {0x800f, 69}, + {0x8018, 69}, + {0x801f, 69}, + {0x8029, 69}, + {0xc038, 69}, + {0x8003, 70}, + {0x8006, 70}, + {0x800a, 70}, + {0x800f, 70}, + {0x8018, 70}, + {0x801f, 70}, + {0x8029, 70}, + {0xc038, 70}, + }, + /* 45 */ + { + {0x8003, 71}, + {0x8006, 71}, + {0x800a, 71}, + {0x800f, 71}, + {0x8018, 71}, + {0x801f, 71}, + {0x8029, 71}, + {0xc038, 71}, + {0x8003, 72}, + {0x8006, 72}, + {0x800a, 72}, + {0x800f, 72}, + {0x8018, 72}, + {0x801f, 72}, + {0x8029, 72}, + {0xc038, 72}, + }, + /* 46 */ + { + {0x8002, 73}, + {0x8009, 73}, + {0x8017, 73}, + {0xc028, 73}, + {0x8002, 74}, + {0x8009, 74}, + {0x8017, 74}, + {0xc028, 74}, + {0x8002, 75}, + {0x8009, 75}, + {0x8017, 75}, + {0xc028, 75}, + {0x8002, 76}, + {0x8009, 76}, + {0x8017, 76}, + {0xc028, 76}, + }, + /* 47 */ + { + {0x8003, 73}, + {0x8006, 73}, + {0x800a, 73}, + {0x800f, 73}, + {0x8018, 73}, + {0x801f, 73}, + {0x8029, 73}, + {0xc038, 73}, + {0x8003, 74}, + {0x8006, 74}, + {0x800a, 74}, + {0x800f, 74}, + {0x8018, 74}, + {0x801f, 74}, + {0x8029, 74}, + {0xc038, 74}, + }, + /* 48 */ + { + {0x8003, 75}, + {0x8006, 75}, + {0x800a, 75}, + {0x800f, 75}, + {0x8018, 75}, + {0x801f, 75}, + {0x8029, 75}, + {0xc038, 75}, + {0x8003, 76}, + {0x8006, 76}, + {0x800a, 76}, + {0x800f, 76}, + {0x8018, 76}, + {0x801f, 76}, + {0x8029, 76}, + {0xc038, 76}, + }, + /* 49 */ + { + {0x8001, 77}, + {0xc016, 77}, + {0x8001, 78}, + {0xc016, 78}, + {0x8001, 79}, + {0xc016, 79}, + {0x8001, 80}, + {0xc016, 80}, + {0x8001, 81}, + {0xc016, 81}, + {0x8001, 82}, + {0xc016, 82}, + {0x8001, 83}, + {0xc016, 83}, + {0x8001, 84}, + {0xc016, 84}, + }, + /* 50 */ + { + {0x8002, 77}, + {0x8009, 77}, + {0x8017, 77}, + {0xc028, 77}, + {0x8002, 78}, + {0x8009, 78}, + {0x8017, 78}, + {0xc028, 78}, + {0x8002, 79}, + {0x8009, 79}, + {0x8017, 79}, + {0xc028, 79}, + {0x8002, 80}, + {0x8009, 80}, + {0x8017, 80}, + {0xc028, 80}, + }, + /* 51 */ + { + {0x8003, 77}, + {0x8006, 77}, + {0x800a, 77}, + {0x800f, 77}, + {0x8018, 77}, + {0x801f, 77}, + {0x8029, 77}, + {0xc038, 77}, + {0x8003, 78}, + {0x8006, 78}, + {0x800a, 78}, + {0x800f, 78}, + {0x8018, 78}, + {0x801f, 78}, + {0x8029, 78}, + {0xc038, 78}, + }, + /* 52 */ + { + {0x8003, 79}, + {0x8006, 79}, + {0x800a, 79}, + {0x800f, 79}, + {0x8018, 79}, + {0x801f, 79}, + {0x8029, 79}, + {0xc038, 79}, + {0x8003, 80}, + {0x8006, 80}, + {0x800a, 80}, + {0x800f, 80}, + {0x8018, 80}, + {0x801f, 80}, + {0x8029, 80}, + {0xc038, 80}, + }, + /* 53 */ + { + {0x8002, 81}, + {0x8009, 81}, + {0x8017, 81}, + {0xc028, 81}, + {0x8002, 82}, + {0x8009, 82}, + {0x8017, 82}, + {0xc028, 82}, + {0x8002, 83}, + {0x8009, 83}, + {0x8017, 83}, + {0xc028, 83}, + {0x8002, 84}, + {0x8009, 84}, + {0x8017, 84}, + {0xc028, 84}, + }, + /* 54 */ + { + {0x8003, 81}, + {0x8006, 81}, + {0x800a, 81}, + {0x800f, 81}, + {0x8018, 81}, + {0x801f, 81}, + {0x8029, 81}, + {0xc038, 81}, + {0x8003, 82}, + {0x8006, 82}, + {0x800a, 82}, + {0x800f, 82}, + {0x8018, 82}, + {0x801f, 82}, + {0x8029, 82}, + {0xc038, 82}, + }, + /* 55 */ + { + {0x8003, 83}, + {0x8006, 83}, + {0x800a, 83}, + {0x800f, 83}, + {0x8018, 83}, + {0x801f, 83}, + {0x8029, 83}, + {0xc038, 83}, + {0x8003, 84}, + {0x8006, 84}, + {0x800a, 84}, + {0x800f, 84}, + {0x8018, 84}, + {0x801f, 84}, + {0x8029, 84}, + {0xc038, 84}, + }, + /* 56 */ + { + {0xc000, 85}, + {0xc000, 86}, + {0xc000, 87}, + {0xc000, 89}, + {0xc000, 106}, + {0xc000, 107}, + {0xc000, 113}, + {0xc000, 118}, + {0xc000, 119}, + {0xc000, 120}, + {0xc000, 121}, + {0xc000, 122}, + {0x46, 0}, + {0x47, 0}, + {0x49, 0}, + {0x404a, 0}, + }, + /* 57 */ + { + {0x8001, 85}, + {0xc016, 85}, + {0x8001, 86}, + {0xc016, 86}, + {0x8001, 87}, + {0xc016, 87}, + {0x8001, 89}, + {0xc016, 89}, + {0x8001, 106}, + {0xc016, 106}, + {0x8001, 107}, + {0xc016, 107}, + {0x8001, 113}, + {0xc016, 113}, + {0x8001, 118}, + {0xc016, 118}, + }, + /* 58 */ + { + {0x8002, 85}, + {0x8009, 85}, + {0x8017, 85}, + {0xc028, 85}, + {0x8002, 86}, + {0x8009, 86}, + {0x8017, 86}, + {0xc028, 86}, + {0x8002, 87}, + {0x8009, 87}, + {0x8017, 87}, + {0xc028, 87}, + {0x8002, 89}, + {0x8009, 89}, + {0x8017, 89}, + {0xc028, 89}, + }, + /* 59 */ + { + {0x8003, 85}, + {0x8006, 85}, + {0x800a, 85}, + {0x800f, 85}, + {0x8018, 85}, + {0x801f, 85}, + {0x8029, 85}, + {0xc038, 85}, + {0x8003, 86}, + {0x8006, 86}, + {0x800a, 86}, + {0x800f, 86}, + {0x8018, 86}, + {0x801f, 86}, + {0x8029, 86}, + {0xc038, 86}, + }, + /* 60 */ + { + {0x8003, 87}, + {0x8006, 87}, + {0x800a, 87}, + {0x800f, 87}, + {0x8018, 87}, + {0x801f, 87}, + {0x8029, 87}, + {0xc038, 87}, + {0x8003, 89}, + {0x8006, 89}, + {0x800a, 89}, + {0x800f, 89}, + {0x8018, 89}, + {0x801f, 89}, + {0x8029, 89}, + {0xc038, 89}, + }, + /* 61 */ + { + {0x8002, 106}, + {0x8009, 106}, + {0x8017, 106}, + {0xc028, 106}, + {0x8002, 107}, + {0x8009, 107}, + {0x8017, 107}, + {0xc028, 107}, + {0x8002, 113}, + {0x8009, 113}, + {0x8017, 113}, + {0xc028, 113}, + {0x8002, 118}, + {0x8009, 118}, + {0x8017, 118}, + {0xc028, 118}, + }, + /* 62 */ + { + {0x8003, 106}, + {0x8006, 106}, + {0x800a, 106}, + {0x800f, 106}, + {0x8018, 106}, + {0x801f, 106}, + {0x8029, 106}, + {0xc038, 106}, + {0x8003, 107}, + {0x8006, 107}, + {0x800a, 107}, + {0x800f, 107}, + {0x8018, 107}, + {0x801f, 107}, + {0x8029, 107}, + {0xc038, 107}, + }, + /* 63 */ + { + {0x8003, 113}, + {0x8006, 113}, + {0x800a, 113}, + {0x800f, 113}, + {0x8018, 113}, + {0x801f, 113}, + {0x8029, 113}, + {0xc038, 113}, + {0x8003, 118}, + {0x8006, 118}, + {0x800a, 118}, + {0x800f, 118}, + {0x8018, 118}, + {0x801f, 118}, + {0x8029, 118}, + {0xc038, 118}, + }, + /* 64 */ + { + {0x8001, 119}, + {0xc016, 119}, + {0x8001, 120}, + {0xc016, 120}, + {0x8001, 121}, + {0xc016, 121}, + {0x8001, 122}, + {0xc016, 122}, + {0xc000, 38}, + {0xc000, 42}, + {0xc000, 44}, + {0xc000, 59}, + {0xc000, 88}, + {0xc000, 90}, + {0x4b, 0}, + {0x4e, 0}, + }, + /* 65 */ + { + {0x8002, 119}, + {0x8009, 119}, + {0x8017, 119}, + {0xc028, 119}, + {0x8002, 120}, + {0x8009, 120}, + {0x8017, 120}, + {0xc028, 120}, + {0x8002, 121}, + {0x8009, 121}, + {0x8017, 121}, + {0xc028, 121}, + {0x8002, 122}, + {0x8009, 122}, + {0x8017, 122}, + {0xc028, 122}, + }, + /* 66 */ + { + {0x8003, 119}, + {0x8006, 119}, + {0x800a, 119}, + {0x800f, 119}, + {0x8018, 119}, + {0x801f, 119}, + {0x8029, 119}, + {0xc038, 119}, + {0x8003, 120}, + {0x8006, 120}, + {0x800a, 120}, + {0x800f, 120}, + {0x8018, 120}, + {0x801f, 120}, + {0x8029, 120}, + {0xc038, 120}, + }, + /* 67 */ + { + {0x8003, 121}, + {0x8006, 121}, + {0x800a, 121}, + {0x800f, 121}, + {0x8018, 121}, + {0x801f, 121}, + {0x8029, 121}, + {0xc038, 121}, + {0x8003, 122}, + {0x8006, 122}, + {0x800a, 122}, + {0x800f, 122}, + {0x8018, 122}, + {0x801f, 122}, + {0x8029, 122}, + {0xc038, 122}, + }, + /* 68 */ + { + {0x8001, 38}, + {0xc016, 38}, + {0x8001, 42}, + {0xc016, 42}, + {0x8001, 44}, + {0xc016, 44}, + {0x8001, 59}, + {0xc016, 59}, + {0x8001, 88}, + {0xc016, 88}, + {0x8001, 90}, + {0xc016, 90}, + {0x4c, 0}, + {0x4d, 0}, + {0x4f, 0}, + {0x51, 0}, + }, + /* 69 */ + { + {0x8002, 38}, + {0x8009, 38}, + {0x8017, 38}, + {0xc028, 38}, + {0x8002, 42}, + {0x8009, 42}, + {0x8017, 42}, + {0xc028, 42}, + {0x8002, 44}, + {0x8009, 44}, + {0x8017, 44}, + {0xc028, 44}, + {0x8002, 59}, + {0x8009, 59}, + {0x8017, 59}, + {0xc028, 59}, + }, + /* 70 */ + { + {0x8003, 38}, + {0x8006, 38}, + {0x800a, 38}, + {0x800f, 38}, + {0x8018, 38}, + {0x801f, 38}, + {0x8029, 38}, + {0xc038, 38}, + {0x8003, 42}, + {0x8006, 42}, + {0x800a, 42}, + {0x800f, 42}, + {0x8018, 42}, + {0x801f, 42}, + {0x8029, 42}, + {0xc038, 42}, + }, + /* 71 */ + { + {0x8003, 44}, + {0x8006, 44}, + {0x800a, 44}, + {0x800f, 44}, + {0x8018, 44}, + {0x801f, 44}, + {0x8029, 44}, + {0xc038, 44}, + {0x8003, 59}, + {0x8006, 59}, + {0x800a, 59}, + {0x800f, 59}, + {0x8018, 59}, + {0x801f, 59}, + {0x8029, 59}, + {0xc038, 59}, + }, + /* 72 */ + { + {0x8002, 88}, + {0x8009, 88}, + {0x8017, 88}, + {0xc028, 88}, + {0x8002, 90}, + {0x8009, 90}, + {0x8017, 90}, + {0xc028, 90}, + {0xc000, 33}, + {0xc000, 34}, + {0xc000, 40}, + {0xc000, 41}, + {0xc000, 63}, + {0x50, 0}, + {0x52, 0}, + {0x54, 0}, + }, + /* 73 */ + { + {0x8003, 88}, + {0x8006, 88}, + {0x800a, 88}, + {0x800f, 88}, + {0x8018, 88}, + {0x801f, 88}, + {0x8029, 88}, + {0xc038, 88}, + {0x8003, 90}, + {0x8006, 90}, + {0x800a, 90}, + {0x800f, 90}, + {0x8018, 90}, + {0x801f, 90}, + {0x8029, 90}, + {0xc038, 90}, + }, + /* 74 */ + { + {0x8001, 33}, + {0xc016, 33}, + {0x8001, 34}, + {0xc016, 34}, + {0x8001, 40}, + {0xc016, 40}, + {0x8001, 41}, + {0xc016, 41}, + {0x8001, 63}, + {0xc016, 63}, + {0xc000, 39}, + {0xc000, 43}, + {0xc000, 124}, + {0x53, 0}, + {0x55, 0}, + {0x58, 0}, + }, + /* 75 */ + { + {0x8002, 33}, + {0x8009, 33}, + {0x8017, 33}, + {0xc028, 33}, + {0x8002, 34}, + {0x8009, 34}, + {0x8017, 34}, + {0xc028, 34}, + {0x8002, 40}, + {0x8009, 40}, + {0x8017, 40}, + {0xc028, 40}, + {0x8002, 41}, + {0x8009, 41}, + {0x8017, 41}, + {0xc028, 41}, + }, + /* 76 */ + { + {0x8003, 33}, + {0x8006, 33}, + {0x800a, 33}, + {0x800f, 33}, + {0x8018, 33}, + {0x801f, 33}, + {0x8029, 33}, + {0xc038, 33}, + {0x8003, 34}, + {0x8006, 34}, + {0x800a, 34}, + {0x800f, 34}, + {0x8018, 34}, + {0x801f, 34}, + {0x8029, 34}, + {0xc038, 34}, + }, + /* 77 */ + { + {0x8003, 40}, + {0x8006, 40}, + {0x800a, 40}, + {0x800f, 40}, + {0x8018, 40}, + {0x801f, 40}, + {0x8029, 40}, + {0xc038, 40}, + {0x8003, 41}, + {0x8006, 41}, + {0x800a, 41}, + {0x800f, 41}, + {0x8018, 41}, + {0x801f, 41}, + {0x8029, 41}, + {0xc038, 41}, + }, + /* 78 */ + { + {0x8002, 63}, + {0x8009, 63}, + {0x8017, 63}, + {0xc028, 63}, + {0x8001, 39}, + {0xc016, 39}, + {0x8001, 43}, + {0xc016, 43}, + {0x8001, 124}, + {0xc016, 124}, + {0xc000, 35}, + {0xc000, 62}, + {0x56, 0}, + {0x57, 0}, + {0x59, 0}, + {0x5a, 0}, + }, + /* 79 */ + { + {0x8003, 63}, + {0x8006, 63}, + {0x800a, 63}, + {0x800f, 63}, + {0x8018, 63}, + {0x801f, 63}, + {0x8029, 63}, + {0xc038, 63}, + {0x8002, 39}, + {0x8009, 39}, + {0x8017, 39}, + {0xc028, 39}, + {0x8002, 43}, + {0x8009, 43}, + {0x8017, 43}, + {0xc028, 43}, + }, + /* 80 */ + { + {0x8003, 39}, + {0x8006, 39}, + {0x800a, 39}, + {0x800f, 39}, + {0x8018, 39}, + {0x801f, 39}, + {0x8029, 39}, + {0xc038, 39}, + {0x8003, 43}, + {0x8006, 43}, + {0x800a, 43}, + {0x800f, 43}, + {0x8018, 43}, + {0x801f, 43}, + {0x8029, 43}, + {0xc038, 43}, + }, + /* 81 */ + { + {0x8002, 124}, + {0x8009, 124}, + {0x8017, 124}, + {0xc028, 124}, + {0x8001, 35}, + {0xc016, 35}, + {0x8001, 62}, + {0xc016, 62}, + {0xc000, 0}, + {0xc000, 36}, + {0xc000, 64}, + {0xc000, 91}, + {0xc000, 93}, + {0xc000, 126}, + {0x5b, 0}, + {0x5c, 0}, + }, + /* 82 */ + { + {0x8003, 124}, + {0x8006, 124}, + {0x800a, 124}, + {0x800f, 124}, + {0x8018, 124}, + {0x801f, 124}, + {0x8029, 124}, + {0xc038, 124}, + {0x8002, 35}, + {0x8009, 35}, + {0x8017, 35}, + {0xc028, 35}, + {0x8002, 62}, + {0x8009, 62}, + {0x8017, 62}, + {0xc028, 62}, + }, + /* 83 */ + { + {0x8003, 35}, + {0x8006, 35}, + {0x800a, 35}, + {0x800f, 35}, + {0x8018, 35}, + {0x801f, 35}, + {0x8029, 35}, + {0xc038, 35}, + {0x8003, 62}, + {0x8006, 62}, + {0x800a, 62}, + {0x800f, 62}, + {0x8018, 62}, + {0x801f, 62}, + {0x8029, 62}, + {0xc038, 62}, + }, + /* 84 */ + { + {0x8001, 0}, + {0xc016, 0}, + {0x8001, 36}, + {0xc016, 36}, + {0x8001, 64}, + {0xc016, 64}, + {0x8001, 91}, + {0xc016, 91}, + {0x8001, 93}, + {0xc016, 93}, + {0x8001, 126}, + {0xc016, 126}, + {0xc000, 94}, + {0xc000, 125}, + {0x5d, 0}, + {0x5e, 0}, + }, + /* 85 */ + { + {0x8002, 0}, + {0x8009, 0}, + {0x8017, 0}, + {0xc028, 0}, + {0x8002, 36}, + {0x8009, 36}, + {0x8017, 36}, + {0xc028, 36}, + {0x8002, 64}, + {0x8009, 64}, + {0x8017, 64}, + {0xc028, 64}, + {0x8002, 91}, + {0x8009, 91}, + {0x8017, 91}, + {0xc028, 91}, + }, + /* 86 */ + { + {0x8003, 0}, + {0x8006, 0}, + {0x800a, 0}, + {0x800f, 0}, + {0x8018, 0}, + {0x801f, 0}, + {0x8029, 0}, + {0xc038, 0}, + {0x8003, 36}, + {0x8006, 36}, + {0x800a, 36}, + {0x800f, 36}, + {0x8018, 36}, + {0x801f, 36}, + {0x8029, 36}, + {0xc038, 36}, + }, + /* 87 */ + { + {0x8003, 64}, + {0x8006, 64}, + {0x800a, 64}, + {0x800f, 64}, + {0x8018, 64}, + {0x801f, 64}, + {0x8029, 64}, + {0xc038, 64}, + {0x8003, 91}, + {0x8006, 91}, + {0x800a, 91}, + {0x800f, 91}, + {0x8018, 91}, + {0x801f, 91}, + {0x8029, 91}, + {0xc038, 91}, + }, + /* 88 */ + { + {0x8002, 93}, + {0x8009, 93}, + {0x8017, 93}, + {0xc028, 93}, + {0x8002, 126}, + {0x8009, 126}, + {0x8017, 126}, + {0xc028, 126}, + {0x8001, 94}, + {0xc016, 94}, + {0x8001, 125}, + {0xc016, 125}, + {0xc000, 60}, + {0xc000, 96}, + {0xc000, 123}, + {0x5f, 0}, + }, + /* 89 */ + { + {0x8003, 93}, + {0x8006, 93}, + {0x800a, 93}, + {0x800f, 93}, + {0x8018, 93}, + {0x801f, 93}, + {0x8029, 93}, + {0xc038, 93}, + {0x8003, 126}, + {0x8006, 126}, + {0x800a, 126}, + {0x800f, 126}, + {0x8018, 126}, + {0x801f, 126}, + {0x8029, 126}, + {0xc038, 126}, + }, + /* 90 */ + { + {0x8002, 94}, + {0x8009, 94}, + {0x8017, 94}, + {0xc028, 94}, + {0x8002, 125}, + {0x8009, 125}, + {0x8017, 125}, + {0xc028, 125}, + {0x8001, 60}, + {0xc016, 60}, + {0x8001, 96}, + {0xc016, 96}, + {0x8001, 123}, + {0xc016, 123}, + {0x60, 0}, + {0x6e, 0}, + }, + /* 91 */ + { + {0x8003, 94}, + {0x8006, 94}, + {0x800a, 94}, + {0x800f, 94}, + {0x8018, 94}, + {0x801f, 94}, + {0x8029, 94}, + {0xc038, 94}, + {0x8003, 125}, + {0x8006, 125}, + {0x800a, 125}, + {0x800f, 125}, + {0x8018, 125}, + {0x801f, 125}, + {0x8029, 125}, + {0xc038, 125}, + }, + /* 92 */ + { + {0x8002, 60}, + {0x8009, 60}, + {0x8017, 60}, + {0xc028, 60}, + {0x8002, 96}, + {0x8009, 96}, + {0x8017, 96}, + {0xc028, 96}, + {0x8002, 123}, + {0x8009, 123}, + {0x8017, 123}, + {0xc028, 123}, + {0x61, 0}, + {0x65, 0}, + {0x6f, 0}, + {0x85, 0}, + }, + /* 93 */ + { + {0x8003, 60}, + {0x8006, 60}, + {0x800a, 60}, + {0x800f, 60}, + {0x8018, 60}, + {0x801f, 60}, + {0x8029, 60}, + {0xc038, 60}, + {0x8003, 96}, + {0x8006, 96}, + {0x800a, 96}, + {0x800f, 96}, + {0x8018, 96}, + {0x801f, 96}, + {0x8029, 96}, + {0xc038, 96}, + }, + /* 94 */ + { + {0x8003, 123}, + {0x8006, 123}, + {0x800a, 123}, + {0x800f, 123}, + {0x8018, 123}, + {0x801f, 123}, + {0x8029, 123}, + {0xc038, 123}, + {0x62, 0}, + {0x63, 0}, + {0x66, 0}, + {0x69, 0}, + {0x70, 0}, + {0x77, 0}, + {0x86, 0}, + {0x99, 0}, + }, + /* 95 */ + { + {0xc000, 92}, + {0xc000, 195}, + {0xc000, 208}, + {0x64, 0}, + {0x67, 0}, + {0x68, 0}, + {0x6a, 0}, + {0x6b, 0}, + {0x71, 0}, + {0x74, 0}, + {0x78, 0}, + {0x7e, 0}, + {0x87, 0}, + {0x8e, 0}, + {0x9a, 0}, + {0xa9, 0}, + }, + /* 96 */ + { + {0x8001, 92}, + {0xc016, 92}, + {0x8001, 195}, + {0xc016, 195}, + {0x8001, 208}, + {0xc016, 208}, + {0xc000, 128}, + {0xc000, 130}, + {0xc000, 131}, + {0xc000, 162}, + {0xc000, 184}, + {0xc000, 194}, + {0xc000, 224}, + {0xc000, 226}, + {0x6c, 0}, + {0x6d, 0}, + }, + /* 97 */ + { + {0x8002, 92}, + {0x8009, 92}, + {0x8017, 92}, + {0xc028, 92}, + {0x8002, 195}, + {0x8009, 195}, + {0x8017, 195}, + {0xc028, 195}, + {0x8002, 208}, + {0x8009, 208}, + {0x8017, 208}, + {0xc028, 208}, + {0x8001, 128}, + {0xc016, 128}, + {0x8001, 130}, + {0xc016, 130}, + }, + /* 98 */ + { + {0x8003, 92}, + {0x8006, 92}, + {0x800a, 92}, + {0x800f, 92}, + {0x8018, 92}, + {0x801f, 92}, + {0x8029, 92}, + {0xc038, 92}, + {0x8003, 195}, + {0x8006, 195}, + {0x800a, 195}, + {0x800f, 195}, + {0x8018, 195}, + {0x801f, 195}, + {0x8029, 195}, + {0xc038, 195}, + }, + /* 99 */ + { + {0x8003, 208}, + {0x8006, 208}, + {0x800a, 208}, + {0x800f, 208}, + {0x8018, 208}, + {0x801f, 208}, + {0x8029, 208}, + {0xc038, 208}, + {0x8002, 128}, + {0x8009, 128}, + {0x8017, 128}, + {0xc028, 128}, + {0x8002, 130}, + {0x8009, 130}, + {0x8017, 130}, + {0xc028, 130}, + }, + /* 100 */ + { + {0x8003, 128}, + {0x8006, 128}, + {0x800a, 128}, + {0x800f, 128}, + {0x8018, 128}, + {0x801f, 128}, + {0x8029, 128}, + {0xc038, 128}, + {0x8003, 130}, + {0x8006, 130}, + {0x800a, 130}, + {0x800f, 130}, + {0x8018, 130}, + {0x801f, 130}, + {0x8029, 130}, + {0xc038, 130}, + }, + /* 101 */ + { + {0x8001, 131}, + {0xc016, 131}, + {0x8001, 162}, + {0xc016, 162}, + {0x8001, 184}, + {0xc016, 184}, + {0x8001, 194}, + {0xc016, 194}, + {0x8001, 224}, + {0xc016, 224}, + {0x8001, 226}, + {0xc016, 226}, + {0xc000, 153}, + {0xc000, 161}, + {0xc000, 167}, + {0xc000, 172}, + }, + /* 102 */ + { + {0x8002, 131}, + {0x8009, 131}, + {0x8017, 131}, + {0xc028, 131}, + {0x8002, 162}, + {0x8009, 162}, + {0x8017, 162}, + {0xc028, 162}, + {0x8002, 184}, + {0x8009, 184}, + {0x8017, 184}, + {0xc028, 184}, + {0x8002, 194}, + {0x8009, 194}, + {0x8017, 194}, + {0xc028, 194}, + }, + /* 103 */ + { + {0x8003, 131}, + {0x8006, 131}, + {0x800a, 131}, + {0x800f, 131}, + {0x8018, 131}, + {0x801f, 131}, + {0x8029, 131}, + {0xc038, 131}, + {0x8003, 162}, + {0x8006, 162}, + {0x800a, 162}, + {0x800f, 162}, + {0x8018, 162}, + {0x801f, 162}, + {0x8029, 162}, + {0xc038, 162}, + }, + /* 104 */ + { + {0x8003, 184}, + {0x8006, 184}, + {0x800a, 184}, + {0x800f, 184}, + {0x8018, 184}, + {0x801f, 184}, + {0x8029, 184}, + {0xc038, 184}, + {0x8003, 194}, + {0x8006, 194}, + {0x800a, 194}, + {0x800f, 194}, + {0x8018, 194}, + {0x801f, 194}, + {0x8029, 194}, + {0xc038, 194}, + }, + /* 105 */ + { + {0x8002, 224}, + {0x8009, 224}, + {0x8017, 224}, + {0xc028, 224}, + {0x8002, 226}, + {0x8009, 226}, + {0x8017, 226}, + {0xc028, 226}, + {0x8001, 153}, + {0xc016, 153}, + {0x8001, 161}, + {0xc016, 161}, + {0x8001, 167}, + {0xc016, 167}, + {0x8001, 172}, + {0xc016, 172}, + }, + /* 106 */ + { + {0x8003, 224}, + {0x8006, 224}, + {0x800a, 224}, + {0x800f, 224}, + {0x8018, 224}, + {0x801f, 224}, + {0x8029, 224}, + {0xc038, 224}, + {0x8003, 226}, + {0x8006, 226}, + {0x800a, 226}, + {0x800f, 226}, + {0x8018, 226}, + {0x801f, 226}, + {0x8029, 226}, + {0xc038, 226}, + }, + /* 107 */ + { + {0x8002, 153}, + {0x8009, 153}, + {0x8017, 153}, + {0xc028, 153}, + {0x8002, 161}, + {0x8009, 161}, + {0x8017, 161}, + {0xc028, 161}, + {0x8002, 167}, + {0x8009, 167}, + {0x8017, 167}, + {0xc028, 167}, + {0x8002, 172}, + {0x8009, 172}, + {0x8017, 172}, + {0xc028, 172}, + }, + /* 108 */ + { + {0x8003, 153}, + {0x8006, 153}, + {0x800a, 153}, + {0x800f, 153}, + {0x8018, 153}, + {0x801f, 153}, + {0x8029, 153}, + {0xc038, 153}, + {0x8003, 161}, + {0x8006, 161}, + {0x800a, 161}, + {0x800f, 161}, + {0x8018, 161}, + {0x801f, 161}, + {0x8029, 161}, + {0xc038, 161}, + }, + /* 109 */ + { + {0x8003, 167}, + {0x8006, 167}, + {0x800a, 167}, + {0x800f, 167}, + {0x8018, 167}, + {0x801f, 167}, + {0x8029, 167}, + {0xc038, 167}, + {0x8003, 172}, + {0x8006, 172}, + {0x800a, 172}, + {0x800f, 172}, + {0x8018, 172}, + {0x801f, 172}, + {0x8029, 172}, + {0xc038, 172}, + }, + /* 110 */ + { + {0x72, 0}, + {0x73, 0}, + {0x75, 0}, + {0x76, 0}, + {0x79, 0}, + {0x7b, 0}, + {0x7f, 0}, + {0x82, 0}, + {0x88, 0}, + {0x8b, 0}, + {0x8f, 0}, + {0x92, 0}, + {0x9b, 0}, + {0xa2, 0}, + {0xaa, 0}, + {0xb4, 0}, + }, + /* 111 */ + { + {0xc000, 176}, + {0xc000, 177}, + {0xc000, 179}, + {0xc000, 209}, + {0xc000, 216}, + {0xc000, 217}, + {0xc000, 227}, + {0xc000, 229}, + {0xc000, 230}, + {0x7a, 0}, + {0x7c, 0}, + {0x7d, 0}, + {0x80, 0}, + {0x81, 0}, + {0x83, 0}, + {0x84, 0}, + }, + /* 112 */ + { + {0x8001, 176}, + {0xc016, 176}, + {0x8001, 177}, + {0xc016, 177}, + {0x8001, 179}, + {0xc016, 179}, + {0x8001, 209}, + {0xc016, 209}, + {0x8001, 216}, + {0xc016, 216}, + {0x8001, 217}, + {0xc016, 217}, + {0x8001, 227}, + {0xc016, 227}, + {0x8001, 229}, + {0xc016, 229}, + }, + /* 113 */ + { + {0x8002, 176}, + {0x8009, 176}, + {0x8017, 176}, + {0xc028, 176}, + {0x8002, 177}, + {0x8009, 177}, + {0x8017, 177}, + {0xc028, 177}, + {0x8002, 179}, + {0x8009, 179}, + {0x8017, 179}, + {0xc028, 179}, + {0x8002, 209}, + {0x8009, 209}, + {0x8017, 209}, + {0xc028, 209}, + }, + /* 114 */ + { + {0x8003, 176}, + {0x8006, 176}, + {0x800a, 176}, + {0x800f, 176}, + {0x8018, 176}, + {0x801f, 176}, + {0x8029, 176}, + {0xc038, 176}, + {0x8003, 177}, + {0x8006, 177}, + {0x800a, 177}, + {0x800f, 177}, + {0x8018, 177}, + {0x801f, 177}, + {0x8029, 177}, + {0xc038, 177}, + }, + /* 115 */ + { + {0x8003, 179}, + {0x8006, 179}, + {0x800a, 179}, + {0x800f, 179}, + {0x8018, 179}, + {0x801f, 179}, + {0x8029, 179}, + {0xc038, 179}, + {0x8003, 209}, + {0x8006, 209}, + {0x800a, 209}, + {0x800f, 209}, + {0x8018, 209}, + {0x801f, 209}, + {0x8029, 209}, + {0xc038, 209}, + }, + /* 116 */ + { + {0x8002, 216}, + {0x8009, 216}, + {0x8017, 216}, + {0xc028, 216}, + {0x8002, 217}, + {0x8009, 217}, + {0x8017, 217}, + {0xc028, 217}, + {0x8002, 227}, + {0x8009, 227}, + {0x8017, 227}, + {0xc028, 227}, + {0x8002, 229}, + {0x8009, 229}, + {0x8017, 229}, + {0xc028, 229}, + }, + /* 117 */ + { + {0x8003, 216}, + {0x8006, 216}, + {0x800a, 216}, + {0x800f, 216}, + {0x8018, 216}, + {0x801f, 216}, + {0x8029, 216}, + {0xc038, 216}, + {0x8003, 217}, + {0x8006, 217}, + {0x800a, 217}, + {0x800f, 217}, + {0x8018, 217}, + {0x801f, 217}, + {0x8029, 217}, + {0xc038, 217}, + }, + /* 118 */ + { + {0x8003, 227}, + {0x8006, 227}, + {0x800a, 227}, + {0x800f, 227}, + {0x8018, 227}, + {0x801f, 227}, + {0x8029, 227}, + {0xc038, 227}, + {0x8003, 229}, + {0x8006, 229}, + {0x800a, 229}, + {0x800f, 229}, + {0x8018, 229}, + {0x801f, 229}, + {0x8029, 229}, + {0xc038, 229}, + }, + /* 119 */ + { + {0x8001, 230}, + {0xc016, 230}, + {0xc000, 129}, + {0xc000, 132}, + {0xc000, 133}, + {0xc000, 134}, + {0xc000, 136}, + {0xc000, 146}, + {0xc000, 154}, + {0xc000, 156}, + {0xc000, 160}, + {0xc000, 163}, + {0xc000, 164}, + {0xc000, 169}, + {0xc000, 170}, + {0xc000, 173}, + }, + /* 120 */ + { + {0x8002, 230}, + {0x8009, 230}, + {0x8017, 230}, + {0xc028, 230}, + {0x8001, 129}, + {0xc016, 129}, + {0x8001, 132}, + {0xc016, 132}, + {0x8001, 133}, + {0xc016, 133}, + {0x8001, 134}, + {0xc016, 134}, + {0x8001, 136}, + {0xc016, 136}, + {0x8001, 146}, + {0xc016, 146}, + }, + /* 121 */ + { + {0x8003, 230}, + {0x8006, 230}, + {0x800a, 230}, + {0x800f, 230}, + {0x8018, 230}, + {0x801f, 230}, + {0x8029, 230}, + {0xc038, 230}, + {0x8002, 129}, + {0x8009, 129}, + {0x8017, 129}, + {0xc028, 129}, + {0x8002, 132}, + {0x8009, 132}, + {0x8017, 132}, + {0xc028, 132}, + }, + /* 122 */ + { + {0x8003, 129}, + {0x8006, 129}, + {0x800a, 129}, + {0x800f, 129}, + {0x8018, 129}, + {0x801f, 129}, + {0x8029, 129}, + {0xc038, 129}, + {0x8003, 132}, + {0x8006, 132}, + {0x800a, 132}, + {0x800f, 132}, + {0x8018, 132}, + {0x801f, 132}, + {0x8029, 132}, + {0xc038, 132}, + }, + /* 123 */ + { + {0x8002, 133}, + {0x8009, 133}, + {0x8017, 133}, + {0xc028, 133}, + {0x8002, 134}, + {0x8009, 134}, + {0x8017, 134}, + {0xc028, 134}, + {0x8002, 136}, + {0x8009, 136}, + {0x8017, 136}, + {0xc028, 136}, + {0x8002, 146}, + {0x8009, 146}, + {0x8017, 146}, + {0xc028, 146}, + }, + /* 124 */ + { + {0x8003, 133}, + {0x8006, 133}, + {0x800a, 133}, + {0x800f, 133}, + {0x8018, 133}, + {0x801f, 133}, + {0x8029, 133}, + {0xc038, 133}, + {0x8003, 134}, + {0x8006, 134}, + {0x800a, 134}, + {0x800f, 134}, + {0x8018, 134}, + {0x801f, 134}, + {0x8029, 134}, + {0xc038, 134}, + }, + /* 125 */ + { + {0x8003, 136}, + {0x8006, 136}, + {0x800a, 136}, + {0x800f, 136}, + {0x8018, 136}, + {0x801f, 136}, + {0x8029, 136}, + {0xc038, 136}, + {0x8003, 146}, + {0x8006, 146}, + {0x800a, 146}, + {0x800f, 146}, + {0x8018, 146}, + {0x801f, 146}, + {0x8029, 146}, + {0xc038, 146}, + }, + /* 126 */ + { + {0x8001, 154}, + {0xc016, 154}, + {0x8001, 156}, + {0xc016, 156}, + {0x8001, 160}, + {0xc016, 160}, + {0x8001, 163}, + {0xc016, 163}, + {0x8001, 164}, + {0xc016, 164}, + {0x8001, 169}, + {0xc016, 169}, + {0x8001, 170}, + {0xc016, 170}, + {0x8001, 173}, + {0xc016, 173}, + }, + /* 127 */ + { + {0x8002, 154}, + {0x8009, 154}, + {0x8017, 154}, + {0xc028, 154}, + {0x8002, 156}, + {0x8009, 156}, + {0x8017, 156}, + {0xc028, 156}, + {0x8002, 160}, + {0x8009, 160}, + {0x8017, 160}, + {0xc028, 160}, + {0x8002, 163}, + {0x8009, 163}, + {0x8017, 163}, + {0xc028, 163}, + }, + /* 128 */ + { + {0x8003, 154}, + {0x8006, 154}, + {0x800a, 154}, + {0x800f, 154}, + {0x8018, 154}, + {0x801f, 154}, + {0x8029, 154}, + {0xc038, 154}, + {0x8003, 156}, + {0x8006, 156}, + {0x800a, 156}, + {0x800f, 156}, + {0x8018, 156}, + {0x801f, 156}, + {0x8029, 156}, + {0xc038, 156}, + }, + /* 129 */ + { + {0x8003, 160}, + {0x8006, 160}, + {0x800a, 160}, + {0x800f, 160}, + {0x8018, 160}, + {0x801f, 160}, + {0x8029, 160}, + {0xc038, 160}, + {0x8003, 163}, + {0x8006, 163}, + {0x800a, 163}, + {0x800f, 163}, + {0x8018, 163}, + {0x801f, 163}, + {0x8029, 163}, + {0xc038, 163}, + }, + /* 130 */ + { + {0x8002, 164}, + {0x8009, 164}, + {0x8017, 164}, + {0xc028, 164}, + {0x8002, 169}, + {0x8009, 169}, + {0x8017, 169}, + {0xc028, 169}, + {0x8002, 170}, + {0x8009, 170}, + {0x8017, 170}, + {0xc028, 170}, + {0x8002, 173}, + {0x8009, 173}, + {0x8017, 173}, + {0xc028, 173}, + }, + /* 131 */ + { + {0x8003, 164}, + {0x8006, 164}, + {0x800a, 164}, + {0x800f, 164}, + {0x8018, 164}, + {0x801f, 164}, + {0x8029, 164}, + {0xc038, 164}, + {0x8003, 169}, + {0x8006, 169}, + {0x800a, 169}, + {0x800f, 169}, + {0x8018, 169}, + {0x801f, 169}, + {0x8029, 169}, + {0xc038, 169}, + }, + /* 132 */ + { + {0x8003, 170}, + {0x8006, 170}, + {0x800a, 170}, + {0x800f, 170}, + {0x8018, 170}, + {0x801f, 170}, + {0x8029, 170}, + {0xc038, 170}, + {0x8003, 173}, + {0x8006, 173}, + {0x800a, 173}, + {0x800f, 173}, + {0x8018, 173}, + {0x801f, 173}, + {0x8029, 173}, + {0xc038, 173}, + }, + /* 133 */ + { + {0x89, 0}, + {0x8a, 0}, + {0x8c, 0}, + {0x8d, 0}, + {0x90, 0}, + {0x91, 0}, + {0x93, 0}, + {0x96, 0}, + {0x9c, 0}, + {0x9f, 0}, + {0xa3, 0}, + {0xa6, 0}, + {0xab, 0}, + {0xae, 0}, + {0xb5, 0}, + {0xbe, 0}, + }, + /* 134 */ + { + {0xc000, 178}, + {0xc000, 181}, + {0xc000, 185}, + {0xc000, 186}, + {0xc000, 187}, + {0xc000, 189}, + {0xc000, 190}, + {0xc000, 196}, + {0xc000, 198}, + {0xc000, 228}, + {0xc000, 232}, + {0xc000, 233}, + {0x94, 0}, + {0x95, 0}, + {0x97, 0}, + {0x98, 0}, + }, + /* 135 */ + { + {0x8001, 178}, + {0xc016, 178}, + {0x8001, 181}, + {0xc016, 181}, + {0x8001, 185}, + {0xc016, 185}, + {0x8001, 186}, + {0xc016, 186}, + {0x8001, 187}, + {0xc016, 187}, + {0x8001, 189}, + {0xc016, 189}, + {0x8001, 190}, + {0xc016, 190}, + {0x8001, 196}, + {0xc016, 196}, + }, + /* 136 */ + { + {0x8002, 178}, + {0x8009, 178}, + {0x8017, 178}, + {0xc028, 178}, + {0x8002, 181}, + {0x8009, 181}, + {0x8017, 181}, + {0xc028, 181}, + {0x8002, 185}, + {0x8009, 185}, + {0x8017, 185}, + {0xc028, 185}, + {0x8002, 186}, + {0x8009, 186}, + {0x8017, 186}, + {0xc028, 186}, + }, + /* 137 */ + { + {0x8003, 178}, + {0x8006, 178}, + {0x800a, 178}, + {0x800f, 178}, + {0x8018, 178}, + {0x801f, 178}, + {0x8029, 178}, + {0xc038, 178}, + {0x8003, 181}, + {0x8006, 181}, + {0x800a, 181}, + {0x800f, 181}, + {0x8018, 181}, + {0x801f, 181}, + {0x8029, 181}, + {0xc038, 181}, + }, + /* 138 */ + { + {0x8003, 185}, + {0x8006, 185}, + {0x800a, 185}, + {0x800f, 185}, + {0x8018, 185}, + {0x801f, 185}, + {0x8029, 185}, + {0xc038, 185}, + {0x8003, 186}, + {0x8006, 186}, + {0x800a, 186}, + {0x800f, 186}, + {0x8018, 186}, + {0x801f, 186}, + {0x8029, 186}, + {0xc038, 186}, + }, + /* 139 */ + { + {0x8002, 187}, + {0x8009, 187}, + {0x8017, 187}, + {0xc028, 187}, + {0x8002, 189}, + {0x8009, 189}, + {0x8017, 189}, + {0xc028, 189}, + {0x8002, 190}, + {0x8009, 190}, + {0x8017, 190}, + {0xc028, 190}, + {0x8002, 196}, + {0x8009, 196}, + {0x8017, 196}, + {0xc028, 196}, + }, + /* 140 */ + { + {0x8003, 187}, + {0x8006, 187}, + {0x800a, 187}, + {0x800f, 187}, + {0x8018, 187}, + {0x801f, 187}, + {0x8029, 187}, + {0xc038, 187}, + {0x8003, 189}, + {0x8006, 189}, + {0x800a, 189}, + {0x800f, 189}, + {0x8018, 189}, + {0x801f, 189}, + {0x8029, 189}, + {0xc038, 189}, + }, + /* 141 */ + { + {0x8003, 190}, + {0x8006, 190}, + {0x800a, 190}, + {0x800f, 190}, + {0x8018, 190}, + {0x801f, 190}, + {0x8029, 190}, + {0xc038, 190}, + {0x8003, 196}, + {0x8006, 196}, + {0x800a, 196}, + {0x800f, 196}, + {0x8018, 196}, + {0x801f, 196}, + {0x8029, 196}, + {0xc038, 196}, + }, + /* 142 */ + { + {0x8001, 198}, + {0xc016, 198}, + {0x8001, 228}, + {0xc016, 228}, + {0x8001, 232}, + {0xc016, 232}, + {0x8001, 233}, + {0xc016, 233}, + {0xc000, 1}, + {0xc000, 135}, + {0xc000, 137}, + {0xc000, 138}, + {0xc000, 139}, + {0xc000, 140}, + {0xc000, 141}, + {0xc000, 143}, + }, + /* 143 */ + { + {0x8002, 198}, + {0x8009, 198}, + {0x8017, 198}, + {0xc028, 198}, + {0x8002, 228}, + {0x8009, 228}, + {0x8017, 228}, + {0xc028, 228}, + {0x8002, 232}, + {0x8009, 232}, + {0x8017, 232}, + {0xc028, 232}, + {0x8002, 233}, + {0x8009, 233}, + {0x8017, 233}, + {0xc028, 233}, + }, + /* 144 */ + { + {0x8003, 198}, + {0x8006, 198}, + {0x800a, 198}, + {0x800f, 198}, + {0x8018, 198}, + {0x801f, 198}, + {0x8029, 198}, + {0xc038, 198}, + {0x8003, 228}, + {0x8006, 228}, + {0x800a, 228}, + {0x800f, 228}, + {0x8018, 228}, + {0x801f, 228}, + {0x8029, 228}, + {0xc038, 228}, + }, + /* 145 */ + { + {0x8003, 232}, + {0x8006, 232}, + {0x800a, 232}, + {0x800f, 232}, + {0x8018, 232}, + {0x801f, 232}, + {0x8029, 232}, + {0xc038, 232}, + {0x8003, 233}, + {0x8006, 233}, + {0x800a, 233}, + {0x800f, 233}, + {0x8018, 233}, + {0x801f, 233}, + {0x8029, 233}, + {0xc038, 233}, + }, + /* 146 */ + { + {0x8001, 1}, + {0xc016, 1}, + {0x8001, 135}, + {0xc016, 135}, + {0x8001, 137}, + {0xc016, 137}, + {0x8001, 138}, + {0xc016, 138}, + {0x8001, 139}, + {0xc016, 139}, + {0x8001, 140}, + {0xc016, 140}, + {0x8001, 141}, + {0xc016, 141}, + {0x8001, 143}, + {0xc016, 143}, + }, + /* 147 */ + { + {0x8002, 1}, + {0x8009, 1}, + {0x8017, 1}, + {0xc028, 1}, + {0x8002, 135}, + {0x8009, 135}, + {0x8017, 135}, + {0xc028, 135}, + {0x8002, 137}, + {0x8009, 137}, + {0x8017, 137}, + {0xc028, 137}, + {0x8002, 138}, + {0x8009, 138}, + {0x8017, 138}, + {0xc028, 138}, + }, + /* 148 */ + { + {0x8003, 1}, + {0x8006, 1}, + {0x800a, 1}, + {0x800f, 1}, + {0x8018, 1}, + {0x801f, 1}, + {0x8029, 1}, + {0xc038, 1}, + {0x8003, 135}, + {0x8006, 135}, + {0x800a, 135}, + {0x800f, 135}, + {0x8018, 135}, + {0x801f, 135}, + {0x8029, 135}, + {0xc038, 135}, + }, + /* 149 */ + { + {0x8003, 137}, + {0x8006, 137}, + {0x800a, 137}, + {0x800f, 137}, + {0x8018, 137}, + {0x801f, 137}, + {0x8029, 137}, + {0xc038, 137}, + {0x8003, 138}, + {0x8006, 138}, + {0x800a, 138}, + {0x800f, 138}, + {0x8018, 138}, + {0x801f, 138}, + {0x8029, 138}, + {0xc038, 138}, + }, + /* 150 */ + { + {0x8002, 139}, + {0x8009, 139}, + {0x8017, 139}, + {0xc028, 139}, + {0x8002, 140}, + {0x8009, 140}, + {0x8017, 140}, + {0xc028, 140}, + {0x8002, 141}, + {0x8009, 141}, + {0x8017, 141}, + {0xc028, 141}, + {0x8002, 143}, + {0x8009, 143}, + {0x8017, 143}, + {0xc028, 143}, + }, + /* 151 */ + { + {0x8003, 139}, + {0x8006, 139}, + {0x800a, 139}, + {0x800f, 139}, + {0x8018, 139}, + {0x801f, 139}, + {0x8029, 139}, + {0xc038, 139}, + {0x8003, 140}, + {0x8006, 140}, + {0x800a, 140}, + {0x800f, 140}, + {0x8018, 140}, + {0x801f, 140}, + {0x8029, 140}, + {0xc038, 140}, + }, + /* 152 */ + { + {0x8003, 141}, + {0x8006, 141}, + {0x800a, 141}, + {0x800f, 141}, + {0x8018, 141}, + {0x801f, 141}, + {0x8029, 141}, + {0xc038, 141}, + {0x8003, 143}, + {0x8006, 143}, + {0x800a, 143}, + {0x800f, 143}, + {0x8018, 143}, + {0x801f, 143}, + {0x8029, 143}, + {0xc038, 143}, + }, + /* 153 */ + { + {0x9d, 0}, + {0x9e, 0}, + {0xa0, 0}, + {0xa1, 0}, + {0xa4, 0}, + {0xa5, 0}, + {0xa7, 0}, + {0xa8, 0}, + {0xac, 0}, + {0xad, 0}, + {0xaf, 0}, + {0xb1, 0}, + {0xb6, 0}, + {0xb9, 0}, + {0xbf, 0}, + {0xcf, 0}, + }, + /* 154 */ + { + {0xc000, 147}, + {0xc000, 149}, + {0xc000, 150}, + {0xc000, 151}, + {0xc000, 152}, + {0xc000, 155}, + {0xc000, 157}, + {0xc000, 158}, + {0xc000, 165}, + {0xc000, 166}, + {0xc000, 168}, + {0xc000, 174}, + {0xc000, 175}, + {0xc000, 180}, + {0xc000, 182}, + {0xc000, 183}, + }, + /* 155 */ + { + {0x8001, 147}, + {0xc016, 147}, + {0x8001, 149}, + {0xc016, 149}, + {0x8001, 150}, + {0xc016, 150}, + {0x8001, 151}, + {0xc016, 151}, + {0x8001, 152}, + {0xc016, 152}, + {0x8001, 155}, + {0xc016, 155}, + {0x8001, 157}, + {0xc016, 157}, + {0x8001, 158}, + {0xc016, 158}, + }, + /* 156 */ + { + {0x8002, 147}, + {0x8009, 147}, + {0x8017, 147}, + {0xc028, 147}, + {0x8002, 149}, + {0x8009, 149}, + {0x8017, 149}, + {0xc028, 149}, + {0x8002, 150}, + {0x8009, 150}, + {0x8017, 150}, + {0xc028, 150}, + {0x8002, 151}, + {0x8009, 151}, + {0x8017, 151}, + {0xc028, 151}, + }, + /* 157 */ + { + {0x8003, 147}, + {0x8006, 147}, + {0x800a, 147}, + {0x800f, 147}, + {0x8018, 147}, + {0x801f, 147}, + {0x8029, 147}, + {0xc038, 147}, + {0x8003, 149}, + {0x8006, 149}, + {0x800a, 149}, + {0x800f, 149}, + {0x8018, 149}, + {0x801f, 149}, + {0x8029, 149}, + {0xc038, 149}, + }, + /* 158 */ + { + {0x8003, 150}, + {0x8006, 150}, + {0x800a, 150}, + {0x800f, 150}, + {0x8018, 150}, + {0x801f, 150}, + {0x8029, 150}, + {0xc038, 150}, + {0x8003, 151}, + {0x8006, 151}, + {0x800a, 151}, + {0x800f, 151}, + {0x8018, 151}, + {0x801f, 151}, + {0x8029, 151}, + {0xc038, 151}, + }, + /* 159 */ + { + {0x8002, 152}, + {0x8009, 152}, + {0x8017, 152}, + {0xc028, 152}, + {0x8002, 155}, + {0x8009, 155}, + {0x8017, 155}, + {0xc028, 155}, + {0x8002, 157}, + {0x8009, 157}, + {0x8017, 157}, + {0xc028, 157}, + {0x8002, 158}, + {0x8009, 158}, + {0x8017, 158}, + {0xc028, 158}, + }, + /* 160 */ + { + {0x8003, 152}, + {0x8006, 152}, + {0x800a, 152}, + {0x800f, 152}, + {0x8018, 152}, + {0x801f, 152}, + {0x8029, 152}, + {0xc038, 152}, + {0x8003, 155}, + {0x8006, 155}, + {0x800a, 155}, + {0x800f, 155}, + {0x8018, 155}, + {0x801f, 155}, + {0x8029, 155}, + {0xc038, 155}, + }, + /* 161 */ + { + {0x8003, 157}, + {0x8006, 157}, + {0x800a, 157}, + {0x800f, 157}, + {0x8018, 157}, + {0x801f, 157}, + {0x8029, 157}, + {0xc038, 157}, + {0x8003, 158}, + {0x8006, 158}, + {0x800a, 158}, + {0x800f, 158}, + {0x8018, 158}, + {0x801f, 158}, + {0x8029, 158}, + {0xc038, 158}, + }, + /* 162 */ + { + {0x8001, 165}, + {0xc016, 165}, + {0x8001, 166}, + {0xc016, 166}, + {0x8001, 168}, + {0xc016, 168}, + {0x8001, 174}, + {0xc016, 174}, + {0x8001, 175}, + {0xc016, 175}, + {0x8001, 180}, + {0xc016, 180}, + {0x8001, 182}, + {0xc016, 182}, + {0x8001, 183}, + {0xc016, 183}, + }, + /* 163 */ + { + {0x8002, 165}, + {0x8009, 165}, + {0x8017, 165}, + {0xc028, 165}, + {0x8002, 166}, + {0x8009, 166}, + {0x8017, 166}, + {0xc028, 166}, + {0x8002, 168}, + {0x8009, 168}, + {0x8017, 168}, + {0xc028, 168}, + {0x8002, 174}, + {0x8009, 174}, + {0x8017, 174}, + {0xc028, 174}, + }, + /* 164 */ + { + {0x8003, 165}, + {0x8006, 165}, + {0x800a, 165}, + {0x800f, 165}, + {0x8018, 165}, + {0x801f, 165}, + {0x8029, 165}, + {0xc038, 165}, + {0x8003, 166}, + {0x8006, 166}, + {0x800a, 166}, + {0x800f, 166}, + {0x8018, 166}, + {0x801f, 166}, + {0x8029, 166}, + {0xc038, 166}, + }, + /* 165 */ + { + {0x8003, 168}, + {0x8006, 168}, + {0x800a, 168}, + {0x800f, 168}, + {0x8018, 168}, + {0x801f, 168}, + {0x8029, 168}, + {0xc038, 168}, + {0x8003, 174}, + {0x8006, 174}, + {0x800a, 174}, + {0x800f, 174}, + {0x8018, 174}, + {0x801f, 174}, + {0x8029, 174}, + {0xc038, 174}, + }, + /* 166 */ + { + {0x8002, 175}, + {0x8009, 175}, + {0x8017, 175}, + {0xc028, 175}, + {0x8002, 180}, + {0x8009, 180}, + {0x8017, 180}, + {0xc028, 180}, + {0x8002, 182}, + {0x8009, 182}, + {0x8017, 182}, + {0xc028, 182}, + {0x8002, 183}, + {0x8009, 183}, + {0x8017, 183}, + {0xc028, 183}, + }, + /* 167 */ + { + {0x8003, 175}, + {0x8006, 175}, + {0x800a, 175}, + {0x800f, 175}, + {0x8018, 175}, + {0x801f, 175}, + {0x8029, 175}, + {0xc038, 175}, + {0x8003, 180}, + {0x8006, 180}, + {0x800a, 180}, + {0x800f, 180}, + {0x8018, 180}, + {0x801f, 180}, + {0x8029, 180}, + {0xc038, 180}, + }, + /* 168 */ + { + {0x8003, 182}, + {0x8006, 182}, + {0x800a, 182}, + {0x800f, 182}, + {0x8018, 182}, + {0x801f, 182}, + {0x8029, 182}, + {0xc038, 182}, + {0x8003, 183}, + {0x8006, 183}, + {0x800a, 183}, + {0x800f, 183}, + {0x8018, 183}, + {0x801f, 183}, + {0x8029, 183}, + {0xc038, 183}, + }, + /* 169 */ + { + {0xc000, 188}, + {0xc000, 191}, + {0xc000, 197}, + {0xc000, 231}, + {0xc000, 239}, + {0xb0, 0}, + {0xb2, 0}, + {0xb3, 0}, + {0xb7, 0}, + {0xb8, 0}, + {0xba, 0}, + {0xbb, 0}, + {0xc0, 0}, + {0xc7, 0}, + {0xd0, 0}, + {0xdf, 0}, + }, + /* 170 */ + { + {0x8001, 188}, + {0xc016, 188}, + {0x8001, 191}, + {0xc016, 191}, + {0x8001, 197}, + {0xc016, 197}, + {0x8001, 231}, + {0xc016, 231}, + {0x8001, 239}, + {0xc016, 239}, + {0xc000, 9}, + {0xc000, 142}, + {0xc000, 144}, + {0xc000, 145}, + {0xc000, 148}, + {0xc000, 159}, + }, + /* 171 */ + { + {0x8002, 188}, + {0x8009, 188}, + {0x8017, 188}, + {0xc028, 188}, + {0x8002, 191}, + {0x8009, 191}, + {0x8017, 191}, + {0xc028, 191}, + {0x8002, 197}, + {0x8009, 197}, + {0x8017, 197}, + {0xc028, 197}, + {0x8002, 231}, + {0x8009, 231}, + {0x8017, 231}, + {0xc028, 231}, + }, + /* 172 */ + { + {0x8003, 188}, + {0x8006, 188}, + {0x800a, 188}, + {0x800f, 188}, + {0x8018, 188}, + {0x801f, 188}, + {0x8029, 188}, + {0xc038, 188}, + {0x8003, 191}, + {0x8006, 191}, + {0x800a, 191}, + {0x800f, 191}, + {0x8018, 191}, + {0x801f, 191}, + {0x8029, 191}, + {0xc038, 191}, + }, + /* 173 */ + { + {0x8003, 197}, + {0x8006, 197}, + {0x800a, 197}, + {0x800f, 197}, + {0x8018, 197}, + {0x801f, 197}, + {0x8029, 197}, + {0xc038, 197}, + {0x8003, 231}, + {0x8006, 231}, + {0x800a, 231}, + {0x800f, 231}, + {0x8018, 231}, + {0x801f, 231}, + {0x8029, 231}, + {0xc038, 231}, + }, + /* 174 */ + { + {0x8002, 239}, + {0x8009, 239}, + {0x8017, 239}, + {0xc028, 239}, + {0x8001, 9}, + {0xc016, 9}, + {0x8001, 142}, + {0xc016, 142}, + {0x8001, 144}, + {0xc016, 144}, + {0x8001, 145}, + {0xc016, 145}, + {0x8001, 148}, + {0xc016, 148}, + {0x8001, 159}, + {0xc016, 159}, + }, + /* 175 */ + { + {0x8003, 239}, + {0x8006, 239}, + {0x800a, 239}, + {0x800f, 239}, + {0x8018, 239}, + {0x801f, 239}, + {0x8029, 239}, + {0xc038, 239}, + {0x8002, 9}, + {0x8009, 9}, + {0x8017, 9}, + {0xc028, 9}, + {0x8002, 142}, + {0x8009, 142}, + {0x8017, 142}, + {0xc028, 142}, + }, + /* 176 */ + { + {0x8003, 9}, + {0x8006, 9}, + {0x800a, 9}, + {0x800f, 9}, + {0x8018, 9}, + {0x801f, 9}, + {0x8029, 9}, + {0xc038, 9}, + {0x8003, 142}, + {0x8006, 142}, + {0x800a, 142}, + {0x800f, 142}, + {0x8018, 142}, + {0x801f, 142}, + {0x8029, 142}, + {0xc038, 142}, + }, + /* 177 */ + { + {0x8002, 144}, + {0x8009, 144}, + {0x8017, 144}, + {0xc028, 144}, + {0x8002, 145}, + {0x8009, 145}, + {0x8017, 145}, + {0xc028, 145}, + {0x8002, 148}, + {0x8009, 148}, + {0x8017, 148}, + {0xc028, 148}, + {0x8002, 159}, + {0x8009, 159}, + {0x8017, 159}, + {0xc028, 159}, + }, + /* 178 */ + { + {0x8003, 144}, + {0x8006, 144}, + {0x800a, 144}, + {0x800f, 144}, + {0x8018, 144}, + {0x801f, 144}, + {0x8029, 144}, + {0xc038, 144}, + {0x8003, 145}, + {0x8006, 145}, + {0x800a, 145}, + {0x800f, 145}, + {0x8018, 145}, + {0x801f, 145}, + {0x8029, 145}, + {0xc038, 145}, + }, + /* 179 */ + { + {0x8003, 148}, + {0x8006, 148}, + {0x800a, 148}, + {0x800f, 148}, + {0x8018, 148}, + {0x801f, 148}, + {0x8029, 148}, + {0xc038, 148}, + {0x8003, 159}, + {0x8006, 159}, + {0x800a, 159}, + {0x800f, 159}, + {0x8018, 159}, + {0x801f, 159}, + {0x8029, 159}, + {0xc038, 159}, + }, + /* 180 */ + { + {0xc000, 171}, + {0xc000, 206}, + {0xc000, 215}, + {0xc000, 225}, + {0xc000, 236}, + {0xc000, 237}, + {0xbc, 0}, + {0xbd, 0}, + {0xc1, 0}, + {0xc4, 0}, + {0xc8, 0}, + {0xcb, 0}, + {0xd1, 0}, + {0xd8, 0}, + {0xe0, 0}, + {0xee, 0}, + }, + /* 181 */ + { + {0x8001, 171}, + {0xc016, 171}, + {0x8001, 206}, + {0xc016, 206}, + {0x8001, 215}, + {0xc016, 215}, + {0x8001, 225}, + {0xc016, 225}, + {0x8001, 236}, + {0xc016, 236}, + {0x8001, 237}, + {0xc016, 237}, + {0xc000, 199}, + {0xc000, 207}, + {0xc000, 234}, + {0xc000, 235}, + }, + /* 182 */ + { + {0x8002, 171}, + {0x8009, 171}, + {0x8017, 171}, + {0xc028, 171}, + {0x8002, 206}, + {0x8009, 206}, + {0x8017, 206}, + {0xc028, 206}, + {0x8002, 215}, + {0x8009, 215}, + {0x8017, 215}, + {0xc028, 215}, + {0x8002, 225}, + {0x8009, 225}, + {0x8017, 225}, + {0xc028, 225}, + }, + /* 183 */ + { + {0x8003, 171}, + {0x8006, 171}, + {0x800a, 171}, + {0x800f, 171}, + {0x8018, 171}, + {0x801f, 171}, + {0x8029, 171}, + {0xc038, 171}, + {0x8003, 206}, + {0x8006, 206}, + {0x800a, 206}, + {0x800f, 206}, + {0x8018, 206}, + {0x801f, 206}, + {0x8029, 206}, + {0xc038, 206}, + }, + /* 184 */ + { + {0x8003, 215}, + {0x8006, 215}, + {0x800a, 215}, + {0x800f, 215}, + {0x8018, 215}, + {0x801f, 215}, + {0x8029, 215}, + {0xc038, 215}, + {0x8003, 225}, + {0x8006, 225}, + {0x800a, 225}, + {0x800f, 225}, + {0x8018, 225}, + {0x801f, 225}, + {0x8029, 225}, + {0xc038, 225}, + }, + /* 185 */ + { + {0x8002, 236}, + {0x8009, 236}, + {0x8017, 236}, + {0xc028, 236}, + {0x8002, 237}, + {0x8009, 237}, + {0x8017, 237}, + {0xc028, 237}, + {0x8001, 199}, + {0xc016, 199}, + {0x8001, 207}, + {0xc016, 207}, + {0x8001, 234}, + {0xc016, 234}, + {0x8001, 235}, + {0xc016, 235}, + }, + /* 186 */ + { + {0x8003, 236}, + {0x8006, 236}, + {0x800a, 236}, + {0x800f, 236}, + {0x8018, 236}, + {0x801f, 236}, + {0x8029, 236}, + {0xc038, 236}, + {0x8003, 237}, + {0x8006, 237}, + {0x800a, 237}, + {0x800f, 237}, + {0x8018, 237}, + {0x801f, 237}, + {0x8029, 237}, + {0xc038, 237}, + }, + /* 187 */ + { + {0x8002, 199}, + {0x8009, 199}, + {0x8017, 199}, + {0xc028, 199}, + {0x8002, 207}, + {0x8009, 207}, + {0x8017, 207}, + {0xc028, 207}, + {0x8002, 234}, + {0x8009, 234}, + {0x8017, 234}, + {0xc028, 234}, + {0x8002, 235}, + {0x8009, 235}, + {0x8017, 235}, + {0xc028, 235}, + }, + /* 188 */ + { + {0x8003, 199}, + {0x8006, 199}, + {0x800a, 199}, + {0x800f, 199}, + {0x8018, 199}, + {0x801f, 199}, + {0x8029, 199}, + {0xc038, 199}, + {0x8003, 207}, + {0x8006, 207}, + {0x800a, 207}, + {0x800f, 207}, + {0x8018, 207}, + {0x801f, 207}, + {0x8029, 207}, + {0xc038, 207}, + }, + /* 189 */ + { + {0x8003, 234}, + {0x8006, 234}, + {0x800a, 234}, + {0x800f, 234}, + {0x8018, 234}, + {0x801f, 234}, + {0x8029, 234}, + {0xc038, 234}, + {0x8003, 235}, + {0x8006, 235}, + {0x800a, 235}, + {0x800f, 235}, + {0x8018, 235}, + {0x801f, 235}, + {0x8029, 235}, + {0xc038, 235}, + }, + /* 190 */ + { + {0xc2, 0}, + {0xc3, 0}, + {0xc5, 0}, + {0xc6, 0}, + {0xc9, 0}, + {0xca, 0}, + {0xcc, 0}, + {0xcd, 0}, + {0xd2, 0}, + {0xd5, 0}, + {0xd9, 0}, + {0xdc, 0}, + {0xe1, 0}, + {0xe7, 0}, + {0xef, 0}, + {0xf6, 0}, + }, + /* 191 */ + { + {0xc000, 192}, + {0xc000, 193}, + {0xc000, 200}, + {0xc000, 201}, + {0xc000, 202}, + {0xc000, 205}, + {0xc000, 210}, + {0xc000, 213}, + {0xc000, 218}, + {0xc000, 219}, + {0xc000, 238}, + {0xc000, 240}, + {0xc000, 242}, + {0xc000, 243}, + {0xc000, 255}, + {0xce, 0}, + }, + /* 192 */ + { + {0x8001, 192}, + {0xc016, 192}, + {0x8001, 193}, + {0xc016, 193}, + {0x8001, 200}, + {0xc016, 200}, + {0x8001, 201}, + {0xc016, 201}, + {0x8001, 202}, + {0xc016, 202}, + {0x8001, 205}, + {0xc016, 205}, + {0x8001, 210}, + {0xc016, 210}, + {0x8001, 213}, + {0xc016, 213}, + }, + /* 193 */ + { + {0x8002, 192}, + {0x8009, 192}, + {0x8017, 192}, + {0xc028, 192}, + {0x8002, 193}, + {0x8009, 193}, + {0x8017, 193}, + {0xc028, 193}, + {0x8002, 200}, + {0x8009, 200}, + {0x8017, 200}, + {0xc028, 200}, + {0x8002, 201}, + {0x8009, 201}, + {0x8017, 201}, + {0xc028, 201}, + }, + /* 194 */ + { + {0x8003, 192}, + {0x8006, 192}, + {0x800a, 192}, + {0x800f, 192}, + {0x8018, 192}, + {0x801f, 192}, + {0x8029, 192}, + {0xc038, 192}, + {0x8003, 193}, + {0x8006, 193}, + {0x800a, 193}, + {0x800f, 193}, + {0x8018, 193}, + {0x801f, 193}, + {0x8029, 193}, + {0xc038, 193}, + }, + /* 195 */ + { + {0x8003, 200}, + {0x8006, 200}, + {0x800a, 200}, + {0x800f, 200}, + {0x8018, 200}, + {0x801f, 200}, + {0x8029, 200}, + {0xc038, 200}, + {0x8003, 201}, + {0x8006, 201}, + {0x800a, 201}, + {0x800f, 201}, + {0x8018, 201}, + {0x801f, 201}, + {0x8029, 201}, + {0xc038, 201}, + }, + /* 196 */ + { + {0x8002, 202}, + {0x8009, 202}, + {0x8017, 202}, + {0xc028, 202}, + {0x8002, 205}, + {0x8009, 205}, + {0x8017, 205}, + {0xc028, 205}, + {0x8002, 210}, + {0x8009, 210}, + {0x8017, 210}, + {0xc028, 210}, + {0x8002, 213}, + {0x8009, 213}, + {0x8017, 213}, + {0xc028, 213}, + }, + /* 197 */ + { + {0x8003, 202}, + {0x8006, 202}, + {0x800a, 202}, + {0x800f, 202}, + {0x8018, 202}, + {0x801f, 202}, + {0x8029, 202}, + {0xc038, 202}, + {0x8003, 205}, + {0x8006, 205}, + {0x800a, 205}, + {0x800f, 205}, + {0x8018, 205}, + {0x801f, 205}, + {0x8029, 205}, + {0xc038, 205}, + }, + /* 198 */ + { + {0x8003, 210}, + {0x8006, 210}, + {0x800a, 210}, + {0x800f, 210}, + {0x8018, 210}, + {0x801f, 210}, + {0x8029, 210}, + {0xc038, 210}, + {0x8003, 213}, + {0x8006, 213}, + {0x800a, 213}, + {0x800f, 213}, + {0x8018, 213}, + {0x801f, 213}, + {0x8029, 213}, + {0xc038, 213}, + }, + /* 199 */ + { + {0x8001, 218}, + {0xc016, 218}, + {0x8001, 219}, + {0xc016, 219}, + {0x8001, 238}, + {0xc016, 238}, + {0x8001, 240}, + {0xc016, 240}, + {0x8001, 242}, + {0xc016, 242}, + {0x8001, 243}, + {0xc016, 243}, + {0x8001, 255}, + {0xc016, 255}, + {0xc000, 203}, + {0xc000, 204}, + }, + /* 200 */ + { + {0x8002, 218}, + {0x8009, 218}, + {0x8017, 218}, + {0xc028, 218}, + {0x8002, 219}, + {0x8009, 219}, + {0x8017, 219}, + {0xc028, 219}, + {0x8002, 238}, + {0x8009, 238}, + {0x8017, 238}, + {0xc028, 238}, + {0x8002, 240}, + {0x8009, 240}, + {0x8017, 240}, + {0xc028, 240}, + }, + /* 201 */ + { + {0x8003, 218}, + {0x8006, 218}, + {0x800a, 218}, + {0x800f, 218}, + {0x8018, 218}, + {0x801f, 218}, + {0x8029, 218}, + {0xc038, 218}, + {0x8003, 219}, + {0x8006, 219}, + {0x800a, 219}, + {0x800f, 219}, + {0x8018, 219}, + {0x801f, 219}, + {0x8029, 219}, + {0xc038, 219}, + }, + /* 202 */ + { + {0x8003, 238}, + {0x8006, 238}, + {0x800a, 238}, + {0x800f, 238}, + {0x8018, 238}, + {0x801f, 238}, + {0x8029, 238}, + {0xc038, 238}, + {0x8003, 240}, + {0x8006, 240}, + {0x800a, 240}, + {0x800f, 240}, + {0x8018, 240}, + {0x801f, 240}, + {0x8029, 240}, + {0xc038, 240}, + }, + /* 203 */ + { + {0x8002, 242}, + {0x8009, 242}, + {0x8017, 242}, + {0xc028, 242}, + {0x8002, 243}, + {0x8009, 243}, + {0x8017, 243}, + {0xc028, 243}, + {0x8002, 255}, + {0x8009, 255}, + {0x8017, 255}, + {0xc028, 255}, + {0x8001, 203}, + {0xc016, 203}, + {0x8001, 204}, + {0xc016, 204}, + }, + /* 204 */ + { + {0x8003, 242}, + {0x8006, 242}, + {0x800a, 242}, + {0x800f, 242}, + {0x8018, 242}, + {0x801f, 242}, + {0x8029, 242}, + {0xc038, 242}, + {0x8003, 243}, + {0x8006, 243}, + {0x800a, 243}, + {0x800f, 243}, + {0x8018, 243}, + {0x801f, 243}, + {0x8029, 243}, + {0xc038, 243}, + }, + /* 205 */ + { + {0x8003, 255}, + {0x8006, 255}, + {0x800a, 255}, + {0x800f, 255}, + {0x8018, 255}, + {0x801f, 255}, + {0x8029, 255}, + {0xc038, 255}, + {0x8002, 203}, + {0x8009, 203}, + {0x8017, 203}, + {0xc028, 203}, + {0x8002, 204}, + {0x8009, 204}, + {0x8017, 204}, + {0xc028, 204}, + }, + /* 206 */ + { + {0x8003, 203}, + {0x8006, 203}, + {0x800a, 203}, + {0x800f, 203}, + {0x8018, 203}, + {0x801f, 203}, + {0x8029, 203}, + {0xc038, 203}, + {0x8003, 204}, + {0x8006, 204}, + {0x800a, 204}, + {0x800f, 204}, + {0x8018, 204}, + {0x801f, 204}, + {0x8029, 204}, + {0xc038, 204}, + }, + /* 207 */ + { + {0xd3, 0}, + {0xd4, 0}, + {0xd6, 0}, + {0xd7, 0}, + {0xda, 0}, + {0xdb, 0}, + {0xdd, 0}, + {0xde, 0}, + {0xe2, 0}, + {0xe4, 0}, + {0xe8, 0}, + {0xeb, 0}, + {0xf0, 0}, + {0xf3, 0}, + {0xf7, 0}, + {0xfa, 0}, + }, + /* 208 */ + { + {0xc000, 211}, + {0xc000, 212}, + {0xc000, 214}, + {0xc000, 221}, + {0xc000, 222}, + {0xc000, 223}, + {0xc000, 241}, + {0xc000, 244}, + {0xc000, 245}, + {0xc000, 246}, + {0xc000, 247}, + {0xc000, 248}, + {0xc000, 250}, + {0xc000, 251}, + {0xc000, 252}, + {0xc000, 253}, + }, + /* 209 */ + { + {0x8001, 211}, + {0xc016, 211}, + {0x8001, 212}, + {0xc016, 212}, + {0x8001, 214}, + {0xc016, 214}, + {0x8001, 221}, + {0xc016, 221}, + {0x8001, 222}, + {0xc016, 222}, + {0x8001, 223}, + {0xc016, 223}, + {0x8001, 241}, + {0xc016, 241}, + {0x8001, 244}, + {0xc016, 244}, + }, + /* 210 */ + { + {0x8002, 211}, + {0x8009, 211}, + {0x8017, 211}, + {0xc028, 211}, + {0x8002, 212}, + {0x8009, 212}, + {0x8017, 212}, + {0xc028, 212}, + {0x8002, 214}, + {0x8009, 214}, + {0x8017, 214}, + {0xc028, 214}, + {0x8002, 221}, + {0x8009, 221}, + {0x8017, 221}, + {0xc028, 221}, + }, + /* 211 */ + { + {0x8003, 211}, + {0x8006, 211}, + {0x800a, 211}, + {0x800f, 211}, + {0x8018, 211}, + {0x801f, 211}, + {0x8029, 211}, + {0xc038, 211}, + {0x8003, 212}, + {0x8006, 212}, + {0x800a, 212}, + {0x800f, 212}, + {0x8018, 212}, + {0x801f, 212}, + {0x8029, 212}, + {0xc038, 212}, + }, + /* 212 */ + { + {0x8003, 214}, + {0x8006, 214}, + {0x800a, 214}, + {0x800f, 214}, + {0x8018, 214}, + {0x801f, 214}, + {0x8029, 214}, + {0xc038, 214}, + {0x8003, 221}, + {0x8006, 221}, + {0x800a, 221}, + {0x800f, 221}, + {0x8018, 221}, + {0x801f, 221}, + {0x8029, 221}, + {0xc038, 221}, + }, + /* 213 */ + { + {0x8002, 222}, + {0x8009, 222}, + {0x8017, 222}, + {0xc028, 222}, + {0x8002, 223}, + {0x8009, 223}, + {0x8017, 223}, + {0xc028, 223}, + {0x8002, 241}, + {0x8009, 241}, + {0x8017, 241}, + {0xc028, 241}, + {0x8002, 244}, + {0x8009, 244}, + {0x8017, 244}, + {0xc028, 244}, + }, + /* 214 */ + { + {0x8003, 222}, + {0x8006, 222}, + {0x800a, 222}, + {0x800f, 222}, + {0x8018, 222}, + {0x801f, 222}, + {0x8029, 222}, + {0xc038, 222}, + {0x8003, 223}, + {0x8006, 223}, + {0x800a, 223}, + {0x800f, 223}, + {0x8018, 223}, + {0x801f, 223}, + {0x8029, 223}, + {0xc038, 223}, + }, + /* 215 */ + { + {0x8003, 241}, + {0x8006, 241}, + {0x800a, 241}, + {0x800f, 241}, + {0x8018, 241}, + {0x801f, 241}, + {0x8029, 241}, + {0xc038, 241}, + {0x8003, 244}, + {0x8006, 244}, + {0x800a, 244}, + {0x800f, 244}, + {0x8018, 244}, + {0x801f, 244}, + {0x8029, 244}, + {0xc038, 244}, + }, + /* 216 */ + { + {0x8001, 245}, + {0xc016, 245}, + {0x8001, 246}, + {0xc016, 246}, + {0x8001, 247}, + {0xc016, 247}, + {0x8001, 248}, + {0xc016, 248}, + {0x8001, 250}, + {0xc016, 250}, + {0x8001, 251}, + {0xc016, 251}, + {0x8001, 252}, + {0xc016, 252}, + {0x8001, 253}, + {0xc016, 253}, + }, + /* 217 */ + { + {0x8002, 245}, + {0x8009, 245}, + {0x8017, 245}, + {0xc028, 245}, + {0x8002, 246}, + {0x8009, 246}, + {0x8017, 246}, + {0xc028, 246}, + {0x8002, 247}, + {0x8009, 247}, + {0x8017, 247}, + {0xc028, 247}, + {0x8002, 248}, + {0x8009, 248}, + {0x8017, 248}, + {0xc028, 248}, + }, + /* 218 */ + { + {0x8003, 245}, + {0x8006, 245}, + {0x800a, 245}, + {0x800f, 245}, + {0x8018, 245}, + {0x801f, 245}, + {0x8029, 245}, + {0xc038, 245}, + {0x8003, 246}, + {0x8006, 246}, + {0x800a, 246}, + {0x800f, 246}, + {0x8018, 246}, + {0x801f, 246}, + {0x8029, 246}, + {0xc038, 246}, + }, + /* 219 */ + { + {0x8003, 247}, + {0x8006, 247}, + {0x800a, 247}, + {0x800f, 247}, + {0x8018, 247}, + {0x801f, 247}, + {0x8029, 247}, + {0xc038, 247}, + {0x8003, 248}, + {0x8006, 248}, + {0x800a, 248}, + {0x800f, 248}, + {0x8018, 248}, + {0x801f, 248}, + {0x8029, 248}, + {0xc038, 248}, + }, + /* 220 */ + { + {0x8002, 250}, + {0x8009, 250}, + {0x8017, 250}, + {0xc028, 250}, + {0x8002, 251}, + {0x8009, 251}, + {0x8017, 251}, + {0xc028, 251}, + {0x8002, 252}, + {0x8009, 252}, + {0x8017, 252}, + {0xc028, 252}, + {0x8002, 253}, + {0x8009, 253}, + {0x8017, 253}, + {0xc028, 253}, + }, + /* 221 */ + { + {0x8003, 250}, + {0x8006, 250}, + {0x800a, 250}, + {0x800f, 250}, + {0x8018, 250}, + {0x801f, 250}, + {0x8029, 250}, + {0xc038, 250}, + {0x8003, 251}, + {0x8006, 251}, + {0x800a, 251}, + {0x800f, 251}, + {0x8018, 251}, + {0x801f, 251}, + {0x8029, 251}, + {0xc038, 251}, + }, + /* 222 */ + { + {0x8003, 252}, + {0x8006, 252}, + {0x800a, 252}, + {0x800f, 252}, + {0x8018, 252}, + {0x801f, 252}, + {0x8029, 252}, + {0xc038, 252}, + {0x8003, 253}, + {0x8006, 253}, + {0x800a, 253}, + {0x800f, 253}, + {0x8018, 253}, + {0x801f, 253}, + {0x8029, 253}, + {0xc038, 253}, + }, + /* 223 */ + { + {0xc000, 254}, + {0xe3, 0}, + {0xe5, 0}, + {0xe6, 0}, + {0xe9, 0}, + {0xea, 0}, + {0xec, 0}, + {0xed, 0}, + {0xf1, 0}, + {0xf2, 0}, + {0xf4, 0}, + {0xf5, 0}, + {0xf8, 0}, + {0xf9, 0}, + {0xfb, 0}, + {0xfc, 0}, + }, + /* 224 */ + { + {0x8001, 254}, + {0xc016, 254}, + {0xc000, 2}, + {0xc000, 3}, + {0xc000, 4}, + {0xc000, 5}, + {0xc000, 6}, + {0xc000, 7}, + {0xc000, 8}, + {0xc000, 11}, + {0xc000, 12}, + {0xc000, 14}, + {0xc000, 15}, + {0xc000, 16}, + {0xc000, 17}, + {0xc000, 18}, + }, + /* 225 */ + { + {0x8002, 254}, + {0x8009, 254}, + {0x8017, 254}, + {0xc028, 254}, + {0x8001, 2}, + {0xc016, 2}, + {0x8001, 3}, + {0xc016, 3}, + {0x8001, 4}, + {0xc016, 4}, + {0x8001, 5}, + {0xc016, 5}, + {0x8001, 6}, + {0xc016, 6}, + {0x8001, 7}, + {0xc016, 7}, + }, + /* 226 */ + { + {0x8003, 254}, + {0x8006, 254}, + {0x800a, 254}, + {0x800f, 254}, + {0x8018, 254}, + {0x801f, 254}, + {0x8029, 254}, + {0xc038, 254}, + {0x8002, 2}, + {0x8009, 2}, + {0x8017, 2}, + {0xc028, 2}, + {0x8002, 3}, + {0x8009, 3}, + {0x8017, 3}, + {0xc028, 3}, + }, + /* 227 */ + { + {0x8003, 2}, + {0x8006, 2}, + {0x800a, 2}, + {0x800f, 2}, + {0x8018, 2}, + {0x801f, 2}, + {0x8029, 2}, + {0xc038, 2}, + {0x8003, 3}, + {0x8006, 3}, + {0x800a, 3}, + {0x800f, 3}, + {0x8018, 3}, + {0x801f, 3}, + {0x8029, 3}, + {0xc038, 3}, + }, + /* 228 */ + { + {0x8002, 4}, + {0x8009, 4}, + {0x8017, 4}, + {0xc028, 4}, + {0x8002, 5}, + {0x8009, 5}, + {0x8017, 5}, + {0xc028, 5}, + {0x8002, 6}, + {0x8009, 6}, + {0x8017, 6}, + {0xc028, 6}, + {0x8002, 7}, + {0x8009, 7}, + {0x8017, 7}, + {0xc028, 7}, + }, + /* 229 */ + { + {0x8003, 4}, + {0x8006, 4}, + {0x800a, 4}, + {0x800f, 4}, + {0x8018, 4}, + {0x801f, 4}, + {0x8029, 4}, + {0xc038, 4}, + {0x8003, 5}, + {0x8006, 5}, + {0x800a, 5}, + {0x800f, 5}, + {0x8018, 5}, + {0x801f, 5}, + {0x8029, 5}, + {0xc038, 5}, + }, + /* 230 */ + { + {0x8003, 6}, + {0x8006, 6}, + {0x800a, 6}, + {0x800f, 6}, + {0x8018, 6}, + {0x801f, 6}, + {0x8029, 6}, + {0xc038, 6}, + {0x8003, 7}, + {0x8006, 7}, + {0x800a, 7}, + {0x800f, 7}, + {0x8018, 7}, + {0x801f, 7}, + {0x8029, 7}, + {0xc038, 7}, + }, + /* 231 */ + { + {0x8001, 8}, + {0xc016, 8}, + {0x8001, 11}, + {0xc016, 11}, + {0x8001, 12}, + {0xc016, 12}, + {0x8001, 14}, + {0xc016, 14}, + {0x8001, 15}, + {0xc016, 15}, + {0x8001, 16}, + {0xc016, 16}, + {0x8001, 17}, + {0xc016, 17}, + {0x8001, 18}, + {0xc016, 18}, + }, + /* 232 */ + { + {0x8002, 8}, + {0x8009, 8}, + {0x8017, 8}, + {0xc028, 8}, + {0x8002, 11}, + {0x8009, 11}, + {0x8017, 11}, + {0xc028, 11}, + {0x8002, 12}, + {0x8009, 12}, + {0x8017, 12}, + {0xc028, 12}, + {0x8002, 14}, + {0x8009, 14}, + {0x8017, 14}, + {0xc028, 14}, + }, + /* 233 */ + { + {0x8003, 8}, + {0x8006, 8}, + {0x800a, 8}, + {0x800f, 8}, + {0x8018, 8}, + {0x801f, 8}, + {0x8029, 8}, + {0xc038, 8}, + {0x8003, 11}, + {0x8006, 11}, + {0x800a, 11}, + {0x800f, 11}, + {0x8018, 11}, + {0x801f, 11}, + {0x8029, 11}, + {0xc038, 11}, + }, + /* 234 */ + { + {0x8003, 12}, + {0x8006, 12}, + {0x800a, 12}, + {0x800f, 12}, + {0x8018, 12}, + {0x801f, 12}, + {0x8029, 12}, + {0xc038, 12}, + {0x8003, 14}, + {0x8006, 14}, + {0x800a, 14}, + {0x800f, 14}, + {0x8018, 14}, + {0x801f, 14}, + {0x8029, 14}, + {0xc038, 14}, + }, + /* 235 */ + { + {0x8002, 15}, + {0x8009, 15}, + {0x8017, 15}, + {0xc028, 15}, + {0x8002, 16}, + {0x8009, 16}, + {0x8017, 16}, + {0xc028, 16}, + {0x8002, 17}, + {0x8009, 17}, + {0x8017, 17}, + {0xc028, 17}, + {0x8002, 18}, + {0x8009, 18}, + {0x8017, 18}, + {0xc028, 18}, + }, + /* 236 */ + { + {0x8003, 15}, + {0x8006, 15}, + {0x800a, 15}, + {0x800f, 15}, + {0x8018, 15}, + {0x801f, 15}, + {0x8029, 15}, + {0xc038, 15}, + {0x8003, 16}, + {0x8006, 16}, + {0x800a, 16}, + {0x800f, 16}, + {0x8018, 16}, + {0x801f, 16}, + {0x8029, 16}, + {0xc038, 16}, + }, + /* 237 */ + { + {0x8003, 17}, + {0x8006, 17}, + {0x800a, 17}, + {0x800f, 17}, + {0x8018, 17}, + {0x801f, 17}, + {0x8029, 17}, + {0xc038, 17}, + {0x8003, 18}, + {0x8006, 18}, + {0x800a, 18}, + {0x800f, 18}, + {0x8018, 18}, + {0x801f, 18}, + {0x8029, 18}, + {0xc038, 18}, + }, + /* 238 */ + { + {0xc000, 19}, + {0xc000, 20}, + {0xc000, 21}, + {0xc000, 23}, + {0xc000, 24}, + {0xc000, 25}, + {0xc000, 26}, + {0xc000, 27}, + {0xc000, 28}, + {0xc000, 29}, + {0xc000, 30}, + {0xc000, 31}, + {0xc000, 127}, + {0xc000, 220}, + {0xc000, 249}, + {0xfd, 0}, + }, + /* 239 */ + { + {0x8001, 19}, + {0xc016, 19}, + {0x8001, 20}, + {0xc016, 20}, + {0x8001, 21}, + {0xc016, 21}, + {0x8001, 23}, + {0xc016, 23}, + {0x8001, 24}, + {0xc016, 24}, + {0x8001, 25}, + {0xc016, 25}, + {0x8001, 26}, + {0xc016, 26}, + {0x8001, 27}, + {0xc016, 27}, + }, + /* 240 */ + { + {0x8002, 19}, + {0x8009, 19}, + {0x8017, 19}, + {0xc028, 19}, + {0x8002, 20}, + {0x8009, 20}, + {0x8017, 20}, + {0xc028, 20}, + {0x8002, 21}, + {0x8009, 21}, + {0x8017, 21}, + {0xc028, 21}, + {0x8002, 23}, + {0x8009, 23}, + {0x8017, 23}, + {0xc028, 23}, + }, + /* 241 */ + { + {0x8003, 19}, + {0x8006, 19}, + {0x800a, 19}, + {0x800f, 19}, + {0x8018, 19}, + {0x801f, 19}, + {0x8029, 19}, + {0xc038, 19}, + {0x8003, 20}, + {0x8006, 20}, + {0x800a, 20}, + {0x800f, 20}, + {0x8018, 20}, + {0x801f, 20}, + {0x8029, 20}, + {0xc038, 20}, + }, + /* 242 */ + { + {0x8003, 21}, + {0x8006, 21}, + {0x800a, 21}, + {0x800f, 21}, + {0x8018, 21}, + {0x801f, 21}, + {0x8029, 21}, + {0xc038, 21}, + {0x8003, 23}, + {0x8006, 23}, + {0x800a, 23}, + {0x800f, 23}, + {0x8018, 23}, + {0x801f, 23}, + {0x8029, 23}, + {0xc038, 23}, + }, + /* 243 */ + { + {0x8002, 24}, + {0x8009, 24}, + {0x8017, 24}, + {0xc028, 24}, + {0x8002, 25}, + {0x8009, 25}, + {0x8017, 25}, + {0xc028, 25}, + {0x8002, 26}, + {0x8009, 26}, + {0x8017, 26}, + {0xc028, 26}, + {0x8002, 27}, + {0x8009, 27}, + {0x8017, 27}, + {0xc028, 27}, + }, + /* 244 */ + { + {0x8003, 24}, + {0x8006, 24}, + {0x800a, 24}, + {0x800f, 24}, + {0x8018, 24}, + {0x801f, 24}, + {0x8029, 24}, + {0xc038, 24}, + {0x8003, 25}, + {0x8006, 25}, + {0x800a, 25}, + {0x800f, 25}, + {0x8018, 25}, + {0x801f, 25}, + {0x8029, 25}, + {0xc038, 25}, + }, + /* 245 */ + { + {0x8003, 26}, + {0x8006, 26}, + {0x800a, 26}, + {0x800f, 26}, + {0x8018, 26}, + {0x801f, 26}, + {0x8029, 26}, + {0xc038, 26}, + {0x8003, 27}, + {0x8006, 27}, + {0x800a, 27}, + {0x800f, 27}, + {0x8018, 27}, + {0x801f, 27}, + {0x8029, 27}, + {0xc038, 27}, + }, + /* 246 */ + { + {0x8001, 28}, + {0xc016, 28}, + {0x8001, 29}, + {0xc016, 29}, + {0x8001, 30}, + {0xc016, 30}, + {0x8001, 31}, + {0xc016, 31}, + {0x8001, 127}, + {0xc016, 127}, + {0x8001, 220}, + {0xc016, 220}, + {0x8001, 249}, + {0xc016, 249}, + {0xfe, 0}, + {0xff, 0}, + }, + /* 247 */ + { + {0x8002, 28}, + {0x8009, 28}, + {0x8017, 28}, + {0xc028, 28}, + {0x8002, 29}, + {0x8009, 29}, + {0x8017, 29}, + {0xc028, 29}, + {0x8002, 30}, + {0x8009, 30}, + {0x8017, 30}, + {0xc028, 30}, + {0x8002, 31}, + {0x8009, 31}, + {0x8017, 31}, + {0xc028, 31}, + }, + /* 248 */ + { + {0x8003, 28}, + {0x8006, 28}, + {0x800a, 28}, + {0x800f, 28}, + {0x8018, 28}, + {0x801f, 28}, + {0x8029, 28}, + {0xc038, 28}, + {0x8003, 29}, + {0x8006, 29}, + {0x800a, 29}, + {0x800f, 29}, + {0x8018, 29}, + {0x801f, 29}, + {0x8029, 29}, + {0xc038, 29}, + }, + /* 249 */ + { + {0x8003, 30}, + {0x8006, 30}, + {0x800a, 30}, + {0x800f, 30}, + {0x8018, 30}, + {0x801f, 30}, + {0x8029, 30}, + {0xc038, 30}, + {0x8003, 31}, + {0x8006, 31}, + {0x800a, 31}, + {0x800f, 31}, + {0x8018, 31}, + {0x801f, 31}, + {0x8029, 31}, + {0xc038, 31}, + }, + /* 250 */ + { + {0x8002, 127}, + {0x8009, 127}, + {0x8017, 127}, + {0xc028, 127}, + {0x8002, 220}, + {0x8009, 220}, + {0x8017, 220}, + {0xc028, 220}, + {0x8002, 249}, + {0x8009, 249}, + {0x8017, 249}, + {0xc028, 249}, + {0xc000, 10}, + {0xc000, 13}, + {0xc000, 22}, + {0x100, 0}, + }, + /* 251 */ + { + {0x8003, 127}, + {0x8006, 127}, + {0x800a, 127}, + {0x800f, 127}, + {0x8018, 127}, + {0x801f, 127}, + {0x8029, 127}, + {0xc038, 127}, + {0x8003, 220}, + {0x8006, 220}, + {0x800a, 220}, + {0x800f, 220}, + {0x8018, 220}, + {0x801f, 220}, + {0x8029, 220}, + {0xc038, 220}, + }, + /* 252 */ + { + {0x8003, 249}, + {0x8006, 249}, + {0x800a, 249}, + {0x800f, 249}, + {0x8018, 249}, + {0x801f, 249}, + {0x8029, 249}, + {0xc038, 249}, + {0x8001, 10}, + {0xc016, 10}, + {0x8001, 13}, + {0xc016, 13}, + {0x8001, 22}, + {0xc016, 22}, + {0x100, 0}, + {0x100, 0}, + }, + /* 253 */ + { + {0x8002, 10}, + {0x8009, 10}, + {0x8017, 10}, + {0xc028, 10}, + {0x8002, 13}, + {0x8009, 13}, + {0x8017, 13}, + {0xc028, 13}, + {0x8002, 22}, + {0x8009, 22}, + {0x8017, 22}, + {0xc028, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 254 */ + { + {0x8003, 10}, + {0x8006, 10}, + {0x800a, 10}, + {0x800f, 10}, + {0x8018, 10}, + {0x801f, 10}, + {0x8029, 10}, + {0xc038, 10}, + {0x8003, 13}, + {0x8006, 13}, + {0x800a, 13}, + {0x800f, 13}, + {0x8018, 13}, + {0x801f, 13}, + {0x8029, 13}, + {0xc038, 13}, + }, + /* 255 */ + { + {0x8003, 22}, + {0x8006, 22}, + {0x800a, 22}, + {0x800f, 22}, + {0x8018, 22}, + {0x801f, 22}, + {0x8029, 22}, + {0xc038, 22}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, + /* 256 */ + { + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + {0x100, 0}, + }, +}; diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_range.c b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c new file mode 100644 index 00000000000000..0ce71480d72fec --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_range.c @@ -0,0 +1,62 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 "nghttp3_range.h" +#include "nghttp3_macro.h" + +void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end) { + r->begin = begin; + r->end = end; +} + +nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, + const nghttp3_range *b) { + nghttp3_range r = {0, 0}; + uint64_t begin = nghttp3_max(a->begin, b->begin); + uint64_t end = nghttp3_min(a->end, b->end); + if (begin < end) { + nghttp3_range_init(&r, begin, end); + } + return r; +} + +uint64_t nghttp3_range_len(const nghttp3_range *r) { return r->end - r->begin; } + +int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b) { + return a->begin == b->begin && a->end == b->end; +} + +void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right, + const nghttp3_range *a, const nghttp3_range *b) { + /* Assume that b is included in a */ + left->begin = a->begin; + left->end = b->begin; + right->begin = b->end; + right->end = a->end; +} + +int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b) { + return a->end <= b->end; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_range.h b/deps/ngtcp2/nghttp3/lib/nghttp3_range.h new file mode 100644 index 00000000000000..20dab69aa62db5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_range.h @@ -0,0 +1,81 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_RANGE_H +#define NGHTTP3_RANGE_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +/* + * nghttp3_range represents half-closed range [begin, end). + */ +typedef struct nghttp3_range { + uint64_t begin; + uint64_t end; +} nghttp3_range; + +/* + * nghttp3_range_init initializes |r| with the range [|begin|, |end|). + */ +void nghttp3_range_init(nghttp3_range *r, uint64_t begin, uint64_t end); + +/* + * nghttp3_range_intersect returns the intersection of |a| and |b|. + * If they do not overlap, it returns empty range. + */ +nghttp3_range nghttp3_range_intersect(const nghttp3_range *a, + const nghttp3_range *b); + +/* + * nghttp3_range_len returns the length of |r|. + */ +uint64_t nghttp3_range_len(const nghttp3_range *r); + +/* + * nghttp3_range_eq returns nonzero if |a| equals |b|, such that + * a->begin == b->begin, and a->end == b->end hold. + */ +int nghttp3_range_eq(const nghttp3_range *a, const nghttp3_range *b); + +/* + * nghttp3_range_cut returns the left and right range after removing + * |b| from |a|. This function assumes that |a| completely includes + * |b|. In other words, a->begin <= b->begin and b->end <= a->end + * hold. + */ +void nghttp3_range_cut(nghttp3_range *left, nghttp3_range *right, + const nghttp3_range *a, const nghttp3_range *b); + +/* + * nghttp3_range_not_after returns nonzero if the right edge of |a| + * does not go beyond of the right edge of |b|. + */ +int nghttp3_range_not_after(const nghttp3_range *a, const nghttp3_range *b); + +#endif /* NGHTTP3_RANGE_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c new file mode 100644 index 00000000000000..1c31ecebf608d5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.c @@ -0,0 +1,109 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 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 "nghttp3_rcbuf.h" + +#include <assert.h> + +#include "nghttp3_mem.h" +#include "nghttp3_str.h" + +int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, + const nghttp3_mem *mem) { + uint8_t *p; + + p = nghttp3_mem_malloc(mem, sizeof(nghttp3_rcbuf) + size); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + *rcbuf_ptr = (void *)p; + + (*rcbuf_ptr)->mem_user_data = mem->mem_user_data; + (*rcbuf_ptr)->free = mem->free; + (*rcbuf_ptr)->base = p + sizeof(nghttp3_rcbuf); + (*rcbuf_ptr)->len = size; + (*rcbuf_ptr)->ref = 1; + + return 0; +} + +int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, const nghttp3_mem *mem) { + int rv; + uint8_t *p; + + rv = nghttp3_rcbuf_new(rcbuf_ptr, srclen + 1, mem); + if (rv != 0) { + return rv; + } + + (*rcbuf_ptr)->len = srclen; + p = (*rcbuf_ptr)->base; + + if (srclen) { + p = nghttp3_cpymem(p, src, srclen); + } + + *p = '\0'; + + return 0; +} + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf) { + nghttp3_mem_free2(rcbuf->free, rcbuf, rcbuf->mem_user_data); +} + +void nghttp3_rcbuf_incref(nghttp3_rcbuf *rcbuf) { + if (rcbuf->ref == -1) { + return; + } + + ++rcbuf->ref; +} + +void nghttp3_rcbuf_decref(nghttp3_rcbuf *rcbuf) { + if (rcbuf == NULL || rcbuf->ref == -1) { + return; + } + + assert(rcbuf->ref > 0); + + if (--rcbuf->ref == 0) { + nghttp3_rcbuf_del(rcbuf); + } +} + +nghttp3_vec nghttp3_rcbuf_get_buf(const nghttp3_rcbuf *rcbuf) { + nghttp3_vec res = {rcbuf->base, rcbuf->len}; + return res; +} + +int nghttp3_rcbuf_is_static(const nghttp3_rcbuf *rcbuf) { + return rcbuf->ref == -1; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h new file mode 100644 index 00000000000000..feea8040005422 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_rcbuf.h @@ -0,0 +1,82 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2016 nghttp2 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 NGHTTP3_RCBUF_H +#define NGHTTP3_RCBUF_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +struct nghttp3_rcbuf { + /* custom memory allocator belongs to the mem parameter when + creating this object. */ + void *mem_user_data; + nghttp3_free free; + /* The pointer to the underlying buffer */ + uint8_t *base; + /* Size of buffer pointed by |base|. */ + size_t len; + /* Reference count */ + int32_t ref; +}; + +/* + * Allocates nghttp3_rcbuf object with |size| as initial buffer size. + * When the function succeeds, the reference count becomes 1. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM: + * Out of memory. + */ +int nghttp3_rcbuf_new(nghttp3_rcbuf **rcbuf_ptr, size_t size, + const nghttp3_mem *mem); + +/* + * Like nghttp3_rcbuf_new(), but initializes the buffer with |src| of + * length |srclen|. This function allocates additional byte at the + * end and puts '\0' into it, so that the resulting buffer could be + * used as NULL-terminated string. Still (*rcbuf_ptr)->len equals to + * |srclen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM: + * Out of memory. + */ +int nghttp3_rcbuf_new2(nghttp3_rcbuf **rcbuf_ptr, const uint8_t *src, + size_t srclen, const nghttp3_mem *mem); + +/* + * Frees |rcbuf| itself, regardless of its reference cout. + */ +void nghttp3_rcbuf_del(nghttp3_rcbuf *rcbuf); + +#endif /* NGHTTP3_RCBUF_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c new file mode 100644 index 00000000000000..9ea91c81c8a1b9 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.c @@ -0,0 +1,159 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 "nghttp3_ringbuf.h" + +#include <assert.h> +#include <string.h> +#ifdef WIN32 +# include <intrin.h> +#endif + +#include "nghttp3_macro.h" + +#if defined(_MSC_VER) && defined(_M_ARM64) +unsigned int __popcnt(unsigned int x) { + unsigned int c = 0; + for (; x; ++c) { + x &= x - 1; + } + return c; +} +#endif + +int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, + const nghttp3_mem *mem) { + if (nmemb) { +#ifdef WIN32 + assert(1 == __popcnt((unsigned int)nmemb)); +#else + assert(1 == __builtin_popcount((unsigned int)nmemb)); +#endif + + rb->buf = nghttp3_mem_malloc(mem, nmemb * size); + if (rb->buf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + } else { + rb->buf = NULL; + } + + rb->mem = mem; + rb->nmemb = nmemb; + rb->size = size; + rb->first = 0; + rb->len = 0; + + return 0; +} + +void nghttp3_ringbuf_free(nghttp3_ringbuf *rb) { + if (rb == NULL) { + return; + } + + nghttp3_mem_free(rb->mem, rb->buf); +} + +void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb) { + rb->first = (rb->first - 1) & (rb->nmemb - 1); + rb->len = nghttp3_min(rb->nmemb, rb->len + 1); + + return (void *)&rb->buf[rb->first * rb->size]; +} + +void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb) { + size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); + + if (rb->len == rb->nmemb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + } else { + ++rb->len; + } + + return (void *)&rb->buf[offset * rb->size]; +} + +void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + --rb->len; +} + +void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb) { + assert(rb->len); + --rb->len; +} + +void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len) { + assert(len <= rb->nmemb); + rb->len = len; +} + +void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset) { + assert(offset < rb->len); + offset = (rb->first + offset) & (rb->nmemb - 1); + return &rb->buf[offset * rb->size]; +} + +int nghttp3_ringbuf_full(nghttp3_ringbuf *rb) { return rb->len == rb->nmemb; } + +int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb) { + uint8_t *buf; + + if (rb->nmemb >= nmemb) { + return 0; + } + +#ifdef WIN32 + assert(1 == __popcnt((unsigned int)nmemb)); +#else + assert(1 == __builtin_popcount((unsigned int)nmemb)); +#endif + + buf = nghttp3_mem_malloc(rb->mem, nmemb * rb->size); + if (buf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + if (rb->buf != NULL) { + if (rb->first + rb->len <= rb->nmemb) { + memcpy(buf, rb->buf + rb->first * rb->size, rb->len * rb->size); + rb->first = 0; + } else { + memcpy(buf, rb->buf + rb->first * rb->size, + (rb->nmemb - rb->first) * rb->size); + memcpy(buf + (rb->nmemb - rb->first) * rb->size, rb->buf, + (rb->len - (rb->nmemb - rb->first)) * rb->size); + rb->first = 0; + } + + nghttp3_mem_free(rb->mem, rb->buf); + } + + rb->buf = buf; + rb->nmemb = nmemb; + + return 0; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h new file mode 100644 index 00000000000000..8e05ec55b24724 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_ringbuf.h @@ -0,0 +1,113 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 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 NGHTTP3_RINGBUF_H +#define NGHTTP3_RINGBUF_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_mem.h" + +typedef struct nghttp3_ringbuf { + /* buf points to the underlying buffer. */ + uint8_t *buf; + const nghttp3_mem *mem; + /* nmemb is the number of elements that can be stored in this ring + buffer. */ + size_t nmemb; + /* size is the size of each element. */ + size_t size; + /* first is the offset to the first element. */ + size_t first; + /* len is the number of elements actually stored. */ + size_t len; +} nghttp3_ringbuf; + +/* + * nghttp3_ringbuf_init initializes |rb|. |nmemb| is the number of + * elements that can be stored in this buffer. |size| is the size of + * each element. |size| must be power of 2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGHTTP3_ERR_NOMEM + * Out of memory. + */ +int nghttp3_ringbuf_init(nghttp3_ringbuf *rb, size_t nmemb, size_t size, + const nghttp3_mem *mem); + +/* + * nghttp3_ringbuf_free frees resources allocated for |rb|. This + * function does not free the memory pointed by |rb|. + */ +void nghttp3_ringbuf_free(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_push_front moves the offset to the first element in + the buffer backward, and returns the pointer to the element. + Caller can store data to the buffer pointed by the returned + pointer. If this action exceeds the capacity of the ring buffer, + the last element is silently overwritten, and rb->len remains + unchanged. */ +void *nghttp3_ringbuf_push_front(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_push_back moves the offset to the last element in + the buffer forward, and returns the pointer to the element. Caller + can store data to the buffer pointed by the returned pointer. If + this action exceeds the capacity of the ring buffer, the first + element is silently overwritten, and rb->len remains unchanged. */ +void *nghttp3_ringbuf_push_back(nghttp3_ringbuf *rb); + +/* + * nghttp3_ringbuf_pop_front removes first element in |rb|. + */ +void nghttp3_ringbuf_pop_front(nghttp3_ringbuf *rb); + +/* + * nghttp3_ringbuf_pop_back removes the last element in |rb|. + */ +void nghttp3_ringbuf_pop_back(nghttp3_ringbuf *rb); + +/* nghttp3_ringbuf_resize changes the number of elements stored. This + does not change the capacity of the underlying buffer. */ +void nghttp3_ringbuf_resize(nghttp3_ringbuf *rb, size_t len); + +/* nghttp3_ringbuf_get returns the pointer to the element at + |offset|. */ +void *nghttp3_ringbuf_get(nghttp3_ringbuf *rb, size_t offset); + +/* nghttp3_ringbuf_len returns the number of elements stored. */ +#define nghttp3_ringbuf_len(RB) ((RB)->len) + +/* nghttp3_ringbuf_full returns nonzero if |rb| is full. */ +int nghttp3_ringbuf_full(nghttp3_ringbuf *rb); + +int nghttp3_ringbuf_reserve(nghttp3_ringbuf *rb, size_t nmemb); + +#endif /* NGHTTP3_RINGBUF_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_str.c b/deps/ngtcp2/nghttp3/lib/nghttp3_str.c new file mode 100644 index 00000000000000..3782aa72cd6e81 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_str.c @@ -0,0 +1,110 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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 "nghttp3_str.h" + +#include <string.h> +#include <assert.h> + +uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n) { + memcpy(dest, src, n); + return dest + n; +} + +/* Generated by gendowncasetbl.py */ +static const uint8_t DOWNCASE_TBL[] = { + 0 /* NUL */, 1 /* SOH */, 2 /* STX */, 3 /* ETX */, + 4 /* EOT */, 5 /* ENQ */, 6 /* ACK */, 7 /* BEL */, + 8 /* BS */, 9 /* HT */, 10 /* LF */, 11 /* VT */, + 12 /* FF */, 13 /* CR */, 14 /* SO */, 15 /* SI */, + 16 /* DLE */, 17 /* DC1 */, 18 /* DC2 */, 19 /* DC3 */, + 20 /* DC4 */, 21 /* NAK */, 22 /* SYN */, 23 /* ETB */, + 24 /* CAN */, 25 /* EM */, 26 /* SUB */, 27 /* ESC */, + 28 /* FS */, 29 /* GS */, 30 /* RS */, 31 /* US */, + 32 /* SPC */, 33 /* ! */, 34 /* " */, 35 /* # */, + 36 /* $ */, 37 /* % */, 38 /* & */, 39 /* ' */, + 40 /* ( */, 41 /* ) */, 42 /* * */, 43 /* + */, + 44 /* , */, 45 /* - */, 46 /* . */, 47 /* / */, + 48 /* 0 */, 49 /* 1 */, 50 /* 2 */, 51 /* 3 */, + 52 /* 4 */, 53 /* 5 */, 54 /* 6 */, 55 /* 7 */, + 56 /* 8 */, 57 /* 9 */, 58 /* : */, 59 /* ; */, + 60 /* < */, 61 /* = */, 62 /* > */, 63 /* ? */, + 64 /* @ */, 97 /* A */, 98 /* B */, 99 /* C */, + 100 /* D */, 101 /* E */, 102 /* F */, 103 /* G */, + 104 /* H */, 105 /* I */, 106 /* J */, 107 /* K */, + 108 /* L */, 109 /* M */, 110 /* N */, 111 /* O */, + 112 /* P */, 113 /* Q */, 114 /* R */, 115 /* S */, + 116 /* T */, 117 /* U */, 118 /* V */, 119 /* W */, + 120 /* X */, 121 /* Y */, 122 /* Z */, 91 /* [ */, + 92 /* \ */, 93 /* ] */, 94 /* ^ */, 95 /* _ */, + 96 /* ` */, 97 /* a */, 98 /* b */, 99 /* c */, + 100 /* d */, 101 /* e */, 102 /* f */, 103 /* g */, + 104 /* h */, 105 /* i */, 106 /* j */, 107 /* k */, + 108 /* l */, 109 /* m */, 110 /* n */, 111 /* o */, + 112 /* p */, 113 /* q */, 114 /* r */, 115 /* s */, + 116 /* t */, 117 /* u */, 118 /* v */, 119 /* w */, + 120 /* x */, 121 /* y */, 122 /* z */, 123 /* { */, + 124 /* | */, 125 /* } */, 126 /* ~ */, 127 /* DEL */, + 128 /* 0x80 */, 129 /* 0x81 */, 130 /* 0x82 */, 131 /* 0x83 */, + 132 /* 0x84 */, 133 /* 0x85 */, 134 /* 0x86 */, 135 /* 0x87 */, + 136 /* 0x88 */, 137 /* 0x89 */, 138 /* 0x8a */, 139 /* 0x8b */, + 140 /* 0x8c */, 141 /* 0x8d */, 142 /* 0x8e */, 143 /* 0x8f */, + 144 /* 0x90 */, 145 /* 0x91 */, 146 /* 0x92 */, 147 /* 0x93 */, + 148 /* 0x94 */, 149 /* 0x95 */, 150 /* 0x96 */, 151 /* 0x97 */, + 152 /* 0x98 */, 153 /* 0x99 */, 154 /* 0x9a */, 155 /* 0x9b */, + 156 /* 0x9c */, 157 /* 0x9d */, 158 /* 0x9e */, 159 /* 0x9f */, + 160 /* 0xa0 */, 161 /* 0xa1 */, 162 /* 0xa2 */, 163 /* 0xa3 */, + 164 /* 0xa4 */, 165 /* 0xa5 */, 166 /* 0xa6 */, 167 /* 0xa7 */, + 168 /* 0xa8 */, 169 /* 0xa9 */, 170 /* 0xaa */, 171 /* 0xab */, + 172 /* 0xac */, 173 /* 0xad */, 174 /* 0xae */, 175 /* 0xaf */, + 176 /* 0xb0 */, 177 /* 0xb1 */, 178 /* 0xb2 */, 179 /* 0xb3 */, + 180 /* 0xb4 */, 181 /* 0xb5 */, 182 /* 0xb6 */, 183 /* 0xb7 */, + 184 /* 0xb8 */, 185 /* 0xb9 */, 186 /* 0xba */, 187 /* 0xbb */, + 188 /* 0xbc */, 189 /* 0xbd */, 190 /* 0xbe */, 191 /* 0xbf */, + 192 /* 0xc0 */, 193 /* 0xc1 */, 194 /* 0xc2 */, 195 /* 0xc3 */, + 196 /* 0xc4 */, 197 /* 0xc5 */, 198 /* 0xc6 */, 199 /* 0xc7 */, + 200 /* 0xc8 */, 201 /* 0xc9 */, 202 /* 0xca */, 203 /* 0xcb */, + 204 /* 0xcc */, 205 /* 0xcd */, 206 /* 0xce */, 207 /* 0xcf */, + 208 /* 0xd0 */, 209 /* 0xd1 */, 210 /* 0xd2 */, 211 /* 0xd3 */, + 212 /* 0xd4 */, 213 /* 0xd5 */, 214 /* 0xd6 */, 215 /* 0xd7 */, + 216 /* 0xd8 */, 217 /* 0xd9 */, 218 /* 0xda */, 219 /* 0xdb */, + 220 /* 0xdc */, 221 /* 0xdd */, 222 /* 0xde */, 223 /* 0xdf */, + 224 /* 0xe0 */, 225 /* 0xe1 */, 226 /* 0xe2 */, 227 /* 0xe3 */, + 228 /* 0xe4 */, 229 /* 0xe5 */, 230 /* 0xe6 */, 231 /* 0xe7 */, + 232 /* 0xe8 */, 233 /* 0xe9 */, 234 /* 0xea */, 235 /* 0xeb */, + 236 /* 0xec */, 237 /* 0xed */, 238 /* 0xee */, 239 /* 0xef */, + 240 /* 0xf0 */, 241 /* 0xf1 */, 242 /* 0xf2 */, 243 /* 0xf3 */, + 244 /* 0xf4 */, 245 /* 0xf5 */, 246 /* 0xf6 */, 247 /* 0xf7 */, + 248 /* 0xf8 */, 249 /* 0xf9 */, 250 /* 0xfa */, 251 /* 0xfb */, + 252 /* 0xfc */, 253 /* 0xfd */, 254 /* 0xfe */, 255 /* 0xff */, +}; + +void nghttp3_downcase(uint8_t *s, size_t len) { + size_t i; + for (i = 0; i < len; ++i) { + s[i] = DOWNCASE_TBL[s[i]]; + } +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_str.h b/deps/ngtcp2/nghttp3/lib/nghttp3_str.h new file mode 100644 index 00000000000000..19c1d2c71b559b --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_str.h @@ -0,0 +1,40 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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 NGHTTP3_STR_H +#define NGHTTP3_STR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +uint8_t *nghttp3_cpymem(uint8_t *dest, const uint8_t *src, size_t n); + +void nghttp3_downcase(uint8_t *s, size_t len); + +#endif /* NGHTTP3_STR_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c new file mode 100644 index 00000000000000..96d60fe82f9a76 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.c @@ -0,0 +1,1287 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 "nghttp3_stream.h" + +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "nghttp3_conv.h" +#include "nghttp3_macro.h" +#include "nghttp3_frame.h" +#include "nghttp3_conn.h" +#include "nghttp3_str.h" +#include "nghttp3_http.h" + +/* NGHTTP3_STREAM_MAX_COPY_THRES is the maximum size of buffer which + makes a copy to outq. */ +#define NGHTTP3_STREAM_MAX_COPY_THRES 128 + +/* NGHTTP3_MIN_RBLEN is the minimum length of nghttp3_ringbuf */ +#define NGHTTP3_MIN_RBLEN 4 + +int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, + uint64_t seq, const nghttp3_stream_callbacks *callbacks, + const nghttp3_mem *mem) { + int rv; + nghttp3_stream *stream = nghttp3_mem_calloc(mem, 1, sizeof(nghttp3_stream)); + nghttp3_node_id nid; + + if (stream == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + nghttp3_tnode_init( + &stream->node, + nghttp3_node_id_init(&nid, NGHTTP3_NODE_ID_TYPE_STREAM, stream_id), seq, + NGHTTP3_DEFAULT_URGENCY); + + rv = nghttp3_ringbuf_init(&stream->frq, 0, sizeof(nghttp3_frame_entry), mem); + if (rv != 0) { + goto frq_init_fail; + } + + rv = nghttp3_ringbuf_init(&stream->chunks, 0, sizeof(nghttp3_buf), mem); + if (rv != 0) { + goto chunks_init_fail; + } + + rv = nghttp3_ringbuf_init(&stream->outq, 0, sizeof(nghttp3_typed_buf), mem); + if (rv != 0) { + goto outq_init_fail; + } + + rv = nghttp3_ringbuf_init(&stream->inq, 0, sizeof(nghttp3_buf), mem); + if (rv != 0) { + goto inq_init_fail; + } + + nghttp3_qpack_stream_context_init(&stream->qpack_sctx, stream_id, mem); + + stream->me.key = (key_type)stream_id; + stream->qpack_blocked_pe.index = NGHTTP3_PQ_BAD_INDEX; + stream->mem = mem; + stream->rx.http.status_code = -1; + stream->rx.http.content_length = -1; + stream->rx.http.pri = NGHTTP3_DEFAULT_URGENCY; + stream->error_code = NGHTTP3_H3_NO_ERROR; + + if (callbacks) { + stream->callbacks = *callbacks; + } + + *pstream = stream; + + return 0; + +inq_init_fail: + nghttp3_ringbuf_free(&stream->outq); +outq_init_fail: + nghttp3_ringbuf_free(&stream->chunks); +chunks_init_fail: + nghttp3_ringbuf_free(&stream->frq); +frq_init_fail: + nghttp3_mem_free(mem, stream); + + return rv; +} + +static void delete_outq(nghttp3_ringbuf *outq, const nghttp3_mem *mem) { + nghttp3_typed_buf *tbuf; + size_t i, len = nghttp3_ringbuf_len(outq); + + for (i = 0; i < len; ++i) { + tbuf = nghttp3_ringbuf_get(outq, i); + if (tbuf->type == NGHTTP3_BUF_TYPE_PRIVATE) { + nghttp3_buf_free(&tbuf->buf, mem); + } + } + + nghttp3_ringbuf_free(outq); +} + +static void delete_chunks(nghttp3_ringbuf *chunks, const nghttp3_mem *mem) { + nghttp3_buf *buf; + size_t i, len = nghttp3_ringbuf_len(chunks); + + for (i = 0; i < len; ++i) { + buf = nghttp3_ringbuf_get(chunks, i); + nghttp3_buf_free(buf, mem); + } + + nghttp3_ringbuf_free(chunks); +} + +static void delete_frq(nghttp3_ringbuf *frq, const nghttp3_mem *mem) { + nghttp3_frame_entry *frent; + size_t i, len = nghttp3_ringbuf_len(frq); + + for (i = 0; i < len; ++i) { + frent = nghttp3_ringbuf_get(frq, i); + switch (frent->fr.hd.type) { + case NGHTTP3_FRAME_HEADERS: + nghttp3_frame_headers_free(&frent->fr.headers, mem); + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + nghttp3_frame_push_promise_free(&frent->fr.push_promise, mem); + break; + default: + break; + } + } + + nghttp3_ringbuf_free(frq); +} + +void nghttp3_stream_del(nghttp3_stream *stream) { + if (stream == NULL) { + return; + } + + nghttp3_qpack_stream_context_free(&stream->qpack_sctx); + delete_chunks(&stream->inq, stream->mem); + delete_outq(&stream->outq, stream->mem); + delete_chunks(&stream->chunks, stream->mem); + delete_frq(&stream->frq, stream->mem); + nghttp3_tnode_free(&stream->node); + + nghttp3_mem_free(stream->mem, stream); +} + +void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint) { + memset(rvint, 0, sizeof(*rvint)); +} + +void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate) { + memset(rstate, 0, sizeof(*rstate)); +} + +nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, + const uint8_t *src, size_t srclen, int fin) { + size_t nread = 0; + size_t n; + size_t i; + + assert(srclen > 0); + + if (rvint->left == 0) { + assert(rvint->acc == 0); + + rvint->left = nghttp3_get_varint_len(src); + if (rvint->left <= srclen) { + rvint->acc = nghttp3_get_varint(&nread, src); + rvint->left = 0; + return (nghttp3_ssize)nread; + } + + if (fin) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + rvint->acc = nghttp3_get_varint_fb(src); + nread = 1; + ++src; + --srclen; + --rvint->left; + } + + n = nghttp3_min(rvint->left, srclen); + + for (i = 0; i < n; ++i) { + rvint->acc = (rvint->acc << 8) + src[i]; + } + + rvint->left -= n; + nread += n; + + if (fin && rvint->left) { + return NGHTTP3_ERR_INVALID_ARGUMENT; + } + + return (nghttp3_ssize)nread; +} + +int nghttp3_stream_frq_add(nghttp3_stream *stream, + const nghttp3_frame_entry *frent) { + nghttp3_ringbuf *frq = &stream->frq; + nghttp3_frame_entry *dest; + int rv; + + if (nghttp3_ringbuf_full(frq)) { + size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(frq) * 2); + rv = nghttp3_ringbuf_reserve(frq, nlen); + if (rv != 0) { + return rv; + } + } + + dest = nghttp3_ringbuf_push_back(frq); + *dest = *frent; + + return 0; +} + +int nghttp3_stream_fill_outq(nghttp3_stream *stream) { + nghttp3_ringbuf *frq = &stream->frq; + nghttp3_frame_entry *frent; + int data_eof; + int rv; + + for (; nghttp3_ringbuf_len(frq) && !nghttp3_stream_outq_is_full(stream) && + stream->unsent_bytes < NGHTTP3_MIN_UNSENT_BYTES;) { + frent = nghttp3_ringbuf_get(frq, 0); + + switch (frent->fr.hd.type) { + case NGHTTP3_FRAME_SETTINGS: + rv = nghttp3_stream_write_settings(stream, frent); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_FRAME_HEADERS: + rv = nghttp3_stream_write_headers(stream, frent); + if (rv != 0) { + return rv; + } + nghttp3_frame_headers_free(&frent->fr.headers, stream->mem); + break; + case NGHTTP3_FRAME_PUSH_PROMISE: + rv = nghttp3_stream_write_push_promise(stream, frent); + if (rv != 0) { + return rv; + } + nghttp3_frame_push_promise_free(&frent->fr.push_promise, stream->mem); + break; + case NGHTTP3_FRAME_CANCEL_PUSH: + rv = nghttp3_stream_write_cancel_push(stream, frent); + if (rv != 0) { + return rv; + } + break; + case NGHTTP3_FRAME_DATA: + rv = nghttp3_stream_write_data(stream, &data_eof, frent); + if (rv != 0) { + return rv; + } + if (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED) { + return 0; + } + if (!data_eof) { + return 0; + } + break; + case NGHTTP3_FRAME_MAX_PUSH_ID: + if (stream->conn->flags & NGHTTP3_CONN_FLAG_GOAWAY_QUEUED) { + break; + } + rv = nghttp3_stream_write_max_push_id(stream, frent); + if (rv != 0) { + return rv; + } + break; + default: + /* TODO Not implemented */ + break; + } + + nghttp3_ringbuf_pop_front(frq); + } + + return 0; +} + +static void typed_buf_shared_init(nghttp3_typed_buf *tbuf, + const nghttp3_buf *chunk) { + nghttp3_typed_buf_init(tbuf, chunk, NGHTTP3_BUF_TYPE_SHARED); + tbuf->buf.pos = tbuf->buf.last; +} + +int nghttp3_stream_write_stream_type(nghttp3_stream *stream) { + size_t len = nghttp3_put_varint_len((int64_t)stream->type); + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + int rv; + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream) { + size_t len; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + int rv; + nghttp3_push_promise *pp = stream->pp; + + assert(stream->type == NGHTTP3_STREAM_TYPE_PUSH); + assert(pp); + + len = nghttp3_put_varint_len((int64_t)stream->type) + + nghttp3_put_varint_len(pp->node.nid.id); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_put_varint(chunk->last, (int64_t)stream->type); + chunk->last = nghttp3_put_varint(chunk->last, pp->node.nid.id); + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_settings(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + size_t len; + int rv; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + struct { + nghttp3_frame_settings settings; + nghttp3_settings_entry iv[15]; + } fr; + nghttp3_settings_entry *iv; + nghttp3_settings *local_settings = frent->aux.settings.local_settings; + + fr.settings.hd.type = NGHTTP3_FRAME_SETTINGS; + fr.settings.niv = 3; + iv = &fr.settings.iv[0]; + + iv[0].id = NGHTTP3_SETTINGS_ID_MAX_FIELD_SECTION_SIZE; + iv[0].value = local_settings->max_field_section_size; + iv[1].id = NGHTTP3_SETTINGS_ID_QPACK_MAX_TABLE_CAPACITY; + iv[1].value = local_settings->qpack_max_table_capacity; + iv[2].id = NGHTTP3_SETTINGS_ID_QPACK_BLOCKED_STREAMS; + iv[2].value = local_settings->qpack_blocked_streams; + + len = nghttp3_frame_write_settings_len(&fr.settings.hd.length, &fr.settings); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_settings(chunk->last, &fr.settings); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_cancel_push *fr = &frent->fr.cancel_push; + size_t len; + int rv; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + + len = nghttp3_frame_write_cancel_push_len(&fr->hd.length, fr); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_cancel_push(chunk->last, fr); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_max_push_id *fr = &frent->fr.max_push_id; + nghttp3_conn *conn = stream->conn; + size_t len; + int rv; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + + assert(conn); + assert(conn->flags & NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED); + + fr->push_id = (int64_t)conn->remote.uni.unsent_max_pushes - 1; + conn->remote.uni.max_pushes = conn->remote.uni.unsent_max_pushes; + conn->flags &= (uint16_t)~NGHTTP3_CONN_FLAG_MAX_PUSH_ID_QUEUED; + + len = nghttp3_frame_write_max_push_id_len(&fr->hd.length, fr); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_max_push_id(chunk->last, fr); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_write_headers(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_headers *fr = &frent->fr.headers; + nghttp3_conn *conn = stream->conn; + + assert(conn); + + return nghttp3_stream_write_header_block( + stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, + &conn->tx.qpack.ebuf, NGHTTP3_FRAME_HEADERS, 0, fr->nva, fr->nvlen); +} + +int nghttp3_stream_write_push_promise(nghttp3_stream *stream, + nghttp3_frame_entry *frent) { + nghttp3_frame_push_promise *fr = &frent->fr.push_promise; + nghttp3_conn *conn = stream->conn; + + assert(conn); + + return nghttp3_stream_write_header_block( + stream, &conn->qenc, conn->tx.qenc, &conn->tx.qpack.rbuf, + &conn->tx.qpack.ebuf, NGHTTP3_FRAME_PUSH_PROMISE, fr->push_id, fr->nva, + fr->nvlen); +} + +int nghttp3_stream_write_header_block(nghttp3_stream *stream, + nghttp3_qpack_encoder *qenc, + nghttp3_stream *qenc_stream, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + int64_t frame_type, int64_t push_id, + const nghttp3_nv *nva, size_t nvlen) { + nghttp3_buf pbuf; + int rv; + size_t len; + nghttp3_buf *chunk; + nghttp3_typed_buf tbuf; + nghttp3_frame_hd hd; + size_t push_idlen = 0; + uint8_t raw_pbuf[16]; + size_t pbuflen, rbuflen, ebuflen; + + nghttp3_buf_wrap_init(&pbuf, raw_pbuf, sizeof(raw_pbuf)); + + rv = nghttp3_qpack_encoder_encode(qenc, &pbuf, rbuf, ebuf, + stream->node.nid.id, nva, nvlen); + if (rv != 0) { + goto fail; + } + + pbuflen = nghttp3_buf_len(&pbuf); + rbuflen = nghttp3_buf_len(rbuf); + ebuflen = nghttp3_buf_len(ebuf); + + if (frame_type == NGHTTP3_FRAME_PUSH_PROMISE) { + push_idlen = nghttp3_put_varint_len(push_id); + } + + hd.type = frame_type; + hd.length = (int64_t)(pbuflen + rbuflen + push_idlen); + + len = nghttp3_frame_write_hd_len(&hd) + push_idlen + pbuflen; + + if (rbuflen <= NGHTTP3_STREAM_MAX_COPY_THRES) { + len += rbuflen; + } + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + goto fail; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + + if (push_idlen) { + chunk->last = nghttp3_put_varint(chunk->last, push_id); + } + + chunk->last = nghttp3_cpymem(chunk->last, pbuf.pos, pbuflen); + nghttp3_buf_init(&pbuf); + + if (rbuflen > NGHTTP3_STREAM_MAX_COPY_THRES) { + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + goto fail; + } + + nghttp3_typed_buf_init(&tbuf, rbuf, NGHTTP3_BUF_TYPE_PRIVATE); + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + goto fail; + } + nghttp3_buf_init(rbuf); + } else if (rbuflen) { + chunk->last = nghttp3_cpymem(chunk->last, rbuf->pos, rbuflen); + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + goto fail; + } + nghttp3_buf_reset(rbuf); + } + + if (ebuflen > NGHTTP3_STREAM_MAX_COPY_THRES) { + assert(qenc_stream); + + nghttp3_typed_buf_init(&tbuf, ebuf, NGHTTP3_BUF_TYPE_PRIVATE); + rv = nghttp3_stream_outq_add(qenc_stream, &tbuf); + if (rv != 0) { + return rv; + } + nghttp3_buf_init(ebuf); + } else if (ebuflen) { + assert(qenc_stream); + + rv = nghttp3_stream_ensure_chunk(qenc_stream, ebuflen); + if (rv != 0) { + goto fail; + } + + chunk = nghttp3_stream_get_chunk(qenc_stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_cpymem(chunk->last, ebuf->pos, ebuflen); + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(qenc_stream, &tbuf); + if (rv != 0) { + goto fail; + } + nghttp3_buf_reset(ebuf); + } + + assert(0 == nghttp3_buf_len(&pbuf)); + assert(0 == nghttp3_buf_len(rbuf)); + assert(0 == nghttp3_buf_len(ebuf)); + + return 0; + +fail: + + return rv; +} + +int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, + nghttp3_frame_entry *frent) { + int rv; + size_t len; + nghttp3_typed_buf tbuf; + nghttp3_buf buf; + nghttp3_buf *chunk; + nghttp3_read_data_callback read_data = frent->aux.data.dr.read_data; + nghttp3_conn *conn = stream->conn; + size_t datalen; + uint32_t flags = 0; + nghttp3_frame_hd hd; + nghttp3_vec vec[8]; + nghttp3_vec *v; + nghttp3_ssize sveccnt; + size_t i; + + assert(!(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); + assert(read_data); + assert(conn); + + *peof = 0; + + sveccnt = read_data(conn, stream->node.nid.id, vec, nghttp3_arraylen(vec), + &flags, conn->user_data, stream->user_data); + if (sveccnt < 0) { + if (sveccnt == NGHTTP3_ERR_WOULDBLOCK) { + stream->flags |= NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED; + return 0; + } + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + + datalen = nghttp3_vec_len(vec, (size_t)sveccnt); + + assert(datalen || flags & NGHTTP3_DATA_FLAG_EOF); + + if (flags & NGHTTP3_DATA_FLAG_EOF) { + *peof = 1; + if (!(flags & NGHTTP3_DATA_FLAG_NO_END_STREAM)) { + stream->flags |= NGHTTP3_STREAM_FLAG_WRITE_END_STREAM; + if (datalen == 0) { + if (nghttp3_stream_outq_write_done(stream)) { + /* If this is the last data and its is 0 length, we don't + need send DATA frame. We rely on the non-emptiness of + outq to schedule stream, so add empty tbuf to outq to + just send fin. */ + nghttp3_buf_init(&buf); + nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_PRIVATE); + return nghttp3_stream_outq_add(stream, &tbuf); + } + return 0; + } + } + + if (datalen == 0) { + /* We are going to send more frames, but no DATA frame this + time. */ + return 0; + } + } + + hd.type = NGHTTP3_FRAME_DATA; + hd.length = (int64_t)datalen; + + len = nghttp3_frame_write_hd_len(&hd); + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + chunk->last = nghttp3_frame_write_hd(chunk->last, &hd); + + tbuf.buf.last = chunk->last; + + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + return rv; + } + + if (datalen) { + for (i = 0; i < (size_t)sveccnt; ++i) { + v = &vec[i]; + if (v->len == 0) { + continue; + } + nghttp3_buf_wrap_init(&buf, v->base, v->len); + buf.last = buf.end; + nghttp3_typed_buf_init(&tbuf, &buf, NGHTTP3_BUF_TYPE_ALIEN); + rv = nghttp3_stream_outq_add(stream, &tbuf); + if (rv != 0) { + return rv; + } + } + } + + return 0; +} + +int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream) { + nghttp3_qpack_decoder *qdec; + nghttp3_buf *chunk; + int rv; + nghttp3_typed_buf tbuf; + size_t len; + + assert(stream->conn); + assert(stream->conn->tx.qdec == stream); + + qdec = &stream->conn->qdec; + + assert(qdec); + + len = nghttp3_qpack_decoder_get_decoder_streamlen(qdec); + if (len == 0) { + return 0; + } + + rv = nghttp3_stream_ensure_chunk(stream, len); + if (rv != 0) { + return rv; + } + + chunk = nghttp3_stream_get_chunk(stream); + typed_buf_shared_init(&tbuf, chunk); + + nghttp3_qpack_decoder_write_decoder(qdec, chunk); + + tbuf.buf.last = chunk->last; + + return nghttp3_stream_outq_add(stream, &tbuf); +} + +int nghttp3_stream_outq_is_full(nghttp3_stream *stream) { + /* TODO Verify that the limit is reasonable. */ + return nghttp3_ringbuf_len(&stream->outq) >= 1024; +} + +int nghttp3_stream_outq_add(nghttp3_stream *stream, + const nghttp3_typed_buf *tbuf) { + nghttp3_ringbuf *outq = &stream->outq; + int rv; + nghttp3_typed_buf *dest; + size_t len = nghttp3_ringbuf_len(outq); + + stream->unsent_bytes += nghttp3_buf_len(&tbuf->buf); + + if (len) { + dest = nghttp3_ringbuf_get(outq, len - 1); + if (dest->type == tbuf->type && dest->type == NGHTTP3_BUF_TYPE_SHARED && + dest->buf.begin == tbuf->buf.begin && dest->buf.last == tbuf->buf.pos) { + /* If we have already written last entry, adjust outq_idx and + offset so that this entry is eligible to send. */ + if (len == stream->outq_idx) { + --stream->outq_idx; + stream->outq_offset = nghttp3_buf_len(&dest->buf); + } + + dest->buf.last = tbuf->buf.last; + /* TODO Is this required? */ + dest->buf.end = tbuf->buf.end; + + return 0; + } + } + + if (nghttp3_ringbuf_full(outq)) { + size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2); + rv = nghttp3_ringbuf_reserve(outq, nlen); + if (rv != 0) { + return rv; + } + } + + dest = nghttp3_ringbuf_push_back(outq); + *dest = *tbuf; + + return 0; +} + +int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need) { + nghttp3_ringbuf *chunks = &stream->chunks; + nghttp3_buf *chunk; + size_t len = nghttp3_ringbuf_len(chunks); + uint8_t *p; + int rv; + size_t n = NGHTTP3_STREAM_MIN_CHUNK_SIZE; + + if (len) { + chunk = nghttp3_ringbuf_get(chunks, len - 1); + if (nghttp3_buf_left(chunk) >= need) { + return 0; + } + } + + for (; n < need; n *= 2) + ; + + p = nghttp3_mem_malloc(stream->mem, n); + if (p == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + if (nghttp3_ringbuf_full(chunks)) { + size_t nlen = nghttp3_max(NGHTTP3_MIN_RBLEN, len * 2); + rv = nghttp3_ringbuf_reserve(chunks, nlen); + if (rv != 0) { + return rv; + } + } + + chunk = nghttp3_ringbuf_push_back(chunks); + nghttp3_buf_wrap_init(chunk, p, n); + + return 0; +} + +nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream) { + nghttp3_ringbuf *chunks = &stream->chunks; + size_t len = nghttp3_ringbuf_len(chunks); + + assert(len); + + return nghttp3_ringbuf_get(chunks, len - 1); +} + +int nghttp3_stream_is_blocked(nghttp3_stream *stream) { + return (stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) || + (stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR) || + (stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED); +} + +int nghttp3_stream_require_schedule(nghttp3_stream *stream) { + return (!nghttp3_stream_outq_write_done(stream) && + !(stream->flags & NGHTTP3_STREAM_FLAG_FC_BLOCKED) && + !(stream->flags & NGHTTP3_STREAM_FLAG_SHUT_WR)) || + (nghttp3_ringbuf_len(&stream->frq) && + !(stream->flags & NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED)); +} + +nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, + nghttp3_vec *vec, size_t veccnt) { + nghttp3_ringbuf *outq = &stream->outq; + size_t len = nghttp3_ringbuf_len(outq); + size_t i; + size_t offset = stream->outq_offset; + size_t buflen; + nghttp3_vec *vbegin = vec, *vend = vec + veccnt; + nghttp3_typed_buf *tbuf; + + assert(veccnt > 0); + + for (i = stream->outq_idx; i < len; ++i) { + tbuf = nghttp3_ringbuf_get(outq, i); + buflen = nghttp3_buf_len(&tbuf->buf); + if (offset >= buflen) { + offset -= buflen; + continue; + } + + vec->base = tbuf->buf.pos + offset; + vec->len = buflen - offset; + ++vec; + ++i; + break; + } + + for (; i < len && vec != vend; ++i, ++vec) { + tbuf = nghttp3_ringbuf_get(outq, i); + vec->base = tbuf->buf.pos; + vec->len = nghttp3_buf_len(&tbuf->buf); + } + + /* TODO Rework this if we have finished implementing HTTP + messaging */ + *pfin = nghttp3_ringbuf_len(&stream->frq) == 0 && i == len && + (stream->flags & NGHTTP3_STREAM_FLAG_WRITE_END_STREAM); + + return vec - vbegin; +} + +int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n) { + nghttp3_ringbuf *outq = &stream->outq; + size_t i; + size_t len = nghttp3_ringbuf_len(outq); + size_t offset = stream->outq_offset + n; + size_t buflen; + nghttp3_typed_buf *tbuf; + + for (i = stream->outq_idx; i < len; ++i) { + tbuf = nghttp3_ringbuf_get(outq, i); + buflen = nghttp3_buf_len(&tbuf->buf); + if (offset >= buflen) { + offset -= buflen; + continue; + } + + break; + } + + assert(i < len || offset == 0); + + stream->unsent_bytes -= n; + stream->outq_idx = i; + stream->outq_offset = offset; + + return 0; +} + +int nghttp3_stream_outq_write_done(nghttp3_stream *stream) { + nghttp3_ringbuf *outq = &stream->outq; + size_t len = nghttp3_ringbuf_len(outq); + + return len == 0 || stream->outq_idx >= len; +} + +static int stream_pop_outq_entry(nghttp3_stream *stream, + nghttp3_typed_buf *tbuf) { + nghttp3_ringbuf *chunks = &stream->chunks; + nghttp3_buf *chunk; + + switch (tbuf->type) { + case NGHTTP3_BUF_TYPE_PRIVATE: + nghttp3_buf_free(&tbuf->buf, stream->mem); + break; + case NGHTTP3_BUF_TYPE_ALIEN: + break; + default: + assert(nghttp3_ringbuf_len(chunks)); + + chunk = nghttp3_ringbuf_get(chunks, 0); + + assert(chunk->begin == tbuf->buf.begin); + assert(chunk->end == tbuf->buf.end); + + if (chunk->last == tbuf->buf.last) { + nghttp3_buf_free(chunk, stream->mem); + nghttp3_ringbuf_pop_front(chunks); + } + }; + + nghttp3_ringbuf_pop_front(&stream->outq); + + return 0; +} + +int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n) { + nghttp3_ringbuf *outq = &stream->outq; + uint64_t offset = stream->ack_offset + n; + size_t buflen; + size_t npopped = 0; + size_t nack; + nghttp3_typed_buf *tbuf; + int rv; + + for (; nghttp3_ringbuf_len(outq);) { + tbuf = nghttp3_ringbuf_get(outq, 0); + buflen = nghttp3_buf_len(&tbuf->buf); + + if (tbuf->type == NGHTTP3_BUF_TYPE_ALIEN) { + nack = (size_t)nghttp3_min(offset, (uint64_t)buflen) - stream->ack_done; + if (stream->callbacks.acked_data) { + rv = stream->callbacks.acked_data(stream, stream->node.nid.id, nack, + stream->user_data); + if (rv != 0) { + return NGHTTP3_ERR_CALLBACK_FAILURE; + } + } + stream->ack_done += nack; + } + + if (offset >= buflen) { + rv = stream_pop_outq_entry(stream, tbuf); + if (rv != 0) { + return rv; + } + + offset -= buflen; + ++npopped; + stream->ack_done = 0; + + if (stream->outq_idx + 1 == npopped) { + stream->outq_offset = 0; + break; + } + + continue; + } + + break; + } + + assert(stream->outq_idx + 1 >= npopped); + if (stream->outq_idx >= npopped) { + stream->outq_idx -= npopped; + } else { + stream->outq_idx = 0; + } + + stream->ack_offset = offset; + + return 0; +} + +int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *data, + size_t datalen) { + nghttp3_ringbuf *inq = &stream->inq; + size_t len = nghttp3_ringbuf_len(inq); + nghttp3_buf *buf; + size_t nwrite; + uint8_t *rawbuf; + size_t bufleft; + int rv; + + if (len) { + buf = nghttp3_ringbuf_get(inq, len - 1); + bufleft = nghttp3_buf_left(buf); + nwrite = nghttp3_min(datalen, bufleft); + buf->last = nghttp3_cpymem(buf->last, data, nwrite); + data += nwrite; + datalen -= nwrite; + } + + for (; datalen;) { + if (nghttp3_ringbuf_full(inq)) { + size_t nlen = + nghttp3_max(NGHTTP3_MIN_RBLEN, nghttp3_ringbuf_len(inq) * 2); + rv = nghttp3_ringbuf_reserve(inq, nlen); + if (rv != 0) { + return rv; + } + } + + rawbuf = nghttp3_mem_malloc(stream->mem, 16384); + if (rawbuf == NULL) { + return NGHTTP3_ERR_NOMEM; + } + + buf = nghttp3_ringbuf_push_back(inq); + nghttp3_buf_wrap_init(buf, rawbuf, 16384); + bufleft = nghttp3_buf_left(buf); + nwrite = nghttp3_min(datalen, bufleft); + buf->last = nghttp3_cpymem(buf->last, data, nwrite); + data += nwrite; + datalen -= nwrite; + } + + return 0; +} + +size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream) { + nghttp3_ringbuf *inq = &stream->inq; + size_t len = nghttp3_ringbuf_len(inq); + size_t i, n = 0; + nghttp3_buf *buf; + + for (i = 0; i < len; ++i) { + buf = nghttp3_ringbuf_get(inq, i); + n += nghttp3_buf_len(buf); + } + + return n; +} + +int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, + nghttp3_stream_http_event event) { + int rv; + + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_NONE: + return NGHTTP3_ERR_H3_INTERNAL_ERROR; + case NGHTTP3_HTTP_STATE_REQ_INITIAL: + switch (event) { + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_HEADERS_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_HEADERS_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + /* TODO Better to check status code */ + if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_DATA_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_DATA_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + /* TODO Better to check status code */ + if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_TRAILERS_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_END: + if (event != NGHTTP3_HTTP_EVENT_MSG_END) { + /* TODO Should ignore unexpected frame in this state as per + spec. */ + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_REQ_END; + return 0; + case NGHTTP3_HTTP_STATE_REQ_END: + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + case NGHTTP3_HTTP_STATE_RESP_INITIAL: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_BEGIN) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; + return 0; + case NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_HEADERS_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + if (stream->rx.http.status_code == -1) { + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN; + return 0; + } + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) && + stream->rx.http.status_code / 100 == 2) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + if (stream->rx.http.flags & NGHTTP3_HTTP_FLAG_EXPECT_FINAL_RESPONSE) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_DATA_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_DATA_END: + switch (event) { + case NGHTTP3_HTTP_EVENT_DATA_BEGIN: + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_HEADERS_BEGIN: + if ((stream->rx.http.flags & NGHTTP3_HTTP_FLAG_METH_CONNECT) && + stream->rx.http.status_code / 100 == 2) { + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + rv = nghttp3_http_on_remote_end_stream(stream); + if (rv != 0) { + return rv; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN; + return 0; + case NGHTTP3_HTTP_EVENT_MSG_END: + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; + return 0; + default: + return NGHTTP3_ERR_H3_FRAME_UNEXPECTED; + } + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + if (event != NGHTTP3_HTTP_EVENT_HEADERS_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_TRAILERS_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_END: + if (event != NGHTTP3_HTTP_EVENT_MSG_END) { + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } + stream->rx.hstate = NGHTTP3_HTTP_STATE_RESP_END; + return 0; + case NGHTTP3_HTTP_STATE_RESP_END: + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + default: + assert(0); + } +} + +int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream) { + switch (stream->rx.hstate) { + case NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN: + case NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN: + return 0; + default: + return NGHTTP3_ERR_MALFORMED_HTTP_MESSAGING; + } +} + +int nghttp3_stream_bidi_or_push(nghttp3_stream *stream) { + return (!nghttp3_stream_uni(stream->node.nid.id) || + stream->type == NGHTTP3_STREAM_TYPE_PUSH); +} + +int nghttp3_stream_uni(int64_t stream_id) { return (stream_id & 0x2) != 0; } + +int nghttp3_client_stream_bidi(int64_t stream_id) { + return (stream_id & 0x3) == 0; +} + +int nghttp3_client_stream_uni(int64_t stream_id) { + return (stream_id & 0x3) == 0x2; +} + +int nghttp3_server_stream_uni(int64_t stream_id) { + return (stream_id & 0x3) == 0x3; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h new file mode 100644 index 00000000000000..f7e375c85eb7f5 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_stream.h @@ -0,0 +1,407 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 NGHTTP3_STREAM_H +#define NGHTTP3_STREAM_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_map.h" +#include "nghttp3_tnode.h" +#include "nghttp3_ringbuf.h" +#include "nghttp3_buf.h" +#include "nghttp3_frame.h" +#include "nghttp3_qpack.h" + +#define NGHTTP3_STREAM_MIN_CHUNK_SIZE 256 + +/* NGHTTP3_MIN_UNSENT_BYTES is the minimum unsent bytes which is large + enough to fill outgoing single QUIC packet. */ +#define NGHTTP3_MIN_UNSENT_BYTES 4096 + +/* NGHTTP3_STREAM_MIN_WRITELEN is the minimum length of write to cause + the stream to reschedule. */ +#define NGHTTP3_STREAM_MIN_WRITELEN 800 + +/* nghttp3_stream_type is unidirectional stream type. */ +typedef enum nghttp3_stream_type { + NGHTTP3_STREAM_TYPE_CONTROL = 0x00, + NGHTTP3_STREAM_TYPE_PUSH = 0x01, + NGHTTP3_STREAM_TYPE_QPACK_ENCODER = 0x02, + NGHTTP3_STREAM_TYPE_QPACK_DECODER = 0x03, + NGHTTP3_STREAM_TYPE_UNKNOWN = UINT64_MAX, +} nghttp3_stream_type; + +typedef enum nghttp3_ctrl_stream_state { + NGHTTP3_CTRL_STREAM_STATE_FRAME_TYPE, + NGHTTP3_CTRL_STREAM_STATE_FRAME_LENGTH, + NGHTTP3_CTRL_STREAM_STATE_CANCEL_PUSH, + NGHTTP3_CTRL_STREAM_STATE_SETTINGS, + NGHTTP3_CTRL_STREAM_STATE_GOAWAY, + NGHTTP3_CTRL_STREAM_STATE_MAX_PUSH_ID, + NGHTTP3_CTRL_STREAM_STATE_IGN_FRAME, + NGHTTP3_CTRL_STREAM_STATE_SETTINGS_ID, + NGHTTP3_CTRL_STREAM_STATE_SETTINGS_VALUE, +} nghttp3_ctrl_stream_state; + +typedef enum nghttp3_req_stream_state { + NGHTTP3_REQ_STREAM_STATE_FRAME_TYPE, + NGHTTP3_REQ_STREAM_STATE_FRAME_LENGTH, + NGHTTP3_REQ_STREAM_STATE_DATA, + NGHTTP3_REQ_STREAM_STATE_HEADERS, + NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE_PUSH_ID, + NGHTTP3_REQ_STREAM_STATE_PUSH_PROMISE, + NGHTTP3_REQ_STREAM_STATE_IGN_PUSH_PROMISE, + NGHTTP3_REQ_STREAM_STATE_IGN_FRAME, + NGHTTP3_REQ_STREAM_STATE_IGN_REST, +} nghttp3_req_stream_state; + +typedef enum nghttp3_push_stream_state { + NGHTTP3_PUSH_STREAM_STATE_FRAME_TYPE, + NGHTTP3_PUSH_STREAM_STATE_FRAME_LENGTH, + NGHTTP3_PUSH_STREAM_STATE_DATA, + NGHTTP3_PUSH_STREAM_STATE_HEADERS, + NGHTTP3_PUSH_STREAM_STATE_IGN_FRAME, + NGHTTP3_PUSH_STREAM_STATE_PUSH_ID, + NGHTTP3_PUSH_STREAM_STATE_IGN_REST, +} nghttp3_push_stream_state; + +typedef struct nghttp3_varint_read_state { + int64_t acc; + size_t left; +} nghttp3_varint_read_state; + +typedef struct nghttp3_stream_read_state { + nghttp3_varint_read_state rvint; + nghttp3_frame fr; + int state; + int64_t left; +} nghttp3_stream_read_state; + +/* NGHTTP3_STREAM_FLAG_NONE indicates that no flag is set. */ +#define NGHTTP3_STREAM_FLAG_NONE 0x0000 +/* NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED is set when a unidirectional + stream type is identified. */ +#define NGHTTP3_STREAM_FLAG_TYPE_IDENTIFIED 0x0001 +/* NGHTTP3_STREAM_FLAG_FC_BLOCKED indicates that stream is blocked by + QUIC flow control. */ +#define NGHTTP3_STREAM_FLAG_FC_BLOCKED 0x0002 +/* NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED indicates that application is + temporarily unable to provide data. */ +#define NGHTTP3_STREAM_FLAG_READ_DATA_BLOCKED 0x0004 +/* NGHTTP3_STREAM_FLAG_WRITE_END_STREAM indicates that application + finished to feed outgoing data. */ +#define NGHTTP3_STREAM_FLAG_WRITE_END_STREAM 0x0008 +/* NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED indicates that stream is + blocked due to QPACK decoding. */ +#define NGHTTP3_STREAM_FLAG_QPACK_DECODE_BLOCKED 0x0010 +/* NGHTTP3_STREAM_FLAG_READ_EOF indicates that remote endpoint sent + fin. */ +#define NGHTTP3_STREAM_FLAG_READ_EOF 0x0020 +/* NGHTTP3_STREAM_FLAG_CLOSED indicates that QUIC stream was closed. + nghttp3_stream object can still alive because it might be blocked + by QPACK decoder. */ +#define NGHTTP3_STREAM_FLAG_CLOSED 0x0040 +/* NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED indicates that stream is + blocked because the corresponding PUSH_PROMISE has not been + received yet. */ +#define NGHTTP3_STREAM_FLAG_PUSH_PROMISE_BLOCKED 0x0080 +/* NGHTTP3_STREAM_FLAG_SHUT_WR indicates that any further write + operation to a stream is prohibited. */ +#define NGHTTP3_STREAM_FLAG_SHUT_WR 0x0100 +/* NGHTTP3_STREAM_FLAG_RESET indicates that stream is reset. */ +#define NGHTTP3_STREAM_FLAG_RESET 0x0200 + +typedef enum nghttp3_stream_http_state { + NGHTTP3_HTTP_STATE_NONE, + NGHTTP3_HTTP_STATE_REQ_INITIAL, + NGHTTP3_HTTP_STATE_REQ_BEGIN, + NGHTTP3_HTTP_STATE_REQ_HEADERS_BEGIN, + NGHTTP3_HTTP_STATE_REQ_HEADERS_END, + NGHTTP3_HTTP_STATE_REQ_DATA_BEGIN, + NGHTTP3_HTTP_STATE_REQ_DATA_END, + NGHTTP3_HTTP_STATE_REQ_TRAILERS_BEGIN, + NGHTTP3_HTTP_STATE_REQ_TRAILERS_END, + NGHTTP3_HTTP_STATE_REQ_END, + NGHTTP3_HTTP_STATE_RESP_INITIAL, + NGHTTP3_HTTP_STATE_RESP_BEGIN, + NGHTTP3_HTTP_STATE_RESP_HEADERS_BEGIN, + NGHTTP3_HTTP_STATE_RESP_HEADERS_END, + NGHTTP3_HTTP_STATE_RESP_DATA_BEGIN, + NGHTTP3_HTTP_STATE_RESP_DATA_END, + NGHTTP3_HTTP_STATE_RESP_TRAILERS_BEGIN, + NGHTTP3_HTTP_STATE_RESP_TRAILERS_END, + NGHTTP3_HTTP_STATE_RESP_END, +} nghttp3_stream_http_state; + +typedef enum nghttp3_stream_http_event { + NGHTTP3_HTTP_EVENT_DATA_BEGIN, + NGHTTP3_HTTP_EVENT_DATA_END, + NGHTTP3_HTTP_EVENT_HEADERS_BEGIN, + NGHTTP3_HTTP_EVENT_HEADERS_END, + NGHTTP3_HTTP_EVENT_PUSH_PROMISE_BEGIN, + NGHTTP3_HTTP_EVENT_PUSH_PROMISE_END, + NGHTTP3_HTTP_EVENT_MSG_END, +} nghttp3_stream_http_event; + +typedef struct nghttp3_stream nghttp3_stream; + +typedef struct nghttp3_push_promise nghttp3_push_promise; + +/* + * nghttp3_stream_acked_data is a callback function which is invoked + * when data sent on stream denoted by |stream_id| supplied from + * application is acknowledged by remote endpoint. The number of + * bytes acknowledged is given in |datalen|. + * + * The implementation of this callback must return 0 if it succeeds. + * Returning NGHTTP3_ERR_CALLBACK_FAILURE will return to the caller + * immediately. Any values other than 0 is treated as + * NGHTTP3_ERR_CALLBACK_FAILURE. + */ +typedef int (*nghttp3_stream_acked_data)(nghttp3_stream *stream, + int64_t stream_id, size_t datalen, + void *user_data); + +typedef struct nghttp3_stream_callbacks { + nghttp3_stream_acked_data acked_data; +} nghttp3_stream_callbacks; + +typedef struct nghttp3_http_state { + /* status_code is HTTP status code received. This field is used + if connection is initialized as client. */ + int32_t status_code; + /* content_length is the value of received content-length header + field. */ + int64_t content_length; + /* recv_content_length is the number of body bytes received so + far. */ + int64_t recv_content_length; + uint16_t flags; + /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ + uint8_t pri; +} nghttp3_http_state; + +struct nghttp3_stream { + const nghttp3_mem *mem; + nghttp3_map_entry me; + /* node is a node in dependency tree. For server initiated + unidirectional stream (push), scheduling is done via + corresponding nghttp3_push_promise object pointed by pp. */ + nghttp3_tnode node; + nghttp3_pq_entry qpack_blocked_pe; + nghttp3_stream_callbacks callbacks; + nghttp3_ringbuf frq; + nghttp3_ringbuf chunks; + nghttp3_ringbuf outq; + /* inq stores the stream raw data which cannot be read because + stream is blocked by QPACK decoder. */ + nghttp3_ringbuf inq; + nghttp3_qpack_stream_context qpack_sctx; + /* conn is a reference to underlying connection. It could be NULL + if stream is not a request/push stream. */ + nghttp3_conn *conn; + void *user_data; + /* unsent_bytes is the number of bytes in outq not written yet */ + size_t unsent_bytes; + /* outq_idx is an index into outq where next write is made. */ + size_t outq_idx; + /* outq_offset is write offset relative to the element at outq_idx + in outq. */ + size_t outq_offset; + /* ack_offset is offset acknowledged by peer relative to the first + element in outq. */ + uint64_t ack_offset; + /* ack_done is the number of bytes notified to an application that + they are acknowledged inside the first outq element if it is of + type NGHTTP3_BUF_TYPE_ALIEN. */ + size_t ack_done; + size_t unscheduled_nwrite; + nghttp3_stream_type type; + nghttp3_stream_read_state rstate; + /* pp is nghttp3_push_promise that this stream fulfills. */ + nghttp3_push_promise *pp; + /* error_code indicates the reason of closure of this stream. */ + uint64_t error_code; + + struct { + nghttp3_stream_http_state hstate; + } tx; + + struct { + nghttp3_stream_http_state hstate; + nghttp3_http_state http; + } rx; + + uint16_t flags; +}; + +typedef struct nghttp3_frame_entry { + nghttp3_frame fr; + union { + struct { + nghttp3_settings *local_settings; + } settings; + struct { + nghttp3_data_reader dr; + } data; + } aux; +} nghttp3_frame_entry; + +int nghttp3_stream_new(nghttp3_stream **pstream, int64_t stream_id, + uint64_t seq, const nghttp3_stream_callbacks *callbacks, + const nghttp3_mem *mem); + +void nghttp3_stream_del(nghttp3_stream *stream); + +void nghttp3_varint_read_state_reset(nghttp3_varint_read_state *rvint); + +void nghttp3_stream_read_state_reset(nghttp3_stream_read_state *rstate); + +nghttp3_ssize nghttp3_read_varint(nghttp3_varint_read_state *rvint, + const uint8_t *src, size_t srclen, int fin); + +int nghttp3_stream_frq_add(nghttp3_stream *stream, + const nghttp3_frame_entry *frent); + +int nghttp3_stream_fill_outq(nghttp3_stream *stream); + +int nghttp3_stream_write_stream_type(nghttp3_stream *stream); + +int nghttp3_stream_write_stream_type_push_id(nghttp3_stream *stream); + +nghttp3_ssize nghttp3_stream_writev(nghttp3_stream *stream, int *pfin, + nghttp3_vec *vec, size_t veccnt); + +int nghttp3_stream_write_qpack_decoder_stream(nghttp3_stream *stream); + +int nghttp3_stream_outq_is_full(nghttp3_stream *stream); + +int nghttp3_stream_outq_add(nghttp3_stream *stream, + const nghttp3_typed_buf *tbuf); + +int nghttp3_stream_write_headers(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_push_promise(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_header_block(nghttp3_stream *stream, + nghttp3_qpack_encoder *qenc, + nghttp3_stream *qenc_stream, + nghttp3_buf *rbuf, nghttp3_buf *ebuf, + int64_t frame_type, int64_t push_id, + const nghttp3_nv *nva, size_t nvlen); + +int nghttp3_stream_write_data(nghttp3_stream *stream, int *peof, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_settings(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_cancel_push(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_write_max_push_id(nghttp3_stream *stream, + nghttp3_frame_entry *frent); + +int nghttp3_stream_ensure_chunk(nghttp3_stream *stream, size_t need); + +nghttp3_buf *nghttp3_stream_get_chunk(nghttp3_stream *stream); + +int nghttp3_stream_is_blocked(nghttp3_stream *stream); + +int nghttp3_stream_add_outq_offset(nghttp3_stream *stream, size_t n); + +/* + * nghttp3_stream_outq_write_done returns nonzero if all contents in + * outq have been written. + */ +int nghttp3_stream_outq_write_done(nghttp3_stream *stream); + +int nghttp3_stream_add_ack_offset(nghttp3_stream *stream, uint64_t n); + +/* + * nghttp3_stream_is_active returns nonzero if |stream| is active. In + * other words, it has something to send. This function does not take + * into account its descendants. + */ +int nghttp3_stream_is_active(nghttp3_stream *stream); + +/* + * nghttp3_stream_require_schedule returns nonzero if |stream| should + * be scheduled. In other words, |stream| or its descendants have + * something to send. + */ +int nghttp3_stream_require_schedule(nghttp3_stream *stream); + +int nghttp3_stream_buffer_data(nghttp3_stream *stream, const uint8_t *src, + size_t srclen); + +size_t nghttp3_stream_get_buffered_datalen(nghttp3_stream *stream); + +int nghttp3_stream_ensure_qpack_stream_context(nghttp3_stream *stream); + +void nghttp3_stream_delete_qpack_stream_context(nghttp3_stream *stream); + +int nghttp3_stream_transit_rx_http_state(nghttp3_stream *stream, + nghttp3_stream_http_event event); + +int nghttp3_stream_empty_headers_allowed(nghttp3_stream *stream); + +/* + * nghttp3_stream_bidi_or_push returns nonzero if |stream| is + * bidirectional or push stream. + */ +int nghttp3_stream_bidi_or_push(nghttp3_stream *stream); + +/* + * nghttp3_stream_uni returns nonzero if stream identified by + * |stream_id| is unidirectional. + */ +int nghttp3_stream_uni(int64_t stream_id); + +/* + * nghttp3_client_stream_bidi returns nonzero if stream identified by + * |stream_id| is client initiated bidirectional stream. + */ +int nghttp3_client_stream_bidi(int64_t stream_id); + +/* + * nghttp3_client_stream_uni returns nonzero if stream identified by + * |stream_id| is client initiated unidirectional stream. + */ +int nghttp3_client_stream_uni(int64_t stream_id); + +/* + * nghttp3_server_stream_uni returns nonzero if stream identified by + * |stream_id| is server initiated unidirectional stream. + */ +int nghttp3_server_stream_uni(int64_t stream_id); + +#endif /* NGHTTP3_STREAM_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c new file mode 100644 index 00000000000000..94dca7dbf76325 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.c @@ -0,0 +1,110 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 "nghttp3_tnode.h" + +#include <assert.h> + +#include "nghttp3_macro.h" +#include "nghttp3_stream.h" +#include "nghttp3_conn.h" +#include "nghttp3_conv.h" + +nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid, + nghttp3_node_id_type type, int64_t id) { + nid->type = type; + nid->id = id; + return nid; +} + +int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b) { + return a->type == b->type && a->id == b->id; +} + +void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, + uint64_t seq, uint8_t pri) { + assert(nghttp3_pri_uint8_urgency(pri) < NGHTTP3_URGENCY_LEVELS); + + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + tnode->nid = *nid; + tnode->seq = seq; + tnode->cycle = 0; + tnode->pri = pri; +} + +void nghttp3_tnode_free(nghttp3_tnode *tnode) { (void)tnode; } + +static void tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) { + assert(tnode->pe.index != NGHTTP3_PQ_BAD_INDEX); + + nghttp3_pq_remove(pq, &tnode->pe); + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; +} + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq) { + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { + return; + } + + tnode_unschedule(tnode, pq); +} + +static uint64_t pq_get_first_cycle(nghttp3_pq *pq) { + nghttp3_tnode *top; + + if (nghttp3_pq_empty(pq)) { + return 0; + } + + top = nghttp3_struct_of(nghttp3_pq_top(pq), nghttp3_tnode, pe); + return top->cycle; +} + +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, + size_t nwrite) { + uint64_t penalty = nwrite / NGHTTP3_STREAM_MIN_WRITELEN; + + if (tnode->pe.index == NGHTTP3_PQ_BAD_INDEX) { + tnode->cycle = pq_get_first_cycle(pq) + + ((nwrite == 0 || !nghttp3_pri_uint8_inc(tnode->pri)) + ? 0 + : nghttp3_max(1, penalty)); + } else if (nwrite > 0) { + if (!nghttp3_pri_uint8_inc(tnode->pri) || nghttp3_pq_size(pq) == 1) { + return 0; + } + + nghttp3_pq_remove(pq, &tnode->pe); + tnode->pe.index = NGHTTP3_PQ_BAD_INDEX; + tnode->cycle += nghttp3_max(1, penalty); + } else { + return 0; + } + + return nghttp3_pq_push(pq, &tnode->pe); +} + +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode) { + return tnode->pe.index != NGHTTP3_PQ_BAD_INDEX; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h new file mode 100644 index 00000000000000..817aec034c03a4 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_tnode.h @@ -0,0 +1,82 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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 NGHTTP3_TNODE_H +#define NGHTTP3_TNODE_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#include "nghttp3_pq.h" + +#define NGHTTP3_TNODE_MAX_CYCLE_GAP (1llu << 24) + +typedef enum nghttp3_node_id_type { + NGHTTP3_NODE_ID_TYPE_STREAM = 0x00, + NGHTTP3_NODE_ID_TYPE_PUSH = 0x01, +} nghttp3_node_id_type; + +typedef struct nghttp3_node_id { + nghttp3_node_id_type type; + int64_t id; +} nghttp3_node_id; + +nghttp3_node_id *nghttp3_node_id_init(nghttp3_node_id *nid, + nghttp3_node_id_type type, int64_t id); + +int nghttp3_node_id_eq(const nghttp3_node_id *a, const nghttp3_node_id *b); + +typedef struct nghttp3_tnode { + nghttp3_pq_entry pe; + size_t num_children; + nghttp3_node_id nid; + uint64_t seq; + uint64_t cycle; + /* pri is a stream priority produced by nghttp3_pri_to_uint8. */ + uint8_t pri; +} nghttp3_tnode; + +void nghttp3_tnode_init(nghttp3_tnode *tnode, const nghttp3_node_id *nid, + uint64_t seq, uint8_t pri); + +void nghttp3_tnode_free(nghttp3_tnode *tnode); + +void nghttp3_tnode_unschedule(nghttp3_tnode *tnode, nghttp3_pq *pq); + +/* + * nghttp3_tnode_schedule schedules |tnode| using |nwrite| as penalty. + * If |tnode| has already been scheduled, it is rescheduled by the + * amount of |nwrite|. + */ +int nghttp3_tnode_schedule(nghttp3_tnode *tnode, nghttp3_pq *pq, size_t nwrite); + +/* + * nghttp3_tnode_is_scheduled returns nonzero if |tnode| is scheduled. + */ +int nghttp3_tnode_is_scheduled(nghttp3_tnode *tnode); + +#endif /* NGHTTP3_TNODE_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c new file mode 100644 index 00000000000000..ab292ac8ae31d2 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.c @@ -0,0 +1,38 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 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 "nghttp3_vec.h" +#include "nghttp3_macro.h" + +size_t nghttp3_vec_len(const nghttp3_vec *vec, size_t n) { + size_t i; + size_t res = 0; + + for (i = 0; i < n; ++i) { + res += vec[i].len; + } + + return res; +} diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h new file mode 100644 index 00000000000000..c1a928e3e1d1ab --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_vec.h @@ -0,0 +1,35 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 contributors + * Copyright (c) 2018 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 NGHTTP3_VEC_H +#define NGHTTP3_VEC_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +#endif /* NGHTTP3_VEC_H */ diff --git a/deps/ngtcp2/nghttp3/lib/nghttp3_version.c b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c new file mode 100644 index 00000000000000..dfad4793c4bc11 --- /dev/null +++ b/deps/ngtcp2/nghttp3/lib/nghttp3_version.c @@ -0,0 +1,39 @@ +/* + * nghttp3 + * + * Copyright (c) 2019 nghttp3 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. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <nghttp3/nghttp3.h> + +static nghttp3_info version = {NGHTTP3_VERSION_AGE, NGHTTP3_VERSION_NUM, + NGHTTP3_VERSION}; + +nghttp3_info *nghttp3_version(int least_version) { + if (least_version > NGHTTP3_VERSION_NUM) { + return NULL; + } + return &version; +} diff --git a/deps/ngtcp2/ngtcp2.gyp b/deps/ngtcp2/ngtcp2.gyp new file mode 100644 index 00000000000000..9ea93be2091ac4 --- /dev/null +++ b/deps/ngtcp2/ngtcp2.gyp @@ -0,0 +1,174 @@ +{ + 'target_defaults': { + 'defines': [ '_U_=' ] + }, + 'variables': { + 'ngtcp2_sources': [ + 'ngtcp2/lib/ngtcp2_acktr.c', + 'ngtcp2/lib/ngtcp2_addr.c', + 'ngtcp2/lib/ngtcp2_buf.c', + 'ngtcp2/lib/ngtcp2_cc.c', + 'ngtcp2/lib/ngtcp2_cid.c', + 'ngtcp2/lib/ngtcp2_conn.c', + 'ngtcp2/lib/ngtcp2_conv.c', + 'ngtcp2/lib/ngtcp2_crypto.c', + 'ngtcp2/lib/ngtcp2_err.c', + 'ngtcp2/lib/ngtcp2_gaptr.c', + 'ngtcp2/lib/ngtcp2_idtr.c', + 'ngtcp2/lib/ngtcp2_ksl.c', + 'ngtcp2/lib/ngtcp2_log.c', + 'ngtcp2/lib/ngtcp2_map.c', + 'ngtcp2/lib/ngtcp2_mem.c', + 'ngtcp2/lib/ngtcp2_path.c', + 'ngtcp2/lib/ngtcp2_pkt.c', + 'ngtcp2/lib/ngtcp2_ppe.c', + 'ngtcp2/lib/ngtcp2_pq.c', + 'ngtcp2/lib/ngtcp2_pv.c', + 'ngtcp2/lib/ngtcp2_qlog.c', + 'ngtcp2/lib/ngtcp2_range.c', + 'ngtcp2/lib/ngtcp2_ringbuf.c', + 'ngtcp2/lib/ngtcp2_rob.c', + 'ngtcp2/lib/ngtcp2_rst.c', + 'ngtcp2/lib/ngtcp2_rtb.c', + 'ngtcp2/lib/ngtcp2_str.c', + 'ngtcp2/lib/ngtcp2_strm.c', + 'ngtcp2/lib/ngtcp2_vec.c', + 'ngtcp2/lib/ngtcp2_version.c', + 'ngtcp2/crypto/shared.c', + ], + 'ngtcp2_sources_openssl': [ + 'ngtcp2/crypto/openssl/openssl.c' + ], + 'ngtcp2_sources_boringssl': [ + 'ngtcp2/crypto/boringssl/boringssl.c' + ], + 'nghttp3_sources': [ + 'nghttp3/lib/nghttp3_buf.c', + 'nghttp3/lib/nghttp3_conn.c', + 'nghttp3/lib/nghttp3_conv.c', + 'nghttp3/lib/nghttp3_debug.c', + 'nghttp3/lib/nghttp3_err.c', + 'nghttp3/lib/nghttp3_frame.c', + 'nghttp3/lib/nghttp3_gaptr.c', + 'nghttp3/lib/nghttp3_http.c', + 'nghttp3/lib/nghttp3_idtr.c', + 'nghttp3/lib/nghttp3_ksl.c', + 'nghttp3/lib/nghttp3_map.c', + 'nghttp3/lib/nghttp3_mem.c', + 'nghttp3/lib/nghttp3_pq.c', + 'nghttp3/lib/nghttp3_qpack.c', + 'nghttp3/lib/nghttp3_qpack_huffman.c', + 'nghttp3/lib/nghttp3_qpack_huffman_data.c', + 'nghttp3/lib/nghttp3_range.c', + 'nghttp3/lib/nghttp3_rcbuf.c', + 'nghttp3/lib/nghttp3_ringbuf.c', + 'nghttp3/lib/nghttp3_str.c', + 'nghttp3/lib/nghttp3_stream.c', + 'nghttp3/lib/nghttp3_tnode.c', + 'nghttp3/lib/nghttp3_vec.c', + 'nghttp3/lib/nghttp3_version.c' + ] + }, + 'targets': [ + { + 'target_name': 'ngtcp2', + 'type': 'static_library', + 'include_dirs': [ + '', + 'ngtcp2/lib/includes/', + 'ngtcp2/crypto/includes/', + 'ngtcp2/lib/', + 'ngtcp2/crypto/', + ], + 'defines': [ + 'BUILDING_NGTCP2', + 'NGTCP2_STATICLIB', + ], + 'conditions': [ + ['node_shared_openssl=="false"', { + 'dependencies': [ + '../openssl/openssl.gyp:openssl' + ] + }], + ['OS=="win"', { + 'defines': [ + 'WIN32', + '_WINDOWS', + 'HAVE_CONFIG_H', + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'CompileAs': '1' + }, + }, + }], + ['OS=="linux"', { + 'defines': [ + 'HAVE_ARPA_INET_H', + 'HAVE_NETINET_IN_H', + ], + }], + ], + 'direct_dependent_settings': { + 'defines': [ + 'NGTCP2_STATICLIB', + ], + 'include_dirs': [ + '', + 'ngtcp2/lib/includes', + 'ngtcp2/crypto/includes', + ] + }, + 'sources': [ + '<@(ngtcp2_sources)', + '<@(ngtcp2_sources_openssl)', + ] + }, + { + 'target_name': 'nghttp3', + 'type': 'static_library', + 'include_dirs': [ + 'nghttp3/lib/includes/', + 'nghttp3/lib/' + ], + 'defines': [ + 'BUILDING_NGHTTP3', + 'NGHTTP3_STATICLIB' + ], + 'dependencies': [ + 'ngtcp2' + ], + 'conditions': [ + ['OS=="win"', { + 'defines': [ + 'WIN32', + '_WINDOWS', + 'HAVE_CONFIG_H', + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'CompileAs': '1' + }, + }, + }], + ['OS=="linux"', { + 'defines': [ + 'HAVE_ARPA_INET_H', + 'HAVE_NETINET_IN_H', + ], + }], + ], + 'direct_dependent_settings': { + 'defines': [ + 'NGHTTP3_STATICLIB' + ], + 'include_dirs': [ + 'nghttp3/lib/includes' + ] + }, + 'sources': [ + '<@(nghttp3_sources)' + ] + } + ] +} diff --git a/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c new file mode 100644 index 00000000000000..8f75b485e3e56b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/boringssl/boringssl.c @@ -0,0 +1,540 @@ +/* + * ngtcp2 + * + * Copyright (c) 2020 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. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <assert.h> +#include <string.h> + +#include <ngtcp2/ngtcp2_crypto.h> +#include <ngtcp2/ngtcp2_crypto_boringssl.h> + +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/hkdf.h> +#include <openssl/chacha.h> + +#include "shared.h" + +/* Define cipher types because BoringSSL does not implement EVP + interface for chacha20. */ +typedef enum ngtcp2_crypto_boringssl_cipher_type { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20, +} ngtcp2_crypto_boringssl_cipher_type; + +typedef struct ngtcp2_crypto_boringssl_cipher { + ngtcp2_crypto_boringssl_cipher_type type; +} ngtcp2_crypto_boringssl_cipher; + +static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_128_ctr = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR, +}; + +static ngtcp2_crypto_boringssl_cipher crypto_cipher_evp_aes_256_ctr = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR, +}; + +static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20, +}; + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aead_aes_128_gcm()); + ctx->md.native_handle = (void *)EVP_sha256(); + ctx->hp.native_handle = (void *)&crypto_cipher_evp_aes_128_ctr; + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + aead->native_handle = aead_native_handle; + aead->max_overhead = EVP_AEAD_max_overhead(aead->native_handle); + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm()); +} + +static const EVP_AEAD *crypto_ssl_get_aead(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + return EVP_aead_aes_128_gcm(); + case TLS1_CK_AES_256_GCM_SHA384: + return EVP_aead_aes_256_gcm(); + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return EVP_aead_chacha20_poly1305(); + default: + return NULL; + } +} + +static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + default: + return 0; + } +} + +static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + default: + return 0; + } +} + +static const ngtcp2_crypto_boringssl_cipher *crypto_ssl_get_hp(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + return &crypto_cipher_evp_aes_128_ctr; + case TLS1_CK_AES_256_GCM_SHA384: + return &crypto_cipher_evp_aes_256_ctr; + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return &crypto_cipher_chacha20; + default: + return NULL; + } +} + +static const EVP_MD *crypto_ssl_get_md(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_CK_AES_128_GCM_SHA256: + case TLS1_CK_CHACHA20_POLY1305_SHA256: + return EVP_sha256(); + case TLS1_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + return NULL; + } +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + SSL *ssl = tls_native_handle; + ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_ssl_get_aead(ssl)); + ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl); + ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl); + ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl); + ctx->max_decryption_failure = crypto_ssl_get_aead_max_decryption_failure(ssl); + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const EVP_MD *md) { + return (size_t)EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const EVP_AEAD *aead) { + return (size_t)EVP_AEAD_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const EVP_AEAD *aead) { + return (size_t)EVP_AEAD_nonce_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_AEAD *cipher = aead->native_handle; + size_t keylen = crypto_aead_keylen(cipher); + EVP_AEAD_CTX *actx; + + (void)noncelen; + + actx = EVP_AEAD_CTX_new(cipher, key, keylen, EVP_AEAD_DEFAULT_TAG_LENGTH); + if (actx == NULL) { + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + return ngtcp2_crypto_aead_ctx_encrypt_init(aead_ctx, aead, key, noncelen); +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + EVP_AEAD_CTX_free(aead_ctx->native_handle); + } +} + +typedef enum ngtcp2_crypto_boringssl_cipher_ctx_type { + NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP, + NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20, +} ngtcp2_crypto_boringssl_cipher_ctx_type; + +typedef struct ngtcp2_crypto_boringssl_cipher_ctx { + ngtcp2_crypto_boringssl_cipher_ctx_type type; + union { + /* ctx is EVP_CIPHER_CTX used when type == + NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP. */ + EVP_CIPHER_CTX *ctx; + /* key contains an encryption key when type == + NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20. */ + uint8_t key[32]; + }; +} ngtcp2_crypto_boringssl_cipher_ctx; + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + ngtcp2_crypto_boringssl_cipher *hp_cipher = cipher->native_handle; + ngtcp2_crypto_boringssl_cipher_ctx *ctx; + EVP_CIPHER_CTX *actx; + const EVP_CIPHER *evp_cipher; + + switch (hp_cipher->type) { + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_128_CTR: + evp_cipher = EVP_aes_128_ctr(); + break; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_EVP_AES_256_CTR: + evp_cipher = EVP_aes_256_ctr(); + break; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20: + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + return -1; + } + + ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20; + memcpy(ctx->key, key, sizeof(ctx->key)); + + cipher_ctx->native_handle = ctx; + + return 0; + default: + assert(0); + }; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, evp_cipher, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + ctx = malloc(sizeof(*ctx)); + if (ctx == NULL) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + ctx->type = NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP; + ctx->ctx = actx; + + cipher_ctx->native_handle = ctx; + + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + ngtcp2_crypto_boringssl_cipher_ctx *ctx; + + if (!cipher_ctx->native_handle) { + return; + } + + ctx = cipher_ctx->native_handle; + + if (ctx->type == NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP) { + EVP_CIPHER_CTX_free(ctx->ctx); + } + + free(ctx); +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + const EVP_MD *prf = md->native_handle; + size_t destlen = (size_t)EVP_MD_size(prf); + + if (HKDF_extract(dest, &destlen, prf, secret, secretlen, salt, saltlen) != + 1) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + const EVP_MD *prf = md->native_handle; + + if (HKDF_expand(dest, destlen, prf, secret, secretlen, info, infolen) != 1) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen) { + const EVP_AEAD *cipher = aead->native_handle; + EVP_AEAD_CTX *actx = aead_ctx->native_handle; + size_t max_outlen = plaintextlen + EVP_AEAD_max_overhead(cipher); + size_t outlen; + + if (EVP_AEAD_CTX_seal(actx, dest, &outlen, max_outlen, nonce, noncelen, + plaintext, plaintextlen, ad, adlen) != 1) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen) { + EVP_AEAD_CTX *actx = aead_ctx->native_handle; + size_t max_outlen = ciphertextlen; + size_t outlen; + + (void)aead; + + if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen, + ciphertext, ciphertextlen, ad, adlen) != 1) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle; + EVP_CIPHER_CTX *actx; + int len; + uint32_t counter; + + (void)hp; + + switch (ctx->type) { + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_EVP: + actx = ctx->ctx; + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || + !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, + sizeof(PLAINTEXT) - 1) || + !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { + return -1; + } + return 0; + case NGTCP2_CRYPTO_BORINGSSL_CIPHER_CTX_TYPE_CHACHA20: +#if defined(WORDS_BIGENDIAN) + counter = (uint32_t)sample[0] + (uint32_t)(sample[1] << 8) + + (uint32_t)(sample[2] << 16) + (uint32_t)(sample[3] << 24); +#else /* !WORDS_BIGENDIAN */ + memcpy(&counter, sample, sizeof(counter)); +#endif /* !WORDS_BIGENDIAN */ + CRYPTO_chacha_20(dest, PLAINTEXT, sizeof(PLAINTEXT) - 1, ctx->key, + sample + sizeof(counter), counter); + return 0; + default: + assert(0); + abort(); + } +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn); + int rv; + int err; + + if (SSL_provide_quic_data( + ssl, ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(crypto_level), + data, datalen) != 1) { + return -1; + } + + if (!ngtcp2_conn_get_handshake_completed(conn)) { + retry: + rv = SSL_do_handshake(ssl); + if (rv <= 0) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + return -1; + case SSL_ERROR_EARLY_DATA_REJECTED: + assert(!ngtcp2_conn_is_server(conn)); + + SSL_reset_early_data_reject(ssl); + + rv = ngtcp2_conn_early_data_rejected(conn); + if (rv != 0) { + return -1; + } + + goto retry; + default: + return -1; + } + } + + if (SSL_in_early_data(ssl)) { + return 0; + } + + ngtcp2_conn_handshake_completed(conn); + } + + rv = SSL_process_quic_post_handshake(ssl); + if (rv != 1) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + return -1; + default: + return -1; + } + } + + return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + SSL *ssl = tls; + ngtcp2_transport_params_type exttype = + ngtcp2_conn_is_server(conn) + ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO + : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; + const uint8_t *tp; + size_t tplen; + ngtcp2_transport_params params; + int rv; + + SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); + + rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + if (SSL_set_quic_transport_params(tls, buf, len) != 1) { + return -1; + } + + return 0; +} + +ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level( + enum ssl_encryption_level_t ssl_level) { + switch (ssl_level) { + case ssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case ssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case ssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case ssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APPLICATION; + default: + assert(0); + } +} + +enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return ssl_encryption_initial; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return ssl_encryption_handshake; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + return ssl_encryption_application; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return ssl_encryption_early_data; + default: + assert(0); + } +} diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h new file mode 100644 index 00000000000000..23901d18c1646e --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto.h @@ -0,0 +1,688 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_CRYPTO_H +#define NGTCP2_CRYPTO_H + +#include <ngtcp2/ngtcp2.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_INITIAL_SECRETLEN` is the length of secret + * for Initial packets. + */ +#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_INITIAL_KEYLEN` is the length of key for + * Initial packets. + */ +#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_INITIAL_IVLEN` is the length of IV for + * Initial packets. + */ +#define NGTCP2_CRYPTO_INITIAL_IVLEN 12 + +/** + * @function + * + * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet + * encryption and decryption. + */ +NGTCP2_EXTERN ngtcp2_crypto_ctx * +ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated + * ciphers and message digests from native TLS session + * |tls_native_handle|. This is used for encrypting/decrypting + * Handshake and Short packets. + * + * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be + * a pointer to SSL object. + */ +NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_ctx_tls_early` initializes |ctx| by extracting early + * ciphers and message digests from native TLS session + * |tls_native_handle|. This is used for encrypting/decrypting 0RTT + * packets. + * + * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be + * a pointer to SSL object. + */ +NGTCP2_EXTERN ngtcp2_crypto_ctx * +ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_init` initializes |aead| with the provided + * |aead_native_handle| which is an underlying AEAD object. + * + * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be + * a pointer to EVP_CIPHER. + * + * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be + * gnutls_cipher_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must + * be a pointer to EVP_AEAD. + */ +NGTCP2_EXTERN ngtcp2_crypto_aead * +ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, void *aead_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher + * AEAD_AES_128_GCM for Retry packet integrity protection. + */ +NGTCP2_EXTERN ngtcp2_crypto_aead * +ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_md_init` initializes |md| with the provided + * |md_native_handle| which is an underlying message digest object. + * + * If libngtcp2_crypto_openssl is linked, |md_native_handle| must be a + * pointer to EVP_MD. + * + * If libngtcp2_crypto_gnutls is linked, |md_native_handle| must be + * gnutls_mac_algorithm_t casted to ``void *``. + * + * If libngtcp2_crypto_boringssl is linked, |md_native_handle| must be + * a pointer to EVP_MD. + */ +NGTCP2_EXTERN ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, + void *md_native_handle); + +/** + * @function + * + * `ngtcp2_crypto_md_hashlen` returns the length of |md| output. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md); + +/** + * @function + * + * `ngtcp2_crypto_aead_keylen` returns the length of key for |aead|. + */ +NGTCP2_EXTERN size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_aead_noncelen` returns the length of nonce for + * |aead|. + */ +NGTCP2_EXTERN size_t +ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_hkdf_extract` performs HKDF extract operation. The + * result is the length of |md| and is stored to the buffer pointed by + * |dest|. The caller is responsible to specify the buffer that can + * store the output. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen); + +/** + * @function + * + * `ngtcp2_crypto_hkdf_expand` performs HKDF expand operation. The + * result is |destlen| bytes long and is stored to the buffer pointed + * by |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen, + const uint8_t *info, + size_t infolen); + +/** + * @function + * + * `ngtcp2_crypto_hkdf_expand_label` performs HKDF expand label. The + * result is |destlen| bytes long and is stored to the buffer pointed + * by |dest|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen, + const uint8_t *label, + size_t labellen); + +/** + * @enum + * + * :type:`ngtcp2_crypto_side` indicates which side the application + * implements; client or server. + */ +typedef enum ngtcp2_crypto_side { + /** + * :enum:`NGTCP2_CRYPTO_SIDE_CLIENT` indicates that the application + * is client. + */ + NGTCP2_CRYPTO_SIDE_CLIENT, + /** + * :enum:`NGTCP2_CRYPTO_SIDE_SERVER` indicates that the application + * is server. + */ + NGTCP2_CRYPTO_SIDE_SERVER +} ngtcp2_crypto_side; + +/** + * @function + * + * `ngtcp2_crypto_packet_protection_ivlen` returns the length of IV + * used to encrypt QUIC packet. + */ +NGTCP2_EXTERN size_t +ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead); + +/** + * @function + * + * `ngtcp2_crypto_derive_packet_protection_key` derives packet + * protection key. This function writes packet protection key into + * the buffer pointed by |key|. |key| must point to the buffer which + * is at least ngtcp2_crypto_aead_keylen(aead) bytes long. This + * function writes packet protection IV into |iv|. |iv| must point to + * the buffer which is at least + * ngtcp2_crypto_packet_protection_ivlen(aead). |key| is + * ngtcp2_crypto_aead_keylen(aead) bytes long. |iv| is + * ngtcp2_crypto_packet_protection_ivlen(aead) bytes long. + * + * If |hp| is not NULL, this function also derives packet header + * protection key and writes the key into the buffer pointed by |hp|. + * The length of key is ngtcp2_crypto_aead_keylen(aead) bytes long. + * |hp|, if not NULL, must have enough capacity to store the key. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_packet_protection_key( + uint8_t *key, uint8_t *iv, uint8_t *hp, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length + * |plaintextlen| and writes the ciphertext into the buffer pointed by + * |dest|. The length of ciphertext is plaintextlen + + * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have + * enough capacity to store the ciphertext. It is allowed to specify + * the same value to |dest| and |plaintext|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, + size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_encrypt_cb` is a wrapper function around + * `ngtcp2_crypto_encrypt`. It can be directly passed to + * :member:`ngtcp2_conn_callbacks.encrypt` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length + * |ciphertextlen| and writes the plaintext into the buffer pointed by + * |dest|. The length of plaintext is ciphertextlen - + * ngtcp2_crypto_aead_max_overhead(aead) bytes long. |dest| must have + * enough capacity to store the plaintext. It is allowed to specify + * the same value to |dest| and |ciphertext|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, + size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_decrypt_cb` is a wrapper function around + * `ngtcp2_crypto_decrypt`. It can be directly passed to + * :member:`ngtcp2_conn_callbacks.decrypt` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_TLS_DECRYPT`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @function + * + * `ngtcp2_crypto_hp_mask` generates mask which is used in packet + * header encryption. The mask is written to the buffer pointed by + * |dest|. The length of mask is 5 bytes. |dest| must have enough + * capacity to store the mask. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest, + const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around + * `ngtcp2_crypto_hp_mask`. It can be directly passed to + * :member:`ngtcp2_conn_callbacks.hp_mask` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_rx_key` derives the rx keys from + * |secret| and installs new keys to |conn|. + * + * If |key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |key|. If |iv| is + * not NULL, the derived packet protection IV for decryption is + * written to the buffer pointed by |iv|. If |hp| is not NULL, the + * derived header protection key for decryption is written to the + * buffer pointed by |hp|. + * + * |secretlen| specifies the length of |secret|. + * + * The length of packet protection key and header protection key is + * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection + * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx + * can be obtained by `ngtcp2_crypto_ctx_tls`. + * + * In the first call of this function, it calls + * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message + * digest algorithm. After the successful call of this function, + * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. + * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag + * length. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key( + ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp, + ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_tx_key` derives the tx keys from + * |secret| and installs new keys to |conn|. + * + * If |key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |key|. If |iv| is + * not NULL, the derived packet protection IV for encryption is + * written to the buffer pointed by |iv|. If |hp| is not NULL, the + * derived header protection key for encryption is written to the + * buffer pointed by |hp|. + * + * |secretlen| specifies the length of |secret|. + * + * The length of packet protection key and header protection key is + * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection + * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx + * can be obtained by `ngtcp2_crypto_ctx_tls`. + * + * In the first call of this function, it calls + * `ngtcp2_conn_set_crypto_ctx` to set negotiated AEAD and message + * digest algorithm. After the successful call of this function, + * application can use `ngtcp2_conn_get_crypto_ctx` to get the object. + * It also calls `ngtcp2_conn_set_aead_overhead` to set AEAD tag + * length. + * + * If |level| is NGTCP2_CRYPTO_LEVEL_APP, this function retrieves a + * remote QUIC transport parameters extension from |tls| and sets it + * to |conn|. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key( + ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp, + ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_update_key` updates traffic keying materials. + * + * The new traffic secret for decryption is written to the buffer + * pointed by |rx_secret|. The length of secret is |secretlen| bytes, + * and |rx_secret| must point to the buffer which has enough capacity. + * + * The new traffic secret for encryption is written to the buffer + * pointed by |tx_secret|. The length of secret is |secretlen| bytes, + * and |tx_secret| must point to the buffer which has enough capacity. + * + * The derived packet protection key for decryption is written to the + * buffer pointed by |rx_key|. The derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. + * |rx_aead_ctx| must be constructed with |rx_key|. + * + * The derived packet protection key for encryption is written to the + * buffer pointed by |tx_key|. The derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. + * |tx_aead_ctx| must be constructed with |rx_key|. + * + * |current_rx_secret| and |current_tx_secret| are the current traffic + * secrets for decryption and encryption. |secretlen| specifies the + * length of |rx_secret| and |tx_secret|. + * + * The length of packet protection key and header protection key is + * ngtcp2_crypto_aead(ctx->aead), and the length of packet protection + * IV is ngtcp2_crypto_packet_protection_ivlen(ctx->aead) where ctx + * can be obtained by `ngtcp2_conn_get_crypto_ctx`. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_update_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, + size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_update_key_cb` is a wrapper function around + * `ngtcp2_crypto_update_key`. It can be directly passed to + * :member:`ngtcp2_conn_callbacks.update_key` field. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, + const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, + size_t secretlen, void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_client_initial_cb` installs initial secrets and + * encryption keys and sets QUIC transport parameters. + * + * This function can be directly passed to + * :member:`ngtcp2_conn_callbacks.client_initial` field. It is only + * used by client. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, + void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_recv_retry_cb` re-installs initial secrets in + * response to incoming Retry packet. + * + * This function can be directly passed to + * :member:`ngtcp2_conn_callbacks.recv_retry` field. It is only used + * by client. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_recv_client_initial_cb` installs initial secrets in + * response to an incoming Initial packet from client, and sets QUIC + * transport parameters. + * + * This function can be directly passed to + * :member:`ngtcp2_conn_callbacks.recv_client_initial` field. It is + * only used by server. + * + * This function returns 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, + const ngtcp2_cid *dcid, + void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of + * length |datalen| in encryption level |crypto_level| and may feed + * outgoing CRYPTO data to |conn|. This function can drive handshake. + * This function can be also used after handshake completes. It is + * allowed to call this function with datalen == 0. In this case, no + * additional read operation is done. + * + * This function returns 0 if it succeeds, or a negative error code. + * The generic error code is -1 if a specific error code is not + * suitable. The error codes less than -10000 are specific to + * underlying TLS implementation. For OpenSSL, the error codes are + * defined in *ngtcp2_crypto_openssl.h*. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_crypto_generate_stateless_reset_token` generates a + * stateless reset token using HKDF extraction with |md| using the + * given |cid| and static key |secret| as input. The token will be + * written to the buffer pointed by |token| and it must have a + * capacity of at least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` + * bytes. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token( + uint8_t *token, const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const ngtcp2_cid *cid); + +/** + * @function + * + * `ngtcp2_crypto_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE with the given |error_code| to the + * buffer pointed by |dest| of length |destlen|. This function is + * designed for server to close connection without committing the + * state when validating Retry token fails. This function must not be + * used by client. The |dcid| must be the Source Connection ID in + * Initial packet from client. The |scid| must be the Destination + * Connection ID in Initial packet from client. |scid| is used to + * derive initial keying materials. + * + * This function wraps around `ngtcp2_pkt_write_connection_close` for + * easier use. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code); + +/** + * @function + * + * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer + * pointed by |dest| of length |destlen|. |odcid| specifies Original + * Destination Connection ID. |token| specifies Retry Token, and + * |tokenlen| specifies its length. + * + * This function wraps around `ngtcp2_pkt_write_retry` for easier use. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_retry( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, + size_t tokenlen); + +/** + * @function + * + * `ngtcp2_crypto_aead_ctx_encrypt_init` initializes |aead_ctx| with + * new AEAD cipher context object for encryption which is constructed + * to use |key| as encryption key. |aead| specifies AEAD cipher to + * use. |noncelen| is the length of nonce. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen); + +/** + * @function + * + * `ngtcp2_crypto_aead_ctx_decrypt_init` initializes |aead_ctx| with + * new AEAD cipher context object for decryption which is constructed + * to use |key| as encryption key. |aead| specifies AEAD cipher to + * use. |noncelen| is the length of nonce. + * + * This function returns 0 if it succeeds, or -1. + */ +NGTCP2_EXTERN int +ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen); + +/** + * @function + * + * `ngtcp2_crypto_aead_ctx_free` frees up resources used by + * |aead_ctx|. This function does not free the memory pointed by + * |aead_ctx| itself. + */ +NGTCP2_EXTERN void +ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @function + * + * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|. + * + * This function can be directly passed to + * :member:`ngtcp2_conn_callbacks.delete_crypto_aead_ctx` field. + */ +NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb( + ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data); + +/** + * @function + * + * `ngtcp2_crypto_delete_crypto_cipher_ctx_cb` deletes the given + * |cipher_ctx|. + * + * This function can be directly passed to + * :member:`ngtcp2_conn_callbacks.delete_crypto_cipher_ctx` field. + */ +NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( + ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h new file mode 100644 index 00000000000000..65f8414249a0c5 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h @@ -0,0 +1,62 @@ +/* + * ngtcp2 + * + * Copyright (c) 2020 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_CRYPTO_BORINGSSL_H +#define NGTCP2_CRYPTO_BORINGSSL_H + +#include <ngtcp2/ngtcp2.h> + +#include <openssl/ssl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @function + * + * `ngtcp2_crypto_boringssl_from_ssl_encryption_level` translates + * |ssl_level| to :type:`ngtcp2_crypto_level`. This function is only + * available for BoringSSL backend. + */ +NGTCP2_EXTERN ngtcp2_crypto_level +ngtcp2_crypto_boringssl_from_ssl_encryption_level( + enum ssl_encryption_level_t ssl_level); + +/** + * @function + * + * `ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level` translates + * |crypto_level| to ssl_encryption_level_t. This function is only + * available for BoringSSL backend. + */ +NGTCP2_EXTERN enum ssl_encryption_level_t +ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_BORINGSSL_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h new file mode 100644 index 00000000000000..10a8e712cfd63c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h @@ -0,0 +1,90 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_CRYPTO_OPENSSL_H +#define NGTCP2_CRYPTO_OPENSSL_H + +#include <ngtcp2/ngtcp2.h> + +#include <openssl/ssl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @macrosection + * + * OpenSSL specific error codes + */ + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP` is the + * error code which indicates that TLS handshake routine is + * interrupted by X509 certificate lookup. See + * :macro:`SSL_ERROR_WANT_X509_LOOKUP` error description from + * `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP -10001 + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB` is the + * error code which indicates that TLS handshake routine is + * interrupted by client hello callback. See + * :macro:`SSL_ERROR_WANT_CLIENT_HELLO_CB` error description from + * `SSL_do_handshake`. + */ +#define NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB -10002 + +/** + * @function + * + * `ngtcp2_crypto_openssl_from_ossl_encryption_level` translates + * |ossl_level| to :type:`ngtcp2_crypto_level`. This function is only + * available for OpenSSL backend. + */ +NGTCP2_EXTERN ngtcp2_crypto_level +ngtcp2_crypto_openssl_from_ossl_encryption_level( + OSSL_ENCRYPTION_LEVEL ossl_level); + +/** + * @function + * + * `ngtcp2_crypto_openssl_from_ngtcp2_crypto_level` translates + * |crypto_level| to OSSL_ENCRYPTION_LEVEL. This function is only + * available for OpenSSL backend. + */ +NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_openssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_CRYPTO_OPENSSL_H */ diff --git a/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c new file mode 100644 index 00000000000000..d4b9e57f27581e --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/openssl/openssl.c @@ -0,0 +1,526 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <assert.h> + +#include <ngtcp2/ngtcp2_crypto.h> +#include <ngtcp2/ngtcp2_crypto_openssl.h> + +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/kdf.h> + +#include "shared.h" + +static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) { + switch (EVP_CIPHER_nid(aead)) { + case NID_aes_128_gcm: + case NID_aes_256_gcm: + return EVP_GCM_TLS_TAG_LEN; + case NID_chacha20_poly1305: + return EVP_CHACHAPOLY_TLS_TAG_LEN; + case NID_aes_128_ccm: + return EVP_CCM_TLS_TAG_LEN; + default: + assert(0); + } +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) { + ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aes_128_gcm()); + ctx->md.native_handle = (void *)EVP_sha256(); + ctx->hp.native_handle = (void *)EVP_aes_128_ctr(); + ctx->max_encryption = 0; + ctx->max_decryption_failure = 0; + return ctx; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead, + void *aead_native_handle) { + aead->native_handle = aead_native_handle; + aead->max_overhead = crypto_aead_max_overhead(aead_native_handle); + return aead; +} + +ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) { + return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm()); +} + +static const EVP_CIPHER *crypto_ssl_get_aead(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + return EVP_aes_128_gcm(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_aes_256_gcm(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return EVP_chacha20_poly1305(); + case TLS1_3_CK_AES_128_CCM_SHA256: + return EVP_aes_128_ccm(); + default: + return NULL; + } +} + +static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305; + case TLS1_3_CK_AES_128_CCM_SHA256: + return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM; + default: + return 0; + } +} + +static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_256_GCM_SHA384: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM; + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305; + case TLS1_3_CK_AES_128_CCM_SHA256: + return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM; + default: + return 0; + } +} + +static const EVP_CIPHER *crypto_ssl_get_hp(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return EVP_aes_128_ctr(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_aes_256_ctr(); + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + return EVP_chacha20(); + default: + return NULL; + } +} + +static const EVP_MD *crypto_ssl_get_md(SSL *ssl) { + switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) { + case TLS1_3_CK_AES_128_GCM_SHA256: + case TLS1_3_CK_CHACHA20_POLY1305_SHA256: + case TLS1_3_CK_AES_128_CCM_SHA256: + return EVP_sha256(); + case TLS1_3_CK_AES_256_GCM_SHA384: + return EVP_sha384(); + default: + return NULL; + } +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + SSL *ssl = tls_native_handle; + ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_ssl_get_aead(ssl)); + ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl); + ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl); + ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl); + ctx->max_decryption_failure = crypto_ssl_get_aead_max_decryption_failure(ssl); + return ctx; +} + +ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, + void *tls_native_handle) { + return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle); +} + +static size_t crypto_md_hashlen(const EVP_MD *md) { + return (size_t)EVP_MD_size(md); +} + +size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) { + return crypto_md_hashlen(md->native_handle); +} + +static size_t crypto_aead_keylen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_key_length(aead); +} + +size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_keylen(aead->native_handle); +} + +static size_t crypto_aead_noncelen(const EVP_CIPHER *aead) { + return (size_t)EVP_CIPHER_iv_length(aead); +} + +size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) { + return crypto_aead_noncelen(aead->native_handle); +} + +int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_CIPHER *cipher = aead->native_handle; + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, + NULL) || + (cipher_nid == NID_aes_128_ccm && + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, + (int)crypto_aead_max_overhead(cipher), NULL)) || + !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx, + const ngtcp2_crypto_aead *aead, + const uint8_t *key, size_t noncelen) { + const EVP_CIPHER *cipher = aead->native_handle; + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen, + NULL) || + (cipher_nid == NID_aes_128_ccm && + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, + (int)crypto_aead_max_overhead(cipher), NULL)) || + !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + aead_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) { + if (aead_ctx->native_handle) { + EVP_CIPHER_CTX_free(aead_ctx->native_handle); + } +} + +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key) { + EVP_CIPHER_CTX *actx; + + actx = EVP_CIPHER_CTX_new(); + if (actx == NULL) { + return -1; + } + + if (!EVP_EncryptInit_ex(actx, cipher->native_handle, NULL, key, NULL)) { + EVP_CIPHER_CTX_free(actx); + return -1; + } + + cipher_ctx->native_handle = actx; + + return 0; +} + +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (cipher_ctx->native_handle) { + EVP_CIPHER_CTX_free(cipher_ctx->native_handle); + } +} + +int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *salt, size_t saltlen) { + const EVP_MD *prf = md->native_handle; + int rv = 0; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + size_t destlen = (size_t)EVP_MD_size(prf); + + if (pctx == NULL) { + return -1; + } + + if (EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1 || + EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 || + EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || + EVP_PKEY_derive(pctx, dest, &destlen) != 1) { + rv = -1; + } + + EVP_PKEY_CTX_free(pctx); + + return rv; +} + +int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, const uint8_t *secret, + size_t secretlen, const uint8_t *info, + size_t infolen) { + const EVP_MD *prf = md->native_handle; + int rv = 0; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL); + if (pctx == NULL) { + return -1; + } + + if (EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 || + EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 || + EVP_PKEY_CTX_set1_hkdf_salt(pctx, "", 0) != 1 || + EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 || + EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 || + EVP_PKEY_derive(pctx, dest, &destlen) != 1) { + rv = -1; + } + + EVP_PKEY_CTX_free(pctx); + + return rv; +} + +int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_max_overhead(cipher); + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx = aead_ctx->native_handle; + int len; + + (void)noncelen; + + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) || + (cipher_nid == NID_aes_128_ccm && + !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) || + !EVP_EncryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) || + !EVP_EncryptFinal_ex(actx, dest + len, &len) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen, + dest + plaintextlen)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen) { + const EVP_CIPHER *cipher = aead->native_handle; + size_t taglen = crypto_aead_max_overhead(cipher); + int cipher_nid = EVP_CIPHER_nid(cipher); + EVP_CIPHER_CTX *actx = aead_ctx->native_handle; + int len; + const uint8_t *tag; + + (void)noncelen; + + if (taglen > ciphertextlen) { + return -1; + } + + ciphertextlen -= taglen; + tag = ciphertext + ciphertextlen; + + if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) || + !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, + (uint8_t *)tag) || + (cipher_nid == NID_aes_128_ccm && + !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) || + !EVP_DecryptUpdate(actx, NULL, &len, ad, (int)adlen) || + !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) || + (cipher_nid != NID_aes_128_ccm && + !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00"; + EVP_CIPHER_CTX *actx = hp_ctx->native_handle; + int len; + + (void)hp; + + if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) || + !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) || + !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, size_t datalen) { + SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn); + int rv; + int err; + + if (SSL_provide_quic_data( + ssl, ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(crypto_level), + data, datalen) != 1) { + return -1; + } + + if (!ngtcp2_conn_get_handshake_completed(conn)) { + rv = SSL_do_handshake(ssl); + if (rv <= 0) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_WANT_CLIENT_HELLO_CB: + return NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB; + case SSL_ERROR_WANT_X509_LOOKUP: + return NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP; + case SSL_ERROR_SSL: + return -1; + default: + return -1; + } + } + + ngtcp2_conn_handshake_completed(conn); + } + + rv = SSL_process_quic_post_handshake(ssl); + if (rv != 1) { + err = SSL_get_error(ssl, rv); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + return 0; + case SSL_ERROR_SSL: + case SSL_ERROR_ZERO_RETURN: + return -1; + default: + return -1; + } + } + + return 0; +} + +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) { + SSL *ssl = tls; + ngtcp2_transport_params_type exttype = + ngtcp2_conn_is_server(conn) + ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO + : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS; + const uint8_t *tp; + size_t tplen; + ngtcp2_transport_params params; + int rv; + + SSL_get_peer_quic_transport_params(ssl, &tp, &tplen); + + rv = ngtcp2_decode_transport_params(¶ms, exttype, tp, tplen); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + if (rv != 0) { + ngtcp2_conn_set_tls_error(conn, rv); + return -1; + } + + return 0; +} + +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len) { + if (SSL_set_quic_transport_params(tls, buf, len) != 1) { + return -1; + } + + return 0; +} + +ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level( + OSSL_ENCRYPTION_LEVEL ossl_level) { + switch (ossl_level) { + case ssl_encryption_initial: + return NGTCP2_CRYPTO_LEVEL_INITIAL; + case ssl_encryption_early_data: + return NGTCP2_CRYPTO_LEVEL_EARLY; + case ssl_encryption_handshake: + return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + case ssl_encryption_application: + return NGTCP2_CRYPTO_LEVEL_APPLICATION; + default: + assert(0); + } +} + +OSSL_ENCRYPTION_LEVEL +ngtcp2_crypto_openssl_from_ngtcp2_crypto_level( + ngtcp2_crypto_level crypto_level) { + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + return ssl_encryption_initial; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + return ssl_encryption_handshake; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + return ssl_encryption_application; + case NGTCP2_CRYPTO_LEVEL_EARLY: + return ssl_encryption_early_data; + default: + assert(0); + } +} diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.c b/deps/ngtcp2/ngtcp2/crypto/shared.c new file mode 100644 index 00000000000000..5d040f2ce75558 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/shared.c @@ -0,0 +1,831 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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 "shared.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_macro.h" + +ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md, + void *md_native_handle) { + md->native_handle = md_native_handle; + return md; +} + +int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen, + const ngtcp2_crypto_md *md, + const uint8_t *secret, size_t secretlen, + const uint8_t *label, size_t labellen) { + static const uint8_t LABEL[] = "tls13 "; + uint8_t info[256]; + uint8_t *p = info; + + *p++ = (uint8_t)(destlen / 256); + *p++ = (uint8_t)(destlen % 256); + *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen); + memcpy(p, LABEL, sizeof(LABEL) - 1); + p += sizeof(LABEL) - 1; + memcpy(p, label, labellen); + p += labellen; + *p++ = 0; + + return ngtcp2_crypto_hkdf_expand(dest, destlen, md, secret, secretlen, info, + (size_t)(p - info)); +} + +#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32 + +int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, + uint8_t *tx_secret, + uint8_t *initial_secret, + const ngtcp2_cid *client_dcid, + ngtcp2_crypto_side side) { + static const uint8_t CLABEL[] = "client in"; + static const uint8_t SLABEL[] = "server in"; + uint8_t initial_secret_buf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t *client_secret; + uint8_t *server_secret; + ngtcp2_crypto_ctx ctx; + const uint8_t *salt; + size_t saltlen; + + if (!initial_secret) { + initial_secret = initial_secret_buf; + } + + ngtcp2_crypto_ctx_initial(&ctx); + + if (version == NGTCP2_PROTO_VER_V1) { + salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1; + saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1; + } else { + salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT; + saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1; + } + + if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data, + client_dcid->datalen, salt, saltlen) != 0) { + return -1; + } + + if (side == NGTCP2_CRYPTO_SIDE_SERVER) { + client_secret = rx_secret; + server_secret = tx_secret; + } else { + client_secret = tx_secret; + server_secret = rx_secret; + } + + if (ngtcp2_crypto_hkdf_expand_label( + client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, + initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL, + sizeof(CLABEL) - 1) != 0 || + ngtcp2_crypto_hkdf_expand_label( + server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md, + initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL, + sizeof(SLABEL) - 1) != 0) { + return -1; + } + + return 0; +} + +size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) { + size_t noncelen = ngtcp2_crypto_aead_noncelen(aead); + return ngtcp2_max(8, noncelen); +} + +int ngtcp2_crypto_derive_packet_protection_key( + uint8_t *key, uint8_t *iv, uint8_t *hp_key, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_md *md, const uint8_t *secret, size_t secretlen) { + static const uint8_t KEY_LABEL[] = "quic key"; + static const uint8_t IV_LABEL[] = "quic iv"; + static const uint8_t HP_KEY_LABEL[] = "quic hp"; + size_t keylen = ngtcp2_crypto_aead_keylen(aead); + size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen, + KEY_LABEL, sizeof(KEY_LABEL) - 1) != 0) { + return -1; + } + + if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen, + IV_LABEL, sizeof(IV_LABEL) - 1) != 0) { + return -1; + } + + if (hp_key != NULL && ngtcp2_crypto_hkdf_expand_label( + hp_key, keylen, md, secret, secretlen, HP_KEY_LABEL, + sizeof(HP_KEY_LABEL) - 1) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen) { + static const uint8_t LABEL[] = "quic ku"; + + if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen, + LABEL, sizeof(LABEL) - 1) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key, + uint8_t *iv, uint8_t *hp_key, + ngtcp2_crypto_level level, + const uint8_t *secret, + size_t secretlen) { + const ngtcp2_crypto_ctx *ctx; + const ngtcp2_crypto_aead *aead; + const ngtcp2_crypto_md *md; + const ngtcp2_crypto_cipher *hp; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + void *tls = ngtcp2_conn_get_tls_native_handle(conn); + uint8_t keybuf[64], ivbuf[64], hp_keybuf[64]; + size_t ivlen; + int rv; + ngtcp2_crypto_ctx cctx; + + if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) { + return 0; + } + + if (!key) { + key = keybuf; + } + if (!iv) { + iv = ivbuf; + } + if (!hp_key) { + hp_key = hp_keybuf; + } + + if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + ngtcp2_crypto_ctx_tls_early(&cctx, tls); + ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); + ctx = ngtcp2_conn_get_early_crypto_ctx(conn); + } else { + ctx = ngtcp2_conn_get_crypto_ctx(conn); + + if (!ctx->aead.native_handle) { + ngtcp2_crypto_ctx_tls(&cctx, tls); + ngtcp2_conn_set_crypto_ctx(conn, &cctx); + ctx = ngtcp2_conn_get_crypto_ctx(conn); + } + } + + aead = &ctx->aead; + md = &ctx->md; + hp = &ctx->hp; + ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, + secret, secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, aead, key, ivlen) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) { + goto fail; + } + + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: + rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); + if (rv != 0) { + goto fail; + } + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, iv, ivlen, + &hp_ctx); + if (rv != 0) { + goto fail; + } + break; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + if (!ngtcp2_conn_is_server(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + goto fail; + } + } + + rv = ngtcp2_conn_install_rx_key(conn, secret, secretlen, &aead_ctx, iv, + ivlen, &hp_ctx); + if (rv != 0) { + goto fail; + } + + break; + default: + goto fail; + } + + return 0; + +fail: + ngtcp2_crypto_cipher_ctx_free(&hp_ctx); + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + return -1; +} + +/* + * crypto_set_local_transport_params gets local QUIC transport + * parameters from |conn| and sets it to |tls|. + * + * This function returns 0 if it succeeds, or -1. + */ +static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) { + ngtcp2_transport_params_type exttype = + ngtcp2_conn_is_server(conn) + ? NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS + : NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO; + ngtcp2_transport_params params; + ngtcp2_ssize nwrite; + uint8_t buf[256]; + + ngtcp2_conn_get_local_transport_params(conn, ¶ms); + + nwrite = ngtcp2_encode_transport_params(buf, sizeof(buf), exttype, ¶ms); + if (nwrite < 0) { + return -1; + } + + if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) { + return -1; + } + + return 0; +} + +int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key, + uint8_t *iv, uint8_t *hp_key, + ngtcp2_crypto_level level, + const uint8_t *secret, + size_t secretlen) { + const ngtcp2_crypto_ctx *ctx; + const ngtcp2_crypto_aead *aead; + const ngtcp2_crypto_md *md; + const ngtcp2_crypto_cipher *hp; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + void *tls = ngtcp2_conn_get_tls_native_handle(conn); + uint8_t keybuf[64], ivbuf[64], hp_keybuf[64]; + size_t ivlen; + int rv; + ngtcp2_crypto_ctx cctx; + + if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) { + return 0; + } + + if (!key) { + key = keybuf; + } + if (!iv) { + iv = ivbuf; + } + if (!hp_key) { + hp_key = hp_keybuf; + } + + if (level == NGTCP2_CRYPTO_LEVEL_EARLY) { + ngtcp2_crypto_ctx_tls_early(&cctx, tls); + ngtcp2_conn_set_early_crypto_ctx(conn, &cctx); + ctx = ngtcp2_conn_get_early_crypto_ctx(conn); + } else { + ctx = ngtcp2_conn_get_crypto_ctx(conn); + + if (!ctx->aead.native_handle) { + ngtcp2_crypto_ctx_tls(&cctx, tls); + ngtcp2_conn_set_crypto_ctx(conn, &cctx); + ctx = ngtcp2_conn_get_crypto_ctx(conn); + } + } + + aead = &ctx->aead; + md = &ctx->md; + hp = &ctx->hp; + ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, aead, md, + secret, secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, aead, key, ivlen) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) { + goto fail; + } + + switch (level) { + case NGTCP2_CRYPTO_LEVEL_EARLY: + rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx); + if (rv != 0) { + goto fail; + } + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen, + &hp_ctx); + if (rv != 0) { + goto fail; + } + + if (ngtcp2_conn_is_server(conn)) { + rv = ngtcp2_crypto_set_remote_transport_params(conn, tls); + if (rv != 0) { + return rv; + } + + if (crypto_set_local_transport_params(conn, tls) != 0) { + return rv; + } + } + + break; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv, + ivlen, &hp_ctx); + if (rv != 0) { + goto fail; + } + + break; + default: + goto fail; + } + + return 0; + +fail: + ngtcp2_crypto_cipher_ctx_free(&hp_ctx); + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + return -1; +} + +int ngtcp2_crypto_derive_and_install_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, + uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key, + const ngtcp2_cid *client_dcid) { + uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + ngtcp2_crypto_ctx ctx; + ngtcp2_crypto_aead retry_aead; + ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0}; + ngtcp2_crypto_aead_ctx tx_aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0}; + ngtcp2_crypto_aead_ctx retry_aead_ctx = {0}; + int rv; + int server = ngtcp2_conn_is_server(conn); + uint32_t version = ngtcp2_conn_get_negotiated_version(conn); + const uint8_t *retry_key; + size_t retry_noncelen; + + ngtcp2_crypto_ctx_initial(&ctx); + + if (!rx_secret) { + rx_secret = rx_secretbuf; + } + if (!tx_secret) { + tx_secret = tx_secretbuf; + } + if (!initial_secret) { + initial_secret = initial_secretbuf; + } + + if (!rx_key) { + rx_key = rx_keybuf; + } + if (!rx_iv) { + rx_iv = rx_ivbuf; + } + if (!rx_hp_key) { + rx_hp_key = rx_hp_keybuf; + } + if (!tx_key) { + tx_key = tx_keybuf; + } + if (!tx_iv) { + tx_iv = tx_ivbuf; + } + if (!tx_hp_key) { + tx_hp_key = tx_hp_keybuf; + } + + ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx); + + if (ngtcp2_crypto_derive_initial_secrets( + version, rx_secret, tx_secret, initial_secret, client_dcid, + server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) != + 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + rx_key, rx_iv, rx_hp_key, &ctx.aead, &ctx.md, rx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx.aead, rx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx.hp, rx_hp_key) != + 0) { + goto fail; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx.aead, tx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + goto fail; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx.hp, tx_hp_key) != + 0) { + goto fail; + } + + if (!server && !ngtcp2_conn_after_retry(conn)) { + ngtcp2_crypto_aead_retry(&retry_aead); + + if (ngtcp2_conn_get_negotiated_version(conn) == NGTCP2_PROTO_VER_V1) { + retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; + retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; + } else { + retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; + retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&retry_aead_ctx, &retry_aead, + retry_key, retry_noncelen) != 0) { + goto fail; + } + } + + rv = ngtcp2_conn_install_initial_key(conn, &rx_aead_ctx, rx_iv, &rx_hp_ctx, + &tx_aead_ctx, tx_iv, &tx_hp_ctx, + NGTCP2_CRYPTO_INITIAL_IVLEN); + if (rv != 0) { + goto fail; + } + + if (retry_aead_ctx.native_handle) { + ngtcp2_conn_set_retry_aead(conn, &retry_aead, &retry_aead_ctx); + } + + return 0; + +fail: + ngtcp2_crypto_aead_ctx_free(&retry_aead_ctx); + ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx); + ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx); + ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx); + + return -1; +} + +int ngtcp2_crypto_update_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv, + const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, + size_t secretlen) { + const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn); + const ngtcp2_crypto_aead *aead = &ctx->aead; + const ngtcp2_crypto_md *md = &ctx->md; + size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead); + + if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret, + secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key(rx_key, rx_iv, NULL, aead, md, + rx_secret, secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_update_traffic_secret(tx_secret, md, current_tx_secret, + secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key(tx_key, tx_iv, NULL, aead, md, + tx_secret, secretlen) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_decrypt_init(rx_aead_ctx, aead, rx_key, ivlen) != + 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(tx_aead_ctx, aead, tx_key, ivlen) != + 0) { + ngtcp2_crypto_aead_ctx_free(rx_aead_ctx); + rx_aead_ctx->native_handle = NULL; + return -1; + } + + return 0; +} + +int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen) { + if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen, + nonce, noncelen, ad, adlen) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen) { + if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen, + nonce, noncelen, ad, adlen) != 0) { + return NGTCP2_ERR_TLS_DECRYPT; + } + return 0; +} + +int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_update_key_cb( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, + const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, + size_t secretlen, void *user_data) { + uint8_t rx_key[64]; + uint8_t tx_key[64]; + (void)conn; + (void)user_data; + + if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_aead_ctx, rx_key, + rx_iv, tx_aead_ctx, tx_key, tx_iv, + current_rx_secret, current_tx_secret, + secretlen) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + return 0; +} + +int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen, + const ngtcp2_cid *cid) { + uint8_t buf[64]; + int rv; + + assert(ngtcp2_crypto_md_hashlen(md) <= sizeof(buf)); + assert(NGTCP2_STATELESS_RESET_TOKENLEN <= sizeof(buf)); + + rv = ngtcp2_crypto_hkdf_extract(buf, md, secret, secretlen, cid->data, + cid->datalen); + if (rv != 0) { + return -1; + } + + memcpy(token, buf, NGTCP2_STATELESS_RESET_TOKENLEN); + + return 0; +} + +ngtcp2_ssize ngtcp2_crypto_write_connection_close(uint8_t *dest, size_t destlen, + uint32_t version, + const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, + uint64_t error_code) { + uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN]; + uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN]; + uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN]; + ngtcp2_crypto_ctx ctx; + ngtcp2_ssize spktlen; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + + ngtcp2_crypto_ctx_initial(&ctx); + + if (ngtcp2_crypto_derive_initial_secrets(version, rx_secret, tx_secret, + initial_secret, scid, + NGTCP2_CRYPTO_SIDE_SERVER) != 0) { + return -1; + } + + if (ngtcp2_crypto_derive_packet_protection_key( + tx_key, tx_iv, tx_hp_key, &ctx.aead, &ctx.md, tx_secret, + NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) { + return -1; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &ctx.aead, tx_key, + NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) { + spktlen = -1; + goto end; + } + + if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, &ctx.hp, tx_hp_key) != 0) { + spktlen = -1; + goto end; + } + + spktlen = ngtcp2_pkt_write_connection_close( + dest, destlen, version, dcid, scid, error_code, ngtcp2_crypto_encrypt_cb, + &ctx.aead, &aead_ctx, tx_iv, ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx); + if (spktlen < 0) { + spktlen = -1; + } + +end: + ngtcp2_crypto_cipher_ctx_free(&hp_ctx); + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + return spktlen; +} + +ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen, + uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, + const ngtcp2_cid *odcid, + const uint8_t *token, size_t tokenlen) { + ngtcp2_crypto_aead aead; + ngtcp2_ssize spktlen; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + const uint8_t *key; + size_t noncelen; + + ngtcp2_crypto_aead_retry(&aead); + + if (version == NGTCP2_PROTO_VER_V1) { + key = (const uint8_t *)NGTCP2_RETRY_KEY_V1; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; + } else { + key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; + } + + if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, noncelen) != + 0) { + return -1; + } + + spktlen = ngtcp2_pkt_write_retry(dest, destlen, version, dcid, scid, odcid, + token, tokenlen, ngtcp2_crypto_encrypt_cb, + &aead, &aead_ctx); + if (spktlen < 0) { + spktlen = -1; + } + + ngtcp2_crypto_aead_ctx_free(&aead_ctx); + + return spktlen; +} + +/* + * crypto_setup_initial_crypto establishes the initial secrets and + * encryption keys, and prepares local QUIC transport parameters. + */ +static int crypto_setup_initial_crypto(ngtcp2_conn *conn, + const ngtcp2_cid *dcid) { + return ngtcp2_crypto_derive_and_install_initial_key( + conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid); +} + +int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) { + const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn); + void *tls = ngtcp2_conn_get_tls_native_handle(conn); + (void)user_data; + + if (crypto_setup_initial_crypto(conn, dcid) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + if (crypto_set_local_transport_params(conn, tls) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + if (ngtcp2_crypto_read_write_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, + NULL, 0) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + void *user_data) { + (void)user_data; + + if (ngtcp2_crypto_derive_and_install_initial_key(conn, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + &hd->scid) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn, + const ngtcp2_cid *dcid, + void *user_data) { + (void)user_data; + + if (crypto_setup_initial_crypto(conn, dcid) != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +void ngtcp2_crypto_delete_crypto_aead_ctx_cb(ngtcp2_conn *conn, + ngtcp2_crypto_aead_ctx *aead_ctx, + void *user_data) { + (void)conn; + (void)user_data; + + ngtcp2_crypto_aead_ctx_free(aead_ctx); +} + +void ngtcp2_crypto_delete_crypto_cipher_ctx_cb( + ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data) { + (void)conn; + (void)user_data; + + ngtcp2_crypto_cipher_ctx_free(cipher_ctx); +} diff --git a/deps/ngtcp2/ngtcp2/crypto/shared.h b/deps/ngtcp2/ngtcp2/crypto/shared.h new file mode 100644 index 00000000000000..b7fe2f15da93a7 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/crypto/shared.h @@ -0,0 +1,211 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_SHARED_H +#define NGTCP2_SHARED_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2_crypto.h> + +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_DRAFT` is a salt value which is used to + * derive initial secret. It is used for QUIC draft versions. + */ +#define NGTCP2_INITIAL_SALT_DRAFT \ + "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90" \ + "\xa8\x99" + +/** + * @macro + * + * :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to + * derive initial secret. It is used for QUIC v1. + */ +#define NGTCP2_INITIAL_SALT_V1 \ + "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \ + "\x7f\x0a" + +/* Maximum key usage (encryption) limits */ +#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23) +#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62) +#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM (2965820ULL) + +/* Maximum authentication failure (decryption) limits during the + lifetime of a connection. */ +#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM (1ULL << 52) +#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36) +#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL) + +/** + * @function + * + * `ngtcp2_crypto_derive_initial_secrets` derives initial secrets. + * |rx_secret| and |tx_secret| must point to the buffer of at least 32 + * bytes capacity. rx for read and tx for write. This function + * writes rx and tx secrets into |rx_secret| and |tx_secret| + * respectively. The length of secret is 32 bytes long. + * |client_dcid| is the destination connection ID in first Initial + * packet of client. If |initial_secret| is not NULL, the initial + * secret is written to it. It must point to the buffer which has at + * least 32 bytes capacity. The initial secret is 32 bytes long. + * |side| specifies the side of application. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret, + uint8_t *tx_secret, + uint8_t *initial_secret, + const ngtcp2_cid *client_dcid, + ngtcp2_crypto_side side); + +/** + * @function + * + * `ngtcp2_crypto_update_traffic_secret` derives the next generation + * of the traffic secret. |secret| specifies the current secret and + * its length is given in |secretlen|. The length of new key is the + * same as the current key. This function writes new key into the + * buffer pointed by |dest|. |dest| must have the enough capacity to + * store the new key. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_update_traffic_secret(uint8_t *dest, + const ngtcp2_crypto_md *md, + const uint8_t *secret, + size_t secretlen); + +/** + * @function + * + * `ngtcp2_crypto_set_local_transport_params` sets QUIC transport + * parameter, which is encoded in wire format and stored in the buffer + * pointed by |buf| of length |len|, to the native handle |tls|. + * + * |tls| points to a implementation dependent TLS session object. If + * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL + * object. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf, + size_t len); + +/** + * @function + * + * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC + * transport parameters from |tls| and sets it to |conn| using + * `ngtcp2_conn_set_remote_transport_params`. + * + * |tls| points to a implementation dependent TLS session object. If + * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL + * object. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls); + +/** + * @function + * + * `ngtcp2_crypto_derive_and_install_initial_key` derives initial + * keying materials and installs keys to |conn|. + * + * If |rx_secret| is not NULL, the secret for decryption is written to + * the buffer pointed by |rx_secret|. The length of secret is 32 + * bytes, and |rx_secret| must point to the buffer which has enough + * capacity. + * + * If |tx_secret| is not NULL, the secret for encryption is written to + * the buffer pointed by |tx_secret|. The length of secret is 32 + * bytes, and |tx_secret| must point to the buffer which has enough + * capacity. + * + * If |initial_secret| is not NULL, the initial secret is written to + * the buffer pointed by |initial_secret|. The length of secret is 32 + * bytes, and |initial_secret| must point to the buffer which has + * enough capacity. + * + * |client_dcid| is the destination connection ID in first Initial + * packet of client. + * + * If |rx_key| is not NULL, the derived packet protection key for + * decryption is written to the buffer pointed by |rx_key|. If + * |rx_iv| is not NULL, the derived packet protection IV for + * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp| + * is not NULL, the derived header protection key for decryption is + * written to the buffer pointed by |rx_hp|. + * + * If |tx_key| is not NULL, the derived packet protection key for + * encryption is written to the buffer pointed by |tx_key|. If + * |tx_iv| is not NULL, the derived packet protection IV for + * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp| + * is not NULL, the derived header protection key for encryption is + * written to the buffer pointed by |tx_hp|. + * + * The length of packet protection key and header protection key is 16 + * bytes long. The length of packet protection IV is 12 bytes long. + * + * This function calls `ngtcp2_conn_set_initial_crypto_ctx` to set + * initial AEAD and message digest algorithm. After the successful + * call of this function, application can use + * `ngtcp2_conn_get_initial_crypto_ctx` to get the object. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_derive_and_install_initial_key( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp, + uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, + const ngtcp2_cid *client_dcid); + +/** + * @function + * + * `ngtcp2_crypto_cipher_ctx_encrypt_init` initializes |cipher_ctx| + * with new cipher context object for encryption which is constructed + * to use |key| as encryption key. |cipher| specifies cipher to use. + * + * This function returns 0 if it succeeds, or -1. + */ +int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx, + const ngtcp2_crypto_cipher *cipher, + const uint8_t *key); + +/** + * @function + * + * `ngtcp2_crypto_cipher_ctx_free` frees up resources used by + * |cipher_ctx|. This function does not free the memory pointed by + * |cipher_ctx| itself. + */ +void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx); + +#endif /* NGTCP2_SHARED_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h new file mode 100644 index 00000000000000..8a37bebda6b095 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/ngtcp2.h @@ -0,0 +1,4831 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2017 nghttp2 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_H +#define NGTCP2_H + +/* Define WIN32 when build target is Win32 API (borrowed from + libcurl) */ +#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) +# define WIN32 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#if defined(_MSC_VER) && (_MSC_VER < 1800) +/* MSVC < 2013 does not have inttypes.h because it is not C99 + compliant. See compiler macros and version number in + https://sourceforge.net/p/predef/wiki/Compilers/ */ +# include <stdint.h> +#else /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +# include <inttypes.h> +#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1800) */ +#include <sys/types.h> +#include <stdarg.h> +#include <stddef.h> + +#ifdef WIN32 +# include <winsock2.h> +#else +# include <sys/socket.h> +#endif + +#include <ngtcp2/version.h> + +#ifdef NGTCP2_STATICLIB +# define NGTCP2_EXTERN +#elif defined(WIN32) +# ifdef BUILDING_NGTCP2 +# define NGTCP2_EXTERN __declspec(dllexport) +# else /* !BUILDING_NGTCP2 */ +# define NGTCP2_EXTERN __declspec(dllimport) +# endif /* !BUILDING_NGTCP2 */ +#else /* !defined(WIN32) */ +# ifdef BUILDING_NGTCP2 +# define NGTCP2_EXTERN __attribute__((visibility("default"))) +# else /* !BUILDING_NGTCP2 */ +# define NGTCP2_EXTERN +# endif /* !BUILDING_NGTCP2 */ +#endif /* !defined(WIN32) */ + +/** + * @typedef + * + * :type:`ngtcp2_ssize` is signed counterpart of size_t. + */ +typedef ptrdiff_t ngtcp2_ssize; + +/** + * @functypedef + * + * Custom memory allocator to replace malloc(). The |mem_user_data| + * is the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void *(*ngtcp2_malloc)(size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace free(). The |mem_user_data| is + * the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void (*ngtcp2_free)(void *ptr, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace calloc(). The |mem_user_data| + * is the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void *(*ngtcp2_calloc)(size_t nmemb, size_t size, void *mem_user_data); + +/** + * @functypedef + * + * Custom memory allocator to replace realloc(). The |mem_user_data| + * is the mem_user_data member of :type:`ngtcp2_mem` structure. + */ +typedef void *(*ngtcp2_realloc)(void *ptr, size_t size, void *mem_user_data); + +/** + * @struct + * + * Custom memory allocator functions and user defined pointer. The + * |mem_user_data| member is passed to each allocator function. This + * can be used, for example, to achieve per-session memory pool. + * + * In the following example code, ``my_malloc``, ``my_free``, + * ``my_calloc`` and ``my_realloc`` are the replacement of the + * standard allocators ``malloc``, ``free``, ``calloc`` and + * ``realloc`` respectively:: + * + * void *my_malloc_cb(size_t size, void *mem_user_data) { + * return my_malloc(size); + * } + * + * void my_free_cb(void *ptr, void *mem_user_data) { my_free(ptr); } + * + * void *my_calloc_cb(size_t nmemb, size_t size, void *mem_user_data) { + * return my_calloc(nmemb, size); + * } + * + * void *my_realloc_cb(void *ptr, size_t size, void *mem_user_data) { + * return my_realloc(ptr, size); + * } + * + * void conn_new() { + * ngtcp2_mem mem = {NULL, my_malloc_cb, my_free_cb, my_calloc_cb, + * my_realloc_cb}; + * + * ... + * } + */ +typedef struct ngtcp2_mem { + /** + * An arbitrary user supplied data. This is passed to each + * allocator function. + */ + void *mem_user_data; + /** + * Custom allocator function to replace malloc(). + */ + ngtcp2_malloc malloc; + /** + * Custom allocator function to replace free(). + */ + ngtcp2_free free; + /** + * Custom allocator function to replace calloc(). + */ + ngtcp2_calloc calloc; + /** + * Custom allocator function to replace realloc(). + */ + ngtcp2_realloc realloc; +} ngtcp2_mem; + +/** + * @macrosection + * + * Time related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_SECONDS` is a count of tick which corresponds to 1 second. + */ +#define NGTCP2_SECONDS ((uint64_t)1000000000ULL) + +/** + * @macro + * + * :macro:`NGTCP2_MILLISECONDS` is a count of tick which corresponds + * to 1 millisecond. + */ +#define NGTCP2_MILLISECONDS ((uint64_t)1000000ULL) + +/** + * @macro + * + * :macro:`NGTCP2_MICROSECONDS` is a count of tick which corresponds + * to 1 microsecond. + */ +#define NGTCP2_MICROSECONDS ((uint64_t)1000ULL) + +/** + * @macro + * + * :macro:`NGTCP2_NANOSECONDS` is a count of tick which corresponds to + * 1 nanosecond. + */ +#define NGTCP2_NANOSECONDS ((uint64_t)1ULL) + +/** + * @macrosection + * + * QUIC protocol version macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_V1` is the QUIC version 1. + */ +#define NGTCP2_PROTO_VER_V1 0x00000001u + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_DRAFT_MAX` is the maximum QUIC draft + * version that this library supports. + */ +#define NGTCP2_PROTO_VER_DRAFT_MAX 0xff000020u + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_DRAFT_MIN` is the minimum QUIC draft + * version that this library supports. + */ +#define NGTCP2_PROTO_VER_DRAFT_MIN 0xff00001du + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_MAX` is the highest QUIC version that this + * library supports. + */ +#define NGTCP2_PROTO_VER_MAX NGTCP2_PROTO_VER_V1 + +/** + * @macro + * + * :macro:`NGTCP2_PROTO_VER_MIN` is the lowest QUIC version that this + * library supports. + */ +#define NGTCP2_PROTO_VER_MIN NGTCP2_PROTO_VER_DRAFT_MIN + +/** + * @macrosection + * + * IP packet related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_MAX_PKTLEN_IPV4` is the maximum datagram size of + * IPv4 packet without PMTUD. + */ +#define NGTCP2_MAX_PKTLEN_IPV4 1252 +/** + * @macro + * + * :macro:`NGTCP2_MAX_PKTLEN_IPV6` is the maximum datagram size of + * IPv6 packet without PMTUD. + */ +#define NGTCP2_MAX_PKTLEN_IPV6 1232 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_INITIAL_PKTLEN` is the minimum datagram size for + * a packet sent by client which contains its first Initial packet. + */ +#define NGTCP2_MIN_INITIAL_PKTLEN 1200 + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_MAX_PKTLEN` is the default maximum datagram + * size that this endpoint transmits. It is used by congestion + * controller to compute congestion window. + */ +#define NGTCP2_DEFAULT_MAX_PKTLEN 1200 + +/** + * @macrosection + * + * QUIC specific macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_MAX_VARINT` is the maximum value which can be + * encoded in variable-length integer encoding. + */ +#define NGTCP2_MAX_VARINT ((1ULL << 62) - 1) + +/** + * @macro + * + * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` is the length of Stateless + * Reset Token. + */ +#define NGTCP2_STATELESS_RESET_TOKENLEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_STATELESS_RESET_RANDLEN` is the minimum length + * of random bytes (Unpredictable Bits) in Stateless Retry packet + */ +#define NGTCP2_MIN_STATELESS_RESET_RANDLEN 5 + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_DRAFT` is an encryption key to create + * integrity tag of Retry packet. It is used for QUIC draft versions. + */ +#define NGTCP2_RETRY_KEY_DRAFT \ + "\xcc\xce\x18\x7e\xd0\x9a\x09\xd0\x57\x28\x15\x5a\x6c\xb9\x6b\xe1" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_DRAFT` is nonce used when generating + * integrity tag of Retry packet. It is used for QUIC draft versions. + */ +#define NGTCP2_RETRY_NONCE_DRAFT \ + "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_KEY_V1` is an encryption key to create + * integrity tag of Retry packet. It is used for QUIC v1. + */ +#define NGTCP2_RETRY_KEY_V1 \ + "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e" + +/** + * @macro + * + * :macro:`NGTCP2_RETRY_NONCE_V1` is nonce used when generating integrity + * tag of Retry packet. It is used for QUIC v1. + */ +#define NGTCP2_RETRY_NONCE_V1 "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb" + +/** + * @macro + * + * :macro:`NGTCP2_HP_MASKLEN` is the length of header protection mask. + */ +#define NGTCP2_HP_MASKLEN 5 + +/** + * @macro + * + * :macro:`NGTCP2_HP_SAMPLELEN` is the number bytes sampled when + * encrypting a packet header. + */ +#define NGTCP2_HP_SAMPLELEN 16 + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` is a default initial RTT. + */ +#define NGTCP2_DEFAULT_INITIAL_RTT (333 * NGTCP2_MILLISECONDS) + +/** + * @macro + * + * :macro:`NGTCP2_MAX_CIDLEN` is the maximum length of Connection ID. + */ +#define NGTCP2_MAX_CIDLEN 20 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_CIDLEN` is the minimum length of Connection ID. + */ +#define NGTCP2_MIN_CIDLEN 1 + +/** + * @macro + * + * :macro:`NGTCP2_MIN_INITIAL_DCIDLEN` is the minimum length of + * Destination Connection ID in Client Initial packet if it does not + * bear token from Retry packet. + */ +#define NGTCP2_MIN_INITIAL_DCIDLEN 8 + +/** + * @macrosection + * + * ECN related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_ECN_NOT_ECT` indicates no ECN marking. + */ +#define NGTCP2_ECN_NOT_ECT 0x0 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_ECT_1` is ECT(1) codepoint. + */ +#define NGTCP2_ECN_ECT_1 0x1 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_ECT_0` is ECT(0) codepoint. + */ +#define NGTCP2_ECN_ECT_0 0x2 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_CE` is CE codepoint. + */ +#define NGTCP2_ECN_CE 0x3 + +/** + * @macro + * + * :macro:`NGTCP2_ECN_MASK` is a bit mask to get ECN marking. + */ +#define NGTCP2_ECN_MASK 0x3 + +/** + * @struct + * + * :type:`ngtcp2_pkt_info` is a packet metadata. + */ +typedef struct ngtcp2_pkt_info { + /** + * :member:`ecn <ngtcp2_pkt_info.ecn>` is ECN marking and when + * passing `ngtcp2_conn_read_pkt()`, and it should be either + * :macro:`NGTCP2_ECN_NOT_ECT`, :macro:`NGTCP2_ECN_ECT_1`, + * :macro:`NGTCP2_ECN_ECT_0`, or :macro:`NGTCP2_ECN_CE`. + */ + uint32_t ecn; +} ngtcp2_pkt_info; + +/** + * @macrosection + * + * ngtcp2 library error codes + */ + +/** + * @macro + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` indicates that a passed + * argument is invalid. + */ +#define NGTCP2_ERR_INVALID_ARGUMENT -201 +/** + * @macro + * + * :macro:`NGTCP2_ERR_NOBUF` indicates that a provided buffer does not + * have enough space to store data. + */ +#define NGTCP2_ERR_NOBUF -203 +/** + * @macro + * + * :macro:`NGTCP2_ERR_PROTO` indicates a general protocol error. + */ +#define NGTCP2_ERR_PROTO -205 +/** + * @macro + * + * :macro:`NGTCP2_ERR_INVALID_STATE` indicates that a requested + * operation is not allowed at the current connection state. + */ +#define NGTCP2_ERR_INVALID_STATE -206 +/** + * @macro + * + * :macro:`NGTCP2_ERR_ACK_FRAME` indicates that an invalid ACK frame + * is received. + */ +#define NGTCP2_ERR_ACK_FRAME -207 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` indicates that there is no + * spare stream ID available. + */ +#define NGTCP2_ERR_STREAM_ID_BLOCKED -208 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_IN_USE` indicates that a stream ID is + * already in use. + */ +#define NGTCP2_ERR_STREAM_IN_USE -209 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` indicates that stream data + * cannot be sent because of flow control. + */ +#define NGTCP2_ERR_STREAM_DATA_BLOCKED -210 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FLOW_CONTROL` indicates flow control error. + */ +#define NGTCP2_ERR_FLOW_CONTROL -211 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CONNECTION_ID_LIMIT` indicates that the number + * of received Connection ID exceeds acceptable limit. + */ +#define NGTCP2_ERR_CONNECTION_ID_LIMIT -212 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_LIMIT` indicates that a remote endpoint + * opens more streams that is permitted. + */ +#define NGTCP2_ERR_STREAM_LIMIT -213 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FINAL_SIZE` indicates that inconsistent final + * size of a stream. + */ +#define NGTCP2_ERR_FINAL_SIZE -214 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CRYPTO` indicates crypto (TLS) related error. + */ +#define NGTCP2_ERR_CRYPTO -215 +/** + * @macro + * + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` indicates that packet number + * is exhausted. + */ +#define NGTCP2_ERR_PKT_NUM_EXHAUSTED -216 +/** + * @macro + * + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` indicates that a + * required transport parameter is missing. + */ +#define NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM -217 +/** + * @macro + * + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` indicates that a + * transport parameter is malformed. + */ +#define NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM -218 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FRAME_ENCODING` indicates there is an error in + * frame encoding. + */ +#define NGTCP2_ERR_FRAME_ENCODING -219 +/** + * @macro + * + * :macro:`NGTCP2_ERR_TLS_DECRYPT` indicates TLS decryption failure. + */ +#define NGTCP2_ERR_TLS_DECRYPT -220 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_SHUT_WR` indicates no more data can be + * sent to a stream. + */ +#define NGTCP2_ERR_STREAM_SHUT_WR -221 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` indicates that a stream was not + * found. + */ +#define NGTCP2_ERR_STREAM_NOT_FOUND -222 +/** + * @macro + * + * :macro:`NGTCP2_ERR_STREAM_STATE` indicates that a requested + * operation is not allowed at the current stream state. + */ +#define NGTCP2_ERR_STREAM_STATE -226 +/** + * @macro + * + * :macro:`NGTCP2_ERR_RECV_VERSION_NEGOTIATION` indicates that Version + * Negotiation packet was received. + */ +#define NGTCP2_ERR_RECV_VERSION_NEGOTIATION -229 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CLOSING` indicates that connection is in closing + * state. + */ +#define NGTCP2_ERR_CLOSING -230 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DRAINING` indicates that connection is in + * draining state. + */ +#define NGTCP2_ERR_DRAINING -231 +/** + * @macro + * + * :macro:`NGTCP2_ERR_TRANSPORT_PARAM` indicates a general transport + * parameter error. + */ +#define NGTCP2_ERR_TRANSPORT_PARAM -234 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DISCARD_PKT` indicates a packet was discarded. + */ +#define NGTCP2_ERR_DISCARD_PKT -235 +/** + * @macro + * + * :macro:`NGTCP2_ERR_PATH_VALIDATION_FAILED` indicates that a path + * validation failed. + */ +#define NGTCP2_ERR_PATH_VALIDATION_FAILED -236 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` indicates that there is no + * spare Connection ID available. + */ +#define NGTCP2_ERR_CONN_ID_BLOCKED -237 +/** + * @macro + * + * :macro:`NGTCP2_ERR_INTERNAL` indicates an internal error. + */ +#define NGTCP2_ERR_INTERNAL -238 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED` indicates that a crypto + * buffer exceeded. + */ +#define NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED -239 +/** + * @macro + * + * :macro:`NGTCP2_ERR_WRITE_MORE` indicates + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used and a function call + * succeeded. + */ +#define NGTCP2_ERR_WRITE_MORE -240 +/** + * @macro + * + * :macro:`NGTCP2_ERR_RETRY` indicates that server should send Retry + * packet. + */ +#define NGTCP2_ERR_RETRY -241 +/** + * @macro + * + * :macro:`NGTCP2_ERR_DROP_CONN` indicates that an endpoint should + * drop connection immediately. + */ +#define NGTCP2_ERR_DROP_CONN -242 +/** + * @macro + * + * :macro:`NGTCP2_ERR_AEAD_LIMIT_REACHED` indicates AEAD encryption + * limit is reached and key update is not available. An endpoint + * should drop connection immediately. + */ +#define NGTCP2_ERR_AEAD_LIMIT_REACHED -243 +/** + * @macro + * + * :macro:`NGTCP2_ERR_NO_VIABLE_PATH` indicates that path validation + * could not probe that a path is not capable of at least 1200 MTU. + */ +#define NGTCP2_ERR_NO_VIABLE_PATH -244 +/** + * @macro + * + * :macro:`NGTCP2_ERR_FATAL` indicates that error codes less than this + * value is fatal error. When this error is returned, an endpoint + * should drop connection immediately. + */ +#define NGTCP2_ERR_FATAL -500 +/** + * @macro + * + * :macro:`NGTCP2_ERR_NOMEM` indicates out of memory. + */ +#define NGTCP2_ERR_NOMEM -501 +/** + * @macro + * + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` indicates that user defined + * callback function failed. + */ +#define NGTCP2_ERR_CALLBACK_FAILURE -502 + +/** + * @macrosection + * + * QUIC packet header flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_PKT_FLAG_NONE 0 + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_LONG_FORM` indicates the Long packet + * header. + */ +#define NGTCP2_PKT_FLAG_LONG_FORM 0x01 + +/** + * @macro + * + * :macro:`NGTCP2_PKT_FLAG_KEY_PHASE` indicates Key Phase bit set. + */ +#define NGTCP2_PKT_FLAG_KEY_PHASE 0x04 + +/** + * @enum + * + * :type:`ngtcp2_pkt_type` defines QUIC packet types. + */ +typedef enum ngtcp2_pkt_type { + /** + * :enum:`NGTCP2_PKT_VERSION_NEGOTIATION` is defined by libngtcp2 + * for convenience. + */ + NGTCP2_PKT_VERSION_NEGOTIATION = 0xf0, + /** + * :enum:`NGTCP2_PKT_INITIAL` indicates Initial packet. + */ + NGTCP2_PKT_INITIAL = 0x0, + /** + * :enum:`NGTCP2_PKT_0RTT` indicates 0RTT packet. + */ + NGTCP2_PKT_0RTT = 0x1, + /** + * :enum:`NGTCP2_PKT_HANDSHAKE` indicates Handshake packet. + */ + NGTCP2_PKT_HANDSHAKE = 0x2, + /** + * :enum:`NGTCP2_PKT_RETRY` indicates Retry packet. + */ + NGTCP2_PKT_RETRY = 0x3, + /** + * :enum:`NGTCP2_PKT_SHORT` is defined by libngtcp2 for convenience. + */ + NGTCP2_PKT_SHORT = 0x70 +} ngtcp2_pkt_type; + +/** + * @macrosection + * + * QUIC transport error code + */ + +/** + * @macro + * + * :macro:`NGTCP2_NO_ERROR` is QUIC transport error code ``NO_ERROR``. + */ +#define NGTCP2_NO_ERROR 0x0u + +/** + * @macro + * + * :macro:`NGTCP2_INTERNAL_ERROR` is QUIC transport error code + * ``INTERNAL_ERROR``. + */ +#define NGTCP2_INTERNAL_ERROR 0x1u + +/** + * @macro + * + * :macro:`NGTCP2_CONNECTION_REFUSED` is QUIC transport error code + * ``CONNECTION_REFUSED``. + */ +#define NGTCP2_CONNECTION_REFUSED 0x2u + +/** + * @macro + * + * :macro:`NGTCP2_FLOW_CONTROL_ERROR` is QUIC transport error code + * ``FLOW_CONTROL_ERROR``. + */ +#define NGTCP2_FLOW_CONTROL_ERROR 0x3u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_LIMIT_ERROR` is QUIC transport error code + * ``STREAM_LIMIT_ERROR``. + */ +#define NGTCP2_STREAM_LIMIT_ERROR 0x4u + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_STATE_ERROR` is QUIC transport error code + * ``STREAM_STATE_ERROR``. + */ +#define NGTCP2_STREAM_STATE_ERROR 0x5u + +/** + * @macro + * + * :macro:`NGTCP2_FINAL_SIZE_ERROR` is QUIC transport error code + * ``FINAL_SIZE_ERROR``. + */ +#define NGTCP2_FINAL_SIZE_ERROR 0x6u + +/** + * @macro + * + * :macro:`NGTCP2_FRAME_ENCODING_ERROR` is QUIC transport error code + * ``FRAME_ENCODING_ERROR``. + */ +#define NGTCP2_FRAME_ENCODING_ERROR 0x7u + +/** + * @macro + * + * :macro:`NGTCP2_TRANSPORT_PARAMETER_ERROR` is QUIC transport error + * code ``TRANSPORT_PARAMETER_ERROR``. + */ +#define NGTCP2_TRANSPORT_PARAMETER_ERROR 0x8u + +/** + * @macro + * + * :macro:`NGTCP2_CONNECTION_ID_LIMIT_ERROR` is QUIC transport error + * code ``CONNECTION_ID_LIMIT_ERROR``. + */ +#define NGTCP2_CONNECTION_ID_LIMIT_ERROR 0x9u + +/** + * @macro + * + * :macro:`NGTCP2_PROTOCOL_VIOLATION` is QUIC transport error code + * ``PROTOCOL_VIOLATION``. + */ +#define NGTCP2_PROTOCOL_VIOLATION 0xau + +/** + * @macro + * + * :macro:`NGTCP2_INVALID_TOKEN` is QUIC transport error code + * ``INVALID_TOKEN``. + */ +#define NGTCP2_INVALID_TOKEN 0xbu + +/** + * @macro + * + * :macro:`NGTCP2_APPLICATION_ERROR` is QUIC transport error code + * ``APPLICATION_ERROR``. + */ +#define NGTCP2_APPLICATION_ERROR 0xcu + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_BUFFER_EXCEEDED` is QUIC transport error code + * ``CRYPTO_BUFFER_EXCEEDED``. + */ +#define NGTCP2_CRYPTO_BUFFER_EXCEEDED 0xdu + +/** + * @macro + * + * :macro:`NGTCP2_KEY_UPDATE_ERROR` is QUIC transport error code + * ``KEY_UPDATE_ERROR``. + */ +#define NGTCP2_KEY_UPDATE_ERROR 0xeu + +/** + * @macro + * + * :macro:`NGTCP2_AEAD_LIMIT_REACHED` is QUIC transport error code + * ``AEAD_LIMIT_REACHED``. + */ +#define NGTCP2_AEAD_LIMIT_REACHED 0xfu + +/** + * @macro + * + * :macro:`NGTCP2_NO_VIABLE_PATH` is QUIC transport error code + * ``NO_VIABLE_PATH``. + */ +#define NGTCP2_NO_VIABLE_PATH 0x10u + +/** + * @macro + * + * :macro:`NGTCP2_CRYPTO_ERROR` is QUIC transport error code + * ``CRYPTO_ERROR``. + */ +#define NGTCP2_CRYPTO_ERROR 0x100u + +/** + * @enum + * + * :type:`ngtcp2_path_validation_result` defines path validation + * result code. + */ +typedef enum ngtcp2_path_validation_result { + /** + * :enum:`NGTCP2_PATH_VALIDATION_RESULT_SUCCESS` indicates + * successful validation. + */ + NGTCP2_PATH_VALIDATION_RESULT_SUCCESS, + /** + * :enum:`NGTCP2_PATH_VALIDATION_RESULT_FAILURE` indicates + * validation failure. + */ + NGTCP2_PATH_VALIDATION_RESULT_FAILURE +} ngtcp2_path_validation_result; + +/** + * @typedef + * + * :type:`ngtcp2_tstamp` is a timestamp with nanosecond resolution. + */ +typedef uint64_t ngtcp2_tstamp; + +/** + * @typedef + * + * :type:`ngtcp2_duration` is a period of time in nanosecond + * resolution. + */ +typedef uint64_t ngtcp2_duration; + +/** + * @struct + * + * :type:`ngtcp2_cid` holds a Connection ID. + */ +typedef struct ngtcp2_cid { + /** + * :member:`datalen <ngtcp2_cid.datalen>` is the length of + * Connection ID. + */ + size_t datalen; + /** + * :member:`data <ngtcp2_cid.data>` is the buffer to store + * Connection ID. + */ + uint8_t data[NGTCP2_MAX_CIDLEN]; +} ngtcp2_cid; + +/** + * @struct + * + * :type:`ngtcp2_vec` is struct iovec compatible structure to + * reference arbitrary array of bytes. + */ +typedef struct ngtcp2_vec { + /** + * :member:`base <ngtcp2_vec.base>` points to the data. + */ + uint8_t *base; + /** + * :member:`len <ngtcp2_vec.len>` is the number of bytes which the + * buffer pointed by base contains. + */ + size_t len; +} ngtcp2_vec; + +/** + * @function + * + * `ngtcp2_cid_init` initializes Connection ID |cid| with the byte + * string pointed by |data| and its length is |datalen|. |datalen| + * must be at least :macro:`NGTCP2_MIN_CIDLEN`, and at most + * :macro:`NGTCP2_MAX_CIDLEN`. + */ +NGTCP2_EXTERN void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, + size_t datalen); + +/** + * @struct + * + * :type:`ngtcp2_pkt_hd` represents QUIC packet header. + */ +typedef struct ngtcp2_pkt_hd { + /** + * :member:`dcid` is Destination Connection ID. + */ + ngtcp2_cid dcid; + /** + * :member:`scid` is Source Connection ID. + */ + ngtcp2_cid scid; + /** + * :member:`pkt_num` is a packet number. + */ + int64_t pkt_num; + /** + * :member:`token` contains token for Initial + * packet. + */ + ngtcp2_vec token; + /** + * :member:`pkt_numlen` is the number of bytes spent to encode + * :member:`pkt_num`. + */ + size_t pkt_numlen; + /** + * :member:`len` is the sum of :member:`pkt_numlen` and the length + * of QUIC packet payload. + */ + size_t len; + /** + * :member:`version` is QUIC version. + */ + uint32_t version; + /** + * :member:`type` is a type of QUIC packet. See + * :type:`ngtcp2_pkt_type`. + */ + uint8_t type; + /** + * :member:`flags` is zero or more of NGTCP2_PKT_FLAG_*. See + * :macro:`NGTCP2_PKT_FLAG_NONE`. + */ + uint8_t flags; +} ngtcp2_pkt_hd; + +/** + * @struct + * + * :type:`ngtcp2_pkt_stateless_reset` represents Stateless Reset. + */ +typedef struct ngtcp2_pkt_stateless_reset { + /** + * :member:`stateless_reset_token` contains stateless reset token. + */ + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; + /** + * :member:`rand` points a buffer which contains random bytes + * section. + */ + const uint8_t *rand; + /** + * :member:`randlen` is the number of random bytes. + */ + size_t randlen; +} ngtcp2_pkt_stateless_reset; + +typedef enum ngtcp2_transport_param_id { + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID = 0x0000, + NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT = 0x0001, + NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN = 0x0002, + NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE = 0x0003, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA = 0x0004, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL = 0x0005, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE = 0x0006, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI = 0x0007, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI = 0x0008, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI = 0x0009, + NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT = 0x000a, + NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY = 0x000b, + NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION = 0x000c, + NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS = 0x000d, + NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT = 0x000e, + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID = 0x000f, + NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID = 0x0010, + NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE = 0x0020 +} ngtcp2_transport_param_id; + +/** + * @enum + * + * :type:`ngtcp2_transport_params_type` defines TLS message type which + * carries transport parameters. + */ +typedef enum ngtcp2_transport_params_type { + /** + * :enum:`NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO` is Client Hello + * TLS message. + */ + NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, + /** + * :enum:`NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS` is + * Encrypted Extensions TLS message. + */ + NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS +} ngtcp2_transport_params_type; + +/** + * @enum + * + * ngtcp2_rand_usage describes the usage of the generated random data. + */ +typedef enum ngtcp2_rand_usage { + NGTCP2_RAND_USAGE_NONE, + /** + * :enum:`NGTCP2_RAND_USAGE_PATH_CHALLENGE` indicates that random + * value is used for PATH_CHALLENGE. + */ + NGTCP2_RAND_USAGE_PATH_CHALLENGE +} ngtcp2_rand_usage; + +/** + * @macrosection + * + * QUIC transport parameters related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` is the default value + * of max_udp_payload_size transport parameter. + */ +#define NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE 65527 + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` is a default value of + * scaling factor of ACK Delay field in ACK frame. + */ +#define NGTCP2_DEFAULT_ACK_DELAY_EXPONENT 3 + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` is a default value of the + * maximum amount of time in nanoseconds by which endpoint delays + * sending acknowledgement. + */ +#define NGTCP2_DEFAULT_MAX_ACK_DELAY (25 * NGTCP2_MILLISECONDS) + +/** + * @macro + * + * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` is the default + * value of active_connection_id_limit transport parameter value if + * omitted. + */ +#define NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT 2 + +/** + * @macro + * + * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS` is TLS extension + * type of quic_transport_parameters. + */ +#define NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS 0xffa5u + +/** + * @struct + * + * :type:`ngtcp2_preferred_addr` represents preferred address + * structure. + */ +typedef struct ngtcp2_preferred_addr { + /** + * :member:`cid` is a Connection ID. + */ + ngtcp2_cid cid; + /** + * :member:`ipv4_port` is a port of IPv4 address. + */ + uint16_t ipv4_port; + /** + * :member:`ipv6_port` is a port of IPv6 address. + */ + uint16_t ipv6_port; + /** + * :member:`ipv4_addr` contains IPv4 address in network byte order. + */ + uint8_t ipv4_addr[4]; + /** + * :member:`ipv6_addr` contains IPv6 address in network byte order. + */ + uint8_t ipv6_addr[16]; + /** + * :member:`stateless_reset_token` contains stateless reset token. + */ + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_preferred_addr; + +/** + * @struct + * + * :type:`ngtcp2_transport_params` represents QUIC transport + * parameters. + */ +typedef struct ngtcp2_transport_params { + /** + * :member:`preferred_address` contains preferred address if + * :member:`preferred_address_present` is nonzero. + */ + ngtcp2_preferred_addr preferred_address; + /** + * :member:`original_dcid` is the Destination Connection ID field + * from the first Initial packet from client. Server must specify + * this field. It is expected that application knows the original + * Destination Connection ID even if it sends Retry packet, for + * example, by including it in retry token. Otherwise, application + * should not specify this field. + */ + ngtcp2_cid original_dcid; + /** + * :member:`initial_scid` is the Source Connection ID field from the + * first Initial packet the endpoint sends. Application should not + * specify this field. + */ + ngtcp2_cid initial_scid; + /** + * :member:`retry_scid` is the Source Connection ID field from Retry + * packet. Only server uses this field. If server application + * received Initial packet with retry token from client and server + * verified its token, server application must set Destination + * Connection ID field from the Initial packet to this field and set + * :member:`retry_scid_present` to nonzero. Server application must + * verify that the Destination Connection ID from Initial packet was + * sent in Retry packet by, for example, including the Connection ID + * in a token, or including it in AAD when encrypting a token. + */ + ngtcp2_cid retry_scid; + /** + * :member:`initial_max_stream_data_bidi_local` is the size of flow + * control window of locally initiated stream. This is the number + * of bytes that the remote endpoint can send and the local endpoint + * must ensure that it has enough buffer to receive them. + */ + uint64_t initial_max_stream_data_bidi_local; + /** + * :member:`initial_max_stream_data_bidi_remote` is the size of flow + * control window of remotely initiated stream. This is the number + * of bytes that the remote endpoint can send and the local endpoint + * must ensure that it has enough buffer to receive them. + */ + uint64_t initial_max_stream_data_bidi_remote; + /** + * :member:`initial_max_stream_data_uni` is the size of flow control + * window of remotely initiated unidirectional stream. This is the + * number of bytes that the remote endpoint can send and the local + * endpoint must ensure that it has enough buffer to receive them. + */ + uint64_t initial_max_stream_data_uni; + /** + * :member:`initial_max_data` is the connection level flow control + * window. + */ + uint64_t initial_max_data; + /** + * :member:`initial_max_streams_bidi` is the number of concurrent + * streams that the remote endpoint can create. + */ + uint64_t initial_max_streams_bidi; + /** + * :member:`initial_max_streams_uni` is the number of concurrent + * unidirectional streams that the remote endpoint can create. + */ + uint64_t initial_max_streams_uni; + /** + * :member:`max_idle_timeout` is a duration during which sender + * allows quiescent. + */ + ngtcp2_duration max_idle_timeout; + /** + * :member:`max_udp_payload_size` is the maximum datagram size that + * the endpoint can receive. + */ + uint64_t max_udp_payload_size; + /** + * :member:`active_connection_id_limit` is the maximum number of + * Connection ID that sender can store. + */ + uint64_t active_connection_id_limit; + /** + * :member:`ack_delay_exponent` is the exponent used in ACK Delay + * field in ACK frame. + */ + uint64_t ack_delay_exponent; + /** + * :member:`max_ack_delay` is the maximum acknowledgement delay by + * which the endpoint will delay sending acknowledgements. + */ + ngtcp2_duration max_ack_delay; + /** + * :member:`max_datagram_frame_size` is the maximum size of DATAGRAM + * frame that this endpoint willingly receives. Specifying 0 + * disables DATAGRAM support. See + * https://tools.ietf.org/html/draft-ietf-quic-datagram-01 + */ + uint64_t max_datagram_frame_size; + /** + * :member:`stateless_reset_token_present` is nonzero if + * :member:`stateless_reset_token` field is set. + */ + uint8_t stateless_reset_token_present; + /** + * :member:`disable_active_migration` is nonzero if the endpoint + * does not support active connection migration. + */ + uint8_t disable_active_migration; + /** + * :member:`retry_scid_present` is nonzero if :member:`retry_scid` + * field is set. + */ + uint8_t retry_scid_present; + /** + * :member:`preferred_address_present` is nonzero if + * :member:`preferred_address` is set. + */ + uint8_t preferred_address_present; + /** + * :member:`stateless_reset_token` contains stateless reset token. + */ + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_transport_params; + +/** + * @struct + * + * :type:`ngtcp2_log` is ngtcp2 library internal logger. + */ +typedef struct ngtcp2_log ngtcp2_log; + +/** + * @enum + * + * :type:`ngtcp2_pktns_id` defines packet number space identifier. + */ +typedef enum ngtcp2_pktns_id { + /** + * :enum:`NGTCP2_PKTNS_ID_INITIAL` is the Initial packet number + * space. + */ + NGTCP2_PKTNS_ID_INITIAL, + /** + * :enum:`NGTCP2_PKTNS_ID_HANDSHAKE` is the Handshake packet number + * space. + */ + NGTCP2_PKTNS_ID_HANDSHAKE, + /** + * :enum:`NGTCP2_PKTNS_ID_APPLICATION` is the Application data + * packet number space. + */ + NGTCP2_PKTNS_ID_APPLICATION, + /** + * :enum:`NGTCP2_PKTNS_ID_MAX` is defined to get the number of + * packet number spaces. + */ + NGTCP2_PKTNS_ID_MAX +} ngtcp2_pktns_id; + +/** + * @struct + * + * :type:`ngtcp2_conn_stat` holds various connection statistics, and + * computed data for recovery and congestion controller. + */ +typedef struct ngtcp2_conn_stat { + /** + * :member:`latest_rtt` is the latest RTT sample which is not + * adjusted by acknowledgement delay. + */ + ngtcp2_duration latest_rtt; + /** + * :member:`min_rtt` is the minimum RTT seen so far. It is not + * adjusted by acknowledgement delay. + */ + ngtcp2_duration min_rtt; + /** + * :member:`smoothed_rtt` is the smoothed RTT. + */ + ngtcp2_duration smoothed_rtt; + /** + * :member:`rttvar` is a mean deviation of observed RTT. + */ + ngtcp2_duration rttvar; + /** + * :member:`initial_rtt` is the initial RTT which is used when no + * RTT sample is available. + */ + ngtcp2_duration initial_rtt; + /** + * :member:`first_rtt_sample_ts` is the timestamp when the first RTT + * sample is obtained. + */ + ngtcp2_tstamp first_rtt_sample_ts; + /** + * :member:`pto_count` is the count of successive PTO timer + * expiration. + */ + size_t pto_count; + /** + * :member:`loss_detection_timer` is the deadline of the current + * loss detection timer. + */ + ngtcp2_tstamp loss_detection_timer; + /** + * :member:`last_tx_pkt_ts` corresponds to + * time_of_last_sent_ack_eliciting_packet in + * draft-ietf-quic-recovery-32. + */ + ngtcp2_tstamp last_tx_pkt_ts[NGTCP2_PKTNS_ID_MAX]; + /** + * :member:`loss_time` corresponds to loss_time in + * draft-ietf-quic-recovery-32. + */ + ngtcp2_tstamp loss_time[NGTCP2_PKTNS_ID_MAX]; + /** + * :member:`cwnd` is the size of congestion window. + */ + uint64_t cwnd; + /** + * :member:`ssthresh` is slow start threshold. + */ + uint64_t ssthresh; + /** + * :member:`congestion_recovery_start_ts` is the timestamp when + * congestion recovery started. + */ + ngtcp2_tstamp congestion_recovery_start_ts; + /** + * :member:`bytes_in_flight` is the number in bytes of all sent + * packets which have not been acknowledged. + */ + uint64_t bytes_in_flight; + /** + * :member:`max_udp_payload_size` is the maximum size of UDP + * datagram payload that this endpoint transmits. It is used by + * congestion controller to compute congestion window. + */ + size_t max_udp_payload_size; + /** + * :member:`delivery_rate_sec` is the current sending rate measured + * in byte per second. + */ + uint64_t delivery_rate_sec; +} ngtcp2_conn_stat; + +/** + * @enum + * + * :type:`ngtcp2_cc_algo` defines congestion control algorithms. + */ +typedef enum ngtcp2_cc_algo { + /** + * :enum:`NGTCP2_CC_ALGO_RENO` represents Reno. + */ + NGTCP2_CC_ALGO_RENO = 0x00, + /** + * :enum:`NGTCP2_CC_ALGO_CUBIC` represents Cubic. + */ + NGTCP2_CC_ALGO_CUBIC = 0x01, + /** + * :enum:`NGTCP2_CC_ALGO_CUSTOM` represents custom congestion + * control algorithm. + */ + NGTCP2_CC_ALGO_CUSTOM = 0xff +} ngtcp2_cc_algo; + +/** + * @struct + * + * :type:`ngtcp2_cc_base` is the base structure of custom congestion + * control algorithm. It must be the first field of custom congestion + * controller. + */ +typedef struct ngtcp2_cc_base { + /** + * :member:`log` is ngtcp2 library internal logger. + */ + ngtcp2_log *log; +} ngtcp2_cc_base; + +/** + * @struct + * + * :type:`ngtcp2_cc_pkt` is a convenient structure to include + * acked/lost/sent packet. + */ +typedef struct ngtcp2_cc_pkt { + /** + * :member:`pkt_num` is the packet number + */ + int64_t pkt_num; + /** + * :member:`pktlen` is the length of packet. + */ + size_t pktlen; + /** + * :member:`pktns_id` is the ID of packet number space which this + * packet belongs to. + */ + ngtcp2_pktns_id pktns_id; + /** + * :member:`ts_sent` is the timestamp when packet is sent. + */ + ngtcp2_tstamp ts_sent; +} ngtcp2_cc_pkt; + +typedef struct ngtcp2_cc ngtcp2_cc; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_acked` is a callback function which is + * called with an acknowledged packet. + */ +typedef void (*ngtcp2_cc_on_pkt_acked)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_congestion_event` is a callback function which is + * called when congestion event happens (e.g., when packet is lost). + */ +typedef void (*ngtcp2_cc_congestion_event)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_persistent_congestion` is a callback function + * which is called when persistent congestion is established. + */ +typedef void (*ngtcp2_cc_on_persistent_congestion)(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_ack_recv` is a callback function which is + * called when an acknowledgement is received. + */ +typedef void (*ngtcp2_cc_on_ack_recv)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_on_pkt_sent` is a callback function which is + * called when an ack-eliciting packet is sent. + */ +typedef void (*ngtcp2_cc_on_pkt_sent)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_new_rtt_sample` is a callback function which is + * called when new RTT sample is obtained. + */ +typedef void (*ngtcp2_cc_new_rtt_sample)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +/** + * @functypedef + * + * :type:`ngtcp2_cc_reset` is a callback function which is called when + * congestion state must be reset. + */ +typedef void (*ngtcp2_cc_reset)(ngtcp2_cc *cc); + +/** + * @enum + * + * :type:`ngtcp2_cc_event_type` defines congestion control events. + */ +typedef enum ngtcp2_cc_event_type { + /** + * :enum:`NGTCP2_CC_EVENT_TX_START` occurs when ack-eliciting packet + * is sent and no other ack-eliciting packet is present. + */ + NGTCP2_CC_EVENT_TYPE_TX_START +} ngtcp2_cc_event_type; + +/** + * @functypedef + * + * :type:`ngtcp2_cc_event` is a callback function which is called when + * a specific event happens. + */ +typedef void (*ngtcp2_cc_event)(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +/** + * @struct + * + * :type:`ngtcp2_cc` is congestion control algorithm interface to + * allow custom implementation. + */ +typedef struct ngtcp2_cc { + /** + * :member:`ccb` is a pointer to :type:`ngtcp2_cc_base` which + * usually contains a state. + */ + ngtcp2_cc_base *ccb; + /** + * :member:`on_pkt_acked` is a callback function which is called + * when a packet is acknowledged. + */ + ngtcp2_cc_on_pkt_acked on_pkt_acked; + /** + * :member:`congestion_event` is a callback function which is called + * when congestion event happens (.e.g, packet is lost). + */ + ngtcp2_cc_congestion_event congestion_event; + /** + * :member:`on_persistent_congestion` is a callback function which + * is called when persistent congestion is established. + */ + ngtcp2_cc_on_persistent_congestion on_persistent_congestion; + /** + * :member:`on_ack_recv` is a callback function which is called when + * an acknowledgement is received. + */ + ngtcp2_cc_on_ack_recv on_ack_recv; + /** + * :member:`on_pkt_sent` is a callback function which is called when + * ack-eliciting packet is sent. + */ + ngtcp2_cc_on_pkt_sent on_pkt_sent; + /** + * :member:`new_rtt_sample` is a callback function which is called + * when new RTT sample is obtained. + */ + ngtcp2_cc_new_rtt_sample new_rtt_sample; + /** + * :member:`reset` is a callback function which is called when + * congestion control state must be reset. + */ + ngtcp2_cc_reset reset; + /** + * :member:`event` is a callback function which is called when a + * specific event happens. + */ + ngtcp2_cc_event event; +} ngtcp2_cc; + +/** + * @functypedef + * + * :type:`ngtcp2_printf` is a callback function for logging. + * |user_data| is the same object passed to `ngtcp2_conn_client_new` + * or `ngtcp2_conn_server_new`. + */ +typedef void (*ngtcp2_printf)(void *user_data, const char *format, ...); + +/** + * @macrosection + * + * QLog related macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_QLOG_WRITE_FLAG_NONE 0 +/** + * @macro + * + * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` indicates that this is the + * final call to :type:`ngtcp2_qlog_write` in the current connection. + */ +#define NGTCP2_QLOG_WRITE_FLAG_FIN 0x01 + +/** + * @struct + * + * :type:`ngtcp2_rand_ctx` is a wrapper around native random number + * generator. It is opaque to the ngtcp2 library. This might be + * useful if application needs to specify random number generator per + * thread or per connection. + */ +typedef struct ngtcp2_rand_ctx { + /** + * :member:`native_handle` is a pointer to an underlying random + * number generator. + */ + void *native_handle; +} ngtcp2_rand_ctx; + +/** + * @functypedef + * + * :type:`ngtcp2_qlog_write` is a callback function which is called to + * write qlog |data| of length |datalen| bytes. |flags| is bitwise OR + * of zero or more of NGTCP2_QLOG_WRITE_FLAG_*. See + * :macro:`NGTCP2_QLOG_WRITE_FLAG_NONE`. If + * :macro:`NGTCP2_QLOG_WRITE_FLAG_FIN` is set, |datalen| may be 0. + */ +typedef void (*ngtcp2_qlog_write)(void *user_data, uint32_t flags, + const void *data, size_t datalen); + +/** + * @struct + * + * :type:`ngtcp2_qlog_settings` is a set of settings for qlog. + */ +typedef struct ngtcp2_qlog_settings { + /** + * :member:`odcid` is Original Destination Connection ID sent by + * client. It is used as group_id and ODCID fields. Client ignores + * this field and uses dcid parameter passed to + * `ngtcp2_conn_client_new()`. + */ + ngtcp2_cid odcid; + /** + * :member:`write` is a callback function to write qlog. Setting + * ``NULL`` disables qlog. + */ + ngtcp2_qlog_write write; +} ngtcp2_qlog_settings; + +/** + * @struct + * + * :type:`ngtcp2_settings` defines QUIC connection settings. + */ +typedef struct ngtcp2_settings { + /** + * :member:`qlog` is qlog settings. + */ + ngtcp2_qlog_settings qlog; + /** + * :member:`cc_algo` specifies congestion control algorithm. If + * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` is set, :member:`cc` + * must be set to a pointer to custom congestion control algorithm. + */ + ngtcp2_cc_algo cc_algo; + /** + * :member:`cc` is a pointer to custom congestion control algorithm. + * :member:`cc_algo` must be set to + * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUSTOM` in order to enable + * custom congestion control algorithm. + */ + ngtcp2_cc *cc; + /** + * :member:`initial_ts` is an initial timestamp given to the + * library. + */ + ngtcp2_tstamp initial_ts; + /** + * :member:`initial_rtt` is an initial RTT. + */ + ngtcp2_duration initial_rtt; + /** + * :member:`log_printf` is a function that the library uses to write + * logs. ``NULL`` means no logging output. It is nothing to do + * with qlog. + */ + ngtcp2_printf log_printf; + /** + * :member:`max_udp_payload_size` is the maximum size of UDP + * datagram payload that this endpoint transmits. It is used by + * congestion controller to compute congestion window. If it is set + * to 0, it defaults to :macro:`NGTCP2_DEFAULT_MAX_PKTLEN`. + */ + size_t max_udp_payload_size; + /** + * :member:`token` is a token from Retry packet or NEW_TOKEN frame. + * + * Server sets this field if it received the token in Client Initial + * packet and successfully validated. + * + * Client sets this field if it intends to send token in its Initial + * packet. + * + * `ngtcp2_conn_server_new` and `ngtcp2_conn_client_new` make a copy + * of token. + */ + ngtcp2_vec token; + /** + * :member:`rand_ctx` is an optional random number generator to be + * passed to :type:`ngtcp2_rand` callback. + */ + ngtcp2_rand_ctx rand_ctx; + /** + * :member:`max_window` is the maximum connection-level flow control + * window if connection-level window auto-tuning is enabled. The + * connection-level window auto tuning is enabled if nonzero value + * is specified in this field. The initial value of window size is + * :member:`ngtcp2_transport_params.initial_max_data`. The window + * size is scaled up to the value specified in this field. + */ + uint64_t max_window; + /** + * :member:`max_stream_window` is the maximum stream-level flow + * control window if stream-level window auto-tuning is enabled. + * The stream-level window auto-tuning is enabled if nonzero value + * is specified in this field. The initial value of window size is + * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_remote`, + * :member:`ngtcp2_transport_params.initial_max_stream_data_bidi_local`, + * or :member:`ngtcp2_transport_params.initial_max_stream_data_uni`, + * depending on the type of stream. The window size is scaled up to + * the value specified in this field. + */ + uint64_t max_stream_window; + /** + * :member:`ack_thresh` is the maximum number of unacknowledged + * packets before sending acknowledgement. It triggers the + * immediate acknowledgement. + */ + size_t ack_thresh; +} ngtcp2_settings; + +/** + * @struct + * + * :type:`ngtcp2_addr` is the endpoint address. + */ +typedef struct ngtcp2_addr { + /** + * :member:`addrlen` is the length of addr. + */ + size_t addrlen; + /** + * :member:`addr` points to the buffer which contains endpoint + * address. It must not be ``NULL``. + */ + struct sockaddr *addr; + /** + * :member:`user_data` is an arbitrary data and opaque to the + * library. + */ + void *user_data; +} ngtcp2_addr; + +/** + * @struct + * + * :type:`ngtcp2_path` is the network endpoints where a packet is sent + * and received. + */ +typedef struct ngtcp2_path { + /** + * :member:`local` is the address of local endpoint. + */ + ngtcp2_addr local; + /** + * :member:`remote` is the address of remote endpoint. + */ + ngtcp2_addr remote; +} ngtcp2_path; + +/** + * @struct + * + * :type:`ngtcp2_path_storage` is a convenient struct to have buffers + * to store the longest addresses. + */ +typedef struct ngtcp2_path_storage { + /** + * :member:`local_addrbuf` is a buffer to store local address. + */ + struct sockaddr_storage local_addrbuf; + /** + * :member:`remote_addrbuf` is a buffer to store remote address. + */ + struct sockaddr_storage remote_addrbuf; + /** + * :member:`path` stores network path. + */ + ngtcp2_path path; +} ngtcp2_path_storage; + +/** + * @struct + * + * :type:`ngtcp2_crypto_md` is a wrapper around native message digest + * object. + */ +typedef struct ngtcp2_crypto_md { + /** + * :member:`native_handle` is a pointer to an underlying message + * digest object. + */ + void *native_handle; +} ngtcp2_crypto_md; + +/** + * @struct + * + * :type:`ngtcp2_crypto_aead` is a wrapper around native AEAD object. + */ +typedef struct ngtcp2_crypto_aead { + /** + * :member:`native_handle` is a pointer to an underlying AEAD + * object. + */ + void *native_handle; + /** + * :member:`max_overhead` is the number of additional bytes which + * AEAD encryption needs on encryption. + */ + size_t max_overhead; +} ngtcp2_crypto_aead; + +/** + * @struct + * + * :type:`ngtcp2_crypto_cipher` is a wrapper around native cipher + * object. + */ +typedef struct ngtcp2_crypto_cipher { + /** + * :member:`native_handle` is a pointer to an underlying cipher + * object. + */ + void *native_handle; +} ngtcp2_crypto_cipher; + +/** + * @struct + * + * :type:`ngtcp2_crypto_aead_ctx` is a wrapper around native AEAD + * cipher context object. It should be initialized with a specific + * key. ngtcp2 library reuses this context object to encrypt or + * decrypt multiple packets. + */ +typedef struct ngtcp2_crypto_aead_ctx { + /** + * :member:`native_handle` is a pointer to an underlying AEAD + * context object. + */ + void *native_handle; +} ngtcp2_crypto_aead_ctx; + +/** + * @struct + * + * :type:`ngtcp2_crypto_cipher_ctx` is a wrapper around native cipher + * context object. It should be initialized with a specific key. + * ngtcp2 library reuses this context object to encrypt or decrypt + * multiple packet headers. + */ +typedef struct ngtcp2_crypto_cipher_ctx { + /** + * :member:`native_handle` is a pointer to an underlying cipher + * context object. + */ + void *native_handle; +} ngtcp2_crypto_cipher_ctx; + +/** + * @struct + * + * :type:`ngtcp2_crypto_ctx` is a convenient structure to bind all + * crypto related objects in one place. Use + * `ngtcp2_crypto_ctx_initial` to initialize this struct for Initial + * packet encryption. For Handshake and Short packets, use + * `ngtcp2_crypto_ctx_tls`. + */ +typedef struct ngtcp2_crypto_ctx { + /** + * :member:`aead` is AEAD object. + */ + ngtcp2_crypto_aead aead; + /** + * :member:`md` is message digest object. + */ + ngtcp2_crypto_md md; + /** + * :member:`hp` is header protection cipher. + */ + ngtcp2_crypto_cipher hp; + /** + * :member:`max_encryption` is the number of encryption which this + * key can be used with. + */ + uint64_t max_encryption; + /** + * :member:`max_decryption_failure` is the number of decryption + * failure with this key. + */ + uint64_t max_decryption_failure; +} ngtcp2_crypto_ctx; + +/** + * @function + * + * `ngtcp2_encode_transport_params` encodes |params| in |dest| of + * length |destlen|. + * + * This function returns the number of written, or one of the + * following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`: + * |exttype| is invalid. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_encode_transport_params( + uint8_t *dest, size_t destlen, ngtcp2_transport_params_type exttype, + const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_decode_transport_params` decodes transport parameters in + * |data| of length |datalen|, and stores the result in the object + * pointed by |params|. + * + * If the optional parameters are missing, the default value is + * assigned. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM` + * The required parameter is missing. + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM` + * The input is malformed. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT`: + * |exttype| is invalid. + */ +NGTCP2_EXTERN int +ngtcp2_decode_transport_params(ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen); + +/** + * @function + * + * `ngtcp2_pkt_decode_version_cid` extracts QUIC version, Destination + * Connection ID and Source Connection ID from the packet pointed by + * |data| of length |datalen|. This function can handle Connection ID + * up to 255 bytes unlike `ngtcp2_pkt_decode_hd_long` or + * `ngtcp2_pkt_decode_hd_short` which are only capable of handling + * Connection ID less than or equal to :macro:`NGTCP2_MAX_CIDLEN`. + * Longer Connection ID is only valid if the version is unsupported + * QUIC version. + * + * If the given packet is Long packet, this function extracts the + * version from the packet and assigns it to |*pversion|. It also + * extracts the pointer to the Destination Connection ID and its + * length and assigns them to |*pdcid| and |*pdcidlen| respectively. + * Similarly, it extracts the pointer to the Source Connection ID and + * its length and assigns them to |*pscid| and |*pscidlen| + * respectively. + * + * If the given packet is Short packet, |*pversion| will be 0, + * |*pscid| will be ``NULL``, and |*pscidlen| will be 0. Because the + * Short packet does not have the length of Destination Connection ID, + * the caller has to pass the length in |short_dcidlen|. This + * function extracts the pointer to the Destination Connection ID and + * assigns it to |*pdcid|. |short_dcidlen| is assigned to + * |*pdcidlen|. + * + * This function returns 0 or 1 if it succeeds. It returns 1 if + * Version Negotiation packet should be sent. Otherwise, one of the + * following negative error code: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * The function could not decode the packet header. + */ +NGTCP2_EXTERN int +ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, + size_t *pdcidlen, const uint8_t **pscid, + size_t *pscidlen, const uint8_t *data, + size_t datalen, size_t short_dcidlen); + +/** + * @function + * + * `ngtcp2_pkt_decode_hd_long` decodes QUIC long packet header in + * |pkt| of length |pktlen|. This function only parses the input just + * before packet number field. + * + * This function does not verify that length field is correct. In + * other words, this function succeeds even if length > |pktlen|. + * + * This function can handle Connection ID up to + * :macro:`NGTCP2_MAX_CIDLEN`. Consider to use + * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. + * + * This function handles Version Negotiation specially. If version + * field is 0, |pkt| must contain Version Negotiation packet. Version + * Negotiation packet has random type in wire format. For + * convenience, this function sets + * :enum:`ngtcp2_pkt_type.NGTCP2_PKT_VERSION_NEGOTIATION` to + * dest->type, and set dest->payloadlen and dest->pkt_num to 0. + * Version Negotiation packet occupies a single packet. + * + * It stores the result in the object pointed by |dest|, and returns + * the number of bytes decoded to read the packet header if it + * succeeds, or one of the following error codes: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * Packet is too short; or it is not a long header + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, + const uint8_t *pkt, + size_t pktlen); + +/** + * @function + * + * `ngtcp2_pkt_decode_hd_short` decodes QUIC short packet header in + * |pkt| of length |pktlen|. |dcidlen| is the length of DCID in + * packet header. Short packet does not encode the length of + * connection ID, thus we need the input from the outside. This + * function only parses the input just before packet number field. + * This function can handle Connection ID up to + * :macro:`NGTCP2_MAX_CIDLEN`. Consider to use + * `ngtcp2_pkt_decode_version_cid` to get longer Connection ID. It + * stores the result in the object pointed by |dest|, and returns the + * number of bytes decoded to read the packet header if it succeeds, + * or one of the following error codes: + * + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * Packet is too short; or it is not a short header + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, + const uint8_t *pkt, + size_t pktlen, + size_t dcidlen); + +/** + * @function + * + * `ngtcp2_pkt_write_stateless_reset` writes Stateless Reset packet in + * the buffer pointed by |dest| whose length is |destlen|. + * |stateless_reset_token| is a pointer to the Stateless Reset Token, + * and its length must be :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` + * bytes long. |rand| specifies the random octets preceding Stateless + * Reset Token. The length of |rand| is specified by |randlen| which + * must be at least :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN` bytes + * long. + * + * If |randlen| is too long to write them all in the buffer, |rand| is + * written to the buffer as much as possible, and is truncated. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * |randlen| is strictly less than + * :macro:`NGTCP2_MIN_STATELESS_RETRY_RANDLEN`. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_stateless_reset( + uint8_t *dest, size_t destlen, const uint8_t *stateless_reset_token, + const uint8_t *rand, size_t randlen); + +/** + * @function + * + * `ngtcp2_pkt_write_version_negotiation` writes Version Negotiation + * packet in the buffer pointed by |dest| whose length is |destlen|. + * |unused_random| should be generated randomly. |dcid| is the + * destination connection ID which appears in a packet as a source + * connection ID sent by client which caused version negotiation. + * Similarly, |scid| is the source connection ID which appears in a + * packet as a destination connection ID sent by client. |sv| is a + * list of supported versions, and |nsv| specifies the number of + * supported versions included in |sv|. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( + uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, + size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, + size_t nsv); + +/** + * @struct + * + * :type:`ngtcp2_conn` represents a single QUIC connection. + */ +typedef struct ngtcp2_conn ngtcp2_conn; + +/** + * @functypedef + * + * :type:`ngtcp2_client_initial` is invoked when client application + * asks TLS stack to produce first TLS cryptographic handshake data. + * + * This implementation of this callback must get the first handshake + * data from TLS stack and pass it to ngtcp2 library using + * `ngtcp2_conn_submit_crypto_data` function. Make sure that before + * calling `ngtcp2_conn_submit_crypto_data` function, client + * application must create initial packet protection keys and IVs, and + * provide them to ngtcp2 library using `ngtcp2_conn_set_initial_key` + * and + * + * This callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + * + * TODO: Define error code for TLS stack failure. Suggestion: + * NGTCP2_ERR_CRYPTO. + */ +typedef int (*ngtcp2_client_initial)(ngtcp2_conn *conn, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_client_initial` is invoked when server receives + * Initial packet from client. An server application must implement + * this callback, and generate initial keys and IVs for both + * transmission and reception. Install them using + * `ngtcp2_conn_set_initial_key`. |dcid| is the destination + * connection ID which client generated randomly. It is used to + * derive initial packet protection keys. + * + * The callback function must return 0 if it succeeds. If an error + * occurs, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the + * library call return immediately. + * + * TODO: Define error code for TLS stack failure. Suggestion: + * NGTCP2_ERR_CRYPTO. + */ +typedef int (*ngtcp2_recv_client_initial)(ngtcp2_conn *conn, + const ngtcp2_cid *dcid, + void *user_data); + +/** + * @enum + * + * :type:`ngtcp2_crypto_level` is encryption level. + */ +typedef enum ngtcp2_crypto_level { + /** + * :enum:`NGTCP2_CRYPTO_LEVEL_INITIAL` is Initial Keys encryption + * level. + */ + NGTCP2_CRYPTO_LEVEL_INITIAL, + /** + * :enum:`NGTCP2_CRYPTO_LEVEL_HANDSHAKE` is Handshake Keys + * encryption level. + */ + NGTCP2_CRYPTO_LEVEL_HANDSHAKE, + /** + * :enum:`NGTCP2_CRYPTO_LEVEL_APPLICATION` is Application Data + * (1-RTT) Keys encryption level. + */ + NGTCP2_CRYPTO_LEVEL_APPLICATION, + /** + * :enum:`NGTCP2_CRYPTO_LEVEL_EARLY` is Early Data (0-RTT) Keys + * encryption level. + */ + NGTCP2_CRYPTO_LEVEL_EARLY +} ngtcp2_crypto_level; + +/** + * @functypedef + * + * :type`ngtcp2_recv_crypto_data` is invoked when crypto data is + * received. The received data is pointed to by |data|, and its + * length is |datalen|. The |offset| specifies the offset where + * |data| is positioned. |user_data| is the arbitrary pointer passed + * to `ngtcp2_conn_client_new` or `ngtcp2_conn_server_new`. The + * ngtcp2 library ensures that the crypto data is passed to the + * application in the increasing order of |offset|. |datalen| is + * always strictly greater than 0. |crypto_level| indicates the + * encryption level where this data is received. Crypto data can + * never be received in + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`. + * + * The application should provide the given data to TLS stack. + * + * The callback function must return 0 if it succeeds. If TLS stack + * reported error, return :macro:`NGTCP2_ERR_CRYPTO`. If application + * encounters fatal error, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * which makes the library call return immediately. If the other + * value is returned, it is treated as + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`. + */ +typedef int (*ngtcp2_recv_crypto_data)(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_handshake_completed` is invoked when QUIC + * cryptographic handshake has completed. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_handshake_completed)(ngtcp2_conn *conn, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_handshake_confirmed` is invoked when QUIC + * cryptographic handshake is confirmed. The handshake confirmation + * means that both endpoints agree that handshake has finished. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_handshake_confirmed)(ngtcp2_conn *conn, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_version_negotiation` is invoked when Version + * Negotiation packet is received. |hd| is the pointer to the QUIC + * packet header object. The vector |sv| of |nsv| elements contains + * the QUIC version the server supports. Since Version Negotiation is + * only sent by server, this callback function is used by client only. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_recv_version_negotiation)(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_retry` is invoked when Retry packet is received. + * This callback is client only. + * + * Application must regenerate packet protection key, IV, and header + * protection key for Initial packets using the destination connection + * ID obtained by `ngtcp2_conn_get_dcid()` and install them by calling + * `ngtcp2_conn_install_initial_key()`. + * + * 0-RTT data accepted by the ngtcp2 library will be retransmitted by + * the library automatically. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_retry)(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_encrypt` is invoked when the ngtcp2 library asks the + * application to encrypt packet payload. The packet payload to + * encrypt is passed as |plaintext| of length |plaintextlen|. The + * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context + * object which is initialized with encryption key. The nonce is + * passed as |nonce| of length |noncelen|. The ad, Additional Data to + * AEAD, is passed as |ad| of length |adlen|. + * + * The implementation of this callback must encrypt |plaintext| using + * the negotiated cipher suite and write the ciphertext into the + * buffer pointed by |dest|. |dest| has enough capacity to store the + * ciphertext. + * + * |dest| and |plaintext| may point to the same buffer. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_encrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @functypedef + * + * :type:`ngtcp2_decrypt` is invoked when the ngtcp2 library asks the + * application to decrypt packet payload. The packet payload to + * decrypt is passed as |ciphertext| of length |ciphertextlen|. The + * AEAD cipher is |aead|. |aead_ctx| is the AEAD cipher context + * object which is initialized with decryption key. The nonce is + * passed as |nonce| of length |noncelen|. The ad, Additional Data to + * AEAD, is passed as |ad| of length |adlen|. + * + * The implementation of this callback must decrypt |ciphertext| using + * the negotiated cipher suite and write the ciphertext into the + * buffer pointed by |dest|. |dest| has enough capacity to store the + * cleartext. + * + * |dest| and |ciphertext| may point to the same buffer. + * + * The callback function must return 0 if it succeeds. If TLS stack + * fails to decrypt data, return :macro:`NGTCP2_ERR_TLS_DECRYPT`. For + * any other errors, return :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which + * makes the library call return immediately. + */ +typedef int (*ngtcp2_decrypt)(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *ad, size_t adlen); + +/** + * @functypedef + * + * :type:`ngtcp2_hp_mask` is invoked when the ngtcp2 library asks the + * application to produce mask to encrypt or decrypt packet header. + * The encryption cipher is |hp|. |hp_ctx| is the cipher context + * object which is initialized with header protection key. The sample + * is passed as |sample|. + * + * The implementation of this callback must produce a mask using the + * header protection cipher suite specified by QUIC specification and + * write the result into the buffer pointed by |dest|. The length of + * mask must be :macro:`NGTCP2_HP_MASKLEN`. The library ensures that + * |dest| has enough capacity. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library call + * return immediately. + */ +typedef int (*ngtcp2_hp_mask)(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample); + +/** + * @macrosection + * + * Stream data flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_STREAM_DATA_FLAG_NONE 0x00 + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` indicates that this chunk of + * data is final piece of an incoming stream. + */ +#define NGTCP2_STREAM_DATA_FLAG_FIN 0x01 + +/** + * @macro + * + * :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` indicates that this chunk of + * data contains data received in 0RTT packet and the handshake has + * not completed yet, which means that the data might be replayed. + */ +#define NGTCP2_STREAM_DATA_FLAG_EARLY 0x02 + +/** + * @functypedef + * + * :type:`ngtcp2_recv_stream_data` is invoked when stream data is + * received. The stream is specified by |stream_id|. |flags| is the + * bitwise-OR of zero or more of NGTCP2_STREAM_DATA_FLAG_*. See + * :macro:`NGTCP2_STREAM_DATA_FLAG_NONE`. If |flags| & + * :macro:`NGTCP2_STREAM_DATA_FLAG_FIN` is nonzero, this portion of + * the data is the last data in this stream. |offset| is the offset + * where this data begins. The library ensures that data is passed to + * the application in the non-decreasing order of |offset|. The data + * is passed as |data| of length |datalen|. |datalen| may be 0 if and + * only if |fin| is nonzero. + * + * If :macro:`NGTCP2_STREAM_DATA_FLAG_EARLY` is set in |flags|, it + * indicates that a part of or whole data was received in 0RTT packet + * and a handshake has not completed yet. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_recv_stream_data)(ngtcp2_conn *conn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *data, size_t datalen, + void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_open` is a callback function which is called + * when remote stream is opened by peer. This function is not called + * if stream is opened by implicitly (we might reconsider this + * behaviour). + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_open)(ngtcp2_conn *conn, int64_t stream_id, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_close` is invoked when a stream is closed. + * This callback is not called when QUIC connection is closed before + * existing streams are closed. |app_error_code| indicates the error + * code of this closure. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_close)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_stream_reset` is invoked when a stream identified by + * |stream_id| is reset by a remote endpoint. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_stream_reset)(ngtcp2_conn *conn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_acked_stream_data_offset` is a callback function + * which is called when stream data is acked, and application can free + * the data. The acked range of data is [offset, offset + datalen). + * For a given stream_id, this callback is called sequentially in + * increasing order of |offset|. |datalen| is normally strictly + * greater than 0. One exception is that when a packet which includes + * STREAM frame which has fin flag set, and 0 length data, this + * callback is invoked with 0 passed as |datalen|. + * + * If a stream is closed prematurely and stream data is still + * in-flight, this callback function is not called for those data. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_acked_stream_data_offset)( + ngtcp2_conn *conn, int64_t stream_id, uint64_t offset, uint64_t datalen, + void *user_data, void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_acked_crypto_offset` is a callback function which is + * called when crypto stream data is acknowledged, and application can + * free the data. |crypto_level| indicates the encryption level where + * this data was sent. Crypto data never be sent in + * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`. This works + * like :type:`ngtcp2_acked_stream_data_offset` but crypto stream has + * no stream_id and stream_user_data, and |datalen| never become 0. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_acked_crypto_offset)(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, uint64_t datalen, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_stateless_reset` is a callback function which is + * called when Stateless Reset packet is received. The stateless + * reset details are given in |sr|. + * + * The implementation of this callback should return 0 if it succeeds. + * Returning :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library + * call return immediately. + */ +typedef int (*ngtcp2_recv_stateless_reset)(ngtcp2_conn *conn, + const ngtcp2_pkt_stateless_reset *sr, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_extend_max_streams` is a callback function which is + * called every time max stream ID is strictly extended. + * |max_streams| is the cumulative number of streams which an endpoint + * can open. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_extend_max_streams)(ngtcp2_conn *conn, + uint64_t max_streams, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_extend_max_stream_data` is a callback function which + * is invoked when max stream data is extended. |stream_id| + * identifies the stream. |max_data| is a cumulative number of bytes + * the endpoint can send on this stream. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_extend_max_stream_data)(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t max_data, void *user_data, + void *stream_user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_rand` is a callback function to get randomized byte + * string from application. Application must fill random |destlen| + * bytes to the buffer pointed by |dest|. |usage| provides the usage + * of the generated random data. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_rand)(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx, + ngtcp2_rand_usage usage); + +/** + * @functypedef + * + * :type:`ngtcp2_get_new_connection_id` is a callback function to ask + * an application for new connection ID. Application must generate + * new unused connection ID with the exact |cidlen| bytes and store it + * in |cid|. It also has to generate stateless reset token into + * |token|. The length of stateless reset token is + * :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` and it is guaranteed that + * the buffer pointed by |cid| has the sufficient space to store the + * token. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_get_new_connection_id)(ngtcp2_conn *conn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_remove_connection_id` is a callback function which + * notifies the application that connection ID |cid| is no longer used + * by remote endpoint. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_remove_connection_id)(ngtcp2_conn *conn, + const ngtcp2_cid *cid, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_update_key` is a callback function which tells the + * application that it must generate new packet protection keying + * materials and AEAD cipher context objects with new keys. The + * current set of secrets are given as |current_rx_secret| and + * |current_tx_secret| of length |secretlen|. They are decryption and + * encryption secrets respectively. + * + * The application has to generate new secrets and keys for both + * encryption and decryption, and write decryption secret and IV to + * the buffer pointed by |rx_secret| and |rx_iv| respectively. It + * also has to create new AEAD cipher context object with new + * decryption key and initialize |rx_aead_ctx| with it. Similarly, + * write encryption secret and IV to the buffer pointed by |tx_secret| + * and |tx_iv|. Create new AEAD cipher context object with new + * encryption key and initialize |tx_aead_ctx| with it. All given + * buffers have the enough capacity to store secret, key and IV. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_update_key)( + ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, + const uint8_t *current_rx_secret, const uint8_t *current_tx_secret, + size_t secretlen, void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_path_validation` is a callback function which tells + * the application the outcome of path validation. |path| is the path + * that was validated. If |res| is + * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_SUCCESS`, + * the path validation succeeded. If |res| is + * :enum:`ngtcp2_path_validation_result.NGTCP2_PATH_VALIDATION_RESULT_FAILURE`, + * the path validation failed. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_path_validation)(ngtcp2_conn *conn, + const ngtcp2_path *path, + ngtcp2_path_validation_result res, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_select_preferred_addr` is a callback function which + * asks a client application to choose server address from preferred + * addresses |paddr| received from server. An application should + * write preferred address in |dest|. If an application denies the + * preferred addresses, just leave |dest| unmodified (or set dest->len + * to 0) and return 0. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_select_preferred_addr)(ngtcp2_conn *conn, + ngtcp2_addr *dest, + const ngtcp2_preferred_addr *paddr, + void *user_data); + +/** + * @enum + * + * :type:`ngtcp2_connection_id_status_type` defines a set of status + * for Destination Connection ID. + */ +typedef enum ngtcp2_connection_id_status_type { + /** + * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE` indicates that + * a local endpoint starts using new destination Connection ID. + */ + NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, + /** + * :enum:`NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE` indicates + * that a local endpoint stops using a given destination Connection + * ID. + */ + NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE +} ngtcp2_connection_id_status_type; + +/** + * @functypedef + * + * :type:`ngtcp2_connection_id_status` is a callback function which is + * called when the status of Connection ID changes. + * + * |token| is the associated stateless reset token and it is ``NULL`` + * if no token is present. + * + * |type| is the one of the value defined in + * :type:`ngtcp2_connection_id_status_type`. The new value might be + * added in the future release. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_connection_id_status)(ngtcp2_conn *conn, int type, + uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_recv_new_token` is a callback function which is + * called when new token is received from server. + * + * |token| is the received token. + * + * The callback function must return 0 if it succeeds. Returning + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` makes the library call return + * immediately. + */ +typedef int (*ngtcp2_recv_new_token)(ngtcp2_conn *conn, const ngtcp2_vec *token, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_delete_crypto_aead_ctx` is a callback function which + * must delete the native object pointed by |aead_ctx|->native_handle. + */ +typedef void (*ngtcp2_delete_crypto_aead_ctx)(ngtcp2_conn *conn, + ngtcp2_crypto_aead_ctx *aead_ctx, + void *user_data); + +/** + * @functypedef + * + * :type:`ngtcp2_delete_crypto_cipher_ctx` is a callback function + * which must delete the native object pointed by + * |cipher_ctx|->native_handle. + */ +typedef void (*ngtcp2_delete_crypto_cipher_ctx)( + ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data); + +/** + * @macrosection + * + * Datagram flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_DATAGRAM_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_DATAGRAM_FLAG_NONE 0x00 + +/** + * @macro + * + * :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` indicates that DATAGRAM frame + * is received in 0RTT packet and the handshake has not completed yet, + * which means that the data might be replayed. + */ +#define NGTCP2_DATAGRAM_FLAG_EARLY 0x01 + +/** + * @functypedef + * + * :type:`ngtcp2_recv_datagram` is invoked when DATAGRAM frame is + * received. |flags| is bitwise-OR of zero or more of + * NGTCP2_DATAGRAM_FLAG_*. See :macro:`NGTCP2_DATAGRAM_FLAG_NONE`. + * + * If :macro:`NGTCP2_DATAGRAM_FLAG_EARLY` is set in |flags|, it + * indicates that DATAGRAM frame was received in 0RTT packet and a + * handshake has not completed yet. + * + * The callback function must return 0 if it succeeds, or + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` which makes the library return + * immediately. + */ +typedef int (*ngtcp2_recv_datagram)(ngtcp2_conn *conn, uint32_t flags, + const uint8_t *data, size_t datalen, + void *user_data); + +/** + * @struct + * + * :type:`ngtcp2_callbacks` holds a set of callback functions. + */ +typedef struct ngtcp2_callbacks { + /** + * :member:`client_initial` is a callback function which is invoked + * when client asks TLS stack to produce first TLS cryptographic + * handshake message. This callback function must be specified. + */ + ngtcp2_client_initial client_initial; + /** + * :member:`recv_client_initial` is a callback function which is + * invoked when a server receives the first packet from client. + * This callback function must be specified. + */ + ngtcp2_recv_client_initial recv_client_initial; + /** + * :member:`recv_crypto_data` is a callback function which is + * invoked when cryptographic data (CRYPTO frame, in other words, + * TLS message) is received. This callback function must be + * specified. + */ + ngtcp2_recv_crypto_data recv_crypto_data; + /** + * :member:`handshake_completed` is a callback function which is + * invoked when QUIC cryptographic handshake has completed. This + * callback function is optional. + */ + ngtcp2_handshake_completed handshake_completed; + /** + * :member:`recv_version_negotiation` is a callback function which + * is invoked when Version Negotiation packet is received by a + * client. This callback function is optional. + */ + ngtcp2_recv_version_negotiation recv_version_negotiation; + /** + * :member:`encrypt` is a callback function which is invoked to + * encrypt a QUIC packet. This callback function must be specified. + */ + ngtcp2_encrypt encrypt; + /** + * :member:`decrypt` is a callback function which is invoked to + * decrypt a QUIC packet. This callback function must be specified. + */ + ngtcp2_decrypt decrypt; + /** + * :member:`hp_mask` is a callback function which is invoked to get + * a mask to encrypt or decrypt packet header. This callback + * function must be specified. + */ + ngtcp2_hp_mask hp_mask; + /** + * :member:`recv_stream_data` is a callback function which is + * invoked when STREAM data, which includes application data, is + * received. This callback function is optional. + */ + ngtcp2_recv_stream_data recv_stream_data; + /** + * :member:`acked_crypto_offset` is a callback function which is + * invoked when CRYPTO data is acknowledged by a remote endpoint. + * It tells an application the largest offset of acknowledged CRYPTO + * data without a gap so that the application can free memory for + * the data. This callback function is optional. + */ + ngtcp2_acked_crypto_offset acked_crypto_offset; + /** + * :member:`acked_stream_data_offset` is a callback function which + * is invoked when STREAM data, which includes application data, is + * acknowledged by a remote endpoint. It tells an application the + * largest offset of acknowledged STREAM data without a gap so that + * application can free memory for the data. This callback function + * is optional. + */ + ngtcp2_acked_stream_data_offset acked_stream_data_offset; + /** + * :member:`stream_open` is a callback function which is invoked + * when new remote stream is opened by a remote endpoint. This + * callback function is optional. + */ + ngtcp2_stream_open stream_open; + /** + * :member:`stream_close` is a callback function which is invoked + * when a stream is closed. This callback function is optional. + */ + ngtcp2_stream_close stream_close; + /** + * :member:`recv_stateless_reset` is a callback function which is + * invoked when Stateless Reset packet is received. This callback + * function is optional. + */ + ngtcp2_recv_stateless_reset recv_stateless_reset; + /** + * :member:`recv_retry` is a callback function which is invoked when + * a client receives Retry packet. For client, this callback + * function must be specified. Server never receive Retry packet. + */ + ngtcp2_recv_retry recv_retry; + /** + * :member:`extend_max_local_streams_bidi` is a callback function + * which is invoked when the number of bidirectional stream which a + * local endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_local_streams_bidi; + /** + * :member:`extend_max_local_streams_uni` is a callback function + * which is invoked when the number of unidirectional stream which a + * local endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_local_streams_uni; + /** + * :member:`rand` is a callback function which is invoked when the + * library needs unpredictable sequence of random data. This + * callback function must be specified. + */ + ngtcp2_rand rand; + /** + * :member:`get_new_connection_id` is a callback function which is + * invoked when the library needs new connection ID. This callback + * function must be specified. + */ + ngtcp2_get_new_connection_id get_new_connection_id; + /** + * :member:`remove_connection_id` is a callback function which + * notifies an application that connection ID is no longer used by a + * remote endpoint. This callback function is optional. + */ + ngtcp2_remove_connection_id remove_connection_id; + /** + * :member:`update_key` is a callback function which is invoked when + * the library tells an application that it must update keying + * materials and install new keys. This function must be specified. + */ + ngtcp2_update_key update_key; + /** + * :member:`path_validation` is a callback function which is invoked + * when path validation completed. This function is optional. + */ + ngtcp2_path_validation path_validation; + /** + * :member:`select_preferred_addr` is a callback function which is + * invoked when the library asks a client to select preferred + * address presented by a server. This function is optional. + */ + ngtcp2_select_preferred_addr select_preferred_addr; + /** + * :member:`stream_reset` is a callback function which is invoked + * when a stream is reset by a remote endpoint. This callback + * function is optional. + */ + ngtcp2_stream_reset stream_reset; + /** + * :member:`extend_max_remote_streams_bidi` is a callback function + * which is invoked when the number of bidirectional streams which a + * remote endpoint can open is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_streams extend_max_remote_streams_bidi; + /** + * :member:`extend_max_remote_streams_uni` is a callback function + * which is invoked when the number of unidirectional streams which + * a remote endpoint can open is increased. This callback function + * is optional. + */ + ngtcp2_extend_max_streams extend_max_remote_streams_uni; + /** + * :member:`extend_max_stream_data` is callback function which is + * invoked when the maximum offset of STREAM data that a local + * endpoint can send is increased. This callback function is + * optional. + */ + ngtcp2_extend_max_stream_data extend_max_stream_data; + /** + * :member:`dcid_status` is a callback function which is invoked + * when the new destination Connection ID is activated or the + * activated destination Connection ID is now deactivated. + */ + ngtcp2_connection_id_status dcid_status; + /** + * :member:`handshake_confirmed` is a callback function which is + * invoked when both endpoints agree that handshake has finished. + * This field is ignored by server because handshake_completed + * indicates the handshake confirmation for server. + */ + ngtcp2_handshake_confirmed handshake_confirmed; + /** + * :member:`recv_new_token` is a callback function which is invoked + * when new token is received from server. This field is ignored by + * server. + */ + ngtcp2_recv_new_token recv_new_token; + /** + * :member:`delete_crypto_aead_ctx` is a callback function which + * deletes a given AEAD cipher context object. + */ + ngtcp2_delete_crypto_aead_ctx delete_crypto_aead_ctx; + /** + * :member:`delete_crypto_cipher_ctx` is a callback function which + * deletes a given cipher context object. + */ + ngtcp2_delete_crypto_cipher_ctx delete_crypto_cipher_ctx; + /** + * :member:`recv_datagram` is a callback function which is invoked + * when DATAGRAM frame is received. + */ + ngtcp2_recv_datagram recv_datagram; +} ngtcp2_callbacks; + +/** + * @function + * + * `ngtcp2_pkt_write_connection_close` writes Initial packet + * containing CONNECTION_CLOSE frame with the given |error_code| to + * the buffer pointed by |dest| of length |destlen|. All encryption + * parameters are for Initial packet encryption. The packet number is + * always 0. + * + * The primary use case of this function is for server to send + * CONNECTION_CLOSE frame in Initial packet to close connection + * without committing the state when validating Retry token fails. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_connection_close( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_pkt_write_retry` writes Retry packet in the buffer pointed + * by |dest| whose length is |destlen|. |odcid| specifies Original + * Destination Connection ID. |token| specifies Retry Token, and + * |tokenlen| specifies its length. |aead| must be AEAD_AES_128_GCM. + * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as an + * encryption key. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * Callback function failed. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_pkt_write_retry( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, + size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @function + * + * `ngtcp2_accept` is used by server implementation, and decides + * whether packet |pkt| of length |pktlen| is acceptable for initial + * packet from client. + * + * If it is acceptable, it returns 0. If it is not acceptable, and + * Version Negotiation packet is required to send, it returns 1. + * Otherwise, it returns -1. + * + * If |dest| is not ``NULL``, and the return value is 0 or 1, the + * decoded packet header is stored to the object pointed by |dest|. + */ +NGTCP2_EXTERN int ngtcp2_accept(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen); + +/** + * @function + * + * `ngtcp2_conn_client_new` creates new :type:`ngtcp2_conn`, and + * initializes it as client. |dcid| is randomized destination + * connection ID. |scid| is source connection ID. |version| is a + * QUIC version to use. |path| is the network path where this QUIC + * connection is being established and must not be ``NULL``. + * |callbacks|, |settings|, and |params| must not be ``NULL``, and the + * function make a copy of each of them. |params| is local QUIC + * transport parameters and sent to a remote endpoint during + * handshake. |user_data| is the arbitrary pointer which is passed to + * the user-defined callback functions. If |mem| is ``NULL``, the + * memory allocator returned by `ngtcp2_mem_default()` is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int +ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); + +/** + * @function + * + * `ngtcp2_conn_server_new` creates new :type:`ngtcp2_conn`, and + * initializes it as server. |dcid| is a destination connection ID. + * |scid| is a source connection ID. |path| is the network path where + * this QUIC connection is being established and must not be ``NULL`. + * |version| is a QUIC version to use. |callbacks|, |settings|, and + * |params| must not be ``NULL``, and the function make a copy of each + * of them. |params| is local QUIC transport parameters and sent to a + * remote endpoint during handshake. |user_data| is the arbitrary + * pointer which is passed to the user-defined callback functions. If + * |mem| is ``NULL``, the memory allocator returned by + * `ngtcp2_mem_default()` is used. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int +ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data); + +/** + * @function + * + * `ngtcp2_conn_del` frees resources allocated for |conn|. It also + * frees memory pointed by |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_del(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_read_pkt` decrypts QUIC packet given in |pkt| of + * length |pktlen| and processes it. |path| is the network path the + * packet is delivered and must not be ``NULL``. |pi| is packet + * metadata and must not be ``NULL``. This function performs QUIC + * handshake as well. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns 0 if it succeeds, or negative error codes. + * In general, if the error code which satisfies + * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned, + * the application should just close the connection by calling + * `ngtcp2_conn_write_connection_close` or just delete the QUIC + * connection using `ngtcp2_conn_del`. It is undefined to call the + * other library functions. If :macro:`NGTCP2_ERR_RETRY` is returned, + * application must be a server and it must perform address validation + * by sending Retry packet and close the connection. If + * :macro:`NGTCP2_ERR_DROP_CONN` is returned, server application must + * drop the connection silently (without sending any CONNECTION_CLOSE + * frame) and discard connection state. + */ +NGTCP2_EXTERN int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, + const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_pkt` is equivalent to calling + * `ngtcp2_conn_writev_stream` without specifying stream data and + * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` as flags. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, + ngtcp2_path *path, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_handshake_completed` tells |conn| that the QUIC + * handshake has completed. + */ +NGTCP2_EXTERN void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_handshake_completed` returns nonzero if handshake + * has completed. + */ +NGTCP2_EXTERN int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_install_initial_key` installs packet protection keying + * materials for Initial packets. |rx_aead_ctx| is AEAD cipher + * context object and must be initialized with decryption key, IV + * |rx_iv| of length |rx_ivlen|, and packet header protection cipher + * context object |rx_hp_ctx| to decrypt incoming Initial packets. + * Similarly, |tx_aead_ctx|, |tx_iv| and |tx_hp_ctx| are for + * encrypting outgoing packets and are the same length with the + * decryption counterpart . If they have already been set, they are + * overwritten. + * + * If this function succeeds, |conn| takes ownership of |rx_aead_ctx|, + * |rx_hp_ctx|, |tx_aead_ctx|, and |tx_hp_ctx|. + * :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * After receiving Retry packet, the DCID most likely changes. In + * that case, client application must generate these keying materials + * again based on new DCID and install them again. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_initial_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx, + const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen); + +/** + * @function + * + * `ngtcp2_conn_install_rx_handshake_key` installs packet protection + * keying materials for decrypting incoming Handshake packets. + * |aead_ctx| is AEAD cipher context object which must be initialized + * with decryption key, IV |iv| of length |ivlen|, and packet header + * protection cipher context object |hp_ctx| to decrypt incoming + * Handshake packets. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx|, + * and |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_rx_handshake_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_tx_handshake_key` installs packet protection + * keying materials for encrypting outgoing Handshake packets. + * |aead_ctx| is AEAD cipher context object which must be initialized + * with encryption key, IV |iv| of length |ivlen|, and packet header + * protection cipher context object |hp_ctx| to encrypt outgoing + * Handshake packets. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_tx_handshake_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_early_key` installs packet protection AEAD + * cipher context object |aead_ctx|, IV |iv| of length |ivlen|, and + * packet header protection cipher context object |hp_ctx| to encrypt + * (for client) or decrypt (for server) 0RTT packets. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_early_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_rx_key` installs packet protection keying + * materials for decrypting Short packets. |secret| of length + * |secretlen| is the decryption secret which is used to derive keying + * materials passed to this function. |aead_ctx| is AEAD cipher + * context object which must be initialized with decryption key, IV + * |iv| of length |ivlen|, and packet header protection cipher context + * object |hp_ctx| to decrypt incoming Short packets. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_rx_key( + ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_install_tx_key` installs packet protection keying + * materials for encrypting Short packets. |secret| of length + * |secretlen| is the encryption secret which is used to derive keying + * materials passed to this function. |aead_ctx| is AEAD cipher + * context object which must be initialized with encryption key, IV + * |iv| of length |ivlen|, and packet header protection cipher context + * object |hp_ctx| to encrypt outgoing Short packets. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx| and + * |hp_ctx|. :type:`ngtcp2_delete_crypto_aead_ctx` and + * :type:`ngtcp2_delete_crypto_cipher_ctx` will be called to delete + * these objects when they are no longer used. If this function + * fails, the caller is responsible to delete them. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_install_tx_key( + ngtcp2_conn *conn, const uint8_t *secret, size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx); + +/** + * @function + * + * `ngtcp2_conn_initiate_key_update` initiates the key update. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The previous key update has not been confirmed yet; or key + * update is too frequent; or new keys are not available yet. + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_set_tls_error` sets the TLS related error |liberr| in + * |conn|. |liberr| must be one of ngtcp2 library error codes (which + * is defined as NGTCP2_ERR_* macro, such as + * :macro:`NGTCP2_ERR_TLS_DECRYPT`). In general, error code should be + * propagated via return value, but sometimes ngtcp2 API is called + * inside callback function of TLS stack and it does not allow to + * return ngtcp2 error code directly. In this case, implementation + * can set the error code (e.g., + * :macro:`NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM`) using this function. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr); + +/** + * @function + * + * `ngtcp2_conn_get_tls_error` returns the value set by + * `ngtcp2_conn_set_tls_error`. If no value is set, this function + * returns 0. + */ +NGTCP2_EXTERN int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_expiry` returns the next expiry time. + * + * Call `ngtcp2_conn_handle_expiry()` and `ngtcp2_conn_write_pkt` (or + * `ngtcp2_conn_writev_stream`) if expiry time is passed. + */ +NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_handle_expiry` handles expired timer. It does nothing + * if timer is not expired. + */ +NGTCP2_EXTERN int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_idle_expiry` returns the time when a connection + * should be closed if it continues to be idle. If idle timeout is + * disabled, this function returns ``UINT64_MAX``. + */ +NGTCP2_EXTERN ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_pto` returns Probe Timeout (PTO). + */ +NGTCP2_EXTERN ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_remote_transport_params` sets transport parameter + * |params| to |conn|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_PROTO` + * If |conn| is server, and negotiated_version field is not the + * same as the used version. + */ +NGTCP2_EXTERN int +ngtcp2_conn_set_remote_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_get_remote_transport_params` fills settings values in + * |params|. original_connection_id and + * original_connection_id_present are always zero filled. + */ +NGTCP2_EXTERN void +ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_set_early_remote_transport_params` sets |params| as + * transport parameter previously received from a server. The + * parameters are used to send 0-RTT data. QUIC requires that client + * application should remember transport parameter as well as session + * ticket. + * + * At least following fields must be set: + * + * * initial_max_stream_id_bidi + * * initial_max_stream_id_uni + * * initial_max_stream_data_bidi_local + * * initial_max_stream_data_bidi_remote + * * initial_max_stream_data_uni + * * initial_max_data + */ +NGTCP2_EXTERN void ngtcp2_conn_set_early_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_set_local_transport_params` sets the local transport + * parameters |params|. This function can only be called by server. + * Although the local transport parameters are passed to + * `ngtcp2_conn_server_new`, server might want to update them after + * ALPN is chosen. In that case, server can update the transport + * parameter with this function. Server must call this function + * before calling `ngtcp2_conn_install_tx_handshake_key`. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + * `ngtcp2_conn_install_tx_handshake_key` has been called. + */ +NGTCP2_EXTERN int +ngtcp2_conn_set_local_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_get_local_transport_params` fills settings values in + * |params|. + */ +NGTCP2_EXTERN void +ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_conn_open_bidi_stream` opens new bidirectional stream. The + * |stream_user_data| is the user data specific to the stream. The + * open stream ID is stored in |*pstream_id|. + * + * Application can call this function before handshake completes. For + * 0RTT packet, application can call this function after calling + * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet, + * application can call this function after calling + * `ngtcp2_conn_set_remote_transport_params` and + * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is + * used, application can call this function after calling + * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` + * The remote peer does not allow |stream_id| yet. + */ +NGTCP2_EXTERN int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, + int64_t *pstream_id, + void *stream_user_data); + +/** + * @function + * + * `ngtcp2_conn_open_uni_stream` opens new unidirectional stream. The + * |stream_user_data| is the user data specific to the stream. The + * open stream ID is stored in |*pstream_id|. + * + * Application can call this function before handshake completes. For + * 0RTT packet, application can call this function after calling + * `ngtcp2_conn_set_early_remote_transport_params`. For 1RTT packet, + * application can call this function after calling + * `ngtcp2_conn_set_remote_transport_params` and + * `ngtcp2_conn_install_tx_key`. If ngtcp2 crypto support library is + * used, application can call this function after calling + * `ngtcp2_crypto_derive_and_install_tx_key` for 1RTT packet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_STREAM_ID_BLOCKED` + * The remote peer does not allow |stream_id| yet. + */ +NGTCP2_EXTERN int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, + int64_t *pstream_id, + void *stream_user_data); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream` closes stream denoted by |stream_id| + * abruptly. |app_error_code| is one of application error codes, and + * indicates the reason of shutdown. Successful call of this function + * does not immediately erase the state of the stream. The actual + * deletion is done when the remote endpoint sends acknowledgement. + * Calling this function is equivalent to call + * `ngtcp2_conn_shutdown_stream_read`, and + * `ngtcp2_conn_shutdown_stream_write` sequentially. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream_write` closes write-side of stream + * denoted by |stream_id| abruptly. |app_error_code| is one of + * application error codes, and indicates the reason of shutdown. If + * this function succeeds, no application data is sent to the remote + * endpoint. It discards all data which has not been acknowledged + * yet. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @function + * + * `ngtcp2_conn_shutdown_stream_read` closes read-side of stream + * denoted by |stream_id| abruptly. |app_error_code| is one of + * application error codes, and indicates the reason of shutdown. If + * this function succeeds, no application data is forwarded to an + * application layer. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t app_error_code); + +/** + * @macrosection + * + * Write stream data flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_WRITE_STREAM_FLAG_NONE 0x00 + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` indicates that more data may + * come and should be coalesced into the same packet if possible. + */ +#define NGTCP2_WRITE_STREAM_FLAG_MORE 0x01 + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` indicates that the passed + * data is the final part of a stream. + */ +#define NGTCP2_WRITE_STREAM_FLAG_FIN 0x02 + +/** + * @function + * + * `ngtcp2_conn_write_stream` is just like + * `ngtcp2_conn_writev_stream`. The only difference is that it + * conveniently accepts a single buffer. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_stream( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, + const uint8_t *data, size_t datalen, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_writev_stream` writes a packet containing stream data + * of stream denoted by |stream_id|. The buffer of the packet is + * pointed by |dest| of length |destlen|. This function performs QUIC + * handshake as well. + * + * Specifying -1 to |stream_id| means no new stream data to send. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. When calling + * this function again after it returns + * :macro:`NGTCP2_ERR_WRITE_MORE`, caller must pass the same |pi| to + * this function. + * + * If the all given data is encoded as STREAM frame in |dest|, and if + * |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, fin + * flag is set to outgoing STREAM frame. Otherwise, fin flag in + * STREAM frame is not set. + * + * This packet may contain frames other than STREAM frame. The packet + * might not contain STREAM frame if other frames occupy the packet. + * In that case, |*pdatalen| would be -1 if |pdatalen| is not + * ``NULL``. + * + * If |flags| & :macro:`NGTCP2_WRITE_STREAM_FLAG_FIN` is nonzero, and + * 0 length STREAM frame is successfully serialized, |*pdatalen| would + * be 0. + * + * The number of data encoded in STREAM frame is stored in |*pdatalen| + * if it is not ``NULL``. The caller must keep the portion of data + * covered by |*pdatalen| bytes in tact until + * :type:`ngtcp2_acked_stream_data_offset` indicates that they are + * acknowledged by a remote endpoint or the stream is closed. + * + * If |flags| equals to :macro:`NGTCP2_WRITE_STREAM_FLAG_NONE`, this + * function produces a single payload of UDP packet. If the given + * stream data is small (e.g., few bytes), the packet might be + * severely under filled. Too many small packet might increase + * overall packet processing costs. Unless there are retransmissions, + * by default, application can only send 1 STREAM frame in one QUIC + * packet. In order to include more than 1 STREAM frame in one QUIC + * packet, specify :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` in |flags|. + * This is analogous to ``MSG_MORE`` flag in ``send(2)``. If the + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is used, there are 4 + * outcomes: + * + * - The function returns the written length of packet just like + * without :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`. This is because + * packet is nearly full and the library decided to make a complete + * packet. |*pdatalen| might be -1 or >= 0. + * + * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`. In this + * case, |*pdatalen| >= 0 is asserted. This indicates that + * application can call this function with different stream data (or + * `ngtcp2_conn_writev_datagram` if it has data to send in + * unreliable datagram) to pack them into the same packet. + * Application has to specify the same |conn|, |path|, |pi|, |dest|, + * |destlen|, and |ts| parameters, otherwise the behaviour is + * undefined. The application can change |flags|. + * + * - The function returns :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` which + * indicates that stream is blocked because of flow control. + * + * - The other error might be returned just like without + * :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE`. + * + * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not + * call other ngtcp2 API functions (application can still call + * `ngtcp2_conn_write_connection_close` or + * `ngtcp2_conn_write_application_close` to handle error from this + * function). Just keep calling `ngtcp2_conn_writev_stream`, + * `ngtcp2_conn_write_pkt`, or `ngtcp2_conn_writev_datagram` until it + * returns a positive number (which indicates a complete packet is + * ready). + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + * :macro:`NGTCP2_ERR_STREAM_SHUT_WR` + * Stream is half closed (local); or stream is being reset. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + * :macro:`NGTCP2_ERR_STREAM_DATA_BLOCKED` + * Stream is blocked because of flow control. + * :macro:`NGTCP2_ERR_WRITE_MORE` + * (Only when :macro:`NGTCP2_WRITE_STREAM_FLAG_MORE` is specified) + * Application can call this function to pack more stream data + * into the same packet. See above to know how it works. + * + * In general, if the error code which satisfies + * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned, + * the application should just close the connection by calling + * `ngtcp2_conn_write_connection_close` or just delete the QUIC + * connection using `ngtcp2_conn_del`. It is undefined to call the + * other library functions. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_stream( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, uint32_t flags, int64_t stream_id, + const ngtcp2_vec *datav, size_t datavcnt, ngtcp2_tstamp ts); + +/** + * @macrosection + * + * Write datagram flags + */ + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_NONE` indicates no flag set. + */ +#define NGTCP2_WRITE_DATAGRAM_FLAG_NONE 0x00 + +/** + * @macro + * + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` indicates that more data + * may come and should be coalesced into the same packet if possible. + */ +#define NGTCP2_WRITE_DATAGRAM_FLAG_MORE 0x01 + +/** + * @function + * + * `ngtcp2_conn_writev_datagram` writes a packet containing unreliable + * data in DATAGRAM frame. The buffer of the packet is pointed by + * |dest| of length |destlen|. This function performs QUIC handshake + * as well. + * + * For |path| and |pi| parameters, refer to + * `ngtcp2_conn_writev_stream`. + * + * If the given data is written to the buffer, nonzero value is + * assigned to |*paccepted| if it is not NULL. The data in DATAGRAM + * frame cannot be fragmented; writing partial data is not possible. + * + * This function might write other frames other than DATAGRAM, just + * like `ngtcp2_conn_writev_stream`. + * + * If the function returns 0, it means that no more data cannot be + * sent because of congestion control limit; or, data does not fit + * into the provided buffer; or, a local endpoint, as a server, is + * unable to send data because of its amplification limit. In this + * case, |*paccepted| is assigned zero if it is not NULL. + * + * If :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is set in |flags|, + * there are 3 outcomes: + * + * - The function returns the written length of packet just like + * without :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`. This is + * because packet is nearly full and the library decided to make a + * complete packet. |*paccepted| might be zero or nonzero. + * + * - The function returns :macro:`NGTCP2_ERR_WRITE_MORE`. In this + * case, |*paccepted| != 0 is asserted. This indicates that + * application can call this function with another unreliable data + * (or `ngtcp2_conn_writev_stream` if it has stream data to send) to + * pack them into the same packet. Application has to specify the + * same |conn|, |path|, |pi|, |dest|, |destlen|, and |ts| + * parameters, otherwise the behaviour is undefined. The + * application can change |flags|. + * + * - The other error might be returned just like without + * :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE`. + * + * When application sees :macro:`NGTCP2_ERR_WRITE_MORE`, it must not + * call other ngtcp2 API functions (application can still call + * `ngtcp2_conn_write_connection_close` or + * `ngtcp2_conn_write_application_close` to handle error from this + * function). Just keep calling `ngtcp2_conn_writev_datagram`, + * `ngtcp2_conn_writev_stream` or `ngtcp2_conn_write_pkt` until it + * returns a positive number (which indicates a complete packet is + * ready). + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + * :macro:`NGTCP2_ERR_WRITE_MORE` + * (Only when :macro:`NGTCP2_WRITE_DATAGRAM_FLAG_MORE` is + * specified) Application can call this function to pack more data + * into the same packet. See above to know how it works. + * :macro:`NGTCP2_ERR_INVALID_STATE` + * A remote endpoint did not express the DATAGRAM frame support. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * The provisional DATAGRAM frame size exceeds the maximum + * DATAGRAM frame size that a remote endpoint can receive. + * + * In general, if the error code which satisfies + * `ngtcp2_err_is_fatal(err) <ngtcp2_err_is_fatal>` != 0 is returned, + * the application should just close the connection by calling + * `ngtcp2_conn_write_connection_close` or just delete the QUIC + * connection using `ngtcp2_conn_del`. It is undefined to call the + * other library functions. + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_writev_datagram( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, int *paccepted, uint32_t flags, const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_connection_close` writes a packet which contains + * a CONNECTION_CLOSE frame (type 0x1c) in the buffer pointed by + * |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_connection_close( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t error_code, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_write_application_close` writes a packet which + * contains a CONNECTION_CLOSE frame (type 0x1d) in the buffer pointed + * by |dest| whose capacity is |datalen|. + * + * If |path| is not ``NULL``, this function stores the network path + * with which the packet should be sent. Each addr field must point + * to the buffer which should be at least ``sizeof(struct + * sockaddr_storage)`` bytes long. The assignment might not be done + * if nothing is written to |dest|. + * + * If |pi| is not ``NULL``, this function stores packet metadata in it + * if it succeeds. The metadata includes ECN markings. + * + * If handshake has not been confirmed yet, CONNECTION_CLOSE (type + * 0x1c) with error code :macro:`NGTCP2_APPLICATION_ERROR` is written + * instead. + * + * This function must not be called from inside the callback + * functions. + * + * At the moment, successful call to this function makes connection + * close. We may change this behaviour in the future to allow + * graceful shutdown. + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + * :macro:`NGTCP2_ERR_NOBUF` + * Buffer is too small + * :macro:`NGTCP2_ERR_INVALID_STATE` + * The current state does not allow sending CONNECTION_CLOSE. + * :macro:`NGTCP2_ERR_PKT_NUM_EXHAUSTED` + * Packet number is exhausted, and cannot send any more packet. + * :macro:`NGTCP2_ERR_CALLBACK_FAILURE` + * User callback failed + */ +NGTCP2_EXTERN ngtcp2_ssize ngtcp2_conn_write_application_close( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_is_in_closing_period` returns nonzero if |conn| is in + * closing period. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_is_in_draining_period` returns nonzero if |conn| is in + * draining period. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_extend_max_stream_offset` extends stream's max stream + * data value by |datalen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream was not found + */ +NGTCP2_EXTERN int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, + int64_t stream_id, + uint64_t datalen); + +/** + * @function + * + * `ngtcp2_conn_extend_max_offset` extends max data offset by + * |datalen|. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, + uint64_t datalen); + +/** + * @function + * + * `ngtcp2_conn_extend_max_streams_bidi` extends the number of maximum + * local bidirectional streams that a remote endpoint can open by |n|. + * + * The library does not increase maximum stream limit automatically. + * The exception is when a stream is closed without + * :type:`ngtcp2_stream_open` callback being called. In this case, + * stream limit is increased automatically. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, + size_t n); + +/** + * @function + * + * `ngtcp2_conn_extend_max_streams_uni` extends the number of maximum + * local unidirectional streams that a remote endpoint can open by + * |n|. + * + * The library does not increase maximum stream limit automatically. + * The exception is when a stream is closed without + * :type:`ngtcp2_stream_open` callback being called. In this case, + * stream limit is increased automatically. + */ +NGTCP2_EXTERN void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, + size_t n); + +/** + * @function + * + * `ngtcp2_conn_get_dcid` returns the non-NULL pointer to destination + * connection ID. If no destination connection ID is present, the + * return value is not ``NULL``, and its datalen field is 0. + */ +NGTCP2_EXTERN const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_num_scid` returns the number of source connection + * IDs which the local endpoint has provided to the peer and have not + * retired. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_scid` writes the all source connection IDs which + * the local endpoint has provided to the peer and have not retired in + * |dest|. The buffer pointed by |dest| must have + * ``sizeof(ngtcp2_cid) * n`` bytes available, where n is the return + * value of `ngtcp2_conn_get_num_scid()`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest); + +/** + * @function + * + * `ngtcp2_conn_get_num_active_dcid` returns the number of the active + * destination connection ID. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn); + +/** + * @struct + * + * :type:`ngtcp2_cid_token` is the convenient struct to store + * Connection ID, its associated path, and stateless reset token. + */ +typedef struct ngtcp2_cid_token { + /** + * :member:`seq` is the sequence number of this Connection ID. + */ + uint64_t seq; + /** + * :member:`cid` is Connection ID. + */ + ngtcp2_cid cid; + /** + * :member:`ps` is the path which is associated to this Connection + * ID. + */ + ngtcp2_path_storage ps; + /** + * :member:`token` is the stateless reset token for this Connection + * ID. + */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + /** + * :member:`token_present` is nonzero if token contains stateless + * reset token. + */ + uint8_t token_present; +} ngtcp2_cid_token; + +/** + * @function + * + * `ngtcp2_conn_get_active_dcid` writes the all active destination + * connection IDs and tokens to |dest|. The buffer pointed by |dest| + * must have ``sizeof(ngtcp2_cid_token) * n`` bytes available, where n + * is the return value of `ngtcp2_conn_get_num_active_dcid()`. + */ +NGTCP2_EXTERN size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, + ngtcp2_cid_token *dest); + +/** + * @function + * + * `ngtcp2_conn_get_negotiated_version` returns the negotiated version. + */ +NGTCP2_EXTERN uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_early_data_rejected` tells |conn| that 0-RTT data was + * rejected by a server. + */ +NGTCP2_EXTERN int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_conn_stat` assigns connection statistics data to + * |*cstat|. + */ +NGTCP2_EXTERN void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, + ngtcp2_conn_stat *cstat); + +/** + * @function + * + * `ngtcp2_conn_on_loss_detection_timer` should be called when a timer + * returned from `ngtcp2_conn_earliest_expiry` fires. + * + * Application should call `ngtcp2_conn_handshake` if handshake has + * not completed, otherwise `ngtcp2_conn_write_pkt` (or + * `ngtcp2_conn_write_stream` if it has data to send) to send PTO + * probe packets. + * + * This function must not be called from inside the callback + * functions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_submit_crypto_data` submits crypto stream data |data| + * of length |datalen| to the library for transmission. The + * encryption level is given in |crypto_level|. + * + * Application should keep the buffer pointed by |data| alive until + * the data is acknowledged. The acknowledgement is notified by + * :type:`ngtcp2_acked_crypto_offset` callback. + */ +NGTCP2_EXTERN int +ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, const size_t datalen); + +/** + * @function + * + * `ngtcp2_conn_submit_new_token` submits address validation token. + * It is sent in NEW_TOKEN frame. Only server can call this function. + * |tokenlen| must not be 0. + * + * This function makes a copy of the buffer pointed by |token| of + * length |tokenlen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory. + */ +NGTCP2_EXTERN int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, + const uint8_t *token, + size_t tokenlen); + +/** + * @function + * + * `ngtcp2_conn_set_local_addr` sets local endpoint address |addr| to + * |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, + const ngtcp2_addr *addr); + +/** + * @function + * + * `ngtcp2_conn_set_remote_addr` sets remote endpoint address |addr| + * to |conn|. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, + const ngtcp2_addr *addr); + +/** + * @function + * + * `ngtcp2_conn_get_path` returns the current path. + */ +NGTCP2_EXTERN const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_initiate_migration` starts connection migration to the + * given |path| which must not be ``NULL``. Only client can initiate + * migration. This function does immediate migration; it does not + * probe peer reachability from a new local address. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_INVALID_STATE` + * Migration is disabled. + * :macro:`NGTCP2_ERR_CONN_ID_BLOCKED` + * No unused connection ID is available. + * :macro:`NGTCP2_ERR_INVALID_ARGUMENT` + * |path| equals the current path. + * :macro:`NGTCP2_ERR_NOMEM` + * Out of memory + */ +NGTCP2_EXTERN int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, + const ngtcp2_path *path, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_get_max_local_streams_uni` returns the cumulative + * number of streams which local endpoint can open. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_max_data_left` returns the number of bytes that + * this local endpoint can send in this connection. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_streams_bidi_left` returns the number of + * bidirectional streams which the local endpoint can open without + * violating stream concurrency limit. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_get_streams_uni_left` returns the number of + * unidirectional streams which the local endpoint can open without + * violating stream concurrency limit. + */ +NGTCP2_EXTERN uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_initial_crypto_ctx` sets |ctx| for Initial packet + * encryption. The passed data will be passed to + * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and + * :type:`ngtcp2_hp_mask` callbacks. + */ +NGTCP2_EXTERN void +ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_get_initial_crypto_ctx` returns + * :type:`ngtcp2_crypto_ctx` object for Initial packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_crypto_ctx` sets |ctx| for Handshake/Short packet + * encryption. The passed data will be passed to + * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and + * :type:`ngtcp2_hp_mask` callbacks. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_get_tls_native_handle` returns TLS native handle set by + * `ngtcp2_conn_set_tls_native_handle()`. + */ +NGTCP2_EXTERN void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_tls_native_handle` sets TLS native handle + * |tls_native_handle| to |conn|. Internally, it is used as an opaque + * pointer. + */ +NGTCP2_EXTERN void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, + void *tls_native_handle); + +/** + * @function + * + * `ngtcp2_conn_set_retry_aead` sets |aead| and |aead_ctx| for Retry + * integrity tag verification. |aead| must be AEAD_AES_128_GCM. + * |aead_ctx| must be initialized with :macro:`NGTCP2_RETRY_KEY` as + * encryption key. This function must be called if |conn| is + * initialized as client. Server does not verify the tag and has no + * need to call this function. + * + * If this function succeeds, |conn| takes ownership of |aead_ctx|. + * :type:`ngtcp2_delete_crypto_aead_ctx` will be called to delete this + * object when it is no longer used. If this function fails, the + * caller is responsible to delete it. + */ +NGTCP2_EXTERN void +ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @function + * + * `ngtcp2_conn_get_crypto_ctx` returns :type:`ngtcp2_crypto_ctx` + * object for Handshake/Short packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_early_crypto_ctx` sets |ctx| for 0RTT packet + * encryption. The passed data will be passed to + * :type:`ngtcp2_encrypt`, :type:`ngtcp2_decrypt` and + * :type:`ngtcp2_hp_mask` callbacks. + */ +NGTCP2_EXTERN void +ngtcp2_conn_set_early_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx); + +/** + * @function + * + * `ngtcp2_conn_get_early_crypto_ctx` returns + * :type:`ngtcp2_crypto_ctx` object for 0RTT packet encryption. + */ +NGTCP2_EXTERN const ngtcp2_crypto_ctx * +ngtcp2_conn_get_early_crypto_ctx(ngtcp2_conn *conn); + +/** + * @enum + * + * :type:`ngtcp2_connection_close_error_code_type` defines connection + * error code type. + */ +typedef enum ngtcp2_connection_close_error_code_type { + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT` + * indicates the error code is QUIC transport error code. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT, + /** + * :enum:`NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION` + * indicates the error code is application error code. + */ + NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION, +} ngtcp2_connection_close_error_code_type; + +/** + * @struct + * + * `ngtcp2_connection_close_error_code` contains connection error code + * and its type. + */ +typedef struct ngtcp2_connection_close_error_code { + /** + * :member:`error_code` is the error code for connection closure. + */ + uint64_t error_code; + /** + * :member:`type` is the type of :member:`error_code`. + */ + ngtcp2_connection_close_error_code_type type; +} ngtcp2_connection_close_error_code; + +/** + * @function + * + * `ngtcp2_conn_get_connection_close_error_code` stores the received + * connection close error code in |ccec|. + */ +NGTCP2_EXTERN void ngtcp2_conn_get_connection_close_error_code( + ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec); + +/** + * @function + * + * `ngtcp2_conn_is_local_stream` returns nonzero if |stream_id| denotes the + * stream which a local endpoint issues. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, + int64_t stream_id); + +/** + * @function + * + * `ngtcp2_conn_is_server` returns nonzero if |conn| is initialized as + * server. + */ +NGTCP2_EXTERN int ngtcp2_conn_is_server(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_after_retry` returns nonzero if |conn| as a client has + * received Retry packet from server and successfully validated it. + */ +NGTCP2_EXTERN int ngtcp2_conn_after_retry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_set_stream_user_data` sets |stream_user_data| to the + * stream identified by |stream_id|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * :macro:`NGTCP2_ERR_STREAM_NOT_FOUND` + * Stream does not exist + */ +NGTCP2_EXTERN int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, + int64_t stream_id, + void *stream_user_data); + +/** + * @function + * + * `ngtcp2_strerror` returns the text representation of |liberr|. + * |liberr| must be one of ngtcp2 library error codes (which is + * defined as NGTCP2_ERR_* macro, such as + * :macro:`NGTCP2_ERR_TLS_DECRYPT`). + */ +NGTCP2_EXTERN const char *ngtcp2_strerror(int liberr); + +/** + * @function + * + * `ngtcp2_err_is_fatal` returns nonzero if |liberr| is a fatal error. + * |liberr| must be one of ngtcp2 library error codes (which is + * defined as NGTCP2_ERR_* macro, such as + * :macro:`NGTCP2_ERR_TLS_DECRYPT`). + */ +NGTCP2_EXTERN int ngtcp2_err_is_fatal(int liberr); + +/** + * @function + * + * `ngtcp2_err_infer_quic_transport_error_code` returns a QUIC + * transport error code which corresponds to |liberr|. |liberr| must + * be one of ngtcp2 library error codes (which is defined as + * NGTCP2_ERR_* macro, such as :macro:`NGTCP2_ERR_TLS_DECRYPT`). + */ +NGTCP2_EXTERN uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr); + +/** + * @function + * + * `ngtcp2_addr_init` initializes |dest| with the given arguments and + * returns |dest|. + */ +NGTCP2_EXTERN ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, + const struct sockaddr *addr, + size_t addrlen, void *user_data); + +/** + * @function + * + * `ngtcp2_path_storage_init` initializes |ps| with the given + * arguments. This function copies |local_addr| and |remote_addr|. + */ +NGTCP2_EXTERN void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, + const struct sockaddr *local_addr, + size_t local_addrlen, + void *local_user_data, + const struct sockaddr *remote_addr, + size_t remote_addrlen, + void *remote_user_data); + +/** + * @function + * + * `ngtcp2_path_storage_zero` initializes |ps| with the zero length + * addresses. + */ +NGTCP2_EXTERN void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps); + +/** + * @function + * + * `ngtcp2_settings_default` initializes |settings| with the default + * values. First this function fills |settings| with 0 and set the + * default value to the following fields: + * + * * :type:`cc_algo <ngtcp2_settings.cc_algo>` = + * :enum:`ngtcp2_cc_algo.NGTCP2_CC_ALGO_CUBIC` + * * :type:`initial_rtt <ngtcp2_settings.initial_rtt>` = + * :macro:`NGTCP2_DEFAULT_INITIAL_RTT` + * * :type:`ack_thresh <ngtcp2_settings.ack_thresh>` = 2 + */ +NGTCP2_EXTERN void ngtcp2_settings_default(ngtcp2_settings *settings); + +/** + * @function + * + * `ngtcp2_transport_params_default` initializes |params| with the + * default values. First this function fills |params| with 0 and set + * the default value to the following fields: + * + * * :type:`max_udp_payload_size + * <ngtcp2_transport_params.max_udp_payload_size>` = + * :macro:`NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE` + * * :type:`ack_delay_exponent + * <ngtcp2_transport_params.ack_delay_exponent>` = + * :macro:`NGTCP2_DEFAULT_ACK_DELAY_EXPONENT` + * * :type:`max_ack_delay <ngtcp2_transport_params.max_ack_delay>` = + * :macro:`NGTCP2_DEFAULT_MAX_ACK_DELAY` + * * :type:`active_connection_id_limit + * <ngtcp2_transport_params.active_connection_id_limit>` = + * :macro:`NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT` + */ +NGTCP2_EXTERN void +ngtcp2_transport_params_default(ngtcp2_transport_params *params); + +/** + * @function + * + * `ngtcp2_mem_default` returns the default, system standard memory + * allocator. + */ +NGTCP2_EXTERN const ngtcp2_mem *ngtcp2_mem_default(void); + +/** + * @macrosection + * + * ngtcp2_info macros + */ + +/** + * @macro + * + * :macro:`NGTCP2_VERSION_AGE` is the age of :type:`ngtcp2_info` + */ +#define NGTCP2_VERSION_AGE 1 + +/** + * @struct + * + * :type:`ngtcp2_info` is what `ngtcp2_version()` returns. It holds + * information about the particular ngtcp2 version. + */ +typedef struct ngtcp2_info { + /** + * :member:`age` is the age of this struct. This instance of ngtcp2 + * sets it to :macro:`NGTCP2_VERSION_AGE` but a future version may + * bump it and add more struct fields at the bottom + */ + int age; + /** + * :member:`version_num` is the :macro:`NGTCP2_VERSION_NUM` number + * (since age ==1) + */ + int version_num; + /** + * :member:`version_str` points to the :macro:`NGTCP2_VERSION` + * string (since age ==1) + */ + const char *version_str; + /* -------- the above fields all exist when age == 1 */ +} ngtcp2_info; + +/** + * @function + * + * Returns a pointer to a ngtcp2_info struct with version information + * about the run-time library in use. The |least_version| argument + * can be set to a 24 bit numerical value for the least accepted + * version number and if the condition is not met, this function will + * return a ``NULL``. Pass in 0 to skip the version checking. + */ +NGTCP2_EXTERN ngtcp2_info *ngtcp2_version(int least_version); + +/** + * @function + * + * `ngtcp2_is_bidi_stream` returns nonzero if |stream_id| denotes + * bidirectional stream. + */ +NGTCP2_EXTERN int ngtcp2_is_bidi_stream(int64_t stream_id); + +/** + * @enum + * + * :type:`ngtcp2_log_event` defines an event of ngtcp2 library + * internal logger. + */ +typedef enum { + /** + * :enum:`NGTCP2_LOG_EVENT_NONE` represents no event. + */ + NGTCP2_LOG_EVENT_NONE, + /** + * :enum:`NGTCP2_LOG_EVENT_CON` is a connection (catch-all) event + */ + NGTCP2_LOG_EVENT_CON, + /** + * :enum:`NGTCP2_LOG_EVENT_PKT` is a packet event. + */ + NGTCP2_LOG_EVENT_PKT, + /** + * :enum:`NGTCP2_LOG_EVENT_FRM` is a QUIC frame event. + */ + NGTCP2_LOG_EVENT_FRM, + /** + * :enum:`NGTCP2_LOG_EVENT_RCV` is a congestion and recovery event. + */ + NGTCP2_LOG_EVENT_RCV, + /** + * :enum:`NGTCP2_LOG_EVENT_CRY` is a crypto event. + */ + NGTCP2_LOG_EVENT_CRY, + /** + * :enum:`NGTCP2_LOG_EVENT_PTV` is a path validation event. + */ + NGTCP2_LOG_EVENT_PTV, +} ngtcp2_log_event; + +/** + * @function + * + * `ngtcp2_log_info` writes info level log. + */ +NGTCP2_EXTERN void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, + const char *fmt, ...); + +/** + * @function + * + * `ngtcp2_path_copy` copies |src| into |dest|. This function assumes + * that |dest| has enough buffer to store the deep copy of src->local + * and src->remote. + */ +NGTCP2_EXTERN void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src); + +/** + * @function + * + * `ngtcp2_path_eq` returns nonzero if |a| and |b| shares the same + * local and remote addresses. + */ +NGTCP2_EXTERN int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b); + +#ifdef __cplusplus +} +#endif + +#endif /* NGTCP2_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h new file mode 100644 index 00000000000000..fb0671de9fcb5d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h @@ -0,0 +1,51 @@ +/* + * ngtcp2 + * + * Copyright (c) 2016 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 VERSION_H +#define VERSION_H + +/** + * @macrosection + * + * Library version macros + */ + +/** + * @macro + * + * Version number of the ngtcp2 library release. + */ +#define NGTCP2_VERSION "0.1.0-DEV" + +/** + * @macro + * + * Numerical representation of the version number of the ngtcp2 + * library release. This is a 24 bit number with 8 bits for major + * number, 8 bits for minor and 8 bits for patch. Version 1.2.3 + * becomes 0x010203. + */ +#define NGTCP2_VERSION_NUM 0x000100 + +#endif /* VERSION_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c new file mode 100644 index 00000000000000..7a7f3e469a2f0e --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.c @@ -0,0 +1,318 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_acktr.h" + +#include <assert.h> + +int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, const ngtcp2_mem *mem) { + *ent = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_acktr_entry)); + if (*ent == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*ent)->pkt_num = pkt_num; + (*ent)->len = 1; + (*ent)->tstamp = tstamp; + + return 0; +} + +void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, ent); +} + +static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *(int64_t *)lhs > *(int64_t *)rhs; +} + +int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, + const ngtcp2_mem *mem) { + int rv; + + rv = ngtcp2_ringbuf_init(&acktr->acks, 128, sizeof(ngtcp2_acktr_ack_entry), + mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_ksl_init(&acktr->ents, greater, sizeof(int64_t), mem); + if (rv != 0) { + ngtcp2_ringbuf_free(&acktr->acks); + return rv; + } + + acktr->log = log; + acktr->mem = mem; + acktr->flags = NGTCP2_ACKTR_FLAG_NONE; + acktr->first_unacked_ts = UINT64_MAX; + acktr->rx_npkt = 0; + + return 0; +} + +void ngtcp2_acktr_free(ngtcp2_acktr *acktr) { + ngtcp2_ksl_it it; + + if (acktr == NULL) { + return; + } + + for (it = ngtcp2_ksl_begin(&acktr->ents); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_acktr_entry_del(ngtcp2_ksl_it_get(&it), acktr->mem); + } + ngtcp2_ksl_free(&acktr->ents); + + ngtcp2_ringbuf_free(&acktr->acks); +} + +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, + ngtcp2_tstamp ts) { + ngtcp2_ksl_it it; + ngtcp2_acktr_entry *ent, *prev_ent, *delent; + int rv; + int added = 0; + + if (ngtcp2_ksl_len(&acktr->ents)) { + it = ngtcp2_ksl_lower_bound(&acktr->ents, &pkt_num); + if (ngtcp2_ksl_it_end(&it)) { + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + assert(ent->pkt_num >= pkt_num + (int64_t)ent->len); + + if (ent->pkt_num == pkt_num + (int64_t)ent->len) { + ++ent->len; + added = 1; + } + } else { + ent = ngtcp2_ksl_it_get(&it); + + assert(ent->pkt_num != pkt_num); + + if (ngtcp2_ksl_it_begin(&it)) { + if (ent->pkt_num + 1 == pkt_num) { + ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); + ent->pkt_num = pkt_num; + ent->tstamp = ts; + ++ent->len; + added = 1; + } + } else { + ngtcp2_ksl_it_prev(&it); + prev_ent = ngtcp2_ksl_it_get(&it); + + assert(prev_ent->pkt_num >= pkt_num + (int64_t)prev_ent->len); + + if (ent->pkt_num + 1 == pkt_num) { + if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { + prev_ent->len += ent->len + 1; + ngtcp2_ksl_remove(&acktr->ents, NULL, &ent->pkt_num); + ngtcp2_acktr_entry_del(ent, acktr->mem); + added = 1; + } else { + ngtcp2_ksl_update_key(&acktr->ents, &ent->pkt_num, &pkt_num); + ent->pkt_num = pkt_num; + ent->tstamp = ts; + ++ent->len; + added = 1; + } + } else if (prev_ent->pkt_num == pkt_num + (int64_t)prev_ent->len) { + ++prev_ent->len; + added = 1; + } + } + } + } + + if (!added) { + rv = ngtcp2_acktr_entry_new(&ent, pkt_num, ts, acktr->mem); + if (rv != 0) { + return rv; + } + rv = ngtcp2_ksl_insert(&acktr->ents, NULL, &ent->pkt_num, ent); + if (rv != 0) { + ngtcp2_acktr_entry_del(ent, acktr->mem); + return rv; + } + } + + if (active_ack) { + acktr->flags |= NGTCP2_ACKTR_FLAG_ACTIVE_ACK; + if (acktr->first_unacked_ts == UINT64_MAX) { + acktr->first_unacked_ts = ts; + } + } + + if (ngtcp2_ksl_len(&acktr->ents) > NGTCP2_ACKTR_MAX_ENT) { + it = ngtcp2_ksl_end(&acktr->ents); + ngtcp2_ksl_it_prev(&it); + delent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&acktr->ents, NULL, &delent->pkt_num); + ngtcp2_acktr_entry_del(delent, acktr->mem); + } + + return 0; +} + +void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent) { + ngtcp2_ksl_it it; + + it = ngtcp2_ksl_lower_bound(&acktr->ents, &ent->pkt_num); + assert(*(int64_t *)ngtcp2_ksl_it_key(&it) == (int64_t)ent->pkt_num); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + ngtcp2_ksl_remove(&acktr->ents, &it, &ent->pkt_num); + ngtcp2_acktr_entry_del(ent, acktr->mem); + } +} + +ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr) { + return ngtcp2_ksl_begin(&acktr->ents); +} + +int ngtcp2_acktr_empty(ngtcp2_acktr *acktr) { + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&acktr->ents); + return ngtcp2_ksl_it_end(&it); +} + +ngtcp2_acktr_ack_entry *ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, + int64_t pkt_num, + int64_t largest_ack) { + ngtcp2_acktr_ack_entry *ent = ngtcp2_ringbuf_push_front(&acktr->acks); + + ent->largest_ack = largest_ack; + ent->pkt_num = pkt_num; + + return ent; +} + +/* + * acktr_remove removes |ent| from |acktr|. The iterator which points + * to the entry next to |ent| is assigned to |it|. + */ +static void acktr_remove(ngtcp2_acktr *acktr, ngtcp2_ksl_it *it, + ngtcp2_acktr_entry *ent) { + ngtcp2_ksl_remove(&acktr->ents, it, &ent->pkt_num); + ngtcp2_acktr_entry_del(ent, acktr->mem); +} + +static void acktr_on_ack(ngtcp2_acktr *acktr, ngtcp2_ringbuf *rb, + size_t ack_ent_offset) { + ngtcp2_acktr_ack_entry *ack_ent; + ngtcp2_acktr_entry *ent; + ngtcp2_ksl_it it; + + assert(ngtcp2_ringbuf_len(rb)); + + ack_ent = ngtcp2_ringbuf_get(rb, ack_ent_offset); + + /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ + it = ngtcp2_ksl_lower_bound(&acktr->ents, &ack_ent->largest_ack); + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + acktr_remove(acktr, &it, ent); + } + + if (ngtcp2_ksl_len(&acktr->ents)) { + assert(ngtcp2_ksl_it_end(&it)); + + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + if (ent->pkt_num > ack_ent->largest_ack && + ack_ent->largest_ack >= ent->pkt_num - (int64_t)(ent->len - 1)) { + ent->len = (size_t)(ent->pkt_num - ack_ent->largest_ack); + } + } + + ngtcp2_ringbuf_resize(rb, ack_ent_offset); +} + +void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr) { + ngtcp2_acktr_ack_entry *ent; + int64_t largest_ack = fr->largest_ack, min_ack; + size_t i, j; + ngtcp2_ringbuf *rb = &acktr->acks; + size_t nacks = ngtcp2_ringbuf_len(rb); + + /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ + for (j = 0; j < nacks; ++j) { + ent = ngtcp2_ringbuf_get(rb, j); + if (largest_ack >= ent->pkt_num) { + break; + } + } + if (j == nacks) { + return; + } + + min_ack = largest_ack - (int64_t)fr->first_ack_blklen; + + if (min_ack <= ent->pkt_num && ent->pkt_num <= largest_ack) { + acktr_on_ack(acktr, rb, j); + return; + } + + for (i = 0; i < fr->num_blks && j < nacks; ++i) { + largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2; + min_ack = largest_ack - (int64_t)fr->blks[i].blklen; + + for (;;) { + if (ent->pkt_num > largest_ack) { + ++j; + if (j == nacks) { + return; + } + ent = ngtcp2_ringbuf_get(rb, j); + continue; + } + if (ent->pkt_num < min_ack) { + break; + } + acktr_on_ack(acktr, rb, j); + return; + } + } +} + +void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr) { + acktr->flags &= (uint16_t) ~(NGTCP2_ACKTR_FLAG_ACTIVE_ACK | + NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK | + NGTCP2_ACKTR_FLAG_CANCEL_TIMER); + acktr->first_unacked_ts = UINT64_MAX; + acktr->rx_npkt = 0; +} + +int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, + ngtcp2_duration max_ack_delay, + ngtcp2_tstamp ts) { + return acktr->first_unacked_ts <= ts - max_ack_delay; +} + +void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr) { + acktr->flags |= NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h new file mode 100644 index 00000000000000..51e1588e3c4a8f --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_acktr.h @@ -0,0 +1,212 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_ACKTR_H +#define NGTCP2_ACKTR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_ringbuf.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pkt.h" + +/* NGTCP2_ACKTR_MAX_ENT is the maximum number of ngtcp2_acktr_entry + which ngtcp2_acktr stores. */ +#define NGTCP2_ACKTR_MAX_ENT 1024 + +typedef struct ngtcp2_log ngtcp2_log; + +/* + * ngtcp2_acktr_entry is a range of packets which need to be acked. + */ +typedef struct ngtcp2_acktr_entry { + /* pkt_num is the largest packet number to acknowledge in this + range. */ + int64_t pkt_num; + /* len is the consecutive packets started from pkt_num which + includes pkt_num itself counting in decreasing order. So pkt_num + = 987 and len = 2, this entry includes packet 987 and 986. */ + size_t len; + /* tstamp is the timestamp when a packet denoted by pkt_num is + received. */ + ngtcp2_tstamp tstamp; +} ngtcp2_acktr_entry; + +/* + * ngtcp2_acktr_entry_new allocates memory for ent, and initializes it + * with the given parameters. The pointer to the allocated object is + * stored to |*ent|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_acktr_entry_new(ngtcp2_acktr_entry **ent, int64_t pkt_num, + ngtcp2_tstamp tstamp, const ngtcp2_mem *mem); + +/* + * ngtcp2_acktr_entry_del deallocates memory allocated for |ent|. It + * deallocates memory pointed by |ent|. + */ +void ngtcp2_acktr_entry_del(ngtcp2_acktr_entry *ent, const ngtcp2_mem *mem); + +typedef struct ngtcp2_acktr_ack_entry { + /* largest_ack is the largest packet number in outgoing ACK frame */ + int64_t largest_ack; + /* pkt_num is the packet number that ACK frame is included. */ + int64_t pkt_num; +} ngtcp2_acktr_ack_entry; + +/* NGTCP2_ACKTR_FLAG_NONE indicates that no flag set. */ +#define NGTCP2_ACKTR_FLAG_NONE 0x00 +/* NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK indicates that immediate + acknowledgement is required. */ +#define NGTCP2_ACKTR_FLAG_IMMEDIATE_ACK 0x01 +/* NGTCP2_ACKTR_FLAG_ACTIVE_ACK indicates that there are pending + protected packet to be acknowledged. */ +#define NGTCP2_ACKTR_FLAG_ACTIVE_ACK 0x02 +/* NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK is set when server received + acknowledgement for ACK which acknowledges the last handshake + packet from client (which contains TLSv1.3 Finished message). */ +#define NGTCP2_ACKTR_FLAG_ACK_FINISHED_ACK 0x80 +/* NGTCP2_ACKTR_FLAG_CANCEL_TIMER is set when ACK delay timer is + expired and canceled. */ +#define NGTCP2_ACKTR_FLAG_CANCEL_TIMER 0x0100 + +/* + * ngtcp2_acktr tracks received packets which we have to send ack. + */ +typedef struct ngtcp2_acktr { + ngtcp2_ringbuf acks; + /* ents includes ngtcp2_acktr_entry sorted by decreasing order of + packet number. */ + ngtcp2_ksl ents; + ngtcp2_log *log; + const ngtcp2_mem *mem; + /* flags is bitwise OR of zero, or more of NGTCP2_ACKTR_FLAG_*. */ + uint16_t flags; + /* first_unacked_ts is timestamp when ngtcp2_acktr_entry is added + first time after the last outgoing ACK frame. */ + ngtcp2_tstamp first_unacked_ts; + /* rx_npkt is the number of packets received without sending ACK. */ + size_t rx_npkt; +} ngtcp2_acktr; + +/* + * ngtcp2_acktr_init initializes |acktr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_acktr_init(ngtcp2_acktr *acktr, ngtcp2_log *log, + const ngtcp2_mem *mem); + +/* + * ngtcp2_acktr_free frees resources allocated for |acktr|. It frees + * any ngtcp2_acktr_entry added to |acktr|. + */ +void ngtcp2_acktr_free(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_add adds packet number |pkt_num| to |acktr|. + * |active_ack| is nonzero if |pkt_num| is retransmittable packet. + * + * This function assumes that |acktr| does not contain |pkt_num|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * OUt of memory. + */ +int ngtcp2_acktr_add(ngtcp2_acktr *acktr, int64_t pkt_num, int active_ack, + ngtcp2_tstamp ts); + +/* + * ngtcp2_acktr_forget removes all entries which have the packet + * number that is equal to or less than ent->pkt_num. This function + * assumes that |acktr| includes |ent|. + */ +void ngtcp2_acktr_forget(ngtcp2_acktr *acktr, ngtcp2_acktr_entry *ent); + +/* + * ngtcp2_acktr_get returns the pointer 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. + */ +ngtcp2_ksl_it ngtcp2_acktr_get(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_empty returns nonzero if it has no packet to + * acknowledge. + */ +int ngtcp2_acktr_empty(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_add_ack records outgoing ACK frame whose largest + * acknowledged packet number is |largest_ack|. |pkt_num| is the + * packet number of a packet in which ACK frame is included. This + * function returns a pointer to the object it adds. + */ +ngtcp2_acktr_ack_entry * +ngtcp2_acktr_add_ack(ngtcp2_acktr *acktr, int64_t pkt_num, int64_t largest_ack); + +/* + * ngtcp2_acktr_recv_ack processes the incoming ACK frame |fr|. + * |pkt_num| is a packet number which includes |fr|. If we receive + * ACK which acknowledges the ACKs added by ngtcp2_acktr_add_ack, + * ngtcp2_acktr_entry which the outgoing ACK acknowledges is removed. + */ +void ngtcp2_acktr_recv_ack(ngtcp2_acktr *acktr, const ngtcp2_ack *fr); + +/* + * ngtcp2_acktr_commit_ack tells |acktr| that ACK frame is generated. + */ +void ngtcp2_acktr_commit_ack(ngtcp2_acktr *acktr); + +/* + * ngtcp2_acktr_require_active_ack returns nonzero if ACK frame should + * be generated actively. + */ +int ngtcp2_acktr_require_active_ack(ngtcp2_acktr *acktr, + ngtcp2_duration max_ack_delay, + ngtcp2_tstamp ts); + +/* + * ngtcp2_acktr_immediate_ack tells |acktr| that immediate + * acknowledgement is required. + */ +void ngtcp2_acktr_immediate_ack(ngtcp2_acktr *acktr); + +#endif /* NGTCP2_ACKTR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c new file mode 100644 index 00000000000000..22af219a9e1d75 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.c @@ -0,0 +1,131 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_addr.h" + +#include <string.h> +#include <assert.h> + +#ifdef WIN32 +# include <winsock2.h> +# include <ws2tcpip.h> +#else +# include <sys/types.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <netinet/ip.h> +# include <netinet/tcp.h> +# include <arpa/inet.h> +#endif + +ngtcp2_addr *ngtcp2_addr_init(ngtcp2_addr *dest, const struct sockaddr *addr, + size_t addrlen, void *user_data) { + dest->addrlen = addrlen; + dest->addr = (struct sockaddr *)addr; + dest->user_data = user_data; + return dest; +} + +void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src) { + dest->addrlen = src->addrlen; + if (src->addrlen) { + memcpy(dest->addr, src->addr, src->addrlen); + } + dest->user_data = src->user_data; +} + +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, + size_t addrlen) { + dest->addrlen = addrlen; + if (addrlen) { + memcpy(dest->addr, addr, addrlen); + } +} + +static int sockaddr_eq(const struct sockaddr *a, const struct sockaddr *b) { + assert(a->sa_family == b->sa_family); + + switch (a->sa_family) { + case AF_INET: { + const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, + *bi = (const struct sockaddr_in *)(void *)b; + return ai->sin_port == bi->sin_port && + memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr)) == 0; + } + case AF_INET6: { + const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, + *bi = (const struct sockaddr_in6 *)(void *)b; + return ai->sin6_port == bi->sin6_port && + memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr)) == 0; + } + default: + assert(0); + abort(); + } +} + +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); +} + +uint32_t ngtcp2_addr_compare(const ngtcp2_addr *aa, const ngtcp2_addr *bb) { + uint32_t flags = NGTCP2_ADDR_COMPARE_FLAG_NONE; + const struct sockaddr *a = aa->addr; + const struct sockaddr *b = bb->addr; + + if (a->sa_family != b->sa_family) { + return NGTCP2_ADDR_COMPARE_FLAG_FAMILY; + } + + switch (a->sa_family) { + case AF_INET: { + const struct sockaddr_in *ai = (const struct sockaddr_in *)(void *)a, + *bi = (const struct sockaddr_in *)(void *)b; + if (memcmp(&ai->sin_addr, &bi->sin_addr, sizeof(ai->sin_addr))) { + flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; + } + if (ai->sin_port != bi->sin_port) { + flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT; + } + return flags; + } + case AF_INET6: { + const struct sockaddr_in6 *ai = (const struct sockaddr_in6 *)(void *)a, + *bi = (const struct sockaddr_in6 *)(void *)b; + if (memcmp(&ai->sin6_addr, &bi->sin6_addr, sizeof(ai->sin6_addr))) { + flags |= NGTCP2_ADDR_COMPARE_FLAG_ADDR; + } + if (ai->sin6_port != bi->sin6_port) { + flags |= NGTCP2_ADDR_COMPARE_FLAG_PORT; + } + return flags; + } + default: + assert(0); + abort(); + } +} + +int ngtcp2_addr_empty(const ngtcp2_addr *addr) { return addr->addrlen == 0; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h new file mode 100644 index 00000000000000..84c0c01ddbf2db --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_addr.h @@ -0,0 +1,78 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_ADDR_H +#define NGTCP2_ADDR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_addr_copy copies |src| to |dest|. This function assumes + * that dest->addr points to a buffer which have sufficient size to + * store the copy. + */ +void ngtcp2_addr_copy(ngtcp2_addr *dest, const ngtcp2_addr *src); + +/* + * ngtcp2_addr_copy_byte copies |addr| of length |addrlen| into the + * buffer pointed by dest->addr. dest->len is updated to have + * |addrlen|. This function assumes that dest->addr points to a + * buffer which have sufficient size to store the copy. + */ +void ngtcp2_addr_copy_byte(ngtcp2_addr *dest, const struct sockaddr *addr, + size_t addrlen); + +/* + * ngtcp2_addr_eq returns nonzero if |a| equals |b|. + */ +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 0x0 +/* NGTCP2_ADDR_COMPARE_FLAG_ADDR indicates IP addresses do not + match. */ +#define NGTCP2_ADDR_COMPARE_FLAG_ADDR 0x1 +/* NGTCP2_ADDR_COMPARE_FLAG_PORT indicates ports do not match. */ +#define NGTCP2_ADDR_COMPARE_FLAG_PORT 0x2 +/* NGTCP2_ADDR_COMPARE_FLAG_FAMILY indicates address families do not + match. */ +#define NGTCP2_ADDR_COMPARE_FLAG_FAMILY 0x4 + +/* + * ngtcp2_addr_compare compares address and port between |a| and |b|, + * and returns zero or more of NGTCP2_ADDR_COMPARE_FLAG_*. + */ +uint32_t ngtcp2_addr_compare(const ngtcp2_addr *a, const ngtcp2_addr *b); + +/* + * ngtcp2_addr_empty returns nonzero if |addr| has zero length + * address. + */ +int ngtcp2_addr_empty(const ngtcp2_addr *addr); + +#endif /* NGTCP2_ADDR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c new file mode 100644 index 00000000000000..373f23d91aed7c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.c @@ -0,0 +1,44 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_buf.h" + +void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len) { + buf->begin = buf->pos = buf->last = begin; + buf->end = begin + len; +} + +void ngtcp2_buf_reset(ngtcp2_buf *buf) { buf->pos = buf->last = buf->begin; } + +size_t ngtcp2_buf_left(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->last); +} + +size_t ngtcp2_buf_len(const ngtcp2_buf *buf) { + return (size_t)(buf->last - buf->pos); +} + +size_t ngtcp2_buf_cap(const ngtcp2_buf *buf) { + return (size_t)(buf->end - buf->begin); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h new file mode 100644 index 00000000000000..8f2c36a1bbb218 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_buf.h @@ -0,0 +1,79 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_BUF_H +#define NGTCP2_BUF_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_buf { + /* begin points to the beginning of the buffer. */ + uint8_t *begin; + /* end points to the one beyond of the last byte of the buffer */ + uint8_t *end; + /* pos pointers to the start of data. Typically, this points to the + point that next data should be read. Initially, it points to + |begin|. */ + uint8_t *pos; + /* last points to the one beyond of the last data of the buffer. + Typically, new data is written at this point. Initially, it + points to |begin|. */ + uint8_t *last; +} ngtcp2_buf; + +/* + * ngtcp2_buf_init initializes |buf| with the given buffer. + */ +void ngtcp2_buf_init(ngtcp2_buf *buf, uint8_t *begin, size_t len); + +/* + * ngtcp2_buf_reset resets pos and last fields to match begin field to + * make ngtcp2_buf_len(buf) return 0. + */ +void ngtcp2_buf_reset(ngtcp2_buf *buf); + +/* + * ngtcp2_buf_left returns the number of additional bytes which can be + * written to the underlying buffer. In other words, it returns + * buf->end - buf->last. + */ +size_t ngtcp2_buf_left(const ngtcp2_buf *buf); + +/* + * ngtcp2_buf_len returns the number of bytes left to read. In other + * words, it returns buf->last - buf->pos. + */ +size_t ngtcp2_buf_len(const ngtcp2_buf *buf); + +/* + * ngtcp2_buf_cap returns the capacity of the buffer. In other words, + * it returns buf->end - buf->begin. + */ +size_t ngtcp2_buf_cap(const ngtcp2_buf *buf); + +#endif /* NGTCP2_BUF_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c new file mode 100644 index 00000000000000..f4670805c73261 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.c @@ -0,0 +1,536 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_cc.h" + +#include <assert.h> + +#if defined(_MSC_VER) +# include <intrin.h> +#endif + +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_rcvry.h" + +uint64_t ngtcp2_cc_compute_initcwnd(size_t max_udp_payload_size) { + uint64_t n = 2 * max_udp_payload_size; + n = ngtcp2_max(n, 14720); + return ngtcp2_min(10 * max_udp_payload_size, n); +} + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, + size_t pktlen, ngtcp2_pktns_id pktns_id, + ngtcp2_tstamp ts_sent) { + pkt->pkt_num = pkt_num; + pkt->pktlen = pktlen; + pkt->pktns_id = pktns_id; + pkt->ts_sent = ts_sent; + + return pkt; +} + +static void reno_cc_reset(ngtcp2_reno_cc *cc) { + cc->max_delivery_rate_sec = 0; + cc->target_cwnd = 0; + cc->pending_add = 0; +} + +void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log) { + cc->ccb.log = log; + reno_cc_reset(cc); +} + +void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc) { (void)cc; } + +int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + const ngtcp2_mem *mem) { + ngtcp2_reno_cc *reno_cc; + + reno_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_reno_cc)); + if (reno_cc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_reno_cc_init(reno_cc, log); + + cc->ccb = &reno_cc->ccb; + cc->on_pkt_acked = ngtcp2_cc_reno_cc_on_pkt_acked; + cc->congestion_event = ngtcp2_cc_reno_cc_congestion_event; + cc->on_persistent_congestion = ngtcp2_cc_reno_cc_on_persistent_congestion; + cc->on_ack_recv = ngtcp2_cc_reno_cc_on_ack_recv; + cc->reset = ngtcp2_cc_reno_cc_reset; + + return 0; +} + +void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { + ngtcp2_reno_cc *reno_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_reno_cc, ccb); + + ngtcp2_reno_cc_free(reno_cc); + ngtcp2_mem_free(mem, reno_cc); +} + +static int in_congestion_recovery(const ngtcp2_conn_stat *cstat, + ngtcp2_tstamp sent_time) { + return cstat->congestion_recovery_start_ts != UINT64_MAX && + sent_time <= cstat->congestion_recovery_start_ts; +} + +void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts) { + ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); + uint64_t m; + (void)ts; + + if (in_congestion_recovery(cstat, pkt->ts_sent)) { + return; + } + + if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) { + return; + } + + if (cstat->cwnd < cstat->ssthresh) { + cstat->cwnd += pkt->pktlen; + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64, + pkt->pkt_num, cstat->cwnd); + return; + } + + m = cstat->max_udp_payload_size * pkt->pktlen + cc->pending_add; + cc->pending_add = m % cstat->cwnd; + + cstat->cwnd += m / cstat->cwnd; +} + +void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts) { + ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); + uint64_t min_cwnd; + + if (in_congestion_recovery(cstat, ts_sent)) { + return; + } + + cstat->congestion_recovery_start_ts = ts; + cstat->cwnd >>= NGTCP2_LOSS_REDUCTION_FACTOR_BITS; + min_cwnd = 2 * cstat->max_udp_payload_size; + cstat->cwnd = ngtcp2_max(cstat->cwnd, min_cwnd); + cstat->ssthresh = cstat->cwnd; + + cc->pending_add = 0; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "reduce cwnd because of packet loss cwnd=%" PRIu64, + cstat->cwnd); +} + +void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + (void)ccx; + (void)ts; + + cstat->cwnd = 2 * cstat->max_udp_payload_size; + cstat->congestion_recovery_start_ts = UINT64_MAX; +} + +void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); + uint64_t target_cwnd, initcwnd; + (void)ts; + + /* TODO Use sliding window for min rtt measurement */ + /* TODO Use sliding window */ + cc->max_delivery_rate_sec = + ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec); + + if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) { + target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS; + initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_udp_payload_size); + cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64 + " min_rtt=%" PRIu64, + cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt); + } +} + +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *ccx) { + ngtcp2_reno_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_reno_cc, ccb); + reno_cc_reset(cc); +} + +static void cubic_cc_reset(ngtcp2_cubic_cc *cc) { + cc->max_delivery_rate_sec = 0; + cc->target_cwnd = 0; + cc->w_last_max = 0; + cc->w_tcp = 0; + cc->origin_point = 0; + cc->epoch_start = UINT64_MAX; + cc->k = 0; + + cc->rtt_sample_count = 0; + cc->current_round_min_rtt = UINT64_MAX; + cc->last_round_min_rtt = UINT64_MAX; + cc->window_end = -1; +} + +void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log) { + cc->ccb.log = log; + cubic_cc_reset(cc); +} + +void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc) { (void)cc; } + +int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + const ngtcp2_mem *mem) { + ngtcp2_cubic_cc *cubic_cc; + + cubic_cc = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_cubic_cc)); + if (cubic_cc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_cubic_cc_init(cubic_cc, log); + + cc->ccb = &cubic_cc->ccb; + cc->on_pkt_acked = ngtcp2_cc_cubic_cc_on_pkt_acked; + cc->congestion_event = ngtcp2_cc_cubic_cc_congestion_event; + cc->on_persistent_congestion = ngtcp2_cc_cubic_cc_on_persistent_congestion; + cc->on_ack_recv = ngtcp2_cc_cubic_cc_on_ack_recv; + cc->on_pkt_sent = ngtcp2_cc_cubic_cc_on_pkt_sent; + cc->new_rtt_sample = ngtcp2_cc_cubic_cc_new_rtt_sample; + cc->reset = ngtcp2_cc_cubic_cc_reset; + cc->event = ngtcp2_cc_cubic_cc_event; + + return 0; +} + +void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem) { + ngtcp2_cubic_cc *cubic_cc = ngtcp2_struct_of(cc->ccb, ngtcp2_cubic_cc, ccb); + + ngtcp2_cubic_cc_free(cubic_cc); + ngtcp2_mem_free(mem, cubic_cc); +} + +static uint64_t ngtcp2_cbrt(uint64_t n) { + int d; + uint64_t a; + + if (n == 0) { + return 0; + } + +#if defined(_MSC_VER) +# if defined(_M_X64) + d = (int)__lzcnt64(n); +# elif defined(_M_ARM64) + { + unsigned long index; + d = sizeof(uint64_t) * CHAR_BIT; + if (_BitScanReverse64(&index, n)) { + d = d - 1 - index; + } + } +# else + if ((n >> 32) != 0) { + d = __lzcnt((unsigned int)(n >> 32)); + } else { + d = 32 + __lzcnt((unsigned int)n); + } +# endif +#else + d = __builtin_clzll(n); +#endif + a = 1ULL << ((64 - d) / 3 + 1); + + for (; a * a * a > n;) { + a = (2 * a + n / a / a) / 3; + } + return a; +} + +/* HyStart++ constants */ +#define NGTCP2_HS_MIN_SSTHRESH 16 +#define NGTCP2_HS_N_RTT_SAMPLE 8 +#define NGTCP2_HS_MIN_ETA (4 * NGTCP2_MILLISECONDS) +#define NGTCP2_HS_MAX_ETA (16 * NGTCP2_MILLISECONDS) + +void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + ngtcp2_duration t, min_rtt, eta; + uint64_t target; + uint64_t tx, kx, time_delta, delta; + uint64_t add, tcp_add; + uint64_t m; + + if (pkt->pktns_id == NGTCP2_PKTNS_ID_APPLICATION && cc->window_end != -1 && + cc->window_end <= pkt->pkt_num) { + cc->window_end = -1; + } + + if (in_congestion_recovery(cstat, pkt->ts_sent)) { + return; + } + + if (cc->target_cwnd && cc->target_cwnd < cstat->cwnd) { + return; + } + + if (cstat->cwnd < cstat->ssthresh) { + /* slow-start */ + cstat->cwnd += pkt->pktlen; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " acked, slow start cwnd=%" PRIu64, + pkt->pkt_num, cstat->cwnd); + + if (cc->last_round_min_rtt != UINT64_MAX && + cc->current_round_min_rtt != UINT64_MAX && + cstat->cwnd >= NGTCP2_HS_MIN_SSTHRESH * cstat->max_udp_payload_size && + cc->rtt_sample_count >= NGTCP2_HS_N_RTT_SAMPLE) { + eta = cc->last_round_min_rtt / 8; + + if (eta < NGTCP2_HS_MIN_ETA) { + eta = NGTCP2_HS_MIN_ETA; + } else if (eta > NGTCP2_HS_MAX_ETA) { + eta = NGTCP2_HS_MAX_ETA; + } + + if (cc->current_round_min_rtt >= cc->last_round_min_rtt + eta) { + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "HyStart++ exit slow start"); + + cc->w_last_max = cstat->cwnd; + cstat->ssthresh = cstat->cwnd; + } + } + + return; + } + + /* congestion avoidance */ + + if (cc->epoch_start == UINT64_MAX) { + cc->epoch_start = ts; + if (cstat->cwnd < cc->w_last_max) { + cc->k = ngtcp2_cbrt((cc->w_last_max - cstat->cwnd) * 10 / 4 / + cstat->max_udp_payload_size); + cc->origin_point = cc->w_last_max; + } else { + cc->k = 0; + cc->origin_point = cstat->cwnd; + } + + cc->w_tcp = cstat->cwnd; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "cubic-ca epoch_start=%" PRIu64 " k=%" PRIu64 + " origin_point=%" PRIu64, + cc->epoch_start, cc->k, cc->origin_point); + + cc->pending_add = 0; + cc->pending_w_add = 0; + } + + min_rtt = cstat->min_rtt == UINT64_MAX ? cstat->initial_rtt : cstat->min_rtt; + + t = ts + min_rtt - cc->epoch_start; + + tx = (t << 4) / NGTCP2_SECONDS; + kx = (cc->k << 4); + + if (tx > kx) { + time_delta = tx - kx; + } else { + time_delta = kx - tx; + } + + delta = cstat->max_udp_payload_size * + ((((time_delta * time_delta) >> 4) * time_delta) >> 8) * 4 / 10; + + if (tx > kx) { + target = cc->origin_point + delta; + } else { + target = cc->origin_point - delta; + } + + if (target > cstat->cwnd) { + m = cc->pending_add + cstat->max_udp_payload_size * (target - cstat->cwnd); + add = m / cstat->cwnd; + cc->pending_add = m % cstat->cwnd; + } else { + m = cc->pending_add + cstat->max_udp_payload_size; + add = m / (100 * cstat->cwnd); + cc->pending_add = m % (100 * cstat->cwnd); + } + + m = cc->pending_w_add + cstat->max_udp_payload_size * pkt->pktlen; + + cc->w_tcp += m / cstat->cwnd; + cc->pending_w_add = m % cstat->cwnd; + + if (cc->w_tcp > cstat->cwnd) { + tcp_add = + cstat->max_udp_payload_size * (cc->w_tcp - cstat->cwnd) / cstat->cwnd; + if (tcp_add > add) { + add = tcp_add; + } + } + + cstat->cwnd += add; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " acked, cubic-ca cwnd=%" PRIu64 " t=%" PRIu64 + " k=%" PRIi64 " time_delta=%" PRIu64 " delta=%" PRIu64 + " target=%" PRIu64 " w_tcp=%" PRIu64, + pkt->pkt_num, cstat->cwnd, t, cc->k, time_delta >> 4, delta, + target, cc->w_tcp); +} + +void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + uint64_t min_cwnd; + + if (in_congestion_recovery(cstat, ts_sent)) { + return; + } + + cstat->congestion_recovery_start_ts = ts; + + cc->epoch_start = UINT64_MAX; + if (cstat->cwnd < cc->w_last_max) { + cc->w_last_max = cstat->cwnd * 17 / 10 / 2; + } else { + cc->w_last_max = cstat->cwnd; + } + + min_cwnd = 2 * cstat->max_udp_payload_size; + cstat->ssthresh = cstat->cwnd * 7 / 10; + cstat->ssthresh = ngtcp2_max(cstat->ssthresh, min_cwnd); + cstat->cwnd = cstat->ssthresh; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "reduce cwnd because of packet loss cwnd=%" PRIu64, + cstat->cwnd); +} + +void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *ccx, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + (void)ccx; + (void)ts; + + cstat->cwnd = 2 * cstat->max_udp_payload_size; + cstat->congestion_recovery_start_ts = UINT64_MAX; +} + +void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + uint64_t target_cwnd, initcwnd; + (void)ts; + + /* TODO Use sliding window for min rtt measurement */ + /* TODO Use sliding window */ + cc->max_delivery_rate_sec = + ngtcp2_max(cc->max_delivery_rate_sec, cstat->delivery_rate_sec); + + if (cstat->min_rtt != UINT64_MAX && cc->max_delivery_rate_sec) { + target_cwnd = cc->max_delivery_rate_sec * cstat->min_rtt / NGTCP2_SECONDS; + initcwnd = ngtcp2_cc_compute_initcwnd(cstat->max_udp_payload_size); + cc->target_cwnd = ngtcp2_max(initcwnd, target_cwnd) * 289 / 100; + + ngtcp2_log_info(cc->ccb.log, NGTCP2_LOG_EVENT_RCV, + "target_cwnd=%" PRIu64 " max_delivery_rate_sec=%" PRIu64 + " min_rtt=%" PRIu64, + cc->target_cwnd, cc->max_delivery_rate_sec, cstat->min_rtt); + } +} + +void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + (void)cstat; + + if (pkt->pktns_id != NGTCP2_PKTNS_ID_APPLICATION || cc->window_end != -1) { + return; + } + + cc->window_end = pkt->pkt_num; + cc->last_round_min_rtt = cc->current_round_min_rtt; + cc->current_round_min_rtt = UINT64_MAX; + cc->rtt_sample_count = 0; +} + +void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + (void)ts; + + if (cc->window_end == -1) { + return; + } + + cc->current_round_min_rtt = + ngtcp2_min(cc->current_round_min_rtt, cstat->latest_rtt); + ++cc->rtt_sample_count; +} + +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *ccx) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + cubic_cc_reset(cc); +} + +void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *ccx, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts) { + ngtcp2_cubic_cc *cc = ngtcp2_struct_of(ccx->ccb, ngtcp2_cubic_cc, ccb); + ngtcp2_tstamp last_ts; + + if (event != NGTCP2_CC_EVENT_TYPE_TX_START || cc->epoch_start == UINT64_MAX) { + return; + } + + last_ts = cstat->last_tx_pkt_ts[NGTCP2_PKTNS_ID_APPLICATION]; + if (last_ts == UINT64_MAX || last_ts <= cc->epoch_start) { + return; + } + + assert(ts >= last_ts); + + cc->epoch_start += ts - last_ts; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h new file mode 100644 index 00000000000000..e1ca7594802994 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cc.h @@ -0,0 +1,135 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_CC_H +#define NGTCP2_CC_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#define NGTCP2_LOSS_REDUCTION_FACTOR_BITS 1 +#define NGTCP2_PERSISTENT_CONGESTION_THRESHOLD 3 + +typedef struct ngtcp2_log ngtcp2_log; + +/* + * ngtcp2_cc_compute_initcwnd computes initial cwnd. + */ +uint64_t ngtcp2_cc_compute_initcwnd(size_t max_packet_size); + +ngtcp2_cc_pkt *ngtcp2_cc_pkt_init(ngtcp2_cc_pkt *pkt, int64_t pkt_num, + size_t pktlen, ngtcp2_pktns_id pktns_id, + ngtcp2_tstamp ts_sent); + +/* ngtcp2_reno_cc is the RENO congestion controller. */ +typedef struct ngtcp2_reno_cc { + ngtcp2_cc_base ccb; + uint64_t max_delivery_rate_sec; + uint64_t target_cwnd; + uint64_t pending_add; +} ngtcp2_reno_cc; + +int ngtcp2_cc_reno_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + const ngtcp2_mem *mem); + +void ngtcp2_cc_reno_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); + +void ngtcp2_reno_cc_init(ngtcp2_reno_cc *cc, ngtcp2_log *log); + +void ngtcp2_reno_cc_free(ngtcp2_reno_cc *cc); + +void ngtcp2_cc_reno_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_on_persistent_congestion(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_reno_cc_reset(ngtcp2_cc *cc); + +/* ngtcp2_cubic_cc is CUBIC congestion controller. */ +typedef struct ngtcp2_cubic_cc { + ngtcp2_cc_base ccb; + uint64_t max_delivery_rate_sec; + uint64_t target_cwnd; + uint64_t w_last_max; + uint64_t w_tcp; + uint64_t origin_point; + ngtcp2_tstamp epoch_start; + uint64_t k; + /* HyStart++ variables */ + size_t rtt_sample_count; + uint64_t current_round_min_rtt; + uint64_t last_round_min_rtt; + int64_t window_end; + uint64_t pending_add; + uint64_t pending_w_add; +} ngtcp2_cubic_cc; + +int ngtcp2_cc_cubic_cc_init(ngtcp2_cc *cc, ngtcp2_log *log, + const ngtcp2_mem *mem); + +void ngtcp2_cc_cubic_cc_free(ngtcp2_cc *cc, const ngtcp2_mem *mem); + +void ngtcp2_cubic_cc_init(ngtcp2_cubic_cc *cc, ngtcp2_log *log); + +void ngtcp2_cubic_cc_free(ngtcp2_cubic_cc *cc); + +void ngtcp2_cc_cubic_cc_on_pkt_acked(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt, + ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_congestion_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts_sent, + ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_on_persistent_congestion(ngtcp2_cc *cc, + ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_on_ack_recv(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_on_pkt_sent(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_cc_pkt *pkt); + +void ngtcp2_cc_cubic_cc_new_rtt_sample(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_tstamp ts); + +void ngtcp2_cc_cubic_cc_reset(ngtcp2_cc *cc); + +void ngtcp2_cc_cubic_cc_event(ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + ngtcp2_cc_event_type event, ngtcp2_tstamp ts); + +#endif /* NGTCP2_CC_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c new file mode 100644 index 00000000000000..cdf31f8f624768 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.c @@ -0,0 +1,121 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_cid.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_path.h" +#include "ngtcp2_str.h" + +void ngtcp2_cid_zero(ngtcp2_cid *cid) { cid->datalen = 0; } + +void ngtcp2_cid_init(ngtcp2_cid *cid, const uint8_t *data, size_t datalen) { + assert(datalen <= NGTCP2_MAX_CIDLEN); + + cid->datalen = datalen; + if (datalen) { + ngtcp2_cpymem(cid->data, data, datalen); + } +} + +int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other) { + return cid->datalen == other->datalen && + 0 == memcmp(cid->data, other->data, cid->datalen); +} + +int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs) { + int s = lhs->datalen < rhs->datalen; + size_t n = s ? lhs->datalen : rhs->datalen; + int c = memcmp(lhs->data, rhs->data, n); + + return c < 0 || (c == 0 && s); +} + +int ngtcp2_cid_empty(const ngtcp2_cid *cid) { return cid->datalen == 0; } + +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token) { + scid->pe.index = NGTCP2_PQ_BAD_INDEX; + scid->seq = seq; + scid->cid = *cid; + scid->ts_retired = UINT64_MAX; + scid->flags = NGTCP2_SCID_FLAG_NONE; + if (token) { + memcpy(scid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); + } else { + memset(scid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + } +} + +void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src) { + ngtcp2_scid_init(dest, src->seq, &src->cid, src->token); + dest->ts_retired = src->ts_retired; + dest->flags = src->flags; +} + +void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token) { + dcid->seq = seq; + dcid->cid = *cid; + if (token) { + memcpy(dcid->token, token, NGTCP2_STATELESS_RESET_TOKENLEN); + } else { + memset(dcid->token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + } + ngtcp2_path_storage_zero(&dcid->ps); + dcid->ts_retired = UINT64_MAX; + dcid->flags = NGTCP2_DCID_FLAG_NONE; + dcid->bytes_sent = 0; + dcid->bytes_recv = 0; +} + +void ngtcp2_dcid_copy(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { + ngtcp2_dcid_init(dest, src->seq, &src->cid, src->token); + ngtcp2_path_copy(&dest->ps.path, &src->ps.path); + dest->ts_retired = src->ts_retired; + dest->flags = src->flags; + dest->bytes_sent = src->bytes_sent; + dest->bytes_recv = src->bytes_recv; +} + +void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src) { + dest->seq = src->seq; + dest->cid = src->cid; + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, + const ngtcp2_cid *cid, const uint8_t *token) { + if (dcid->seq == seq) { + return ngtcp2_cid_eq(&dcid->cid, cid) && + memcmp(dcid->token, token, + NGTCP2_STATELESS_RESET_TOKENLEN) == 0 + ? 0 + : NGTCP2_ERR_PROTO; + } + + return !ngtcp2_cid_eq(&dcid->cid, cid) ? 0 : NGTCP2_ERR_PROTO; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h new file mode 100644 index 00000000000000..a356b432d4f548 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_cid.h @@ -0,0 +1,153 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_CID_H +#define NGTCP2_CID_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pq.h" +#include "ngtcp2_path.h" + +/* NGTCP2_SCID_FLAG_NONE indicats that no flag is set. */ +#define NGTCP2_SCID_FLAG_NONE 0x00 +/* NGTCP2_SCID_FLAG_USED indicates that a local endpoint observed that + a remote endpoint uses a particular Connection ID. */ +#define NGTCP2_SCID_FLAG_USED 0x01 +/* NGTCP2_SCID_FLAG_RETIRED indicates that a particular Connection ID + is retired. */ +#define NGTCP2_SCID_FLAG_RETIRED 0x02 + +typedef struct ngtcp2_scid { + ngtcp2_pq_entry pe; + /* seq is the sequence number associated to the CID. */ + uint64_t seq; + /* cid is a connection ID */ + ngtcp2_cid cid; + /* ts_retired is the timestamp when peer tells that this CID is + retired. */ + ngtcp2_tstamp ts_retired; + /* flags is the bitwise OR of zero or more of NGTCP2_SCID_FLAG_*. */ + uint8_t flags; + /* token is a stateless reset token associated to this CID. + Actually, the stateless reset token is tied to the connection, + not to the particular connection ID. */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_scid; + +/* NGTCP2_DCID_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_DCID_FLAG_NONE 0x00 +/* NGTCP2_DCID_FLAG_PATH_VALIDATED indicates that an associated path + has been validated. */ +#define NGTCP2_DCID_FLAG_PATH_VALIDATED 0x01 + +typedef struct ngtcp2_dcid { + /* seq is the sequence number associated to the CID. */ + uint64_t seq; + /* cid is a connection ID */ + ngtcp2_cid cid; + /* path is a path which cid is bound to. The addresses are zero + length if cid has not been bound to a particular path yet. */ + ngtcp2_path_storage ps; + /* ts_retired is the timestamp when peer tells that this CID is + retired. */ + ngtcp2_tstamp ts_retired; + /* bytes_sent is the number of bytes sent to an associated path. */ + uint64_t bytes_sent; + /* bytes_recv is the number of bytes received from an associated + path. */ + uint64_t bytes_recv; + /* flags is bitwise OR of zero or more of NGTCP2_DCID_FLAG_*. */ + uint8_t flags; + /* token is a stateless reset token associated to this CID. + Actually, the stateless reset token is tied to the connection, + not to the particular connection ID. */ + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_dcid; + +/* ngtcp2_cid_zero makes |cid| zero-length. */ +void ngtcp2_cid_zero(ngtcp2_cid *cid); + +/* + * ngtcp2_cid_eq returns nonzero if |cid| and |other| share the same + * connection ID. + */ +int ngtcp2_cid_eq(const ngtcp2_cid *cid, const ngtcp2_cid *other); + +/* + * ngtcp2_cid_less returns nonzero if |lhs| is lexicographical smaller + * than |rhs|. + */ +int ngtcp2_cid_less(const ngtcp2_cid *lhs, const ngtcp2_cid *rhs); + +/* + * ngtcp2_cid_empty returns nonzero if |cid| includes empty connection + * ID. + */ +int ngtcp2_cid_empty(const ngtcp2_cid *cid); + +/* + * ngtcp2_scid_init initializes |scid| with the given parameters. If + * |token| is NULL, the function fills scid->token it with 0. |token| + * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_scid_init(ngtcp2_scid *scid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token); + +/* + * ngtcp2_scid_copy copies |src| into |dest|. + */ +void ngtcp2_scid_copy(ngtcp2_scid *dest, const ngtcp2_scid *src); + +/* + * ngtcp2_dcid_init initializes |dcid| with the given parameters. If + * |token| is NULL, the function fills dcid->token it with 0. |token| + * must be NGTCP2_STATELESS_RESET_TOKENLEN bytes long. + */ +void ngtcp2_dcid_init(ngtcp2_dcid *dcid, uint64_t seq, const ngtcp2_cid *cid, + const uint8_t *token); + +/* + * ngtcp2_dcid_copy copies |src| into |dest|. + */ +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 path. + */ +void ngtcp2_dcid_copy_cid_token(ngtcp2_dcid *dest, const ngtcp2_dcid *src); + +/* + * ngtcp2_dcid_verify_uniqueness verifies uniqueness of (|seq|, |cid|, + * |token|) tuple against |dcid|. + */ +int ngtcp2_dcid_verify_uniqueness(ngtcp2_dcid *dcid, uint64_t seq, + const ngtcp2_cid *cid, const uint8_t *token); + +#endif /* NGTCP2_CID_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c new file mode 100644 index 00000000000000..c8b1d15db5a6f7 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.c @@ -0,0 +1,11236 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_conn.h" + +#include <string.h> +#include <assert.h> +#include <math.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_log.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_addr.h" +#include "ngtcp2_path.h" +#include "ngtcp2_rcvry.h" + +/* NGTCP2_FLOW_WINDOW_RTT_FACTOR is the factor of RTT when flow + control window auto-tuning is triggered. */ +#define NGTCP2_FLOW_WINDOW_RTT_FACTOR 2 +/* NGTCP2_FLOW_WINDOW_SCALING_FACTOR is the growth factor of flow + control window. */ +#define NGTCP2_FLOW_WINDOW_SCALING_FACTOR 2 + +/* + * conn_local_stream returns nonzero if |stream_id| indicates that it + * is the stream initiated by local endpoint. + */ +static int conn_local_stream(ngtcp2_conn *conn, int64_t stream_id) { + return (uint8_t)(stream_id & 1) == conn->server; +} + +/* + * bidi_stream returns nonzero if |stream_id| is a bidirectional + * stream ID. + */ +static int bidi_stream(int64_t stream_id) { return (stream_id & 0x2) == 0; } + +static int conn_call_recv_client_initial(ngtcp2_conn *conn, + const ngtcp2_cid *dcid) { + int rv; + + assert(conn->callbacks.recv_client_initial); + + rv = conn->callbacks.recv_client_initial(conn, dcid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_handshake_completed(ngtcp2_conn *conn) { + int rv; + + if (!conn->callbacks.handshake_completed) { + return 0; + } + + rv = conn->callbacks.handshake_completed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint32_t flags, uint64_t offset, + const uint8_t *data, size_t datalen) { + int rv; + + if (!conn->callbacks.recv_stream_data) { + return 0; + } + + rv = conn->callbacks.recv_stream_data(conn, flags, strm->stream_id, offset, + data, datalen, conn->user_data, + strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_recv_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen) { + int rv; + + assert(conn->callbacks.recv_crypto_data); + + rv = conn->callbacks.recv_crypto_data(conn, crypto_level, offset, data, + datalen, conn->user_data); + switch (rv) { + case 0: + case NGTCP2_ERR_CRYPTO: + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + case NGTCP2_ERR_PROTO: + case NGTCP2_ERR_CALLBACK_FAILURE: + return rv; + default: + return NGTCP2_ERR_CALLBACK_FAILURE; + } +} + +static int conn_call_stream_open(ngtcp2_conn *conn, ngtcp2_strm *strm) { + int rv; + + if (!conn->callbacks.stream_open) { + return 0; + } + + rv = conn->callbacks.stream_open(conn, strm->stream_id, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_stream_close(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + + if (!conn->callbacks.stream_close) { + return 0; + } + + rv = conn->callbacks.stream_close(conn, strm->stream_id, app_error_code, + conn->user_data, strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_stream_reset(ngtcp2_conn *conn, int64_t stream_id, + uint64_t final_size, uint64_t app_error_code, + void *stream_user_data) { + int rv; + + if (!conn->callbacks.stream_reset) { + return 0; + } + + rv = conn->callbacks.stream_reset(conn, stream_id, final_size, app_error_code, + conn->user_data, stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_local_streams_bidi(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_local_streams_bidi) { + return 0; + } + + rv = conn->callbacks.extend_max_local_streams_bidi(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_local_streams_uni(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_local_streams_uni) { + return 0; + } + + rv = conn->callbacks.extend_max_local_streams_uni(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen) { + int rv; + + assert(conn->callbacks.get_new_connection_id); + + rv = conn->callbacks.get_new_connection_id(conn, cid, token, cidlen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_remove_connection_id(ngtcp2_conn *conn, + const ngtcp2_cid *cid) { + int rv; + + if (!conn->callbacks.remove_connection_id) { + return 0; + } + + rv = conn->callbacks.remove_connection_id(conn, cid, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_path_validation(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_path_validation_result res) { + int rv; + + if (!conn->callbacks.path_validation) { + return 0; + } + + rv = conn->callbacks.path_validation(conn, path, res, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_select_preferred_addr(ngtcp2_conn *conn, + ngtcp2_addr *dest) { + int rv; + + if (!conn->callbacks.select_preferred_addr) { + return 0; + } + + assert(conn->remote.transport_params.preferred_address_present); + + rv = conn->callbacks.select_preferred_addr( + conn, dest, &conn->remote.transport_params.preferred_address, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_remote_streams_bidi(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_remote_streams_bidi) { + return 0; + } + + rv = conn->callbacks.extend_max_remote_streams_bidi(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_remote_streams_uni(ngtcp2_conn *conn, + uint64_t max_streams) { + int rv; + + if (!conn->callbacks.extend_max_remote_streams_uni) { + return 0; + } + + rv = conn->callbacks.extend_max_remote_streams_uni(conn, max_streams, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_extend_max_stream_data(ngtcp2_conn *conn, + ngtcp2_strm *strm, + int64_t stream_id, + uint64_t datalen) { + int rv; + + if (!conn->callbacks.extend_max_stream_data) { + return 0; + } + + rv = conn->callbacks.extend_max_stream_data( + conn, stream_id, datalen, conn->user_data, strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_dcid_status(ngtcp2_conn *conn, + ngtcp2_connection_id_status_type type, + const ngtcp2_dcid *dcid) { + int rv; + + if (!conn->callbacks.dcid_status) { + return 0; + } + + rv = conn->callbacks.dcid_status( + conn, (int)type, dcid->seq, &dcid->cid, + ngtcp2_check_invalid_stateless_reset_token(dcid->token) ? NULL + : dcid->token, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int conn_call_activate_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid) { + return conn_call_dcid_status(conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_ACTIVATE, + dcid); +} + +static int conn_call_deactivate_dcid(ngtcp2_conn *conn, + const ngtcp2_dcid *dcid) { + return conn_call_dcid_status( + conn, NGTCP2_CONNECTION_ID_STATUS_TYPE_DEACTIVATE, dcid); +} + +static void conn_call_delete_crypto_aead_ctx(ngtcp2_conn *conn, + ngtcp2_crypto_aead_ctx *aead_ctx) { + if (!aead_ctx->native_handle) { + return; + } + + assert(conn->callbacks.delete_crypto_aead_ctx); + + conn->callbacks.delete_crypto_aead_ctx(conn, aead_ctx, conn->user_data); +} + +static void +conn_call_delete_crypto_cipher_ctx(ngtcp2_conn *conn, + ngtcp2_crypto_cipher_ctx *cipher_ctx) { + if (!cipher_ctx->native_handle) { + return; + } + + assert(conn->callbacks.delete_crypto_cipher_ctx); + + conn->callbacks.delete_crypto_cipher_ctx(conn, cipher_ctx, conn->user_data); +} + +static int crypto_offset_less(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + return *(int64_t *)lhs < *(int64_t *)rhs; +} + +static int pktns_init(ngtcp2_pktns *pktns, ngtcp2_pktns_id pktns_id, + ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + int rv; + + memset(pktns, 0, sizeof(*pktns)); + + rv = ngtcp2_gaptr_init(&pktns->rx.pngap, mem); + if (rv != 0) { + return rv; + } + + pktns->tx.last_pkt_num = -1; + pktns->rx.max_pkt_num = -1; + pktns->rx.max_ack_eliciting_pkt_num = -1; + + rv = ngtcp2_acktr_init(&pktns->acktr, log, mem); + if (rv != 0) { + goto fail_acktr_init; + } + + rv = ngtcp2_strm_init(&pktns->crypto.strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, + NULL, mem); + if (rv != 0) { + goto fail_crypto_init; + } + + rv = ngtcp2_ksl_init(&pktns->crypto.tx.frq, crypto_offset_less, + sizeof(uint64_t), mem); + if (rv != 0) { + goto fail_tx_frq_init; + } + + ngtcp2_rtb_init(&pktns->rtb, pktns_id, &pktns->crypto.strm, rst, cc, log, + qlog, mem); + + return 0; + +fail_tx_frq_init: + ngtcp2_strm_free(&pktns->crypto.strm); +fail_crypto_init: + ngtcp2_acktr_free(&pktns->acktr); +fail_acktr_init: + ngtcp2_gaptr_free(&pktns->rx.pngap); + + return rv; +} + +static int pktns_new(ngtcp2_pktns **ppktns, ngtcp2_pktns_id pktns_id, + ngtcp2_rst *rst, ngtcp2_cc *cc, ngtcp2_log *log, + ngtcp2_qlog *qlog, const ngtcp2_mem *mem) { + int rv; + + *ppktns = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pktns)); + if (*ppktns == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = pktns_init(*ppktns, pktns_id, rst, cc, log, qlog, mem); + if (rv != 0) { + ngtcp2_mem_free(mem, *ppktns); + } + + return rv; +} + +static int cycle_less(const ngtcp2_pq_entry *lhs, const ngtcp2_pq_entry *rhs) { + ngtcp2_strm *ls = ngtcp2_struct_of(lhs, ngtcp2_strm, pe); + ngtcp2_strm *rs = ngtcp2_struct_of(rhs, ngtcp2_strm, pe); + + if (ls->cycle == rs->cycle) { + return ls->stream_id < rs->stream_id; + } + + return rs->cycle - ls->cycle <= 1; +} + +static void delete_buffed_pkts(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { + ngtcp2_pkt_chain *next; + + for (; pc;) { + next = pc->next; + ngtcp2_pkt_chain_del(pc, mem); + pc = next; + } +} + +static void pktns_free(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it it; + + delete_buffed_pkts(pktns->rx.buffed_pkts, mem); + + ngtcp2_frame_chain_list_del(pktns->tx.frq, mem); + + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, mem); + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, mem); + + for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + ngtcp2_frame_chain_del(frc, mem); + } + + ngtcp2_ksl_free(&pktns->crypto.tx.frq); + ngtcp2_rtb_free(&pktns->rtb); + ngtcp2_strm_free(&pktns->crypto.strm); + ngtcp2_acktr_free(&pktns->acktr); + ngtcp2_gaptr_free(&pktns->rx.pngap); +} + +static void pktns_del(ngtcp2_pktns *pktns, const ngtcp2_mem *mem) { + if (pktns == NULL) { + return; + } + + pktns_free(pktns, mem); + + ngtcp2_mem_free(mem, pktns); +} + +static void cc_del(ngtcp2_cc *cc, ngtcp2_cc_algo cc_algo, + const ngtcp2_mem *mem) { + switch (cc_algo) { + case NGTCP2_CC_ALGO_RENO: + ngtcp2_cc_reno_cc_free(cc, mem); + break; + case NGTCP2_CC_ALGO_CUBIC: + ngtcp2_cc_cubic_cc_free(cc, mem); + break; + default: + break; + } +} + +static int cid_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return ngtcp2_cid_less(lhs, rhs); +} + +static int ts_retired_less(const ngtcp2_pq_entry *lhs, + const ngtcp2_pq_entry *rhs) { + const ngtcp2_scid *a = ngtcp2_struct_of(lhs, ngtcp2_scid, pe); + const ngtcp2_scid *b = ngtcp2_struct_of(rhs, ngtcp2_scid, pe); + + return a->ts_retired < b->ts_retired; +} + +/* + * conn_reset_conn_stat_cc resets congestion state in |cstat|. + */ +static void conn_reset_conn_stat_cc(ngtcp2_conn *conn, + ngtcp2_conn_stat *cstat) { + cstat->latest_rtt = 0; + cstat->min_rtt = UINT64_MAX; + cstat->smoothed_rtt = conn->local.settings.initial_rtt; + cstat->rttvar = conn->local.settings.initial_rtt / 2; + cstat->first_rtt_sample_ts = UINT64_MAX; + cstat->pto_count = 0; + cstat->loss_detection_timer = UINT64_MAX; + cstat->cwnd = + ngtcp2_cc_compute_initcwnd(conn->local.settings.max_udp_payload_size); + cstat->ssthresh = UINT64_MAX; + cstat->congestion_recovery_start_ts = UINT64_MAX; + cstat->bytes_in_flight = 0; + cstat->delivery_rate_sec = 0; +} + +/* + * reset_conn_stat_recovery resets the fields related to the recovery + * function + */ +static void reset_conn_stat_recovery(ngtcp2_conn_stat *cstat) { + // Initializes them with UINT64_MAX. + memset(cstat->loss_time, 0xff, sizeof(cstat->loss_time)); + memset(cstat->last_tx_pkt_ts, 0xff, sizeof(cstat->last_tx_pkt_ts)); +} + +/* + * conn_reset_conn_stat resets |cstat|. The following fields are not + * reset: initial_rtt and max_udp_payload_size. + */ +static void conn_reset_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { + conn_reset_conn_stat_cc(conn, cstat); + reset_conn_stat_recovery(cstat); +} + +static void delete_scid(ngtcp2_ksl *scids, const ngtcp2_mem *mem) { + ngtcp2_ksl_it it; + + for (it = ngtcp2_ksl_begin(scids); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_mem_free(mem, ngtcp2_ksl_it_get(&it)); + } +} + +/* + * compute_pto computes PTO. + */ +static ngtcp2_duration compute_pto(ngtcp2_duration smoothed_rtt, + ngtcp2_duration rttvar, + ngtcp2_duration max_ack_delay) { + ngtcp2_duration var = ngtcp2_max(4 * rttvar, NGTCP2_GRANULARITY); + return smoothed_rtt + var + max_ack_delay; +} + +/* + * conn_compute_initial_pto computes PTO using the initial RTT. + */ +static ngtcp2_duration conn_compute_initial_pto(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + ngtcp2_duration initial_rtt = conn->local.settings.initial_rtt; + ngtcp2_duration max_ack_delay = + pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION + ? conn->remote.transport_params.max_ack_delay + : 0; + return compute_pto(initial_rtt, initial_rtt / 2, max_ack_delay); +} + +/* + * conn_compute_pto computes the current PTO. + */ +static ngtcp2_duration conn_compute_pto(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_duration max_ack_delay = + pktns->rtb.pktns_id == NGTCP2_PKTNS_ID_APPLICATION + ? conn->remote.transport_params.max_ack_delay + : 0; + return compute_pto(cstat->smoothed_rtt, cstat->rttvar, max_ack_delay); +} + +static void conn_handle_tx_ecn(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, + uint8_t *prtb_entry_flags, ngtcp2_pktns *pktns, + const ngtcp2_pkt_hd *hd, ngtcp2_tstamp ts) { + assert(pi); + + if (pi->ecn != NGTCP2_ECN_NOT_ECT) { + /* We have already made a transition of validation state and + deceided to send UDP datagram with ECN bit set. Coalesced QUIC + packets also bear ECN bits set. */ + if (pktns->tx.ecn.start_pkt_num == INT64_MAX) { + pktns->tx.ecn.start_pkt_num = hd->pkt_num; + } + + ++pktns->tx.ecn.validation_pkt_sent; + + if (prtb_entry_flags) { + *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN; + } + + ++pktns->tx.ecn.ect0; + + return; + } + + switch (conn->tx.ecn.state) { + case NGTCP2_ECN_STATE_TESTING: + if (conn->tx.ecn.validation_start_ts == UINT64_MAX) { + assert(0 == pktns->tx.ecn.validation_pkt_sent); + assert(0 == pktns->tx.ecn.validation_pkt_lost); + + conn->tx.ecn.validation_start_ts = ts; + } else if (ts - conn->tx.ecn.validation_start_ts >= + 3 * conn_compute_pto(conn, pktns)) { + conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN; + break; + } + + if (pktns->tx.ecn.start_pkt_num == INT64_MAX) { + pktns->tx.ecn.start_pkt_num = hd->pkt_num; + } + + ++pktns->tx.ecn.validation_pkt_sent; + + if (++conn->tx.ecn.dgram_sent == NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS) { + conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN; + } + /* fall through */ + case NGTCP2_ECN_STATE_CAPABLE: + /* pi is provided per UDP datagram. */ + assert(NGTCP2_ECN_NOT_ECT == pi->ecn); + + pi->ecn = NGTCP2_ECN_ECT_0; + + if (prtb_entry_flags) { + *prtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ECN; + } + + ++pktns->tx.ecn.ect0; + break; + case NGTCP2_ECN_STATE_UNKNOWN: + case NGTCP2_ECN_STATE_FAILED: + break; + default: + assert(0); + } +} + +static void conn_reset_ecn_validation_state(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *pktns = &conn->pktns; + + conn->tx.ecn.state = NGTCP2_ECN_STATE_TESTING; + conn->tx.ecn.validation_start_ts = UINT64_MAX; + conn->tx.ecn.dgram_sent = 0; + + if (in_pktns) { + in_pktns->tx.ecn.start_pkt_num = INT64_MAX; + in_pktns->tx.ecn.validation_pkt_sent = 0; + in_pktns->tx.ecn.validation_pkt_lost = 0; + } + + if (hs_pktns) { + hs_pktns->tx.ecn.start_pkt_num = INT64_MAX; + hs_pktns->tx.ecn.validation_pkt_sent = 0; + hs_pktns->tx.ecn.validation_pkt_lost = 0; + } + + pktns->tx.ecn.start_pkt_num = INT64_MAX; + pktns->tx.ecn.validation_pkt_sent = 0; + pktns->tx.ecn.validation_pkt_lost = 0; +} + +static int conn_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data, int server) { + int rv; + ngtcp2_scid *scident; + uint8_t *buf; + + assert(settings->max_window <= NGTCP2_MAX_VARINT); + assert(settings->max_stream_window <= NGTCP2_MAX_VARINT); + assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_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); + assert(params->initial_max_stream_data_uni <= NGTCP2_MAX_VARINT); + + if (mem == NULL) { + mem = ngtcp2_mem_default(); + } + + *pconn = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_conn)); + if (*pconn == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_conn; + } + + rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.bound, + NGTCP2_MAX_BOUND_DCID_POOL_SIZE, sizeof(ngtcp2_dcid), + mem); + if (rv != 0) { + goto fail_dcid_bound_init; + } + + rv = ngtcp2_ringbuf_init(&(*pconn)->dcid.unused, NGTCP2_MAX_DCID_POOL_SIZE, + sizeof(ngtcp2_dcid), mem); + if (rv != 0) { + goto fail_dcid_unused_init; + } + + rv = + ngtcp2_ringbuf_init(&(*pconn)->dcid.retired, NGTCP2_MAX_DCID_RETIRED_SIZE, + sizeof(ngtcp2_dcid), mem); + if (rv != 0) { + goto fail_dcid_retired_init; + } + + rv = ngtcp2_gaptr_init(&(*pconn)->dcid.seqgap, mem); + if (rv != 0) { + goto fail_seqgap_init; + } + + rv = ngtcp2_ksl_init(&(*pconn)->scid.set, cid_less, sizeof(ngtcp2_cid), mem); + if (rv != 0) { + goto fail_scid_set_init; + } + + ngtcp2_pq_init(&(*pconn)->scid.used, ts_retired_less, mem); + + rv = ngtcp2_map_init(&(*pconn)->strms, mem); + if (rv != 0) { + goto fail_strms_init; + } + + ngtcp2_pq_init(&(*pconn)->tx.strmq, cycle_less, mem); + + rv = ngtcp2_idtr_init(&(*pconn)->remote.bidi.idtr, !server, mem); + if (rv != 0) { + goto fail_remote_bidi_idtr_init; + } + + rv = ngtcp2_idtr_init(&(*pconn)->remote.uni.idtr, !server, mem); + if (rv != 0) { + goto fail_remote_uni_idtr_init; + } + + rv = ngtcp2_ringbuf_init(&(*pconn)->rx.path_challenge, 4, + sizeof(ngtcp2_path_challenge_entry), mem); + if (rv != 0) { + goto fail_rx_path_challenge_init; + } + + ngtcp2_log_init(&(*pconn)->log, scid, settings->log_printf, + settings->initial_ts, user_data); + ngtcp2_qlog_init(&(*pconn)->qlog, settings->qlog.write, settings->initial_ts, + user_data); + if ((*pconn)->qlog.write) { + buf = ngtcp2_mem_malloc(mem, NGTCP2_QLOG_BUFLEN); + if (buf == NULL) { + goto fail_qlog_buf; + } + ngtcp2_buf_init(&(*pconn)->qlog.buf, buf, NGTCP2_QLOG_BUFLEN); + } + + (*pconn)->local.settings = *settings; + (*pconn)->local.transport_params = *params; + + if (settings->token.len) { + buf = ngtcp2_mem_malloc(mem, settings->token.len); + if (buf == NULL) { + goto fail_token; + } + memcpy(buf, settings->token.base, settings->token.len); + (*pconn)->local.settings.token.base = buf; + } else { + (*pconn)->local.settings.token.base = NULL; + (*pconn)->local.settings.token.len = 0; + } + + if (settings->max_udp_payload_size == 0) { + (*pconn)->local.settings.max_udp_payload_size = NGTCP2_DEFAULT_MAX_PKTLEN; + } + + conn_reset_conn_stat(*pconn, &(*pconn)->cstat); + (*pconn)->cstat.initial_rtt = settings->initial_rtt; + (*pconn)->cstat.max_udp_payload_size = + (*pconn)->local.settings.max_udp_payload_size; + + ngtcp2_rst_init(&(*pconn)->rst); + + (*pconn)->cc_algo = settings->cc_algo; + + switch (settings->cc_algo) { + case NGTCP2_CC_ALGO_RENO: + rv = ngtcp2_cc_reno_cc_init(&(*pconn)->cc, &(*pconn)->log, mem); + if (rv != 0) { + goto fail_cc_init; + } + break; + case NGTCP2_CC_ALGO_CUBIC: + rv = ngtcp2_cc_cubic_cc_init(&(*pconn)->cc, &(*pconn)->log, mem); + if (rv != 0) { + goto fail_cc_init; + } + break; + case NGTCP2_CC_ALGO_CUSTOM: + assert(settings->cc); + (*pconn)->cc = *settings->cc; + (*pconn)->cc.ccb->log = &(*pconn)->log; + break; + default: + assert(0); + } + + rv = pktns_new(&(*pconn)->in_pktns, NGTCP2_PKTNS_ID_INITIAL, &(*pconn)->rst, + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + if (rv != 0) { + goto fail_in_pktns_init; + } + + rv = pktns_new(&(*pconn)->hs_pktns, NGTCP2_PKTNS_ID_HANDSHAKE, &(*pconn)->rst, + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + if (rv != 0) { + goto fail_hs_pktns_init; + } + + rv = pktns_init(&(*pconn)->pktns, NGTCP2_PKTNS_ID_APPLICATION, &(*pconn)->rst, + &(*pconn)->cc, &(*pconn)->log, &(*pconn)->qlog, mem); + if (rv != 0) { + goto fail_pktns_init; + } + + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + rv = NGTCP2_ERR_NOMEM; + goto fail_scident; + } + + /* Set stateless reset token later if it is available in the local + transport parameters */ + ngtcp2_scid_init(scident, 0, scid, NULL); + + rv = ngtcp2_ksl_insert(&(*pconn)->scid.set, NULL, &scident->cid, scident); + if (rv != 0) { + goto fail_scid_set_insert; + } + + scident = NULL; + + ngtcp2_dcid_init(&(*pconn)->dcid.current, 0, dcid, NULL); + ngtcp2_path_copy(&(*pconn)->dcid.current.ps.path, path); + + rv = ngtcp2_gaptr_push(&(*pconn)->dcid.seqgap, 0, 1); + if (rv != 0) { + goto fail_seqgap_push; + } + + (*pconn)->server = server; + (*pconn)->oscid = *scid; + (*pconn)->callbacks = *callbacks; + (*pconn)->version = version; + (*pconn)->mem = mem; + (*pconn)->user_data = user_data; + (*pconn)->idle_ts = settings->initial_ts; + (*pconn)->crypto.key_update.confirmed_ts = UINT64_MAX; + (*pconn)->tx.last_max_data_ts = UINT64_MAX; + (*pconn)->early.discard_started_ts = UINT64_MAX; + + conn_reset_ecn_validation_state(*pconn); + + ngtcp2_qlog_start(&(*pconn)->qlog, server ? &settings->qlog.odcid : dcid, + server); + + return 0; + +fail_seqgap_push: +fail_scid_set_insert: + ngtcp2_mem_free(mem, scident); +fail_scident: + ngtcp2_mem_free(mem, (*pconn)->local.settings.token.base); +fail_token: + pktns_free(&(*pconn)->pktns, mem); +fail_pktns_init: + pktns_del((*pconn)->hs_pktns, mem); +fail_hs_pktns_init: + pktns_del((*pconn)->in_pktns, mem); +fail_in_pktns_init: + cc_del(&(*pconn)->cc, settings->cc_algo, mem); +fail_cc_init: + ngtcp2_mem_free(mem, (*pconn)->qlog.buf.begin); +fail_qlog_buf: + ngtcp2_ringbuf_free(&(*pconn)->rx.path_challenge); +fail_rx_path_challenge_init: + ngtcp2_idtr_free(&(*pconn)->remote.uni.idtr); +fail_remote_uni_idtr_init: + ngtcp2_idtr_free(&(*pconn)->remote.bidi.idtr); +fail_remote_bidi_idtr_init: + ngtcp2_map_free(&(*pconn)->strms); +fail_strms_init: + delete_scid(&(*pconn)->scid.set, mem); + ngtcp2_ksl_free(&(*pconn)->scid.set); +fail_scid_set_init: + ngtcp2_gaptr_free(&(*pconn)->dcid.seqgap); +fail_seqgap_init: + ngtcp2_ringbuf_free(&(*pconn)->dcid.retired); +fail_dcid_retired_init: + ngtcp2_ringbuf_free(&(*pconn)->dcid.unused); +fail_dcid_unused_init: + ngtcp2_ringbuf_free(&(*pconn)->dcid.bound); +fail_dcid_bound_init: + ngtcp2_mem_free(mem, *pconn); +fail_conn: + return rv; +} + +int ngtcp2_conn_client_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { + int rv; + rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params, + mem, user_data, 0); + if (rv != 0) { + return rv; + } + (*pconn)->rcid = *dcid; + (*pconn)->state = NGTCP2_CS_CLIENT_INITIAL; + (*pconn)->local.bidi.next_stream_id = 0; + (*pconn)->local.uni.next_stream_id = 2; + + rv = ngtcp2_conn_commit_local_transport_params(*pconn); + if (rv != 0) { + ngtcp2_conn_del(*pconn); + return rv; + } + + return 0; +} + +int ngtcp2_conn_server_new(ngtcp2_conn **pconn, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_path *path, + uint32_t version, const ngtcp2_callbacks *callbacks, + const ngtcp2_settings *settings, + const ngtcp2_transport_params *params, + const ngtcp2_mem *mem, void *user_data) { + int rv; + rv = conn_new(pconn, dcid, scid, path, version, callbacks, settings, params, + mem, user_data, 1); + if (rv != 0) { + return rv; + } + (*pconn)->state = NGTCP2_CS_SERVER_INITIAL; + (*pconn)->local.bidi.next_stream_id = 1; + (*pconn)->local.uni.next_stream_id = 3; + + if ((*pconn)->local.settings.token.len) { + /* Usage of token lifts amplification limit */ + (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + } + + return 0; +} + +/* + * conn_fc_credits returns the number of bytes allowed to be sent to + * the given stream. Both connection and stream level flow control + * credits are considered. + */ +static uint64_t conn_fc_credits(ngtcp2_conn *conn, ngtcp2_strm *strm) { + return ngtcp2_min(strm->tx.max_offset - strm->tx.offset, + conn->tx.max_offset - conn->tx.offset); +} + +/* + * conn_enforce_flow_control returns the number of bytes allowed to be + * sent to the given stream. |len| might be shorted because of + * available flow control credits. + */ +static size_t conn_enforce_flow_control(ngtcp2_conn *conn, ngtcp2_strm *strm, + size_t len) { + uint64_t fc_credits = conn_fc_credits(conn, strm); + return (size_t)ngtcp2_min((uint64_t)len, fc_credits); +} + +static int delete_strms_each(ngtcp2_map_entry *ent, void *ptr) { + const ngtcp2_mem *mem = ptr; + ngtcp2_strm *s = ngtcp2_struct_of(ent, ngtcp2_strm, me); + + ngtcp2_strm_free(s); + ngtcp2_mem_free(mem, s); + + return 0; +} + +void ngtcp2_conn_del(ngtcp2_conn *conn) { + if (conn == NULL) { + return; + } + + ngtcp2_qlog_end(&conn->qlog); + + if (conn->early.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx); + + if (conn->crypto.key_update.old_rx_ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx); + } + if (conn->crypto.key_update.new_rx_ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->crypto.key_update.new_rx_ckm->aead_ctx); + } + if (conn->crypto.key_update.new_tx_ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->crypto.key_update.new_tx_ckm->aead_ctx); + } + + if (conn->pktns.crypto.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, + &conn->pktns.crypto.rx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.rx.hp_ctx); + + if (conn->pktns.crypto.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, + &conn->pktns.crypto.tx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->pktns.crypto.tx.hp_ctx); + + if (conn->hs_pktns) { + if (conn->hs_pktns->crypto.rx.ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->hs_pktns->crypto.rx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.rx.hp_ctx); + + if (conn->hs_pktns->crypto.tx.ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->hs_pktns->crypto.tx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->hs_pktns->crypto.tx.hp_ctx); + } + if (conn->in_pktns) { + if (conn->in_pktns->crypto.rx.ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->in_pktns->crypto.rx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.rx.hp_ctx); + + if (conn->in_pktns->crypto.tx.ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->in_pktns->crypto.tx.ckm->aead_ctx); + } + conn_call_delete_crypto_cipher_ctx(conn, &conn->in_pktns->crypto.tx.hp_ctx); + } + + conn_call_delete_crypto_aead_ctx(conn, &conn->crypto.retry_aead_ctx); + + ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_buf.base); + ngtcp2_mem_free(conn->mem, conn->crypto.decrypt_hp_buf.base); + ngtcp2_mem_free(conn->mem, conn->local.settings.token.base); + + ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); + ngtcp2_crypto_km_del(conn->crypto.key_update.new_rx_ckm, conn->mem); + ngtcp2_crypto_km_del(conn->crypto.key_update.new_tx_ckm, conn->mem); + ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); + + pktns_free(&conn->pktns, conn->mem); + pktns_del(conn->hs_pktns, conn->mem); + pktns_del(conn->in_pktns, conn->mem); + + cc_del(&conn->cc, conn->cc_algo, conn->mem); + + ngtcp2_mem_free(conn->mem, conn->qlog.buf.begin); + + ngtcp2_ringbuf_free(&conn->rx.path_challenge); + + ngtcp2_pv_del(conn->pv); + + 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_free(&conn->strms, delete_strms_each, (void *)conn->mem); + ngtcp2_map_free(&conn->strms); + + ngtcp2_pq_free(&conn->scid.used); + delete_scid(&conn->scid.set, conn->mem); + ngtcp2_ksl_free(&conn->scid.set); + ngtcp2_gaptr_free(&conn->dcid.seqgap); + ngtcp2_ringbuf_free(&conn->dcid.retired); + ngtcp2_ringbuf_free(&conn->dcid.unused); + ngtcp2_ringbuf_free(&conn->dcid.bound); + + ngtcp2_mem_free(conn->mem, conn); +} + +/* + * conn_ensure_ack_blks makes sure that conn->tx.ack->ack.blks can + * contain at least |n| additional ngtcp2_ack_blk. + * + * 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_blks(ngtcp2_conn *conn, size_t n) { + ngtcp2_frame *fr; + size_t max = conn->tx.max_ack_blks; + + 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_blk) * max); + if (fr == NULL) { + return NGTCP2_ERR_NOMEM; + } + + conn->tx.ack = fr; + conn->tx.max_ack_blks = max; + + return 0; +} + +/* + * conn_compute_ack_delay computes ACK delay for outgoing protected + * ACK. + */ +static ngtcp2_duration conn_compute_ack_delay(ngtcp2_conn *conn) { + return ngtcp2_min(conn->local.transport_params.max_ack_delay, + conn->cstat.smoothed_rtt / 8); +} + +/* + * 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. + * If |nodelay| is nonzero, delayed ACK timer is ignored. + * + * The memory for ACK frame is dynamically allocated by this function. + * A caller is responsible to free it. + * + * 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. + */ +static int 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_blks = 8; + int64_t last_pkt_num; + ngtcp2_acktr *acktr = &pktns->acktr; + ngtcp2_ack_blk *blk; + ngtcp2_ksl_it it; + ngtcp2_acktr_entry *rpkt; + ngtcp2_ack *ack; + size_t blk_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_blk) * initial_max_ack_blks); + if (conn->tx.ack == NULL) { + return NGTCP2_ERR_NOMEM; + } + conn->tx.max_ack_blks = initial_max_ack_blks; + } + + 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->num_blks = 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_blklen = rpkt->len - 1; + + 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_blklen = 0; + } + + if (type == NGTCP2_PKT_SHORT) { + 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->num_blks == NGTCP2_MAX_ACK_BLKS) { + break; + } + + rpkt = ngtcp2_ksl_it_get(&it); + + blk_idx = ack->num_blks++; + rv = conn_ensure_ack_blks(conn, ack->num_blks); + if (rv != 0) { + return rv; + } + ack = &conn->tx.ack->ack; + blk = &ack->blks[blk_idx]; + blk->gap = (uint64_t)(last_pkt_num - rpkt->pkt_num - 2); + blk->blklen = 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 + * assigned to |*hd_logged|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too small. + */ +static int conn_ppe_write_frame_hd_log(ngtcp2_conn *conn, ngtcp2_ppe *ppe, + int *hd_logged, const ngtcp2_pkt_hd *hd, + ngtcp2_frame *fr) { + int rv; + + rv = ngtcp2_ppe_encode_frame(ppe, fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + if (hd_logged && !*hd_logged) { + *hd_logged = 1; + ngtcp2_log_tx_pkt_hd(&conn->log, hd); + ngtcp2_qlog_pkt_sent_start(&conn->qlog); + } + + ngtcp2_log_tx_fr(&conn->log, hd, fr); + ngtcp2_qlog_write_frame(&conn->qlog, fr); + + return 0; +} + +/* + * conn_ppe_write_frame writes |fr| to |ppe|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too small. + */ +static int conn_ppe_write_frame(ngtcp2_conn *conn, ngtcp2_ppe *ppe, + const ngtcp2_pkt_hd *hd, ngtcp2_frame *fr) { + return conn_ppe_write_frame_hd_log(conn, ppe, NULL, hd, fr); +} + +/* + * conn_on_pkt_sent is called when new non-ACK-only packet is sent. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_on_pkt_sent(ngtcp2_conn *conn, ngtcp2_rtb *rtb, + ngtcp2_rtb_entry *ent) { + int rv; + + /* This function implements OnPacketSent, but it handles only + non-ACK-only packet. */ + rv = ngtcp2_rtb_add(rtb, ent, &conn->cstat); + if (rv != 0) { + return rv; + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + conn->cstat.last_tx_pkt_ts[rtb->pktns_id] = ent->ts; + } + + ngtcp2_conn_set_loss_detection_timer(conn, ent->ts); + + return 0; +} + +/* + * pktns_select_pkt_numlen selects shortest packet number encoding for + * the next packet number based on the largest acknowledged packet + * number. It returns the number of bytes to encode the packet + * number. + */ +static size_t pktns_select_pkt_numlen(ngtcp2_pktns *pktns) { + int64_t pkt_num = pktns->tx.last_pkt_num + 1; + ngtcp2_rtb *rtb = &pktns->rtb; + int64_t n = pkt_num - rtb->largest_acked_tx_pkt_num; + + if (NGTCP2_MAX_PKT_NUM / 2 <= pkt_num) { + return 4; + } + + n = n * 2 + 1; + + if (n > 0xffffff) { + return 4; + } + if (n > 0xffff) { + return 3; + } + if (n > 0xff) { + return 2; + } + return 1; +} + +/* + * conn_cwnd_is_zero returns nonzero if the number of bytes the local + * endpoint can sent at this time is zero. + */ +static uint64_t conn_cwnd_is_zero(ngtcp2_conn *conn) { + uint64_t bytes_in_flight = conn->cstat.bytes_in_flight; + uint64_t cwnd = + conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) + ? ngtcp2_cc_compute_initcwnd(conn->cstat.max_udp_payload_size) + : conn->cstat.cwnd; + + return bytes_in_flight >= cwnd; +} + +/* + * conn_retry_early_payloadlen returns the estimated wire length of + * the first STREAM frame of 0-RTT packet which should be + * retransmitted due to Retry frame + */ +static size_t conn_retry_early_payloadlen(ngtcp2_conn *conn) { + ngtcp2_frame_chain *frc; + ngtcp2_strm *strm; + + if (conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED) { + return 0; + } + + for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { + strm = ngtcp2_conn_tx_strmq_top(conn); + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + frc = ngtcp2_strm_streamfrq_top(strm); + return ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt) + + NGTCP2_STREAM_OVERHEAD; + } + + return 0; +} + +static void conn_cryptofrq_clear(ngtcp2_conn *conn, ngtcp2_pktns *pktns) { + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it it; + + for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + ngtcp2_frame_chain_del(frc, conn->mem); + } + ngtcp2_ksl_clear(&pktns->crypto.tx.frq); +} + +/* + * conn_cryptofrq_unacked_offset returns the CRYPTO frame offset by + * taking into account acknowledged offset. If there is no data to + * send, this function returns (uint64_t)-1. + */ +static uint64_t conn_cryptofrq_unacked_offset(ngtcp2_conn *conn, + ngtcp2_pktns *pktns) { + ngtcp2_frame_chain *frc; + ngtcp2_crypto *fr; + ngtcp2_range gap; + ngtcp2_rtb *rtb = &pktns->rtb; + ngtcp2_ksl_it it; + size_t datalen; + + (void)conn; + + for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + fr = &frc->fr.crypto; + + gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->offset); + + datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + if (gap.begin <= fr->offset) { + return fr->offset; + } + if (gap.begin < fr->offset + datalen) { + return gap.begin; + } + } + + return (uint64_t)-1; +} + +static int conn_cryptofrq_unacked_pop(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_frame_chain **pfrc) { + ngtcp2_frame_chain *frc, *nfrc; + ngtcp2_crypto *fr, *nfr; + uint64_t offset, end_offset; + size_t idx, end_idx; + uint64_t base_offset, end_base_offset; + ngtcp2_range gap; + ngtcp2_rtb *rtb = &pktns->rtb; + ngtcp2_vec *v; + int rv; + ngtcp2_ksl_it it; + + *pfrc = NULL; + + for (it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); !ngtcp2_ksl_it_end(&it);) { + frc = ngtcp2_ksl_it_get(&it); + fr = &frc->fr.crypto; + + ngtcp2_ksl_remove(&pktns->crypto.tx.frq, &it, &fr->offset); + + idx = 0; + offset = fr->offset; + base_offset = 0; + + gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, offset); + if (gap.begin < offset) { + gap.begin = offset; + } + + for (; idx < fr->datacnt && offset < gap.begin; ++idx) { + v = &fr->data[idx]; + if (offset + v->len > gap.begin) { + base_offset = gap.begin - offset; + break; + } + + offset += v->len; + } + + if (idx == fr->datacnt) { + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + + assert(gap.begin == offset + base_offset); + + end_idx = idx; + end_offset = offset; + end_base_offset = 0; + + for (; end_idx < fr->datacnt; ++end_idx) { + v = &fr->data[end_idx]; + if (end_offset + v->len > gap.end) { + end_base_offset = gap.end - end_offset; + break; + } + + end_offset += v->len; + } + + if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) { + *pfrc = frc; + return 0; + } + + if (fr->datacnt == end_idx) { + memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + + assert(fr->data[0].len > base_offset); + + fr->offset = offset + base_offset; + fr->datacnt = end_idx - idx; + fr->data[0].base += base_offset; + fr->data[0].len -= (size_t)base_offset; + + *pfrc = frc; + return 0; + } + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->datacnt - end_idx, + conn->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + nfr->type = NGTCP2_FRAME_CRYPTO; + memcpy(nfr->data, fr->data + end_idx, + sizeof(nfr->data[0]) * (fr->datacnt - end_idx)); + + assert(nfr->data[0].len > end_base_offset); + + nfr->offset = end_offset + end_base_offset; + nfr->datacnt = fr->datacnt - end_idx; + nfr->data[0].base += end_base_offset; + nfr->data[0].len -= (size_t)end_base_offset; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + if (end_base_offset) { + ++end_idx; + } + + memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + + assert(fr->data[0].len > base_offset); + + fr->offset = offset + base_offset; + fr->datacnt = end_idx - idx; + if (end_base_offset) { + assert(fr->data[fr->datacnt - 1].len > end_base_offset); + fr->data[fr->datacnt - 1].len = (size_t)end_base_offset; + } + fr->data[0].base += base_offset; + fr->data[0].len -= (size_t)base_offset; + + *pfrc = frc; + return 0; + } + + return 0; +} +static int conn_cryptofrq_pop(ngtcp2_conn *conn, ngtcp2_frame_chain **pfrc, + ngtcp2_pktns *pktns, size_t left) { + ngtcp2_crypto *fr, *nfr; + ngtcp2_frame_chain *frc, *nfrc; + int rv; + size_t nmerged; + size_t datalen; + ngtcp2_vec a[NGTCP2_MAX_CRYPTO_DATACNT]; + ngtcp2_vec b[NGTCP2_MAX_CRYPTO_DATACNT]; + size_t acnt, bcnt; + ngtcp2_ksl_it it; + + rv = conn_cryptofrq_unacked_pop(conn, pktns, &frc); + if (rv != 0) { + return rv; + } + if (frc == NULL) { + *pfrc = NULL; + return 0; + } + + fr = &frc->fr.crypto; + datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + if (datalen > left) { + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + bcnt = 0; + ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_CRYPTO_DATACNT); + + assert(acnt > 0); + assert(bcnt > 0); + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, bcnt, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + nfr->type = NGTCP2_FRAME_CRYPTO; + nfr->offset = fr->offset + left; + nfr->datacnt = bcnt; + ngtcp2_vec_copy(nfr->data, b, bcnt); + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + *nfr = *fr; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, conn->mem); + + *pfrc = nfrc; + + return 0; + } + + left -= datalen; + + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + for (; left && ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + it = ngtcp2_ksl_begin(&pktns->crypto.tx.frq); + nfrc = ngtcp2_ksl_it_get(&it); + nfr = &nfrc->fr.crypto; + + if (nfr->offset != fr->offset + datalen) { + assert(fr->offset + datalen < nfr->offset); + break; + } + + rv = conn_cryptofrq_unacked_pop(conn, pktns, &nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + if (nfrc == NULL) { + break; + } + + nfr = &nfrc->fr.crypto; + + nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left, + NGTCP2_MAX_CRYPTO_DATACNT); + if (nmerged == 0) { + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + break; + } + + datalen += nmerged; + left -= nmerged; + + if (nfr->datacnt == 0) { + ngtcp2_frame_chain_del(nfrc, conn->mem); + continue; + } + + nfr->offset += nmerged; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + ngtcp2_frame_chain_del(nfrc, conn->mem); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + break; + } + + if (acnt == fr->datacnt) { + assert(acnt > 0); + fr->data[acnt - 1] = a[acnt - 1]; + + *pfrc = frc; + return 0; + } + + assert(acnt > fr->datacnt); + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, acnt, conn->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + nfr = &nfrc->fr.crypto; + *nfr = *fr; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, conn->mem); + + *pfrc = nfrc; + + return 0; +} + +/* + * conn_verify_dcid verifies that destination connection ID in |hd| is + * valid for the connection. If it is successfully verified and the + * remote endpoint uses new DCID in the packet, nonzero value is + * assigned to |*pnew_cid_used| if it is not NULL. Otherwise 0 is + * assigned to it. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_INVALID_ARGUMENT + * |dcid| is not known to the local endpoint. + */ +static int conn_verify_dcid(ngtcp2_conn *conn, int *pnew_cid_used, + const ngtcp2_pkt_hd *hd) { + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + int rv; + + it = ngtcp2_ksl_lower_bound(&conn->scid.set, &hd->dcid); + if (ngtcp2_ksl_it_end(&it)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + scid = ngtcp2_ksl_it_get(&it); + if (!ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (!(scid->flags & NGTCP2_SCID_FLAG_USED)) { + scid->flags |= NGTCP2_SCID_FLAG_USED; + + if (scid->pe.index == NGTCP2_PQ_BAD_INDEX) { + rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe); + if (rv != 0) { + return rv; + } + } + + if (pnew_cid_used) { + *pnew_cid_used = 1; + } + } else if (pnew_cid_used) { + *pnew_cid_used = 0; + } + + return 0; +} + +/* + * conn_should_pad_pkt returns nonzero if the packet should be padded. + * |type| is the type of packet. |left| is the space left in packet + * buffer. |early_datalen| is the number of bytes which will be sent + * in the next, coalesced 0-RTT packet. + */ +static int conn_should_pad_pkt(ngtcp2_conn *conn, uint8_t type, size_t left, + size_t early_datalen, int ack_eliciting) { + size_t min_payloadlen; + + if (conn->server) { + if (type != NGTCP2_PKT_INITIAL || !ack_eliciting) { + return 0; + } + + if (conn->hs_pktns->crypto.tx.ckm && + (conn->hs_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || + !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { + /* If we have something to send in Handshake packet, then add + PADDING in Handshake packet. */ + min_payloadlen = 128; + } else { + return 1; + } + } else { + if (type == NGTCP2_PKT_HANDSHAKE) { + return conn->in_pktns != NULL; + } + + if (conn->hs_pktns->crypto.tx.ckm && + (conn->hs_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->hs_pktns->crypto.tx.frq) || + !ngtcp2_acktr_empty(&conn->hs_pktns->acktr))) { + /* If we have something to send in Handshake packet, then add + PADDING in Handshake packet. */ + min_payloadlen = 128; + } else if (!conn->early.ckm || early_datalen == 0) { + return 1; + } else { + /* If we have something to send in 0RTT packet, then add PADDING + in 0RTT packet. */ + min_payloadlen = ngtcp2_min(early_datalen, 128); + } + } + + return left < + /* TODO Assuming that pkt_num is encoded in 1 byte. */ + NGTCP2_MIN_LONG_HEADERLEN + conn->dcid.current.cid.datalen + + conn->oscid.datalen + 1 /* payloadlen bytes - 1 */ + + min_payloadlen + NGTCP2_MAX_AEAD_OVERHEAD; +} + +static void conn_restart_timer_on_write(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + conn->idle_ts = ts; + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; +} + +static void conn_restart_timer_on_read(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + conn->idle_ts = ts; + conn->flags |= NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE; +} + +/* NGTCP2_WRITE_PKT_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_WRITE_PKT_FLAG_NONE 0x00 +/* NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING indicates that packet should + be padded */ +#define NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING 0x01 +/* NGTCP2_WRITE_PKT_FLAG_MORE indicates that more frames might come + and it should be encoded into the current packet. */ +#define NGTCP2_WRITE_PKT_FLAG_MORE 0x02 + +/* + * conn_write_handshake_pkt writes handshake packet in the buffer + * pointed by |dest| whose length is |destlen|. |type| specifies long + * packet type. It should be either NGTCP2_PKT_INITIAL or + * NGTCP2_PKT_HANDSHAKE_PKT. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize +conn_write_handshake_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint8_t type, uint8_t flags, + size_t early_datalen, ngtcp2_tstamp ts) { + int rv; + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + ngtcp2_frame_chain *frq = NULL, **pfrc = &frq; + ngtcp2_frame_chain *nfrc; + ngtcp2_frame *ackfr = NULL, lfr; + ngtcp2_ssize spktlen; + ngtcp2_crypto_cc cc; + ngtcp2_rtb_entry *rtbent; + ngtcp2_pktns *pktns; + size_t left; + uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; + int pkt_empty = 1; + int padded = 0; + int hd_logged = 0; + uint64_t crypto_offset; + ngtcp2_ssize num_reclaimed; + + switch (type) { + case NGTCP2_PKT_INITIAL: + if (!conn->in_pktns) { + return 0; + } + assert(conn->in_pktns->crypto.tx.ckm); + pktns = conn->in_pktns; + break; + case NGTCP2_PKT_HANDSHAKE: + if (!conn->hs_pktns || !conn->hs_pktns->crypto.tx.ckm) { + return 0; + } + pktns = conn->hs_pktns; + break; + default: + assert(0); + } + + cc.aead = pktns->crypto.ctx.aead; + cc.hp = pktns->crypto.ctx.hp; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + cc.encrypt = conn->callbacks.encrypt; + cc.hp_mask = conn->callbacks.hp_mask; + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, type, + &conn->dcid.current.cid, &conn->oscid, + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), + conn->version, 0); + + if (!conn->server && type == NGTCP2_PKT_INITIAL && + conn->local.settings.token.len) { + hd.token = conn->local.settings.token; + } + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return 0; + } + + rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts, + /* ack_delay = */ 0, + NGTCP2_DEFAULT_ACK_DELAY_EXPONENT); + if (rv != 0) { + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + + if (ackfr) { + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, ackfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, ackfr->ack.largest_ack); + pkt_empty = 0; + } + } + + /* Server requires at least NGTCP2_DEFAULT_MAX_PKTLEN bytes in order + to send ack-eliciting Initial packet. */ + if (!conn->server || type != NGTCP2_PKT_INITIAL || + destlen >= NGTCP2_DEFAULT_MAX_PKTLEN) { + build_pkt: + for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(&ppe); + + crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns); + if (crypto_offset == (size_t)-1) { + conn_cryptofrq_clear(conn, pktns); + break; + } + + left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); + if (left == (size_t)-1) { + break; + } + + rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + + if (nfrc == NULL) { + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + } + + if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && + pktns->rtb.num_retransmittable && pktns->rtb.probe_pkt_left) { + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, + pktns->rtb.probe_pkt_left + 1); + if (num_reclaimed < 0) { + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + if (num_reclaimed) { + goto build_pkt; + } + /* We had pktns->rtb.num_retransmittable > 0 but the contents of + those packets have been acknowledged (i.e., retransmission in + another packet). For server, in this case, we don't have to + send any probe packet. Client needs to send probe packets + until it knows that server has completed address validation or + handshake has been confirmed. */ + if (pktns->rtb.num_retransmittable == 0 && + (conn->server || + (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | + NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { + pktns->rtb.probe_pkt_left = 0; + ngtcp2_conn_set_loss_detection_timer(conn, ts); + } + } + + /* Don't send any PING frame if client Initial has not been + acknowledged yet. */ + if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && + pktns->rtb.probe_pkt_left && + (type != NGTCP2_PKT_INITIAL || + ngtcp2_strm_is_all_tx_data_acked(&pktns->crypto.strm))) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + rtb_entry_flags |= + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | NGTCP2_RTB_ENTRY_FLAG_PROBE; + pkt_empty = 0; + } + } + + if (!pkt_empty) { + if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + /* The intention of smaller limit is get more chance to measure + RTT samples in early phase. */ + if (pktns->rtb.probe_pkt_left || pktns->tx.num_non_ack_pkt >= 1) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, &ppe, &hd_logged, &hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + } else { + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + pktns->tx.num_non_ack_pkt = 0; + } + } else { + ++pktns->tx.num_non_ack_pkt; + } + } else { + pktns->tx.num_non_ack_pkt = 0; + } + } + } + + if (pkt_empty) { + return 0; + } + + /* If we cannot write another packet, then we need to add padding to + Initial here. */ + if (require_padding || + conn_should_pad_pkt( + conn, type, ngtcp2_ppe_left(&ppe), early_datalen, + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) != 0)) { + lfr.type = NGTCP2_FRAME_PADDING; + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + lfr.type = NGTCP2_FRAME_PADDING; + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } + + if (lfr.padding.len) { + padded = 1; + ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + spktlen = ngtcp2_ppe_final(&ppe, NULL); + if (spktlen < 0) { + assert(ngtcp2_err_is_fatal((int)spktlen)); + ngtcp2_frame_chain_list_del(frq, conn->mem); + return spktlen; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)spktlen); + + if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) { + if (pi) { + conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, &hd, ts); + } + + rv = ngtcp2_rtb_entry_new(&rtbent, &hd, frq, ts, (size_t)spktlen, + rtb_entry_flags, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_list_del(frq, conn->mem); + return rv; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); + if (rv != 0) { + ngtcp2_rtb_entry_del(rtbent, conn->mem); + return rv; + } + + if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && + (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { + conn_restart_timer_on_write(conn, ts); + } + } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { + conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts); + } + + if (pktns->rtb.probe_pkt_left && + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + --pktns->rtb.probe_pkt_left; + } + + conn->dcid.current.bytes_sent += (uint64_t)spktlen; + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + + ++pktns->tx.last_pkt_num; + + return spktlen; +} + +/* + * conn_write_ack_pkt writes QUIC packet for type |type| which only + * includes ACK frame in the buffer pointed by |dest| whose length is + * |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +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; + + assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + + switch (type) { + case NGTCP2_PKT_INITIAL: + assert(conn->server); + pktns = conn->in_pktns; + ack_delay = 0; + ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + break; + case NGTCP2_PKT_HANDSHAKE: + pktns = conn->hs_pktns; + ack_delay = 0; + ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + break; + case NGTCP2_PKT_SHORT: + pktns = &conn->pktns; + ack_delay = conn_compute_ack_delay(conn); + ack_delay_exponent = conn->local.transport_params.ack_delay_exponent; + break; + default: + assert(0); + } + + if (!pktns->crypto.tx.ckm) { + return 0; + } + + ackfr = NULL; + rv = conn_create_ack_frame(conn, &ackfr, pktns, type, ts, ack_delay, + ack_delay_exponent); + if (rv != 0) { + return rv; + } + + if (!ackfr) { + return 0; + } + + spktlen = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, type, &conn->dcid.current.cid, ackfr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + + if (spktlen <= 0) { + return spktlen; + } + + conn->dcid.current.bytes_sent += (uint64_t)spktlen; + + return spktlen; +} + +static void conn_discard_pktns(ngtcp2_conn *conn, ngtcp2_pktns **ppktns, + ngtcp2_tstamp ts) { + ngtcp2_pktns *pktns = *ppktns; + uint64_t bytes_in_flight; + + bytes_in_flight = pktns->rtb.cc_bytes_in_flight; + + assert(conn->cstat.bytes_in_flight >= bytes_in_flight); + + conn->cstat.bytes_in_flight -= bytes_in_flight; + conn->cstat.pto_count = 0; + conn->cstat.last_tx_pkt_ts[pktns->rtb.pktns_id] = UINT64_MAX; + conn->cstat.loss_time[pktns->rtb.pktns_id] = UINT64_MAX; + + conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx); + conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); + conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); + conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx); + + pktns_del(pktns, conn->mem); + *ppktns = NULL; + + ngtcp2_conn_set_loss_detection_timer(conn, ts); +} + +/* + * conn_discard_initial_state discards state for Initial packet number + * space. + */ +static void conn_discard_initial_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + if (!conn->in_pktns) { + return; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "discarding Initial packet number space"); + + conn_discard_pktns(conn, &conn->in_pktns, ts); +} + +/* + * conn_discard_handshake_state discards state for Handshake packet + * number space. + */ +static void conn_discard_handshake_state(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + if (!conn->hs_pktns) { + return; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "discarding Handshake packet number space"); + + conn_discard_pktns(conn, &conn->hs_pktns, ts); +} + +/* + * conn_discard_early_key discards early key. + */ +static void conn_discard_early_key(ngtcp2_conn *conn) { + assert(conn->early.ckm); + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "discarding early key"); + + conn_call_delete_crypto_aead_ctx(conn, &conn->early.ckm->aead_ctx); + conn_call_delete_crypto_cipher_ctx(conn, &conn->early.hp_ctx); + memset(&conn->early.hp_ctx, 0, sizeof(conn->early.hp_ctx)); + + ngtcp2_crypto_km_del(conn->early.ckm, conn->mem); + conn->early.ckm = NULL; +} + +/* + * conn_write_handshake_ack_pkts writes packets which contain ACK + * frame only. This function writes at most 2 packets for each + * Initial and Handshake packet. + */ +static ngtcp2_ssize conn_write_handshake_ack_pkts(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + ngtcp2_ssize res = 0, nwrite = 0; + + /* In the most cases, client sends ACK in conn_write_handshake_pkt. + This function is only called when it is CWND limited. It is not + required for client to send ACK for server Initial. This is + because once it gets server Initial, it gets Handshake tx key and + discards Initial key. The only good reason to send ACK is give + server RTT measurement early. */ + if (conn->server && conn->in_pktns) { + nwrite = + conn_write_ack_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + + if (conn->hs_pktns->crypto.tx.ckm) { + nwrite = + conn_write_ack_pkt(conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + + if (!conn->server && nwrite) { + conn_discard_initial_state(conn, ts); + } + } + + return res; +} + +/* + * conn_write_client_initial writes Initial packet in the buffer + * pointed by |dest| whose length is |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_client_initial(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + size_t early_datalen, + ngtcp2_tstamp ts) { + int rv; + + assert(conn->callbacks.client_initial); + + rv = conn->callbacks.client_initial(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, + NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, + ts); +} + +/* + * conn_write_handshake_pkts writes Initial and Handshake packets in + * the buffer pointed by |dest| whose length is |destlen|. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_handshake_pkts(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + size_t early_datalen, + ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_ssize res = 0; + int64_t prev_pkt_num = -1; + ngtcp2_rtb_entry *rtbent; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + ngtcp2_ksl_it it; + + /* As a client, we would like to discard Initial packet number space + when sending the first Handshake packet. When sending Handshake + packet, it should be one of 1) sending ACK, 2) sending PTO probe + packet, or 3) sending CRYPTO. If we have pending acknowledgement + for Initial, then do not discard Initial packet number space. + Otherwise, if either 1) or 2) is satisfied, discard Initial + packet number space. When sending Handshake CRYPTO, it indicates + that client has received Handshake CRYPTO from server. Initial + packet number space is discarded because 1) is met. If there is + pending Initial ACK, Initial packet number space is discarded + after writing the first Handshake packet. + */ + if (!conn->server && conn->hs_pktns->crypto.tx.ckm && conn->in_pktns && + !ngtcp2_acktr_require_active_ack(&conn->in_pktns->acktr, + /* max_ack_delay = */ 0, ts) && + (ngtcp2_acktr_require_active_ack(&conn->hs_pktns->acktr, + /* max_ack_delay = */ 0, ts) || + conn->hs_pktns->rtb.probe_pkt_left)) { + /* Discard Initial state here so that Handshake packet is not + padded. */ + conn_discard_initial_state(conn, ts); + } else if (conn->in_pktns) { + if (conn->server) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + prev_pkt_num = rtbent->hd.pkt_num; + } + } + + nwrite = + conn_write_handshake_pkt(conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, + NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + if (nwrite == 0) { + if (conn->server && (conn->in_pktns->rtb.probe_pkt_left || + ngtcp2_ksl_len(&conn->in_pktns->crypto.tx.frq))) { + return 0; + } + } else { + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + + if (conn->server && destlen) { + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + if (!ngtcp2_ksl_it_end(&it)) { + rtbent = ngtcp2_ksl_it_get(&it); + if (rtbent->hd.pkt_num != prev_pkt_num && + (rtbent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + /* We might have already added padding to Initial, but in + that case, we should have destlen == 0 and no Handshake + packet will be written. */ + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + } + } + } + } + + nwrite = conn_write_handshake_pkt(conn, pi, dest, destlen, + NGTCP2_PKT_HANDSHAKE, wflags, 0, ts); + if (nwrite < 0) { + assert(nwrite != NGTCP2_ERR_NOBUF); + return nwrite; + } + + res += nwrite; + + if (!conn->server && conn->hs_pktns->crypto.tx.ckm && nwrite) { + /* We don't need to send further Initial packet if we have + Handshake key and sent something with it. So discard initial + state here. */ + conn_discard_initial_state(conn, ts); + } + + return res; +} + +/* + * conn_initial_stream_rx_offset returns the initial maximum offset of + * data for a stream denoted by |stream_id|. + */ +static uint64_t conn_initial_stream_rx_offset(ngtcp2_conn *conn, + int64_t stream_id) { + int local_stream = conn_local_stream(conn, stream_id); + + if (bidi_stream(stream_id)) { + if (local_stream) { + return conn->local.transport_params.initial_max_stream_data_bidi_local; + } + return conn->local.transport_params.initial_max_stream_data_bidi_remote; + } + + if (local_stream) { + return 0; + } + return conn->local.transport_params.initial_max_stream_data_uni; +} + +/* + * conn_should_send_max_stream_data returns nonzero if MAX_STREAM_DATA + * frame should be send for |strm|. + */ +static int conn_should_send_max_stream_data(ngtcp2_conn *conn, + ngtcp2_strm *strm) { + uint64_t inc = strm->rx.unsent_max_offset - strm->rx.max_offset; + (void)conn; + + return strm->rx.window < 2 * inc; +} + +/* + * conn_should_send_max_data returns nonzero if MAX_DATA frame should + * be sent. + */ +static int conn_should_send_max_data(ngtcp2_conn *conn) { + uint64_t inc = conn->rx.unsent_max_offset - conn->rx.max_offset; + + return conn->rx.window < 2 * inc; +} + +/* + * conn_required_num_new_connection_id returns the number of + * additional connection ID the local endpoint has to provide to the + * remote endpoint. + */ +static size_t conn_required_num_new_connection_id(ngtcp2_conn *conn) { + uint64_t n; + size_t len = ngtcp2_ksl_len(&conn->scid.set); + + if (len >= NGTCP2_MAX_SCID_POOL_SIZE) { + return 0; + } + + assert(conn->remote.transport_params.active_connection_id_limit); + + /* len includes retired CID. We don't provide extra CID if doing so + exceeds NGTCP2_MAX_SCID_POOL_SIZE. */ + + n = conn->remote.transport_params.active_connection_id_limit + + conn->scid.num_retired; + + return (size_t)ngtcp2_min(NGTCP2_MAX_SCID_POOL_SIZE, n) - len; +} + +/* + * conn_enqueue_new_connection_id generates additional connection IDs + * and prepares to send them to the remote endpoint. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_enqueue_new_connection_id(ngtcp2_conn *conn) { + size_t i, need = conn_required_num_new_connection_id(conn); + size_t cidlen = conn->oscid.datalen; + ngtcp2_cid cid; + uint64_t seq; + int rv; + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + ngtcp2_frame_chain *nfrc; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_scid *scid; + ngtcp2_ksl_it it; + + for (i = 0; i < need; ++i) { + rv = conn_call_get_new_connection_id(conn, &cid, token, cidlen); + if (rv != 0) { + return rv; + } + + if (cid.datalen != cidlen) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + /* Assert uniqueness */ + it = ngtcp2_ksl_lower_bound(&conn->scid.set, &cid); + if (!ngtcp2_ksl_it_end(&it) && + ngtcp2_cid_eq(ngtcp2_ksl_it_key(&it), &cid)) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + seq = ++conn->scid.last_seq; + + scid = ngtcp2_mem_malloc(conn->mem, sizeof(*scid)); + if (scid == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_scid_init(scid, seq, &cid, token); + + rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scid->cid, scid); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, scid); + return rv; + } + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + nfrc->fr.new_connection_id.seq = seq; + nfrc->fr.new_connection_id.retire_prior_to = 0; + nfrc->fr.new_connection_id.cid = cid; + memcpy(nfrc->fr.new_connection_id.stateless_reset_token, token, + sizeof(token)); + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + } + + return 0; +} + +/* + * conn_remove_retired_connection_id removes the already retired + * connection ID. It waits PTO before actually removing a connection + * ID after it receives RETIRE_CONNECTION_ID from peer to catch + * reordered packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_remove_retired_connection_id(ngtcp2_conn *conn, + ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_duration timeout = pto; + ngtcp2_scid *scid; + ngtcp2_dcid *dcid; + int rv; + + for (; !ngtcp2_pq_empty(&conn->scid.used);) { + scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); + + if (scid->ts_retired == UINT64_MAX || scid->ts_retired + timeout >= ts) { + break; + } + + assert(scid->flags & NGTCP2_SCID_FLAG_RETIRED); + + rv = conn_call_remove_connection_id(conn, &scid->cid); + if (rv != 0) { + return rv; + } + + ngtcp2_ksl_remove(&conn->scid.set, NULL, &scid->cid); + ngtcp2_pq_pop(&conn->scid.used); + ngtcp2_mem_free(conn->mem, scid); + + assert(conn->scid.num_retired); + --conn->scid.num_retired; + } + + for (; ngtcp2_ringbuf_len(&conn->dcid.retired);) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); + if (dcid->ts_retired + timeout >= ts) { + break; + } + + rv = conn_call_deactivate_dcid(conn, dcid); + if (rv != 0) { + return rv; + } + + ngtcp2_ringbuf_pop_front(&conn->dcid.retired); + } + + return 0; +} + +/* + * conn_min_short_pktlen returns the minimum length of Short packet + * this endpoint sends. + */ +static size_t conn_min_short_pktlen(ngtcp2_conn *conn) { + return conn->dcid.current.cid.datalen + NGTCP2_MIN_PKT_EXPANDLEN; +} + +/* + * conn_write_pkt writes a protected packet in the buffer pointed by + * |dest| whose length if |destlen|. |type| specifies the type of + * packet. It can be NGTCP2_PKT_SHORT or NGTCP2_PKT_0RTT. + * + * This function can send new stream data. In order to send stream + * data, specify the underlying stream and parameters to + * |vmsg|->stream. If |vmsg|->stream.fin is set to nonzero, it + * signals that the given data is the final portion of the stream. + * |vmsg|->stream.data vector of length |vmsg|->stream.datacnt + * specifies stream data to send. The number of bytes sent to the + * stream is assigned to *|vmsg|->stream.pdatalen. If 0 length STREAM + * data is sent, 0 is assigned to it. The caller should initialize + * *|vmsg|->stream.pdatalen to -1. + * + * If |require_padding| is nonzero, padding bytes are added to occupy + * the remaining packet payload. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_STREAM_DATA_BLOCKED + * Stream data could not be written because of flow control. + */ +static ngtcp2_ssize conn_write_pkt(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_vmsg *vmsg, uint8_t type, + uint8_t flags, ngtcp2_tstamp ts) { + int rv = 0; + ngtcp2_crypto_cc *cc = &conn->pkt.cc; + ngtcp2_ppe *ppe = &conn->pkt.ppe; + ngtcp2_pkt_hd *hd = &conn->pkt.hd; + ngtcp2_frame *ackfr = NULL, lfr; + ngtcp2_ssize nwrite; + ngtcp2_frame_chain **pfrc, *nfrc, *frc; + ngtcp2_rtb_entry *ent; + ngtcp2_strm *strm; + int pkt_empty = 1; + size_t ndatalen = 0; + int send_stream = 0; + int stream_blocked = 0; + int send_datagram = 0; + ngtcp2_pktns *pktns = &conn->pktns; + size_t left; + size_t datalen = 0; + ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT]; + size_t datacnt; + uint8_t rtb_entry_flags = NGTCP2_RTB_ENTRY_FLAG_NONE; + int hd_logged = 0; + ngtcp2_path_challenge_entry *pcent; + uint8_t hd_flags; + int require_padding = (flags & NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING) != 0; + int write_more = (flags & NGTCP2_WRITE_PKT_FLAG_MORE) != 0; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + size_t min_pktlen = conn_min_short_pktlen(conn); + int padded = 0; + int credit_expanded = 0; + ngtcp2_cc_pkt cc_pkt; + uint64_t crypto_offset; + uint64_t stream_offset; + ngtcp2_ssize num_reclaimed; + int fin; + uint64_t target_max_data; + ngtcp2_conn_stat *cstat = &conn->cstat; + uint64_t delta; + + /* Return 0 if destlen is less than minimum packet length which can + trigger Stateless Reset */ + if (destlen < min_pktlen) { + return 0; + } + + if (vmsg) { + switch (vmsg->type) { + case NGTCP2_VMSG_TYPE_STREAM: + datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); + ndatalen = conn_enforce_flow_control(conn, vmsg->stream.strm, datalen); + /* 0 length STREAM frame is allowed */ + if (ndatalen || datalen == 0) { + send_stream = 1; + } else { + stream_blocked = 1; + } + break; + case NGTCP2_VMSG_TYPE_DATAGRAM: + datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt); + send_datagram = 1; + break; + default: + break; + } + } + + if (!ppe_pending) { + switch (type) { + case NGTCP2_PKT_SHORT: + hd_flags = + (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE; + cc->aead = pktns->crypto.ctx.aead; + cc->hp = pktns->crypto.ctx.hp; + cc->ckm = pktns->crypto.tx.ckm; + cc->hp_ctx = pktns->crypto.tx.hp_ctx; + + /* transport parameter is only valid after handshake completion + which means we don't know how many connection ID that remote + peer can accept before handshake completion. */ + if (conn->oscid.datalen && + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + rv = conn_enqueue_new_connection_id(conn); + if (rv != 0) { + return rv; + } + } + + break; + case NGTCP2_PKT_0RTT: + assert(!conn->server); + if (!conn->early.ckm) { + return 0; + } + hd_flags = NGTCP2_PKT_FLAG_LONG_FORM; + cc->aead = conn->early.ctx.aead; + cc->hp = conn->early.ctx.hp; + cc->ckm = conn->early.ckm; + cc->hp_ctx = conn->early.hp_ctx; + break; + default: + /* Unreachable */ + assert(0); + } + + cc->encrypt = conn->callbacks.encrypt; + cc->hp_mask = conn->callbacks.hp_mask; + + if (conn_should_send_max_data(conn)) { + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + if (conn->local.settings.max_window && + conn->tx.last_max_data_ts != UINT64_MAX && + ts - conn->tx.last_max_data_ts < + NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt && + conn->local.settings.max_window > conn->rx.window) { + target_max_data = NGTCP2_FLOW_WINDOW_SCALING_FACTOR * conn->rx.window; + if (target_max_data > conn->local.settings.max_window) { + target_max_data = conn->local.settings.max_window; + } + + delta = target_max_data - conn->rx.window; + if (conn->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) { + delta = NGTCP2_MAX_VARINT - conn->rx.unsent_max_offset; + } + + conn->rx.window = target_max_data; + } else { + delta = 0; + } + + conn->tx.last_max_data_ts = ts; + + nfrc->fr.type = NGTCP2_FRAME_MAX_DATA; + nfrc->fr.max_data.max_data = conn->rx.unsent_max_offset + delta; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + conn->rx.max_offset = conn->rx.unsent_max_offset = + nfrc->fr.max_data.max_data; + credit_expanded = 1; + } + + ngtcp2_pkt_hd_init(hd, hd_flags, type, &conn->dcid.current.cid, + &conn->oscid, pktns->tx.last_pkt_num + 1, + pktns_select_pkt_numlen(pktns), conn->version, 0); + + ngtcp2_ppe_init(ppe, dest, destlen, cc); + + rv = ngtcp2_ppe_encode_hd(ppe, hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(ppe)) { + return 0; + } + + if (ngtcp2_ringbuf_len(&conn->rx.path_challenge)) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + + /* PATH_RESPONSE is bound to the path that the corresponding + PATH_CHALLENGE is received. */ + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { + lfr.type = NGTCP2_FRAME_PATH_RESPONSE; + memcpy(lfr.path_response.data, pcent->data, + sizeof(lfr.path_response.data)); + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + require_padding = !conn->server || destlen >= 1200; + /* We don't retransmit PATH_RESPONSE. */ + } + } + } + + rv = conn_create_ack_frame(conn, &ackfr, pktns, 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) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, + ackfr->ack.largest_ack); + pkt_empty = 0; + } + } + + build_pkt: + for (pfrc = &pktns->tx.frq; *pfrc;) { + if ((*pfrc)->binder && + ((*pfrc)->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + + switch ((*pfrc)->fr.type) { + case NGTCP2_FRAME_STOP_SENDING: + strm = + ngtcp2_conn_find_stream(conn, (*pfrc)->fr.stop_sending.stream_id); + if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_STREAM: + assert(0); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + if ((*pfrc)->fr.max_streams.max_streams < + conn->remote.bidi.max_streams) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_STREAMS_UNI: + if ((*pfrc)->fr.max_streams.max_streams < + conn->remote.uni.max_streams) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + strm = ngtcp2_conn_find_stream(conn, + (*pfrc)->fr.max_stream_data.stream_id); + if (strm == NULL || (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) || + (*pfrc)->fr.max_stream_data.max_stream_data < strm->rx.max_offset) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_MAX_DATA: + if ((*pfrc)->fr.max_data.max_data < conn->rx.max_offset) { + frc = *pfrc; + *pfrc = (*pfrc)->next; + ngtcp2_frame_chain_del(frc, conn->mem); + continue; + } + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + ++conn->dcid.num_retire_queued; + break; + case NGTCP2_FRAME_CRYPTO: + assert(0); + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + break; + } + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + pfrc = &(*pfrc)->next; + } + + if (rv != NGTCP2_ERR_NOBUF) { + for (; ngtcp2_ksl_len(&pktns->crypto.tx.frq);) { + left = ngtcp2_ppe_left(ppe); + + crypto_offset = conn_cryptofrq_unacked_offset(conn, pktns); + if (crypto_offset == (size_t)-1) { + conn_cryptofrq_clear(conn, pktns); + break; + } + + left = ngtcp2_pkt_crypto_max_datalen(crypto_offset, left, left); + + if (left == (size_t)-1) { + break; + } + + rv = conn_cryptofrq_pop(conn, &nfrc, pktns, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (nfrc == NULL) { + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + } + } + + /* Write MAX_STREAM_ID after RESET_STREAM so that we can extend stream + ID space in one packet. */ + if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL && + conn->remote.bidi.unsent_max_streams > conn->remote.bidi.max_streams) { + rv = conn_call_extend_max_remote_streams_bidi( + conn, conn->remote.bidi.unsent_max_streams); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; + nfrc->fr.max_streams.max_streams = conn->remote.bidi.unsent_max_streams; + *pfrc = nfrc; + + conn->remote.bidi.max_streams = conn->remote.bidi.unsent_max_streams; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + pfrc = &(*pfrc)->next; + } + } + + if (rv != NGTCP2_ERR_NOBUF && *pfrc == NULL) { + if (conn->remote.uni.unsent_max_streams > conn->remote.uni.max_streams) { + rv = conn_call_extend_max_remote_streams_uni( + conn, conn->remote.uni.unsent_max_streams); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI; + nfrc->fr.max_streams.max_streams = conn->remote.uni.unsent_max_streams; + *pfrc = nfrc; + + conn->remote.uni.max_streams = conn->remote.uni.unsent_max_streams; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, + &(*pfrc)->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + pfrc = &(*pfrc)->next; + } + } + } + + if (rv != NGTCP2_ERR_NOBUF) { + for (; !ngtcp2_pq_empty(&conn->tx.strmq);) { + strm = ngtcp2_conn_tx_strmq_top(conn); + + if (!(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + conn_should_send_max_stream_data(conn, strm)) { + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (conn->local.settings.max_stream_window && + strm->tx.last_max_stream_data_ts != UINT64_MAX && + ts - strm->tx.last_max_stream_data_ts < + NGTCP2_FLOW_WINDOW_RTT_FACTOR * cstat->smoothed_rtt && + conn->local.settings.max_stream_window > strm->rx.window) { + target_max_data = + NGTCP2_FLOW_WINDOW_SCALING_FACTOR * strm->rx.window; + if (target_max_data > conn->local.settings.max_stream_window) { + target_max_data = conn->local.settings.max_stream_window; + } + + delta = target_max_data - strm->rx.window; + if (strm->rx.unsent_max_offset + delta > NGTCP2_MAX_VARINT) { + delta = NGTCP2_MAX_VARINT - strm->rx.unsent_max_offset; + } + + strm->rx.window = target_max_data; + } else { + delta = 0; + } + + strm->tx.last_max_stream_data_ts = ts; + + nfrc->fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + nfrc->fr.max_stream_data.stream_id = strm->stream_id; + nfrc->fr.max_stream_data.max_stream_data = + strm->rx.unsent_max_offset + delta; + ngtcp2_list_insert(nfrc, pfrc); + + rv = + conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + break; + } + + pkt_empty = 0; + credit_expanded = 1; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + pfrc = &(*pfrc)->next; + strm->rx.max_offset = strm->rx.unsent_max_offset = + nfrc->fr.max_stream_data.max_stream_data; + } + + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + stream_offset = ngtcp2_strm_streamfrq_unacked_offset(strm); + if (stream_offset == (uint64_t)-1) { + ngtcp2_strm_streamfrq_clear(strm); + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + left = ngtcp2_ppe_left(ppe); + + left = ngtcp2_pkt_stream_max_datalen(strm->stream_id, stream_offset, + left, left); + + if (left == (size_t)-1) { + break; + } + + rv = ngtcp2_strm_streamfrq_pop(strm, &nfrc, left); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + if (nfrc == NULL) { + /* TODO Why? */ + break; + } + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + + if (ngtcp2_strm_streamfrq_empty(strm)) { + ngtcp2_conn_tx_strmq_pop(conn); + continue; + } + + ngtcp2_conn_tx_strmq_pop(conn); + ++strm->cycle; + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + } + } + + /* Add ACK if MAX_DATA or MAX_STREAM_DATA frame is encoded to + decrease packet count. */ + if (ackfr == NULL && credit_expanded) { + rv = conn_create_ack_frame( + conn, &ackfr, pktns, type, ts, /* ack_delay = */ 0, + 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) { + assert(NGTCP2_ERR_NOBUF == rv); + } else { + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd->pkt_num, + ackfr->ack.largest_ack); + } + } + } + + if (rv != NGTCP2_ERR_NOBUF && !send_stream && !send_datagram && + !(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && + pktns->rtb.num_retransmittable && pktns->tx.frq == NULL && + pktns->rtb.probe_pkt_left) { + num_reclaimed = ngtcp2_rtb_reclaim_on_pto(&pktns->rtb, conn, pktns, + pktns->rtb.probe_pkt_left + 1); + if (num_reclaimed < 0) { + return rv; + } + if (num_reclaimed) { + goto build_pkt; + } + + /* We had pktns->rtb.num_retransmittable > 0 but the contents of + those packets have been acknowledged (i.e., retransmission in + another packet). In this case, we don't have to send any + probe packet. */ + if (pktns->rtb.num_retransmittable == 0) { + pktns->rtb.probe_pkt_left = 0; + ngtcp2_conn_set_loss_detection_timer(conn, ts); + } + } + } else { + pfrc = conn->pkt.pfrc; + rtb_entry_flags |= conn->pkt.rtb_entry_flags; + pkt_empty = conn->pkt.pkt_empty; + hd_logged = conn->pkt.hd_logged; + } + + left = ngtcp2_ppe_left(ppe); + + if (rv != NGTCP2_ERR_NOBUF && send_stream && *pfrc == NULL && + (ndatalen = ngtcp2_pkt_stream_max_datalen( + vmsg->stream.strm->stream_id, vmsg->stream.strm->tx.offset, ndatalen, + left)) != (size_t)-1 && + (ndatalen || datalen == 0)) { + datacnt = ngtcp2_vec_copy_at_most( + data, &ndatalen, NGTCP2_MAX_STREAM_DATACNT, vmsg->stream.data, + vmsg->stream.datacnt, ndatalen); + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, datacnt, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + nfrc->fr.stream.type = NGTCP2_FRAME_STREAM; + nfrc->fr.stream.flags = 0; + nfrc->fr.stream.stream_id = vmsg->stream.strm->stream_id; + nfrc->fr.stream.offset = vmsg->stream.strm->tx.offset; + nfrc->fr.stream.datacnt = datacnt; + ngtcp2_vec_copy(nfrc->fr.stream.data, data, datacnt); + + fin = (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_FIN) && + ndatalen == datalen; + nfrc->fr.stream.fin = (uint8_t)fin; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &nfrc->fr); + if (rv != 0) { + assert(0); + } + + *pfrc = nfrc; + pfrc = &(*pfrc)->next; + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING | + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE; + + vmsg->stream.strm->tx.offset += ndatalen; + conn->tx.offset += ndatalen; + + if (fin) { + ngtcp2_strm_shutdown(vmsg->stream.strm, NGTCP2_STRM_FLAG_SHUT_WR); + } + + if (vmsg->stream.pdatalen) { + *vmsg->stream.pdatalen = (ngtcp2_ssize)ndatalen; + } + } else { + send_stream = 0; + } + + if (rv != NGTCP2_ERR_NOBUF && send_datagram && + left >= ngtcp2_pkt_datagram_framelen(datalen)) { + lfr.datagram.type = NGTCP2_FRAME_DATAGRAM_LEN; + lfr.datagram.datacnt = vmsg->datagram.datacnt; + lfr.datagram.data = (ngtcp2_vec *)vmsg->datagram.data; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + assert(rv == 0); + + pkt_empty = 0; + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + + if (vmsg->datagram.paccepted) { + *vmsg->datagram.paccepted = 1; + } + } else { + send_datagram = 0; + } + + if (pkt_empty) { + assert(rv == 0 || NGTCP2_ERR_NOBUF == rv); + if (rv == 0 && stream_blocked && ngtcp2_conn_get_max_data_left(conn)) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + + if (conn->pktns.rtb.probe_pkt_left == 0) { + return 0; + } + } else if (write_more) { + conn->pkt.pfrc = pfrc; + conn->pkt.pkt_empty = pkt_empty; + conn->pkt.rtb_entry_flags = rtb_entry_flags; + conn->pkt.hd_logged = hd_logged; + conn->flags |= NGTCP2_CONN_FLAG_PPE_PENDING; + + assert(vmsg); + + switch (vmsg->type) { + case NGTCP2_VMSG_TYPE_STREAM: + if (send_stream) { + if (ngtcp2_ppe_left(ppe)) { + return NGTCP2_ERR_WRITE_MORE; + } + } else if (ngtcp2_conn_get_max_data_left(conn) && stream_blocked) { + return NGTCP2_ERR_STREAM_DATA_BLOCKED; + } + break; + case NGTCP2_VMSG_TYPE_DATAGRAM: + if (send_datagram && ngtcp2_ppe_left(ppe)) { + return NGTCP2_ERR_WRITE_MORE; + } + /* If DATAGRAM cannot be written due to insufficient space, + continue to create a packet with the hope that application + calls ngtcp2_conn_writev_datagram again. */ + break; + default: + assert(0); + } + } + + if (!(rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + if (pktns->tx.num_non_ack_pkt >= NGTCP2_MAX_NON_ACK_TX_PKT) { + lfr.type = NGTCP2_FRAME_PING; + + rv = conn_ppe_write_frame_hd_log(conn, ppe, &hd_logged, hd, &lfr); + if (rv != 0) { + assert(rv == NGTCP2_ERR_NOBUF); + /* TODO If buffer is too small, PING cannot be written if + packet is still empty. */ + } else { + rtb_entry_flags |= NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING; + pktns->tx.num_non_ack_pkt = 0; + } + } else { + ++pktns->tx.num_non_ack_pkt; + } + } else { + pktns->tx.num_non_ack_pkt = 0; + } + + /* TODO Push STREAM frame back to ngtcp2_strm if there is an error + before ngtcp2_rtb_entry is safely created and added. */ + lfr.type = NGTCP2_FRAME_PADDING; + if ((require_padding || + /* Making full sized packet will help GSO a bit */ + ngtcp2_ppe_left(ppe) < 10 || + (type == NGTCP2_PKT_0RTT && conn->state == NGTCP2_CS_CLIENT_INITIAL)) && + ngtcp2_ppe_left(ppe)) { + lfr.padding.len = ngtcp2_ppe_padding(ppe); + } else { + lfr.padding.len = ngtcp2_ppe_padding_size(ppe, min_pktlen); + } + + if (lfr.padding.len) { + padded = 1; + ngtcp2_log_tx_fr(&conn->log, hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + nwrite = ngtcp2_ppe_final(ppe, NULL); + if (nwrite < 0) { + assert(ngtcp2_err_is_fatal((int)nwrite)); + return nwrite; + } + + ++cc->ckm->use_count; + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, hd, (size_t)nwrite); + + /* TODO ack-eliciting vs needs-tracking */ + /* probe packet needs tracking but it does not need ACK, could be lost. */ + if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) { + if (pi) { + conn_handle_tx_ecn(conn, pi, &rtb_entry_flags, pktns, hd, ts); + } + + rv = ngtcp2_rtb_entry_new(&ent, hd, NULL, ts, (size_t)nwrite, + rtb_entry_flags, conn->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal((int)nwrite)); + return rv; + } + + if (*pfrc != pktns->tx.frq) { + ent->frc = pktns->tx.frq; + pktns->tx.frq = *pfrc; + *pfrc = NULL; + } + + if ((rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && + pktns->rtb.num_ack_eliciting == 0 && conn->cc.event) { + conn->cc.event(&conn->cc, &conn->cstat, NGTCP2_CC_EVENT_TYPE_TX_START, + ts); + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, ent); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_rtb_entry_del(ent, conn->mem); + return rv; + } + + if (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + if (conn->cc.on_pkt_sent) { + conn->cc.on_pkt_sent( + &conn->cc, &conn->cstat, + ngtcp2_cc_pkt_init(&cc_pkt, hd->pkt_num, (size_t)nwrite, + NGTCP2_PKTNS_ID_APPLICATION, ts)); + } + + if (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE) { + conn_restart_timer_on_write(conn, ts); + } + } + } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { + conn_handle_tx_ecn(conn, pi, NULL, pktns, hd, ts); + } + + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_PPE_PENDING; + + if (pktns->rtb.probe_pkt_left && + (rtb_entry_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + --pktns->rtb.probe_pkt_left; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "probe pkt size=%td", + nwrite); + } + + conn->dcid.current.bytes_sent += (uint64_t)nwrite; + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + + ++pktns->tx.last_pkt_num; + + return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( + ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, + const ngtcp2_path *path, ngtcp2_tstamp ts) { + int rv; + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + ngtcp2_frame lfr; + ngtcp2_ssize nwrite; + ngtcp2_crypto_cc cc; + ngtcp2_pktns *pktns; + uint8_t flags; + ngtcp2_rtb_entry *rtbent; + int padded = 0; + + switch (type) { + case NGTCP2_PKT_INITIAL: + pktns = conn->in_pktns; + flags = NGTCP2_PKT_FLAG_LONG_FORM; + break; + case NGTCP2_PKT_HANDSHAKE: + pktns = conn->hs_pktns; + flags = NGTCP2_PKT_FLAG_LONG_FORM; + break; + case NGTCP2_PKT_SHORT: + /* 0 means Short packet. */ + pktns = &conn->pktns; + flags = (pktns->crypto.tx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) + ? NGTCP2_PKT_FLAG_KEY_PHASE + : NGTCP2_PKT_FLAG_NONE; + break; + default: + /* We don't support 0-RTT packet in this function. */ + assert(0); + } + + cc.aead = pktns->crypto.ctx.aead; + cc.hp = pktns->crypto.ctx.hp; + cc.ckm = pktns->crypto.tx.ckm; + cc.hp_ctx = pktns->crypto.tx.hp_ctx; + cc.encrypt = conn->callbacks.encrypt; + cc.hp_mask = conn->callbacks.hp_mask; + + ngtcp2_pkt_hd_init(&hd, flags, type, dcid, &conn->oscid, + pktns->tx.last_pkt_num + 1, pktns_select_pkt_numlen(pktns), + conn->version, 0); + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return 0; + } + + ngtcp2_log_tx_pkt_hd(&conn->log, &hd); + ngtcp2_qlog_pkt_sent_start(&conn->qlog); + + rv = conn_ppe_write_frame(conn, &ppe, &hd, fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return 0; + } + + lfr.type = NGTCP2_FRAME_PADDING; + switch (fr->type) { + case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_PATH_RESPONSE: + if (!conn->server || destlen >= 1200) { + lfr.padding.len = ngtcp2_ppe_padding(&ppe); + } else { + lfr.padding.len = 0; + } + break; + default: + if (type == NGTCP2_PKT_SHORT) { + lfr.padding.len = + ngtcp2_ppe_padding_size(&ppe, conn_min_short_pktlen(conn)); + } else { + lfr.padding.len = ngtcp2_ppe_padding_hp_sample(&ppe); + } + } + if (lfr.padding.len) { + padded = 1; + ngtcp2_log_tx_fr(&conn->log, &hd, &lfr); + ngtcp2_qlog_write_frame(&conn->qlog, &lfr); + } + + nwrite = ngtcp2_ppe_final(&ppe, NULL); + if (nwrite < 0) { + return nwrite; + } + + if (type == NGTCP2_PKT_SHORT) { + ++cc.ckm->use_count; + } + + ngtcp2_qlog_pkt_sent_end(&conn->qlog, &hd, (size_t)nwrite); + + /* Do this when we are sure that there is no error. */ + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + ngtcp2_acktr_commit_ack(&pktns->acktr); + ngtcp2_acktr_add_ack(&pktns->acktr, hd.pkt_num, fr->ack.largest_ack); + break; + } + + if (((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) || padded) && + (!path || ngtcp2_path_eq(&conn->dcid.current.ps.path, path))) { + if (pi) { + conn_handle_tx_ecn(conn, pi, &rtb_flags, pktns, &hd, ts); + } + + rv = ngtcp2_rtb_entry_new(&rtbent, &hd, NULL, ts, (size_t)nwrite, rtb_flags, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = conn_on_pkt_sent(conn, &pktns->rtb, rtbent); + if (rv != 0) { + ngtcp2_rtb_entry_del(rtbent, conn->mem); + return rv; + } + + if ((rtb_flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) && + (conn->flags & NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE)) { + conn_restart_timer_on_write(conn, ts); + } + } else if (pi && conn->tx.ecn.state == NGTCP2_ECN_STATE_CAPABLE) { + conn_handle_tx_ecn(conn, pi, NULL, pktns, &hd, ts); + } + + ngtcp2_qlog_metrics_updated(&conn->qlog, &conn->cstat); + + ++pktns->tx.last_pkt_num; + + return nwrite; +} + +/* + * conn_process_early_rtb makes any pending 0RTT packet Short packet. + */ +static void conn_process_early_rtb(ngtcp2_conn *conn) { + ngtcp2_rtb_entry *ent; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + ngtcp2_ksl_it it; + + for (it = ngtcp2_rtb_head(rtb); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + if ((ent->hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) == 0 || + ent->hd.type != NGTCP2_PKT_0RTT) { + continue; + } + + /* 0-RTT packet is retransmitted as a Short packet. */ + ent->hd.flags &= (uint8_t)~NGTCP2_PKT_FLAG_LONG_FORM; + ent->hd.type = NGTCP2_PKT_SHORT; + } +} + +/* + * conn_handshake_remnants_left returns nonzero if there may be + * handshake packets the local endpoint has to send, including new + * packets and lost ones. + */ +static int conn_handshake_remnants_left(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + + return !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + (in_pktns && (in_pktns->rtb.num_retransmittable || + ngtcp2_ksl_len(&in_pktns->crypto.tx.frq))) || + (hs_pktns && (hs_pktns->rtb.num_retransmittable || + ngtcp2_ksl_len(&hs_pktns->crypto.tx.frq))); +} + +/* + * conn_retire_dcid_seq retires destination connection ID denoted by + * |seq|. + * + * 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_dcid_seq(ngtcp2_conn *conn, uint64_t seq) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_frame_chain *nfrc; + int rv; + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + nfrc->fr.retire_connection_id.seq = seq; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + return 0; +} + +/* + * conn_retire_dcid retires |dcid|. + * + * 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_dcid(ngtcp2_conn *conn, const ngtcp2_dcid *dcid, + ngtcp2_tstamp ts) { + ngtcp2_ringbuf *rb = &conn->dcid.retired; + ngtcp2_dcid *dest, *stale_dcid; + 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); + } + + dest = ngtcp2_ringbuf_push_back(rb); + ngtcp2_dcid_copy(dest, dcid); + dest->ts_retired = ts; + + return conn_retire_dcid_seq(conn, dcid->seq); +} + +/* + * conn_bind_dcid stores the DCID to |*pdcid| bound to |path|. If + * such DCID is not found, bind the new DCID to |path| and stores it + * to |*pdcid|. If a remote endpoint uses zero-length connection ID, + * the pointer to conn->dcid.current is assigned to |*pdcid|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONN_ID_BLOCKED + * No unused DCID is available + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_bind_dcid(ngtcp2_conn *conn, ngtcp2_dcid **pdcid, + const ngtcp2_path *path, ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_dcid *dcid, *ndcid; + ngtcp2_cid cid; + size_t i, len; + int rv; + + assert(!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)); + assert(!pv || !ngtcp2_path_eq(&pv->dcid.ps.path, path)); + assert(!pv || !(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) || + !ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)); + + len = ngtcp2_ringbuf_len(&conn->dcid.bound); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + + if (ngtcp2_path_eq(&dcid->ps.path, path)) { + *pdcid = dcid; + return 0; + } + } + + if (conn->dcid.current.cid.datalen == 0) { + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + ngtcp2_cid_zero(&cid); + ngtcp2_dcid_init(ndcid, ++conn->dcid.zerolen_seq, &cid, NULL); + ngtcp2_path_copy(&ndcid->ps.path, path); + + *pdcid = ndcid; + + return 0; + } + + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return NGTCP2_ERR_CONN_ID_BLOCKED; + } + + if (ngtcp2_ringbuf_full(&conn->dcid.bound)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 0); + rv = conn_retire_dcid(conn, dcid, ts); + if (rv != 0) { + return rv; + } + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ndcid = ngtcp2_ringbuf_push_back(&conn->dcid.bound); + + ngtcp2_dcid_copy(ndcid, dcid); + ngtcp2_path_copy(&ndcid->ps.path, path); + + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + *pdcid = ndcid; + + return 0; +} + +/* + * conn_stop_pv stops the path validation which is currently running. + * This function does nothing if no path validation is currently being + * performed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_stop_pv(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv = 0; + ngtcp2_pv *pv = conn->pv; + + if (pv == NULL) { + return 0; + } + + if (pv->dcid.cid.datalen && pv->dcid.seq != conn->dcid.current.seq) { + rv = conn_retire_dcid(conn, &pv->dcid, ts); + if (rv != 0) { + goto fin; + } + } + + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + 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); + if (rv != 0) { + goto fin; + } + } + +fin: + ngtcp2_pv_del(pv); + conn->pv = NULL; + + return rv; +} + +static void conn_reset_congestion_state(ngtcp2_conn *conn); + +/* + * conn_on_path_validation_failed is called when path validation + * fails. This function may delete |pv|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_on_path_validation_failed(ngtcp2_conn *conn, ngtcp2_pv *pv, + ngtcp2_tstamp ts) { + int rv; + + rv = conn_call_path_validation(conn, &pv->dcid.ps.path, + NGTCP2_PATH_VALIDATION_RESULT_FAILURE); + if (rv != 0) { + return rv; + } + + if (pv->flags & NGTCP2_PV_FLAG_MTU_PROBE) { + return NGTCP2_ERR_NO_VIABLE_PATH; + } + + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + ngtcp2_dcid_copy(&conn->dcid.current, &pv->fallback_dcid); + conn_reset_congestion_state(conn); + } + + return conn_stop_pv(conn, ts); +} + +/* + * dcid_tx_left returns the maximum number of bytes that server is + * allowed to send to an unvalidated path associated to |dcid|. + */ +static size_t dcid_tx_left(ngtcp2_dcid *dcid) { + if (dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + return SIZE_MAX; + } + /* From QUIC spec: Prior to validating the client address, servers + MUST NOT send more than three times as many bytes as the number + of bytes they have received. */ + assert(dcid->bytes_recv * 3 >= dcid->bytes_sent); + + return dcid->bytes_recv * 3 - dcid->bytes_sent; +} + +/* + * conn_server_tx_left returns the maximum number of bytes that server + * is allowed to send to an unvalidated path. + */ +static size_t conn_server_tx_left(ngtcp2_conn *conn, ngtcp2_dcid *dcid) { + assert(conn->server); + + /* If pv->dcid has the current path, use conn->dcid.current. This + is because conn->dcid.current gets update for bytes_recv and + bytes_sent. */ + if (ngtcp2_path_eq(&dcid->ps.path, &conn->dcid.current.ps.path)) { + return dcid_tx_left(&conn->dcid.current); + } + + return dcid_tx_left(dcid); +} + +/* + * conn_write_path_challenge writes a packet which includes + * PATH_CHALLENGE frame into |dest| of length |destlen|. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_path_challenge(ngtcp2_conn *conn, + ngtcp2_path *path, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_ssize nwrite; + ngtcp2_tstamp expiry; + ngtcp2_pv *pv = conn->pv; + ngtcp2_frame lfr; + ngtcp2_duration timeout; + uint8_t flags; + size_t tx_left; + + if (ngtcp2_pv_validation_timed_out(pv, ts)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, + "path validation was timed out"); + return conn_on_path_validation_failed(conn, pv, ts); + } + + ngtcp2_pv_handle_entry_expiry(pv, ts); + + if (!ngtcp2_pv_should_send_probe(pv)) { + return 0; + } + + assert(conn->callbacks.rand); + rv = conn->callbacks.rand( + lfr.path_challenge.data, sizeof(lfr.path_challenge.data), + &conn->local.settings.rand_ctx, NGTCP2_RAND_USAGE_PATH_CHALLENGE); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + lfr.type = NGTCP2_FRAME_PATH_CHALLENGE; + + timeout = conn_compute_pto(conn, &conn->pktns); + timeout = ngtcp2_max(timeout, 3 * conn->cstat.initial_rtt); + expiry = ts + timeout * (1ULL << pv->round); + + if (conn->server) { + if (!(pv->dcid.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { + tx_left = conn_server_tx_left(conn, &pv->dcid); + destlen = ngtcp2_min(destlen, tx_left); + if (destlen == 0) { + return 0; + } + } + + if (destlen < 1200) { + flags = NGTCP2_PV_ENTRY_FLAG_UNDERSIZED; + } else { + flags = NGTCP2_PV_ENTRY_FLAG_NONE; + } + } else { + flags = NGTCP2_PV_ENTRY_FLAG_NONE; + } + + ngtcp2_pv_add_entry(pv, lfr.path_challenge.data, expiry, flags, ts); + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &pv->dcid.cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pv->dcid.ps.path, ts); + if (nwrite <= 0) { + return nwrite; + } + + if (path) { + ngtcp2_path_copy(path, &pv->dcid.ps.path); + } + + if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) { + conn->dcid.current.bytes_sent += (uint64_t)nwrite; + } else { + pv->dcid.bytes_sent += (uint64_t)nwrite; + } + + return nwrite; +} + +/* + * conn_write_path_response writes a packet which includes + * PATH_RESPONSE frame into |dest| of length |destlen|. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static ngtcp2_ssize conn_write_path_response(ngtcp2_conn *conn, + ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_path_challenge_entry *pcent = NULL; + ngtcp2_dcid *dcid = NULL; + ngtcp2_frame lfr; + ngtcp2_ssize nwrite; + int rv; + size_t tx_left; + + for (; ngtcp2_ringbuf_len(&conn->rx.path_challenge);) { + pcent = ngtcp2_ringbuf_get(&conn->rx.path_challenge, 0); + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, &pcent->ps.path)) { + /* Send PATH_RESPONSE from conn_write_pkt. */ + return 0; + } + + if (pv) { + if (ngtcp2_path_eq(&pv->dcid.ps.path, &pcent->ps.path)) { + dcid = &pv->dcid; + break; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + ngtcp2_path_eq(&pv->fallback_dcid.ps.path, &pcent->ps.path)) { + dcid = &pv->fallback_dcid; + break; + } + } + + if (conn->server) { + break; + } + + /* Client does not expect to respond to path validation against + unknown path */ + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + pcent = NULL; + } + + if (pcent == NULL) { + return 0; + } + + if (dcid == NULL) { + /* client is expected to have |path| in conn->dcid.current or + conn->pv. */ + assert(conn->server); + + rv = conn_bind_dcid(conn, &dcid, &pcent->ps.path, ts); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + return 0; + } + } + + if (conn->server && !(dcid->flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { + tx_left = conn_server_tx_left(conn, dcid); + destlen = ngtcp2_min(destlen, tx_left); + if (destlen == 0) { + return 0; + } + } + + lfr.type = NGTCP2_FRAME_PATH_RESPONSE; + memcpy(lfr.path_response.data, pcent->data, sizeof(lfr.path_response.data)); + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &dcid->cid, &lfr, + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING, &pcent->ps.path, ts); + if (nwrite <= 0) { + return nwrite; + } + + if (path) { + ngtcp2_path_copy(path, &pcent->ps.path); + } + + ngtcp2_ringbuf_pop_front(&conn->rx.path_challenge); + + dcid->bytes_sent += (uint64_t)nwrite; + + return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_pkt(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_tstamp ts) { + return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, + /* pdatalen = */ NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, + /* stream_id = */ -1, + /* datav = */ NULL, /* datavcnt = */ 0, ts); +} + +/* + * conn_on_version_negotiation is called when Version Negotiation + * packet is received. The function decodes the data in the buffer + * pointed by |payload| whose length is |payloadlen| as Version + * Negotiation packet payload. The packet header is given in |hd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_INVALID_ARGUMENT + * Packet payload is badly formatted. + */ +static int conn_on_version_negotiation(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const uint8_t *payload, + size_t payloadlen) { + uint32_t sv[16]; + uint32_t *p; + int rv = 0; + size_t nsv; + size_t i; + + if (payloadlen % sizeof(uint32_t)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (payloadlen > sizeof(sv)) { + p = ngtcp2_mem_malloc(conn->mem, payloadlen); + if (p == NULL) { + return NGTCP2_ERR_NOMEM; + } + } else { + p = sv; + } + + nsv = ngtcp2_pkt_decode_version_negotiation(p, payload, payloadlen); + + ngtcp2_log_rx_vn(&conn->log, hd, p, nsv); + + for (i = 0; i < nsv; ++i) { + if (p[i] == conn->version) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "ignore Version Negotiation because it contains version " + "selected by client"); + + rv = NGTCP2_ERR_INVALID_ARGUMENT; + goto fin; + } + } + + if (conn->callbacks.recv_version_negotiation) { + rv = conn->callbacks.recv_version_negotiation(conn, hd, p, nsv, + conn->user_data); + if (rv != 0) { + rv = NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + if (rv == 0) { + /* TODO Just move to the terminal state for now in order not to + send CONNECTION_CLOSE frame. */ + conn->state = NGTCP2_CS_DRAINING; + } + +fin: + if (p != sv) { + ngtcp2_mem_free(conn->mem, p); + } + + return rv; +} + +static uint64_t conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { + ngtcp2_strm *strm; + + if (ngtcp2_pq_empty(&conn->tx.strmq)) { + return 0; + } + + strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); + return strm->cycle; +} + +uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn) { + ngtcp2_strm *strm; + + if (ngtcp2_pq_empty(&conn->tx.strmq)) { + return 0; + } + + strm = ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); + return strm->cycle; +} + +int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_frame_chain **pfrc) { + ngtcp2_frame_chain **first = pfrc; + ngtcp2_frame_chain *frc; + ngtcp2_stream *sfr; + ngtcp2_strm *strm; + int rv; + + if (*pfrc == NULL) { + return 0; + } + + for (; *pfrc;) { + switch ((*pfrc)->fr.type) { + case NGTCP2_FRAME_STREAM: + frc = *pfrc; + + *pfrc = frc->next; + frc->next = NULL; + sfr = &frc->fr.stream; + + strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); + if (!strm) { + ngtcp2_frame_chain_del(frc, conn->mem); + break; + } + rv = ngtcp2_strm_streamfrq_push(strm, frc); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + if (!ngtcp2_strm_is_tx_queued(strm)) { + strm->cycle = conn_tx_strmq_first_cycle(conn); + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + return rv; + } + } + break; + case NGTCP2_FRAME_CRYPTO: + frc = *pfrc; + + *pfrc = frc->next; + frc->next = NULL; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + &frc->fr.crypto.offset, frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + break; + default: + pfrc = &(*pfrc)->next; + } + } + + *pfrc = pktns->tx.frq; + pktns->tx.frq = *first; + + return 0; +} + +/* + * conn_on_retry is called when Retry packet is received. The + * function decodes the data in the buffer pointed by |pkt| whose + * length is |pktlen| as Retry packet. The length of long packet + * header is given in |hdpktlen|. |pkt| includes packet header. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_INVALID_ARGUMENT + * Packet payload is badly formatted. + * NGTCP2_ERR_PROTO + * ODCID does not match; or Token is empty. + */ +static int conn_on_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + size_t hdpktlen, const uint8_t *pkt, size_t pktlen) { + int rv; + ngtcp2_pkt_retry retry; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + ngtcp2_rtb *in_rtb; + uint8_t cidbuf[sizeof(retry.odcid.data) * 2 + 1]; + ngtcp2_vec *token; + + if (!in_pktns || conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { + return 0; + } + + in_rtb = &in_pktns->rtb; + + rv = ngtcp2_pkt_decode_retry(&retry, pkt + hdpktlen, pktlen - hdpktlen); + if (rv != 0) { + return rv; + } + + retry.odcid = conn->dcid.current.cid; + + rv = ngtcp2_pkt_verify_retry_tag( + conn->version, &retry, pkt, pktlen, conn->callbacks.encrypt, + &conn->crypto.retry_aead, &conn->crypto.retry_aead_ctx); + if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "unable to verify Retry packet integrity"); + return rv; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "odcid=0x%s", + (const char *)ngtcp2_encode_hex(cidbuf, retry.odcid.data, + retry.odcid.datalen)); + + if (retry.token.len == 0) { + return NGTCP2_ERR_PROTO; + } + + if (ngtcp2_cid_eq(&conn->dcid.current.cid, &hd->scid)) { + return 0; + } + + ngtcp2_qlog_retry_pkt_received(&conn->qlog, hd); + + /* DCID must be updated before invoking callback because client + generates new initial keys there. */ + conn->dcid.current.cid = hd->scid; + conn->retry_scid = hd->scid; + + conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; + + assert(conn->callbacks.recv_retry); + + rv = conn->callbacks.recv_retry(conn, hd, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + conn->state = NGTCP2_CS_CLIENT_INITIAL; + + /* Just freeing memory is dangerous because we might free twice. */ + + rv = ngtcp2_rtb_remove_all(rtb, conn, &conn->pktns, &conn->cstat); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_rtb_remove_all(in_rtb, conn, in_pktns, &conn->cstat); + if (rv != 0) { + return rv; + } + + token = &conn->local.settings.token; + + ngtcp2_mem_free(conn->mem, token->base); + token->base = NULL; + token->len = 0; + + token->base = ngtcp2_mem_malloc(conn->mem, retry.token.len); + if (token->base == NULL) { + return NGTCP2_ERR_NOMEM; + } + token->len = retry.token.len; + + ngtcp2_cpymem(token->base, retry.token.base, retry.token.len); + + reset_conn_stat_recovery(&conn->cstat); + conn_reset_congestion_state(conn); + conn_reset_ecn_validation_state(conn); + + return 0; +} + +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { + ngtcp2_duration pto = conn_compute_pto(conn, pktns); + int rv = ngtcp2_rtb_detect_lost_pkt(&pktns->rtb, conn, pktns, cstat, pto, ts); + if (rv != 0) { + return rv; + } + + return 0; +} + +/* + * conn_recv_ack processes received ACK frame |fr|. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_PROTO + * |fr| acknowledges a packet this endpoint has not sent. + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + */ +static int conn_recv_ack(ngtcp2_conn *conn, ngtcp2_pktns *pktns, ngtcp2_ack *fr, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { + int rv; + ngtcp2_frame_chain *frc = NULL; + ngtcp2_ssize num_acked; + ngtcp2_conn_stat *cstat = &conn->cstat; + + if (pktns->tx.last_pkt_num < fr->largest_ack) { + return NGTCP2_ERR_PROTO; + } + + rv = ngtcp2_pkt_validate_ack(fr); + if (rv != 0) { + return rv; + } + + ngtcp2_acktr_recv_ack(&pktns->acktr, fr); + + num_acked = ngtcp2_rtb_recv_ack(&pktns->rtb, fr, &conn->cstat, conn, pktns, + pkt_ts, ts); + if (num_acked < 0) { + /* TODO assert this */ + assert(ngtcp2_err_is_fatal((int)num_acked)); + ngtcp2_frame_chain_list_del(frc, conn->mem); + return (int)num_acked; + } + + if (num_acked == 0) { + return 0; + } + + rv = ngtcp2_conn_detect_lost_pkt(conn, pktns, &conn->cstat, ts); + if (rv != 0) { + return rv; + } + + pktns->rtb.probe_pkt_left = 0; + + if (cstat->pto_count && + (conn->server || (conn->flags & NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED))) { + /* Reset PTO count but no less than 2 to avoid frequent probe + packet transmission. */ + cstat->pto_count = ngtcp2_min(cstat->pto_count, 2); + } + + ngtcp2_conn_set_loss_detection_timer(conn, ts); + + return 0; +} + +/* + * conn_assign_recved_ack_delay_unscaled assigns + * fr->ack_delay_unscaled. + */ +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; +} + +/* + * conn_recv_max_stream_data processes received MAX_STREAM_DATA frame + * |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * Stream ID indicates that it is a local stream, and the local + * endpoint has not initiated it; or stream is peer initiated + * unidirectional stream. + * NGTCP2_ERR_STREAM_LIMIT + * Stream ID exceeds allowed limit. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_recv_max_stream_data(ngtcp2_conn *conn, + const ngtcp2_max_stream_data *fr) { + ngtcp2_strm *strm; + ngtcp2_idtr *idtr; + int local_stream = conn_local_stream(conn, fr->stream_id); + int bidi = bidi_stream(fr->stream_id); + int rv; + + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + + idtr = &conn->remote.uni.idtr; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + /* Stream has been closed. */ + return 0; + } + + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + /* Stream has been closed. */ + return 0; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + } + + if (strm->tx.max_offset < fr->max_stream_data) { + strm->tx.max_offset = fr->max_stream_data; + + /* Don't call callback if stream is half-closed local */ + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return 0; + } + + rv = conn_call_extend_max_stream_data(conn, strm, fr->stream_id, + fr->max_stream_data); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * conn_recv_max_data processes received MAX_DATA frame |fr|. + */ +static void conn_recv_max_data(ngtcp2_conn *conn, const ngtcp2_max_data *fr) { + conn->tx.max_offset = ngtcp2_max(conn->tx.max_offset, fr->max_data); +} + +/* + * conn_buffer_pkt buffers |pkt| of length |pktlen|, chaining it from + * |*ppc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_buffer_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + const ngtcp2_path *path, const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, size_t dgramlen, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_pkt_chain **ppc = &pktns->rx.buffed_pkts, *pc; + size_t i; + for (i = 0; *ppc && i < NGTCP2_MAX_NUM_BUFFED_RX_PKTS; + ppc = &(*ppc)->next, ++i) + ; + + if (i == NGTCP2_MAX_NUM_BUFFED_RX_PKTS) { + return 0; + } + + rv = + ngtcp2_pkt_chain_new(&pc, path, pi, pkt, pktlen, dgramlen, ts, conn->mem); + if (rv != 0) { + return rv; + } + + *ppc = pc; + + return 0; +} + +static int ensure_decrypt_buffer(ngtcp2_vec *vec, size_t n, size_t initial, + const ngtcp2_mem *mem) { + uint8_t *nbuf; + size_t len; + + if (vec->len >= n) { + return 0; + } + + len = vec->len == 0 ? initial : vec->len * 2; + for (; len < n; len *= 2) + ; + nbuf = ngtcp2_mem_realloc(mem, vec->base, len); + if (nbuf == NULL) { + return NGTCP2_ERR_NOMEM; + } + vec->base = nbuf; + vec->len = len; + + return 0; +} + +/* + * conn_ensure_decrypt_hp_buffer ensures that + * conn->crypto.decrypt_hp_buf has at least |n| bytes space. + * + * 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_decrypt_hp_buffer(ngtcp2_conn *conn, size_t n) { + return ensure_decrypt_buffer(&conn->crypto.decrypt_hp_buf, n, 256, conn->mem); +} + +/* + * conn_ensure_decrypt_buffer ensures that conn->crypto.decrypt_buf + * has at least |n| bytes space. + * + * 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_decrypt_buffer(ngtcp2_conn *conn, size_t n) { + return ensure_decrypt_buffer(&conn->crypto.decrypt_buf, n, 2048, conn->mem); +} + +/* + * decrypt_pkt decrypts the data pointed by |payload| whose length is + * |payloadlen|, and writes plaintext data to the buffer pointed by + * |dest|. The buffer pointed by |ad| is the Additional Data, and its + * length is |adlen|. |pkt_num| is used to create a nonce. |ckm| is + * the cryptographic key, and iv to use. |decrypt| is a callback + * function which actually decrypts a packet. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + * NGTCP2_ERR_TLS_DECRYPT + * TLS backend failed to decrypt data. + */ +static ngtcp2_ssize decrypt_pkt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const uint8_t *payload, size_t payloadlen, + const uint8_t *ad, size_t adlen, + int64_t pkt_num, ngtcp2_crypto_km *ckm, + ngtcp2_decrypt decrypt) { + /* TODO nonce is limited to 64 bytes. */ + uint8_t nonce[64]; + int rv; + + assert(sizeof(nonce) >= ckm->iv.len); + + ngtcp2_crypto_create_nonce(nonce, ckm->iv.base, ckm->iv.len, pkt_num); + + rv = decrypt(dest, aead, &ckm->aead_ctx, payload, payloadlen, nonce, + ckm->iv.len, ad, adlen); + + if (rv != 0) { + if (rv == NGTCP2_ERR_TLS_DECRYPT) { + return rv; + } + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + assert(payloadlen >= aead->max_overhead); + + return (ngtcp2_ssize)(payloadlen - aead->max_overhead); +} + +/* + * decrypt_hp decryptes packet header. The packet number starts at + * |pkt| + |pkt_num_offset|. The entire plaintext QUIC packet header + * will be written to the buffer pointed by |dest| whose capacity is + * |destlen|. + * + * This function returns the number of bytes written to |dest|, or one + * of the following negative error codes: + * + * NGTCP2_ERR_PROTO + * Packet is badly formatted + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed; or it does not return + * expected result. + */ +static ngtcp2_ssize decrypt_hp(ngtcp2_pkt_hd *hd, uint8_t *dest, + const ngtcp2_crypto_cipher *hp, + const uint8_t *pkt, size_t pktlen, + size_t pkt_num_offset, ngtcp2_crypto_km *ckm, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + ngtcp2_hp_mask hp_mask) { + size_t sample_offset; + uint8_t *p = dest; + uint8_t mask[NGTCP2_HP_MASKLEN]; + size_t i; + int rv; + + assert(hp_mask); + assert(ckm); + + if (pkt_num_offset + 4 + NGTCP2_HP_SAMPLELEN > pktlen) { + return NGTCP2_ERR_PROTO; + } + + p = ngtcp2_cpymem(p, pkt, pkt_num_offset); + + sample_offset = pkt_num_offset + 4; + + rv = hp_mask(mask, hp, hp_ctx, pkt + sample_offset); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x0f)); + } else { + dest[0] = (uint8_t)(dest[0] ^ (mask[0] & 0x1f)); + if (dest[0] & NGTCP2_SHORT_KEY_PHASE_BIT) { + hd->flags |= NGTCP2_PKT_FLAG_KEY_PHASE; + } + } + + hd->pkt_numlen = (size_t)((dest[0] & NGTCP2_PKT_NUMLEN_MASK) + 1); + + for (i = 0; i < hd->pkt_numlen; ++i) { + *p++ = *(pkt + pkt_num_offset + i) ^ mask[i + 1]; + } + + hd->pkt_num = ngtcp2_get_pkt_num(p - hd->pkt_numlen, hd->pkt_numlen); + + return p - dest; +} + +/* + * conn_emit_pending_crypto_data delivers pending stream data to the + * application due to packet reordering. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed + * NGTCP2_ERR_CRYPTO + * TLS backend reported error + */ +static int conn_emit_pending_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + ngtcp2_strm *strm, + uint64_t rx_offset) { + size_t datalen; + const uint8_t *data; + int rv; + uint64_t offset; + + if (!strm->rx.rob) { + return 0; + } + + for (;;) { + datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset); + if (datalen == 0) { + assert(rx_offset == ngtcp2_strm_rx_offset(strm)); + return 0; + } + + offset = rx_offset; + rx_offset += datalen; + + rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen); + if (rv != 0) { + return rv; + } + + ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen); + } +} + +/* + * conn_recv_connection_close is called when CONNECTION_CLOSE or + * APPLICATION_CLOSE frame is received. + */ +static void conn_recv_connection_close(ngtcp2_conn *conn, + ngtcp2_connection_close *fr) { + conn->state = NGTCP2_CS_DRAINING; + if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { + conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT; + } else { + conn->rx.ccec.type = NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_APPLICATION; + } + conn->rx.ccec.error_code = fr->error_code; +} + +static void conn_recv_path_challenge(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_path_challenge *fr) { + ngtcp2_path_challenge_entry *ent; + + /* client only responds to PATH_CHALLENGE from the current path. */ + if (!conn->server && !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_CON, + "discard PATH_CHALLENGE from the path which is not current"); + return; + } + + ent = ngtcp2_ringbuf_push_front(&conn->rx.path_challenge); + ngtcp2_path_challenge_entry_init(ent, path, fr->data); +} + +/* + * conn_reset_congestion_state resets congestion state. + */ +static void conn_reset_congestion_state(ngtcp2_conn *conn) { + conn_reset_conn_stat_cc(conn, &conn->cstat); + + conn->cc.reset(&conn->cc); + + if (conn->hs_pktns) { + ngtcp2_rtb_reset_cc_state(&conn->hs_pktns->rtb, + 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); +} + +static int conn_recv_path_response(ngtcp2_conn *conn, ngtcp2_path_response *fr, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_duration pto, timeout; + ngtcp2_pv *pv = conn->pv, *npv; + uint8_t ent_flags; + + if (!pv) { + return 0; + } + + rv = ngtcp2_pv_validate(pv, &ent_flags, fr->data); + if (rv != 0) { + if (rv == NGTCP2_ERR_PATH_VALIDATION_FAILED) { + return conn_on_path_validation_failed(conn, pv, ts); + } + return 0; + } + + if (!(pv->flags & NGTCP2_PV_FLAG_DONT_CARE)) { + if (!(pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)) { + if (pv->dcid.seq != conn->dcid.current.seq) { + assert(conn->dcid.current.cid.datalen); + + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + goto fail; + } + ngtcp2_dcid_copy(&conn->dcid.current, &pv->dcid); + } + conn_reset_congestion_state(conn); + conn_reset_ecn_validation_state(conn); + } + + if (ngtcp2_path_eq(&pv->dcid.ps.path, &conn->dcid.current.ps.path)) { + conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + } + + rv = conn_call_path_validation(conn, &pv->dcid.ps.path, + NGTCP2_PATH_VALIDATION_RESULT_SUCCESS); + if (rv != 0) { + goto fail; + } + } + + if (pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) { + pto = conn_compute_pto(conn, &conn->pktns); + timeout = 3 * ngtcp2_max(pto, pv->fallback_pto); + + if (ent_flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED) { + assert(conn->server); + + /* Validate path again */ + rv = ngtcp2_pv_new(&npv, &pv->dcid, timeout, + NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE | + NGTCP2_PV_FLAG_MTU_PROBE, + &conn->log, conn->mem); + if (rv != 0) { + goto fail; + } + + 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, timeout, + NGTCP2_PV_FLAG_DONT_CARE, &conn->log, conn->mem); + if (rv != 0) { + goto fail; + } + } + + /* Unset the flag bit so that conn_stop_pv does not retire + DCID. */ + pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; + + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + ngtcp2_pv_del(npv); + return rv; + } + + conn->pv = npv; + + return 0; + } + +fail: + return conn_stop_pv(conn, ts); +} + +/* + * pkt_num_bits returns the number of bits available when packet + * number is encoded in |pkt_numlen| bytes. + */ +static size_t pkt_num_bits(size_t pkt_numlen) { + switch (pkt_numlen) { + case 1: + return 8; + case 2: + return 16; + case 3: + return 24; + case 4: + return 32; + default: + assert(0); + abort(); + } +} + +/* + * pktns_pkt_num_is_duplicate returns nonzero if |pkt_num| is + * duplicated packet number. + */ +static int pktns_pkt_num_is_duplicate(ngtcp2_pktns *pktns, int64_t pkt_num) { + return ngtcp2_gaptr_is_pushed(&pktns->rx.pngap, (uint64_t)pkt_num, 1); +} + +/* + * pktns_commit_recv_pkt_num marks packet number |pkt_num| as + * received. + */ +static int pktns_commit_recv_pkt_num(ngtcp2_pktns *pktns, int64_t pkt_num, + int ack_eliciting, ngtcp2_tstamp ts) { + int rv; + + if (ack_eliciting && pktns->rx.max_ack_eliciting_pkt_num + 1 != pkt_num) { + ngtcp2_acktr_immediate_ack(&pktns->acktr); + } + if (pktns->rx.max_pkt_num < pkt_num) { + pktns->rx.max_pkt_num = pkt_num; + pktns->rx.max_pkt_ts = ts; + } + if (ack_eliciting && pktns->rx.max_ack_eliciting_pkt_num < pkt_num) { + pktns->rx.max_ack_eliciting_pkt_num = pkt_num; + } + + rv = ngtcp2_gaptr_push(&pktns->rx.pngap, (uint64_t)pkt_num, 1); + if (rv != 0) { + return rv; + } + + if (ngtcp2_ksl_len(&pktns->rx.pngap.gap) > 256) { + ngtcp2_gaptr_drop_first_gap(&pktns->rx.pngap); + } + + return 0; +} + +/* + * verify_token verifies |hd| contains |token| in its token field. It + * returns 0 if it succeeds, or NGTCP2_ERR_PROTO. + */ +static int verify_token(const ngtcp2_vec *token, const ngtcp2_pkt_hd *hd) { + if (token->len == hd->token.len && + ngtcp2_cmemeq(token->base, hd->token.base, token->len)) { + return 0; + } + 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; + } +} + +static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *strm, const ngtcp2_crypto *fr); + +static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, size_t dgramlen, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts); + +static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_tstamp ts); + +/* + * conn_recv_handshake_pkt processes received packet |pkt| whose + * length is |pktlen| during handshake period. The buffer pointed by + * |pkt| might contain multiple packets. This function only processes + * one packet. |pkt_ts| is the timestamp when packet is received. + * |ts| should be the current time. Usually they are the same, but + * for buffered packets, |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of bytes it reads if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_RECV_VERSION_NEGOTIATION + * Version Negotiation packet is received. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_DISCARD_PKT + * Packet was discarded because plain text header was malformed; + * or its payload could not be decrypted. + * NGTCP2_ERR_FRAME_FORMAT + * Frame is badly formatted + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_CRYPTO + * TLS stack reported error. + * NGTCP2_ERR_PROTO + * Generic QUIC protocol error. + * + * In addition to the above error codes, error codes returned from + * conn_recv_pkt are also returned. + */ +static ngtcp2_ssize +conn_recv_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, size_t dgramlen, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + ngtcp2_pkt_hd hd; + ngtcp2_max_frame mfr; + ngtcp2_frame *fr = &mfr.fr; + int rv; + int require_ack = 0; + size_t hdpktlen; + const uint8_t *payload; + size_t payloadlen; + ngtcp2_ssize nwrite; + ngtcp2_crypto_aead *aead; + ngtcp2_crypto_cipher *hp; + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx *hp_ctx; + ngtcp2_hp_mask hp_mask; + ngtcp2_decrypt decrypt; + ngtcp2_pktns *pktns; + ngtcp2_strm *crypto; + ngtcp2_crypto_level crypto_level; + int invalid_reserved_bits = 0; + + if (pktlen == 0) { + return 0; + } + + if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { + if (conn->state == NGTCP2_CS_SERVER_INITIAL) { + /* Ignore Short packet unless server's first Handshake packet + has been transmitted. */ + return (ngtcp2_ssize)pktlen; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering Short packet len=%zu", pktlen); + + rv = conn_buffer_pkt(conn, &conn->pktns, path, pi, pkt, pktlen, dgramlen, + ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + return (ngtcp2_ssize)pktlen; + } + + nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); + if (nread < 0) { + return NGTCP2_ERR_DISCARD_PKT; + } + + switch (hd.type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + hdpktlen = (size_t)nread; + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + if (conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Receiving Version Negotiation packet after getting Handshake + packet from server is invalid. */ + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + return NGTCP2_ERR_DISCARD_PKT; + } + + if (!ngtcp2_cid_eq(&conn->oscid, &hd.dcid)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { + /* Just discard invalid Version Negotiation packet */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched SCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + rv = conn_on_version_negotiation(conn, &hd, pkt + hdpktlen, + pktlen - hdpktlen); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + return NGTCP2_ERR_DISCARD_PKT; + } + return NGTCP2_ERR_RECV_VERSION_NEGOTIATION; + case NGTCP2_PKT_RETRY: + hdpktlen = (size_t)nread; + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + if (conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Receiving Retry packet after getting Initial packet from server + is invalid. */ + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_on_retry(conn, &hd, hdpktlen, pkt, pktlen); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + return NGTCP2_ERR_DISCARD_PKT; + } + return (ngtcp2_ssize)pktlen; + } + + if (pktlen < (size_t)nread + hd.len) { + return NGTCP2_ERR_DISCARD_PKT; + } + + pktlen = (size_t)nread + hd.len; + + if (conn->version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + + /* Quoted from spec: if subsequent packets of those types include a + different Source Connection ID, they MUST be discarded. */ + if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) && + !ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched SCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + switch (hd.type) { + case NGTCP2_PKT_0RTT: + if (!conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + if (conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) { + if (conn->early.ckm) { + ngtcp2_ssize nread2; + /* TODO Avoid to parse header twice. */ + nread2 = + conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, pkt_ts, ts); + if (nread2 < 0) { + return nread2; + } + } + + /* Discard 0-RTT packet if we don't have a key to decrypt it. */ + return (ngtcp2_ssize)pktlen; + } + + /* Buffer re-ordered 0-RTT packet. */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering 0-RTT packet len=%zu", pktlen); + + rv = conn_buffer_pkt(conn, conn->in_pktns, path, pi, pkt, pktlen, dgramlen, + ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + + return (ngtcp2_ssize)pktlen; + case NGTCP2_PKT_INITIAL: + if (!conn->in_pktns) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Initial packet is discarded because keys have been discarded"); + return (ngtcp2_ssize)pktlen; + } + + assert(conn->in_pktns); + + if (conn->server) { + if (conn->local.settings.token.len) { + rv = verify_token(&conn->local.settings.token, &hd); + if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is invalid"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + if ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) == 0) { + /* Set rcid here so that it is available to callback. If this + packet is discarded later in this function and no packet is + processed in this connection attempt so far, connection + will be dropped. */ + conn->rcid = hd.dcid; + + rv = conn_call_recv_client_initial(conn, &hd.dcid); + if (rv != 0) { + return rv; + } + } + } else if (hd.token.len != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because token is not empty"); + return NGTCP2_ERR_DISCARD_PKT; + } + + pktns = conn->in_pktns; + crypto = &pktns->crypto.strm; + crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; + + break; + case NGTCP2_PKT_HANDSHAKE: + if (!conn->hs_pktns->crypto.rx.ckm) { + if (conn->server) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PKT, + "Handshake packet at this point is unexpected and discarded"); + return (ngtcp2_ssize)pktlen; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "buffering Handshake packet len=%zu", pktlen); + + rv = conn_buffer_pkt(conn, conn->hs_pktns, path, pi, pkt, pktlen, + dgramlen, ts); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + return rv; + } + return (ngtcp2_ssize)pktlen; + } + + pktns = conn->hs_pktns; + crypto = &pktns->crypto.strm; + crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + + break; + default: + /* unknown packet type */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of unknown packet type"); + return (ngtcp2_ssize)pktlen; + } + + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + aead = &pktns->crypto.ctx.aead; + hp = &pktns->crypto.ctx.hp; + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; + + assert(ckm); + assert(hp_mask); + assert(decrypt); + + rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4); + if (rv != 0) { + return rv; + } + + nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, + (size_t)nread, ckm, hp_ctx, hp_mask); + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + hdpktlen = (size_t)nwrite; + 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, + pkt_num_bits(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); + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + rv = ngtcp2_pkt_verify_reserved_bits(conn->crypto.decrypt_hp_buf.base[0]); + if (rv != 0) { + invalid_reserved_bits = 1; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet has incorrect reserved bits"); + + /* Will return error after decrypting payload */ + } + + if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was discarded because of duplicated packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_ensure_decrypt_buffer(conn, payloadlen); + if (rv != 0) { + return rv; + } + + nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen, + conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num, + ckm, decrypt); + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet payload"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (invalid_reserved_bits) { + return NGTCP2_ERR_PROTO; + } + + payload = conn->crypto.decrypt_buf.base; + payloadlen = (size_t)nwrite; + + switch (hd.type) { + case NGTCP2_PKT_INITIAL: + if (!conn->server || ((conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED) && + !ngtcp2_cid_eq(&conn->rcid, &hd.dcid))) { + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + break; + case NGTCP2_PKT_HANDSHAKE: + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + break; + default: + assert(0); + } + + if (payloadlen == 0) { + /* QUIC packet must contain at least one frame */ + if (hd.type == NGTCP2_PKT_INITIAL) { + return NGTCP2_ERR_DISCARD_PKT; + } + return NGTCP2_ERR_PROTO; + } + + if (hd.type == NGTCP2_PKT_INITIAL && + !(conn->flags & NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED)) { + conn->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED; + if (!conn->server) { + conn->dcid.current.cid = hd.scid; + } + } + + ngtcp2_qlog_pkt_received_start(&conn->qlog); + + for (; payloadlen;) { + nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); + if (nread < 0) { + return nread; + } + + payload += nread; + payloadlen -= (size_t)nread; + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + fr->ack.ack_delay = 0; + fr->ack.ack_delay_unscaled = 0; + break; + } + + ngtcp2_log_rx_fr(&conn->log, &hd, fr); + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + if (!conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_PADDING: + break; + case NGTCP2_FRAME_CRYPTO: + rv = conn_recv_crypto(conn, crypto_level, crypto, &fr->crypto); + if (rv != 0) { + return rv; + } + require_ack = 1; + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + conn_recv_connection_close(conn, &fr->connection_close); + break; + case NGTCP2_FRAME_PING: + require_ack = 1; + break; + default: + return NGTCP2_ERR_PROTO; + } + + ngtcp2_qlog_write_frame(&conn->qlog, fr); + } + + if (conn->server && hd.type == NGTCP2_PKT_HANDSHAKE) { + /* Successful processing of Handshake packet from client verifies + source address. */ + conn->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + } + + ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); + + rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, require_ack, pkt_ts); + if (rv != 0) { + return rv; + } + + pktns_increase_ecn_counts(pktns, pi); + + /* TODO Initial and Handshake are always acknowledged without + delay. */ + 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); + + return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING + : (ngtcp2_ssize)pktlen; +} + +/* + * conn_recv_handshake_cpkt processes compound packet during + * handshake. The buffer pointed by |pkt| might contain multiple + * packets. The Short packet must be the last one because it does not + * have payload length field. + * + * This function returns the same error code returned by + * conn_recv_handshake_pkt. + */ +static int conn_recv_handshake_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, + const uint8_t *pkt, size_t pktlen, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + size_t dgramlen = pktlen; + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + conn->dcid.current.bytes_recv += dgramlen; + } + + while (pktlen) { + nread = + conn_recv_handshake_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); + if (nread < 0) { + if (ngtcp2_err_is_fatal((int)nread)) { + return (int)nread; + } + + if (nread == NGTCP2_ERR_DRAINING) { + return NGTCP2_ERR_DRAINING; + } + + if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) && + /* Not a Version Negotiation packet */ + pktlen > 4 && ngtcp2_get_uint32(&pkt[1]) > 0 && + ngtcp2_pkt_get_type_long(pkt[0]) == NGTCP2_PKT_INITIAL) { + if (conn->server) { + /* If server discards first Initial, then drop connection + state. This is because SCID in packet might be corrupted + 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 this is crypto related error, then return normally + in order to send CONNECTION_CLOSE with TLS alert (e.g., + no_application_protocol). */ + switch (nread) { + case NGTCP2_ERR_CRYPTO: + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + return (int)nread; + } + + return NGTCP2_ERR_DROP_CONN; + } + return 0; + } + /* client */ + if (nread == NGTCP2_ERR_CRYPTO) { + /* If client gets crypto error from TLS stack, it is + unrecoverable, therefore drop connection. */ + return (int)nread; + } + return 0; + } + + if (nread == NGTCP2_ERR_DISCARD_PKT) { + return 0; + } + + return (int)nread; + } + + assert(pktlen >= (size_t)nread); + pkt += nread; + pktlen -= (size_t)nread; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "read packet %td left %zu", nread, pktlen); + } + + return 0; +} + +int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + int64_t stream_id, void *stream_user_data) { + int rv; + uint64_t max_rx_offset; + uint64_t max_tx_offset; + int local_stream = conn_local_stream(conn, stream_id); + + if (bidi_stream(stream_id)) { + if (local_stream) { + max_rx_offset = + conn->local.transport_params.initial_max_stream_data_bidi_local; + max_tx_offset = + conn->remote.transport_params.initial_max_stream_data_bidi_remote; + } else { + max_rx_offset = + conn->local.transport_params.initial_max_stream_data_bidi_remote; + max_tx_offset = + conn->remote.transport_params.initial_max_stream_data_bidi_local; + } + } else if (local_stream) { + max_rx_offset = 0; + max_tx_offset = conn->remote.transport_params.initial_max_stream_data_uni; + } else { + max_rx_offset = conn->local.transport_params.initial_max_stream_data_uni; + max_tx_offset = 0; + } + + rv = ngtcp2_strm_init(strm, stream_id, NGTCP2_STRM_FLAG_NONE, max_rx_offset, + max_tx_offset, stream_user_data, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_map_insert(&conn->strms, &strm->me); + if (rv != 0) { + assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); + goto fail; + } + + if (!conn_local_stream(conn, stream_id)) { + rv = conn_call_stream_open(conn, strm); + if (rv != 0) { + goto fail; + } + } + + return 0; + +fail: + ngtcp2_strm_free(strm); + return rv; +} + +/* + * conn_emit_pending_stream_data passes buffered ordered stream data + * to the application. |rx_offset| is the first offset to deliver to + * the application. This function assumes that the data up to + * |rx_offset| has been delivered already. This function only passes + * the ordered data without any gap. If there is a gap, it stops + * providing the data to the application, and returns. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_emit_pending_stream_data(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t rx_offset) { + size_t datalen; + const uint8_t *data; + int rv; + uint64_t offset; + uint32_t sdflags; + int handshake_completed = conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; + + if (!strm->rx.rob) { + return 0; + } + + for (;;) { + /* Stop calling callback if application has called + ngtcp2_conn_shutdown_stream_read() inside the callback. + Because it doubly counts connection window. */ + if (strm->flags & (NGTCP2_STRM_FLAG_STOP_SENDING)) { + return 0; + } + + datalen = ngtcp2_rob_data_at(strm->rx.rob, &data, rx_offset); + if (datalen == 0) { + assert(rx_offset == ngtcp2_strm_rx_offset(strm)); + return 0; + } + + offset = rx_offset; + rx_offset += datalen; + + sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + rx_offset == strm->rx.last_offset) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; + } + if (!handshake_completed) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY; + } + + rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, datalen); + if (rv != 0) { + return rv; + } + + ngtcp2_rob_pop(strm->rx.rob, rx_offset - datalen, datalen); + } +} + +/* + * conn_recv_crypto is called when CRYPTO frame |fr| is received. + * |rx_offset_base| is the offset in the entire TLS handshake stream. + * fr->offset specifies the offset in each encryption level. + * |max_rx_offset| is, if it is nonzero, the maximum offset in the + * entire TLS handshake stream that |fr| can carry. |crypto_level| is + * the encryption level where this data is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * CRYPTO frame has invalid offset. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CRYPTO + * TLS stack reported error. + * NGTCP2_ERR_FRAME_ENCODING + * The end offset exceeds the maximum value. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_crypto(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, + ngtcp2_strm *crypto, const ngtcp2_crypto *fr) { + uint64_t fr_end_offset; + uint64_t rx_offset; + int rv; + + if (fr->datacnt == 0) { + return 0; + } + + fr_end_offset = fr->offset + fr->data[0].len; + + if (NGTCP2_MAX_VARINT < fr_end_offset) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + rx_offset = ngtcp2_strm_rx_offset(crypto); + + if (fr_end_offset <= rx_offset) { + if (conn->server && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT) && + crypto_level == NGTCP2_CRYPTO_LEVEL_INITIAL) { + /* recovery draft: Speeding Up Handshake Completion + + When a server receives an Initial packet containing duplicate + CRYPTO data, it can assume the client did not receive all of + the server's CRYPTO data sent in Initial packets, or the + client's estimated RTT is too small. ... To speed up + handshake completion under these conditions, an endpoint MAY + send a packet containing unacknowledged CRYPTO data earlier + than the PTO expiry, subject to address validation limits; + ... */ + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT; + conn->in_pktns->rtb.probe_pkt_left = 1; + conn->hs_pktns->rtb.probe_pkt_left = 1; + } + return 0; + } + + crypto->rx.last_offset = ngtcp2_max(crypto->rx.last_offset, fr_end_offset); + + /* TODO Before dispatching incoming data to TLS stack, make sure + that previous data in previous encryption level has been + completely sent to TLS stack. Usually, if data is left, it is an + error because key is generated after consuming all data in the + previous encryption level. */ + if (fr->offset <= rx_offset) { + size_t ncut = (size_t)(rx_offset - fr->offset); + const uint8_t *data = fr->data[0].base + ncut; + size_t datalen = fr->data[0].len - ncut; + uint64_t offset = rx_offset; + + rx_offset += datalen; + rv = ngtcp2_strm_update_rx_offset(crypto, rx_offset); + if (rv != 0) { + return rv; + } + + rv = conn_call_recv_crypto_data(conn, crypto_level, offset, data, datalen); + if (rv != 0) { + return rv; + } + + rv = conn_emit_pending_crypto_data(conn, crypto_level, crypto, rx_offset); + if (rv != 0) { + return rv; + } + + return 0; + } + + if (fr_end_offset - rx_offset > NGTCP2_MAX_REORDERED_CRYPTO_DATA) { + return NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED; + } + + return ngtcp2_strm_recv_reordering(crypto, fr->data[0].base, fr->data[0].len, + fr->offset); +} + +/* + * conn_max_data_violated returns nonzero if receiving |datalen| + * violates connection flow control on local endpoint. + */ +static int conn_max_data_violated(ngtcp2_conn *conn, uint64_t datalen) { + return conn->rx.max_offset - conn->rx.offset < datalen; +} + +/* + * conn_recv_stream is called when STREAM frame |fr| is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * STREAM frame is received for a local stream which is not + * initiated; or STREAM frame is received for a local + * unidirectional stream + * NGTCP2_ERR_STREAM_LIMIT + * STREAM frame has remote stream ID which is strictly greater + * than the allowed limit. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_FLOW_CONTROL + * Flow control limit is violated; or the end offset of stream + * data is beyond the NGTCP2_MAX_VARINT. + * NGTCP2_ERR_FINAL_SIZE + * STREAM frame has strictly larger end offset than it is + * permitted. + */ +static int conn_recv_stream(ngtcp2_conn *conn, const ngtcp2_stream *fr) { + int rv; + ngtcp2_strm *strm; + ngtcp2_idtr *idtr; + uint64_t rx_offset, fr_end_offset; + int local_stream; + int bidi; + size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + uint32_t sdflags = NGTCP2_STREAM_DATA_FLAG_NONE; + + local_stream = conn_local_stream(conn, fr->stream_id); + bidi = bidi_stream(fr->stream_id); + + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (local_stream) { + return NGTCP2_ERR_STREAM_STATE; + } + if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.uni.idtr; + } + + if (NGTCP2_MAX_VARINT - datalen < fr->offset) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + /* TODO The stream has been closed. This should be responded + with RESET_STREAM, or simply ignored. */ + return 0; + } + + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + /* TODO The stream has been closed. This should be responded + with RESET_STREAM, or simply ignored. */ + return 0; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + /* TODO Perhaps, call new_stream callback? */ + rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + if (!bidi) { + ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_WR); + } + } + + fr_end_offset = fr->offset + datalen; + + if (strm->rx.max_offset < fr_end_offset) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + if (strm->rx.last_offset < fr_end_offset) { + uint64_t len = fr_end_offset - strm->rx.last_offset; + + if (conn_max_data_violated(conn, len)) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + conn->rx.offset += len; + + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + ngtcp2_conn_extend_max_offset(conn, len); + } + } + + rx_offset = ngtcp2_strm_rx_offset(strm); + + if (fr->fin) { + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) { + if (strm->rx.last_offset != fr_end_offset) { + return NGTCP2_ERR_FINAL_SIZE; + } + + if (strm->flags & + (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + return 0; + } + + if (rx_offset == fr_end_offset) { + return 0; + } + } else if (strm->rx.last_offset > fr_end_offset) { + return NGTCP2_ERR_FINAL_SIZE; + } else { + strm->rx.last_offset = fr_end_offset; + + ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); + + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, + strm->app_error_code); + } + } + } else { + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + strm->rx.last_offset < fr_end_offset) { + return NGTCP2_ERR_FINAL_SIZE; + } + + strm->rx.last_offset = ngtcp2_max(strm->rx.last_offset, fr_end_offset); + + if (fr_end_offset <= rx_offset) { + return 0; + } + + if (strm->flags & + (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST)) { + return 0; + } + } + + if (fr->offset <= rx_offset) { + size_t ncut = (size_t)(rx_offset - fr->offset); + uint64_t offset = rx_offset; + const uint8_t *data; + int fin; + + if (fr->datacnt) { + data = fr->data[0].base + ncut; + datalen -= ncut; + + rx_offset += datalen; + rv = ngtcp2_strm_update_rx_offset(strm, rx_offset); + if (rv != 0) { + return rv; + } + } else { + data = NULL; + datalen = 0; + } + + fin = (strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + rx_offset == strm->rx.last_offset; + + if (fin || datalen) { + if (fin) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_FIN; + } + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + sdflags |= NGTCP2_STREAM_DATA_FLAG_EARLY; + } + rv = conn_call_recv_stream_data(conn, strm, sdflags, offset, data, + datalen); + if (rv != 0) { + return rv; + } + + rv = conn_emit_pending_stream_data(conn, strm, rx_offset); + if (rv != 0) { + return rv; + } + } + } else if (fr->datacnt) { + rv = ngtcp2_strm_recv_reordering(strm, fr->data[0].base, fr->data[0].len, + fr->offset); + if (rv != 0) { + return rv; + } + } + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); +} + +/* + * conn_reset_stream adds RESET_STREAM frame to the transmission + * queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_reset_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + ngtcp2_frame_chain *frc; + ngtcp2_pktns *pktns = &conn->pktns; + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + frc->fr.type = NGTCP2_FRAME_RESET_STREAM; + frc->fr.reset_stream.stream_id = strm->stream_id; + frc->fr.reset_stream.app_error_code = app_error_code; + frc->fr.reset_stream.final_size = strm->tx.offset; + + /* TODO This prepends RESET_STREAM to pktns->tx.frq. */ + frc->next = pktns->tx.frq; + pktns->tx.frq = frc; + + return 0; +} + +/* + * conn_stop_sending adds STOP_SENDING frame to the transmission + * queue. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_stop_sending(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + ngtcp2_frame_chain *frc; + ngtcp2_pktns *pktns = &conn->pktns; + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + frc->fr.type = NGTCP2_FRAME_STOP_SENDING; + frc->fr.stop_sending.stream_id = strm->stream_id; + frc->fr.stop_sending.app_error_code = app_error_code; + + /* TODO This prepends STOP_SENDING to pktns->tx.frq. */ + frc->next = pktns->tx.frq; + pktns->tx.frq = frc; + + return 0; +} + +/* + * handle_max_remote_streams_extension extends + * |*punsent_max_remote_streams| by |n| if a condition allows it. + */ +static void +handle_max_remote_streams_extension(uint64_t *punsent_max_remote_streams, + size_t n) { + if ( +#if SIZE_MAX > UINT32_MAX + NGTCP2_MAX_STREAMS < n || +#endif /* SIZE_MAX > UINT32_MAX */ + *punsent_max_remote_streams > (uint64_t)(NGTCP2_MAX_STREAMS - n)) { + *punsent_max_remote_streams = NGTCP2_MAX_STREAMS; + } else { + *punsent_max_remote_streams += n; + } +} + +/* + * conn_recv_reset_stream is called when RESET_STREAM |fr| is + * received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * RESET_STREAM frame is received to the local stream which is not + * initiated. + * NGTCP2_ERR_STREAM_LIMIT + * RESET_STREAM frame has remote stream ID which is strictly + * greater than the allowed limit. + * NGTCP2_ERR_PROTO + * RESET_STREAM frame is received to the local unidirectional + * stream + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_FLOW_CONTROL + * Flow control limit is violated; or the final size is beyond the + * NGTCP2_MAX_VARINT. + * NGTCP2_ERR_FINAL_SIZE + * The final offset is strictly larger than it is permitted. + */ +static int conn_recv_reset_stream(ngtcp2_conn *conn, + const ngtcp2_reset_stream *fr) { + ngtcp2_strm *strm; + int local_stream = conn_local_stream(conn, fr->stream_id); + int bidi = bidi_stream(fr->stream_id); + uint64_t datalen; + ngtcp2_idtr *idtr; + int rv; + + /* TODO share this piece of code */ + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (local_stream) { + return NGTCP2_ERR_PROTO; + } + if (conn->remote.uni.max_streams < ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.uni.idtr; + } + + if (NGTCP2_MAX_VARINT < fr->final_size) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + return 0; + } + + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + return 0; + } + + if (conn_initial_stream_rx_offset(conn, fr->stream_id) < fr->final_size || + conn_max_data_violated(conn, fr->final_size)) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + /* Stream is reset before we create ngtcp2_strm object. */ + conn->rx.offset += fr->final_size; + ngtcp2_conn_extend_max_offset(conn, fr->final_size); + + rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, + fr->app_error_code, NULL); + if (rv != 0) { + return rv; + } + + /* There will be no activity in this stream because we got + RESET_STREAM and don't write stream data any further. This + effectively allows another new stream for peer. */ + if (bidi) { + handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, + 1); + } else { + handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, + 1); + } + + return 0; + } + + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD)) { + if (strm->rx.last_offset != fr->final_size) { + return NGTCP2_ERR_FINAL_SIZE; + } + } else if (strm->rx.last_offset > fr->final_size) { + return NGTCP2_ERR_FINAL_SIZE; + } + + datalen = fr->final_size - strm->rx.last_offset; + + if (strm->rx.max_offset < fr->final_size || + conn_max_data_violated(conn, datalen)) { + return NGTCP2_ERR_FLOW_CONTROL; + } + + if (!(strm->flags & NGTCP2_STRM_FLAG_RECV_RST)) { + rv = conn_call_stream_reset(conn, fr->stream_id, fr->final_size, + fr->app_error_code, strm->stream_user_data); + if (rv != 0) { + return rv; + } + + /* Extend connection flow control window for the amount of data + which are not passed to application. */ + if (!(strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING)) { + ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - + ngtcp2_strm_rx_offset(strm)); + } + } + + conn->rx.offset += datalen; + ngtcp2_conn_extend_max_offset(conn, datalen); + + strm->rx.last_offset = fr->final_size; + strm->flags |= NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_RECV_RST; + + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); +} + +/* + * conn_recv_stop_sending is called when STOP_SENDING |fr| is received. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_STREAM_STATE + * STOP_SENDING frame is received for a local stream which is not + * initiated; or STOP_SENDING frame is received for a local + * unidirectional stream. + * NGTCP2_ERR_STREAM_LIMIT + * STOP_SENDING frame has remote stream ID which is strictly + * greater than the allowed limit. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_stop_sending(ngtcp2_conn *conn, + const ngtcp2_stop_sending *fr) { + int rv; + ngtcp2_strm *strm; + ngtcp2_idtr *idtr; + int local_stream = conn_local_stream(conn, fr->stream_id); + int bidi = bidi_stream(fr->stream_id); + + if (bidi) { + if (local_stream) { + if (conn->local.bidi.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + } else if (conn->remote.bidi.max_streams < + ngtcp2_ord_stream_id(fr->stream_id)) { + return NGTCP2_ERR_STREAM_LIMIT; + } + + idtr = &conn->remote.bidi.idtr; + } else { + if (!local_stream || conn->local.uni.next_stream_id <= fr->stream_id) { + return NGTCP2_ERR_STREAM_STATE; + } + + idtr = &conn->remote.uni.idtr; + } + + strm = ngtcp2_conn_find_stream(conn, fr->stream_id); + if (strm == NULL) { + if (local_stream) { + return 0; + } + rv = ngtcp2_idtr_open(idtr, fr->stream_id); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + assert(rv == NGTCP2_ERR_STREAM_IN_USE); + return 0; + } + + /* Frame is received reset before we create ngtcp2_strm + object. */ + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + rv = ngtcp2_conn_init_stream(conn, strm, fr->stream_id, NULL); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + } + + /* No RESET_STREAM is required if we have sent FIN and all data have + been acknowledged. */ + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) && + ngtcp2_strm_is_all_tx_data_acked(strm)) { + return 0; + } + + rv = conn_reset_stream(conn, strm, fr->app_error_code); + if (rv != 0) { + return rv; + } + + strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + + ngtcp2_strm_streamfrq_clear(strm); + + return ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, fr->app_error_code); +} + +/* + * check_stateless_reset returns nonzero if Stateless Reset |sr| + * coming via |path| is valid against |dcid|. + */ +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_verify_stateless_reset_token(dcid->token, + sr->stateless_reset_token) == 0; +} + +/* + * conn_on_stateless_reset decodes Stateless Reset from the buffer + * pointed by |payload| whose length is |payloadlen|. |payload| + * should start after first byte of packet. + * + * If Stateless Reset is decoded, and the Stateless Reset Token is + * validated, the connection is closed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Could not decode Stateless Reset; or Stateless Reset Token does + * not match; or No stateless reset token is available. + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + */ +static int conn_on_stateless_reset(ngtcp2_conn *conn, const ngtcp2_path *path, + const uint8_t *payload, size_t payloadlen) { + int rv = 1; + 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) { + return rv; + } + + 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) || + !check_stateless_reset(&pv->fallback_dcid, path, &sr))))) { + len = ngtcp2_ringbuf_len(&conn->dcid.retired); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + if (check_stateless_reset(dcid, path, &sr)) { + break; + } + } + + if (i == len) { + len = ngtcp2_ringbuf_len(&conn->dcid.bound); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, i); + if (check_stateless_reset(dcid, path, &sr)) { + break; + } + } + + if (i == len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + } + } + + conn->state = NGTCP2_CS_DRAINING; + + ngtcp2_log_rx_sr(&conn->log, &sr); + + if (!conn->callbacks.recv_stateless_reset) { + return 0; + } + + rv = conn->callbacks.recv_stateless_reset(conn, &sr, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +/* + * conn_recv_max_streams processes the incoming MAX_STREAMS frame + * |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + * NGTCP2_ERR_FRAME_ENCODING + * The maximum streams field exceeds the maximum value. + */ +static int conn_recv_max_streams(ngtcp2_conn *conn, + const ngtcp2_max_streams *fr) { + uint64_t n; + + if (fr->max_streams > NGTCP2_MAX_STREAMS) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + n = ngtcp2_min(fr->max_streams, NGTCP2_MAX_STREAMS); + + if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { + if (conn->local.bidi.max_streams < n) { + conn->local.bidi.max_streams = n; + return conn_call_extend_max_local_streams_bidi(conn, n); + } + return 0; + } + + if (conn->local.uni.max_streams < n) { + conn->local.uni.max_streams = n; + return conn_call_extend_max_local_streams_uni(conn, n); + } + 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); + } else if (i == ngtcp2_ringbuf_len(rb) - 1) { + ngtcp2_ringbuf_pop_back(rb); + break; + } else { + 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|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * |fr| has the duplicated sequence number with different CID or + * token; or DCID is zero-length. + */ +static int conn_recv_new_connection_id(ngtcp2_conn *conn, + const ngtcp2_new_connection_id *fr) { + size_t i, len; + ngtcp2_dcid *dcid; + ngtcp2_pv *pv = conn->pv; + int rv; + int found = 0; + size_t extra_dcid = 0; + + if (conn->dcid.current.cid.datalen == 0) { + return NGTCP2_ERR_PROTO; + } + + if (fr->retire_prior_to > fr->seq) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + rv = ngtcp2_dcid_verify_uniqueness(&conn->dcid.current, fr->seq, &fr->cid, + fr->stateless_reset_token); + 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; + } + } + + len = ngtcp2_ringbuf_len(&conn->dcid.bound); + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 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; + } + } + + len = ngtcp2_ringbuf_len(&conn->dcid.unused); + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 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; + } + } + + 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, fr->retire_prior_to); + if (rv != 0) { + return rv; + } + + rv = conn_retire_dcid_prior_to(conn, &conn->dcid.unused, + conn->dcid.retire_prior_to); + if (rv != 0) { + return rv; + } + } else if (fr->seq < conn->dcid.retire_prior_to) { + /* If packets are reordered, we might have retire_prior_to which + is larger than fr->seq. + + A malicious peer might send crafted NEW_CONNECTION_ID to force + local endpoint to create lots of RETIRE_CONNECTION_ID frames. + For example, a peer might send seq = 50000 and retire_prior_to + = 50000. Then send NEW_CONNECTION_ID frames with seq < + 50000. */ + /* TODO we might queue lots of RETIRE_CONNECTION_ID frame here + because conn->dcid.num_retire_queued is incremented when the + frame is serialized. */ + if (conn->dcid.num_retire_queued < NGTCP2_MAX_DCID_POOL_SIZE * 2) { + return conn_retire_dcid_seq(conn, fr->seq); + } + return 0; + } + + if (found) { + return 0; + } + + if (ngtcp2_gaptr_is_pushed(&conn->dcid.seqgap, fr->seq, 1)) { + return 0; + } + + rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, fr->seq, 1); + if (rv != 0) { + return rv; + } + + if (ngtcp2_ksl_len(&conn->dcid.seqgap.gap) > 32) { + ngtcp2_gaptr_drop_first_gap(&conn->dcid.seqgap); + } + + len = ngtcp2_ringbuf_len(&conn->dcid.unused); + + if (conn->dcid.current.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq && + pv->dcid.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq >= conn->dcid.retire_prior_to) { + ++extra_dcid; + } + } + + if (conn->local.transport_params.active_connection_id_limit <= + len + extra_dcid) { + return NGTCP2_ERR_CONNECTION_ID_LIMIT; + } + + if (len >= NGTCP2_MAX_DCID_POOL_SIZE) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "too many connection ID"); + return 0; + } + + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + ngtcp2_dcid_init(dcid, fr->seq, &fr->cid, fr->stateless_reset_token); + + return 0; +} + +/* + * conn_post_process_recv_new_connection_id handles retirement request + * of active DCIDs. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_post_process_recv_new_connection_id(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_dcid *dcid; + int rv; + + if (conn->dcid.current.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return 0; + } + + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + if (pv) { + if (conn->dcid.current.seq == pv->dcid.seq) { + ngtcp2_dcid_copy_cid_token(&pv->dcid, dcid); + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + conn->dcid.current.seq == pv->fallback_dcid.seq) { + 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); + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + } + + if (pv) { + if (pv->dcid.seq < conn->dcid.retire_prior_to) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused)) { + rv = conn_retire_dcid(conn, &pv->dcid, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 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); + + rv = conn_call_activate_dcid(conn, &pv->dcid); + if (rv != 0) { + return rv; + } + } else { + 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_stop_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)) { + rv = conn_retire_dcid(conn, &pv->fallback_dcid, ts); + if (rv != 0) { + return rv; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + ngtcp2_dcid_copy_cid_token(&pv->fallback_dcid, dcid); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &pv->fallback_dcid); + if (rv != 0) { + return rv; + } + } else { + /* Now we have no fallback dcid. */ + return conn_stop_pv(conn, ts); + } + } + } + + return 0; +} + +/* + * conn_recv_retire_connection_id processes the incoming + * RETIRE_CONNECTION_ID frame |fr|. |hd| is a packet header which + * |fr| is included. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_PROTO + * SCID is zero-length. + * NGTCP2_ERR_FRAME_ENCODING + * Attempt to retire CID which is used as DCID to send this frame. + */ +static int conn_recv_retire_connection_id(ngtcp2_conn *conn, + const ngtcp2_pkt_hd *hd, + const ngtcp2_retire_connection_id *fr, + ngtcp2_tstamp ts) { + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + + if (conn->oscid.datalen == 0 || conn->scid.last_seq < fr->seq) { + return NGTCP2_ERR_PROTO; + } + + for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + scid = ngtcp2_ksl_it_get(&it); + if (scid->seq == fr->seq) { + if (ngtcp2_cid_eq(&scid->cid, &hd->dcid)) { + return NGTCP2_ERR_PROTO; + } + + if (!(scid->flags & NGTCP2_SCID_FLAG_RETIRED)) { + scid->flags |= NGTCP2_SCID_FLAG_RETIRED; + ++conn->scid.num_retired; + } + + if (scid->pe.index != NGTCP2_PQ_BAD_INDEX) { + ngtcp2_pq_remove(&conn->scid.used, &scid->pe); + scid->pe.index = NGTCP2_PQ_BAD_INDEX; + } + + scid->ts_retired = ts; + + return ngtcp2_pq_push(&conn->scid.used, &scid->pe); + } + } + + return 0; +} + +/* + * conn_recv_new_token processes the incoming NEW_TOKEN frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Token is empty + * NGTCP2_ERR_PROTO: + * Server received NEW_TOKEN. + */ +static int conn_recv_new_token(ngtcp2_conn *conn, const ngtcp2_new_token *fr) { + int rv; + + if (conn->server) { + return NGTCP2_ERR_PROTO; + } + + if (fr->token.len == 0) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + if (conn->callbacks.recv_new_token) { + rv = conn->callbacks.recv_new_token(conn, &fr->token, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + return 0; +} + +/* + * conn_recv_streams_blocked_bidi processes the incoming + * STREAMS_BLOCKED (0x16). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Maximum Streams is larger than advertised value. + */ +static int conn_recv_streams_blocked_bidi(ngtcp2_conn *conn, + ngtcp2_streams_blocked *fr) { + if (fr->max_streams > conn->remote.bidi.max_streams) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + return 0; +} + +/* + * conn_recv_streams_blocked_uni processes the incoming + * STREAMS_BLOCKED (0x17). + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Maximum Streams is larger than advertised value. + */ +static int conn_recv_streams_blocked_uni(ngtcp2_conn *conn, + ngtcp2_streams_blocked *fr) { + if (fr->max_streams > conn->remote.uni.max_streams) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + return 0; +} + +/* + * conn_select_preferred_addr asks a client application to select a + * server address from preferred addresses received from server. If a + * client chooses the address, path validation will start. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_select_preferred_addr(ngtcp2_conn *conn) { + struct sockaddr_storage buf; + ngtcp2_addr addr; + int rv; + ngtcp2_duration pto, initial_pto, timeout; + ngtcp2_pv *pv; + ngtcp2_dcid *dcid; + + ngtcp2_addr_init(&addr, (struct sockaddr *)&buf, 0, NULL); + + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return 0; + } + + rv = conn_call_select_preferred_addr(conn, &addr); + if (rv != 0) { + return rv; + } + + if (addr.addrlen == 0 || + ngtcp2_addr_eq(&conn->dcid.current.ps.path.remote, &addr)) { + return 0; + } + + assert(conn->pv == NULL); + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + pto = conn_compute_pto(conn, &conn->pktns); + initial_pto = conn_compute_initial_pto(conn, &conn->pktns); + timeout = 3 * ngtcp2_max(pto, initial_pto); + + rv = ngtcp2_pv_new(&pv, dcid, timeout, NGTCP2_PV_FLAG_NONE, &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); + conn->pv = pv; + + ngtcp2_addr_copy(&pv->dcid.ps.path.local, &conn->dcid.current.ps.path.local); + ngtcp2_addr_copy(&pv->dcid.ps.path.remote, &addr); + + return conn_call_activate_dcid(conn, &pv->dcid); +} + +/* + * conn_recv_handshake_done processes the incoming HANDSHAKE_DONE + * frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Server received HANDSHAKE_DONE frame. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_handshake_done(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + + if (conn->server) { + return NGTCP2_ERR_PROTO; + } + + if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { + return 0; + } + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED | + NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + + conn->pktns.rtb.persistent_congestion_start_ts = ts; + + conn_discard_handshake_state(conn, ts); + + if (conn->remote.transport_params.preferred_address_present) { + rv = conn_select_preferred_addr(conn); + if (rv != 0) { + return rv; + } + } + + if (conn->callbacks.handshake_confirmed) { + rv = conn->callbacks.handshake_confirmed(conn, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + /* Re-arm loss detection timer after handshake has been + confirmed. */ + ngtcp2_conn_set_loss_detection_timer(conn, ts); + + return 0; +} + +/* + * conn_recv_datagram processes the incoming DATAGRAM frame |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +static int conn_recv_datagram(ngtcp2_conn *conn, ngtcp2_datagram *fr) { + const uint8_t *data; + size_t datalen; + int rv; + uint32_t flags = NGTCP2_DATAGRAM_FLAG_NONE; + + assert(conn->local.transport_params.max_datagram_frame_size); + + if (!conn->callbacks.recv_datagram) { + return 0; + } + + if (fr->datacnt) { + assert(fr->datacnt == 1); + + data = fr->data->base; + datalen = fr->data->len; + } else { + data = NULL; + datalen = 0; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + flags |= NGTCP2_DATAGRAM_FLAG_EARLY; + } + + rv = conn->callbacks.recv_datagram(conn, flags, data, datalen, + conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +/* + * conn_key_phase_changed returns nonzero if |hd| indicates that the + * key phase has unexpected value. + */ +static int conn_key_phase_changed(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd) { + ngtcp2_pktns *pktns = &conn->pktns; + + return !(pktns->crypto.rx.ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE) ^ + !(hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE); +} + +/* + * conn_prepare_key_update installs new updated keys. + */ +static int conn_prepare_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; + ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_crypto_km *rx_ckm = pktns->crypto.rx.ckm; + ngtcp2_crypto_km *tx_ckm = pktns->crypto.tx.ckm; + ngtcp2_crypto_km *new_rx_ckm, *new_tx_ckm; + ngtcp2_crypto_aead_ctx rx_aead_ctx = {0}, tx_aead_ctx = {0}; + size_t secretlen, ivlen; + + if ((conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && + tx_ckm->use_count >= pktns->crypto.ctx.max_encryption && + ngtcp2_conn_initiate_key_update(conn, ts) != 0) { + return NGTCP2_ERR_AEAD_LIMIT_REACHED; + } + + if ((conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + (confirmed_ts != UINT64_MAX && confirmed_ts + pto > ts)) { + return 0; + } + + if (conn->crypto.key_update.new_rx_ckm || + conn->crypto.key_update.new_tx_ckm) { + assert(conn->crypto.key_update.new_rx_ckm); + assert(conn->crypto.key_update.new_tx_ckm); + return 0; + } + + secretlen = rx_ckm->secret.len; + ivlen = rx_ckm->iv.len; + + rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_rx_ckm, + secretlen, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_nocopy_new(&conn->crypto.key_update.new_tx_ckm, + secretlen, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + new_rx_ckm = conn->crypto.key_update.new_rx_ckm; + new_tx_ckm = conn->crypto.key_update.new_tx_ckm; + + assert(conn->callbacks.update_key); + + rv = conn->callbacks.update_key( + conn, new_rx_ckm->secret.base, new_tx_ckm->secret.base, &rx_aead_ctx, + new_rx_ckm->iv.base, &tx_aead_ctx, new_tx_ckm->iv.base, + rx_ckm->secret.base, tx_ckm->secret.base, secretlen, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + new_rx_ckm->aead_ctx = rx_aead_ctx; + new_tx_ckm->aead_ctx = tx_aead_ctx; + + if (!(rx_ckm->flags & NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE)) { + new_rx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; + new_tx_ckm->flags |= NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE; + } + + if (conn->crypto.key_update.old_rx_ckm) { + conn_call_delete_crypto_aead_ctx( + conn, &conn->crypto.key_update.old_rx_ckm->aead_ctx); + ngtcp2_crypto_km_del(conn->crypto.key_update.old_rx_ckm, conn->mem); + conn->crypto.key_update.old_rx_ckm = NULL; + } + + return 0; +} + +/* + * conn_rotate_keys rotates keys. The current key moves to old key, + * and new key moves to the current key. + */ +static void conn_rotate_keys(ngtcp2_conn *conn, int64_t pkt_num) { + ngtcp2_pktns *pktns = &conn->pktns; + + assert(conn->crypto.key_update.new_rx_ckm); + assert(conn->crypto.key_update.new_tx_ckm); + assert(!conn->crypto.key_update.old_rx_ckm); + assert(!(conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING)); + + conn->crypto.key_update.old_rx_ckm = pktns->crypto.rx.ckm; + + pktns->crypto.rx.ckm = conn->crypto.key_update.new_rx_ckm; + conn->crypto.key_update.new_rx_ckm = NULL; + pktns->crypto.rx.ckm->pkt_num = pkt_num; + + assert(pktns->crypto.tx.ckm); + + conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + + pktns->crypto.tx.ckm = conn->crypto.key_update.new_tx_ckm; + conn->crypto.key_update.new_tx_ckm = NULL; + pktns->crypto.tx.ckm->pkt_num = pktns->tx.last_pkt_num + 1; + + conn->flags |= NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; +} + +/* + * conn_path_validation_in_progress returns nonzero if path validation + * against |path| is underway. + */ +static int conn_path_validation_in_progress(ngtcp2_conn *conn, + const ngtcp2_path *path) { + ngtcp2_pv *pv = conn->pv; + + return pv && ngtcp2_path_eq(&pv->dcid.ps.path, path); +} + +/* + * conn_recv_non_probing_pkt_on_new_path is called when non-probing + * packet is received via new path. It starts path validation against + * the new path. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONN_ID_BLOCKED + * No DCID is available + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_recv_non_probing_pkt_on_new_path(ngtcp2_conn *conn, + const ngtcp2_path *path, + size_t dgramlen, + int new_cid_used, + ngtcp2_tstamp ts) { + + ngtcp2_dcid dcid, *bound_dcid, *last; + ngtcp2_pv *pv; + int rv; + ngtcp2_duration pto, initial_pto, timeout; + int require_new_cid; + int local_addr_eq; + uint32_t remote_addr_cmp; + size_t len, i; + + assert(conn->server); + + if (conn->pv && (conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + 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 + considered to be validated. */ + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PTV, + "path is migrated back to the original path"); + ngtcp2_dcid_copy(&conn->dcid.current, &conn->pv->fallback_dcid); + conn_reset_congestion_state(conn); + conn->dcid.current.bytes_recv += dgramlen; + conn_reset_ecn_validation_state(conn); + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + return rv; + } + return 0; + } + + remote_addr_cmp = + ngtcp2_addr_compare(&conn->dcid.current.ps.path.remote, &path->remote); + local_addr_eq = + ngtcp2_addr_eq(&conn->dcid.current.ps.path.local, &path->local); + + /* The transport specification draft-27 says: + * + * An endpoint MUST use a new connection ID if it initiates + * connection migration as described in Section 9.2 or probes a new + * network path as described in Section 9.1. An endpoint MUST use a + * new connection ID in response to a change in the address of a + * peer if the packet with the new peer address uses an active + * connection ID that has not been previously used by the peer. + */ + require_new_cid = conn->dcid.current.cid.datalen && + ((new_cid_used && remote_addr_cmp) || !local_addr_eq); + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "non-probing packet was received from new remote address"); + + pto = conn_compute_pto(conn, &conn->pktns); + initial_pto = conn_compute_initial_pto(conn, &conn->pktns); + timeout = 3 * ngtcp2_max(pto, initial_pto); + + len = ngtcp2_ringbuf_len(&conn->dcid.bound); + + for (i = 0; i < len; ++i) { + bound_dcid = ngtcp2_ringbuf_get(&conn->dcid.bound, 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); + } else if (i == ngtcp2_ringbuf_len(&conn->dcid.bound) - 1) { + ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + } else { + last = ngtcp2_ringbuf_get(&conn->dcid.bound, len - 1); + ngtcp2_dcid_copy(bound_dcid, last); + ngtcp2_ringbuf_pop_back(&conn->dcid.bound); + } + require_new_cid = 0; + + if (dcid.cid.datalen) { + rv = conn_call_activate_dcid(conn, &dcid); + if (rv != 0) { + return rv; + } + } + break; + } + } + + if (i == len) { + if (require_new_cid) { + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return NGTCP2_ERR_CONN_ID_BLOCKED; + } + ngtcp2_dcid_copy(&dcid, ngtcp2_ringbuf_get(&conn->dcid.unused, 0)); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &dcid); + if (rv != 0) { + return rv; + } + } else { + /* Use the current DCID if a remote endpoint does not change + DCID. */ + ngtcp2_dcid_copy(&dcid, &conn->dcid.current); + dcid.bytes_sent = 0; + dcid.bytes_recv = 0; + dcid.flags &= (uint8_t)~NGTCP2_DCID_FLAG_PATH_VALIDATED; + } + } + + ngtcp2_path_copy(&dcid.ps.path, path); + dcid.bytes_recv += dgramlen; + + rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE, + &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; + } else { + ngtcp2_dcid_copy(&pv->fallback_dcid, &conn->dcid.current); + pv->fallback_pto = pto; + } + + 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); + } + conn_reset_ecn_validation_state(conn); + + if (conn->pv) { + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_PTV, + "path migration is aborted because new migration has started"); + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + return rv; + } + } + + conn->pv = pv; + + return 0; +} + +/* + * conn_recv_pkt_from_new_path is called when a Short packet is + * received from new path (not current path). This packet would be a + * packet which only contains probing frame, or reordered packet, or a + * path is being validated. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CONN_ID_BLOCKED + * No unused DCID is available + * NGTCP2_ERR_NOMEM + * Out of memory + */ +static int conn_recv_pkt_from_new_path(ngtcp2_conn *conn, + const ngtcp2_path *path, size_t dgramlen, + int path_challenge_recved, + ngtcp2_tstamp ts) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_dcid *bound_dcid; + int rv; + + if (pv) { + if (ngtcp2_path_eq(&pv->dcid.ps.path, path)) { + pv->dcid.bytes_recv += dgramlen; + return 0; + } + + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + ngtcp2_path_eq(&pv->fallback_dcid.ps.path, path)) { + pv->fallback_dcid.bytes_recv += dgramlen; + return 0; + } + } + + if (!path_challenge_recved) { + return 0; + } + + rv = conn_bind_dcid(conn, &bound_dcid, path, ts); + if (rv != 0) { + return rv; + } + + ngtcp2_path_copy(&bound_dcid->ps.path, path); + bound_dcid->bytes_recv += dgramlen; + + return 0; +} + +/* + * conn_recv_delayed_handshake_pkt processes the received Handshake + * packet which is received after handshake completed. This function + * does the minimal job, and its purpose is send acknowledgement of + * this packet to the peer. We assume that hd->type == + * NGTCP2_PKT_HANDSHAKE. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Frame is badly formatted; or frame type is unknown. + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_DISCARD_PKT + * Packet was discarded. + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_PROTO + * Frame that is not allowed in Handshake packet is received. + */ +static int +conn_recv_delayed_handshake_pkt(ngtcp2_conn *conn, const ngtcp2_pkt_info *pi, + const ngtcp2_pkt_hd *hd, size_t pktlen, + const uint8_t *payload, size_t payloadlen, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + ngtcp2_max_frame mfr; + ngtcp2_frame *fr = &mfr.fr; + int rv; + int require_ack = 0; + ngtcp2_pktns *pktns; + + assert(hd->type == NGTCP2_PKT_HANDSHAKE); + + pktns = conn->hs_pktns; + + if (payloadlen == 0) { + /* QUIC packet must contain at least one frame */ + return NGTCP2_ERR_PROTO; + } + + ngtcp2_qlog_pkt_received_start(&conn->qlog); + + for (; payloadlen;) { + nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); + if (nread < 0) { + return (int)nread; + } + + payload += nread; + payloadlen -= (size_t)nread; + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + fr->ack.ack_delay = 0; + fr->ack.ack_delay_unscaled = 0; + break; + } + + ngtcp2_log_rx_fr(&conn->log, hd, fr); + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + if (!conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_PADDING: + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + conn_recv_connection_close(conn, &fr->connection_close); + break; + case NGTCP2_FRAME_CRYPTO: + case NGTCP2_FRAME_PING: + require_ack = 1; + break; + default: + return NGTCP2_ERR_PROTO; + } + + ngtcp2_qlog_write_frame(&conn->qlog, fr); + } + + ngtcp2_qlog_pkt_received_end(&conn->qlog, hd, pktlen); + + rv = pktns_commit_recv_pkt_num(pktns, hd->pkt_num, 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); + + return 0; +} + +/* + * conn_recv_pkt processes a packet contained in the buffer pointed by + * |pkt| of length |pktlen|. |pkt| may contain multiple QUIC packets. + * This function only processes the first packet. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of bytes processed if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_DISCARD_PKT + * Packet was discarded because plain text header was malformed; + * or its payload could not be decrypted. + * NGTCP2_ERR_PROTO + * Packet is badly formatted; or 0RTT packet contains other than + * PADDING or STREAM frames; or other QUIC protocol violation is + * found. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_FRAME_ENCODING + * Frame is badly formatted; or frame type is unknown. + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed. + * NGTCP2_ERR_STREAM_STATE + * Frame is received to the local stream which is not initiated. + * NGTCP2_ERR_STREAM_LIMIT + * Frame has remote stream ID which is strictly greater than the + * allowed limit. + * NGTCP2_ERR_FLOW_CONTROL + * Flow control limit is violated. + * NGTCP2_ERR_FINAL_SIZE + * Frame has strictly larger end offset than it is permitted. + */ +static ngtcp2_ssize conn_recv_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, size_t dgramlen, + ngtcp2_tstamp pkt_ts, ngtcp2_tstamp ts) { + ngtcp2_pkt_hd hd; + int rv = 0; + size_t hdpktlen; + const uint8_t *payload; + size_t payloadlen; + ngtcp2_ssize nread, nwrite; + ngtcp2_max_frame mfr; + ngtcp2_frame *fr = &mfr.fr; + int require_ack = 0; + ngtcp2_crypto_aead *aead; + ngtcp2_crypto_cipher *hp; + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx *hp_ctx; + ngtcp2_hp_mask hp_mask; + ngtcp2_decrypt decrypt; + ngtcp2_pktns *pktns; + int non_probing_pkt = 0; + int key_phase_bit_changed = 0; + int force_decrypt_failure = 0; + int recv_ncid = 0; + int new_cid_used = 0; + int path_challenge_recved = 0; + + if (pkt[0] & NGTCP2_HEADER_FORM_BIT) { + nread = ngtcp2_pkt_decode_hd_long(&hd, pkt, pktlen); + if (nread < 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decode long header"); + return NGTCP2_ERR_DISCARD_PKT; + } + + if (pktlen < (size_t)nread + hd.len || conn->version != hd.version) { + return NGTCP2_ERR_DISCARD_PKT; + } + + pktlen = (size_t)nread + hd.len; + + /* Quoted from spec: if subsequent packets of those types include + a different Source Connection ID, they MUST be discarded. */ + if (!ngtcp2_cid_eq(&conn->dcid.current.cid, &hd.scid)) { + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched SCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + switch (hd.type) { + case NGTCP2_PKT_INITIAL: + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "delayed Initial packet was discarded"); + return (ngtcp2_ssize)pktlen; + case NGTCP2_PKT_HANDSHAKE: + if (!conn->hs_pktns) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "delayed Handshake packet was discarded"); + return (ngtcp2_ssize)pktlen; + } + + pktns = conn->hs_pktns; + aead = &pktns->crypto.ctx.aead; + hp = &pktns->crypto.ctx.hp; + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + break; + case NGTCP2_PKT_0RTT: + if (!conn->server) { + return NGTCP2_ERR_DISCARD_PKT; + } + + if (!conn->early.ckm) { + return (ngtcp2_ssize)pktlen; + } + + pktns = &conn->pktns; + aead = &conn->early.ctx.aead; + hp = &conn->early.ctx.hp; + ckm = conn->early.ckm; + hp_ctx = &conn->early.hp_ctx; + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + break; + default: + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet type 0x%02x was ignored", hd.type); + return (ngtcp2_ssize)pktlen; + } + } else { + nread = ngtcp2_pkt_decode_hd_short(&hd, pkt, pktlen, conn->oscid.datalen); + if (nread < 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decode short header"); + return NGTCP2_ERR_DISCARD_PKT; + } + + pktns = &conn->pktns; + aead = &pktns->crypto.ctx.aead; + hp = &pktns->crypto.ctx.hp; + ckm = pktns->crypto.rx.ckm; + hp_ctx = &pktns->crypto.rx.hp_ctx; + hp_mask = conn->callbacks.hp_mask; + decrypt = conn->callbacks.decrypt; + } + + rv = conn_ensure_decrypt_hp_buffer(conn, (size_t)nread + 4); + if (rv != 0) { + return rv; + } + + nwrite = decrypt_hp(&hd, conn->crypto.decrypt_hp_buf.base, hp, pkt, pktlen, + (size_t)nread, ckm, hp_ctx, hp_mask); + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + hdpktlen = (size_t)nwrite; + payload = pkt + hdpktlen; + payloadlen = pktlen - hdpktlen; + + hd.pkt_num = ngtcp2_pkt_adjust_pkt_num(pktns->rx.max_pkt_num, hd.pkt_num, + pkt_num_bits(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); + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_log_rx_pkt_hd(&conn->log, &hd); + + if (hd.type == NGTCP2_PKT_SHORT) { + key_phase_bit_changed = conn_key_phase_changed(conn, &hd); + } + + rv = conn_ensure_decrypt_buffer(conn, payloadlen); + if (rv != 0) { + return rv; + } + + if (key_phase_bit_changed) { + assert(hd.type == NGTCP2_PKT_SHORT); + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, "unexpected KEY_PHASE"); + + if (ckm->pkt_num > hd.pkt_num) { + if (conn->crypto.key_update.old_rx_ckm) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "decrypting with old key"); + ckm = conn->crypto.key_update.old_rx_ckm; + } else { + force_decrypt_failure = 1; + } + } else if (pktns->rx.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, + "new key is not available"); + force_decrypt_failure = 1; + } else { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "decrypting with new key"); + ckm = conn->crypto.key_update.new_rx_ckm; + } + } else { + force_decrypt_failure = 1; + } + } + + nwrite = decrypt_pkt(conn->crypto.decrypt_buf.base, aead, payload, payloadlen, + conn->crypto.decrypt_hp_buf.base, hdpktlen, hd.pkt_num, + ckm, decrypt); + + if (force_decrypt_failure) { + nwrite = NGTCP2_ERR_TLS_DECRYPT; + } + + if (nwrite < 0) { + if (ngtcp2_err_is_fatal((int)nwrite)) { + return nwrite; + } + + assert(NGTCP2_ERR_TLS_DECRYPT == nwrite); + + if (hd.type == NGTCP2_PKT_SHORT && + ++conn->crypto.decryption_failure_count >= + pktns->crypto.ctx.max_decryption_failure) { + return NGTCP2_ERR_AEAD_LIMIT_REACHED; + } + + if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet payload"); + return NGTCP2_ERR_DISCARD_PKT; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "could not decrypt packet payload"); + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = ngtcp2_pkt_verify_reserved_bits(conn->crypto.decrypt_hp_buf.base[0]); + if (rv != 0) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet has incorrect reserved bits"); + + return NGTCP2_ERR_PROTO; + } + + if (pktns_pkt_num_is_duplicate(pktns, hd.pkt_num)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was discarded because of duplicated packet number"); + return NGTCP2_ERR_DISCARD_PKT; + } + + payload = conn->crypto.decrypt_buf.base; + payloadlen = (size_t)nwrite; + + if (payloadlen == 0) { + /* QUIC packet must contain at least one frame */ + return NGTCP2_ERR_PROTO; + } + + if (hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) { + switch (hd.type) { + case NGTCP2_PKT_HANDSHAKE: + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + rv = conn_recv_delayed_handshake_pkt(conn, pi, &hd, pktlen, payload, + payloadlen, pkt_ts, ts); + if (rv < 0) { + return (ngtcp2_ssize)rv; + } + + return (ngtcp2_ssize)pktlen; + case NGTCP2_PKT_0RTT: + if (!ngtcp2_cid_eq(&conn->rcid, &hd.dcid)) { + rv = conn_verify_dcid(conn, NULL, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + } + break; + default: + /* Unreachable */ + assert(0); + } + } else { + rv = conn_verify_dcid(conn, &new_cid_used, &hd); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "packet was ignored because of mismatched DCID"); + return NGTCP2_ERR_DISCARD_PKT; + } + + conn->flags |= NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT; + } + + ngtcp2_qlog_pkt_received_start(&conn->qlog); + + for (; payloadlen;) { + nread = ngtcp2_pkt_decode_frame(fr, payload, payloadlen); + if (nread < 0) { + return nread; + } + + payload += nread; + payloadlen -= (size_t)nread; + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + if ((hd.flags & NGTCP2_PKT_FLAG_LONG_FORM) && + hd.type == NGTCP2_PKT_0RTT) { + return NGTCP2_ERR_PROTO; + } + assign_recved_ack_delay_unscaled( + &fr->ack, conn->remote.transport_params.ack_delay_exponent); + break; + } + + ngtcp2_log_rx_fr(&conn->log, &hd, fr); + + if (hd.type == NGTCP2_PKT_0RTT) { + switch (fr->type) { + case NGTCP2_FRAME_PADDING: + case NGTCP2_FRAME_PING: + case NGTCP2_FRAME_RESET_STREAM: + case NGTCP2_FRAME_STOP_SENDING: + case NGTCP2_FRAME_STREAM: + case NGTCP2_FRAME_MAX_DATA: + case NGTCP2_FRAME_MAX_STREAM_DATA: + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + case NGTCP2_FRAME_DATA_BLOCKED: + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + case NGTCP2_FRAME_NEW_CONNECTION_ID: + case NGTCP2_FRAME_PATH_CHALLENGE: + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + break; + default: + return NGTCP2_ERR_PROTO; + } + } + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + case NGTCP2_FRAME_PADDING: + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + break; + default: + require_ack = 1; + } + + switch (fr->type) { + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + if (!conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED; + } + rv = conn_recv_ack(conn, pktns, &fr->ack, pkt_ts, ts); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STREAM: + rv = conn_recv_stream(conn, &fr->stream); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_CRYPTO: + rv = conn_recv_crypto(conn, NGTCP2_CRYPTO_LEVEL_APPLICATION, + &pktns->crypto.strm, &fr->crypto); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_RESET_STREAM: + rv = conn_recv_reset_stream(conn, &fr->reset_stream); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STOP_SENDING: + rv = conn_recv_stop_sending(conn, &fr->stop_sending); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + rv = conn_recv_max_stream_data(conn, &fr->max_stream_data); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_MAX_DATA: + conn_recv_max_data(conn, &fr->max_data); + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + rv = conn_recv_max_streams(conn, &fr->max_streams); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + conn_recv_connection_close(conn, &fr->connection_close); + break; + case NGTCP2_FRAME_PING: + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_PATH_CHALLENGE: + conn_recv_path_challenge(conn, path, &fr->path_challenge); + path_challenge_recved = 1; + break; + case NGTCP2_FRAME_PATH_RESPONSE: + rv = conn_recv_path_response(conn, &fr->path_response, ts); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_NEW_CONNECTION_ID: + rv = conn_recv_new_connection_id(conn, &fr->new_connection_id); + if (rv != 0) { + return rv; + } + recv_ncid = 1; + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + rv = conn_recv_retire_connection_id(conn, &hd, &fr->retire_connection_id, + ts); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_NEW_TOKEN: + rv = conn_recv_new_token(conn, &fr->new_token); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + rv = conn_recv_handshake_done(conn, ts); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + rv = conn_recv_streams_blocked_bidi(conn, &fr->streams_blocked); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + rv = conn_recv_streams_blocked_uni(conn, &fr->streams_blocked); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_DATA_BLOCKED: + /* TODO Not implemented yet */ + non_probing_pkt = 1; + break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + if ((uint64_t)nread > + conn->local.transport_params.max_datagram_frame_size) { + return NGTCP2_ERR_PROTO; + } + rv = conn_recv_datagram(conn, &fr->datagram); + if (rv != 0) { + return rv; + } + non_probing_pkt = 1; + break; + } + + ngtcp2_qlog_write_frame(&conn->qlog, fr); + } + + ngtcp2_qlog_pkt_received_end(&conn->qlog, &hd, pktlen); + + if (recv_ncid) { + rv = conn_post_process_recv_new_connection_id(conn, ts); + if (rv != 0) { + return rv; + } + } + + if (conn->server && hd.type == NGTCP2_PKT_SHORT && + !ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + if (non_probing_pkt && pktns->rx.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); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + + /* DCID is not available. Just continue. */ + assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv); + } + } else { + rv = conn_recv_pkt_from_new_path(conn, path, dgramlen, + path_challenge_recved, ts); + if (rv != 0) { + if (ngtcp2_err_is_fatal(rv)) { + return rv; + } + + /* DCID is not available. Just continue. */ + assert(NGTCP2_ERR_CONN_ID_BLOCKED == rv); + } + } + } + + if (hd.type == NGTCP2_PKT_SHORT) { + if (ckm == conn->crypto.key_update.new_rx_ckm) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "rotate keys"); + conn_rotate_keys(conn, hd.pkt_num); + } else if (ckm->pkt_num > hd.pkt_num) { + ckm->pkt_num = hd.pkt_num; + } + + if (conn->server && conn->early.ckm && + conn->early.discard_started_ts == UINT64_MAX) { + conn->early.discard_started_ts = ts; + } + } + + rv = pktns_commit_recv_pkt_num(pktns, hd.pkt_num, 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); + + return conn->state == NGTCP2_CS_DRAINING ? NGTCP2_ERR_DRAINING + : (ngtcp2_ssize)pktlen; +} + +/* + * conn_process_buffered_protected_pkt processes buffered 0RTT or + * Short packets. + * + * This function returns 0 if it succeeds, or the same negative error + * codes from conn_recv_pkt. + */ +static int conn_process_buffered_protected_pkt(ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + ngtcp2_pkt_chain **ppc, *next; + int rv; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "processing buffered protected packet"); + + for (ppc = &pktns->rx.buffed_pkts; *ppc;) { + next = (*ppc)->next; + nread = conn_recv_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi, (*ppc)->pkt, + (*ppc)->pktlen, (*ppc)->dgramlen, (*ppc)->ts, ts); + if (nread < 0 && !ngtcp2_err_is_fatal((int)nread) && + nread != NGTCP2_ERR_DRAINING) { + /* TODO We don't know this is the first QUIC packet in a + datagram. */ + rv = conn_on_stateless_reset(conn, &(*ppc)->path.path, (*ppc)->pkt, + (*ppc)->pktlen); + if (rv == 0) { + ngtcp2_pkt_chain_del(*ppc, conn->mem); + *ppc = next; + return NGTCP2_ERR_DRAINING; + } + } + + ngtcp2_pkt_chain_del(*ppc, conn->mem); + *ppc = next; + if (nread < 0) { + if (nread == NGTCP2_ERR_DISCARD_PKT) { + continue; + } + return (int)nread; + } + } + + return 0; +} + +/* + * conn_process_buffered_handshake_pkt processes buffered Handshake + * packets. + * + * This function returns 0 if it succeeds, or the same negative error + * codes from conn_recv_handshake_pkt. + */ +static int conn_process_buffered_handshake_pkt(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + ngtcp2_pktns *pktns = conn->hs_pktns; + ngtcp2_ssize nread; + ngtcp2_pkt_chain **ppc, *next; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "processing buffered handshake packet"); + + for (ppc = &pktns->rx.buffed_pkts; *ppc;) { + next = (*ppc)->next; + nread = conn_recv_handshake_pkt(conn, &(*ppc)->path.path, &(*ppc)->pi, + (*ppc)->pkt, (*ppc)->pktlen, + (*ppc)->dgramlen, (*ppc)->ts, ts); + ngtcp2_pkt_chain_del(*ppc, conn->mem); + *ppc = next; + if (nread < 0) { + if (nread == NGTCP2_ERR_DISCARD_PKT) { + continue; + } + return (int)nread; + } + } + + return 0; +} + +static void conn_sync_stream_id_limit(ngtcp2_conn *conn) { + ngtcp2_transport_params *params = &conn->remote.transport_params; + + conn->local.bidi.max_streams = params->initial_max_streams_bidi; + conn->local.uni.max_streams = params->initial_max_streams_uni; +} + +/* + * conn_handshake_completed is called once cryptographic handshake has + * completed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed. + */ +static int conn_handshake_completed(ngtcp2_conn *conn) { + int rv; + + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED; + + rv = conn_call_handshake_completed(conn); + if (rv != 0) { + return rv; + } + + if (conn->local.bidi.max_streams > 0) { + rv = conn_call_extend_max_local_streams_bidi(conn, + conn->local.bidi.max_streams); + if (rv != 0) { + return rv; + } + } + if (conn->local.uni.max_streams > 0) { + rv = conn_call_extend_max_local_streams_uni(conn, + conn->local.uni.max_streams); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +/* + * conn_recv_cpkt processes compound packet after handshake. The + * buffer pointed by |pkt| might contain multiple packets. The Short + * packet must be the last one because it does not have payload length + * field. + * + * This function returns 0 if it succeeds, or the same negative error + * codes from conn_recv_pkt except for NGTCP2_ERR_DISCARD_PKT. + */ +static int conn_recv_cpkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, ngtcp2_tstamp ts) { + ngtcp2_ssize nread; + int rv; + const uint8_t *origpkt = pkt; + size_t dgramlen = pktlen; + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + conn->dcid.current.bytes_recv += dgramlen; + } + + while (pktlen) { + nread = conn_recv_pkt(conn, path, pi, pkt, pktlen, dgramlen, ts, ts); + if (nread < 0) { + if (ngtcp2_err_is_fatal((int)nread)) { + return (int)nread; + } + + if (nread == NGTCP2_ERR_DRAINING) { + return NGTCP2_ERR_DRAINING; + } + + if (origpkt == pkt) { + rv = conn_on_stateless_reset(conn, path, origpkt, dgramlen); + if (rv == 0) { + return NGTCP2_ERR_DRAINING; + } + } + if (nread == NGTCP2_ERR_DISCARD_PKT) { + return 0; + } + return (int)nread; + } + + assert(pktlen >= (size_t)nread); + pkt += nread; + pktlen -= (size_t)nread; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_PKT, + "read packet %td left %zu", nread, pktlen); + } + + 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); + ngtcp2_dcid *dcid; + + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + if (ngtcp2_path_eq(&dcid->ps.path, path)) { + return 1; + } + } + + return 0; +} + +/* + * conn_enqueue_handshake_done enqueues HANDSHAKE_DONE frame for + * transmission. + */ +static int conn_enqueue_handshake_done(ngtcp2_conn *conn) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_frame_chain *nfrc; + int rv; + + assert(conn->server); + + rv = ngtcp2_frame_chain_new(&nfrc, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_HANDSHAKE_DONE; + nfrc->next = pktns->tx.frq; + pktns->tx.frq = nfrc; + + return 0; +} + +/** + * @function + * + * `conn_read_handshake` performs QUIC cryptographic handshake by + * reading given data. |pkt| points to the buffer to read and + * |pktlen| is the length of the buffer. |path| is the network path. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: (TBD). + */ +static int conn_read_handshake(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, ngtcp2_tstamp ts) { + int rv; + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + /* TODO Better to log something when we ignore input */ + return 0; + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (rv < 0) { + return rv; + } + + if (conn->state == NGTCP2_CS_CLIENT_INITIAL) { + /* Retry packet was received */ + return 0; + } + + assert(conn->hs_pktns); + + if (conn->hs_pktns->crypto.rx.ckm && conn->in_pktns) { + rv = conn_process_buffered_handshake_pkt(conn, ts); + if (rv != 0) { + return rv; + } + } + + if ((conn->flags & (NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED | + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) == + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) { + rv = conn_handshake_completed(conn); + if (rv != 0) { + return rv; + } + } + + return 0; + case NGTCP2_CS_SERVER_INITIAL: + rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (rv < 0) { + return rv; + } + + /* + * Client ServerHello might not fit into single Initial packet + * (e.g., resuming session with client authentication). If we get + * Client Initial which does not increase offset or it is 0RTT + * packet buffered, perform address validation in order to buffer + * validated data only. + */ + if (ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0) { + if (conn->in_pktns->crypto.strm.rx.rob && + ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob)) { + /* Address has been validated with token */ + if (conn->local.settings.token.len) { + return 0; + } + return NGTCP2_ERR_RETRY; + } + if (conn->in_pktns->rx.buffed_pkts) { + /* 0RTT is buffered, force retry */ + return NGTCP2_ERR_RETRY; + } + /* If neither CRYPTO frame nor 0RTT packet is processed, just + drop connection. */ + return NGTCP2_ERR_DROP_CONN; + } + + /* Process re-ordered 0-RTT packets which arrived before Initial + packet. */ + if (conn->early.ckm) { + assert(conn->in_pktns); + + rv = conn_process_buffered_protected_pkt(conn, conn->in_pktns, ts); + if (rv != 0) { + return rv; + } + } + + return 0; + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + rv = conn_recv_handshake_cpkt(conn, path, pi, pkt, pktlen, ts); + if (rv < 0) { + return rv; + } + + if (conn->hs_pktns->crypto.rx.ckm) { + rv = conn_process_buffered_handshake_pkt(conn, ts); + if (rv != 0) { + return rv; + } + } + + if (conn->hs_pktns->rx.max_pkt_num != -1) { + conn_discard_initial_state(conn, ts); + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + /* If server hits amplification limit, it cancels loss detection + timer. If server receives a packet from client, the limit is + increased and server can send more. If server has + ack-eliciting Initial or Handshake packets, it should resend + it if timer fired but timer is not armed in this case. So + instead of resending Initial/Handshake packets, if server has + 1RTT data to send, it might send them and then might hit + amplification limit again until it hits stream data limit. + Initial/Handshake data is not resent. In order to avoid this + situation, try to arm loss detection and check the expiry + here so that on next write call, we can resend + Initial/Handshake first. */ + if (conn->cstat.loss_detection_timer == UINT64_MAX) { + ngtcp2_conn_set_loss_detection_timer(conn, ts); + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { + rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); + if (rv != 0) { + return rv; + } + } + } + + return 0; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } + + rv = conn_handshake_completed(conn); + if (rv != 0) { + return rv; + } + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); + if (rv != 0) { + return rv; + } + + conn_discard_handshake_state(conn, ts); + + rv = conn_enqueue_handshake_done(conn); + if (rv != 0) { + return rv; + } + + conn->pktns.rtb.persistent_congestion_start_ts = ts; + + /* Re-arm loss detection timer here after handshake has been + confirmed. */ + ngtcp2_conn_set_loss_detection_timer(conn, ts); + + return 0; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } +} + +int ngtcp2_conn_read_pkt(ngtcp2_conn *conn, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, ngtcp2_tstamp ts) { + int rv = 0; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "recv packet len=%zu", + pktlen); + + if (pktlen == 0) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + /* 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_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "ignore packet from unknown path"); + return 0; + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: + return conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + case NGTCP2_CS_SERVER_INITIAL: + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: + if (!ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "ignore packet from unknown path during handshake"); + + if (conn->state == NGTCP2_CS_SERVER_INITIAL && + ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm) == 0 && + (!conn->in_pktns->crypto.strm.rx.rob || + !ngtcp2_rob_data_buffered(conn->in_pktns->crypto.strm.rx.rob))) { + return NGTCP2_ERR_DROP_CONN; + } + + return 0; + } + return conn_read_handshake(conn, path, pi, pkt, pktlen, ts); + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + case NGTCP2_CS_POST_HANDSHAKE: + rv = conn_prepare_key_update(conn, ts); + if (rv != 0) { + return rv; + } + return conn_recv_cpkt(conn, path, pi, pkt, pktlen, ts); + default: + assert(0); + abort(); + } +} + +/* + * conn_check_pkt_num_exhausted returns nonzero if packet number is + * exhausted in at least one of packet number space. + */ +static int conn_check_pkt_num_exhausted(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + + return (in_pktns && in_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || + (hs_pktns && hs_pktns->tx.last_pkt_num == NGTCP2_MAX_PKT_NUM) || + conn->pktns.tx.last_pkt_num == NGTCP2_MAX_PKT_NUM; +} + +/* + * conn_retransmit_retry_early retransmits 0RTT packet after Retry is + * received from server. + */ +static ngtcp2_ssize conn_retransmit_retry_early(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_tstamp ts) { + return conn_write_pkt(conn, pi, dest, destlen, NULL, NGTCP2_PKT_0RTT, + NGTCP2_WRITE_PKT_FLAG_NONE, ts); +} + +/* + * conn_handshake_probe_left returns nonzero if there are probe + * packets to be sent for Initial or Handshake packet number space + * left. + */ +static int conn_handshake_probe_left(ngtcp2_conn *conn) { + return (conn->in_pktns && conn->in_pktns->rtb.probe_pkt_left) || + conn->hs_pktns->rtb.probe_pkt_left; +} + +/* + * conn_write_handshake writes QUIC handshake packets to the buffer + * pointed by |dest| of length |destlen|. |early_datalen| specifies + * the expected length of early data to send. Specify 0 to + * |early_datalen| if there is no early data. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * NGTCP2_ERR_PKT_NUM_EXHAUSTED + * Packet number is exhausted. + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM + * Required transport parameter is missing. + * NGTCP2_CS_CLOSING + * Connection is in closing state. + * NGTCP2_CS_DRAINING + * Connection is in draining state. + * + * In addition to the above negative error codes, the same error codes + * from conn_recv_pkt may also be returned. + */ +static ngtcp2_ssize conn_write_handshake(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + size_t early_datalen, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_ssize res = 0, nwrite = 0, early_spktlen = 0; + size_t origlen = destlen; + size_t server_tx_left; + ngtcp2_conn_stat *cstat = &conn->cstat; + size_t pending_early_datalen; + ngtcp2_dcid *dcid; + ngtcp2_preferred_addr *paddr; + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + pending_early_datalen = conn_retry_early_payloadlen(conn); + if (pending_early_datalen) { + early_datalen = pending_early_datalen; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY)) { + nwrite = + conn_write_client_initial(conn, pi, dest, destlen, early_datalen, ts); + if (nwrite <= 0) { + return nwrite; + } + } else { + nwrite = conn_write_handshake_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, + NGTCP2_WRITE_PKT_FLAG_NONE, early_datalen, ts); + if (nwrite < 0) { + return nwrite; + } + } + + if (pending_early_datalen) { + early_spktlen = conn_retransmit_retry_early(conn, pi, dest + nwrite, + destlen - (size_t)nwrite, ts); + + if (early_spktlen < 0) { + assert(ngtcp2_err_is_fatal((int)early_spktlen)); + return early_spktlen; + } + } + + conn->state = NGTCP2_CS_CLIENT_WAIT_HANDSHAKE; + + res = nwrite + early_spktlen; + + return res; + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + if (!conn_handshake_probe_left(conn) && conn_cwnd_is_zero(conn)) { + destlen = 0; + } else { + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + pending_early_datalen = conn_retry_early_payloadlen(conn); + if (pending_early_datalen) { + early_datalen = pending_early_datalen; + } + } + + nwrite = + conn_write_handshake_pkts(conn, pi, dest, destlen, early_datalen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + nwrite = conn_retransmit_retry_early(conn, pi, dest, destlen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + } + + if (res == 0) { + nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; + } + res = nwrite; + } + + return res; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } + + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + if (conn->remote.transport_params.preferred_address_present) { + assert(!ngtcp2_ringbuf_full(&conn->dcid.unused)); + + paddr = &conn->remote.transport_params.preferred_address; + dcid = ngtcp2_ringbuf_push_back(&conn->dcid.unused); + ngtcp2_dcid_init(dcid, 1, &paddr->cid, paddr->stateless_reset_token); + + rv = ngtcp2_gaptr_push(&conn->dcid.seqgap, 1, 1); + if (rv != 0) { + return (ngtcp2_ssize)rv; + } + } + + if (conn->remote.transport_params.stateless_reset_token_present) { + assert(conn->dcid.current.seq == 0); + memcpy(conn->dcid.current.token, + conn->remote.transport_params.stateless_reset_token, + sizeof(conn->dcid.current.token)); + } + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + + conn_process_early_rtb(conn); + + rv = conn_process_buffered_protected_pkt(conn, &conn->pktns, ts); + if (rv != 0) { + return (ngtcp2_ssize)rv; + } + + return res; + case NGTCP2_CS_SERVER_INITIAL: + nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, + /* early_datalen = */ 0, ts); + if (nwrite < 0) { + return nwrite; + } + + if (nwrite) { + conn->state = NGTCP2_CS_SERVER_WAIT_HANDSHAKE; + } + + return nwrite; + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + if (server_tx_left == 0) { + if (cstat->loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + cstat->loss_detection_timer = UINT64_MAX; + cstat->pto_count = 0; + } + return 0; + } + } + + if (conn_handshake_probe_left(conn) || !conn_cwnd_is_zero(conn)) { + nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, + /* early_datalen = */ 0, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + + if (res == 0) { + nwrite = conn_write_handshake_ack_pkts(conn, pi, dest, origlen, ts); + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + dest += nwrite; + origlen -= (size_t)nwrite; + } + + return res; + } + + return 0; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } +} + +/** + * @function + * + * `conn_client_write_handshake` writes client side handshake data and + * 0RTT packet. + * + * In order to send STREAM data in 0RTT packet, specify + * |vmsg|->stream. |vmsg|->stream.strm, |vmsg|->stream.fin, + * |vmsg|->stream.data, and |vmsg|->stream.datacnt are stream to which + * 0-RTT data is sent, whether it is a last data chunk in this stream, + * a vector of 0-RTT data, and its number of elements respectively. + * The amount of 0RTT data sent is assigned to + * *|vmsg|->stream.pdatalen. If no data is sent, -1 is assigned. + * Note that 0 length STREAM frame is allowed in QUIC, so 0 might be + * assigned to *|vmsg|->stream.pdatalen. + * + * This function returns 0 if it cannot write any frame because buffer + * is too small, or packet is congestion limited. Application should + * keep reading and wait for congestion window to grow. + * + * This function returns the number of bytes written to the buffer + * pointed by |dest| if it succeeds, or one of the following negative + * error codes: (TBD). + */ +static ngtcp2_ssize conn_client_write_handshake(ngtcp2_conn *conn, + ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, + ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts) { + int send_stream = 0; + int send_datagram = 0; + ngtcp2_ssize spktlen, early_spktlen; + int was_client_initial; + size_t datalen; + size_t early_datalen = 0; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + + assert(!conn->server); + + /* conn->early.ckm might be created in the first call of + conn_handshake(). Check it later. */ + if (vmsg && !(conn->flags & NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED)) { + switch (vmsg->type) { + case NGTCP2_VMSG_TYPE_STREAM: + datalen = ngtcp2_vec_len(vmsg->stream.data, vmsg->stream.datacnt); + send_stream = + conn_retry_early_payloadlen(conn) == 0 && + /* 0 length STREAM frame is allowed */ + (datalen == 0 || + (datalen > 0 && + (vmsg->stream.strm->tx.max_offset - vmsg->stream.strm->tx.offset) && + (conn->tx.max_offset - conn->tx.offset))); + if (send_stream) { + early_datalen = + conn_enforce_flow_control(conn, vmsg->stream.strm, datalen) + + NGTCP2_STREAM_OVERHEAD; + + if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; + } + } else { + vmsg = NULL; + } + break; + case NGTCP2_VMSG_TYPE_DATAGRAM: + datalen = ngtcp2_vec_len(vmsg->datagram.data, vmsg->datagram.datacnt); + /* TODO Do we need this? DATAGRAM is independent from STREAM + data and no retransmission */ + send_datagram = conn_retry_early_payloadlen(conn) == 0; + if (send_datagram) { + early_datalen = datalen + NGTCP2_DATAGRAM_OVERHEAD; + + if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; + } + } else { + vmsg = NULL; + } + break; + } + } + + if (!ppe_pending) { + was_client_initial = conn->state == NGTCP2_CS_CLIENT_INITIAL; + spktlen = conn_write_handshake(conn, pi, dest, destlen, early_datalen, ts); + + if (spktlen < 0) { + return spktlen; + } + + if (conn->pktns.crypto.tx.ckm || !conn->early.ckm || + (!send_stream && !send_datagram)) { + return spktlen; + } + } else { + assert(!conn->pktns.crypto.tx.ckm); + assert(conn->early.ckm); + + was_client_initial = conn->pkt.was_client_initial; + spktlen = conn->pkt.hs_spktlen; + } + + /* If spktlen > 0, we are making a compound packet. If Initial + packet is written, we have to pad bytes to 0-RTT packet. */ + + if (spktlen && was_client_initial) { + wflags |= NGTCP2_WRITE_PKT_FLAG_REQUIRE_PADDING; + } + + dest += spktlen; + destlen -= (size_t)spktlen; + + if (conn_cwnd_is_zero(conn)) { + return spktlen; + } + + early_spktlen = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_0RTT, + wflags, ts); + + if (early_spktlen < 0) { + switch (early_spktlen) { + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + return spktlen; + case NGTCP2_ERR_WRITE_MORE: + conn->pkt.was_client_initial = was_client_initial; + conn->pkt.hs_spktlen = spktlen; + break; + } + return early_spktlen; + } + + return spktlen + early_spktlen; +} + +void ngtcp2_conn_handshake_completed(ngtcp2_conn *conn) { + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED; + if (conn->server) { + conn->flags |= NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + } +} + +int ngtcp2_conn_get_handshake_completed(ngtcp2_conn *conn) { + return (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) && + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED); +} + +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; + + if (dest) { + p = dest; + } else { + p = &hd; + } + + if (pktlen == 0 || (pkt[0] & NGTCP2_HEADER_FORM_BIT) == 0) { + return -1; + } + + nread = ngtcp2_pkt_decode_hd_long(p, pkt, pktlen); + if (nread < 0) { + return -1; + } + + switch (p->type) { + case NGTCP2_PKT_INITIAL: + if (pktlen < NGTCP2_MIN_INITIAL_PKTLEN) { + return -1; + } + if (p->token.len == 0 && p->dcid.datalen < NGTCP2_MIN_INITIAL_DCIDLEN) { + return -1; + } + break; + case NGTCP2_PKT_0RTT: + /* 0-RTT packet may arrive before Initial packet due to + re-ordering. */ + break; + default: + return -1; + } + + if (p->version != NGTCP2_PROTO_VER_V1 && + (p->version < NGTCP2_PROTO_VER_DRAFT_MIN || + NGTCP2_PROTO_VER_DRAFT_MAX < p->version)) { + return 1; + } + + return 0; +} + +int ngtcp2_conn_install_initial_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *rx_aead_ctx, + const uint8_t *rx_iv, const ngtcp2_crypto_cipher_ctx *rx_hp_ctx, + const ngtcp2_crypto_aead_ctx *tx_aead_ctx, const uint8_t *tx_iv, + const ngtcp2_crypto_cipher_ctx *tx_hp_ctx, size_t ivlen) { + ngtcp2_pktns *pktns = conn->in_pktns; + int rv; + + assert(pktns); + + conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.rx.hp_ctx); + pktns->crypto.rx.hp_ctx.native_handle = NULL; + + if (pktns->crypto.rx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.rx.ckm->aead_ctx); + ngtcp2_crypto_km_del(pktns->crypto.rx.ckm, conn->mem); + pktns->crypto.rx.ckm = NULL; + } + + conn_call_delete_crypto_cipher_ctx(conn, &pktns->crypto.tx.hp_ctx); + pktns->crypto.tx.hp_ctx.native_handle = NULL; + + if (pktns->crypto.tx.ckm) { + conn_call_delete_crypto_aead_ctx(conn, &pktns->crypto.tx.ckm->aead_ctx); + ngtcp2_crypto_km_del(pktns->crypto.tx.ckm, conn->mem); + pktns->crypto.tx.ckm = NULL; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, NULL, rx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, NULL, tx_iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + /* Take owner ship after we are sure that no failure occurs, so that + caller can delete these contexts on failure. */ + pktns->crypto.rx.ckm->aead_ctx = *rx_aead_ctx; + pktns->crypto.rx.hp_ctx = *rx_hp_ctx; + pktns->crypto.tx.ckm->aead_ctx = *tx_aead_ctx; + pktns->crypto.tx.hp_ctx = *tx_hp_ctx; + + return 0; +} + +int ngtcp2_conn_install_rx_handshake_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pktns *pktns = conn->hs_pktns; + int rv; + + assert(pktns); + assert(!pktns->crypto.rx.hp_ctx.native_handle); + assert(!pktns->crypto.rx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, NULL, 0, aead_ctx, iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + pktns->crypto.rx.hp_ctx = *hp_ctx; + + return 0; +} + +int ngtcp2_conn_install_tx_handshake_key( + ngtcp2_conn *conn, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pktns *pktns = conn->hs_pktns; + int rv; + + assert(pktns); + assert(!pktns->crypto.tx.hp_ctx.native_handle); + assert(!pktns->crypto.tx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, NULL, 0, aead_ctx, iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + pktns->crypto.tx.hp_ctx = *hp_ctx; + + if (conn->server) { + return ngtcp2_conn_commit_local_transport_params(conn); + } + + return 0; +} + +int ngtcp2_conn_install_early_key(ngtcp2_conn *conn, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx) { + int rv; + + assert(!conn->early.hp_ctx.native_handle); + assert(!conn->early.ckm); + + rv = ngtcp2_crypto_km_new(&conn->early.ckm, NULL, 0, aead_ctx, iv, ivlen, + conn->mem); + if (rv != 0) { + return rv; + } + + conn->early.hp_ctx = *hp_ctx; + + return 0; +} + +int ngtcp2_conn_install_rx_key(ngtcp2_conn *conn, const uint8_t *secret, + size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pktns *pktns = &conn->pktns; + int rv; + + assert(!pktns->crypto.rx.hp_ctx.native_handle); + assert(!pktns->crypto.rx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.rx.ckm, secret, secretlen, aead_ctx, + iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + pktns->crypto.rx.hp_ctx = *hp_ctx; + + if (!conn->server) { + conn->remote.transport_params = conn->remote.pending_transport_params; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + } + + return 0; +} + +int ngtcp2_conn_install_tx_key(ngtcp2_conn *conn, const uint8_t *secret, + size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pktns *pktns = &conn->pktns; + int rv; + + assert(!pktns->crypto.tx.hp_ctx.native_handle); + assert(!pktns->crypto.tx.ckm); + + rv = ngtcp2_crypto_km_new(&pktns->crypto.tx.ckm, secret, secretlen, aead_ctx, + iv, ivlen, conn->mem); + if (rv != 0) { + return rv; + } + + pktns->crypto.tx.hp_ctx = *hp_ctx; + + if (conn->server) { + conn->remote.transport_params = conn->remote.pending_transport_params; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + } else if (conn->early.ckm) { + conn_discard_early_key(conn); + } + + return 0; +} + +int ngtcp2_conn_initiate_key_update(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_tstamp confirmed_ts = conn->crypto.key_update.confirmed_ts; + ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + + assert(conn->state == NGTCP2_CS_POST_HANDSHAKE); + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) || + (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) || + !conn->crypto.key_update.new_tx_ckm || + !conn->crypto.key_update.new_rx_ckm || + (confirmed_ts != UINT64_MAX && confirmed_ts + 3 * pto > ts)) { + return NGTCP2_ERR_INVALID_STATE; + } + + conn_rotate_keys(conn, NGTCP2_MAX_PKT_NUM); + + 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, t; + ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + ngtcp2_scid *scid; + ngtcp2_dcid *dcid; + + if (conn->pv) { + res = ngtcp2_pv_next_expiry(conn->pv); + } + + if (!ngtcp2_pq_empty(&conn->scid.used)) { + scid = ngtcp2_struct_of(ngtcp2_pq_top(&conn->scid.used), ngtcp2_scid, pe); + if (scid->ts_retired != UINT64_MAX) { + res = ngtcp2_min(res, scid->ts_retired + pto); + } + } + + if (ngtcp2_ringbuf_len(&conn->dcid.retired)) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, 0); + res = ngtcp2_min(res, dcid->ts_retired + pto); + } + + if (conn->server && conn->early.ckm && + conn->early.discard_started_ts != UINT64_MAX) { + t = conn->early.discard_started_ts + 3 * pto; + res = ngtcp2_min(res, t); + } + + return res; +} + +ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn) { + ngtcp2_acktr *acktr = &conn->pktns.acktr; + + if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + acktr->first_unacked_ts != UINT64_MAX) { + return acktr->first_unacked_ts + conn_compute_ack_delay(conn); + } + return UINT64_MAX; +} + +ngtcp2_tstamp ngtcp2_conn_get_expiry(ngtcp2_conn *conn) { + ngtcp2_tstamp t1 = ngtcp2_conn_loss_detection_expiry(conn); + ngtcp2_tstamp t2 = ngtcp2_conn_ack_delay_expiry(conn); + ngtcp2_tstamp t3 = ngtcp2_conn_internal_expiry(conn); + ngtcp2_tstamp t4 = ngtcp2_conn_lost_pkt_expiry(conn); + ngtcp2_tstamp res = ngtcp2_min(t1, t2); + res = ngtcp2_min(res, t3); + return ngtcp2_min(res, t4); +} + +int ngtcp2_conn_handle_expiry(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + int rv; + ngtcp2_duration pto = conn_compute_pto(conn, &conn->pktns); + + ngtcp2_conn_cancel_expired_ack_delay_timer(conn, ts); + + ngtcp2_conn_remove_lost_pkt(conn, ts); + + if (conn->pv) { + ngtcp2_pv_cancel_expired_timer(conn->pv, ts); + } + + if (ngtcp2_conn_loss_detection_expiry(conn) <= ts) { + rv = ngtcp2_conn_on_loss_detection_timer(conn, ts); + if (rv != 0) { + return rv; + } + } + + rv = conn_remove_retired_connection_id(conn, pto, ts); + if (rv != 0) { + return rv; + } + + if (conn->server && conn->early.ckm && + conn->early.discard_started_ts != UINT64_MAX) { + if (conn->early.discard_started_ts + 3 * pto <= ts) { + conn_discard_early_key(conn); + } + } + + return 0; +} + +static void acktr_cancel_expired_ack_delay_timer(ngtcp2_acktr *acktr, + ngtcp2_tstamp ts) { + if (!(acktr->flags & NGTCP2_ACKTR_FLAG_CANCEL_TIMER) && + acktr->first_unacked_ts <= ts) { + acktr->flags |= NGTCP2_ACKTR_FLAG_CANCEL_TIMER; + } +} + +void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts) { + if (conn->in_pktns) { + acktr_cancel_expired_ack_delay_timer(&conn->in_pktns->acktr, ts); + } + if (conn->hs_pktns) { + acktr_cancel_expired_ack_delay_timer(&conn->hs_pktns->acktr, ts); + } + acktr_cancel_expired_ack_delay_timer(&conn->pktns.acktr, ts); +} + +ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn) { + ngtcp2_tstamp res = UINT64_MAX, ts; + + if (conn->in_pktns) { + ts = ngtcp2_rtb_lost_pkt_ts(&conn->in_pktns->rtb); + if (ts != UINT64_MAX) { + ts += conn_compute_pto(conn, conn->in_pktns); + res = ngtcp2_min(res, ts); + } + } + + if (conn->hs_pktns) { + ts = ngtcp2_rtb_lost_pkt_ts(&conn->hs_pktns->rtb); + if (ts != UINT64_MAX) { + ts += conn_compute_pto(conn, conn->hs_pktns); + res = ngtcp2_min(res, ts); + } + } + + ts = ngtcp2_rtb_lost_pkt_ts(&conn->pktns.rtb); + if (ts != UINT64_MAX) { + ts += conn_compute_pto(conn, &conn->pktns); + res = ngtcp2_min(res, ts); + } + + return res; +} + +void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_duration pto; + + if (conn->in_pktns) { + pto = conn_compute_pto(conn, conn->in_pktns); + ngtcp2_rtb_remove_expired_lost_pkt(&conn->in_pktns->rtb, pto, ts); + } + if (conn->hs_pktns) { + pto = conn_compute_pto(conn, conn->hs_pktns); + ngtcp2_rtb_remove_expired_lost_pkt(&conn->hs_pktns->rtb, pto, ts); + } + pto = conn_compute_pto(conn, &conn->pktns); + ngtcp2_rtb_remove_expired_lost_pkt(&conn->pktns.rtb, pto, ts); +} + +/* + * conn_client_validate_transport_params validates |params| as client. + * |params| must be sent with Encrypted Extensions. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Validation against either of original_dcid and retry_scid is + * failed. + * NGTCP2_ERR_TRANSPORT_PARAM + * params contains preferred address but server chose zero-length + * connection ID. + */ +static int +conn_client_validate_transport_params(ngtcp2_conn *conn, + const ngtcp2_transport_params *params) { + if (!ngtcp2_cid_eq(&conn->rcid, ¶ms->original_dcid)) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + if (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) { + if (!params->retry_scid_present) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + if (!ngtcp2_cid_eq(&conn->retry_scid, ¶ms->retry_scid)) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + } else if (params->retry_scid_present) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + if (params->preferred_address_present && + conn->dcid.current.cid.datalen == 0) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + return 0; +} + +int ngtcp2_conn_set_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params) { + int rv; + + assert(!(conn->flags & NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED)); + + /* Assume that ngtcp2_decode_transport_params sets default value if + active_connection_id_limit is omitted. */ + if (params->active_connection_id_limit < + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + /* We assume that conn->dcid.current.cid is still the initial one. + This requires that transport parameter must be fed into + ngtcp2_conn as early as possible. */ + if (!ngtcp2_cid_eq(&conn->dcid.current.cid, ¶ms->initial_scid)) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + if (params->max_udp_payload_size < 1200) { + return NGTCP2_ERR_TRANSPORT_PARAM; + } + + if (!conn->server) { + rv = conn_client_validate_transport_params(conn, params); + if (rv != 0) { + return rv; + } + } + + ngtcp2_log_remote_tp(&conn->log, + conn->server + ? NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO + : NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + params); + + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, + NGTCP2_QLOG_SIDE_REMOTE); + + if ((conn->server && conn->pktns.crypto.tx.ckm) || + (!conn->server && conn->pktns.crypto.rx.ckm)) { + conn->remote.transport_params = *params; + conn_sync_stream_id_limit(conn); + conn->tx.max_offset = conn->remote.transport_params.initial_max_data; + } else { + conn->remote.pending_transport_params = *params; + } + + conn->flags |= NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED; + + return 0; +} + +void ngtcp2_conn_get_remote_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params) { + if (conn->pktns.crypto.rx.ckm) { + *params = conn->remote.transport_params; + } else { + *params = conn->remote.pending_transport_params; + } +} + +void ngtcp2_conn_set_early_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params) { + ngtcp2_transport_params *p = &conn->remote.transport_params; + + assert(!conn->server); + + memset(p, 0, sizeof(*p)); + + p->initial_max_streams_bidi = params->initial_max_streams_bidi; + p->initial_max_streams_uni = params->initial_max_streams_uni; + p->initial_max_stream_data_bidi_local = + params->initial_max_stream_data_bidi_local; + p->initial_max_stream_data_bidi_remote = + params->initial_max_stream_data_bidi_remote; + p->initial_max_stream_data_uni = params->initial_max_stream_data_uni; + p->initial_max_data = params->initial_max_data; + p->max_datagram_frame_size = params->max_datagram_frame_size; + + conn_sync_stream_id_limit(conn); + + conn->tx.max_offset = p->initial_max_data; + + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, p, conn->server, + NGTCP2_QLOG_SIDE_REMOTE); +} + +int ngtcp2_conn_set_local_transport_params( + ngtcp2_conn *conn, const ngtcp2_transport_params *params) { + assert(conn->server); + assert(params->active_connection_id_limit <= NGTCP2_MAX_DCID_POOL_SIZE); + + if (conn->hs_pktns == NULL || conn->hs_pktns->crypto.tx.ckm) { + return NGTCP2_ERR_INVALID_STATE; + } + + conn->local.transport_params = *params; + + return 0; +} + +int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn) { + const ngtcp2_mem *mem = conn->mem; + ngtcp2_transport_params *params = &conn->local.transport_params; + ngtcp2_scid *scident; + ngtcp2_ksl_it it; + int rv; + + assert(1 == ngtcp2_ksl_len(&conn->scid.set)); + + if (params->active_connection_id_limit == 0) { + params->active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + } + + params->initial_scid = conn->oscid; + + if (conn->oscid.datalen == 0) { + params->preferred_address_present = 0; + } + + if (conn->server) { + if (params->stateless_reset_token_present) { + it = ngtcp2_ksl_begin(&conn->scid.set); + scident = ngtcp2_ksl_it_get(&it); + + memcpy(scident->token, params->stateless_reset_token, + NGTCP2_STATELESS_RESET_TOKENLEN); + } + + if (params->preferred_address_present) { + scident = ngtcp2_mem_malloc(mem, sizeof(*scident)); + if (scident == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_scid_init(scident, 1, ¶ms->preferred_address.cid, + params->preferred_address.stateless_reset_token); + + rv = ngtcp2_ksl_insert(&conn->scid.set, NULL, &scident->cid, scident); + if (rv != 0) { + ngtcp2_mem_free(mem, scident); + return rv; + } + + conn->scid.last_seq = 1; + } + } + + conn->rx.window = conn->rx.unsent_max_offset = conn->rx.max_offset = + params->initial_max_data; + conn->remote.bidi.unsent_max_streams = params->initial_max_streams_bidi; + conn->remote.bidi.max_streams = params->initial_max_streams_bidi; + conn->remote.uni.unsent_max_streams = params->initial_max_streams_uni; + conn->remote.uni.max_streams = params->initial_max_streams_uni; + + ngtcp2_qlog_parameters_set_transport_params(&conn->qlog, params, conn->server, + NGTCP2_QLOG_SIDE_LOCAL); + + return 0; +} + +void ngtcp2_conn_get_local_transport_params(ngtcp2_conn *conn, + ngtcp2_transport_params *params) { + *params = conn->local.transport_params; +} + +int ngtcp2_conn_open_bidi_stream(ngtcp2_conn *conn, int64_t *pstream_id, + void *stream_user_data) { + int rv; + ngtcp2_strm *strm; + + if (ngtcp2_conn_get_streams_bidi_left(conn) == 0) { + return NGTCP2_ERR_STREAM_ID_BLOCKED; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_conn_init_stream(conn, strm, conn->local.bidi.next_stream_id, + stream_user_data); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + + *pstream_id = conn->local.bidi.next_stream_id; + conn->local.bidi.next_stream_id += 4; + + return 0; +} + +int ngtcp2_conn_open_uni_stream(ngtcp2_conn *conn, int64_t *pstream_id, + void *stream_user_data) { + int rv; + ngtcp2_strm *strm; + + if (ngtcp2_conn_get_streams_uni_left(conn) == 0) { + return NGTCP2_ERR_STREAM_ID_BLOCKED; + } + + strm = ngtcp2_mem_malloc(conn->mem, sizeof(ngtcp2_strm)); + if (strm == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_conn_init_stream(conn, strm, conn->local.uni.next_stream_id, + stream_user_data); + if (rv != 0) { + ngtcp2_mem_free(conn->mem, strm); + return rv; + } + ngtcp2_strm_shutdown(strm, NGTCP2_STRM_FLAG_SHUT_RD); + + *pstream_id = conn->local.uni.next_stream_id; + conn->local.uni.next_stream_id += 4; + + return 0; +} + +ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id) { + ngtcp2_map_entry *me; + + me = ngtcp2_map_find(&conn->strms, (uint64_t)stream_id); + if (me == NULL) { + return NULL; + } + + return ngtcp2_struct_of(me, ngtcp2_strm, me); +} + +ngtcp2_ssize ngtcp2_conn_write_stream(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, + const uint8_t *data, size_t datalen, + ngtcp2_tstamp ts) { + ngtcp2_vec datav; + + datav.len = datalen; + datav.base = (uint8_t *)data; + + return ngtcp2_conn_writev_stream(conn, path, pi, dest, destlen, pdatalen, + flags, stream_id, &datav, 1, ts); +} + +ngtcp2_ssize ngtcp2_conn_writev_stream(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_ssize *pdatalen, + uint32_t flags, int64_t stream_id, + const ngtcp2_vec *datav, size_t datavcnt, + ngtcp2_tstamp ts) { + ngtcp2_vmsg vmsg, *pvmsg; + ngtcp2_strm *strm; + + if (pdatalen) { + *pdatalen = -1; + } + + if (stream_id != -1) { + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + if (strm->flags & NGTCP2_STRM_FLAG_SHUT_WR) { + return NGTCP2_ERR_STREAM_SHUT_WR; + } + + vmsg.type = NGTCP2_VMSG_TYPE_STREAM; + vmsg.stream.strm = strm; + vmsg.stream.flags = flags; + vmsg.stream.data = datav; + vmsg.stream.datacnt = datavcnt; + vmsg.stream.pdatalen = pdatalen; + + pvmsg = &vmsg; + } else { + pvmsg = NULL; + } + + return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, pvmsg, ts); +} + +ngtcp2_ssize ngtcp2_conn_writev_datagram(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, int *paccepted, + uint32_t flags, + const ngtcp2_vec *datav, + size_t datavcnt, ngtcp2_tstamp ts) { + ngtcp2_vmsg vmsg; + + if (paccepted) { + *paccepted = 0; + } + + if (conn->remote.transport_params.max_datagram_frame_size == 0) { + return NGTCP2_ERR_INVALID_STATE; + } + if (conn->remote.transport_params.max_datagram_frame_size < + ngtcp2_pkt_datagram_framelen(ngtcp2_vec_len(datav, datavcnt))) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + vmsg.type = NGTCP2_VMSG_TYPE_DATAGRAM; + vmsg.datagram.flags = flags; + vmsg.datagram.data = datav; + vmsg.datagram.datacnt = datavcnt; + vmsg.datagram.paccepted = paccepted; + + return ngtcp2_conn_write_vmsg(conn, path, pi, dest, destlen, &vmsg, ts); +} + +ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_pktns *pktns = &conn->pktns; + size_t origlen = destlen; + int rv; + uint8_t wflags = NGTCP2_WRITE_PKT_FLAG_NONE; + int ppe_pending = (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) != 0; + ngtcp2_ssize res = 0; + size_t server_tx_left; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + if (!ppe_pending && pi) { + pi->ecn = NGTCP2_ECN_NOT_ECT; + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLIENT_WAIT_HANDSHAKE: + case NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED: + nwrite = conn_client_write_handshake(conn, pi, dest, destlen, vmsg, ts); + if (nwrite < 0) { + return nwrite; + } + if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { + return nwrite; + } + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + /* Break here so that we can coalesces Short packets. */ + break; + case NGTCP2_CS_SERVER_INITIAL: + case NGTCP2_CS_SERVER_WAIT_HANDSHAKE: + case NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED: + if (!ppe_pending) { + if (!(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + destlen = ngtcp2_min(destlen, server_tx_left); + } + + nwrite = conn_write_handshake(conn, pi, dest, destlen, 0, ts); + if (nwrite < 0) { + return nwrite; + } + + if (conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED) { + destlen = origlen; + } else { + origlen = destlen; + } + + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + if (conn->state != NGTCP2_CS_POST_HANDSHAKE && + conn->pktns.crypto.tx.ckm == NULL) { + return res; + } + break; + case NGTCP2_CS_POST_HANDSHAKE: + break; + case NGTCP2_CS_CLOSING: + return NGTCP2_ERR_CLOSING; + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_DRAINING; + default: + return 0; + } + + assert(pktns->crypto.tx.ckm); + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + if (vmsg) { + switch (vmsg->type) { + case NGTCP2_VMSG_TYPE_STREAM: + if (vmsg->stream.flags & NGTCP2_WRITE_STREAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; + } + break; + case NGTCP2_VMSG_TYPE_DATAGRAM: + if (vmsg->datagram.flags & NGTCP2_WRITE_DATAGRAM_FLAG_MORE) { + wflags |= NGTCP2_WRITE_PKT_FLAG_MORE; + } + break; + default: + break; + } + } + + if (ppe_pending) { + res = conn->pkt.hs_spktlen; + conn->pkt.hs_spktlen = 0; + /* dest and destlen have already been adjusted in ppe in the first + run. They are adjusted for probe packet later. */ + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + wflags, ts); + goto fin; + } else { + if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { + rv = conn_prepare_key_update(conn, ts); + if (rv != 0) { + return rv; + } + } + + if (!conn->pktns.rtb.probe_pkt_left && conn_cwnd_is_zero(conn)) { + destlen = 0; + } else { + nwrite = conn_write_path_response(conn, path, pi, dest, destlen, ts); + if (nwrite) { + goto fin; + } + + if (conn->pv) { + nwrite = conn_write_path_challenge(conn, path, pi, dest, destlen, ts); + if (nwrite) { + goto fin; + } + } + + if (conn->server && + !(conn->dcid.current.flags & NGTCP2_DCID_FLAG_PATH_VALIDATED)) { + server_tx_left = conn_server_tx_left(conn, &conn->dcid.current); + origlen = ngtcp2_min(origlen, server_tx_left); + destlen = ngtcp2_min(destlen, server_tx_left); + + if (destlen == 0 && conn->cstat.loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + conn->cstat.loss_detection_timer = UINT64_MAX; + conn->cstat.pto_count = 0; + } + } + } + } + + if (res == 0) { + if (conn_handshake_remnants_left(conn)) { + if (conn_handshake_probe_left(conn)) { + destlen = origlen; + } + nwrite = conn_write_handshake_pkts(conn, pi, dest, destlen, 0, ts); + if (nwrite < 0) { + return nwrite; + } + if (nwrite > 0) { + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + } + } + + if (conn->pktns.rtb.probe_pkt_left) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "transmit probe pkt left=%zu", + conn->pktns.rtb.probe_pkt_left); + + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + wflags, ts); + + goto fin; + } + + nwrite = conn_write_pkt(conn, pi, dest, destlen, vmsg, NGTCP2_PKT_SHORT, + wflags, ts); + if (nwrite) { + assert(nwrite != NGTCP2_ERR_NOBUF); + goto fin; + } + + if (res == 0) { + nwrite = conn_write_ack_pkt(conn, pi, dest, origlen, NGTCP2_PKT_SHORT, ts); + } + +fin: + conn->pkt.hs_spktlen = 0; + + if (nwrite >= 0) { + res += nwrite; + return res; + } + /* NGTCP2_CONN_FLAG_PPE_PENDING is set in conn_write_pkt above. + ppe_pending cannot be used here. */ + if (conn->flags & NGTCP2_CONN_FLAG_PPE_PENDING) { + conn->pkt.hs_spktlen = res; + } + + return nwrite; +} + +static ngtcp2_ssize +conn_write_connection_close(ngtcp2_conn *conn, ngtcp2_pkt_info *pi, + uint8_t *dest, size_t destlen, uint8_t pkt_type, + uint64_t error_code, ngtcp2_tstamp ts) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_ssize res = 0, nwrite; + ngtcp2_frame fr; + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = error_code; + fr.connection_close.frame_type = 0; + fr.connection_close.reasonlen = 0; + fr.connection_close.reason = NULL; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && + pkt_type != NGTCP2_PKT_INITIAL) { + if (in_pktns && conn->server) { + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_INITIAL, &conn->dcid.current.cid, + &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + if (nwrite < 0) { + return nwrite; + } + + dest += nwrite; + destlen -= (size_t)nwrite; + res += nwrite; + } + + if (pkt_type != NGTCP2_PKT_HANDSHAKE && hs_pktns && + hs_pktns->crypto.tx.ckm) { + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_HANDSHAKE, + &conn->dcid.current.cid, &fr, NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + if (nwrite < 0) { + return nwrite; + } + + dest += nwrite; + destlen -= (size_t)nwrite; + res += nwrite; + } + } + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, pkt_type, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + + if (res == 0) { + return NGTCP2_ERR_NOBUF; + } + + return res; +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t error_code, ngtcp2_tstamp ts) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + uint8_t pkt_type; + ngtcp2_ssize nwrite; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_INVALID_STATE; + default: + break; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + if (pi) { + pi->ecn = NGTCP2_ECN_NOT_ECT; + } + + if (conn->state == NGTCP2_CS_POST_HANDSHAKE || + (conn->server && conn->pktns.crypto.tx.ckm)) { + pkt_type = NGTCP2_PKT_SHORT; + } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { + pkt_type = NGTCP2_PKT_HANDSHAKE; + } else if (in_pktns && in_pktns->crypto.tx.ckm) { + pkt_type = NGTCP2_PKT_INITIAL; + } else { + /* This branch is taken if server has not read any Initial packet + from client. */ + return NGTCP2_ERR_INVALID_STATE; + } + + nwrite = conn_write_connection_close(conn, pi, dest, destlen, pkt_type, + error_code, ts); + if (nwrite < 0) { + return nwrite; + } + + conn->state = NGTCP2_CS_CLOSING; + + return nwrite; +} + +ngtcp2_ssize ngtcp2_conn_write_application_close( + ngtcp2_conn *conn, ngtcp2_path *path, ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, uint64_t app_error_code, ngtcp2_tstamp ts) { + ngtcp2_ssize nwrite; + ngtcp2_ssize res = 0; + ngtcp2_frame fr; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_INVALID_STATE; + default: + break; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + if (pi) { + pi->ecn = NGTCP2_ECN_NOT_ECT; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + nwrite = conn_write_connection_close(conn, pi, dest, destlen, + conn->hs_pktns->crypto.tx.ckm + ? NGTCP2_PKT_HANDSHAKE + : NGTCP2_PKT_INITIAL, + NGTCP2_APPLICATION_ERROR, ts); + if (nwrite < 0) { + return nwrite; + } + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + + if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { + assert(res); + + if (!conn->server || !conn->pktns.crypto.tx.ckm) { + return res; + } + } + + assert(conn->pktns.crypto.tx.ckm); + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; + fr.connection_close.error_code = app_error_code; + fr.connection_close.frame_type = 0; + fr.connection_close.reasonlen = 0; + fr.connection_close.reason = NULL; + + nwrite = ngtcp2_conn_write_single_frame_pkt( + conn, pi, dest, destlen, NGTCP2_PKT_SHORT, &conn->dcid.current.cid, &fr, + NGTCP2_RTB_ENTRY_FLAG_NONE, NULL, ts); + + if (nwrite < 0) { + return nwrite; + } + + res += nwrite; + + if (res == 0) { + return NGTCP2_ERR_NOBUF; + } + + conn->state = NGTCP2_CS_CLOSING; + + return res; +} + +int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { + return conn->state == NGTCP2_CS_CLOSING; +} + +int ngtcp2_conn_is_in_draining_period(ngtcp2_conn *conn) { + return conn->state == NGTCP2_CS_DRAINING; +} + +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + int rv; + + if (!strm->app_error_code) { + app_error_code = strm->app_error_code; + } + + rv = ngtcp2_map_remove(&conn->strms, strm->me.key); + if (rv != 0) { + assert(rv != NGTCP2_ERR_INVALID_ARGUMENT); + return rv; + } + + rv = conn_call_stream_close(conn, strm, app_error_code); + if (rv != 0) { + goto fin; + } + + if (ngtcp2_strm_is_tx_queued(strm)) { + ngtcp2_pq_remove(&conn->tx.strmq, &strm->pe); + } + +fin: + ngtcp2_strm_free(strm); + ngtcp2_mem_free(conn->mem, strm); + + return rv; +} + +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RDWR) == + NGTCP2_STRM_FLAG_SHUT_RDWR && + ((strm->flags & NGTCP2_STRM_FLAG_RECV_RST) || + ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) && + (((strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && + (strm->flags & NGTCP2_STRM_FLAG_RST_ACKED)) || + (!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST) && + ngtcp2_strm_is_all_tx_data_acked(strm)))) { + return ngtcp2_conn_close_stream(conn, strm, app_error_code); + } + return 0; +} + +/* + * conn_shutdown_stream_write closes send stream with error code + * |app_error_code|. RESET_STREAM frame is scheduled. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_shutdown_stream_write(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + if (strm->flags & NGTCP2_STRM_FLAG_SENT_RST) { + return 0; + } + + /* Set this flag so that we don't accidentally send DATA to this + stream. */ + strm->flags |= NGTCP2_STRM_FLAG_SHUT_WR | NGTCP2_STRM_FLAG_SENT_RST; + strm->app_error_code = app_error_code; + + ngtcp2_strm_streamfrq_clear(strm); + + return conn_reset_stream(conn, strm, app_error_code); +} + +/* + * conn_shutdown_stream_read closes read stream with error code + * |app_error_code|. STOP_SENDING frame is scheduled. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_shutdown_stream_read(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code) { + if (strm->flags & NGTCP2_STRM_FLAG_STOP_SENDING) { + return 0; + } + if ((strm->flags & NGTCP2_STRM_FLAG_SHUT_RD) && + ngtcp2_strm_rx_offset(strm) == strm->rx.last_offset) { + return 0; + } + + /* Extend connection flow control window for the amount of data + which are not passed to application. */ + if (!(strm->flags & + (NGTCP2_STRM_FLAG_STOP_SENDING | NGTCP2_STRM_FLAG_RECV_RST))) { + ngtcp2_conn_extend_max_offset(conn, strm->rx.last_offset - + ngtcp2_strm_rx_offset(strm)); + } + + strm->flags |= NGTCP2_STRM_FLAG_STOP_SENDING; + strm->app_error_code = app_error_code; + + return conn_stop_sending(conn, strm, app_error_code); +} + +int ngtcp2_conn_shutdown_stream(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + int rv; + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + rv = conn_shutdown_stream_read(conn, strm, app_error_code); + if (rv != 0) { + return rv; + } + + rv = conn_shutdown_stream_write(conn, strm, app_error_code); + if (rv != 0) { + return rv; + } + + return 0; +} + +int ngtcp2_conn_shutdown_stream_write(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + return conn_shutdown_stream_write(conn, strm, app_error_code); +} + +int ngtcp2_conn_shutdown_stream_read(ngtcp2_conn *conn, int64_t stream_id, + uint64_t app_error_code) { + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + return conn_shutdown_stream_read(conn, strm, app_error_code); +} + +/* + * conn_extend_max_stream_offset extends stream level flow control + * window by |datalen| of the stream denoted by |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int conn_extend_max_stream_offset(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t datalen) { + ngtcp2_strm *top; + + if (datalen > NGTCP2_MAX_VARINT || + strm->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { + strm->rx.unsent_max_offset = NGTCP2_MAX_VARINT; + } else { + strm->rx.unsent_max_offset += datalen; + } + + if (!(strm->flags & + (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_STOP_SENDING)) && + !ngtcp2_strm_is_tx_queued(strm) && + conn_should_send_max_stream_data(conn, strm)) { + if (!ngtcp2_pq_empty(&conn->tx.strmq)) { + top = ngtcp2_conn_tx_strmq_top(conn); + strm->cycle = top->cycle; + } + strm->cycle = conn_tx_strmq_first_cycle(conn); + return ngtcp2_conn_tx_strmq_push(conn, strm); + } + + return 0; +} + +int ngtcp2_conn_extend_max_stream_offset(ngtcp2_conn *conn, int64_t stream_id, + uint64_t datalen) { + ngtcp2_strm *strm; + + strm = ngtcp2_conn_find_stream(conn, stream_id); + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + return conn_extend_max_stream_offset(conn, strm, datalen); +} + +void ngtcp2_conn_extend_max_offset(ngtcp2_conn *conn, uint64_t datalen) { + if (NGTCP2_MAX_VARINT < datalen || + conn->rx.unsent_max_offset > NGTCP2_MAX_VARINT - datalen) { + conn->rx.unsent_max_offset = NGTCP2_MAX_VARINT; + return; + } + + conn->rx.unsent_max_offset += datalen; +} + +void ngtcp2_conn_extend_max_streams_bidi(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.bidi.unsent_max_streams, n); +} + +void ngtcp2_conn_extend_max_streams_uni(ngtcp2_conn *conn, size_t n) { + handle_max_remote_streams_extension(&conn->remote.uni.unsent_max_streams, n); +} + +const ngtcp2_cid *ngtcp2_conn_get_dcid(ngtcp2_conn *conn) { + return &conn->dcid.current.cid; +} + +uint32_t ngtcp2_conn_get_negotiated_version(ngtcp2_conn *conn) { + return conn->version; +} + +int ngtcp2_conn_early_data_rejected(ngtcp2_conn *conn) { + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_rtb *rtb = &conn->pktns.rtb; + int rv; + + conn->flags |= NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED; + + rv = ngtcp2_rtb_remove_all(rtb, conn, pktns, &conn->cstat); + if (rv != 0) { + return rv; + } + + return rv; +} + +void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_duration min_rtt; + + rtt = ngtcp2_max(rtt, NGTCP2_GRANULARITY); + + cstat->latest_rtt = rtt; + + if (cstat->min_rtt == UINT64_MAX) { + cstat->min_rtt = rtt; + cstat->smoothed_rtt = rtt; + cstat->rttvar = rtt / 2; + cstat->first_rtt_sample_ts = ts; + } else { + min_rtt = ngtcp2_min(cstat->min_rtt, rtt); + if (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) { + ack_delay = + ngtcp2_min(ack_delay, conn->remote.transport_params.max_ack_delay); + } else if (ack_delay > 0 && rtt < cstat->min_rtt + ack_delay) { + /* Ignore RTT sample if adjusting ack_delay causes the sample + less than min_rtt before handshake confirmation. */ + ngtcp2_log_info( + &conn->log, NGTCP2_LOG_EVENT_RCV, + "ignore rtt sample because ack_delay is too large latest_rtt=%" PRIu64 + " min_rtt=%" PRIu64 " ack_delay=%" PRIu64, + (uint64_t)(rtt / NGTCP2_MILLISECONDS), + (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), + (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); + return; + } + + if (rtt > min_rtt + ack_delay) { + rtt -= ack_delay; + } + + cstat->min_rtt = min_rtt; + cstat->rttvar = (cstat->rttvar * 3 + (cstat->smoothed_rtt < rtt + ? rtt - cstat->smoothed_rtt + : cstat->smoothed_rtt - rtt)) / + 4; + cstat->smoothed_rtt = (cstat->smoothed_rtt * 7 + rtt) / 8; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "latest_rtt=%" PRIu64 " min_rtt=%" PRIu64 + " smoothed_rtt=%" PRIu64 " rttvar=%" PRIu64 + " ack_delay=%" PRIu64, + (uint64_t)(cstat->latest_rtt / NGTCP2_MILLISECONDS), + (uint64_t)(cstat->min_rtt / NGTCP2_MILLISECONDS), + cstat->smoothed_rtt / NGTCP2_MILLISECONDS, + cstat->rttvar / NGTCP2_MILLISECONDS, + (uint64_t)(ack_delay / NGTCP2_MILLISECONDS)); +} + +void ngtcp2_conn_get_conn_stat(ngtcp2_conn *conn, ngtcp2_conn_stat *cstat) { + *cstat = conn->cstat; +} + +static ngtcp2_pktns *conn_get_earliest_pktns(ngtcp2_conn *conn, + ngtcp2_tstamp *pts, + const ngtcp2_tstamp *times) { + ngtcp2_pktns *ns[] = {conn->in_pktns, conn->hs_pktns, &conn->pktns}; + ngtcp2_pktns *res = ns[0]; + size_t i; + ngtcp2_tstamp earliest_ts = times[NGTCP2_PKTNS_ID_INITIAL]; + + for (i = NGTCP2_PKTNS_ID_HANDSHAKE; i < NGTCP2_PKTNS_ID_MAX; ++i) { + if (ns[i] == NULL || ns[i]->rtb.num_retransmittable == 0 || + (times[i] == UINT64_MAX || + (earliest_ts != UINT64_MAX && times[i] >= earliest_ts) || + (i == NGTCP2_PKTNS_ID_APPLICATION && + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { + continue; + } + + earliest_ts = times[i]; + res = ns[i]; + } + + if (res == NULL && !conn->server) { + earliest_ts = UINT64_MAX; + + if (conn->hs_pktns && conn->hs_pktns->crypto.tx.ckm) { + res = conn->hs_pktns; + } else { + res = conn->in_pktns; + } + } + + if (pts) { + *pts = earliest_ts; + } + return res; +} + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; + ngtcp2_duration timeout; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *pktns = &conn->pktns; + ngtcp2_pktns *earliest_pktns; + ngtcp2_tstamp earliest_loss_time; + ngtcp2_tstamp last_tx_pkt_ts; + + conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); + + if (earliest_loss_time != UINT64_MAX) { + cstat->loss_detection_timer = earliest_loss_time; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss_detection_timer=%" PRIu64 " nonzero crypto loss time", + cstat->loss_detection_timer); + return; + } + + if ((!in_pktns || in_pktns->rtb.num_retransmittable == 0) && + (!hs_pktns || hs_pktns->rtb.num_retransmittable == 0) && + (pktns->rtb.num_retransmittable == 0 || + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) && + (conn->server || + (conn->flags & (NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED | + NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)))) { + if (cstat->loss_detection_timer != UINT64_MAX) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer canceled"); + cstat->loss_detection_timer = UINT64_MAX; + cstat->pto_count = 0; + } + return; + } + + earliest_pktns = + conn_get_earliest_pktns(conn, &last_tx_pkt_ts, cstat->last_tx_pkt_ts); + + assert(earliest_pktns); + + if (last_tx_pkt_ts == UINT64_MAX) { + last_tx_pkt_ts = ts; + } + + timeout = conn_compute_pto(conn, earliest_pktns) * (1ULL << cstat->pto_count); + + cstat->loss_detection_timer = last_tx_pkt_ts + timeout; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss_detection_timer=%" PRIu64 " last_tx_pkt_ts=%" PRIu64 + " timeout=%" PRIu64, + cstat->loss_detection_timer, last_tx_pkt_ts, + (uint64_t)(timeout / NGTCP2_MILLISECONDS)); +} + +int ngtcp2_conn_on_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts) { + ngtcp2_conn_stat *cstat = &conn->cstat; + int rv; + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_tstamp earliest_loss_time; + ngtcp2_pktns *loss_pktns = + conn_get_earliest_pktns(conn, &earliest_loss_time, cstat->loss_time); + ngtcp2_pktns *earliest_pktns; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + switch (conn->state) { + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: + cstat->loss_detection_timer = UINT64_MAX; + cstat->pto_count = 0; + return 0; + default: + break; + } + + if (cstat->loss_detection_timer == UINT64_MAX) { + return 0; + } + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, + "loss detection timer fired"); + + if (earliest_loss_time != UINT64_MAX) { + rv = ngtcp2_conn_detect_lost_pkt(conn, loss_pktns, cstat, ts); + if (rv != 0) { + return rv; + } + ngtcp2_conn_set_loss_detection_timer(conn, ts); + return 0; + } + + if (!conn->server && !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED)) { + if (hs_pktns->crypto.tx.ckm) { + hs_pktns->rtb.probe_pkt_left = 1; + } else { + in_pktns->rtb.probe_pkt_left = 1; + } + } else { + earliest_pktns = conn_get_earliest_pktns(conn, NULL, cstat->last_tx_pkt_ts); + + assert(earliest_pktns); + + switch (earliest_pktns->rtb.pktns_id) { + case NGTCP2_PKTNS_ID_INITIAL: + assert(in_pktns); + in_pktns->rtb.probe_pkt_left = 1; + if (!conn->server) { + break; + } + /* fall through for server so that it can coalesce packets. */ + /* fall through */ + case NGTCP2_PKTNS_ID_HANDSHAKE: + assert(hs_pktns); + hs_pktns->rtb.probe_pkt_left = 1; + break; + case NGTCP2_PKTNS_ID_APPLICATION: + conn->pktns.rtb.probe_pkt_left = 2; + break; + default: + assert(0); + } + } + + ++cstat->pto_count; + + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_RCV, "pto_count=%zu", + cstat->pto_count); + + ngtcp2_conn_set_loss_detection_timer(conn, ts); + + return 0; +} + +int ngtcp2_conn_submit_crypto_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + const uint8_t *data, const size_t datalen) { + ngtcp2_pktns *pktns; + ngtcp2_frame_chain *frc; + ngtcp2_crypto *fr; + int rv; + + if (datalen == 0) { + return 0; + } + + switch (crypto_level) { + case NGTCP2_CRYPTO_LEVEL_INITIAL: + assert(conn->in_pktns); + pktns = conn->in_pktns; + break; + case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: + assert(conn->hs_pktns); + pktns = conn->hs_pktns; + break; + case NGTCP2_CRYPTO_LEVEL_APPLICATION: + pktns = &conn->pktns; + break; + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + rv = ngtcp2_frame_chain_new(&frc, conn->mem); + if (rv != 0) { + return rv; + } + + fr = &frc->fr.crypto; + + fr->type = NGTCP2_FRAME_CRYPTO; + fr->offset = pktns->crypto.tx.offset; + fr->datacnt = 1; + fr->data[0].len = datalen; + fr->data[0].base = (uint8_t *)data; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, &fr->offset, frc); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + + pktns->crypto.strm.tx.offset += datalen; + pktns->crypto.tx.offset += datalen; + + return 0; +} + +int ngtcp2_conn_submit_new_token(ngtcp2_conn *conn, const uint8_t *token, + size_t tokenlen) { + int rv; + ngtcp2_frame_chain *nfrc; + uint8_t *p; + + assert(conn->server); + assert(token); + assert(tokenlen); + + rv = ngtcp2_frame_chain_extralen_new(&nfrc, tokenlen, conn->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr.type = NGTCP2_FRAME_NEW_TOKEN; + + p = (uint8_t *)nfrc + sizeof(*nfrc); + memcpy(p, token, tokenlen); + + ngtcp2_vec_init(&nfrc->fr.new_token.token, p, tokenlen); + + nfrc->next = conn->pktns.tx.frq; + conn->pktns.tx.frq = nfrc; + + return 0; +} + +ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn) { + assert(!ngtcp2_pq_empty(&conn->tx.strmq)); + return ngtcp2_struct_of(ngtcp2_pq_top(&conn->tx.strmq), ngtcp2_strm, pe); +} + +void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn) { + ngtcp2_strm *strm = ngtcp2_conn_tx_strmq_top(conn); + assert(strm); + ngtcp2_pq_pop(&conn->tx.strmq); + strm->pe.index = NGTCP2_PQ_BAD_INDEX; +} + +int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm) { + return ngtcp2_pq_push(&conn->tx.strmq, &strm->pe); +} + +size_t ngtcp2_conn_get_num_scid(ngtcp2_conn *conn) { + return ngtcp2_ksl_len(&conn->scid.set); +} + +size_t ngtcp2_conn_get_scid(ngtcp2_conn *conn, ngtcp2_cid *dest) { + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + + for (it = ngtcp2_ksl_begin(&conn->scid.set); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + scid = ngtcp2_ksl_it_get(&it); + *dest++ = scid->cid; + } + + return ngtcp2_ksl_len(&conn->scid.set); +} + +size_t ngtcp2_conn_get_num_active_dcid(ngtcp2_conn *conn) { + size_t n = 1; /* for conn->dcid.current */ + ngtcp2_pv *pv = conn->pv; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } + + if (pv) { + if (pv->dcid.seq != conn->dcid.current.seq) { + ++n; + } + if ((pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq != pv->dcid.seq) { + ++n; + } + } + + n += ngtcp2_ringbuf_len(&conn->dcid.retired); + + return n; +} + +static void copy_dcid_to_cid_token(ngtcp2_cid_token *dest, + const ngtcp2_dcid *src) { + dest->seq = src->seq; + dest->cid = src->cid; + ngtcp2_path_storage_init2(&dest->ps, &src->ps.path); + dest->token_present = + (uint8_t)!ngtcp2_check_invalid_stateless_reset_token(src->token); + memcpy(dest->token, src->token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +size_t ngtcp2_conn_get_active_dcid(ngtcp2_conn *conn, ngtcp2_cid_token *dest) { + ngtcp2_pv *pv = conn->pv; + ngtcp2_cid_token *orig = dest; + ngtcp2_dcid *dcid; + size_t len, i; + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED)) { + return 0; + } + + copy_dcid_to_cid_token(dest, &conn->dcid.current); + ++dest; + + 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) && + pv->fallback_dcid.seq != conn->dcid.current.seq && + pv->fallback_dcid.seq != pv->dcid.seq) { + copy_dcid_to_cid_token(dest, &pv->fallback_dcid); + ++dest; + } + } + + len = ngtcp2_ringbuf_len(&conn->dcid.retired); + for (i = 0; i < len; ++i) { + dcid = ngtcp2_ringbuf_get(&conn->dcid.retired, i); + copy_dcid_to_cid_token(dest, dcid); + ++dest; + } + + return (size_t)(dest - orig); +} + +void ngtcp2_conn_set_local_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { + ngtcp2_addr *dest = &conn->dcid.current.ps.path.local; + + assert(addr->addrlen <= sizeof(conn->dcid.current.ps.local_addrbuf)); + ngtcp2_addr_copy(dest, addr); +} + +void ngtcp2_conn_set_remote_addr(ngtcp2_conn *conn, const ngtcp2_addr *addr) { + ngtcp2_addr *dest = &conn->dcid.current.ps.path.remote; + + assert(addr->addrlen <= sizeof(conn->dcid.current.ps.remote_addrbuf)); + ngtcp2_addr_copy(dest, addr); +} + +const ngtcp2_path *ngtcp2_conn_get_path(ngtcp2_conn *conn) { + return &conn->dcid.current.ps.path; +} + +int ngtcp2_conn_initiate_migration(ngtcp2_conn *conn, const ngtcp2_path *path, + ngtcp2_tstamp ts) { + int rv; + ngtcp2_dcid *dcid; + + assert(!conn->server); + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn->remote.transport_params.disable_active_migration || + conn->dcid.current.cid.datalen == 0 || + !(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + return NGTCP2_ERR_INVALID_STATE; + } + if (ngtcp2_ringbuf_len(&conn->dcid.unused) == 0) { + return NGTCP2_ERR_CONN_ID_BLOCKED; + } + + if (ngtcp2_path_eq(&conn->dcid.current.ps.path, path)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused, 0); + + rv = conn_stop_pv(conn, ts); + if (rv != 0) { + return rv; + } + + rv = conn_retire_dcid(conn, &conn->dcid.current, ts); + if (rv != 0) { + return rv; + } + + ngtcp2_dcid_copy(&conn->dcid.current, dcid); + ngtcp2_path_copy(&conn->dcid.current.ps.path, path); + ngtcp2_ringbuf_pop_front(&conn->dcid.unused); + + rv = conn_call_activate_dcid(conn, &conn->dcid.current); + if (rv != 0) { + return rv; + } + + conn_reset_congestion_state(conn); + conn_reset_ecn_validation_state(conn); + + return 0; +} + +uint64_t ngtcp2_conn_get_max_local_streams_uni(ngtcp2_conn *conn) { + return conn->local.uni.max_streams; +} + +uint64_t ngtcp2_conn_get_max_data_left(ngtcp2_conn *conn) { + return conn->tx.max_offset - conn->tx.offset; +} + +uint64_t ngtcp2_conn_get_streams_bidi_left(ngtcp2_conn *conn) { + uint64_t n = ngtcp2_ord_stream_id(conn->local.bidi.next_stream_id); + + return n > conn->local.bidi.max_streams + ? 0 + : conn->local.bidi.max_streams - n + 1; +} + +uint64_t ngtcp2_conn_get_streams_uni_left(ngtcp2_conn *conn) { + uint64_t n = ngtcp2_ord_stream_id(conn->local.uni.next_stream_id); + + return n > conn->local.uni.max_streams ? 0 + : conn->local.uni.max_streams - n + 1; +} + +ngtcp2_tstamp ngtcp2_conn_get_idle_expiry(ngtcp2_conn *conn) { + ngtcp2_duration trpto; + ngtcp2_duration idle_timeout; + + /* TODO Remote max_idle_timeout becomes effective after handshake + completion. */ + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) || + conn->remote.transport_params.max_idle_timeout == 0 || + (conn->local.transport_params.max_idle_timeout && + conn->local.transport_params.max_idle_timeout < + conn->remote.transport_params.max_idle_timeout)) { + idle_timeout = conn->local.transport_params.max_idle_timeout; + } else { + idle_timeout = conn->remote.transport_params.max_idle_timeout; + } + + if (idle_timeout == 0) { + return UINT64_MAX; + } + + trpto = 3 * conn_compute_pto( + conn, (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) + ? &conn->pktns + : conn->hs_pktns); + + return conn->idle_ts + ngtcp2_max(idle_timeout, trpto); +} + +ngtcp2_duration ngtcp2_conn_get_pto(ngtcp2_conn *conn) { + return conn_compute_pto(conn, + (conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED) + ? &conn->pktns + : conn->hs_pktns); +} + +void ngtcp2_conn_set_initial_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx) { + assert(conn->in_pktns); + conn->in_pktns->crypto.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_initial_crypto_ctx(ngtcp2_conn *conn) { + assert(conn->in_pktns); + return &conn->in_pktns->crypto.ctx; +} + +void ngtcp2_conn_set_retry_aead(ngtcp2_conn *conn, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx) { + assert(!conn->crypto.retry_aead_ctx.native_handle); + + conn->crypto.retry_aead = *aead; + conn->crypto.retry_aead_ctx = *aead_ctx; +} + +void ngtcp2_conn_set_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx) { + assert(conn->hs_pktns); + conn->hs_pktns->crypto.ctx = *ctx; + conn->pktns.crypto.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_crypto_ctx(ngtcp2_conn *conn) { + return &conn->pktns.crypto.ctx; +} + +void ngtcp2_conn_set_early_crypto_ctx(ngtcp2_conn *conn, + const ngtcp2_crypto_ctx *ctx) { + conn->early.ctx = *ctx; +} + +const ngtcp2_crypto_ctx *ngtcp2_conn_get_early_crypto_ctx(ngtcp2_conn *conn) { + return &conn->early.ctx; +} + +void *ngtcp2_conn_get_tls_native_handle(ngtcp2_conn *conn) { + return conn->crypto.tls_native_handle; +} + +void ngtcp2_conn_set_tls_native_handle(ngtcp2_conn *conn, + void *tls_native_handle) { + conn->crypto.tls_native_handle = tls_native_handle; +} + +void ngtcp2_conn_get_connection_close_error_code( + ngtcp2_conn *conn, ngtcp2_connection_close_error_code *ccec) { + *ccec = conn->rx.ccec; +} + +void ngtcp2_conn_set_tls_error(ngtcp2_conn *conn, int liberr) { + conn->crypto.tls_error = liberr; +} + +int ngtcp2_conn_get_tls_error(ngtcp2_conn *conn) { + return conn->crypto.tls_error; +} + +int ngtcp2_conn_is_local_stream(ngtcp2_conn *conn, int64_t stream_id) { + return conn_local_stream(conn, stream_id); +} + +int ngtcp2_conn_is_server(ngtcp2_conn *conn) { return conn->server; } + +int ngtcp2_conn_after_retry(ngtcp2_conn *conn) { + return (conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY) != 0; +} + +int ngtcp2_conn_set_stream_user_data(ngtcp2_conn *conn, int64_t stream_id, + void *stream_user_data) { + ngtcp2_strm *strm = ngtcp2_conn_find_stream(conn, stream_id); + + if (strm == NULL) { + return NGTCP2_ERR_STREAM_NOT_FOUND; + } + + strm->stream_user_data = stream_user_data; + + return 0; +} + +void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, + const ngtcp2_path *path, + const uint8_t *data) { + ngtcp2_path_storage_init2(&pcent->ps, path); + memcpy(pcent->data, data, sizeof(pcent->data)); +} + +void ngtcp2_settings_default(ngtcp2_settings *settings) { + memset(settings, 0, sizeof(*settings)); + settings->cc_algo = NGTCP2_CC_ALGO_CUBIC; + settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; + settings->ack_thresh = 2; +} + +void ngtcp2_transport_params_default(ngtcp2_transport_params *params) { + memset(params, 0, sizeof(*params)); + params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + params->active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; +} + +/* The functions prefixed with ngtcp2_pkt_ are usually put inside + ngtcp2_pkt.c. This function uses encryption construct and uses + test data defined only in ngtcp2_conn_test.c, so it is written + here. */ +ngtcp2_ssize ngtcp2_pkt_write_connection_close( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t error_code, ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, ngtcp2_hp_mask hp_mask, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx) { + ngtcp2_pkt_hd hd; + ngtcp2_crypto_km ckm; + ngtcp2_crypto_cc cc; + ngtcp2_ppe ppe; + ngtcp2_frame fr = {0}; + 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); + + ngtcp2_vec_init(&ckm.secret, NULL, 0); + ngtcp2_vec_init(&ckm.iv, iv, 12); + ckm.aead_ctx = *aead_ctx; + ckm.pkt_num = 0; + ckm.flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + + cc.aead = *aead; + cc.hp = *hp; + cc.ckm = &ckm; + cc.hp_ctx = *hp_ctx; + cc.encrypt = encrypt; + cc.hp_mask = hp_mask; + + ngtcp2_ppe_init(&ppe, dest, destlen, &cc); + + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + if (!ngtcp2_ppe_ensure_hp_sample(&ppe)) { + return NGTCP2_ERR_NOBUF; + } + + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = error_code; + + rv = ngtcp2_ppe_encode_frame(&ppe, &fr); + if (rv != 0) { + assert(NGTCP2_ERR_NOBUF == rv); + return rv; + } + + return ngtcp2_ppe_final(&ppe, NULL); +} + +int ngtcp2_is_bidi_stream(int64_t stream_id) { return bidi_stream(stream_id); } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h new file mode 100644 index 00000000000000..825e4502e13e03 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conn.h @@ -0,0 +1,822 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_CONN_H +#define NGTCP2_CONN_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_crypto.h" +#include "ngtcp2_acktr.h" +#include "ngtcp2_rtb.h" +#include "ngtcp2_strm.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_idtr.h" +#include "ngtcp2_str.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_log.h" +#include "ngtcp2_pq.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_pv.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_ppe.h" +#include "ngtcp2_qlog.h" +#include "ngtcp2_rst.h" + +typedef enum { + /* Client specific handshake states */ + NGTCP2_CS_CLIENT_INITIAL, + NGTCP2_CS_CLIENT_WAIT_HANDSHAKE, + NGTCP2_CS_CLIENT_TLS_HANDSHAKE_FAILED, + /* Server specific handshake states */ + NGTCP2_CS_SERVER_INITIAL, + NGTCP2_CS_SERVER_WAIT_HANDSHAKE, + NGTCP2_CS_SERVER_TLS_HANDSHAKE_FAILED, + /* Shared by both client and server */ + NGTCP2_CS_POST_HANDSHAKE, + NGTCP2_CS_CLOSING, + NGTCP2_CS_DRAINING, +} ngtcp2_conn_state; + +/* NGTCP2_MAX_STREAMS is the maximum number of streams. */ +#define NGTCP2_MAX_STREAMS (1LL << 60) + +/* NGTCP2_MAX_NUM_BUFFED_RX_PKTS is the maximum number of buffered + reordered packets. */ +#define NGTCP2_MAX_NUM_BUFFED_RX_PKTS 4 + +/* NGTCP2_MAX_REORDERED_CRYPTO_DATA is the maximum offset of crypto + data which is not continuous. In other words, there is a gap of + unreceived data. */ +#define NGTCP2_MAX_REORDERED_CRYPTO_DATA 65536 + +/* NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA is the maximum offset of received + crypto stream in Initial packet. We set this hard limit here + because crypto stream is unbounded. */ +#define NGTCP2_MAX_RX_INITIAL_CRYPTO_DATA 65536 +/* NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA is the maximum offset of + received crypto stream in Handshake packet. We set this hard limit + here because crypto stream is unbounded. */ +#define NGTCP2_MAX_RX_HANDSHAKE_CRYPTO_DATA 65536 + +/* NGTCP2_MAX_RETRIES is the number of Retry packet which client can + 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 + tells the maximum value. The value can be quite large, and we have + to put the sane limit.*/ +#define NGTCP2_MAX_SCID_POOL_SIZE 8 + +/* NGTCP2_MAX_NON_ACK_TX_PKT is the maximum number of continuous non + ACK-eliciting packets. */ +#define NGTCP2_MAX_NON_ACK_TX_PKT 3 + +/* NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS is the maximum number of ECN marked + packets sent in NGTCP2_ECN_STATE_TESTING period. */ +#define NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS 10 + +/* + * ngtcp2_max_frame is defined so that it covers the largest ACK + * frame. + */ +typedef union ngtcp2_max_frame { + ngtcp2_frame fr; + struct { + ngtcp2_ack ack; + /* ack includes 1 ngtcp2_ack_blk. */ + ngtcp2_ack_blk blks[NGTCP2_MAX_ACK_BLKS - 1]; + } ackfr; +} ngtcp2_max_frame; + +typedef struct ngtcp2_path_challenge_entry { + ngtcp2_path_storage ps; + uint8_t data[8]; +} ngtcp2_path_challenge_entry; + +void ngtcp2_path_challenge_entry_init(ngtcp2_path_challenge_entry *pcent, + const ngtcp2_path *path, + const uint8_t *data); + +/* NGTCP2_CONN_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_CONN_FLAG_NONE 0x00 +/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED is set if handshake + completed. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED 0x01 +/* NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED is set if connection ID is + negotiated. This is only used for client. */ +#define NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED 0x02 +/* NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED is set if transport + parameters are received. */ +#define NGTCP2_CONN_FLAG_TRANSPORT_PARAM_RECVED 0x04 +/* NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT is set when a protected packet + is received, and decrypted successfully. This flag is used to stop + retransmitting handshake packets. It might be replaced with an + another mechanism when we implement key update. */ +#define NGTCP2_CONN_FLAG_RECV_PROTECTED_PKT 0x08 +/* NGTCP2_CONN_FLAG_RECV_RETRY is set when a client receives Retry + packet. */ +#define NGTCP2_CONN_FLAG_RECV_RETRY 0x10 +/* NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED is set when 0-RTT packet is + rejected by a peer. */ +#define NGTCP2_CONN_FLAG_EARLY_DATA_REJECTED 0x20 +/* NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED is set when an endpoint + confirmed completion of handshake. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED 0x80 +/* NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED is set when the + library transitions its state to "post handshake". */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED 0x0100 +/* NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT is set when the early + handshake retransmission has done when server receives overlapping + Initial crypto data. */ +#define NGTCP2_CONN_FLAG_HANDSHAKE_EARLY_RETRANSMIT 0x0200 +/* NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED is set when key update is + not confirmed by the local endpoint. That is, it has not received + ACK frame which acknowledges packet which is encrypted with new + key. */ +#define NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED 0x0800 +/* NGTCP2_CONN_FLAG_PPE_PENDING is set when + NGTCP2_WRITE_STREAM_FLAG_MORE is used and the intermediate state of + ngtcp2_ppe is stored in pkt struct of ngtcp2_conn. */ +#define NGTCP2_CONN_FLAG_PPE_PENDING 0x1000 +/* NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE is set when idle timer + should be restarted on next write. */ +#define NGTCP2_CONN_FLAG_RESTART_IDLE_TIMER_ON_WRITE 0x2000 +/* NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED indicates that server as peer + verified client address. This flag is only used by client. */ +#define NGTCP2_CONN_FLAG_SERVER_ADDR_VERIFIED 0x4000 + +typedef struct ngtcp2_crypto_data { + ngtcp2_buf buf; + /* pkt_type is the type of packet to send data in buf. If it is 0, + it must be sent in Short packet. Otherwise, it is sent the long + packet type denoted by pkt_type. */ + uint8_t pkt_type; +} ngtcp2_crypto_data; + +typedef struct ngtcp2_pktns { + struct { + /* last_pkt_num is the packet number which the local endpoint sent + last time.*/ + int64_t last_pkt_num; + ngtcp2_frame_chain *frq; + /* num_non_ack_pkt is the number of continuous non ACK-eliciting + packets. */ + size_t num_non_ack_pkt; + + struct { + /* ect0 is the number of QUIC packets, not UDP datagram, which + are sent in UDP datagram with ECT0 marking. */ + size_t ect0; + /* start_pkt_num is the lowest packet number that are sent + during ECN validation period. */ + int64_t start_pkt_num; + /* validation_pkt_sent is the number of QUIC packets sent during + validation period. */ + size_t validation_pkt_sent; + /* validation_pkt_lost is the number of QUIC packets lost during + validation period. */ + size_t validation_pkt_lost; + } ecn; + } tx; + + struct { + /* 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; + /* + * buffed_pkts is buffered packets which cannot be decrypted with + * the current encryption level. + * + * In server Initial encryption level, 0-RTT packet may be buffered. + * In server Handshake encryption level, Short packet may be buffered. + * + * In client Initial encryption level, Handshake or Short packet may + * be buffered. In client Handshake encryption level, Short packet + * may be buffered. + * + * - 0-RTT packet is only buffered in server Initial encryption + * level ngtcp2_pktns. + * + * - Handshake packet is only buffered in client Handshake + * encryption level ngtcp2_pktns. + * + * - Short packet is only buffered in Short encryption level + * 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. */ + size_t ect0; + size_t ect1; + size_t ce; + } ack; + } ecn; + } rx; + + struct { + struct { + /* frq contains crypto data sorted by their offset. */ + ngtcp2_ksl frq; + /* offset is the offset of crypto stream in this packet number + space. */ + uint64_t offset; + /* ckm is a cryptographic key, and iv to encrypt outgoing + packets. */ + ngtcp2_crypto_km *ckm; + /* hp_ctx is cipher context for packet header protection. */ + ngtcp2_crypto_cipher_ctx hp_ctx; + } tx; + + struct { + /* ckm is a cryptographic key, and iv to decrypt incoming + packets. */ + ngtcp2_crypto_km *ckm; + /* hp_ctx is cipher context for packet header protection. */ + ngtcp2_crypto_cipher_ctx hp_ctx; + } rx; + + ngtcp2_strm strm; + ngtcp2_crypto_ctx ctx; + } crypto; + + ngtcp2_acktr acktr; + ngtcp2_rtb rtb; +} ngtcp2_pktns; + +typedef enum ngtcp2_ecn_state { + NGTCP2_ECN_STATE_TESTING, + NGTCP2_ECN_STATE_UNKNOWN, + NGTCP2_ECN_STATE_FAILED, + NGTCP2_ECN_STATE_CAPABLE, +} ngtcp2_ecn_state; + +struct ngtcp2_conn { + ngtcp2_conn_state state; + ngtcp2_callbacks callbacks; + /* rcid is a connection ID present in Initial or 0-RTT packet from + client as destination connection ID. Server uses this field to + check that duplicated Initial or 0-RTT packet are indeed sent to + this connection. It is also sent to client as + original_destination_connection_id transport parameter. Client + uses this field to validate original_destination_connection_id + transport parameter if no Retry packet is involved. */ + ngtcp2_cid rcid; + /* oscid is the source connection ID initially used by the local + endpoint. */ + ngtcp2_cid oscid; + /* retry_scid is the source connection ID from Retry packet. Client + records it in order to verify retry_source_connection_id + transport parameter. Server does not use this field. */ + ngtcp2_cid retry_scid; + ngtcp2_pktns *in_pktns; + ngtcp2_pktns *hs_pktns; + ngtcp2_pktns pktns; + + 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_ringbuf bound; + /* unused is a set of unused CID received from peer. */ + ngtcp2_ringbuf 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_ringbuf retired; + /* 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; + /* num_retire_queued is the number of RETIRE_CONNECTION_ID frames + queued for transmission. */ + size_t num_retire_queued; + /* 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 { + /* set is a set of CID sent to peer. The peer can use any CIDs in + this set. This includes used CID as well as unused ones. */ + ngtcp2_ksl set; + /* used is a set of CID used by peer. The sort function of this + priority queue takes timestamp when CID is retired and sorts + them in ascending order. */ + ngtcp2_pq used; + /* last_seq is the last sequence number of connection ID. */ + uint64_t last_seq; + /* num_retired is the number of retired Connection ID still + included in set. */ + size_t num_retired; + } scid; + + 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_blks is the number of additional ngtcp2_ack_blk which + ack can contain. */ + size_t max_ack_blks; + /* offset is the offset the local endpoint has sent to the remote + endpoint. */ + uint64_t offset; + /* max_offset is the maximum offset that local endpoint can + send. */ + uint64_t max_offset; + /* last_max_data_ts is the timestamp when last MAX_DATA frame is + sent. */ + ngtcp2_tstamp last_max_data_ts; + + struct { + /* state is the state of ECN validation */ + ngtcp2_ecn_state state; + /* validation_start_ts is the timestamp when ECN validation is + started. It is UINT64_MAX if it has not started yet. */ + ngtcp2_tstamp validation_start_ts; + /* dgram_sent is the number of UDP datagram sent during ECN + validation period. */ + size_t dgram_sent; + } ecn; + } tx; + + struct { + /* unsent_max_offset is the maximum offset that remote endpoint + can send without extending MAX_DATA. This limit is not yet + notified to the remote endpoint. */ + uint64_t unsent_max_offset; + /* offset is the cumulative sum of stream data received for this + connection. */ + uint64_t offset; + /* max_offset is the maximum offset that remote endpoint can + send. */ + uint64_t max_offset; + /* window is the connection-level flow control window size. */ + uint64_t window; + /* path_challenge stores received PATH_CHALLENGE data. */ + ngtcp2_ringbuf path_challenge; + /* ccec is the received connection close error code. */ + ngtcp2_connection_close_error_code ccec; + } rx; + + struct { + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + ngtcp2_crypto_ctx ctx; + /* discard_started_ts is the timestamp when the timer to discard + early key has started. Used by server only. */ + ngtcp2_tstamp discard_started_ts; + } early; + + struct { + ngtcp2_settings settings; + /* transport_params is the local transport parameters. It is used + for Short packet only. */ + ngtcp2_transport_params transport_params; + struct { + /* max_streams is the maximum number of bidirectional streams which + the local endpoint can open. */ + uint64_t max_streams; + /* next_stream_id is the bidirectional stream ID which the local + endpoint opens next. */ + int64_t next_stream_id; + } bidi; + + struct { + /* max_streams is the maximum number of unidirectional streams + which the local endpoint can open. */ + uint64_t max_streams; + /* next_stream_id is the unidirectional stream ID which the + local endpoint opens next. */ + int64_t next_stream_id; + } uni; + } local; + + struct { + /* transport_params is the received transport parameters during + handshake. It is used for Short packet only. */ + ngtcp2_transport_params transport_params; + /* pending_transport_params is received transport parameters + during handshake. It is copied to transport_params when 1RTT + key is available. */ + ngtcp2_transport_params pending_transport_params; + struct { + ngtcp2_idtr idtr; + /* unsent_max_streams is the maximum number of streams of peer + initiated bidirectional stream which the local endpoint can + accept. This limit is not yet notified to the remote + endpoint. */ + uint64_t unsent_max_streams; + /* max_streams is the maximum number of streams of peer + initiated bidirectional stream which the local endpoint can + accept. */ + uint64_t max_streams; + } bidi; + + struct { + ngtcp2_idtr idtr; + /* unsent_max_streams is the maximum number of streams of peer + initiated unidirectional stream which the local endpoint can + accept. This limit is not yet notified to the remote + endpoint. */ + uint64_t unsent_max_streams; + /* max_streams is the maximum number of streams of peer + initiated unidirectional stream which the local endpoint can + accept. */ + uint64_t max_streams; + } uni; + } remote; + + struct { + struct { + /* new_tx_ckm is a new sender 1RTT key which has not been + used. */ + ngtcp2_crypto_km *new_tx_ckm; + /* new_rx_ckm is a new receiver 1RTT key which has not + successfully decrypted incoming packet yet. */ + ngtcp2_crypto_km *new_rx_ckm; + /* old_rx_ckm is an old receiver 1RTT key. */ + ngtcp2_crypto_km *old_rx_ckm; + /* confirmed_ts is the time instant when the key update is + confirmed by the local endpoint last time. UINT64_MAX means + undefined value. */ + ngtcp2_tstamp confirmed_ts; + } key_update; + + /* tls_native_handle is a native handle to TLS session object. */ + void *tls_native_handle; + /* decrypt_hp_buf is a buffer which is used to write unprotected + packet header. */ + ngtcp2_vec decrypt_hp_buf; + /* decrypt_buf is a buffer which is used to write decrypted data. */ + ngtcp2_vec decrypt_buf; + /* retry_aead is AEAD to verify Retry packet integrity. It is + used by client only. */ + ngtcp2_crypto_aead retry_aead; + /* retry_aead_ctx is AEAD cipher context to verify Retry packet + integrity. It is used by client only. */ + ngtcp2_crypto_aead_ctx retry_aead_ctx; + /* tls_error is TLS related error. */ + int tls_error; + /* decryption_failure_count is the number of received packets that + fail authentication. */ + uint64_t decryption_failure_count; + } crypto; + + /* pkt contains the packet intermediate construction data to support + NGTCP2_WRITE_STREAM_FLAG_MORE */ + struct { + ngtcp2_crypto_cc cc; + ngtcp2_pkt_hd hd; + ngtcp2_ppe ppe; + ngtcp2_frame_chain **pfrc; + int pkt_empty; + int hd_logged; + uint8_t rtb_entry_flags; + int was_client_initial; + ngtcp2_ssize hs_spktlen; + } pkt; + + ngtcp2_map strms; + ngtcp2_conn_stat cstat; + ngtcp2_pv *pv; + ngtcp2_log log; + ngtcp2_qlog qlog; + ngtcp2_rst rst; + ngtcp2_cc_algo cc_algo; + ngtcp2_cc cc; + const ngtcp2_mem *mem; + /* idle_ts is the time instant when idle timer started. */ + ngtcp2_tstamp idle_ts; + void *user_data; + uint32_t version; + /* flags is bitwise OR of zero or more of NGTCP2_CONN_FLAG_*. */ + uint16_t flags; + int server; +}; + +typedef enum ngtcp2_vmsg_type { + NGTCP2_VMSG_TYPE_STREAM, + NGTCP2_VMSG_TYPE_DATAGRAM, +} ngtcp2_vmsg_type; + +typedef struct ngtcp2_vmsg_stream { + /* strm is a stream that data is sent to. */ + ngtcp2_strm *strm; + /* flags is bitwise OR of zero or more of + NGTCP2_WRITE_STREAM_FLAG_*. */ + uint32_t flags; + /* data is the pointer to ngtcp2_vec array which contains the stream + data to send. */ + const ngtcp2_vec *data; + /* datacnt is the number of ngtcp2_vec pointed by data. */ + size_t datacnt; + /* pdatalen is the pointer to the variable which the number of bytes + written is assigned to if pdatalen is not NULL. */ + ngtcp2_ssize *pdatalen; +} ngtcp2_vmsg_stream; + +typedef struct ngtcp2_vmsg_datagram { + /* data is the pointer to ngtcp2_vec array which contains the data + to send. */ + const ngtcp2_vec *data; + /* datacnt is the number of ngtcp2_vec pointed by data. */ + size_t datacnt; + /* flags is bitwise OR of zero or more of + NGTCP2_WRITE_DATAGRAM_FLAG_*. */ + uint32_t flags; + /* paccepted is the pointer to the variable which, if it is not + NULL, is assigned nonzero if data is written to a packet. */ + int *paccepted; +} ngtcp2_vmsg_datagram; + +typedef struct ngtcp2_vmsg { + ngtcp2_vmsg_type type; + union { + ngtcp2_vmsg_stream stream; + ngtcp2_vmsg_datagram datagram; + }; +} 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. + */ +ngtcp2_strm *ngtcp2_conn_find_stream(ngtcp2_conn *conn, int64_t stream_id); + +/* + * conn_init_stream initializes |strm|. Its stream ID is |stream_id|. + * This function adds |strm| to conn->strms. |strm| must be allocated + * by the caller. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + * NGTCP2_ERR_CALLBACK_FAILURE + * User-callback function failed. + */ +int ngtcp2_conn_init_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + int64_t stream_id, void *stream_user_data); + +/* + * ngtcp2_conn_close_stream closes stream |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Stream is not found. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +int ngtcp2_conn_close_stream(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code); + +/* + * ngtcp2_conn_close_stream closes stream |strm| if no further + * transmission and reception are allowed, and all reordered incoming + * data are emitted to the application, and the transmitted data are + * acked. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Stream is not found. + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +int ngtcp2_conn_close_stream_if_shut_rdwr(ngtcp2_conn *conn, ngtcp2_strm *strm, + uint64_t app_error_code); + +/* + * ngtcp2_conn_update_rtt updates RTT measurements. |rtt| is a latest + * RTT which is not adjusted by ack delay. |ack_delay| is unscaled + * ack_delay included in ACK frame. |ack_delay| is actually tainted + * (sent by peer), so don't assume that |ack_delay| is always smaller + * than, or equals to |rtt|. + */ +void ngtcp2_conn_update_rtt(ngtcp2_conn *conn, ngtcp2_duration rtt, + ngtcp2_duration ack_delay, ngtcp2_tstamp ts); + +void ngtcp2_conn_set_loss_detection_timer(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_detect_lost_pkt detects lost packets. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_conn_detect_lost_pkt(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_tx_strmq_top returns the ngtcp2_strm which sits on the + * top of queue. tx_strmq must not be empty. + */ +ngtcp2_strm *ngtcp2_conn_tx_strmq_top(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_tx_strmq_pop pops the ngtcp2_strm from the queue. + * tx_strmq must not be empty. + */ +void ngtcp2_conn_tx_strmq_pop(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_tx_strmq_push pushes |strm| into tx_strmq. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_conn_tx_strmq_push(ngtcp2_conn *conn, ngtcp2_strm *strm); + +/* + * ngtcp2_conn_internal_expiry returns the minimum expiry time among + * all timers in |conn|. + */ +ngtcp2_tstamp ngtcp2_conn_internal_expiry(ngtcp2_conn *conn); + +ngtcp2_ssize ngtcp2_conn_write_vmsg(ngtcp2_conn *conn, ngtcp2_path *path, + ngtcp2_pkt_info *pi, uint8_t *dest, + size_t destlen, ngtcp2_vmsg *vmsg, + ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_write_single_frame_pkt writes a packet which contains |fr| + * frame only in the buffer pointed by |dest| whose length if + * |destlen|. |type| is a long packet type to send. If |type| is 0, + * Short packet is used. |dcid| is used as a destination connection + * ID. + * + * The packet written by this function will not be retransmitted. + * + * This function returns the number of bytes written in |dest| if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +ngtcp2_ssize ngtcp2_conn_write_single_frame_pkt( + ngtcp2_conn *conn, ngtcp2_pkt_info *pi, uint8_t *dest, size_t destlen, + uint8_t type, const ngtcp2_cid *dcid, ngtcp2_frame *fr, uint8_t rtb_flags, + const ngtcp2_path *path, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_commit_local_transport_params commits the local + * transport parameters, which is currently set to + * conn->local.settings.transport_params. This function will do some + * amends on transport parameters for adjusting default values. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_INVALID_ARGUMENT + * CID in preferred address equals to the original SCID. + */ +int ngtcp2_conn_commit_local_transport_params(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_lost_pkt_expiry returns the earliest expiry time of + * lost packet. + */ +ngtcp2_tstamp ngtcp2_conn_lost_pkt_expiry(ngtcp2_conn *conn); + +/* + * ngtcp2_conn_remove_lost_pkt removes the expired lost packet. + */ +void ngtcp2_conn_remove_lost_pkt(ngtcp2_conn *conn, ngtcp2_tstamp ts); + +/* + * ngtcp2_conn_resched_frames reschedules frames linked from |*pfrc| + * for retransmission. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_conn_resched_frames(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_frame_chain **pfrc); + +uint64_t ngtcp2_conn_tx_strmq_first_cycle(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_ack_delay_expiry` returns the expiry time point of + * delayed protected ACK. One should call + * `ngtcp2_conn_cancel_expired_ack_delay_timer` and + * `ngtcp2_conn_write_pkt` (or `ngtcp2_conn_writev_stream`) when it + * expires. It returns UINT64_MAX if there is no expiry. + */ +ngtcp2_tstamp ngtcp2_conn_ack_delay_expiry(ngtcp2_conn *conn); + +/** + * @function + * + * `ngtcp2_conn_cancel_expired_ack_delay_timer` stops expired ACK + * delay timer. |ts| is the current time. This function must be + * called when `ngtcp2_conn_ack_delay_expiry` <= ts. + */ +void ngtcp2_conn_cancel_expired_ack_delay_timer(ngtcp2_conn *conn, + ngtcp2_tstamp ts); + +/** + * @function + * + * `ngtcp2_conn_loss_detection_expiry` returns the expiry time point + * of loss detection timer. One should call + * `ngtcp2_conn_on_loss_detection_timer` and `ngtcp2_conn_write_pkt` + * (or `ngtcp2_conn_writev_stream`) when it expires. It returns + * UINT64_MAX if loss detection timer is not armed. + */ +ngtcp2_tstamp ngtcp2_conn_loss_detection_expiry(ngtcp2_conn *conn); + +#endif /* NGTCP2_CONN_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c new file mode 100644 index 00000000000000..9064218dacb61d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.c @@ -0,0 +1,257 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_conv.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_pkt.h" + +uint64_t ngtcp2_get_uint64(const uint8_t *p) { + uint64_t n; + memcpy(&n, p, 8); + return ngtcp2_ntohl64(n); +} + +uint64_t ngtcp2_get_uint48(const uint8_t *p) { + uint64_t n = 0; + memcpy(((uint8_t *)&n) + 2, p, 6); + return ngtcp2_ntohl64(n); +} + +uint32_t ngtcp2_get_uint32(const uint8_t *p) { + uint32_t n; + memcpy(&n, p, 4); + return ngtcp2_ntohl(n); +} + +uint32_t ngtcp2_get_uint24(const uint8_t *p) { + uint32_t n = 0; + memcpy(((uint8_t *)&n) + 1, p, 3); + return ngtcp2_ntohl(n); +} + +uint16_t ngtcp2_get_uint16(const uint8_t *p) { + uint16_t n; + memcpy(&n, p, 2); + return ngtcp2_ntohs(n); +} + +uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p) { + union { + char b[8]; + uint16_t n16; + uint32_t n32; + uint64_t n64; + } n; + + *plen = (size_t)(1u << (*p >> 6)); + + switch (*plen) { + case 1: + return *p; + case 2: + memcpy(&n, p, 2); + n.b[0] &= 0x3f; + return ngtcp2_ntohs(n.n16); + case 4: + memcpy(&n, p, 4); + n.b[0] &= 0x3f; + return ngtcp2_ntohl(n.n32); + case 8: + memcpy(&n, p, 8); + n.b[0] &= 0x3f; + return ngtcp2_ntohl64(n.n64); + default: + assert(0); + } + + return 0; +} + +int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen) { + switch (pkt_numlen) { + case 1: + return *p; + case 2: + return (int64_t)ngtcp2_get_uint16(p); + case 3: + return (int64_t)ngtcp2_get_uint24(p); + case 4: + return (int64_t)ngtcp2_get_uint32(p); + default: + assert(0); + abort(); + } +} + +uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n) { + n = ngtcp2_htonl64(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n) { + n = ngtcp2_htonl64(n); + return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 2, 6); +} + +uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n) { + n = ngtcp2_htonl(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n) { + n = ngtcp2_htonl(n); + return ngtcp2_cpymem(p, ((const uint8_t *)&n) + 1, 3); +} + +uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n) { + n = ngtcp2_htons(n); + return ngtcp2_cpymem(p, (const uint8_t *)&n, sizeof(n)); +} + +uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n) { + uint8_t *rv; + if (n < 64) { + *p++ = (uint8_t)n; + return p; + } + if (n < 16384) { + rv = ngtcp2_put_uint16be(p, (uint16_t)n); + *p |= 0x40; + return rv; + } + if (n < 1073741824) { + rv = ngtcp2_put_uint32be(p, (uint32_t)n); + *p |= 0x80; + return rv; + } + assert(n < 4611686018427387904ULL); + rv = ngtcp2_put_uint64be(p, n); + *p |= 0xc0; + return rv; +} + +uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n) { + uint8_t *rv; + + assert(n < 16384); + + rv = ngtcp2_put_uint16be(p, n); + *p |= 0x40; + + return rv; +} + +uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len) { + switch (len) { + case 1: + *p++ = (uint8_t)pkt_num; + return p; + case 2: + ngtcp2_put_uint16be(p, (uint16_t)pkt_num); + return p + 2; + case 3: + ngtcp2_put_uint24be(p, (uint32_t)pkt_num); + return p + 3; + case 4: + ngtcp2_put_uint32be(p, (uint32_t)pkt_num); + return p + 4; + default: + assert(0); + abort(); + } +} + +size_t ngtcp2_get_varint_len(const uint8_t *p) { + return (size_t)(1u << (*p >> 6)); +} + +size_t ngtcp2_put_varint_len(uint64_t n) { + if (n < 64) { + return 1; + } + if (n < 16384) { + return 2; + } + if (n < 1073741824) { + return 4; + } + assert(n < 4611686018427387904ULL); + return 8; +} + +int64_t ngtcp2_nth_server_bidi_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_SERVER_STREAM_ID_BIDI; + } + + return (int64_t)(((n - 1) << 2) | 0x01); +} + +int64_t ngtcp2_nth_client_bidi_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_CLIENT_STREAM_ID_BIDI; + } + + return (int64_t)((n - 1) << 2); +} + +int64_t ngtcp2_nth_server_uni_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_SERVER_STREAM_ID_UNI; + } + + return (int64_t)(((n - 1) << 2) | 0x03); +} + +int64_t ngtcp2_nth_client_uni_id(uint64_t n) { + if (n == 0) { + return 0; + } + + if ((NGTCP2_MAX_VARINT >> 2) < n - 1) { + return NGTCP2_MAX_CLIENT_STREAM_ID_UNI; + } + + return (int64_t)(((n - 1) << 2) | 0x02); +} + +uint64_t ngtcp2_ord_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2) + 1; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h new file mode 100644 index 00000000000000..dcff0fdb8c174b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_conv.h @@ -0,0 +1,285 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_CONV_H +#define NGTCP2_CONV_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ARPA_INET_H +# include <arpa/inet.h> +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_BYTESWAP_H +# include <byteswap.h> +#endif /* HAVE_BYTESWAP_H */ + +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif /* HAVE_ENDIAN_H */ + +#ifdef HAVE_SYS_ENDIAN_H +# include <sys/endian.h> +#endif /* HAVE_SYS_ENDIAN_H */ + +#include <ngtcp2/ngtcp2.h> + +#if defined(HAVE_BSWAP_64) || \ + (defined(HAVE_DECL_BSWAP_64) && HAVE_DECL_BSWAP_64 > 0) +# define ngtcp2_bswap64 bswap_64 +#else /* !HAVE_BSWAP_64 */ +# define ngtcp2_bswap64(N) \ + ((uint64_t)(ngtcp2_ntohl((uint32_t)(N))) << 32 | \ + ngtcp2_ntohl((uint32_t)((N) >> 32))) +#endif /* !HAVE_BSWAP_64 */ + +#if defined(HAVE_BE64TOH) || \ + (defined(HAVE_DECL_BE64TOH) && HAVE_DECL_BE64TOH > 0) +# define ngtcp2_ntohl64(N) be64toh(N) +# define ngtcp2_htonl64(N) htobe64(N) +#else /* !HAVE_BE64TOH */ +# if defined(WORDS_BIGENDIAN) +# define ngtcp2_ntohl64(N) (N) +# define ngtcp2_htonl64(N) (N) +# else /* !WORDS_BIGENDIAN */ +# define ngtcp2_ntohl64(N) ngtcp2_bswap64(N) +# define ngtcp2_htonl64(N) ngtcp2_bswap64(N) +# endif /* !WORDS_BIGENDIAN */ +#endif /* !HAVE_BE64TOH */ + +#if defined(WIN32) +/* Windows requires ws2_32 library for ntonl family functions. We + define inline functions for those function so that we don't have + dependeny on that lib. */ + +# ifdef _MSC_VER +# define STIN static __inline +# else +# define STIN static inline +# endif + +STIN uint32_t ngtcp2_htonl(uint32_t hostlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostlong >> 24; + *p++ = (hostlong >> 16) & 0xffu; + *p++ = (hostlong >> 8) & 0xffu; + *p = hostlong & 0xffu; + return res; +} + +STIN uint16_t ngtcp2_htons(uint16_t hostshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&res; + *p++ = hostshort >> 8; + *p = hostshort & 0xffu; + return res; +} + +STIN uint32_t ngtcp2_ntohl(uint32_t netlong) { + uint32_t res; + unsigned char *p = (unsigned char *)&netlong; + res = *p++ << 24; + res += *p++ << 16; + res += *p++ << 8; + res += *p; + return res; +} + +STIN uint16_t ngtcp2_ntohs(uint16_t netshort) { + uint16_t res; + unsigned char *p = (unsigned char *)&netshort; + res = *p++ << 8; + res += *p; + return res; +} + +#else /* !WIN32 */ + +# define ngtcp2_htonl htonl +# define ngtcp2_htons htons +# define ngtcp2_ntohl ntohl +# define ngtcp2_ntohs ntohs + +#endif /* !WIN32 */ + +/* + * ngtcp2_get_uint64 reads 8 bytes from |p| as 64 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint64_t ngtcp2_get_uint64(const uint8_t *p); + +/* + * ngtcp2_get_uint48 reads 6 bytes from |p| as 48 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint64_t ngtcp2_get_uint48(const uint8_t *p); + +/* + * ngtcp2_get_uint32 reads 4 bytes from |p| as 32 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint32_t ngtcp2_get_uint32(const uint8_t *p); + +/* + * ngtcp2_get_uint24 reads 3 bytes from |p| as 24 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint32_t ngtcp2_get_uint24(const uint8_t *p); + +/* + * ngtcp2_get_uint16 reads 2 bytes from |p| as 16 bits unsigned + * integer encoded as network byte order, and returns it in host byte + * order. + */ +uint16_t ngtcp2_get_uint16(const uint8_t *p); + +/* + * ngtcp2_get_varint reads variable-length integer from |p|, and + * returns it in host byte order. The number of bytes read is stored + * in |*plen|. + */ +uint64_t ngtcp2_get_varint(size_t *plen, const uint8_t *p); + +/* + * ngtcp2_get_pkt_num reads encoded packet number from |p|. The + * packet number is encoed in |pkt_numlen| bytes. + */ +int64_t ngtcp2_get_pkt_num(const uint8_t *p, size_t pkt_numlen); + +/* + * ngtcp2_put_uint64be writes |n| in host byte order in |p| in network + * byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *ngtcp2_put_uint64be(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_uint48be writes |n| in host byte order in |p| in network + * byte order. It writes only least significant 48 bits. It returns + * the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_uint48be(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_uint32be writes |n| in host byte order in |p| in network + * byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *ngtcp2_put_uint32be(uint8_t *p, uint32_t n); + +/* + * ngtcp2_put_uint24be writes |n| in host byte order in |p| in network + * byte order. It writes only least significant 24 bits. It returns + * the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_uint24be(uint8_t *p, uint32_t n); + +/* + * ngtcp2_put_uint16be writes |n| in host byte order in |p| in network + * byte order. It returns the one beyond of the last written + * position. + */ +uint8_t *ngtcp2_put_uint16be(uint8_t *p, uint16_t n); + +/* + * ngtcp2_put_varint writes |n| in |p| using variable-length integer + * encoding. It returns the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_varint(uint8_t *p, uint64_t n); + +/* + * ngtcp2_put_varint14 writes |n| in |p| using variable-length integer + * encoding. |n| must be strictly less than 16384. The function + * always encodes |n| in 2 bytes. It returns the one beyond of the + * last written position. + */ +uint8_t *ngtcp2_put_varint14(uint8_t *p, uint16_t n); + +/* + * ngtcp2_put_pkt_num encodes |pkt_num| using |len| bytes. It + * returns the one beyond of the last written position. + */ +uint8_t *ngtcp2_put_pkt_num(uint8_t *p, int64_t pkt_num, size_t len); + +/* + * ngtcp2_get_varint_len returns the required number of bytes to read + * variable-length integer starting at |p|. + */ +size_t ngtcp2_get_varint_len(const uint8_t *p); + +/* + * ngtcp2_put_varint_len returns the required number of bytes to + * encode |n|. + */ +size_t ngtcp2_put_varint_len(uint64_t n); + +/* + * ngtcp2_nth_server_bidi_id returns |n|-th server bidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_SERVER_STREAM_ID_BIDI, this function returns + * NGTCP2_MAX_SERVER_STREAM_ID_BIDI. + */ +int64_t ngtcp2_nth_server_bidi_id(uint64_t n); + +/* + * ngtcp2_nth_client_bidi_id returns |n|-th client bidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_CLIENT_STREAM_ID_BIDI, this function returns + * NGTCP2_MAX_CLIENT_STREAM_ID_BIDI. + */ +int64_t ngtcp2_nth_client_bidi_id(uint64_t n); + +/* + * ngtcp2_nth_server_uni_id returns |n|-th server unidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_SERVER_STREAM_ID_UNI, this function returns + * NGTCP2_MAX_SERVER_STREAM_ID_UNI. + */ +int64_t ngtcp2_nth_server_uni_id(uint64_t n); + +/* + * ngtcp2_nth_client_uni_id returns |n|-th client unidirectional + * stream ID. If |n| is 0, it returns 0. If the |n|-th stream ID is + * larger than NGTCP2_MAX_CLIENT_STREAM_ID_UNI, this function returns + * NGTCP2_MAX_CLIENT_STREAM_ID_UNI. + */ +int64_t ngtcp2_nth_client_uni_id(uint64_t n); + +/* + * ngtcp2_ord_stream_id returns the ordinal number of |stream_id|. + */ +uint64_t ngtcp2_ord_stream_id(int64_t stream_id); + +#endif /* NGTCP2_CONV_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c new file mode 100644 index 00000000000000..e11287fd4ad8dd --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.c @@ -0,0 +1,749 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_crypto.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_conn.h" + +int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, + size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_mem *mem) { + int rv = ngtcp2_crypto_km_nocopy_new(pckm, secretlen, ivlen, mem); + if (rv != 0) { + return rv; + } + + if (secretlen) { + memcpy((*pckm)->secret.base, secret, secretlen); + } + if (aead_ctx) { + (*pckm)->aead_ctx = *aead_ctx; + } + memcpy((*pckm)->iv.base, iv, ivlen); + + return 0; +} + +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, + size_t ivlen, const ngtcp2_mem *mem) { + size_t len; + uint8_t *p; + + len = sizeof(ngtcp2_crypto_km) + secretlen + ivlen; + + *pckm = ngtcp2_mem_malloc(mem, len); + if (*pckm == NULL) { + return NGTCP2_ERR_NOMEM; + } + + p = (uint8_t *)(*pckm) + sizeof(ngtcp2_crypto_km); + (*pckm)->secret.base = p; + (*pckm)->secret.len = secretlen; + p += secretlen; + (*pckm)->iv.base = p; + (*pckm)->iv.len = ivlen; + (*pckm)->aead_ctx.native_handle = NULL; + (*pckm)->pkt_num = -1; + (*pckm)->use_count = 0; + (*pckm)->flags = NGTCP2_CRYPTO_KM_FLAG_NONE; + + return 0; +} + +void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem) { + if (ckm == NULL) { + return; + } + + ngtcp2_mem_free(mem, ckm); +} + +void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, + int64_t pkt_num) { + size_t i; + uint64_t n; + + memcpy(dest, iv, ivlen); + n = ngtcp2_htonl64((uint64_t)pkt_num); + + for (i = 0; i < 8; ++i) { + dest[ivlen - 8 + i] ^= ((uint8_t *)&n)[i]; + } +} + +/* + * varint_paramlen returns the length of a single transport parameter + * which has variable integer in its parameter. + */ +static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t param) { + size_t valuelen = ngtcp2_put_varint_len(param); + return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(valuelen) + valuelen; +} + +/* + * write_varint_param writes parameter |id| of the given |value| in + * varint encoding. It returns p + the number of bytes written. + */ +static uint8_t *write_varint_param(uint8_t *p, ngtcp2_transport_param_id id, + uint64_t value) { + p = ngtcp2_put_varint(p, id); + p = ngtcp2_put_varint(p, ngtcp2_put_varint_len(value)); + return ngtcp2_put_varint(p, value); +} + +/* + * cid_paramlen returns the length of a single transport parameter + * which has |cid| as value. + */ +static size_t cid_paramlen(ngtcp2_transport_param_id id, + const ngtcp2_cid *cid) { + return ngtcp2_put_varint_len(id) + ngtcp2_put_varint_len(cid->datalen) + + cid->datalen; +} + +/* + * write_cid_param writes parameter |id| of the given |cid|. It + * returns p + the number of bytes written. + */ +static uint8_t *write_cid_param(uint8_t *p, ngtcp2_transport_param_id id, + const ngtcp2_cid *cid) { + assert(cid->datalen == 0 || cid->datalen >= NGTCP2_MIN_CIDLEN); + assert(cid->datalen <= NGTCP2_MAX_CIDLEN); + + p = ngtcp2_put_varint(p, id); + p = ngtcp2_put_varint(p, cid->datalen); + if (cid->datalen) { + p = ngtcp2_cpymem(p, cid->data, cid->datalen); + } + return p; +} + +ngtcp2_ssize +ngtcp2_encode_transport_params(uint8_t *dest, size_t destlen, + ngtcp2_transport_params_type exttype, + const ngtcp2_transport_params *params) { + uint8_t *p; + size_t len = 0; + /* For some reason, gcc 7.3.0 requires this initialization. */ + size_t preferred_addrlen = 0; + + switch (exttype) { + case NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO: + break; + case NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS: + len += + cid_paramlen(NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, + ¶ms->original_dcid); + + if (params->stateless_reset_token_present) { + len += + ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + + ngtcp2_put_varint_len(NGTCP2_STATELESS_RESET_TOKENLEN) + + NGTCP2_STATELESS_RESET_TOKENLEN; + } + if (params->preferred_address_present) { + assert(params->preferred_address.cid.datalen >= NGTCP2_MIN_CIDLEN); + assert(params->preferred_address.cid.datalen <= NGTCP2_MAX_CIDLEN); + preferred_addrlen = 4 /* ipv4Address */ + 2 /* ipv4Port */ + + 16 /* ipv6Address */ + 2 /* ipv6Port */ + + 1 + + params->preferred_address.cid.datalen /* CID */ + + NGTCP2_STATELESS_RESET_TOKENLEN; + len += ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + + ngtcp2_put_varint_len(preferred_addrlen) + preferred_addrlen; + } + if (params->retry_scid_present) { + len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, + ¶ms->retry_scid); + } + break; + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + len += cid_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, + ¶ms->initial_scid); + + if (params->initial_max_stream_data_bidi_local) { + len += varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params->initial_max_stream_data_bidi_local); + } + if (params->initial_max_stream_data_bidi_remote) { + len += varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params->initial_max_stream_data_bidi_remote); + } + if (params->initial_max_stream_data_uni) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params->initial_max_stream_data_uni); + } + if (params->initial_max_data) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params->initial_max_data); + } + if (params->initial_max_streams_bidi) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params->initial_max_streams_bidi); + } + if (params->initial_max_streams_uni) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params->initial_max_streams_uni); + } + if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, + params->max_udp_payload_size); + } + if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params->ack_delay_exponent); + } + if (params->disable_active_migration) { + len += + ngtcp2_put_varint_len(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) + + ngtcp2_put_varint_len(0); + } + if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params->max_ack_delay / NGTCP2_MILLISECONDS); + } + if (params->max_idle_timeout) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + } + if (params->active_connection_id_limit && + params->active_connection_id_limit != + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); + } + if (params->max_datagram_frame_size) { + len += varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, + params->max_datagram_frame_size); + } + + if (destlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = dest; + + if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + p = write_cid_param( + p, NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID, + ¶ms->original_dcid); + + if (params->stateless_reset_token_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN); + p = ngtcp2_put_varint(p, sizeof(params->stateless_reset_token)); + p = ngtcp2_cpymem(p, params->stateless_reset_token, + sizeof(params->stateless_reset_token)); + } + if (params->preferred_address_present) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS); + p = ngtcp2_put_varint(p, preferred_addrlen); + + p = ngtcp2_cpymem(p, params->preferred_address.ipv4_addr, + sizeof(params->preferred_address.ipv4_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv4_port); + + p = ngtcp2_cpymem(p, params->preferred_address.ipv6_addr, + sizeof(params->preferred_address.ipv6_addr)); + p = ngtcp2_put_uint16be(p, params->preferred_address.ipv6_port); + + *p++ = (uint8_t)params->preferred_address.cid.datalen; + if (params->preferred_address.cid.datalen) { + p = ngtcp2_cpymem(p, params->preferred_address.cid.data, + params->preferred_address.cid.datalen); + } + p = ngtcp2_cpymem( + p, params->preferred_address.stateless_reset_token, + sizeof(params->preferred_address.stateless_reset_token)); + } + if (params->retry_scid_present) { + p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID, + ¶ms->retry_scid); + } + } + + p = write_cid_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID, + ¶ms->initial_scid); + + if (params->initial_max_stream_data_bidi_local) { + p = write_varint_param( + p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params->initial_max_stream_data_bidi_local); + } + + if (params->initial_max_stream_data_bidi_remote) { + p = write_varint_param( + p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params->initial_max_stream_data_bidi_remote); + } + + if (params->initial_max_stream_data_uni) { + p = write_varint_param(p, + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params->initial_max_stream_data_uni); + } + + if (params->initial_max_data) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params->initial_max_data); + } + + if (params->initial_max_streams_bidi) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params->initial_max_streams_bidi); + } + + if (params->initial_max_streams_uni) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params->initial_max_streams_uni); + } + + if (params->max_udp_payload_size != NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, + params->max_udp_payload_size); + } + + if (params->ack_delay_exponent != NGTCP2_DEFAULT_ACK_DELAY_EXPONENT) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params->ack_delay_exponent); + } + + if (params->disable_active_migration) { + p = ngtcp2_put_varint(p, NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION); + p = ngtcp2_put_varint(p, 0); + } + + if (params->max_ack_delay != NGTCP2_DEFAULT_MAX_ACK_DELAY) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params->max_ack_delay / NGTCP2_MILLISECONDS); + } + + if (params->max_idle_timeout) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + } + + if (params->active_connection_id_limit && + params->active_connection_id_limit != + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params->active_connection_id_limit); + } + + if (params->max_datagram_frame_size) { + p = write_varint_param(p, NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, + params->max_datagram_frame_size); + } + + assert((size_t)(p - dest) == len); + + return (ngtcp2_ssize)len; +} + +/* + * decode_varint decodes a single varint from the buffer pointed by + * |p| of length |end - p|. If it decodes an integer successfully, it + * stores the integer in |*pdest| and returns 0. Otherwise it returns + * -1. + */ +static ngtcp2_ssize decode_varint(uint64_t *pdest, const uint8_t *p, + const uint8_t *end) { + size_t len; + + if (p == end) { + return -1; + } + + len = ngtcp2_get_varint_len(p); + if ((uint64_t)(end - p) < len) { + return -1; + } + + *pdest = ngtcp2_get_varint(&len, p); + + return (ngtcp2_ssize)len; +} + +/* + * decode_varint_param decodes length prefixed value from the buffer + * pointed by |p| of length |end - p|. The length and value are + * encoded in varint form. If it decodes a value successfully, it + * stores the value in |*pdest| and returns 0. Otherwise it returns + * -1. + */ +static ngtcp2_ssize decode_varint_param(uint64_t *pdest, const uint8_t *p, + const uint8_t *end) { + const uint8_t *begin = p; + ngtcp2_ssize nread; + uint64_t valuelen; + size_t n; + + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return -1; + } + + p += nread; + + if (p == end) { + return -1; + } + + if ((uint64_t)(end - p) < valuelen) { + return -1; + } + + if (ngtcp2_get_varint_len(p) != valuelen) { + return -1; + } + + *pdest = ngtcp2_get_varint(&n, p); + + p += valuelen; + + return (ngtcp2_ssize)(p - begin); +} + +/* + * decode_cid_param decodes length prefixed ngtcp2_cid from the buffer + * pointed by |p| of length |end - p|. The length is encoded in + * varint form. If it decodes a value successfully, it stores the + * value in |*pdest| and returns the number of bytes read. Otherwise + * it returns -1. + */ +static ngtcp2_ssize decode_cid_param(ngtcp2_cid *pdest, const uint8_t *p, + const uint8_t *end) { + const uint8_t *begin = p; + uint64_t valuelen; + ngtcp2_ssize nread = decode_varint(&valuelen, p, end); + + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + p += nread; + + if ((valuelen != 0 && valuelen < NGTCP2_MIN_CIDLEN) || + valuelen > NGTCP2_MAX_CIDLEN || (size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + ngtcp2_cid_init(pdest, p, (size_t)valuelen); + + p += valuelen; + + return (ngtcp2_ssize)(p - begin); +} + +int ngtcp2_decode_transport_params(ngtcp2_transport_params *params, + ngtcp2_transport_params_type exttype, + const uint8_t *data, size_t datalen) { + const uint8_t *p, *end; + size_t len; + uint64_t param_type; + uint64_t valuelen; + ngtcp2_ssize nread; + int initial_scid_present = 0; + int original_dcid_present = 0; + + p = data; + end = data + datalen; + + /* Set default values */ + memset(params, 0, sizeof(*params)); + params->initial_max_streams_bidi = 0; + params->initial_max_streams_uni = 0; + params->initial_max_stream_data_bidi_local = 0; + params->initial_max_stream_data_bidi_remote = 0; + params->initial_max_stream_data_uni = 0; + params->max_udp_payload_size = NGTCP2_DEFAULT_MAX_UDP_PAYLOAD_SIZE; + params->ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params->stateless_reset_token_present = 0; + params->preferred_address_present = 0; + params->disable_active_migration = 0; + params->max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + params->max_idle_timeout = 0; + params->active_connection_id_limit = + NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params->retry_scid_present = 0; + params->max_datagram_frame_size = 0; + memset(¶ms->retry_scid, 0, sizeof(params->retry_scid)); + memset(¶ms->initial_scid, 0, sizeof(params->initial_scid)); + memset(¶ms->original_dcid, 0, sizeof(params->original_dcid)); + + if (datalen == 0) { + return 0; + } + + for (; (size_t)(end - p) >= 2;) { + nread = decode_varint(¶m_type, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + + switch (param_type) { + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: + nread = decode_varint_param(¶ms->initial_max_stream_data_bidi_local, + p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: + nread = decode_varint_param(¶ms->initial_max_stream_data_bidi_remote, + p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI: + nread = decode_varint_param(¶ms->initial_max_stream_data_uni, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA: + nread = decode_varint_param(¶ms->initial_max_data, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI: + nread = decode_varint_param(¶ms->initial_max_streams_bidi, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (params->initial_max_streams_bidi > NGTCP2_MAX_STREAMS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI: + nread = decode_varint_param(¶ms->initial_max_streams_uni, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (params->initial_max_streams_uni > NGTCP2_MAX_STREAMS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT: + nread = decode_varint_param(¶ms->max_idle_timeout, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->max_idle_timeout *= NGTCP2_MILLISECONDS; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE: + nread = decode_varint_param(¶ms->max_udp_payload_size, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)valuelen != sizeof(params->stateless_reset_token)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if ((size_t)(end - p) < sizeof(params->stateless_reset_token)) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + memcpy(params->stateless_reset_token, p, + sizeof(params->stateless_reset_token)); + params->stateless_reset_token_present = 1; + + p += sizeof(params->stateless_reset_token); + break; + case NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT: + nread = decode_varint_param(¶ms->ack_delay_exponent, p, end); + if (nread < 0 || params->ack_delay_exponent > 20) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + len = 4 /* ipv4Address */ + 2 /* ipv4Port */ + 16 /* ipv6Address */ + + 2 /* ipv6Port */ + + 1 /* cid length */ + NGTCP2_STATELESS_RESET_TOKENLEN; + if (valuelen < len) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + memcpy(params->preferred_address.ipv4_addr, p, + sizeof(params->preferred_address.ipv4_addr)); + p += sizeof(params->preferred_address.ipv4_addr); + params->preferred_address.ipv4_port = ngtcp2_get_uint16(p); + p += sizeof(uint16_t); + + memcpy(params->preferred_address.ipv6_addr, p, + sizeof(params->preferred_address.ipv6_addr)); + p += sizeof(params->preferred_address.ipv6_addr); + params->preferred_address.ipv6_port = ngtcp2_get_uint16(p); + p += sizeof(uint16_t); + + /* cid */ + params->preferred_address.cid.datalen = *p++; + len += params->preferred_address.cid.datalen; + if (valuelen != len || + params->preferred_address.cid.datalen > NGTCP2_MAX_CIDLEN || + params->preferred_address.cid.datalen < NGTCP2_MIN_CIDLEN) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + if (params->preferred_address.cid.datalen) { + memcpy(params->preferred_address.cid.data, p, + params->preferred_address.cid.datalen); + p += params->preferred_address.cid.datalen; + } + + /* stateless reset token */ + memcpy(params->preferred_address.stateless_reset_token, p, + sizeof(params->preferred_address.stateless_reset_token)); + p += sizeof(params->preferred_address.stateless_reset_token); + params->preferred_address_present = 1; + break; + case NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION: + nread = decode_varint(&valuelen, p, end); + if (nread < 0 || valuelen != 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + params->disable_active_migration = 1; + break; + case NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_cid_param(¶ms->original_dcid, p, end); + if (nread < 0) { + return (int)nread; + } + original_dcid_present = 1; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID: + if (exttype != NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + nread = decode_cid_param(¶ms->retry_scid, p, end); + if (nread < 0) { + return (int)nread; + } + params->retry_scid_present = 1; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID: + nread = decode_cid_param(¶ms->initial_scid, p, end); + if (nread < 0) { + return (int)nread; + } + initial_scid_present = 1; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY: + nread = decode_varint_param(¶ms->max_ack_delay, p, end); + if (nread < 0 || params->max_ack_delay >= 16384) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + params->max_ack_delay *= NGTCP2_MILLISECONDS; + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT: + nread = decode_varint_param(¶ms->active_connection_id_limit, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + case NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE: + nread = decode_varint_param(¶ms->max_datagram_frame_size, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + break; + default: + /* Ignore unknown parameter */ + nread = decode_varint(&valuelen, p, end); + if (nread < 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += nread; + if ((size_t)(end - p) < valuelen) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + p += valuelen; + break; + } + } + + if (end - p != 0) { + return NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM; + } + + if (!initial_scid_present || + (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS && + !original_dcid_present)) { + return NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM; + } + + return 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h new file mode 100644 index 00000000000000..6e6f12a0956ade --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_crypto.h @@ -0,0 +1,103 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_CRYPTO_H +#define NGTCP2_CRYPTO_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* NGTCP2_INITIAL_AEAD_OVERHEAD is an overhead of AEAD used by Initial + packets. Because QUIC uses AEAD_AES_128_GCM, the overhead is 16 + bytes. */ +#define NGTCP2_INITIAL_AEAD_OVERHEAD 16 + +/* NGTCP2_MAX_AEAD_OVERHEAD is expected maximum AEAD overhead. */ +#define NGTCP2_MAX_AEAD_OVERHEAD 16 + +/* NGTCP2_CRYPTO_KM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_CRYPTO_KM_FLAG_NONE 0x00 +/* NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE is set if key phase bit is + set. */ +#define NGTCP2_CRYPTO_KM_FLAG_KEY_PHASE_ONE 0x01 + +typedef struct ngtcp2_crypto_km { + ngtcp2_vec secret; + ngtcp2_crypto_aead_ctx aead_ctx; + ngtcp2_vec iv; + /* pkt_num is a packet number of a packet which uses this keying + material. For encryption key, it is the lowest packet number of + a packet. For decryption key, it is the lowest packet number of + a packet which can be decrypted with this keying material. */ + int64_t pkt_num; + /* use_count is the number of encryption applied with this key. + This field is only used for tx key. */ + uint64_t use_count; + /* flags is the bitwise OR of zero or more of + NGTCP2_CRYPTO_KM_FLAG_*. */ + uint8_t flags; +} ngtcp2_crypto_km; + +/* + * ngtcp2_crypto_km_new creates new ngtcp2_crypto_km object and + * assigns its pointer to |*pckm|. The |secret| of length + * |secretlen|, the |key| of length |keylen| and the |iv| of length + * |ivlen| are copied to |*pckm|. If |secretlen| == 0, the function + * assumes no secret is given which is acceptable. The sole reason to + * store secret is update keys. Only 1RTT key can be updated. + */ +int ngtcp2_crypto_km_new(ngtcp2_crypto_km **pckm, const uint8_t *secret, + size_t secretlen, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *iv, size_t ivlen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_crypto_km_nocopy_new is similar to ngtcp2_crypto_km_new, but + * it does not copy secret, key and IV. + */ +int ngtcp2_crypto_km_nocopy_new(ngtcp2_crypto_km **pckm, size_t secretlen, + size_t ivlen, const ngtcp2_mem *mem); + +void ngtcp2_crypto_km_del(ngtcp2_crypto_km *ckm, const ngtcp2_mem *mem); + +typedef struct ngtcp2_crypto_cc { + ngtcp2_crypto_aead aead; + ngtcp2_crypto_cipher hp; + ngtcp2_crypto_km *ckm; + ngtcp2_crypto_cipher_ctx hp_ctx; + ngtcp2_encrypt encrypt; + ngtcp2_decrypt decrypt; + ngtcp2_hp_mask hp_mask; +} ngtcp2_crypto_cc; + +void ngtcp2_crypto_create_nonce(uint8_t *dest, const uint8_t *iv, size_t ivlen, + int64_t pkt_num); + +#endif /* NGTCP2_CRYPTO_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c new file mode 100644 index 00000000000000..bd15e0988be9d8 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.c @@ -0,0 +1,144 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_err.h" + +const char *ngtcp2_strerror(int liberr) { + switch (liberr) { + case 0: + return "NO_ERROR"; + case NGTCP2_ERR_INVALID_ARGUMENT: + return "ERR_INVALID_ARGUMENT"; + case NGTCP2_ERR_NOBUF: + return "ERR_NOBUF"; + case NGTCP2_ERR_PROTO: + return "ERR_PROTO"; + case NGTCP2_ERR_INVALID_STATE: + return "ERR_INVALID_STATE"; + case NGTCP2_ERR_ACK_FRAME: + return "ERR_ACK_FRAME"; + case NGTCP2_ERR_STREAM_ID_BLOCKED: + return "ERR_STREAM_ID_BLOCKED"; + case NGTCP2_ERR_STREAM_IN_USE: + return "ERR_STREAM_IN_USE"; + case NGTCP2_ERR_STREAM_DATA_BLOCKED: + return "ERR_STREAM_DATA_BLOCKED"; + case NGTCP2_ERR_FLOW_CONTROL: + return "ERR_FLOW_CONTROL"; + case NGTCP2_ERR_CONNECTION_ID_LIMIT: + return "ERR_CONNECTION_ID_LIMIT"; + case NGTCP2_ERR_STREAM_LIMIT: + return "ERR_STREAM_LIMIT"; + case NGTCP2_ERR_FINAL_SIZE: + return "ERR_FINAL_SIZE"; + case NGTCP2_ERR_CRYPTO: + return "ERR_CRYPTO"; + case NGTCP2_ERR_PKT_NUM_EXHAUSTED: + return "ERR_PKT_NUM_EXHAUSTED"; + case NGTCP2_ERR_NOMEM: + return "ERR_NOMEM"; + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + return "ERR_REQUIRED_TRANSPORT_PARAM"; + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + return "ERR_MALFORMED_TRANSPORT_PARAM"; + case NGTCP2_ERR_FRAME_ENCODING: + return "ERR_FRAME_ENCODING"; + case NGTCP2_ERR_TLS_DECRYPT: + return "ERR_TLS_DECRYPT"; + case NGTCP2_ERR_STREAM_SHUT_WR: + return "ERR_STREAM_SHUT_WR"; + case NGTCP2_ERR_STREAM_NOT_FOUND: + return "ERR_STREAM_NOT_FOUND"; + case NGTCP2_ERR_STREAM_STATE: + return "ERR_STREAM_STATE"; + case NGTCP2_ERR_RECV_VERSION_NEGOTIATION: + return "ERR_RECV_VERSION_NEGOTIATION"; + case NGTCP2_ERR_CLOSING: + return "ERR_CLOSING"; + case NGTCP2_ERR_DRAINING: + return "ERR_DRAINING"; + case NGTCP2_ERR_TRANSPORT_PARAM: + return "ERR_TRANSPORT_PARAM"; + case NGTCP2_ERR_DISCARD_PKT: + return "ERR_DISCARD_PKT"; + case NGTCP2_ERR_PATH_VALIDATION_FAILED: + return "ERR_PATH_VALIDATION_FAILED"; + case NGTCP2_ERR_CONN_ID_BLOCKED: + return "ERR_CONN_ID_BLOCKED"; + case NGTCP2_ERR_CALLBACK_FAILURE: + return "ERR_CALLBACK_FAILURE"; + case NGTCP2_ERR_INTERNAL: + return "ERR_INTERNAL"; + case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: + return "ERR_CRYPTO_BUFFER_EXCEEDED"; + case NGTCP2_ERR_WRITE_MORE: + return "ERR_WRITE_MORE"; + case NGTCP2_ERR_RETRY: + return "ERR_RETRY"; + case NGTCP2_ERR_DROP_CONN: + return "ERR_DROP_CONN"; + case NGTCP2_ERR_AEAD_LIMIT_REACHED: + return "ERR_AEAD_LIMIT_REACHED"; + case NGTCP2_ERR_NO_VIABLE_PATH: + return "ERR_NO_VIABLE_PATH"; + default: + return "(unknown)"; + } +} + +int ngtcp2_err_is_fatal(int liberr) { return liberr < NGTCP2_ERR_FATAL; } + +uint64_t ngtcp2_err_infer_quic_transport_error_code(int liberr) { + switch (liberr) { + case 0: + return NGTCP2_NO_ERROR; + case NGTCP2_ERR_ACK_FRAME: + case NGTCP2_ERR_FRAME_ENCODING: + return NGTCP2_FRAME_ENCODING_ERROR; + case NGTCP2_ERR_FLOW_CONTROL: + return NGTCP2_FLOW_CONTROL_ERROR; + case NGTCP2_ERR_CONNECTION_ID_LIMIT: + return NGTCP2_CONNECTION_ID_LIMIT_ERROR; + case NGTCP2_ERR_STREAM_LIMIT: + return NGTCP2_STREAM_LIMIT_ERROR; + case NGTCP2_ERR_FINAL_SIZE: + return NGTCP2_FINAL_SIZE_ERROR; + case NGTCP2_ERR_REQUIRED_TRANSPORT_PARAM: + case NGTCP2_ERR_MALFORMED_TRANSPORT_PARAM: + case NGTCP2_ERR_TRANSPORT_PARAM: + return NGTCP2_TRANSPORT_PARAMETER_ERROR; + case NGTCP2_ERR_INVALID_ARGUMENT: + return NGTCP2_INTERNAL_ERROR; + case NGTCP2_ERR_STREAM_STATE: + return NGTCP2_STREAM_STATE_ERROR; + case NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED: + return NGTCP2_CRYPTO_BUFFER_EXCEEDED; + case NGTCP2_ERR_AEAD_LIMIT_REACHED: + return NGTCP2_AEAD_LIMIT_REACHED; + case NGTCP2_ERR_NO_VIABLE_PATH: + return NGTCP2_NO_VIABLE_PATH; + default: + return NGTCP2_PROTOCOL_VIOLATION; + } +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h new file mode 100644 index 00000000000000..9229f5425a63cf --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_err.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_ERR_H +#define NGTCP2_ERR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#endif /* NGTCP2_ERR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c new file mode 100644 index 00000000000000..6e7f3b7e554826 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.c @@ -0,0 +1,129 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_gaptr.h" +#include "ngtcp2_range.h" + +#include <string.h> +#include <assert.h> + +int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem) { + int rv; + ngtcp2_range range = {0, UINT64_MAX}; + + rv = ngtcp2_ksl_init(&gaptr->gap, ngtcp2_ksl_range_compar, + sizeof(ngtcp2_range), mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_ksl_insert(&gaptr->gap, NULL, &range, NULL); + if (rv != 0) { + ngtcp2_ksl_free(&gaptr->gap); + return rv; + } + + gaptr->mem = mem; + + return 0; +} + +void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr) { + if (gaptr == NULL) { + return; + } + + ngtcp2_ksl_free(&gaptr->gap); +} + +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen) { + int rv; + ngtcp2_range k, m, l, r, q = {offset, offset + datalen}; + ngtcp2_ksl_it it; + + it = ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); + + for (; !ngtcp2_ksl_it_end(&it);) { + k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + m = ngtcp2_range_intersect(&q, &k); + if (!ngtcp2_range_len(&m)) { + break; + } + + if (ngtcp2_range_eq(&k, &m)) { + ngtcp2_ksl_remove(&gaptr->gap, &it, &k); + continue; + } + ngtcp2_range_cut(&l, &r, &k, &m); + if (ngtcp2_range_len(&l)) { + ngtcp2_ksl_update_key(&gaptr->gap, &k, &l); + + if (ngtcp2_range_len(&r)) { + rv = ngtcp2_ksl_insert(&gaptr->gap, &it, &r, NULL); + if (rv != 0) { + return rv; + } + } + } else if (ngtcp2_range_len(&r)) { + ngtcp2_ksl_update_key(&gaptr->gap, &k, &r); + } + ngtcp2_ksl_it_next(&it); + } + return 0; +} + +uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr) { + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); + ngtcp2_range r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + return r.begin; +} + +ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset) { + ngtcp2_range q = {offset, offset + 1}; + return ngtcp2_ksl_lower_bound_compar(&gaptr->gap, &q, + ngtcp2_ksl_range_exclusive_compar); +} + +int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, + size_t datalen) { + ngtcp2_range q = {offset, offset + datalen}; + ngtcp2_ksl_it it = ngtcp2_ksl_lower_bound_compar( + &gaptr->gap, &q, ngtcp2_ksl_range_exclusive_compar); + ngtcp2_range k = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + ngtcp2_range m = ngtcp2_range_intersect(&q, &k); + return ngtcp2_range_len(&m) == 0; +} + +void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr) { + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&gaptr->gap); + ngtcp2_range r; + + assert(!ngtcp2_ksl_it_end(&it)); + + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + ngtcp2_ksl_remove(&gaptr->gap, NULL, &r); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h new file mode 100644 index 00000000000000..500d376008ac29 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_gaptr.h @@ -0,0 +1,103 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_GAPTR_H +#define NGTCP2_GAPTR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_ksl.h" + +/* + * ngtcp2_gaptr maintains the gap in the range [0, UINT64_MAX). + */ +typedef struct ngtcp2_gaptr { + /* gap maintains the range of offset which is not received + yet. Initially, its range is [0, UINT64_MAX). */ + ngtcp2_ksl gap; + /* mem is custom memory allocator */ + const ngtcp2_mem *mem; +} ngtcp2_gaptr; + +/* + * ngtcp2_gaptr_init initializes |gaptr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_gaptr_init(ngtcp2_gaptr *gaptr, const ngtcp2_mem *mem); + +/* + * ngtcp2_gaptr_free frees resources allocated for |gaptr|. + */ +void ngtcp2_gaptr_free(ngtcp2_gaptr *gaptr); + +/* + * ngtcp2_gaptr_push adds new data of length |datalen| at the stream + * offset |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_gaptr_push(ngtcp2_gaptr *gaptr, uint64_t offset, size_t datalen); + +/* + * ngtcp2_gaptr_first_gap_offset returns the offset to the first gap. + * If there is no gap, it returns UINT64_MAX. + */ +uint64_t ngtcp2_gaptr_first_gap_offset(ngtcp2_gaptr *gaptr); + +/* + * ngtcp2_gaptr_get_first_gap_after returns the iterator pointing to + * the first gap which overlaps or comes after |offset|. + */ +ngtcp2_ksl_it ngtcp2_gaptr_get_first_gap_after(ngtcp2_gaptr *gaptr, + uint64_t offset); + +/* + * ngtcp2_gaptr_is_pushed returns nonzero if range [offset, offset + + * datalen) is completely pushed into this object. + */ +int ngtcp2_gaptr_is_pushed(ngtcp2_gaptr *gaptr, uint64_t offset, + size_t datalen); + +/* + * ngtcp2_gaptr_drop_first_gap deletes the first gap entirely as if + * the range is pushed. This function assumes that at least one gap + * exists. + */ +void ngtcp2_gaptr_drop_first_gap(ngtcp2_gaptr *gaptr); + +#endif /* NGTCP2_GAPTR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c new file mode 100644 index 00000000000000..f04806b4a8b22f --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.c @@ -0,0 +1,86 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_idtr.h" + +#include <assert.h> + +int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem) { + int rv; + + rv = ngtcp2_gaptr_init(&idtr->gap, mem); + if (rv != 0) { + return rv; + } + + idtr->server = server; + + return 0; +} + +void ngtcp2_idtr_free(ngtcp2_idtr *idtr) { + if (idtr == NULL) { + return; + } + + ngtcp2_gaptr_free(&idtr->gap); +} + +/* + * id_from_stream_id translates |stream_id| to id space used by + * ngtcp2_idtr. + */ +static uint64_t id_from_stream_id(int64_t stream_id) { + return (uint64_t)(stream_id >> 2); +} + +int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + if (ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1)) { + return NGTCP2_ERR_STREAM_IN_USE; + } + + return ngtcp2_gaptr_push(&idtr->gap, q, 1); +} + +int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id) { + uint64_t q; + + assert((idtr->server && (stream_id % 2)) || + (!idtr->server && (stream_id % 2)) == 0); + + q = id_from_stream_id(stream_id); + + return ngtcp2_gaptr_is_pushed(&idtr->gap, q, 1); +} + +uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr) { + return ngtcp2_gaptr_first_gap_offset(&idtr->gap); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h new file mode 100644 index 00000000000000..1be64dc16e7ae7 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_idtr.h @@ -0,0 +1,95 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_IDTR_H +#define NGTCP2_IDTR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_gaptr.h" + +/* + * ngtcp2_idtr tracks the usage of stream ID. + */ +typedef struct ngtcp2_idtr { + /* gap maintains the range of ID which is not used yet. Initially, + its range is [0, UINT64_MAX). */ + ngtcp2_gaptr gap; + /* server is nonzero if this object records server initiated stream + ID. */ + int server; +} ngtcp2_idtr; + +/* + * ngtcp2_idtr_init initializes |idtr|. + * + * If this object records server initiated ID (even number), set + * |server| to nonzero. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_idtr_init(ngtcp2_idtr *idtr, int server, const ngtcp2_mem *mem); + +/* + * ngtcp2_idtr_free frees resources allocated for |idtr|. + */ +void ngtcp2_idtr_free(ngtcp2_idtr *idtr); + +/* + * ngtcp2_idtr_open claims that |stream_id| is in used. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_STREAM_IN_USE + * ID has already been used. + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_idtr_open(ngtcp2_idtr *idtr, int64_t stream_id); + +/* + * ngtcp2_idtr_open tells whether ID |stream_id| is in used or not. + * + * It returns nonzero if |stream_id| is used. + */ +int ngtcp2_idtr_is_open(ngtcp2_idtr *idtr, int64_t stream_id); + +/* + * ngtcp2_idtr_first_gap returns the first id of first gap. If there + * is no gap, it returns UINT64_MAX. The returned id is an id space + * used in this object internally, and not stream ID. + */ +uint64_t ngtcp2_idtr_first_gap(ngtcp2_idtr *idtr); + +#endif /* NGTCP2_IDTR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c new file mode 100644 index 00000000000000..fd25e3514e827b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.c @@ -0,0 +1,741 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_ksl.h" + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <stdio.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_range.h" + +static size_t ksl_nodelen(size_t keylen) { + return (sizeof(ngtcp2_ksl_node) + keylen - sizeof(uint64_t) + 0xf) & + (size_t)~0xf; +} + +static size_t ksl_blklen(size_t nodelen) { + return sizeof(ngtcp2_ksl_blk) + nodelen * NGTCP2_KSL_MAX_NBLK - + sizeof(uint64_t); +} + +/* + * ksl_node_set_key sets |key| to |node|. + */ +static void ksl_node_set_key(ngtcp2_ksl *ksl, ngtcp2_ksl_node *node, + const void *key) { + memcpy(node->key, key, ksl->keylen); +} + +int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem) { + size_t nodelen = ksl_nodelen(keylen); + size_t blklen = ksl_blklen(nodelen); + ngtcp2_ksl_blk *head; + + ksl->head = ngtcp2_mem_malloc(mem, blklen); + if (!ksl->head) { + return NGTCP2_ERR_NOMEM; + } + ksl->front = ksl->back = ksl->head; + ksl->compar = compar; + ksl->keylen = keylen; + ksl->nodelen = nodelen; + ksl->n = 0; + ksl->mem = mem; + + head = ksl->head; + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; + + return 0; +} + +/* + * ksl_free_blk frees |blk| recursively. + */ +static void ksl_free_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + size_t i; + + if (!blk->leaf) { + for (i = 0; i < blk->n; ++i) { + ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk); + } + } + + ngtcp2_mem_free(ksl->mem, blk); +} + +void ngtcp2_ksl_free(ngtcp2_ksl *ksl) { + if (!ksl) { + return; + } + + ksl_free_blk(ksl, ksl->head); +} + +/* + * ksl_split_blk splits |blk| into 2 ngtcp2_ksl_blk objects. The new + * ngtcp2_ksl_blk is always the "right" block. + * + * It returns the pointer to the ngtcp2_ksl_blk created which is the + * located at the right of |blk|, or NULL which indicates out of + * memory error. + */ +static ngtcp2_ksl_blk *ksl_split_blk(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk) { + ngtcp2_ksl_blk *rblk; + + rblk = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (rblk == NULL) { + return NULL; + } + + rblk->next = blk->next; + blk->next = rblk; + if (rblk->next) { + rblk->next->prev = rblk; + } else if (ksl->back == blk) { + ksl->back = rblk; + } + rblk->prev = blk; + rblk->leaf = blk->leaf; + + rblk->n = blk->n / 2; + + memcpy(rblk->nodes, blk->nodes + ksl->nodelen * (blk->n - rblk->n), + ksl->nodelen * rblk->n); + + blk->n -= rblk->n; + + assert(blk->n >= NGTCP2_KSL_MIN_NBLK); + assert(rblk->n >= NGTCP2_KSL_MIN_NBLK); + + return rblk; +} + +/* + * ksl_split_node splits a node included in |blk| at the position |i| + * into 2 adjacent nodes. The new node is always inserted at the + * position |i+1|. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + ngtcp2_ksl_node *node; + ngtcp2_ksl_blk *lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk, *rblk; + + rblk = ksl_split_blk(ksl, lblk); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + memmove(blk->nodes + (i + 2) * ksl->nodelen, + blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + node = ngtcp2_ksl_nth_node(ksl, blk, i + 1); + node->blk = rblk; + ++blk->n; + ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + + node = ngtcp2_ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + + return 0; +} + +/* + * ksl_split_head splits a head (root) block. It increases the height + * of skip list by 1. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +static int ksl_split_head(ngtcp2_ksl *ksl) { + ngtcp2_ksl_blk *rblk = NULL, *lblk, *nhead = NULL; + ngtcp2_ksl_node *node; + + rblk = ksl_split_blk(ksl, ksl->head); + if (rblk == NULL) { + return NGTCP2_ERR_NOMEM; + } + + lblk = ksl->head; + + nhead = ngtcp2_mem_malloc(ksl->mem, ksl_blklen(ksl->nodelen)); + if (nhead == NULL) { + ngtcp2_mem_free(ksl->mem, rblk); + return NGTCP2_ERR_NOMEM; + } + nhead->next = nhead->prev = NULL; + nhead->n = 2; + nhead->leaf = 0; + + node = ngtcp2_ksl_nth_node(ksl, nhead, 0); + ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + node->blk = lblk; + + node = ngtcp2_ksl_nth_node(ksl, nhead, 1); + ksl_node_set_key(ksl, node, ngtcp2_ksl_nth_node(ksl, rblk, rblk->n - 1)->key); + node->blk = rblk; + + ksl->head = nhead; + + return 0; +} + +/* + * insert_node inserts a node whose key is |key| with the associated + * |data| at the index of |i|. This function assumes that the number + * of nodes contained by |blk| is strictly less than + * NGTCP2_KSL_MAX_NBLK. + */ +static void ksl_insert_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i, + const ngtcp2_ksl_key *key, void *data) { + ngtcp2_ksl_node *node; + + assert(blk->n < NGTCP2_KSL_MAX_NBLK); + + memmove(blk->nodes + (i + 1) * ksl->nodelen, blk->nodes + i * ksl->nodelen, + ksl->nodelen * (blk->n - i)); + + node = ngtcp2_ksl_nth_node(ksl, blk, i); + ksl_node_set_key(ksl, node, key); + node->data = data; + + ++blk->n; +} + +static size_t ksl_bsearch(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + const ngtcp2_ksl_key *key, ngtcp2_ksl_compar compar) { + ngtcp2_ssize left = -1, right = (ngtcp2_ssize)blk->n, mid; + ngtcp2_ksl_node *node; + + while (right - left > 1) { + mid = (left + right) >> 1; + node = ngtcp2_ksl_nth_node(ksl, blk, (size_t)mid); + if (compar((ngtcp2_ksl_key *)node->key, key)) { + left = mid; + } else { + right = mid; + } + } + + return (size_t)right; +} + +int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key, void *data) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_node *node; + size_t i; + int rv; + + if (blk->n == NGTCP2_KSL_MAX_NBLK) { + rv = ksl_split_head(ksl); + if (rv != 0) { + return rv; + } + blk = ksl->head; + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i < blk->n && + !ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) { + if (it) { + *it = ngtcp2_ksl_end(ksl); + } + return NGTCP2_ERR_INVALID_ARGUMENT; + } + ksl_insert_node(ksl, blk, i, key, data); + ++ksl->n; + if (it) { + ngtcp2_ksl_it_init(it, ksl, blk, i); + } + return 0; + } + + if (i == blk->n) { + /* This insertion extends the largest key in this subtree. */ + for (; !blk->leaf;) { + node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1); + if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, blk->n - 1); + if (rv != 0) { + return rv; + } + node = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1); + } + ksl_node_set_key(ksl, node, key); + blk = node->blk; + } + ksl_insert_node(ksl, blk, blk->n, key, data); + ++ksl->n; + if (it) { + ngtcp2_ksl_it_init(it, ksl, blk, blk->n - 1); + } + return 0; + } + + node = ngtcp2_ksl_nth_node(ksl, blk, i); + + if (node->blk->n == NGTCP2_KSL_MAX_NBLK) { + rv = ksl_split_node(ksl, blk, i); + if (rv != 0) { + return rv; + } + if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) { + node = ngtcp2_ksl_nth_node(ksl, blk, i + 1); + if (ksl->compar((ngtcp2_ksl_key *)node->key, key)) { + ksl_node_set_key(ksl, node, key); + } + } + } + + blk = node->blk; + } +} + +/* + * ksl_remove_node removes the node included in |blk| at the index of + * |i|. + */ +static void ksl_remove_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + memmove(blk->nodes + i * ksl->nodelen, blk->nodes + (i + 1) * ksl->nodelen, + ksl->nodelen * (blk->n - (i + 1))); + + --blk->n; +} + +/* + * ksl_merge_node merges 2 nodes which are the nodes at the index of + * |i| and |i + 1|. + * + * If |blk| is the direct descendant of head (root) block and the head + * block contains just 2 nodes, the merged block becomes head block, + * which decreases the height of |ksl| by 1. + * + * This function returns the pointer to the merged block. + */ +static ngtcp2_ksl_blk *ksl_merge_node(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, + size_t i) { + ngtcp2_ksl_blk *lblk, *rblk; + + assert(i + 1 < blk->n); + + lblk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; + rblk = ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk; + + assert(lblk->n + rblk->n < NGTCP2_KSL_MAX_NBLK); + + memcpy(lblk->nodes + ksl->nodelen * lblk->n, rblk->nodes, + ksl->nodelen * rblk->n); + + lblk->n += rblk->n; + lblk->next = rblk->next; + if (lblk->next) { + lblk->next->prev = lblk; + } else if (ksl->back == rblk) { + ksl->back = lblk; + } + + ngtcp2_mem_free(ksl->mem, rblk); + + if (ksl->head == blk && blk->n == 2) { + ngtcp2_mem_free(ksl->mem, ksl->head); + ksl->head = lblk; + } else { + ksl_remove_node(ksl, blk, i + 1); + ksl_node_set_key(ksl, ngtcp2_ksl_nth_node(ksl, blk, i), + ngtcp2_ksl_nth_node(ksl, lblk, lblk->n - 1)->key); + } + + return lblk; +} + +/* + * ksl_shift_left moves the first nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i - 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible. + */ +static void ksl_shift_left(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + ngtcp2_ksl_node *lnode, *rnode; + size_t n; + + assert(i > 0); + + lnode = ngtcp2_ksl_nth_node(ksl, blk, i - 1); + rnode = ngtcp2_ksl_nth_node(ksl, blk, i); + + assert(lnode->blk->n < NGTCP2_KSL_MAX_NBLK); + assert(rnode->blk->n > NGTCP2_KSL_MIN_NBLK); + + n = (lnode->blk->n + rnode->blk->n + 1) / 2 - lnode->blk->n; + + assert(n > 0); + assert(lnode->blk->n <= NGTCP2_KSL_MAX_NBLK - n); + assert(rnode->blk->n >= NGTCP2_KSL_MIN_NBLK + n); + + memcpy(lnode->blk->nodes + ksl->nodelen * lnode->blk->n, rnode->blk->nodes, + ksl->nodelen * n); + + lnode->blk->n += (uint32_t)n; + rnode->blk->n -= (uint32_t)n; + + ksl_node_set_key( + ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); + + memmove(rnode->blk->nodes, rnode->blk->nodes + ksl->nodelen * n, + ksl->nodelen * rnode->blk->n); +} + +/* + * ksl_shift_right moves the last nodes in blk->nodes[i]->blk->nodes + * to blk->nodes[i + 1]->blk->nodes in a manner that they have the + * same amount of nodes as much as possible.. + */ +static void ksl_shift_right(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t i) { + ngtcp2_ksl_node *lnode, *rnode; + size_t n; + + assert(i < blk->n - 1); + + lnode = ngtcp2_ksl_nth_node(ksl, blk, i); + rnode = ngtcp2_ksl_nth_node(ksl, blk, i + 1); + + assert(lnode->blk->n > NGTCP2_KSL_MIN_NBLK); + assert(rnode->blk->n < NGTCP2_KSL_MAX_NBLK); + + n = (lnode->blk->n + rnode->blk->n + 1) / 2 - rnode->blk->n; + + assert(n > 0); + assert(lnode->blk->n >= NGTCP2_KSL_MIN_NBLK + n); + assert(rnode->blk->n <= NGTCP2_KSL_MAX_NBLK - n); + + memmove(rnode->blk->nodes + ksl->nodelen * n, rnode->blk->nodes, + ksl->nodelen * rnode->blk->n); + + rnode->blk->n += (uint32_t)n; + lnode->blk->n -= (uint32_t)n; + + memcpy(rnode->blk->nodes, lnode->blk->nodes + ksl->nodelen * lnode->blk->n, + ksl->nodelen * n); + + ksl_node_set_key( + ksl, lnode, ngtcp2_ksl_nth_node(ksl, lnode->blk, lnode->blk->n - 1)->key); +} + +/* + * key_equal returns nonzero if |lhs| and |rhs| are equal using the + * function |compar|. + */ +static int key_equal(ngtcp2_ksl_compar compar, const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + return !compar(lhs, rhs) && !compar(rhs, lhs); +} + +int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_node *node; + size_t i; + + if (!blk->leaf && blk->n == 2 && + ngtcp2_ksl_nth_node(ksl, blk, 0)->blk->n == NGTCP2_KSL_MIN_NBLK && + ngtcp2_ksl_nth_node(ksl, blk, 1)->blk->n == NGTCP2_KSL_MIN_NBLK) { + blk = ksl_merge_node(ksl, ksl->head, 0); + } + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (i == blk->n) { + if (it) { + *it = ngtcp2_ksl_end(ksl); + } + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (blk->leaf) { + if (ksl->compar(key, ngtcp2_ksl_nth_node(ksl, blk, i)->key)) { + if (it) { + *it = ngtcp2_ksl_end(ksl); + } + return NGTCP2_ERR_INVALID_ARGUMENT; + } + ksl_remove_node(ksl, blk, i); + --ksl->n; + if (it) { + if (blk->n == i && blk->next) { + ngtcp2_ksl_it_init(it, ksl, blk->next, 0); + } else { + ngtcp2_ksl_it_init(it, ksl, blk, i); + } + } + return 0; + } + + node = ngtcp2_ksl_nth_node(ksl, blk, i); + + if (node->blk->n > NGTCP2_KSL_MIN_NBLK) { + blk = node->blk; + continue; + } + + assert(node->blk->n == NGTCP2_KSL_MIN_NBLK); + + if (i + 1 < blk->n && + ngtcp2_ksl_nth_node(ksl, blk, i + 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { + ksl_shift_left(ksl, blk, i + 1); + blk = node->blk; + continue; + } + + if (i > 0 && + ngtcp2_ksl_nth_node(ksl, blk, i - 1)->blk->n > NGTCP2_KSL_MIN_NBLK) { + ksl_shift_right(ksl, blk, i - 1); + blk = node->blk; + continue; + } + + if (i + 1 < blk->n) { + blk = ksl_merge_node(ksl, blk, i); + continue; + } + + assert(i > 0); + + blk = ksl_merge_node(ksl, blk, i - 1); + } +} + +ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, ksl->compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; + } +} + +ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key, + ngtcp2_ksl_compar compar) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_it it; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, key, compar); + + if (blk->leaf) { + if (i == blk->n && blk->next) { + blk = blk->next; + i = 0; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + + if (i == blk->n) { + /* This happens if descendant has smaller key. Fast forward to + find last node in this subtree. */ + for (; !blk->leaf; blk = ngtcp2_ksl_nth_node(ksl, blk, blk->n - 1)->blk) + ; + if (blk->next) { + blk = blk->next; + i = 0; + } else { + i = blk->n; + } + ngtcp2_ksl_it_init(&it, ksl, blk, i); + return it; + } + blk = ngtcp2_ksl_nth_node(ksl, blk, i)->blk; + } +} + +void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, + const ngtcp2_ksl_key *new_key) { + ngtcp2_ksl_blk *blk = ksl->head; + ngtcp2_ksl_node *node; + size_t i; + + for (;;) { + i = ksl_bsearch(ksl, blk, old_key, ksl->compar); + + assert(i < blk->n); + node = ngtcp2_ksl_nth_node(ksl, blk, i); + + if (blk->leaf) { + assert(key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key)); + ksl_node_set_key(ksl, node, new_key); + return; + } + + if (key_equal(ksl->compar, (ngtcp2_ksl_key *)node->key, old_key) || + ksl->compar((ngtcp2_ksl_key *)node->key, new_key)) { + ksl_node_set_key(ksl, node, new_key); + } + + blk = node->blk; + } +} + +static void ksl_print(ngtcp2_ksl *ksl, ngtcp2_ksl_blk *blk, size_t level) { + size_t i; + ngtcp2_ksl_node *node; + + fprintf(stderr, "LV=%zu n=%u\n", level, blk->n); + + if (blk->leaf) { + for (i = 0; i < blk->n; ++i) { + node = ngtcp2_ksl_nth_node(ksl, blk, i); + fprintf(stderr, " %" PRId64, *(int64_t *)(void *)node->key); + } + fprintf(stderr, "\n"); + return; + } + + for (i = 0; i < blk->n; ++i) { + ksl_print(ksl, ngtcp2_ksl_nth_node(ksl, blk, i)->blk, level + 1); + } +} + +size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl) { return ksl->n; } + +void ngtcp2_ksl_clear(ngtcp2_ksl *ksl) { + size_t i; + ngtcp2_ksl_blk *head; + + if (!ksl->head->leaf) { + for (i = 0; i < ksl->head->n; ++i) { + ksl_free_blk(ksl, ngtcp2_ksl_nth_node(ksl, ksl->head, i)->blk); + } + } + + ksl->front = ksl->back = ksl->head; + ksl->n = 0; + + head = ksl->head; + + head->next = head->prev = NULL; + head->n = 0; + head->leaf = 1; +} + +void ngtcp2_ksl_print(ngtcp2_ksl *ksl) { ksl_print(ksl, ksl->head, 0); } + +ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl) { + ngtcp2_ksl_it it; + ngtcp2_ksl_it_init(&it, ksl, ksl->front, 0); + return it; +} + +ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl) { + ngtcp2_ksl_it it; + ngtcp2_ksl_it_init(&it, ksl, ksl->back, ksl->back->n); + return it; +} + +void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, + ngtcp2_ksl_blk *blk, size_t i) { + it->ksl = ksl; + it->blk = blk; + it->i = i; +} + +void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it) { + assert(it->i < it->blk->n); + return ngtcp2_ksl_nth_node(it->ksl, it->blk, it->i)->data; +} + +void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it) { + assert(!ngtcp2_ksl_it_begin(it)); + + if (it->i == 0) { + it->blk = it->blk->prev; + it->i = it->blk->n - 1; + } else { + --it->i; + } +} + +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it) { + return it->i == 0 && it->blk->prev == NULL; +} + +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + const ngtcp2_range *a = lhs, *b = rhs; + return a->begin < b->begin; +} + +int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs) { + const ngtcp2_range *a = lhs, *b = rhs; + return a->begin < b->begin && + !(ngtcp2_max(a->begin, b->begin) < ngtcp2_min(a->end, b->end)); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h new file mode 100644 index 00000000000000..f24487d03e9524 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ksl.h @@ -0,0 +1,329 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_KSL_H +#define NGTCP2_KSL_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> + +#include <ngtcp2/ngtcp2.h> + +/* + * Skip List using single key instead of range. + */ + +#define NGTCP2_KSL_DEGR 16 +/* NGTCP2_KSL_MAX_NBLK is the maximum number of nodes which a single + block can contain. */ +#define NGTCP2_KSL_MAX_NBLK (2 * NGTCP2_KSL_DEGR - 1) +/* NGTCP2_KSL_MIN_NBLK is the minimum number of nodes which a single + block other than root must contains. */ +#define NGTCP2_KSL_MIN_NBLK (NGTCP2_KSL_DEGR - 1) + +/* + * ngtcp2_ksl_key represents key in ngtcp2_ksl. + */ +typedef void ngtcp2_ksl_key; + +typedef struct ngtcp2_ksl_node ngtcp2_ksl_node; + +typedef struct ngtcp2_ksl_blk ngtcp2_ksl_blk; + +/* + * ngtcp2_ksl_node is a node which contains either ngtcp2_ksl_blk or + * opaque data. If a node is an internal node, it contains + * ngtcp2_ksl_blk. Otherwise, it has data. The key is stored at the + * location starting at key. + */ +struct ngtcp2_ksl_node { + union { + ngtcp2_ksl_blk *blk; + void *data; + }; + union { + uint64_t align; + /* key is a buffer to include key associated to this node. + Because the length of key is unknown until ngtcp2_ksl_init is + called, the actual buffer will be allocated after this + field. */ + uint8_t key[1]; + }; +}; + +/* + * ngtcp2_ksl_blk contains ngtcp2_ksl_node objects. + */ +struct ngtcp2_ksl_blk { + /* next points to the next block if leaf field is nonzero. */ + ngtcp2_ksl_blk *next; + /* prev points to the previous block if leaf field is nonzero. */ + ngtcp2_ksl_blk *prev; + /* n is the number of nodes this object contains in nodes. */ + uint32_t n; + /* leaf is nonzero if this block contains leaf nodes. */ + uint32_t leaf; + union { + uint64_t align; + /* nodes is a buffer to contain NGTCP2_KSL_MAX_NBLK + ngtcp2_ksl_node objects. Because ngtcp2_ksl_node object is + allocated along with the additional variable length key + storage, the size of buffer is unknown until ngtcp2_ksl_init is + called. */ + uint8_t nodes[1]; + }; +}; + +/* + * ngtcp2_ksl_compar is a function type which returns nonzero if key + * |lhs| should be placed before |rhs|. It returns 0 otherwise. + */ +typedef int (*ngtcp2_ksl_compar)(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs); + +typedef struct ngtcp2_ksl ngtcp2_ksl; + +typedef struct ngtcp2_ksl_it ngtcp2_ksl_it; + +/* + * ngtcp2_ksl_it is a forward iterator to iterate nodes. + */ +struct ngtcp2_ksl_it { + const ngtcp2_ksl *ksl; + ngtcp2_ksl_blk *blk; + size_t i; +}; + +/* + * ngtcp2_ksl is a deterministic paged skip list. + */ +struct ngtcp2_ksl { + /* head points to the root block. */ + ngtcp2_ksl_blk *head; + /* front points to the first leaf block. */ + ngtcp2_ksl_blk *front; + /* back points to the last leaf block. */ + ngtcp2_ksl_blk *back; + ngtcp2_ksl_compar compar; + size_t n; + /* keylen is the size of key */ + size_t keylen; + /* nodelen is the actual size of ngtcp2_ksl_node including key + storage. */ + size_t nodelen; + const ngtcp2_mem *mem; +}; + +/* + * ngtcp2_ksl_init initializes |ksl|. |compar| specifies compare + * function. |keylen| is the length of key. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_ksl_init(ngtcp2_ksl *ksl, ngtcp2_ksl_compar compar, size_t keylen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_ksl_free frees resources allocated for |ksl|. If |ksl| is + * NULL, this function does nothing. It does not free the memory + * region pointed by |ksl| itself. + */ +void ngtcp2_ksl_free(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_insert inserts |key| with its associated |data|. On + * successful insertion, the iterator points to the inserted node is + * stored in |*it|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + * NGTCP2_ERR_INVALID_ARGUMENT + * |key| already exists. + */ +int ngtcp2_ksl_insert(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key, void *data); + +/* + * ngtcp2_ksl_remove removes the |key| from |ksl|. + * + * This function assigns the iterator to |*it|, which points to the + * node which is located at the right next of the removed node if |it| + * is not NULL. If |key| is not found, no deletion takes place and + * the return value of ngtcp2_ksl_end(ksl) is assigned to |*it|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * |key| does not exist. + */ +int ngtcp2_ksl_remove(ngtcp2_ksl *ksl, ngtcp2_ksl_it *it, + const ngtcp2_ksl_key *key); + +/* + * ngtcp2_ksl_lower_bound returns the iterator which points to the + * first node which has the key which is equal to |key| or the last + * node which satisfies !compar(&node->key, key). If there is no such + * node, it returns the iterator which satisfies ngtcp2_ksl_it_end(it) + * != 0. + */ +ngtcp2_ksl_it ngtcp2_ksl_lower_bound(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key); + +/* + * ngtcp2_ksl_lower_bound_compar works like ngtcp2_ksl_lower_bound, + * but it takes custom function |compar| to do lower bound search. + */ +ngtcp2_ksl_it ngtcp2_ksl_lower_bound_compar(ngtcp2_ksl *ksl, + const ngtcp2_ksl_key *key, + ngtcp2_ksl_compar compar); + +/* + * ngtcp2_ksl_update_key replaces the key of nodes which has |old_key| + * with |new_key|. |new_key| must be strictly greater than the + * previous node and strictly smaller than the next node. + */ +void ngtcp2_ksl_update_key(ngtcp2_ksl *ksl, const ngtcp2_ksl_key *old_key, + const ngtcp2_ksl_key *new_key); + +/* + * ngtcp2_ksl_begin returns the iterator which points to the first + * node. If there is no node in |ksl|, it returns the iterator which + * satisfies ngtcp2_ksl_it_end(it) != 0. + */ +ngtcp2_ksl_it ngtcp2_ksl_begin(const ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_end returns the iterator which points to the node + * following the last node. The returned object satisfies + * ngtcp2_ksl_it_end(). If there is no node in |ksl|, it returns the + * iterator which satisfies ngtcp2_ksl_it_begin(it) != 0. + */ +ngtcp2_ksl_it ngtcp2_ksl_end(const ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_len returns the number of elements stored in |ksl|. + */ +size_t ngtcp2_ksl_len(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_clear removes all elements stored in |ksl|. + */ +void ngtcp2_ksl_clear(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_nth_node returns the |n|th node under |blk|. + */ +#define ngtcp2_ksl_nth_node(KSL, BLK, N) \ + ((ngtcp2_ksl_node *)(void *)((BLK)->nodes + (KSL)->nodelen * (N))) + +/* + * ngtcp2_ksl_print prints its internal state in stderr. It assumes + * that the key is of type int64_t. This function should be used for + * the debugging purpose only. + */ +void ngtcp2_ksl_print(ngtcp2_ksl *ksl); + +/* + * ngtcp2_ksl_it_init initializes |it|. + */ +void ngtcp2_ksl_it_init(ngtcp2_ksl_it *it, const ngtcp2_ksl *ksl, + ngtcp2_ksl_blk *blk, size_t i); + +/* + * ngtcp2_ksl_it_get returns the data associated to the node which + * |it| points to. It is undefined to call this function when + * ngtcp2_ksl_it_end(it) returns nonzero. + */ +void *ngtcp2_ksl_it_get(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_it_next advances the iterator by one. It is undefined + * if this function is called when ngtcp2_ksl_it_end(it) returns + * nonzero. + */ +#define ngtcp2_ksl_it_next(IT) \ + (++(IT)->i == (IT)->blk->n && (IT)->blk->next \ + ? ((IT)->blk = (IT)->blk->next, (IT)->i = 0) \ + : 0) + +/* + * ngtcp2_ksl_it_prev moves backward the iterator by one. It is + * undefined if this function is called when ngtcp2_ksl_it_begin(it) + * returns nonzero. + */ +void ngtcp2_ksl_it_prev(ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_it_end returns nonzero if |it| points to the beyond the + * last node. + */ +#define ngtcp2_ksl_it_end(IT) \ + ((IT)->blk->n == (IT)->i && (IT)->blk->next == NULL) + +/* + * ngtcp2_ksl_it_begin returns nonzero if |it| points to the first + * node. |it| might satisfy both ngtcp2_ksl_it_begin(&it) and + * ngtcp2_ksl_it_end(&it) if the skip list has no node. + */ +int ngtcp2_ksl_it_begin(const ngtcp2_ksl_it *it); + +/* + * ngtcp2_ksl_key returns the key of the node which |it| points to. + * It is undefined to call this function when ngtcp2_ksl_it_end(it) + * returns nonzero. + */ +#define ngtcp2_ksl_it_key(IT) \ + ((ngtcp2_ksl_key *)ngtcp2_ksl_nth_node((IT)->ksl, (IT)->blk, (IT)->i)->key) + +/* + * ngtcp2_ksl_range_compar is an implementation of ngtcp2_ksl_compar. + * lhs->ptr and rhs->ptr must point to ngtcp2_range object and the + * function returns nonzero if (const ngtcp2_range *)(lhs->ptr)->begin + * < (const ngtcp2_range *)(rhs->ptr)->begin. + */ +int ngtcp2_ksl_range_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs); + +/* + * ngtcp2_ksl_range_exclusive_compar is an implementation of + * ngtcp2_ksl_compar. lhs->ptr and rhs->ptr must point to + * ngtcp2_range object and the function returns nonzero if (const + * ngtcp2_range *)(lhs->ptr)->begin < (const ngtcp2_range + * *)(rhs->ptr)->begin and the 2 ranges do not intersect. + */ +int ngtcp2_ksl_range_exclusive_compar(const ngtcp2_ksl_key *lhs, + const ngtcp2_ksl_key *rhs); + +#endif /* NGTCP2_KSL_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c new file mode 100644 index 00000000000000..0259404d3e2c06 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.c @@ -0,0 +1,767 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_log.h" + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif +#include <assert.h> +#include <string.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_macro.h" + +void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, + ngtcp2_printf log_printf, ngtcp2_tstamp ts, + void *user_data) { + if (scid) { + ngtcp2_encode_hex(log->scid, scid->data, scid->datalen); + } else { + log->scid[0] = '\0'; + } + log->log_printf = log_printf; + log->ts = log->last_ts = ts; + log->user_data = user_data; +} + +/* + * # Log header + * + * <LEVEL><TIMESTAMP> <SCID> <EVENT> + * + * <LEVEL>: + * Log level. I=Info, W=Warning, E=Error + * + * <TIMESTAMP>: + * Timestamp relative to ngtcp2_log.ts field in milliseconds + * resolution. + * + * <SCID>: + * Source Connection ID in hex string. + * + * <EVENT>: + * Event. pkt=packet, frm=frame, rcv=recovery, cry=crypto, + * con=connection(catch all) + * + * # Frame event + * + * <DIR> <PKN> <PKTNAME>(<PKTTYPE>) <FRAMENAME>(<FRAMETYPE>) + * + * <DIR>: + * Flow direction. tx=transmission, rx=reception + * + * <PKN>: + * Packet number. + * + * <PKTNAME>: + * Packet name. (e.g., Initial, Handshake, S01) + * + * <PKTTYPE>: + * Packet type in hex string. + * + * <FRAMENAME>: + * Frame name. (e.g., STREAM, ACK, PING) + * + * <FRAMETYPE>: + * Frame type in hex string. + */ + +#define NGTCP2_LOG_BUFLEN 4096 + +/* TODO Split second and remaining fraction with comma */ +#define NGTCP2_LOG_HD "I%08" PRIu64 " 0x%s %s" +#define NGTCP2_LOG_PKT NGTCP2_LOG_HD " %s %" PRId64 " %s(0x%02x)" +#define NGTCP2_LOG_TP NGTCP2_LOG_HD " remote transport_parameters" + +#define NGTCP2_LOG_FRM_HD_FIELDS(DIR) \ + timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "frm", \ + (DIR), hd->pkt_num, strpkttype(hd), hd->type + +#define NGTCP2_LOG_PKT_HD_FIELDS(DIR) \ + timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "pkt", \ + (DIR), hd->pkt_num, strpkttype(hd), hd->type + +#define NGTCP2_LOG_TP_HD_FIELDS \ + timestamp_cast(log->last_ts - log->ts), (const char *)log->scid, "cry" + +static const char *strerrorcode(uint64_t error_code) { + switch (error_code) { + case NGTCP2_NO_ERROR: + return "NO_ERROR"; + case NGTCP2_INTERNAL_ERROR: + return "INTERNAL_ERROR"; + case NGTCP2_CONNECTION_REFUSED: + return "CONNECTION_REFUSED"; + case NGTCP2_FLOW_CONTROL_ERROR: + return "FLOW_CONTROL_ERROR"; + case NGTCP2_STREAM_LIMIT_ERROR: + return "STREAM_LIMIT_ERROR"; + case NGTCP2_STREAM_STATE_ERROR: + return "STREAM_STATE_ERROR"; + case NGTCP2_FINAL_SIZE_ERROR: + return "FINAL_SIZE_ERROR"; + case NGTCP2_FRAME_ENCODING_ERROR: + return "FRAME_ENCODING_ERROR"; + case NGTCP2_TRANSPORT_PARAMETER_ERROR: + return "TRANSPORT_PARAMETER_ERROR"; + case NGTCP2_CONNECTION_ID_LIMIT_ERROR: + return "CONNECTION_ID_LIMIT_ERROR"; + case NGTCP2_PROTOCOL_VIOLATION: + return "PROTOCOL_VIOLATION"; + case NGTCP2_INVALID_TOKEN: + return "INVALID_TOKEN"; + case NGTCP2_APPLICATION_ERROR: + return "APPLICATION_ERROR"; + case NGTCP2_CRYPTO_BUFFER_EXCEEDED: + return "CRYPTO_BUFFER_EXCEEDED"; + case NGTCP2_KEY_UPDATE_ERROR: + return "KEY_UPDATE_ERROR"; + default: + if (0x100u <= error_code && error_code <= 0x1ffu) { + return "CRYPTO_ERROR"; + } + return "(unknown)"; + } +} + +static const char *strapperrorcode(uint64_t app_error_code) { + (void)app_error_code; + return "(unknown)"; +} + +static const char *strpkttype_long(uint8_t type) { + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + return "VN"; + case NGTCP2_PKT_INITIAL: + return "Initial"; + case NGTCP2_PKT_RETRY: + return "Retry"; + case NGTCP2_PKT_HANDSHAKE: + return "Handshake"; + case NGTCP2_PKT_0RTT: + return "0RTT"; + default: + return "(unknown)"; + } +} + +static const char *strpkttype(const ngtcp2_pkt_hd *hd) { + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + return strpkttype_long(hd->type); + } + return "Short"; +} + +static const char *strevent(ngtcp2_log_event ev) { + switch (ev) { + case NGTCP2_LOG_EVENT_CON: + return "con"; + case NGTCP2_LOG_EVENT_PKT: + return "pkt"; + case NGTCP2_LOG_EVENT_FRM: + return "frm"; + case NGTCP2_LOG_EVENT_RCV: + return "rcv"; + case NGTCP2_LOG_EVENT_CRY: + return "cry"; + case NGTCP2_LOG_EVENT_PTV: + return "ptv"; + case NGTCP2_LOG_EVENT_NONE: + default: + return "non"; + } +} + +static uint64_t timestamp_cast(uint64_t ns) { return ns / NGTCP2_MILLISECONDS; } + +static void log_fr_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_stream *fr, const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " STREAM(0x%02x) id=0x%" PRIx64 + " fin=%d offset=%" PRIu64 " len=%zu uni=%d"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type | fr->flags, + fr->stream_id, fr->fin, fr->offset, + ngtcp2_vec_len(fr->data, fr->datacnt), + (fr->stream_id & 0x2) != 0); +} + +static void log_fr_ack(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_ack *fr, const char *dir) { + int64_t largest_ack, min_ack; + size_t i; + + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) largest_ack=%" PRId64 + " ack_delay=%" PRIu64 "(%" PRIu64 + ") ack_block_count=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->largest_ack, + fr->ack_delay_unscaled / NGTCP2_MILLISECONDS, fr->ack_delay, + fr->num_blks); + + largest_ack = fr->largest_ack; + min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; + + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64 + "] block_count=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, min_ack, + fr->first_ack_blklen); + + for (i = 0; i < fr->num_blks; ++i) { + const ngtcp2_ack_blk *blk = &fr->blks[i]; + largest_ack = min_ack - (int64_t)blk->gap - 2; + min_ack = largest_ack - (int64_t)blk->blklen; + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) block=[%" PRId64 "..%" PRId64 + "] gap=%" PRIu64 " block_count=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, largest_ack, + min_ack, blk->gap, blk->blklen); + } + + if (fr->type == NGTCP2_FRAME_ACK_ECN) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " ACK(0x%02x) ect0=%" PRIu64 + " ect1=%" PRIu64 " ce=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->ecn.ect0, + fr->ecn.ect1, fr->ecn.ce); + } +} + +static void log_fr_padding(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_padding *fr, const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PADDING(0x%02x) len=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->len); +} + +static void log_fr_reset_stream(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_reset_stream *fr, + const char *dir) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " RESET_STREAM(0x%02x) id=0x%" PRIx64 + " app_error_code=%s(0x%" PRIx64 ") final_size=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + strapperrorcode(fr->app_error_code), fr->app_error_code, fr->final_size); +} + +static void log_fr_connection_close(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_connection_close *fr, + const char *dir) { + char reason[256]; + size_t reasonlen = ngtcp2_min(sizeof(reason) - 1, fr->reasonlen); + + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT + " CONNECTION_CLOSE(0x%02x) error_code=%s(0x%" PRIx64 ") " + "frame_type=%" PRIx64 " reason_len=%zu reason=[%s]"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + fr->type == NGTCP2_FRAME_CONNECTION_CLOSE + ? strerrorcode(fr->error_code) + : strapperrorcode(fr->error_code), + fr->error_code, fr->frame_type, fr->reasonlen, + ngtcp2_encode_printable_ascii(reason, fr->reason, reasonlen)); +} + +static void log_fr_max_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_max_data *fr, const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " MAX_DATA(0x%02x) max_data=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_data); +} + +static void log_fr_max_stream_data(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_max_stream_data *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " MAX_STREAM_DATA(0x%02x) id=0x%" PRIx64 + " max_stream_data=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + fr->max_stream_data); +} + +static void log_fr_max_streams(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_max_streams *fr, const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " MAX_STREAMS(0x%02x) max_streams=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams); +} + +static void log_fr_ping(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_ping *fr, const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " PING(0x%02x)"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); +} + +static void log_fr_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_data_blocked *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " DATA_BLOCKED(0x%02x) offset=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset); +} + +static void log_fr_stream_data_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_stream_data_blocked *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " STREAM_DATA_BLOCKED(0x%02x) id=0x%" PRIx64 + " offset=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + fr->offset); +} + +static void log_fr_streams_blocked(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_streams_blocked *fr, + const char *dir) { + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " STREAMS_BLOCKED(0x%02x) max_streams=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->max_streams); +} + +static void log_fr_new_connection_id(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_new_connection_id *fr, + const char *dir) { + uint8_t buf[sizeof(fr->stateless_reset_token) * 2 + 1]; + uint8_t cid[sizeof(fr->cid.data) * 2 + 1]; + + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " NEW_CONNECTION_ID(0x%02x) seq=%" PRIu64 + " cid=0x%s retire_prior_to=%" PRIu64 + " stateless_reset_token=0x%s"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq, + (const char *)ngtcp2_encode_hex(cid, fr->cid.data, fr->cid.datalen), + fr->retire_prior_to, + (const char *)ngtcp2_encode_hex(buf, fr->stateless_reset_token, + sizeof(fr->stateless_reset_token))); +} + +static void log_fr_stop_sending(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_stop_sending *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " STOP_SENDING(0x%02x) id=0x%" PRIx64 + " app_error_code=%s(0x%" PRIx64 ")"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->stream_id, + strapperrorcode(fr->app_error_code), fr->app_error_code); +} + +static void log_fr_path_challenge(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_path_challenge *fr, + const char *dir) { + uint8_t buf[sizeof(fr->data) * 2 + 1]; + + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " PATH_CHALLENGE(0x%02x) data=0x%s"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data))); +} + +static void log_fr_path_response(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_path_response *fr, + const char *dir) { + uint8_t buf[sizeof(fr->data) * 2 + 1]; + + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " PATH_RESPONSE(0x%02x) data=0x%s"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + (const char *)ngtcp2_encode_hex(buf, fr->data, sizeof(fr->data))); +} + +static void log_fr_crypto(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_crypto *fr, const char *dir) { + size_t datalen = 0; + size_t i; + + for (i = 0; i < fr->datacnt; ++i) { + datalen += fr->data[i].len; + } + + log->log_printf( + log->user_data, + (NGTCP2_LOG_PKT " CRYPTO(0x%02x) offset=%" PRIu64 " len=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->offset, datalen); +} + +static void log_fr_new_token(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_new_token *fr, const char *dir) { + /* Show at most first 64 bytes of token. If token is longer than 64 + bytes, log first 64 bytes and then append "*" */ + uint8_t buf[128 + 1 + 1]; + uint8_t *p; + + if (fr->token.len > 64) { + p = ngtcp2_encode_hex(buf, fr->token.base, 64); + p[128] = '*'; + p[129] = '\0'; + } else { + p = ngtcp2_encode_hex(buf, fr->token.base, fr->token.len); + } + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " NEW_TOKEN(0x%02x) token=0x%s len=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, (const char *)p, fr->token.len); +} + +static void log_fr_retire_connection_id(ngtcp2_log *log, + const ngtcp2_pkt_hd *hd, + const ngtcp2_retire_connection_id *fr, + const char *dir) { + log->log_printf(log->user_data, + (NGTCP2_LOG_PKT " RETIRE_CONNECTION_ID(0x%02x) seq=%" PRIu64), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, fr->seq); +} + +static void log_fr_handshake_done(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_handshake_done *fr, + const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " HANDSHAKE_DONE(0x%02x)"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type); +} + +static void log_fr_datagram(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_datagram *fr, const char *dir) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " DATAGRAM(0x%02x) len=%zu"), + NGTCP2_LOG_FRM_HD_FIELDS(dir), fr->type, + ngtcp2_vec_len(fr->data, fr->datacnt)); +} + +static void log_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr, const char *dir) { + switch (fr->type) { + case NGTCP2_FRAME_STREAM: + log_fr_stream(log, hd, &fr->stream, dir); + break; + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + log_fr_ack(log, hd, &fr->ack, dir); + break; + case NGTCP2_FRAME_PADDING: + log_fr_padding(log, hd, &fr->padding, dir); + break; + case NGTCP2_FRAME_RESET_STREAM: + log_fr_reset_stream(log, hd, &fr->reset_stream, dir); + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + log_fr_connection_close(log, hd, &fr->connection_close, dir); + break; + case NGTCP2_FRAME_MAX_DATA: + log_fr_max_data(log, hd, &fr->max_data, dir); + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + log_fr_max_stream_data(log, hd, &fr->max_stream_data, dir); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + log_fr_max_streams(log, hd, &fr->max_streams, dir); + break; + case NGTCP2_FRAME_PING: + log_fr_ping(log, hd, &fr->ping, dir); + break; + case NGTCP2_FRAME_DATA_BLOCKED: + log_fr_data_blocked(log, hd, &fr->data_blocked, dir); + break; + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + log_fr_stream_data_blocked(log, hd, &fr->stream_data_blocked, dir); + break; + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + log_fr_streams_blocked(log, hd, &fr->streams_blocked, dir); + break; + case NGTCP2_FRAME_NEW_CONNECTION_ID: + log_fr_new_connection_id(log, hd, &fr->new_connection_id, dir); + break; + case NGTCP2_FRAME_STOP_SENDING: + log_fr_stop_sending(log, hd, &fr->stop_sending, dir); + break; + case NGTCP2_FRAME_PATH_CHALLENGE: + log_fr_path_challenge(log, hd, &fr->path_challenge, dir); + break; + case NGTCP2_FRAME_PATH_RESPONSE: + log_fr_path_response(log, hd, &fr->path_response, dir); + break; + case NGTCP2_FRAME_CRYPTO: + log_fr_crypto(log, hd, &fr->crypto, dir); + break; + case NGTCP2_FRAME_NEW_TOKEN: + log_fr_new_token(log, hd, &fr->new_token, dir); + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + log_fr_retire_connection_id(log, hd, &fr->retire_connection_id, dir); + break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + log_fr_handshake_done(log, hd, &fr->handshake_done, dir); + break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + log_fr_datagram(log, hd, &fr->datagram, dir); + break; + default: + assert(0); + } +} + +void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr) { + if (!log->log_printf) { + return; + } + + log_fr(log, hd, fr, "rx"); +} + +void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr) { + if (!log->log_printf) { + return; + } + + log_fr(log, hd, fr, "tx"); +} + +void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv) { + size_t i; + + if (!log->log_printf) { + return; + } + + for (i = 0; i < nsv; ++i) { + log->log_printf(log->user_data, (NGTCP2_LOG_PKT " v=0x%08x"), + NGTCP2_LOG_PKT_HD_FIELDS("rx"), sv[i]); + } +} + +void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr) { + uint8_t buf[sizeof(sr->stateless_reset_token) * 2 + 1]; + ngtcp2_pkt_hd shd; + ngtcp2_pkt_hd *hd = &shd; + + if (!log->log_printf) { + return; + } + + memset(&shd, 0, sizeof(shd)); + + log->log_printf( + log->user_data, (NGTCP2_LOG_PKT " token=0x%s randlen=%zu"), + NGTCP2_LOG_PKT_HD_FIELDS("rx"), + (const char *)ngtcp2_encode_hex(buf, sr->stateless_reset_token, + sizeof(sr->stateless_reset_token)), + sr->randlen); +} + +void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, + const ngtcp2_transport_params *params) { + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN * 2 + 1]; + uint8_t addr[16 * 2 + 7 + 1]; + uint8_t cid[NGTCP2_MAX_CIDLEN * 2 + 1]; + + if (!log->log_printf) { + return; + } + + if (exttype == NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS) { + if (params->stateless_reset_token_present) { + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " stateless_reset_token=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + token, params->stateless_reset_token, + sizeof(params->stateless_reset_token))); + } + + if (params->preferred_address_present) { + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " preferred_address.ipv4_addr=%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_ipv4( + addr, params->preferred_address.ipv4_addr)); + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv4_port=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv4_port); + + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " preferred_address.ipv6_addr=%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_ipv6( + addr, params->preferred_address.ipv6_addr)); + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " preferred_address.ipv6_port=%u"), + NGTCP2_LOG_TP_HD_FIELDS, params->preferred_address.ipv6_port); + + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " preferred_address.cid=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + cid, params->preferred_address.cid.data, + params->preferred_address.cid.datalen)); + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " preferred_address.stateless_reset_token=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex( + token, params->preferred_address.stateless_reset_token, + sizeof(params->preferred_address.stateless_reset_token))); + } + + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " original_destination_connection_id=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex(cid, params->original_dcid.data, + params->original_dcid.datalen)); + + if (params->retry_scid_present) { + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " retry_source_connection_id=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex(cid, params->retry_scid.data, + params->retry_scid.datalen)); + } + } + + log->log_printf( + log->user_data, (NGTCP2_LOG_TP " initial_source_connection_id=0x%s"), + NGTCP2_LOG_TP_HD_FIELDS, + (const char *)ngtcp2_encode_hex(cid, params->initial_scid.data, + params->initial_scid.datalen)); + + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " initial_max_stream_data_bidi_local=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_local); + log->log_printf( + log->user_data, + (NGTCP2_LOG_TP " initial_max_stream_data_bidi_remote=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_bidi_remote); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " initial_max_stream_data_uni=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_stream_data_uni); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " initial_max_data=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_data); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " initial_max_streams_bidi=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_bidi); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " initial_max_streams_uni=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->initial_max_streams_uni); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_idle_timeout=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, + params->max_idle_timeout / NGTCP2_MILLISECONDS); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " max_udp_payload_size=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->max_udp_payload_size); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " ack_delay_exponent=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->ack_delay_exponent); + log->log_printf(log->user_data, (NGTCP2_LOG_TP " max_ack_delay=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, + params->max_ack_delay / NGTCP2_MILLISECONDS); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " active_connection_id_limit=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->active_connection_id_limit); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " disable_active_migration=%d"), + NGTCP2_LOG_TP_HD_FIELDS, params->disable_active_migration); + log->log_printf(log->user_data, + (NGTCP2_LOG_TP " max_datagram_frame_size=%" PRIu64), + NGTCP2_LOG_TP_HD_FIELDS, params->max_datagram_frame_size); +} + +void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, + uint8_t flags, ngtcp2_tstamp sent_ts) { + if (!log->log_printf) { + return; + } + + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " lost type=%s(0x%02x) sent_ts=%" PRIu64, pkt_num, + (flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(type) : "Short", + type, sent_ts); +} + +static void log_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const char *dir) { + uint8_t dcid[sizeof(hd->dcid.data) * 2 + 1]; + uint8_t scid[sizeof(hd->scid.data) * 2 + 1]; + + if (!log->log_printf) { + return; + } + + ngtcp2_log_info( + log, NGTCP2_LOG_EVENT_PKT, + "%s pkn=%" PRId64 " dcid=0x%s scid=0x%s type=%s(0x%02x) len=%zu k=%d", + dir, hd->pkt_num, + (const char *)ngtcp2_encode_hex(dcid, hd->dcid.data, hd->dcid.datalen), + (const char *)ngtcp2_encode_hex(scid, hd->scid.data, hd->scid.datalen), + (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) ? strpkttype_long(hd->type) + : "Short", + hd->type, hd->len, (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) != 0); +} + +void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { + log_pkt_hd(log, hd, "rx"); +} + +void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { + log_pkt_hd(log, hd, "tx"); +} + +void ngtcp2_log_info(ngtcp2_log *log, ngtcp2_log_event ev, const char *fmt, + ...) { + va_list ap; + int n; + char buf[NGTCP2_LOG_BUFLEN]; + + if (!log->log_printf) { + return; + } + + va_start(ap, fmt); + n = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (n < 0 || (size_t)n >= sizeof(buf)) { + return; + } + + log->log_printf(log->user_data, (NGTCP2_LOG_HD " %s"), + timestamp_cast(log->last_ts - log->ts), log->scid, + strevent(ev), buf); +} + +void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd) { + ngtcp2_log_info(log, NGTCP2_LOG_EVENT_PKT, + "cancel tx pkn=%" PRId64 " type=%s(0x%02x)", hd->pkt_num, + strpkttype(hd), hd->type); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h new file mode 100644 index 00000000000000..bd1ac240a9398a --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_log.h @@ -0,0 +1,80 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_LOG_H +#define NGTCP2_LOG_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" + +typedef struct ngtcp2_log ngtcp2_log; + +struct ngtcp2_log { + /* log_printf is a sink to write log. NULL means no logging + output. */ + ngtcp2_printf log_printf; + /* ts is the time point used to write time delta in the log. */ + ngtcp2_tstamp ts; + /* last_ts is the most recent time point that this object is + told. */ + ngtcp2_tstamp last_ts; + /* user_data is user-defined opaque data which is passed to + log_pritnf. */ + void *user_data; + /* scid is SCID encoded as NULL-terminated hex string. */ + uint8_t scid[NGTCP2_MAX_CIDLEN * 2 + 1]; +}; + +void ngtcp2_log_init(ngtcp2_log *log, const ngtcp2_cid *scid, + ngtcp2_printf log_printf, ngtcp2_tstamp ts, + void *user_data); + +void ngtcp2_log_rx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr); +void ngtcp2_log_tx_fr(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const ngtcp2_frame *fr); + +void ngtcp2_log_rx_vn(ngtcp2_log *log, const ngtcp2_pkt_hd *hd, + const uint32_t *sv, size_t nsv); + +void ngtcp2_log_rx_sr(ngtcp2_log *log, const ngtcp2_pkt_stateless_reset *sr); + +void ngtcp2_log_remote_tp(ngtcp2_log *log, uint8_t exttype, + const ngtcp2_transport_params *params); + +void ngtcp2_log_pkt_lost(ngtcp2_log *log, int64_t pkt_num, uint8_t type, + uint8_t flags, ngtcp2_tstamp sent_ts); + +void ngtcp2_log_rx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); + +void ngtcp2_log_tx_pkt_hd(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); + +void ngtcp2_log_tx_cancel(ngtcp2_log *log, const ngtcp2_pkt_hd *hd); + +#endif /* NGTCP2_LOG_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h new file mode 100644 index 00000000000000..e2603aae15dd04 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_macro.h @@ -0,0 +1,53 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_MACRO_H +#define NGTCP2_MACRO_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stddef.h> + +#include <ngtcp2/ngtcp2.h> + +#define ngtcp2_min(A, B) ((A) < (B) ? (A) : (B)) +#define ngtcp2_max(A, B) ((A) > (B) ? (A) : (B)) + +#define ngtcp2_struct_of(ptr, type, member) \ + ((type *)(void *)((char *)(ptr)-offsetof(type, member))) + +/* ngtcp2_list_insert inserts |T| before |*PD|. The contract is that + this is singly linked list, and the next element is pointed by next + field of the previous element. |PD| must be a pointer to the + pointer to the next field of the previous element of |*PD|: if C is + the previous element of |PD|, PD = &C->next. */ +#define ngtcp2_list_insert(T, PD) \ + do { \ + (T)->next = *(PD); \ + *(PD) = (T); \ + } while (0) + +#endif /* NGTCP2_MACRO_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c new file mode 100644 index 00000000000000..5d24961dc98afe --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.c @@ -0,0 +1,332 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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_map.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_conv.h" + +#define INITIAL_TABLE_LENGTH 256 + +int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem) { + map->mem = mem; + map->tablelen = INITIAL_TABLE_LENGTH; + map->table = ngtcp2_mem_calloc(mem, map->tablelen, sizeof(ngtcp2_map_bucket)); + if (map->table == NULL) { + return NGTCP2_ERR_NOMEM; + } + + map->size = 0; + + return 0; +} + +void ngtcp2_map_free(ngtcp2_map *map) { + size_t i; + ngtcp2_map_bucket *bkt; + + if (!map) { + return; + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + if (bkt->ksl) { + ngtcp2_ksl_free(bkt->ksl); + ngtcp2_mem_free(map->mem, bkt->ksl); + } + } + + ngtcp2_mem_free(map->mem, map->table); +} + +void ngtcp2_map_each_free(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr) { + uint32_t i; + ngtcp2_map_bucket *bkt; + ngtcp2_ksl_it it; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->ptr) { + func(bkt->ptr, ptr); + bkt->ptr = NULL; + assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + func(ngtcp2_ksl_it_get(&it), ptr); + } + + ngtcp2_ksl_free(bkt->ksl); + ngtcp2_mem_free(map->mem, bkt->ksl); + bkt->ksl = NULL; + } + } +} + +int ngtcp2_map_each(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr) { + int rv; + uint32_t i; + ngtcp2_map_bucket *bkt; + ngtcp2_ksl_it it; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->ptr) { + rv = func(bkt->ptr, ptr); + if (rv != 0) { + return rv; + } + assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + rv = func(ngtcp2_ksl_it_get(&it), ptr); + if (rv != 0) { + return rv; + } + } + } + } + return 0; +} + +void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key) { + entry->key = key; +} + +/* FNV1a hash */ +static uint32_t hash(key_type key, uint32_t mod) { + uint8_t *p, *end; + uint32_t h = 0x811C9DC5u; + + key = ngtcp2_htonl64(key); + p = (uint8_t *)&key; + end = p + sizeof(key_type); + + for (; p != end;) { + h ^= *p++; + h += (h << 1) + (h << 4) + (h << 7) + (h << 8) + (h << 24); + } + + return h & (mod - 1); +} + +static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *(key_type *)lhs < *(key_type *)rhs; +} + +static int map_insert(ngtcp2_map *map, ngtcp2_map_bucket *table, + uint32_t tablelen, ngtcp2_map_entry *entry) { + uint32_t h = hash(entry->key, tablelen); + ngtcp2_map_bucket *bkt = &table[h]; + const ngtcp2_mem *mem = map->mem; + int rv; + + if (bkt->ptr == NULL && (bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0)) { + bkt->ptr = entry; + return 0; + } + + if (!bkt->ksl) { + bkt->ksl = ngtcp2_mem_malloc(mem, sizeof(*bkt->ksl)); + if (bkt->ksl == NULL) { + return NGTCP2_ERR_NOMEM; + } + ngtcp2_ksl_init(bkt->ksl, less, sizeof(key_type), mem); + } + + if (bkt->ptr) { + rv = ngtcp2_ksl_insert(bkt->ksl, NULL, &bkt->ptr->key, bkt->ptr); + if (rv != 0) { + return rv; + } + + bkt->ptr = NULL; + } + + return ngtcp2_ksl_insert(bkt->ksl, NULL, &entry->key, entry); +} + +/* new_tablelen must be power of 2 */ +static int map_resize(ngtcp2_map *map, uint32_t new_tablelen) { + uint32_t i; + ngtcp2_map_bucket *new_table; + ngtcp2_map_bucket *bkt; + ngtcp2_ksl_it it; + int rv; + + new_table = + ngtcp2_mem_calloc(map->mem, new_tablelen, sizeof(ngtcp2_map_bucket)); + if (new_table == NULL) { + return NGTCP2_ERR_NOMEM; + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + + if (bkt->ptr) { + rv = map_insert(map, new_table, new_tablelen, bkt->ptr); + if (rv != 0) { + goto fail; + } + assert(bkt->ksl == NULL || ngtcp2_ksl_len(bkt->ksl) == 0); + continue; + } + + if (bkt->ksl) { + for (it = ngtcp2_ksl_begin(bkt->ksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + rv = map_insert(map, new_table, new_tablelen, ngtcp2_ksl_it_get(&it)); + if (rv != 0) { + goto fail; + } + } + } + } + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + if (bkt->ksl) { + ngtcp2_ksl_free(bkt->ksl); + ngtcp2_mem_free(map->mem, bkt->ksl); + } + } + + ngtcp2_mem_free(map->mem, map->table); + map->tablelen = new_tablelen; + map->table = new_table; + + return 0; + +fail: + for (i = 0; i < new_tablelen; ++i) { + bkt = &new_table[i]; + if (bkt->ksl) { + ngtcp2_ksl_free(bkt->ksl); + ngtcp2_mem_free(map->mem, bkt->ksl); + } + } + + return rv; +} + +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *new_entry) { + int rv; + + /* Load factor is 0.75 */ + if ((map->size + 1) * 4 > map->tablelen * 3) { + rv = map_resize(map, map->tablelen * 2); + if (rv != 0) { + return rv; + } + } + rv = map_insert(map, map->table, map->tablelen, new_entry); + if (rv != 0) { + return rv; + } + ++map->size; + return 0; +} + +ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key) { + ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + ngtcp2_ksl_it it; + + if (bkt->ptr) { + if (bkt->ptr->key == key) { + return bkt->ptr; + } + return NULL; + } + + if (bkt->ksl) { + it = ngtcp2_ksl_lower_bound(bkt->ksl, &key); + if (ngtcp2_ksl_it_end(&it) || *(key_type *)ngtcp2_ksl_it_key(&it) != key) { + return NULL; + } + return ngtcp2_ksl_it_get(&it); + } + + return NULL; +} + +int ngtcp2_map_remove(ngtcp2_map *map, key_type key) { + ngtcp2_map_bucket *bkt = &map->table[hash(key, map->tablelen)]; + int rv; + + if (bkt->ptr) { + if (bkt->ptr->key == key) { + bkt->ptr = NULL; + --map->size; + return 0; + } + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (bkt->ksl) { + rv = ngtcp2_ksl_remove(bkt->ksl, NULL, &key); + if (rv != 0) { + return rv; + } + --map->size; + return 0; + } + + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +void ngtcp2_map_clear(ngtcp2_map *map) { + uint32_t i; + ngtcp2_map_bucket *bkt; + + for (i = 0; i < map->tablelen; ++i) { + bkt = &map->table[i]; + bkt->ptr = NULL; + if (bkt->ksl) { + ngtcp2_ksl_free(bkt->ksl); + ngtcp2_mem_free(map->mem, bkt->ksl); + bkt->ksl = NULL; + } + } + + map->size = 0; +} + +size_t ngtcp2_map_size(ngtcp2_map *map) { return map->size; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h new file mode 100644 index 00000000000000..bbb2e705d69e6c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_map.h @@ -0,0 +1,152 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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_MAP_H +#define NGTCP2_MAP_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_ksl.h" + +/* Implementation of unordered map */ + +typedef uint64_t key_type; + +typedef struct ngtcp2_map_entry ngtcp2_map_entry; + +struct ngtcp2_map_entry { + key_type key; +}; + +typedef struct ngtcp2_map_bucket { + ngtcp2_map_entry *ptr; + ngtcp2_ksl *ksl; +} ngtcp2_map_bucket; + +typedef struct ngtcp2_map { + ngtcp2_map_bucket *table; + const ngtcp2_mem *mem; + size_t size; + uint32_t tablelen; +} ngtcp2_map; + +/* + * Initializes the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_map_init(ngtcp2_map *map, const ngtcp2_mem *mem); + +/* + * Deallocates any resources allocated for |map|. The stored entries + * are not freed by this function. Use ngtcp2_map_each_free() to free + * each entries. + */ +void ngtcp2_map_free(ngtcp2_map *map); + +/* + * Deallocates each entries using |func| function and any resources + * allocated for |map|. The |func| function is responsible for freeing + * given the |entry| object. The |ptr| will be passed to the |func| as + * send argument. The return value of the |func| will be ignored. + */ +void ngtcp2_map_each_free(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), + void *ptr); + +/* + * Initializes the |entry| with the |key|. All entries to be inserted + * to the map must be initialized with this function. + */ +void ngtcp2_map_entry_init(ngtcp2_map_entry *entry, key_type key); + +/* + * Inserts the new |entry| with the key |entry->key| to the map |map|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * The item associated by |key| already exists. + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_map_insert(ngtcp2_map *map, ngtcp2_map_entry *entry); + +/* + * Returns the entry associated by the key |key|. If there is no such + * entry, this function returns NULL. + */ +ngtcp2_map_entry *ngtcp2_map_find(ngtcp2_map *map, key_type key); + +/* + * Removes the entry associated by the key |key| from the |map|. The + * removed entry is not freed by this function. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * The entry associated by |key| does not exist. + */ +int ngtcp2_map_remove(ngtcp2_map *map, key_type key); + +/* + * Removes all entries from |map|. + */ +void ngtcp2_map_clear(ngtcp2_map *map); + +/* + * Returns the number of items stored in the map |map|. + */ +size_t ngtcp2_map_size(ngtcp2_map *map); + +/* + * Applies the function |func| to each entry in the |map| with the + * optional user supplied pointer |ptr|. + * + * If the |func| returns 0, this function calls the |func| with the + * next entry. If the |func| returns nonzero, it will not call the + * |func| for further entries and return the return value of the + * |func| immediately. Thus, this function returns 0 if all the + * invocations of the |func| return 0, or nonzero value which the last + * invocation of |func| returns. + * + * Don't use this function to free each entry. Use + * ngtcp2_map_each_free() instead. + */ +int ngtcp2_map_each(ngtcp2_map *map, + int (*func)(ngtcp2_map_entry *entry, void *ptr), void *ptr); + +#endif /* NGTCP2_MAP_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c new file mode 100644 index 00000000000000..2c036ad1634b05 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.c @@ -0,0 +1,75 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 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_mem.h" + +static void *default_malloc(size_t size, void *mem_user_data) { + (void)mem_user_data; + + return malloc(size); +} + +static void default_free(void *ptr, void *mem_user_data) { + (void)mem_user_data; + + free(ptr); +} + +static void *default_calloc(size_t nmemb, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return calloc(nmemb, size); +} + +static void *default_realloc(void *ptr, size_t size, void *mem_user_data) { + (void)mem_user_data; + + return realloc(ptr, size); +} + +static const ngtcp2_mem mem_default = {NULL, default_malloc, default_free, + default_calloc, default_realloc}; + +const ngtcp2_mem *ngtcp2_mem_default(void) { return &mem_default; } + +void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size) { + return mem->malloc(size, mem->mem_user_data); +} + +void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr) { + mem->free(ptr, mem->mem_user_data); +} + +void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data) { + free_func(ptr, mem_user_data); +} + +void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size) { + return mem->calloc(nmemb, size, mem->mem_user_data); +} + +void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size) { + return mem->realloc(ptr, size, mem->mem_user_data); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h new file mode 100644 index 00000000000000..cdecf8763a5f36 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_mem.h @@ -0,0 +1,43 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2014 nghttp2 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_MEM_H +#define NGTCP2_MEM_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +/* Convenient wrapper functions to call allocator function in + |mem|. */ +void *ngtcp2_mem_malloc(const ngtcp2_mem *mem, size_t size); +void ngtcp2_mem_free(const ngtcp2_mem *mem, void *ptr); +void ngtcp2_mem_free2(ngtcp2_free free_func, void *ptr, void *mem_user_data); +void *ngtcp2_mem_calloc(const ngtcp2_mem *mem, size_t nmemb, size_t size); +void *ngtcp2_mem_realloc(const ngtcp2_mem *mem, void *ptr, size_t size); + +#endif /* NGTCP2_MEM_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c new file mode 100644 index 00000000000000..3f35f28ef6a394 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.c @@ -0,0 +1,74 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_path.h" + +#include <string.h> + +#include "ngtcp2_addr.h" + +void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, + const ngtcp2_addr *remote) { + path->local = *local; + path->remote = *remote; +} + +void ngtcp2_path_copy(ngtcp2_path *dest, const ngtcp2_path *src) { + ngtcp2_addr_copy(&dest->local, &src->local); + ngtcp2_addr_copy(&dest->remote, &src->remote); +} + +int ngtcp2_path_eq(const ngtcp2_path *a, const ngtcp2_path *b) { + return ngtcp2_addr_eq(&a->local, &b->local) && + ngtcp2_addr_eq(&a->remote, &b->remote); +} + +void ngtcp2_path_storage_init(ngtcp2_path_storage *ps, + const struct sockaddr *local_addr, + size_t local_addrlen, void *local_user_data, + const struct sockaddr *remote_addr, + size_t remote_addrlen, void *remote_user_data) { + ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, + 0, local_user_data); + ngtcp2_addr_init(&ps->path.remote, + (const struct sockaddr *)&ps->remote_addrbuf, 0, + remote_user_data); + + ngtcp2_addr_copy_byte(&ps->path.local, local_addr, local_addrlen); + ngtcp2_addr_copy_byte(&ps->path.remote, remote_addr, remote_addrlen); +} + +void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, + const ngtcp2_path *path) { + ngtcp2_path_storage_init(ps, path->local.addr, path->local.addrlen, + path->local.user_data, path->remote.addr, + path->remote.addrlen, path->remote.user_data); +} + +void ngtcp2_path_storage_zero(ngtcp2_path_storage *ps) { + ngtcp2_addr_init(&ps->path.local, (const struct sockaddr *)&ps->local_addrbuf, + 0, NULL); + ngtcp2_addr_init(&ps->path.remote, + (const struct sockaddr *)&ps->remote_addrbuf, 0, NULL); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h new file mode 100644 index 00000000000000..0c360e936231c8 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_path.h @@ -0,0 +1,49 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_PATH_H +#define NGTCP2_PATH_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_path_init initializes |path| with the given addresses. Note + * that the buffer pointed by local->addr and remote->addr are not + * copied. Their pointer values are assigned instead. + */ +void ngtcp2_path_init(ngtcp2_path *path, const ngtcp2_addr *local, + const ngtcp2_addr *remote); + +/* + * ngtcp2_path_storage_init2 initializes |ps| using |path| as initial + * data. + */ +void ngtcp2_path_storage_init2(ngtcp2_path_storage *ps, + const ngtcp2_path *path); + +#endif /* NGTCP2_PATH_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c new file mode 100644 index 00000000000000..ddfbd24bbc9c77 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.c @@ -0,0 +1,2432 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_pkt.h" + +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include "ngtcp2_conv.h" +#include "ngtcp2_str.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_vec.h" + +int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, size_t dgramlen, ngtcp2_tstamp ts, + const ngtcp2_mem *mem) { + *ppc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pkt_chain) + pktlen); + if (*ppc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_path_storage_init2(&(*ppc)->path, path); + (*ppc)->pi = *pi; + (*ppc)->next = NULL; + (*ppc)->pkt = (uint8_t *)(*ppc) + sizeof(ngtcp2_pkt_chain); + (*ppc)->pktlen = pktlen; + (*ppc)->dgramlen = dgramlen; + (*ppc)->ts = ts; + + memcpy((*ppc)->pkt, pkt, pktlen); + + return 0; +} + +void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, pc); +} + +int ngtcp2_pkt_decode_version_cid(uint32_t *pversion, const uint8_t **pdcid, + size_t *pdcidlen, const uint8_t **pscid, + size_t *pscidlen, const uint8_t *data, + size_t datalen, size_t short_dcidlen) { + size_t len; + uint32_t version; + size_t dcidlen, scidlen; + + assert(datalen); + + if (data[0] & NGTCP2_HEADER_FORM_BIT) { + /* 1 byte (Header Form, Fixed Bit, Long Packet Type, Type-Specific bits) + * 4 bytes Version + * 1 byte DCID Length + * 1 byte SCID Length + */ + len = 1 + 4 + 1 + 1; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dcidlen = data[5]; + len += dcidlen; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + scidlen = data[5 + 1 + dcidlen]; + len += scidlen; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + version = ngtcp2_get_uint32(&data[1]); + + if ((version == 0 || version == NGTCP2_PROTO_VER_V1 || + (NGTCP2_PROTO_VER_DRAFT_MIN <= version && + version <= NGTCP2_PROTO_VER_DRAFT_MAX)) && + (dcidlen > NGTCP2_MAX_CIDLEN || scidlen > NGTCP2_MAX_CIDLEN)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + *pversion = version; + *pdcid = &data[6]; + *pdcidlen = dcidlen; + *pscid = &data[6 + dcidlen + 1]; + *pscidlen = scidlen; + + if (version && version != NGTCP2_PROTO_VER_V1 && + (version < NGTCP2_PROTO_VER_DRAFT_MIN || + NGTCP2_PROTO_VER_DRAFT_MAX < version)) { + return 1; + } + return 0; + } + + assert(short_dcidlen <= NGTCP2_MAX_CIDLEN); + + len = 1 + short_dcidlen; + if (datalen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + *pversion = 0; + *pdcid = &data[1]; + *pdcidlen = short_dcidlen; + *pscid = NULL; + *pscidlen = 0; + + return 0; +} + +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) { + hd->flags = flags; + hd->type = type; + if (dcid) { + hd->dcid = *dcid; + } else { + ngtcp2_cid_zero(&hd->dcid); + } + if (scid) { + hd->scid = *scid; + } else { + ngtcp2_cid_zero(&hd->scid); + } + hd->pkt_num = pkt_num; + hd->token.base = NULL; + hd->token.len = 0; + hd->pkt_numlen = pkt_numlen; + hd->version = version; + hd->len = len; +} + +static int has_mask(uint8_t b, uint8_t mask) { return (b & mask) == mask; } + +ngtcp2_ssize ngtcp2_pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen) { + uint8_t type; + uint32_t version; + size_t dcil, scil; + const uint8_t *p; + size_t len = 0; + size_t n; + size_t ntokenlen = 0; + const uint8_t *token = NULL; + size_t tokenlen = 0; + uint64_t vi; + + if (pktlen < 5) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if (!(pkt[0] & NGTCP2_HEADER_FORM_BIT)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + version = ngtcp2_get_uint32(&pkt[1]); + + if (version == 0) { + type = NGTCP2_PKT_VERSION_NEGOTIATION; + /* This must be Version Negotiation packet which lacks packet + number and payload length fields. */ + len = 5 + 2; + } else { + if (!(pkt[0] & NGTCP2_FIXED_BIT_MASK)) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + type = ngtcp2_pkt_get_type_long(pkt[0]); + switch (type) { + case NGTCP2_PKT_INITIAL: + len = 1 /* Token Length */ + NGTCP2_MIN_LONG_HEADERLEN - + 1; /* Cut packet number field */ + break; + case NGTCP2_PKT_RETRY: + /* Retry packet does not have packet number and length fields */ + len = 5 + 2; + break; + case NGTCP2_PKT_HANDSHAKE: + case NGTCP2_PKT_0RTT: + len = NGTCP2_MIN_LONG_HEADERLEN - 1; /* Cut packet number field */ + break; + default: + /* Unreachable */ + assert(0); + } + } + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = &pkt[5]; + dcil = *p; + if (dcil > NGTCP2_MAX_CIDLEN) { + /* QUIC v1 implementation never expect to receive CID length more + than NGTCP2_MAX_CIDLEN. */ + return NGTCP2_ERR_INVALID_ARGUMENT; + } + len += dcil; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p += 1 + dcil; + scil = *p; + if (scil > NGTCP2_MAX_CIDLEN) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + len += scil; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p += 1 + scil; + + if (type == NGTCP2_PKT_INITIAL) { + /* Token Length */ + ntokenlen = ngtcp2_get_varint_len(p); + len += ntokenlen - 1; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + vi = ngtcp2_get_varint(&ntokenlen, p); + if (pktlen - len < vi) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + tokenlen = (size_t)vi; + len += tokenlen; + + p += ntokenlen; + + if (tokenlen) { + token = p; + } + + p += tokenlen; + } + + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + case NGTCP2_PKT_RETRY: + break; + default: + /* Length */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + } + + dest->flags = NGTCP2_PKT_FLAG_LONG_FORM; + dest->type = type; + dest->version = version; + dest->pkt_num = 0; + dest->pkt_numlen = 0; + + p = &pkt[6]; + ngtcp2_cid_init(&dest->dcid, p, dcil); + p += dcil + 1; + ngtcp2_cid_init(&dest->scid, p, scil); + p += scil; + + dest->token.base = (uint8_t *)token; + dest->token.len = tokenlen; + p += ntokenlen + tokenlen; + + switch (type) { + case NGTCP2_PKT_VERSION_NEGOTIATION: + case NGTCP2_PKT_RETRY: + dest->len = 0; + break; + default: + vi = ngtcp2_get_varint(&n, p); + if (vi > SIZE_MAX) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + dest->len = (size_t)vi; + p += n; + } + + assert((size_t)(p - pkt) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen) { + size_t len = 1 + dcidlen; + const uint8_t *p = pkt; + + assert(dcidlen <= NGTCP2_MAX_CIDLEN); + + if (pktlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + if ((pkt[0] & NGTCP2_HEADER_FORM_BIT) || + (pkt[0] & NGTCP2_FIXED_BIT_MASK) == 0) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = &pkt[1]; + + dest->type = NGTCP2_PKT_SHORT; + + ngtcp2_cid_init(&dest->dcid, p, dcidlen); + p += dcidlen; + + /* Set 0 to SCID so that we don't accidentally reference it and gets + garbage. */ + ngtcp2_cid_zero(&dest->scid); + + dest->flags = NGTCP2_PKT_FLAG_NONE; + dest->version = 0; + dest->len = 0; + dest->pkt_num = 0; + dest->pkt_numlen = 0; + + assert((size_t)(p - pkt) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd) { + uint8_t *p; + size_t len = NGTCP2_MIN_LONG_HEADERLEN + hd->dcid.datalen + hd->scid.datalen - + 2; /* NGTCP2_MIN_LONG_HEADERLEN includes 1 byte for + len and 1 byte for packet number. */ + + if (hd->type != NGTCP2_PKT_RETRY) { + len += 2 /* Length */ + hd->pkt_numlen; + } + + if (hd->type == NGTCP2_PKT_INITIAL) { + len += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; + } + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = (uint8_t)(NGTCP2_HEADER_FORM_BIT | NGTCP2_FIXED_BIT_MASK | + (hd->type << 4) | (uint8_t)(hd->pkt_numlen - 1)); + p = ngtcp2_put_uint32be(p, hd->version); + *p++ = (uint8_t)hd->dcid.datalen; + if (hd->dcid.datalen) { + p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); + } + *p++ = (uint8_t)hd->scid.datalen; + if (hd->scid.datalen) { + p = ngtcp2_cpymem(p, hd->scid.data, hd->scid.datalen); + } + + if (hd->type == NGTCP2_PKT_INITIAL) { + p = ngtcp2_put_varint(p, hd->token.len); + if (hd->token.len) { + p = ngtcp2_cpymem(p, hd->token.base, hd->token.len); + } + } + + if (hd->type != NGTCP2_PKT_RETRY) { + p = ngtcp2_put_varint14(p, (uint16_t)hd->len); + p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd) { + uint8_t *p; + size_t len = 1 + hd->dcid.datalen + hd->pkt_numlen; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p = NGTCP2_FIXED_BIT_MASK | (uint8_t)(hd->pkt_numlen - 1); + if (hd->flags & NGTCP2_PKT_FLAG_KEY_PHASE) { + *p |= NGTCP2_SHORT_KEY_PHASE_BIT; + } + + ++p; + + if (hd->dcid.datalen) { + p = ngtcp2_cpymem(p, hd->dcid.data, hd->dcid.datalen); + } + + p = ngtcp2_put_pkt_num(p, hd->pkt_num, hd->pkt_numlen); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, + size_t payloadlen) { + uint8_t type; + + if (payloadlen == 0) { + return 0; + } + + type = payload[0]; + + switch (type) { + case NGTCP2_FRAME_PADDING: + return (ngtcp2_ssize)ngtcp2_pkt_decode_padding_frame(&dest->padding, + payload, payloadlen); + case NGTCP2_FRAME_RESET_STREAM: + return ngtcp2_pkt_decode_reset_stream_frame(&dest->reset_stream, payload, + payloadlen); + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + return ngtcp2_pkt_decode_connection_close_frame(&dest->connection_close, + payload, payloadlen); + case NGTCP2_FRAME_MAX_DATA: + return ngtcp2_pkt_decode_max_data_frame(&dest->max_data, payload, + payloadlen); + case NGTCP2_FRAME_MAX_STREAM_DATA: + return ngtcp2_pkt_decode_max_stream_data_frame(&dest->max_stream_data, + payload, payloadlen); + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + return ngtcp2_pkt_decode_max_streams_frame(&dest->max_streams, payload, + payloadlen); + case NGTCP2_FRAME_PING: + return ngtcp2_pkt_decode_ping_frame(&dest->ping, payload, payloadlen); + case NGTCP2_FRAME_DATA_BLOCKED: + return ngtcp2_pkt_decode_data_blocked_frame(&dest->data_blocked, payload, + payloadlen); + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + return ngtcp2_pkt_decode_stream_data_blocked_frame( + &dest->stream_data_blocked, payload, payloadlen); + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + return ngtcp2_pkt_decode_streams_blocked_frame(&dest->streams_blocked, + payload, payloadlen); + case NGTCP2_FRAME_NEW_CONNECTION_ID: + return ngtcp2_pkt_decode_new_connection_id_frame(&dest->new_connection_id, + payload, payloadlen); + case NGTCP2_FRAME_STOP_SENDING: + return ngtcp2_pkt_decode_stop_sending_frame(&dest->stop_sending, payload, + payloadlen); + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + return ngtcp2_pkt_decode_ack_frame(&dest->ack, payload, payloadlen); + case NGTCP2_FRAME_PATH_CHALLENGE: + return ngtcp2_pkt_decode_path_challenge_frame(&dest->path_challenge, + payload, payloadlen); + case NGTCP2_FRAME_PATH_RESPONSE: + return ngtcp2_pkt_decode_path_response_frame(&dest->path_response, payload, + payloadlen); + case NGTCP2_FRAME_CRYPTO: + return ngtcp2_pkt_decode_crypto_frame(&dest->crypto, payload, payloadlen); + case NGTCP2_FRAME_NEW_TOKEN: + return ngtcp2_pkt_decode_new_token_frame(&dest->new_token, payload, + payloadlen); + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + return ngtcp2_pkt_decode_retire_connection_id_frame( + &dest->retire_connection_id, payload, payloadlen); + case NGTCP2_FRAME_HANDSHAKE_DONE: + return ngtcp2_pkt_decode_handshake_done_frame(&dest->handshake_done, + payload, payloadlen); + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + return ngtcp2_pkt_decode_datagram_frame(&dest->datagram, payload, + payloadlen); + default: + if (has_mask(type, NGTCP2_FRAME_STREAM)) { + return ngtcp2_pkt_decode_stream_frame(&dest->stream, payload, payloadlen); + } + return NGTCP2_ERR_FRAME_ENCODING; + } +} + +ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest, + const uint8_t *payload, + size_t payloadlen) { + uint8_t type; + size_t len = 1 + 1; + const uint8_t *p; + size_t datalen; + size_t ndatalen = 0; + size_t n; + uint64_t vi; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + if (type & NGTCP2_STREAM_OFF_BIT) { + ++len; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + + if (type & NGTCP2_STREAM_LEN_BIT) { + ++len; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + ndatalen = ngtcp2_get_varint_len(p); + len += ndatalen - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + vi = ngtcp2_get_varint(&ndatalen, p); + if (payloadlen - len < vi) { + return NGTCP2_ERR_FRAME_ENCODING; + } + datalen = (size_t)vi; + len += datalen; + } else { + len = payloadlen; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_STREAM; + dest->flags = (uint8_t)(type & ~NGTCP2_FRAME_STREAM); + dest->fin = (type & NGTCP2_STREAM_FIN_BIT) != 0; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + + if (type & NGTCP2_STREAM_OFF_BIT) { + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + } else { + dest->offset = 0; + } + + if (type & NGTCP2_STREAM_LEN_BIT) { + p += ndatalen; + } else { + datalen = payloadlen - (size_t)(p - payload); + } + + if (datalen) { + dest->data[0].len = datalen; + dest->data[0].base = (uint8_t *)p; + dest->datacnt = 1; + p += datalen; + } else { + dest->datacnt = 0; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t num_blks, max_num_blks; + size_t nnum_blks; + size_t len = 1 + 1 + 1 + 1 + 1; + const uint8_t *p; + size_t i, j; + ngtcp2_ack_blk *blk; + size_t n; + uint8_t type; + uint64_t vi; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + /* Largest Acknowledged */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + /* ACK Delay */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + /* ACK Block Count */ + nnum_blks = ngtcp2_get_varint_len(p); + len += nnum_blks - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + vi = ngtcp2_get_varint(&nnum_blks, p); + if (vi > SIZE_MAX / (1 + 1) || payloadlen - len < vi * (1 + 1)) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + num_blks = (size_t)vi; + len += num_blks * (1 + 1); + + p += nnum_blks; + + /* First ACK Block */ + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + for (i = 0; i < num_blks; ++i) { + /* Gap, and Additional ACK Block */ + for (j = 0; j < 2; ++j) { + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + } + + if (type == NGTCP2_FRAME_ACK_ECN) { + len += 3; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + for (i = 0; i < 3; ++i) { + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + } + + /* TODO We might not decode all blocks. It could be very large. */ + max_num_blks = ngtcp2_min(NGTCP2_MAX_ACK_BLKS, num_blks); + + p = payload + 1; + + dest->type = type; + dest->largest_ack = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->ack_delay = ngtcp2_get_varint(&n, p); + /* This value will be assigned in the upper layer. */ + dest->ack_delay_unscaled = 0; + p += n; + dest->num_blks = max_num_blks; + p += nnum_blks; + dest->first_ack_blklen = ngtcp2_get_varint(&n, p); + p += n; + + for (i = 0; i < max_num_blks; ++i) { + blk = &dest->blks[i]; + blk->gap = ngtcp2_get_varint(&n, p); + p += n; + blk->blklen = ngtcp2_get_varint(&n, p); + p += n; + } + for (i = max_num_blks; i < num_blks; ++i) { + p += ngtcp2_get_varint_len(p); + p += ngtcp2_get_varint_len(p); + } + + if (type == NGTCP2_FRAME_ACK_ECN) { + dest->ecn.ect0 = ngtcp2_get_varint(&n, p); + p += n; + + dest->ecn.ect1 = ngtcp2_get_varint(&n, p); + p += n; + + dest->ecn.ce = ngtcp2_get_varint(&n, p); + p += n; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest, + const uint8_t *payload, + size_t payloadlen) { + const uint8_t *p, *ep; + + assert(payloadlen > 0); + + p = payload + 1; + ep = payload + payloadlen; + + for (; p != ep && *p == NGTCP2_FRAME_PADDING; ++p) + ; + + dest->type = NGTCP2_FRAME_PADDING; + dest->len = (size_t)(p - payload); + + return dest->len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + p += n; + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + p += n; + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_RESET_STREAM; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->app_error_code = ngtcp2_get_varint(&n, p); + p += n; + dest->final_size = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( + ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t reasonlen; + size_t nreasonlen; + size_t n; + uint8_t type; + uint64_t vi; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { + ++len; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + } + + nreasonlen = ngtcp2_get_varint_len(p); + len += nreasonlen - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + vi = ngtcp2_get_varint(&nreasonlen, p); + if (payloadlen - len < vi) { + return NGTCP2_ERR_FRAME_ENCODING; + } + reasonlen = (size_t)vi; + len += reasonlen; + + p = payload + 1; + + dest->type = type; + dest->error_code = ngtcp2_get_varint(&n, p); + p += n; + if (type == NGTCP2_FRAME_CONNECTION_CLOSE) { + dest->frame_type = ngtcp2_get_varint(&n, p); + p += n; + } else { + dest->frame_type = 0; + } + dest->reasonlen = reasonlen; + p += nreasonlen; + if (reasonlen == 0) { + dest->reason = NULL; + } else { + dest->reason = (uint8_t *)p; + p += reasonlen; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_MAX_DATA; + dest->max_data = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame( + ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_MAX_STREAM_DATA; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->max_stream_data = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = payload[0]; + dest->max_streams = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest, + const uint8_t *payload, + size_t payloadlen) { + (void)payload; + (void)payloadlen; + + dest->type = NGTCP2_FRAME_PING; + return 1; +} + +ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_DATA_BLOCKED; + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame( + ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = payload[0]; + dest->max_streams = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame( + ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen) { + size_t len = 1 + 1 + 1 + 1 + 16; + const uint8_t *p; + size_t n; + size_t cil; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + cil = *p; + if (cil < NGTCP2_MIN_CIDLEN || cil > NGTCP2_MAX_CIDLEN) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + len += cil; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_NEW_CONNECTION_ID; + dest->seq = ngtcp2_get_varint(&n, p); + p += n; + dest->retire_prior_to = ngtcp2_get_varint(&n, p); + p += n + 1; + ngtcp2_cid_init(&dest->cid, p, cil); + p += cil; + memcpy(dest->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN); + p += NGTCP2_STATELESS_RESET_TOKENLEN; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + p += n; + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_STOP_SENDING; + dest->stream_id = (int64_t)ngtcp2_get_varint(&n, p); + p += n; + dest->app_error_code = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 8; + const uint8_t *p; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_PATH_CHALLENGE; + ngtcp2_cpymem(dest->data, p, sizeof(dest->data)); + p += sizeof(dest->data); + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 8; + const uint8_t *p; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + dest->type = NGTCP2_FRAME_PATH_RESPONSE; + ngtcp2_cpymem(dest->data, p, sizeof(dest->data)); + p += sizeof(dest->data); + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1 + 1; + const uint8_t *p; + size_t datalen; + size_t ndatalen; + size_t n; + uint64_t vi; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p += n; + + ndatalen = ngtcp2_get_varint_len(p); + len += ndatalen - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + vi = ngtcp2_get_varint(&ndatalen, p); + if (payloadlen - len < vi) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + datalen = (size_t)vi; + len += datalen; + + p = payload + 1; + + dest->type = NGTCP2_FRAME_CRYPTO; + dest->offset = ngtcp2_get_varint(&n, p); + p += n; + dest->data[0].len = datalen; + p += ndatalen; + if (dest->data[0].len) { + dest->data[0].base = (uint8_t *)p; + p += dest->data[0].len; + dest->datacnt = 1; + } else { + dest->data[0].base = NULL; + dest->datacnt = 0; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + size_t datalen; + uint64_t vi; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + vi = ngtcp2_get_varint(&n, p); + if (payloadlen - len < vi) { + return NGTCP2_ERR_FRAME_ENCODING; + } + datalen = (size_t)vi; + len += datalen; + + dest->type = NGTCP2_FRAME_NEW_TOKEN; + dest->token.len = datalen; + p += n; + dest->token.base = (uint8_t *)p; + p += dest->token.len; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1 + 1; + const uint8_t *p; + size_t n; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + p = payload + 1; + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + dest->type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + dest->seq = ngtcp2_get_varint(&n, p); + p += n; + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, + const uint8_t *payload, + size_t payloadlen) { + (void)payload; + (void)payloadlen; + + dest->type = NGTCP2_FRAME_HANDSHAKE_DONE; + return 1; +} + +ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, + const uint8_t *payload, + size_t payloadlen) { + size_t len = 1; + const uint8_t *p; + uint8_t type; + size_t datalen; + size_t n; + uint64_t vi; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + type = payload[0]; + + p = payload + 1; + + switch (type) { + case NGTCP2_FRAME_DATAGRAM: + datalen = payloadlen - 1; + len = payloadlen; + break; + case NGTCP2_FRAME_DATAGRAM_LEN: + ++len; + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + n = ngtcp2_get_varint_len(p); + len += n - 1; + + if (payloadlen < len) { + return NGTCP2_ERR_FRAME_ENCODING; + } + + vi = ngtcp2_get_varint(&n, p); + if (payloadlen - len < vi) { + return NGTCP2_FRAME_ENCODING_ERROR; + } + + datalen = (size_t)vi; + len += datalen; + break; + default: + assert(0); + } + + dest->type = type; + + if (datalen == 0) { + dest->datacnt = 0; + dest->data = NULL; + + if (type == NGTCP2_FRAME_DATAGRAM_LEN) { + p += n; + } + } else { + dest->datacnt = 1; + dest->data = dest->rdata; + dest->rdata[0].len = datalen; + + if (type == NGTCP2_FRAME_DATAGRAM_LEN) { + p += n; + } + + dest->rdata[0].base = (uint8_t *)p; + p += datalen; + } + + assert((size_t)(p - payload) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, + ngtcp2_frame *fr) { + switch (fr->type) { + case NGTCP2_FRAME_STREAM: + return ngtcp2_pkt_encode_stream_frame(out, outlen, &fr->stream); + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + return ngtcp2_pkt_encode_ack_frame(out, outlen, &fr->ack); + case NGTCP2_FRAME_PADDING: + return ngtcp2_pkt_encode_padding_frame(out, outlen, &fr->padding); + case NGTCP2_FRAME_RESET_STREAM: + return ngtcp2_pkt_encode_reset_stream_frame(out, outlen, &fr->reset_stream); + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + return ngtcp2_pkt_encode_connection_close_frame(out, outlen, + &fr->connection_close); + case NGTCP2_FRAME_MAX_DATA: + return ngtcp2_pkt_encode_max_data_frame(out, outlen, &fr->max_data); + case NGTCP2_FRAME_MAX_STREAM_DATA: + return ngtcp2_pkt_encode_max_stream_data_frame(out, outlen, + &fr->max_stream_data); + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + return ngtcp2_pkt_encode_max_streams_frame(out, outlen, &fr->max_streams); + case NGTCP2_FRAME_PING: + return ngtcp2_pkt_encode_ping_frame(out, outlen, &fr->ping); + case NGTCP2_FRAME_DATA_BLOCKED: + return ngtcp2_pkt_encode_data_blocked_frame(out, outlen, &fr->data_blocked); + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + return ngtcp2_pkt_encode_stream_data_blocked_frame( + out, outlen, &fr->stream_data_blocked); + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + return ngtcp2_pkt_encode_streams_blocked_frame(out, outlen, + &fr->streams_blocked); + case NGTCP2_FRAME_NEW_CONNECTION_ID: + return ngtcp2_pkt_encode_new_connection_id_frame(out, outlen, + &fr->new_connection_id); + case NGTCP2_FRAME_STOP_SENDING: + return ngtcp2_pkt_encode_stop_sending_frame(out, outlen, &fr->stop_sending); + case NGTCP2_FRAME_PATH_CHALLENGE: + return ngtcp2_pkt_encode_path_challenge_frame(out, outlen, + &fr->path_challenge); + case NGTCP2_FRAME_PATH_RESPONSE: + return ngtcp2_pkt_encode_path_response_frame(out, outlen, + &fr->path_response); + case NGTCP2_FRAME_CRYPTO: + return ngtcp2_pkt_encode_crypto_frame(out, outlen, &fr->crypto); + case NGTCP2_FRAME_NEW_TOKEN: + return ngtcp2_pkt_encode_new_token_frame(out, outlen, &fr->new_token); + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + return ngtcp2_pkt_encode_retire_connection_id_frame( + out, outlen, &fr->retire_connection_id); + case NGTCP2_FRAME_HANDSHAKE_DONE: + return ngtcp2_pkt_encode_handshake_done_frame(out, outlen, + &fr->handshake_done); + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + return ngtcp2_pkt_encode_datagram_frame(out, outlen, &fr->datagram); + default: + return NGTCP2_ERR_INVALID_ARGUMENT; + } +} + +ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, + ngtcp2_stream *fr) { + size_t len = 1; + uint8_t flags = NGTCP2_STREAM_LEN_BIT; + uint8_t *p; + size_t i; + size_t datalen = 0; + + if (fr->fin) { + flags |= NGTCP2_STREAM_FIN_BIT; + } + + if (fr->offset) { + flags |= NGTCP2_STREAM_OFF_BIT; + len += ngtcp2_put_varint_len(fr->offset); + } + + len += ngtcp2_put_varint_len((uint64_t)fr->stream_id); + + for (i = 0; i < fr->datacnt; ++i) { + datalen += fr->data[i].len; + } + + len += ngtcp2_put_varint_len(datalen); + len += datalen; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = flags | NGTCP2_FRAME_STREAM; + + fr->flags = flags; + + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + + if (fr->offset) { + p = ngtcp2_put_varint(p, fr->offset); + } + + p = ngtcp2_put_varint(p, datalen); + + for (i = 0; i < fr->datacnt; ++i) { + assert(fr->data[i].len); + assert(fr->data[i].base); + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen, + ngtcp2_ack *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->largest_ack) + + ngtcp2_put_varint_len(fr->ack_delay) + + ngtcp2_put_varint_len(fr->num_blks) + + ngtcp2_put_varint_len(fr->first_ack_blklen); + uint8_t *p; + size_t i; + const ngtcp2_ack_blk *blk; + + for (i = 0; i < fr->num_blks; ++i) { + blk = &fr->blks[i]; + len += ngtcp2_put_varint_len(blk->gap); + len += ngtcp2_put_varint_len(blk->blklen); + } + + if (fr->type == NGTCP2_FRAME_ACK_ECN) { + len += ngtcp2_put_varint_len(fr->ecn.ect0) + + ngtcp2_put_varint_len(fr->ecn.ect1) + + ngtcp2_put_varint_len(fr->ecn.ce); + } + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, (uint64_t)fr->largest_ack); + p = ngtcp2_put_varint(p, fr->ack_delay); + p = ngtcp2_put_varint(p, fr->num_blks); + p = ngtcp2_put_varint(p, fr->first_ack_blklen); + + for (i = 0; i < fr->num_blks; ++i) { + blk = &fr->blks[i]; + p = ngtcp2_put_varint(p, blk->gap); + p = ngtcp2_put_varint(p, blk->blklen); + } + + if (fr->type == NGTCP2_FRAME_ACK_ECN) { + p = ngtcp2_put_varint(p, fr->ecn.ect0); + p = ngtcp2_put_varint(p, fr->ecn.ect1); + p = ngtcp2_put_varint(p, fr->ecn.ce); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen, + const ngtcp2_padding *fr) { + if (outlen < fr->len) { + return NGTCP2_ERR_NOBUF; + } + + memset(out, 0, fr->len); + + return (ngtcp2_ssize)fr->len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen, + const ngtcp2_reset_stream *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->app_error_code) + + ngtcp2_put_varint_len(fr->final_size); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_RESET_STREAM; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->app_error_code); + p = ngtcp2_put_varint(p, fr->final_size); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen, + const ngtcp2_connection_close *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->error_code) + + (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE + ? ngtcp2_put_varint_len(fr->frame_type) + : 0) + + ngtcp2_put_varint_len(fr->reasonlen) + fr->reasonlen; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, fr->error_code); + if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { + p = ngtcp2_put_varint(p, fr->frame_type); + } + p = ngtcp2_put_varint(p, fr->reasonlen); + if (fr->reasonlen) { + p = ngtcp2_cpymem(p, fr->reason, fr->reasonlen); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_data *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->max_data); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_MAX_DATA; + p = ngtcp2_put_varint(p, fr->max_data); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_stream_data *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->max_stream_data); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_MAX_STREAM_DATA; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->max_stream_data); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_streams *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->max_streams); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, fr->max_streams); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen, + const ngtcp2_ping *fr) { + (void)fr; + + if (outlen < 1) { + return NGTCP2_ERR_NOBUF; + } + + *out++ = NGTCP2_FRAME_PING; + + return 1; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_data_blocked *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->offset); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_DATA_BLOCKED; + p = ngtcp2_put_varint(p, fr->offset); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame( + uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->offset); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_STREAM_DATA_BLOCKED; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->offset); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_streams_blocked *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->max_streams); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + p = ngtcp2_put_varint(p, fr->max_streams); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_connection_id *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->seq) + + ngtcp2_put_varint_len(fr->retire_prior_to) + 1 + + fr->cid.datalen + NGTCP2_STATELESS_RESET_TOKENLEN; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_NEW_CONNECTION_ID; + p = ngtcp2_put_varint(p, fr->seq); + p = ngtcp2_put_varint(p, fr->retire_prior_to); + *p++ = (uint8_t)fr->cid.datalen; + p = ngtcp2_cpymem(p, fr->cid.data, fr->cid.datalen); + p = ngtcp2_cpymem(p, fr->stateless_reset_token, + NGTCP2_STATELESS_RESET_TOKENLEN); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen, + const ngtcp2_stop_sending *fr) { + size_t len = 1 + ngtcp2_put_varint_len((uint64_t)fr->stream_id) + + ngtcp2_put_varint_len(fr->app_error_code); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_STOP_SENDING; + p = ngtcp2_put_varint(p, (uint64_t)fr->stream_id); + p = ngtcp2_put_varint(p, fr->app_error_code); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_challenge *fr) { + size_t len = 1 + 8; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_PATH_CHALLENGE; + p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data)); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_response *fr) { + size_t len = 1 + 8; + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_PATH_RESPONSE; + p = ngtcp2_cpymem(p, fr->data, sizeof(fr->data)); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen, + const ngtcp2_crypto *fr) { + size_t len = 1; + uint8_t *p; + size_t i; + size_t datalen = 0; + + len += ngtcp2_put_varint_len(fr->offset); + + for (i = 0; i < fr->datacnt; ++i) { + datalen += fr->data[i].len; + } + + len += ngtcp2_put_varint_len(datalen); + len += datalen; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_CRYPTO; + + p = ngtcp2_put_varint(p, fr->offset); + p = ngtcp2_put_varint(p, datalen); + + for (i = 0; i < fr->datacnt; ++i) { + assert(fr->data[i].base); + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_token *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->token.len) + fr->token.len; + uint8_t *p; + + assert(fr->token.len); + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_NEW_TOKEN; + + p = ngtcp2_put_varint(p, fr->token.len); + p = ngtcp2_cpymem(p, fr->token.base, fr->token.len); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( + uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr) { + size_t len = 1 + ngtcp2_put_varint_len(fr->seq); + uint8_t *p; + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + + p = ngtcp2_put_varint(p, fr->seq); + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize +ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, + const ngtcp2_handshake_done *fr) { + (void)fr; + + if (outlen < 1) { + return NGTCP2_ERR_NOBUF; + } + + *out++ = NGTCP2_FRAME_HANDSHAKE_DONE; + + return 1; +} + +ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, + const ngtcp2_datagram *fr) { + size_t datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + size_t len = + 1 + + (fr->type == NGTCP2_FRAME_DATAGRAM ? 0 : ngtcp2_put_varint_len(datalen)) + + datalen; + uint8_t *p; + size_t i; + + assert(fr->type == NGTCP2_FRAME_DATAGRAM || + fr->type == NGTCP2_FRAME_DATAGRAM_LEN); + + if (outlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = out; + + *p++ = fr->type; + if (fr->type == NGTCP2_FRAME_DATAGRAM_LEN) { + p = ngtcp2_put_varint(p, (uint64_t)datalen); + } + + for (i = 0; i < fr->datacnt; ++i) { + assert(fr->data[i].len); + assert(fr->data[i].base); + p = ngtcp2_cpymem(p, fr->data[i].base, fr->data[i].len); + } + + assert((size_t)(p - out) == len); + + return (ngtcp2_ssize)len; +} + +ngtcp2_ssize ngtcp2_pkt_write_version_negotiation( + uint8_t *dest, size_t destlen, uint8_t unused_random, const uint8_t *dcid, + size_t dcidlen, const uint8_t *scid, size_t scidlen, const uint32_t *sv, + size_t nsv) { + size_t len = 1 + 4 + 1 + dcidlen + 1 + scidlen + nsv * 4; + uint8_t *p; + size_t i; + + assert(dcidlen < 256); + assert(scidlen < 256); + + if (destlen < len) { + return NGTCP2_ERR_NOBUF; + } + + p = dest; + + *p++ = 0x80 | unused_random; + p = ngtcp2_put_uint32be(p, 0); + *p++ = (uint8_t)dcidlen; + if (dcidlen) { + p = ngtcp2_cpymem(p, dcid, dcidlen); + } + *p++ = (uint8_t)scidlen; + if (scidlen) { + p = ngtcp2_cpymem(p, scid, scidlen); + } + + for (i = 0; i < nsv; ++i) { + p = ngtcp2_put_uint32be(p, sv[i]); + } + + assert((size_t)(p - dest) == len); + + return (ngtcp2_ssize)len; +} + +size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest, + const uint8_t *payload, + size_t payloadlen) { + const uint8_t *end = payload + payloadlen; + + assert((payloadlen % sizeof(uint32_t)) == 0); + + for (; payload != end; payload += sizeof(uint32_t)) { + *dest++ = ngtcp2_get_uint32(payload); + } + + return payloadlen / sizeof(uint32_t); +} + +int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, + const uint8_t *payload, + size_t payloadlen) { + const uint8_t *p = payload; + + if (payloadlen < + NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + sr->rand = p; + sr->randlen = payloadlen - NGTCP2_STATELESS_RESET_TOKENLEN; + p += sr->randlen; + memcpy(sr->stateless_reset_token, p, NGTCP2_STATELESS_RESET_TOKENLEN); + + return 0; +} + +int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, + size_t payloadlen) { + size_t len = /* token */ 1 + NGTCP2_RETRY_TAGLEN; + + if (payloadlen < len) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dest->token.base = (uint8_t *)payload; + dest->token.len = (size_t)(payloadlen - NGTCP2_RETRY_TAGLEN); + ngtcp2_cpymem(dest->tag, payload + dest->token.len, NGTCP2_RETRY_TAGLEN); + + return 0; +} + +int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, + size_t n) { + int64_t expected = max_pkt_num + 1; + int64_t win = (int64_t)1 << n; + int64_t hwin = win / 2; + int64_t mask = win - 1; + int64_t cand = (expected & ~mask) | pkt_num; + + if (cand <= expected - hwin) { + assert(cand <= (int64_t)NGTCP2_MAX_VARINT - win); + return cand + win; + } + if (cand > expected + hwin && cand >= win) { + return cand - win; + } + return cand; +} + +int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr) { + int64_t largest_ack = fr->largest_ack; + size_t i; + + if (largest_ack < (int64_t)fr->first_ack_blklen) { + return NGTCP2_ERR_ACK_FRAME; + } + + largest_ack -= (int64_t)fr->first_ack_blklen; + + for (i = 0; i < fr->num_blks; ++i) { + if (largest_ack < (int64_t)fr->blks[i].gap + 2) { + return NGTCP2_ERR_ACK_FRAME; + } + + largest_ack -= (int64_t)fr->blks[i].gap + 2; + + if (largest_ack < (int64_t)fr->blks[i].blklen) { + return NGTCP2_ERR_ACK_FRAME; + } + + largest_ack -= (int64_t)fr->blks[i].blklen; + } + + return 0; +} + +ngtcp2_ssize +ngtcp2_pkt_write_stateless_reset(uint8_t *dest, size_t destlen, + const uint8_t *stateless_reset_token, + const uint8_t *rand, size_t randlen) { + uint8_t *p; + + if (destlen < + NGTCP2_MIN_STATELESS_RESET_RANDLEN + NGTCP2_STATELESS_RESET_TOKENLEN) { + return NGTCP2_ERR_NOBUF; + } + + if (randlen < NGTCP2_MIN_STATELESS_RESET_RANDLEN) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = dest; + + randlen = ngtcp2_min(destlen - NGTCP2_STATELESS_RESET_TOKENLEN, randlen); + + p = ngtcp2_cpymem(p, rand, randlen); + p = ngtcp2_cpymem(p, stateless_reset_token, NGTCP2_STATELESS_RESET_TOKENLEN); + *dest = (uint8_t)((*dest & 0x7fu) | 0x40u); + + return p - dest; +} + +ngtcp2_ssize ngtcp2_pkt_write_retry( + uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token, + size_t tokenlen, ngtcp2_encrypt encrypt, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx) { + ngtcp2_pkt_hd hd; + uint8_t pseudo_retry[1500]; + ngtcp2_ssize pseudo_retrylen; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; + int rv; + uint8_t *p; + size_t offset; + const uint8_t *nonce; + size_t noncelen; + + assert(tokenlen > 0); + assert(!ngtcp2_cid_eq(scid, odcid)); + + /* Retry packet is sent at most once per one connection attempt. In + the first connection attempt, client has to send random DCID + which is at least 8 bytes long. */ + if (odcid->datalen < 8) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_RETRY, dcid, + scid, /* pkt_num = */ 0, /* pkt_numlen = */ 1, version, + /* len = */ 0); + + pseudo_retrylen = + ngtcp2_pkt_encode_pseudo_retry(pseudo_retry, sizeof(pseudo_retry), &hd, + /* unused = */ 0, odcid, token, tokenlen); + if (pseudo_retrylen < 0) { + return pseudo_retrylen; + } + + if (version == NGTCP2_PROTO_VER_V1) { + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; + } else { + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; + } + + /* OpenSSL does not like NULL plaintext. */ + rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen, + pseudo_retry, (size_t)pseudo_retrylen); + if (rv != 0) { + return rv; + } + + offset = 1 + odcid->datalen; + if (destlen < (size_t)pseudo_retrylen + sizeof(tag) - offset) { + return NGTCP2_ERR_NOBUF; + } + + p = ngtcp2_cpymem(dest, pseudo_retry + offset, + (size_t)pseudo_retrylen - offset); + p = ngtcp2_cpymem(p, tag, sizeof(tag)); + + return p - dest; +} + +ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( + uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, + const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen) { + uint8_t *p = dest; + ngtcp2_ssize nwrite; + + if (destlen < 1 + odcid->datalen) { + return NGTCP2_ERR_NOBUF; + } + + *p++ = (uint8_t)odcid->datalen; + p = ngtcp2_cpymem(p, odcid->data, odcid->datalen); + destlen -= (size_t)(p - dest); + + nwrite = ngtcp2_pkt_encode_hd_long(p, destlen, hd); + if (nwrite < 0) { + return nwrite; + } + + if (destlen < (size_t)nwrite + tokenlen) { + return NGTCP2_ERR_NOBUF; + } + + *p &= 0xf0; + *p |= unused; + + p += nwrite; + + p = ngtcp2_cpymem(p, token, tokenlen); + + return p - dest; +} + +int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, + const uint8_t *pkt, size_t pktlen, + ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx) { + uint8_t pseudo_retry[1500]; + size_t pseudo_retrylen; + uint8_t *p = pseudo_retry; + int rv; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; + const uint8_t *nonce; + size_t noncelen; + + assert(pktlen >= sizeof(retry->tag)); + + if (sizeof(pseudo_retry) < + 1 + retry->odcid.datalen + pktlen - sizeof(retry->tag)) { + return NGTCP2_ERR_PROTO; + } + + *p++ = (uint8_t)retry->odcid.datalen; + p = ngtcp2_cpymem(p, retry->odcid.data, retry->odcid.datalen); + p = ngtcp2_cpymem(p, pkt, pktlen - sizeof(retry->tag)); + + pseudo_retrylen = (size_t)(p - pseudo_retry); + + if (version == NGTCP2_PROTO_VER_V1) { + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_V1; + noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1; + } else { + nonce = (const uint8_t *)NGTCP2_RETRY_NONCE_DRAFT; + noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1; + } + + /* OpenSSL does not like NULL plaintext. */ + rv = encrypt(tag, aead, aead_ctx, (const uint8_t *)"", 0, nonce, noncelen, + pseudo_retry, pseudo_retrylen); + if (rv != 0) { + return rv; + } + + if (0 != memcmp(retry->tag, tag, sizeof(retry->tag))) { + return NGTCP2_ERR_PROTO; + } + + return 0; +} + +size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, + size_t len, size_t left) { + size_t n = 1 /* type */ + ngtcp2_put_varint_len((uint64_t)stream_id) + + (offset ? ngtcp2_put_varint_len(offset) : 0); + + if (left <= n) { + return (size_t)-1; + } + + left -= n; + + if (left > 8 + 1073741823 && len > 1073741823) { +#if SIZE_MAX > UINT32_MAX + len = ngtcp2_min(len, 4611686018427387903lu); +#endif /* SIZE_MAX > UINT32_MAX */ + return ngtcp2_min(len, left - 8); + } + + if (left > 4 + 16383 && len > 16383) { + len = ngtcp2_min(len, 1073741823); + return ngtcp2_min(len, left - 4); + } + + if (left > 2 + 63 && len > 63) { + len = ngtcp2_min(len, 16383); + return ngtcp2_min(len, left - 2); + } + + len = ngtcp2_min(len, 63); + return ngtcp2_min(len, left - 1); +} + +size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left) { + size_t n = 1 /* type */ + ngtcp2_put_varint_len(offset); + + /* CRYPTO frame must contain nonzero length data. Return -1 if + there is no space to write crypto data. */ + if (left <= n + 1) { + return (size_t)-1; + } + + left -= n; + + if (left > 8 + 1073741823 && len > 1073741823) { +#if SIZE_MAX > UINT32_MAX + len = ngtcp2_min(len, 4611686018427387903lu); +#endif /* SIZE_MAX > UINT32_MAX */ + return ngtcp2_min(len, left - 8); + } + + if (left > 4 + 16383 && len > 16383) { + len = ngtcp2_min(len, 1073741823); + return ngtcp2_min(len, left - 4); + } + + if (left > 2 + 63 && len > 63) { + len = ngtcp2_min(len, 16383); + return ngtcp2_min(len, left - 2); + } + + len = ngtcp2_min(len, 63); + return ngtcp2_min(len, left - 1); +} + +size_t ngtcp2_pkt_datagram_framelen(size_t len) { + return 1 /* type */ + ngtcp2_put_varint_len((uint64_t)len) + len; +} + +uint8_t ngtcp2_pkt_get_type_long(uint8_t c) { + return (uint8_t)((c & NGTCP2_LONG_TYPE_MASK) >> 4); +} + +int ngtcp2_pkt_verify_reserved_bits(uint8_t c) { + if (c & NGTCP2_HEADER_FORM_BIT) { + return (c & NGTCP2_LONG_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; + } + return (c & NGTCP2_SHORT_RESERVED_BIT_MASK) == 0 ? 0 : NGTCP2_ERR_PROTO; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h new file mode 100644 index 00000000000000..708e0f6df3f87d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pkt.h @@ -0,0 +1,1195 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_PKT_H +#define NGTCP2_PKT_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +/* QUIC header macros */ +#define NGTCP2_HEADER_FORM_BIT 0x80 +#define NGTCP2_FIXED_BIT_MASK 0x40 +#define NGTCP2_PKT_NUMLEN_MASK 0x03 + +/* Long header specific macros */ +#define NGTCP2_LONG_TYPE_MASK 0x30 +#define NGTCP2_LONG_RESERVED_BIT_MASK 0x0c + +/* Short header specific macros */ +#define NGTCP2_SHORT_SPIN_BIT_MASK 0x20 +#define NGTCP2_SHORT_RESERVED_BIT_MASK 0x18 +#define NGTCP2_SHORT_KEY_PHASE_BIT 0x04 + +/* NGTCP2_SR_TYPE is a Type field of Stateless Reset. */ +#define NGTCP2_SR_TYPE 0x1f + +/* NGTCP2_MIN_LONG_HEADERLEN is the minimum length of long header. + That is (1|1|TT|RR|PP)<1> + VERSION<4> + DCIL<1> + SCIL<1> + + LENGTH<1> + PKN<1> */ +#define NGTCP2_MIN_LONG_HEADERLEN (1 + 4 + 1 + 1 + 1 + 1) + +#define NGTCP2_STREAM_FIN_BIT 0x01 +#define NGTCP2_STREAM_LEN_BIT 0x02 +#define NGTCP2_STREAM_OFF_BIT 0x04 + +/* 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. */ +#define NGTCP2_STREAM_OVERHEAD (1 + 8 + 8 + 8) + +/* NGTCP2_CRYPTO_OVERHEAD is the maximum number of bytes required + other than payload for CRYPTO frame. That is from type field to + the beginning of the payload. */ +#define NGTCP2_CRYPTO_OVERHEAD (1 + 8 + 8) + +/* NGTCP2_DATAGRAM_OVERHEAD is the maximum number of bytes required + other than payload for DATAGRAM frame. That is from type field to + the beginning of the payload. */ +#define NGTCP2_DATAGRAM_OVERHEAD (1 + 8) + +/* NGTCP2_MIN_FRAME_PAYLOADLEN is the minimum frame payload length. */ +#define NGTCP2_MIN_FRAME_PAYLOADLEN 16 + +/* NGTCP2_MAX_SERVER_STREAM_ID_BIDI is the maximum bidirectional + server stream ID. */ +#define NGTCP2_MAX_SERVER_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffdll) +/* NGTCP2_MAX_CLIENT_STREAM_ID_BIDI is the maximum bidirectional + client stream ID. */ +#define NGTCP2_MAX_CLIENT_STREAM_ID_BIDI ((int64_t)0x3ffffffffffffffcll) +/* NGTCP2_MAX_SERVER_STREAM_ID_UNI is the maximum unidirectional + server stream ID. */ +#define NGTCP2_MAX_SERVER_STREAM_ID_UNI ((int64_t)0x3fffffffffffffffll) +/* NGTCP2_MAX_CLIENT_STREAM_ID_UNI is the maximum unidirectional + client stream ID. */ +#define NGTCP2_MAX_CLIENT_STREAM_ID_UNI ((int64_t)0x3ffffffffffffffell) + +/* NGTCP2_MAX_NUM_ACK_BLK is the maximum number of Additional ACK + blocks which this library can create, or decode. */ +#define NGTCP2_MAX_ACK_BLKS 32 + +/* NGTCP2_MAX_PKT_NUM is the maximum packet number. */ +#define NGTCP2_MAX_PKT_NUM ((int64_t)((1ll << 62) - 1)) + +/* NGTCP2_MIN_PKT_EXPANDLEN is the minimum packet size expansion in + addition to the minimum DCID length to hide/trigger Stateless + Reset. */ +#define NGTCP2_MIN_PKT_EXPANDLEN 22 + +/* NGTCP2_RETRY_TAGLEN is the length of Retry packet integrity tag. */ +#define NGTCP2_RETRY_TAGLEN 16 + +typedef struct ngtcp2_pkt_retry { + ngtcp2_cid odcid; + ngtcp2_vec token; + uint8_t tag[NGTCP2_RETRY_TAGLEN]; +} ngtcp2_pkt_retry; + +typedef enum { + NGTCP2_FRAME_PADDING = 0x00, + NGTCP2_FRAME_PING = 0x01, + NGTCP2_FRAME_ACK = 0x02, + NGTCP2_FRAME_ACK_ECN = 0x03, + NGTCP2_FRAME_RESET_STREAM = 0x04, + NGTCP2_FRAME_STOP_SENDING = 0x05, + NGTCP2_FRAME_CRYPTO = 0x06, + NGTCP2_FRAME_NEW_TOKEN = 0x07, + NGTCP2_FRAME_STREAM = 0x08, + NGTCP2_FRAME_MAX_DATA = 0x10, + NGTCP2_FRAME_MAX_STREAM_DATA = 0x11, + NGTCP2_FRAME_MAX_STREAMS_BIDI = 0x12, + NGTCP2_FRAME_MAX_STREAMS_UNI = 0x13, + NGTCP2_FRAME_DATA_BLOCKED = 0x14, + NGTCP2_FRAME_STREAM_DATA_BLOCKED = 0x15, + NGTCP2_FRAME_STREAMS_BLOCKED_BIDI = 0x16, + NGTCP2_FRAME_STREAMS_BLOCKED_UNI = 0x17, + NGTCP2_FRAME_NEW_CONNECTION_ID = 0x18, + NGTCP2_FRAME_RETIRE_CONNECTION_ID = 0x19, + NGTCP2_FRAME_PATH_CHALLENGE = 0x1a, + NGTCP2_FRAME_PATH_RESPONSE = 0x1b, + NGTCP2_FRAME_CONNECTION_CLOSE = 0x1c, + NGTCP2_FRAME_CONNECTION_CLOSE_APP = 0x1d, + NGTCP2_FRAME_HANDSHAKE_DONE = 0x1e, + NGTCP2_FRAME_DATAGRAM = 0x30, + NGTCP2_FRAME_DATAGRAM_LEN = 0x31, +} ngtcp2_frame_type; + +typedef struct ngtcp2_stream { + uint8_t type; + /** + * flags of decoded STREAM frame. This gets ignored when encoding + * STREAM frame. + */ + uint8_t flags; + uint8_t fin; + int64_t stream_id; + uint64_t offset; + /* datacnt is the number of elements that data contains. Although + the length of data is 1 in this definition, the library may + allocate extra bytes to hold more elements. */ + size_t datacnt; + /* data is the array of ngtcp2_vec which references data. */ + ngtcp2_vec data[1]; +} ngtcp2_stream; + +typedef struct ngtcp2_ack_blk { + uint64_t gap; + uint64_t blklen; +} ngtcp2_ack_blk; + +typedef struct ngtcp2_ack { + uint8_t type; + int64_t largest_ack; + uint64_t ack_delay; + /** + * ack_delay_unscaled is an ack_delay multiplied by + * 2**ack_delay_component * NGTCP2_MICROSECONDS. + */ + ngtcp2_duration ack_delay_unscaled; + struct { + uint64_t ect0; + uint64_t ect1; + uint64_t ce; + } ecn; + uint64_t first_ack_blklen; + size_t num_blks; + ngtcp2_ack_blk blks[1]; +} ngtcp2_ack; + +typedef struct ngtcp2_padding { + uint8_t type; + /** + * The length of contiguous PADDING frames. + */ + size_t len; +} ngtcp2_padding; + +typedef struct ngtcp2_reset_stream { + uint8_t type; + int64_t stream_id; + uint64_t app_error_code; + uint64_t final_size; +} ngtcp2_reset_stream; + +typedef struct ngtcp2_connection_close { + uint8_t type; + uint64_t error_code; + uint64_t frame_type; + size_t reasonlen; + uint8_t *reason; +} ngtcp2_connection_close; + +typedef struct ngtcp2_max_data { + uint8_t type; + /** + * max_data is Maximum Data. + */ + uint64_t max_data; +} ngtcp2_max_data; + +typedef struct ngtcp2_max_stream_data { + uint8_t type; + int64_t stream_id; + uint64_t max_stream_data; +} ngtcp2_max_stream_data; + +typedef struct ngtcp2_max_streams { + uint8_t type; + uint64_t max_streams; +} ngtcp2_max_streams; + +typedef struct ngtcp2_ping { + uint8_t type; +} ngtcp2_ping; + +typedef struct ngtcp2_data_blocked { + uint8_t type; + uint64_t offset; +} ngtcp2_data_blocked; + +typedef struct ngtcp2_stream_data_blocked { + uint8_t type; + int64_t stream_id; + uint64_t offset; +} ngtcp2_stream_data_blocked; + +typedef struct ngtcp2_streams_blocked { + uint8_t type; + uint64_t max_streams; +} ngtcp2_streams_blocked; + +typedef struct ngtcp2_new_connection_id { + uint8_t type; + uint64_t seq; + uint64_t retire_prior_to; + ngtcp2_cid cid; + uint8_t stateless_reset_token[NGTCP2_STATELESS_RESET_TOKENLEN]; +} ngtcp2_new_connection_id; + +typedef struct ngtcp2_stop_sending { + uint8_t type; + int64_t stream_id; + uint64_t app_error_code; +} ngtcp2_stop_sending; + +typedef struct ngtcp2_path_challenge { + uint8_t type; + uint8_t data[8]; +} ngtcp2_path_challenge; + +typedef struct ngtcp2_path_response { + uint8_t type; + uint8_t data[8]; +} ngtcp2_path_response; + +typedef struct ngtcp2_crypto { + uint8_t type; + uint64_t offset; + /* datacnt is the number of elements that data contains. Although + the length of data is 1 in this definition, the library may + allocate extra bytes to hold more elements. */ + size_t datacnt; + /* data is the array of ngtcp2_vec which references data. */ + ngtcp2_vec data[1]; +} ngtcp2_crypto; + +typedef struct ngtcp2_new_token { + uint8_t type; + ngtcp2_vec token; +} ngtcp2_new_token; + +typedef struct ngtcp2_retire_connection_id { + uint8_t type; + uint64_t seq; +} ngtcp2_retire_connection_id; + +typedef struct ngtcp2_handshake_done { + uint8_t type; +} ngtcp2_handshake_done; + +typedef struct ngtcp2_datagram { + uint8_t type; + /* datacnt is the number of elements that data contains. */ + size_t datacnt; + /* data is a pointer to ngtcp2_vec array that stores data. */ + ngtcp2_vec *data; + /* rdata is conveniently embedded to ngtcp2_datagram, so that data + field can just point to the address of this field to store a + single vector which is the case when DATAGRAM is received from a + remote endpoint. */ + ngtcp2_vec rdata[1]; +} ngtcp2_datagram; + +typedef union ngtcp2_frame { + uint8_t type; + ngtcp2_stream stream; + ngtcp2_ack ack; + ngtcp2_padding padding; + ngtcp2_reset_stream reset_stream; + ngtcp2_connection_close connection_close; + ngtcp2_max_data max_data; + ngtcp2_max_stream_data max_stream_data; + ngtcp2_max_streams max_streams; + ngtcp2_ping ping; + ngtcp2_data_blocked data_blocked; + ngtcp2_stream_data_blocked stream_data_blocked; + ngtcp2_streams_blocked streams_blocked; + ngtcp2_new_connection_id new_connection_id; + ngtcp2_stop_sending stop_sending; + ngtcp2_path_challenge path_challenge; + ngtcp2_path_response path_response; + ngtcp2_crypto crypto; + ngtcp2_new_token new_token; + ngtcp2_retire_connection_id retire_connection_id; + ngtcp2_handshake_done handshake_done; + ngtcp2_datagram datagram; +} ngtcp2_frame; + +typedef struct ngtcp2_pkt_chain ngtcp2_pkt_chain; + +/* + * ngtcp2_pkt_chain is the chain of incoming packets buffered. + */ +struct ngtcp2_pkt_chain { + ngtcp2_path_storage path; + ngtcp2_pkt_info pi; + ngtcp2_pkt_chain *next; + uint8_t *pkt; + /* pktlen is length of a QUIC packet. */ + size_t pktlen; + /* dgramlen is length of UDP datagram that a QUIC packet is + included. */ + size_t dgramlen; + ngtcp2_tstamp ts; +}; + +/* + * ngtcp2_pkt_chain_new allocates ngtcp2_pkt_chain objects, and + * assigns its pointer to |*ppc|. The content of buffer pointed by + * |pkt| of length |pktlen| is copied into |*ppc|. The packet is + * obtained via the network |path|. The values of path->local and + * path->remote are copied into |*ppc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pkt_chain_new(ngtcp2_pkt_chain **ppc, const ngtcp2_path *path, + const ngtcp2_pkt_info *pi, const uint8_t *pkt, + size_t pktlen, size_t dgramlen, ngtcp2_tstamp ts, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pkt_chain_del deallocates |pc|. It also frees the memory + * pointed by |pc|. + */ +void ngtcp2_pkt_chain_del(ngtcp2_pkt_chain *pc, const ngtcp2_mem *mem); + +/* + * ngtcp2_pkt_hd_init initializes |hd| with the given values. If + * |dcid| and/or |scid| is NULL, DCID and SCID 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. + */ +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); + +/* + * ngtcp2_pkt_encode_hd_long encodes |hd| as QUIC long header into + * |out| which has length |outlen|. It returns the number of bytes + * written into |outlen| if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too short + */ +ngtcp2_ssize ngtcp2_pkt_encode_hd_long(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd); + +/* + * ngtcp2_pkt_encode_hd_short encodes |hd| as QUIC short header into + * |out| which has length |outlen|. It returns the number of bytes + * written into |outlen| if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer is too short + */ +ngtcp2_ssize ngtcp2_pkt_encode_hd_short(uint8_t *out, size_t outlen, + const ngtcp2_pkt_hd *hd); + +/** + * @function + * + * `ngtcp2_pkt_decode_frame` decodes a QUIC frame from the buffer + * pointed by |payload| whose length is |payloadlen|. + * + * This function returns the number of bytes read to decode a single + * frame if it succeeds, or one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_FRAME_ENCODING` + * Frame is badly formatted; or frame type is unknown. + */ +ngtcp2_ssize ngtcp2_pkt_decode_frame(ngtcp2_frame *dest, const uint8_t *payload, + size_t payloadlen); + +/** + * @function + * + * `ngtcp2_pkt_encode_frame` encodes a frame |fm| into the buffer + * pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written to the buffer, or + * one of the following negative error codes: + * + * :enum:`NGTCP2_ERR_NOBUF` + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_frame(uint8_t *out, size_t outlen, + ngtcp2_frame *fr); + +/* + * ngtcp2_pkt_decode_version_negotiation decodes Version Negotiation + * packet payload |payload| of length |payloadlen|, and stores the + * result in |dest|. |dest| must have enough capacity to store the + * result. |payloadlen| also must be a multiple of sizeof(uint32_t). + * + * This function returns the number of versions written in |dest|. + */ +size_t ngtcp2_pkt_decode_version_negotiation(uint32_t *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stateless_reset decodes Stateless Reset payload + * |payload| of length |payloadlen|. The |payload| must start with + * Stateless Reset Token. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Payloadlen is too short. + */ +int ngtcp2_pkt_decode_stateless_reset(ngtcp2_pkt_stateless_reset *sr, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_retry decodes Retry packet payload |payload| of + * length |payloadlen|. The |payload| must start with Retry token + * field. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Payloadlen is too short. + */ +int ngtcp2_pkt_decode_retry(ngtcp2_pkt_retry *dest, const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stream_frame decodes STREAM frame from |payload| + * of length |payloadlen|. The result is stored in the object pointed + * by |dest|. STREAM frame must start at payload[0]. This function + * finishes when it decodes one STREAM frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STREAM frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_stream_frame(ngtcp2_stream *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_ack_frame decodes ACK frame from |payload| of + * length |payloadlen|. The result is stored in the object pointed by + * |dest|. ACK frame must start at payload[0]. This function + * finishes when it decodes one ACK frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include ACK frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_ack_frame(ngtcp2_ack *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_padding_frame decodes contiguous PADDING frames + * from |payload| of length |payloadlen|. It continues to parse + * frames as long as the frame type is PADDING. This finishes when it + * encounters the frame type which is not PADDING, or all input data + * is read. The first byte (payload[0]) must be NGTCP2_FRAME_PADDING. + * This function returns the exact number of bytes read to decode + * PADDING frames. + */ +size_t ngtcp2_pkt_decode_padding_frame(ngtcp2_padding *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_reset_stream_frame decodes RESET_STREAM frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. RESET_STREAM frame must start at + * payload[0]. This function finishes when it decodes one + * RESET_STREAM frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include RESET_STREAM frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_reset_stream_frame(ngtcp2_reset_stream *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_connection_close_frame decodes CONNECTION_CLOSE + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. CONNECTION_CLOSE frame must start + * at payload[0]. This function finishes it decodes one + * CONNECTION_CLOSE frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include CONNECTION_CLOSE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_connection_close_frame( + ngtcp2_connection_close *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_max_data_frame decodes MAX_DATA frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. MAX_DATA frame must start at payload[0]. + * This function finishes when it decodes one MAX_DATA frame, and + * returns the exact number of bytes read to decode a frame if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include MAX_DATA frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_max_data_frame(ngtcp2_max_data *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_max_stream_data_frame decodes MAX_STREAM_DATA + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. MAX_STREAM_DATA frame must start + * at payload[0]. This function finishes when it decodes one + * MAX_STREAM_DATA frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include MAX_STREAM_DATA frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_max_stream_data_frame( + ngtcp2_max_stream_data *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_max_streams_frame decodes MAX_STREAMS frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. MAX_STREAMS frame must start at + * payload[0]. This function finishes when it decodes one MAX_STREAMS + * frame, and returns the exact number of bytes read to decode a frame + * if it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include MAX_STREAMS frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_max_streams_frame(ngtcp2_max_streams *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_ping_frame decodes PING frame from |payload| of + * length |payloadlen|. The result is stored in the object pointed by + * |dest|. PING frame must start at payload[0]. This function + * finishes when it decodes one PING frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include PING frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_ping_frame(ngtcp2_ping *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_data_blocked_frame decodes DATA_BLOCKED frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. DATA_BLOCKED frame must start at + * payload[0]. This function finishes when it decodes one + * DATA_BLOCKED frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include DATA_BLOCKED frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_data_blocked_frame(ngtcp2_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stream_data_blocked_frame decodes + * STREAM_DATA_BLOCKED frame from |payload| of length |payloadlen|. + * The result is stored in the object pointed by |dest|. + * STREAM_DATA_BLOCKED frame must start at payload[0]. This function + * finishes when it decodes one STREAM_DATA_BLOCKED frame, and returns + * the exact number of bytes read to decode a frame if it succeeds, or + * one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STREAM_DATA_BLOCKED frame. + */ +ngtcp2_ssize +ngtcp2_pkt_decode_stream_data_blocked_frame(ngtcp2_stream_data_blocked *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_streams_blocked_frame decodes STREAMS_BLOCKED + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. STREAMS_BLOCKED frame must start + * at payload[0]. This function finishes when it decodes one + * STREAMS_BLOCKED frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STREAMS_BLOCKED frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_streams_blocked_frame( + ngtcp2_streams_blocked *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_new_connection_id_frame decodes NEW_CONNECTION_ID + * frame from |payload| of length |payloadlen|. The result is stored + * in the object pointed by |dest|. NEW_CONNECTION_ID frame must + * start at payload[0]. This function finishes when it decodes one + * NEW_CONNECTION_ID frame, and returns the exact number of bytes read + * to decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include NEW_CONNECTION_ID frame; or the + * length of CID is strictly less than NGTCP2_MIN_CIDLEN or + * greater than NGTCP2_MAX_CIDLEN. + */ +ngtcp2_ssize ngtcp2_pkt_decode_new_connection_id_frame( + ngtcp2_new_connection_id *dest, const uint8_t *payload, size_t payloadlen); + +/* + * ngtcp2_pkt_decode_stop_sending_frame decodes STOP_SENDING frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. STOP_SENDING frame must start at + * payload[0]. This function finishes when it decodes one + * STOP_SENDING frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include STOP_SENDING frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_stop_sending_frame(ngtcp2_stop_sending *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_path_challenge_frame decodes PATH_CHALLENGE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. PATH_CHALLENGE frame must start at + * payload[0]. This function finishes when it decodes one + * PATH_CHALLENGE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include PATH_CHALLENGE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_path_challenge_frame(ngtcp2_path_challenge *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_path_response_frame decodes PATH_RESPONSE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. PATH_RESPONSE frame must start at + * payload[0]. This function finishes when it decodes one + * PATH_RESPONSE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include PATH_RESPONSE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_path_response_frame(ngtcp2_path_response *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_crypto_frame decodes CRYPTO frame from |payload| + * of length |payloadlen|. The result is stored in the object pointed + * by |dest|. CRYPTO frame must start at payload[0]. This function + * finishes when it decodes one CRYPTO frame, and returns the exact + * number of bytes read to decode a frame if it succeeds, or one of + * the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include CRYPTO frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_crypto_frame(ngtcp2_crypto *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_new_token_frame decodes NEW_TOKEN frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. NEW_TOKEN frame must start at + * payload[0]. This function finishes when it decodes one NEW_TOKEN + * frame, and returns the exact number of bytes read to decode a frame + * if it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include NEW_TOKEN frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_new_token_frame(ngtcp2_new_token *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_retire_connection_id_frame decodes RETIRE_CONNECTION_ID + * frame from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. RETIRE_CONNECTION_ID frame must start at + * payload[0]. This function finishes when it decodes one RETIRE_CONNECTION_ID + * frame, and returns the exact number of bytes read to decode a frame + * if it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include RETIRE_CONNECTION_ID frame. + */ +ngtcp2_ssize +ngtcp2_pkt_decode_retire_connection_id_frame(ngtcp2_retire_connection_id *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_handshake_done_frame decodes HANDSHAKE_DONE frame + * from |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. HANDSHAKE_DONE frame must start at + * payload[0]. This function finishes when it decodes one + * HANDSHAKE_DONE frame, and returns the exact number of bytes read to + * decode a frame if it succeeds, or one of the following negative + * error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include HANDSHAKE_DONE frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_handshake_done_frame(ngtcp2_handshake_done *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_decode_datagram_frame decodes DATAGRAM frame from + * |payload| of length |payloadlen|. The result is stored in the + * object pointed by |dest|. DATAGRAM frame must start at payload[0]. + * This function finishes when it decodes one DATAGRAM frame, and + * returns the exact number of bytes read to decode a frame if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_FRAME_ENCODING + * Payload is too short to include DATAGRAM frame. + */ +ngtcp2_ssize ngtcp2_pkt_decode_datagram_frame(ngtcp2_datagram *dest, + const uint8_t *payload, + size_t payloadlen); + +/* + * ngtcp2_pkt_encode_stream_frame encodes STREAM frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function assigns <the serialized frame type> & + * ~NGTCP2_FRAME_STREAM to fr->flags. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_stream_frame(uint8_t *out, size_t outlen, + ngtcp2_stream *fr); + +/* + * ngtcp2_pkt_encode_ack_frame encodes ACK frame |fr| into the buffer + * pointed by |out| of length |outlen|. + * + * This function assigns <the serialized frame type> & + * ~NGTCP2_FRAME_ACK to fr->flags. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_ack_frame(uint8_t *out, size_t outlen, + ngtcp2_ack *fr); + +/* + * ngtcp2_pkt_encode_padding_frame encodes PADDING frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function encodes consecutive fr->len PADDING frames. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write frame(s). + */ +ngtcp2_ssize ngtcp2_pkt_encode_padding_frame(uint8_t *out, size_t outlen, + const ngtcp2_padding *fr); + +/* + * ngtcp2_pkt_encode_reset_stream_frame encodes RESET_STREAM frame + * |fr| into the buffer pointed by |out| of length |buflen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_reset_stream_frame(uint8_t *out, size_t outlen, + const ngtcp2_reset_stream *fr); + +/* + * ngtcp2_pkt_encode_connection_close_frame encodes CONNECTION_CLOSE + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_connection_close_frame(uint8_t *out, size_t outlen, + const ngtcp2_connection_close *fr); + +/* + * ngtcp2_pkt_encode_max_data_frame encodes MAX_DATA frame |fr| into + * the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_max_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_data *fr); + +/* + * ngtcp2_pkt_encode_max_stream_data_frame encodes MAX_STREAM_DATA + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_max_stream_data_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_stream_data *fr); + +/* + * ngtcp2_pkt_encode_max_streams_frame encodes MAX_STREAMS + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_max_streams_frame(uint8_t *out, size_t outlen, + const ngtcp2_max_streams *fr); + +/* + * ngtcp2_pkt_encode_ping_frame encodes PING frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_ping_frame(uint8_t *out, size_t outlen, + const ngtcp2_ping *fr); + +/* + * ngtcp2_pkt_encode_data_blocked_frame encodes DATA_BLOCKED frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_data_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_data_blocked *fr); + +/* + * ngtcp2_pkt_encode_stream_data_blocked_frame encodes + * STREAM_DATA_BLOCKED frame |fr| into the buffer pointed by |out| of + * length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_stream_data_blocked_frame( + uint8_t *out, size_t outlen, const ngtcp2_stream_data_blocked *fr); + +/* + * ngtcp2_pkt_encode_streams_blocked_frame encodes STREAMS_BLOCKED + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_streams_blocked_frame(uint8_t *out, size_t outlen, + const ngtcp2_streams_blocked *fr); + +/* + * ngtcp2_pkt_encode_new_connection_id_frame encodes NEW_CONNECTION_ID + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_new_connection_id_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_connection_id *fr); + +/* + * ngtcp2_pkt_encode_stop_sending_frame encodes STOP_SENDING frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_stop_sending_frame(uint8_t *out, size_t outlen, + const ngtcp2_stop_sending *fr); + +/* + * ngtcp2_pkt_encode_path_challenge_frame encodes PATH_CHALLENGE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_path_challenge_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_challenge *fr); + +/* + * ngtcp2_pkt_encode_path_response_frame encodes PATH_RESPONSE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_path_response_frame(uint8_t *out, size_t outlen, + const ngtcp2_path_response *fr); + +/* + * ngtcp2_pkt_encode_crypto_frame encodes CRYPTO frame |fr| into the + * buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_crypto_frame(uint8_t *out, size_t outlen, + const ngtcp2_crypto *fr); + +/* + * ngtcp2_pkt_encode_new_token_frame encodes NEW_TOKEN frame |fr| into + * the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_new_token_frame(uint8_t *out, size_t outlen, + const ngtcp2_new_token *fr); + +/* + * ngtcp2_pkt_encode_retire_connection_id_frame encodes RETIRE_CONNECTION_ID + * frame |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_retire_connection_id_frame( + uint8_t *out, size_t outlen, const ngtcp2_retire_connection_id *fr); + +/* + * ngtcp2_pkt_encode_handshake_done_frame encodes HANDSHAKE_DONE frame + * |fr| into the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize +ngtcp2_pkt_encode_handshake_done_frame(uint8_t *out, size_t outlen, + const ngtcp2_handshake_done *fr); + +/* + * ngtcp2_pkt_encode_datagram_frame encodes DATAGRAM frame |fr| into + * the buffer pointed by |out| of length |outlen|. + * + * This function returns the number of bytes written if it succeeds, + * or one of the following negative error codes: + * + * NGTCP2_ERR_NOBUF + * Buffer does not have enough capacity to write a frame. + */ +ngtcp2_ssize ngtcp2_pkt_encode_datagram_frame(uint8_t *out, size_t outlen, + const ngtcp2_datagram *fr); + +/* + * ngtcp2_pkt_adjust_pkt_num find the full 64 bits packet number for + * |pkt_num|, which is expected to be least significant |n| bits. The + * |max_pkt_num| is the highest successfully authenticated packet + * number. + */ +int64_t ngtcp2_pkt_adjust_pkt_num(int64_t max_pkt_num, int64_t pkt_num, + size_t n); + +/* + * ngtcp2_pkt_validate_ack checks that ack is malformed or not. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_ACK_FRAME + * ACK frame is malformed + */ +int ngtcp2_pkt_validate_ack(ngtcp2_ack *fr); + +/* + * ngtcp2_pkt_stream_max_datalen returns the maximum number of bytes + * which can be sent for stream denoted by |stream_id|. |offset| is + * an offset of within the stream. |len| is the estimated number of + * bytes to be sent. |left| is the size of buffer. If |left| is too + * small to write STREAM frame, this function returns (size_t)-1. + */ +size_t ngtcp2_pkt_stream_max_datalen(int64_t stream_id, uint64_t offset, + size_t len, size_t left); + +/* + * ngtcp2_pkt_crypto_max_datalen returns the maximum number of bytes + * which can be sent for crypto stream. |offset| is an offset of + * within the crypto stream. |len| is the estimated number of bytes + * to be sent. |left| is the size of buffer. If |left| is too small + * to write CRYPTO frame, this function returns (size_t)-1. + */ +size_t ngtcp2_pkt_crypto_max_datalen(uint64_t offset, size_t len, size_t left); + +/* + * ngtcp2_pkt_datagram_framelen returns the length of DATAGRAM frame + * to encode |len| bytes of data. + */ +size_t ngtcp2_pkt_datagram_framelen(size_t len); + +/* + * ngtcp2_pkt_verify_reserved_bits verifies that the first byte |c| of + * the packet header has the correct reserved bits. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Reserved bits has wrong value. + */ +int ngtcp2_pkt_verify_reserved_bits(uint8_t c); + +/* + * ngtcp2_pkt_encode_pseudo_retry encodes Retry pseudo-packet in the + * buffer pointed by |dest| of length |destlen|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_BUF + * Buffer is too short. + */ +ngtcp2_ssize ngtcp2_pkt_encode_pseudo_retry( + uint8_t *dest, size_t destlen, const ngtcp2_pkt_hd *hd, uint8_t unused, + const ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen); + +/* + * ngtcp2_pkt_verify_retry_tag verifies Retry packet. The buffer + * pointed by |pkt| of length |pktlen| must contain Retry packet + * including packet header. The odcid and tag fields of |retry| must + * be specified. |aead| must be AEAD_AES_128_GCM. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PROTO + * Verification failed. + */ +int ngtcp2_pkt_verify_retry_tag(uint32_t version, const ngtcp2_pkt_retry *retry, + const uint8_t *pkt, size_t pktlen, + ngtcp2_encrypt encrypt, + const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx); + +/** + * @function + * + * `ngtcp2_pkt_get_type_long` returns the long packet type. |c| is + * the first byte of Long packet header. + */ +uint8_t ngtcp2_pkt_get_type_long(uint8_t c); + +#endif /* NGTCP2_PKT_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c new file mode 100644 index 00000000000000..47f4f10a29f34c --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.c @@ -0,0 +1,230 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_ppe.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_conv.h" + +void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, + ngtcp2_crypto_cc *cc) { + ngtcp2_buf_init(&ppe->buf, out, outlen); + + ppe->hdlen = 0; + ppe->len_offset = 0; + ppe->pkt_num_offset = 0; + ppe->pkt_numlen = 0; + ppe->pkt_num = 0; + ppe->sample_offset = 0; + ppe->cc = cc; +} + +int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd) { + ngtcp2_ssize rv; + ngtcp2_buf *buf = &ppe->buf; + ngtcp2_crypto_cc *cc = ppe->cc; + + if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) { + return NGTCP2_ERR_NOBUF; + } + + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + ppe->len_offset = 1 + 4 + 1 + hd->dcid.datalen + 1 + hd->scid.datalen; + if (hd->type == NGTCP2_PKT_INITIAL) { + ppe->len_offset += ngtcp2_put_varint_len(hd->token.len) + hd->token.len; + } + ppe->pkt_num_offset = ppe->len_offset + 2; + rv = ngtcp2_pkt_encode_hd_long( + buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); + } else { + ppe->pkt_num_offset = 1 + hd->dcid.datalen; + rv = ngtcp2_pkt_encode_hd_short( + buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, hd); + } + if (rv < 0) { + return (int)rv; + } + + ppe->sample_offset = ppe->pkt_num_offset + 4; + + buf->last += rv; + + ppe->pkt_numlen = hd->pkt_numlen; + ppe->hdlen = (size_t)rv; + + ppe->pkt_num = hd->pkt_num; + + return 0; +} + +int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr) { + ngtcp2_ssize rv; + ngtcp2_buf *buf = &ppe->buf; + ngtcp2_crypto_cc *cc = ppe->cc; + + if (ngtcp2_buf_left(buf) < cc->aead.max_overhead) { + return NGTCP2_ERR_NOBUF; + } + + rv = ngtcp2_pkt_encode_frame( + buf->last, ngtcp2_buf_left(buf) - cc->aead.max_overhead, fr); + if (rv < 0) { + return (int)rv; + } + + buf->last += rv; + + return 0; +} + +ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt) { + ngtcp2_buf *buf = &ppe->buf; + ngtcp2_crypto_cc *cc = ppe->cc; + uint8_t *payload = buf->begin + ppe->hdlen; + size_t payloadlen = ngtcp2_buf_len(buf) - ppe->hdlen; + uint8_t mask[NGTCP2_HP_SAMPLELEN]; + uint8_t *p; + size_t i; + int rv; + + assert(cc->encrypt); + assert(cc->hp_mask); + + if (ppe->len_offset) { + ngtcp2_put_varint14( + buf->begin + ppe->len_offset, + (uint16_t)(payloadlen + ppe->pkt_numlen + cc->aead.max_overhead)); + } + + ngtcp2_crypto_create_nonce(ppe->nonce, cc->ckm->iv.base, cc->ckm->iv.len, + ppe->pkt_num); + + rv = cc->encrypt(payload, &cc->aead, &cc->ckm->aead_ctx, payload, payloadlen, + ppe->nonce, cc->ckm->iv.len, buf->begin, ppe->hdlen); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + buf->last = payload + payloadlen + cc->aead.max_overhead; + + /* TODO Check that we have enough space to get sample */ + assert(ppe->sample_offset + NGTCP2_HP_SAMPLELEN <= ngtcp2_buf_len(buf)); + + rv = cc->hp_mask(mask, &cc->hp, &cc->hp_ctx, buf->begin + ppe->sample_offset); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + p = buf->begin; + if (*p & NGTCP2_HEADER_FORM_BIT) { + *p = (uint8_t)(*p ^ (mask[0] & 0x0f)); + } else { + *p = (uint8_t)(*p ^ (mask[0] & 0x1f)); + } + + p = buf->begin + ppe->pkt_num_offset; + for (i = 0; i < ppe->pkt_numlen; ++i) { + *(p + i) ^= mask[i + 1]; + } + + if (ppkt != NULL) { + *ppkt = buf->begin; + } + + return (ngtcp2_ssize)ngtcp2_buf_len(buf); +} + +size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + + if (ngtcp2_buf_left(&ppe->buf) < cc->aead.max_overhead) { + return 0; + } + + return ngtcp2_buf_left(&ppe->buf) - cc->aead.max_overhead; +} + +size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + + return ngtcp2_buf_len(&ppe->buf) + cc->aead.max_overhead; +} + +size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + ngtcp2_buf *buf = &ppe->buf; + size_t len; + + assert(ngtcp2_buf_left(buf) >= cc->aead.max_overhead); + + len = ngtcp2_buf_left(buf) - cc->aead.max_overhead; + memset(buf->last, 0, len); + buf->last += len; + + return len; +} + +size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe) { + ngtcp2_crypto_cc *cc = ppe->cc; + ngtcp2_buf *buf = &ppe->buf; + size_t max_samplelen; + size_t len = 0; + + assert(cc->aead.max_overhead); + + max_samplelen = + ngtcp2_buf_len(buf) + cc->aead.max_overhead - ppe->sample_offset; + if (max_samplelen < NGTCP2_HP_SAMPLELEN) { + len = NGTCP2_HP_SAMPLELEN - max_samplelen; + assert(ngtcp2_ppe_left(ppe) >= len); + memset(buf->last, 0, len); + buf->last += len; + } + + return len; +} + +size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n) { + ngtcp2_crypto_cc *cc = ppe->cc; + ngtcp2_buf *buf = &ppe->buf; + size_t pktlen = ngtcp2_buf_len(buf) + cc->aead.max_overhead; + size_t len; + + if (pktlen >= n) { + return 0; + } + + len = n - pktlen; + buf->last = ngtcp2_setmem(buf->last, 0, len); + + return len; +} + +int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe) { + ngtcp2_buf *buf = &ppe->buf; + return ngtcp2_buf_left(buf) >= (4 - ppe->pkt_numlen) + NGTCP2_HP_SAMPLELEN; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h new file mode 100644 index 00000000000000..bf220df37c14f2 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ppe.h @@ -0,0 +1,153 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_PPE_H +#define NGTCP2_PPE_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_crypto.h" + +/* + * ngtcp2_ppe is the Protected Packet Encoder. + */ +typedef struct ngtcp2_ppe { + ngtcp2_buf buf; + ngtcp2_crypto_cc *cc; + /* hdlen is the number of bytes for packet header written in buf. */ + size_t hdlen; + /* len_offset is the offset to Length field. */ + size_t len_offset; + /* pkt_num_offset is the offset to packet number field. */ + size_t pkt_num_offset; + /* pkt_numlen is the number of bytes used to encode a packet + number */ + size_t pkt_numlen; + /* sample_offset is the offset to sample for packet number + encryption. */ + size_t sample_offset; + /* pkt_num is the packet number written in buf. */ + int64_t pkt_num; + /* nonce is the buffer to store nonce. It should be equal or longer + than then length of IV. */ + uint8_t nonce[32]; +} ngtcp2_ppe; + +/* + * ngtcp2_ppe_init initializes |ppe| with the given buffer. + */ +void ngtcp2_ppe_init(ngtcp2_ppe *ppe, uint8_t *out, size_t outlen, + ngtcp2_crypto_cc *cc); + +/* + * ngtcp2_ppe_encode_hd encodes |hd|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * The buffer is too small. + */ +int ngtcp2_ppe_encode_hd(ngtcp2_ppe *ppe, const ngtcp2_pkt_hd *hd); + +/* + * ngtcp2_ppe_encode_frame encodes |fr|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOBUF + * The buffer is too small. + */ +int ngtcp2_ppe_encode_frame(ngtcp2_ppe *ppe, ngtcp2_frame *fr); + +/* + * ngtcp2_ppe_final encrypts QUIC packet payload. If |**ppkt| is not + * NULL, the pointer to the packet is assigned to it. + * + * This function returns the length of QUIC packet, including header, + * and payload if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User-defined callback function failed. + */ +ngtcp2_ssize ngtcp2_ppe_final(ngtcp2_ppe *ppe, const uint8_t **ppkt); + +/* + * ngtcp2_ppe_left returns the number of bytes left to write + * additional frames. This does not count AEAD overhead. + */ +size_t ngtcp2_ppe_left(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_pktlen returns the provisional packet length. It + * includes AEAD overhead. + */ +size_t ngtcp2_ppe_pktlen(ngtcp2_ppe *ppe); + +/** + * @function + * + * `ngtcp2_ppe_padding` encodes PADDING frames to the end of the + * buffer. This function returns the number of bytes padded. + */ +size_t ngtcp2_ppe_padding(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_padding_hp_sample adds PADDING frame if the current + * payload does not have enough space for header protection sample. + * This function should be called just before calling + * ngtcp2_ppe_final(). + * + * This function returns the number of bytes added as padding. + */ +size_t ngtcp2_ppe_padding_hp_sample(ngtcp2_ppe *ppe); + +/* + * ngtcp2_ppe_padding_size adds PADDING frame so that the size of QUIC + * packet is at least |n| bytes long. If it is unable to add PADDING + * in that way, this function still adds PADDING frame as much as + * possible. This function should be called just before calling + * ngtcp2_ppe_final(). For Short packet, this function should be + * called instead of ngtcp2_ppe_padding_hp_sample. + * + * This function returns the number of bytes added as padding. + */ +size_t ngtcp2_ppe_padding_size(ngtcp2_ppe *ppe, size_t n); + +/* + * ngtcp2_ppe_ensure_hp_sample returns nonzero if the buffer has + * enough space for header protection sample. This should be called + * right after packet header is written. + */ +int ngtcp2_ppe_ensure_hp_sample(ngtcp2_ppe *ppe); + +#endif /* NGTCP2_PPE_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c new file mode 100644 index 00000000000000..5e1003d7942e53 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.c @@ -0,0 +1,164 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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_pq.h" + +#include <assert.h> + +#include "ngtcp2_macro.h" + +void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem) { + pq->mem = mem; + pq->capacity = 0; + pq->q = NULL; + pq->length = 0; + pq->less = less; +} + +void ngtcp2_pq_free(ngtcp2_pq *pq) { + ngtcp2_mem_free(pq->mem, pq->q); + pq->q = NULL; +} + +static void swap(ngtcp2_pq *pq, size_t i, size_t j) { + ngtcp2_pq_entry *a = pq->q[i]; + ngtcp2_pq_entry *b = pq->q[j]; + + pq->q[i] = b; + b->index = i; + pq->q[j] = a; + a->index = j; +} + +static void bubble_up(ngtcp2_pq *pq, size_t index) { + size_t parent; + while (index != 0) { + parent = (index - 1) / 2; + if (!pq->less(pq->q[index], pq->q[parent])) { + return; + } + swap(pq, parent, index); + index = parent; + } +} + +int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { + if (pq->capacity <= pq->length) { + void *nq; + size_t ncapacity; + + ncapacity = ngtcp2_max(4, (pq->capacity * 2)); + + nq = ngtcp2_mem_realloc(pq->mem, pq->q, + ncapacity * sizeof(ngtcp2_pq_entry *)); + if (nq == NULL) { + return NGTCP2_ERR_NOMEM; + } + pq->capacity = ncapacity; + pq->q = nq; + } + pq->q[pq->length] = item; + item->index = pq->length; + ++pq->length; + bubble_up(pq, pq->length - 1); + return 0; +} + +ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq) { + assert(pq->length); + return pq->q[0]; +} + +static void bubble_down(ngtcp2_pq *pq, size_t index) { + size_t i, j, minindex; + for (;;) { + j = index * 2 + 1; + minindex = index; + for (i = 0; i < 2; ++i, ++j) { + if (j >= pq->length) { + break; + } + if (pq->less(pq->q[j], pq->q[minindex])) { + minindex = j; + } + } + if (minindex == index) { + return; + } + swap(pq, index, minindex); + index = minindex; + } +} + +void ngtcp2_pq_pop(ngtcp2_pq *pq) { + if (pq->length > 0) { + pq->q[0] = pq->q[pq->length - 1]; + pq->q[0]->index = 0; + --pq->length; + bubble_down(pq, 0); + } +} + +void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item) { + assert(pq->q[item->index] == item); + + if (item->index == 0) { + ngtcp2_pq_pop(pq); + return; + } + + if (item->index == pq->length - 1) { + --pq->length; + return; + } + + pq->q[item->index] = pq->q[pq->length - 1]; + pq->q[item->index]->index = item->index; + --pq->length; + + if (pq->less(item, pq->q[item->index])) { + bubble_down(pq, item->index); + } else { + bubble_up(pq, item->index); + } +} + +int ngtcp2_pq_empty(ngtcp2_pq *pq) { return pq->length == 0; } + +size_t ngtcp2_pq_size(ngtcp2_pq *pq) { return pq->length; } + +int ngtcp2_pq_each(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 new file mode 100644 index 00000000000000..720c309f5adb5e --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pq.h @@ -0,0 +1,126 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 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_PQ_H +#define NGTCP2_PQ_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* Implementation of priority queue */ + +/* NGTCP2_PQ_BAD_INDEX is the priority queue index which indicates + that an entry is not queued. Assigning this value to + ngtcp2_pq_entry.index can check that the entry is queued or not. */ +#define NGTCP2_PQ_BAD_INDEX SIZE_MAX + +typedef struct ngtcp2_pq_entry { + size_t index; +} ngtcp2_pq_entry; + +/* "less" function, return nonzero if |lhs| is less than |rhs|. */ +typedef int (*ngtcp2_less)(const ngtcp2_pq_entry *lhs, + const ngtcp2_pq_entry *rhs); + +typedef struct ngtcp2_pq { + /* The pointer to the pointer to the item stored */ + ngtcp2_pq_entry **q; + /* Memory allocator */ + const ngtcp2_mem *mem; + /* The number of items stored */ + size_t length; + /* The maximum number of items this pq can store. This is + automatically extended when length is reached to this value. */ + size_t capacity; + /* The less function between items */ + ngtcp2_less less; +} ngtcp2_pq; + +/* + * Initializes priority queue |pq| with compare function |cmp|. + */ +void ngtcp2_pq_init(ngtcp2_pq *pq, ngtcp2_less less, const ngtcp2_mem *mem); + +/* + * Deallocates any resources allocated for |pq|. The stored items are + * not freed by this function. + */ +void ngtcp2_pq_free(ngtcp2_pq *pq); + +/* + * Adds |item| to the priority queue |pq|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_pq_push(ngtcp2_pq *pq, ngtcp2_pq_entry *item); + +/* + * Returns item at the top of the queue |pq|. It is undefined if the + * queue is empty. + */ +ngtcp2_pq_entry *ngtcp2_pq_top(ngtcp2_pq *pq); + +/* + * Pops item at the top of the queue |pq|. The popped item is not + * freed by this function. + */ +void ngtcp2_pq_pop(ngtcp2_pq *pq); + +/* + * Returns nonzero if the queue |pq| is empty. + */ +int ngtcp2_pq_empty(ngtcp2_pq *pq); + +/* + * Returns the number of items in the queue |pq|. + */ +size_t ngtcp2_pq_size(ngtcp2_pq *pq); + +typedef int (*ngtcp2_pq_item_cb)(ngtcp2_pq_entry *item, void *arg); + +/* + * Applys |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(ngtcp2_pq *pq, ngtcp2_pq_item_cb fun, void *arg); + +/* + * Removes |item| from priority queue. + */ +void ngtcp2_pq_remove(ngtcp2_pq *pq, ngtcp2_pq_entry *item); + +#endif /* NGTCP2_PQ_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c new file mode 100644 index 00000000000000..d5f7759d6c9bad --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.c @@ -0,0 +1,180 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_pv.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_log.h" +#include "ngtcp2_macro.h" +#include "ngtcp2_addr.h" + +void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, + ngtcp2_tstamp expiry, uint8_t flags) { + memcpy(pvent->data, data, sizeof(pvent->data)); + pvent->expiry = expiry; + pvent->flags = flags; +} + +int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, + ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, + const ngtcp2_mem *mem) { + int rv; + + (*ppv) = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_pv)); + if (*ppv == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_ringbuf_init(&(*ppv)->ents, NGTCP2_PV_MAX_ENTRIES, + sizeof(ngtcp2_pv_entry), mem); + if (rv != 0) { + ngtcp2_mem_free(mem, *ppv); + return 0; + } + + ngtcp2_dcid_copy(&(*ppv)->dcid, dcid); + + (*ppv)->mem = mem; + (*ppv)->log = log; + (*ppv)->timeout = timeout; + (*ppv)->fallback_pto = 0; + (*ppv)->started_ts = UINT64_MAX; + (*ppv)->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT; + (*ppv)->round = 0; + (*ppv)->flags = flags; + + return 0; +} + +void ngtcp2_pv_del(ngtcp2_pv *pv) { + if (pv == NULL) { + return; + } + ngtcp2_ringbuf_free(&pv->ents); + ngtcp2_mem_free(pv->mem, pv); +} + +void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, + ngtcp2_tstamp expiry, uint8_t flags, + ngtcp2_tstamp ts) { + ngtcp2_pv_entry *ent; + + assert(pv->probe_pkt_left); + + if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + pv->started_ts = ts; + } + + ent = ngtcp2_ringbuf_push_back(&pv->ents); + ngtcp2_pv_entry_init(ent, data, expiry, flags); + + pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_CANCEL_TIMER; + --pv->probe_pkt_left; +} + +int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data) { + size_t len = ngtcp2_ringbuf_len(&pv->ents); + size_t i; + ngtcp2_pv_entry *ent; + + if (len == 0) { + return NGTCP2_ERR_INVALID_STATE; + } + + for (i = 0; i < len; ++i) { + ent = ngtcp2_ringbuf_get(&pv->ents, i); + if (memcmp(ent->data, data, sizeof(ent->data)) == 0) { + *pflags = ent->flags; + ngtcp2_log_info(pv->log, NGTCP2_LOG_EVENT_PTV, "path has been validated"); + return 0; + } + } + + return NGTCP2_ERR_INVALID_ARGUMENT; +} + +void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts) { + ngtcp2_pv_entry *ent; + + if (ngtcp2_ringbuf_len(&pv->ents) == 0) { + return; + } + + ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + + if (ent->expiry > ts) { + return; + } + + ++pv->round; + pv->probe_pkt_left = NGTCP2_PV_NUM_PROBE_PKT; +} + +int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv) { + return pv->probe_pkt_left > 0; +} + +int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts) { + ngtcp2_tstamp t; + ngtcp2_pv_entry *ent; + + if (pv->started_ts == UINT64_MAX) { + return 0; + } + + assert(ngtcp2_ringbuf_len(&pv->ents)); + + ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + + t = pv->started_ts + pv->timeout; + t = ngtcp2_max(t, ent->expiry); + + return t <= ts; +} + +ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv) { + ngtcp2_pv_entry *ent; + + if ((pv->flags & NGTCP2_PV_FLAG_CANCEL_TIMER) || + ngtcp2_ringbuf_len(&pv->ents) == 0) { + return UINT64_MAX; + } + + ent = ngtcp2_ringbuf_get(&pv->ents, ngtcp2_ringbuf_len(&pv->ents) - 1); + + return ent->expiry; +} + +void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts) { + ngtcp2_tstamp expiry = ngtcp2_pv_next_expiry(pv); + + if (expiry > ts) { + return; + } + + pv->flags |= NGTCP2_PV_FLAG_CANCEL_TIMER; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h new file mode 100644 index 00000000000000..b532dbca98356b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_pv.h @@ -0,0 +1,193 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_PV_H +#define NGTCP2_PV_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_cid.h" +#include "ngtcp2_ringbuf.h" + +/* NGTCP2_PV_MAX_ENTRIES is the maximum number of entries that + ngtcp2_pv can contain. It must be power of 2. */ +#define NGTCP2_PV_MAX_ENTRIES 8 +/* NGTCP2_PV_NUM_PROBE_PKT is the number of probe packets containing + PATH_CHALLENGE sent at a time. */ +#define NGTCP2_PV_NUM_PROBE_PKT 2 + +typedef struct ngtcp2_log ngtcp2_log; + +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* NGTCP2_PV_ENTRY_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_PV_ENTRY_FLAG_NONE 0x00 +/* NGTCP2_PV_ENTRY_FLAG_UNDERSIZED indicates that UDP datagram which + contains PATH_CHALLENGE is undersized (< 1200 bytes) */ +#define NGTCP2_PV_ENTRY_FLAG_UNDERSIZED 0x01 + +typedef struct ngtcp2_pv_entry { + /* expiry is the timestamp when this PATH_CHALLENGE expires. */ + ngtcp2_tstamp expiry; + /* flags is zero or more of NGTCP2_PV_ENTRY_FLAG_*. */ + uint8_t flags; + /* data is a byte string included in PATH_CHALLENGE. */ + uint8_t data[8]; +} ngtcp2_pv_entry; + +void ngtcp2_pv_entry_init(ngtcp2_pv_entry *pvent, const uint8_t *data, + ngtcp2_tstamp expiry, uint8_t flags); + +/* NGTCP2_PV_FLAG_NONE indicates no flag is set. */ +#define NGTCP2_PV_FLAG_NONE 0x00 +/* NGTCP2_PV_FLAG_DONT_CARE indicates that the outcome of path + validation should be ignored entirely. */ +#define NGTCP2_PV_FLAG_DONT_CARE 0x01 +/* NGTCP2_PV_FLAG_CANCEL_TIMER indicates that the expiry timer is + cancelled. */ +#define NGTCP2_PV_FLAG_CANCEL_TIMER 0x02 +/* 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 0x04 +/* NGTCP2_PV_FLAG_MTU_PROBE indicates that a validation must probe + least MTU that QUIC requires, which is 1200 bytes. If it fails, a + path is not viable. */ +#define NGTCP2_PV_FLAG_MTU_PROBE 0x08 + +typedef struct ngtcp2_pv ngtcp2_pv; + +/* + * ngtcp2_pv is the context of a single path validation. + */ +struct ngtcp2_pv { + const ngtcp2_mem *mem; + ngtcp2_log *log; + /* dcid is DCID and path this path validation uses. */ + ngtcp2_dcid dcid; + /* fallback_dcid is the usually validated DCID and used as a + fallback if this path validation fails. */ + ngtcp2_dcid fallback_dcid; + /* ents is the ring buffer of ngtcp2_pv_entry */ + ngtcp2_ringbuf ents; + /* timeout is the duration within which this path validation should + succeed. */ + ngtcp2_duration timeout; + /* fallback_pto is PTO of fallback connection. */ + ngtcp2_duration fallback_pto; + /* started_ts is the timestamp this path validation starts. */ + ngtcp2_tstamp started_ts; + /* round is the number of times that probe_pkt_left is reset. */ + size_t round; + /* probe_pkt_left is the number of probe packets containing + PATH_CHALLENGE which can be send without waiting for an + expiration of a previous flight. */ + size_t probe_pkt_left; + /* flags is bitwise-OR of zero or more of NGTCP2_PV_FLAG_*. */ + uint8_t flags; +}; + +/* + * ngtcp2_pv_new creates new ngtcp2_pv object and assigns its pointer + * to |*ppv|. This function makes a copy of |dcid|. |timeout| is a + * duration within which this path validation must succeed. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_pv_new(ngtcp2_pv **ppv, const ngtcp2_dcid *dcid, + ngtcp2_duration timeout, uint8_t flags, ngtcp2_log *log, + const ngtcp2_mem *mem); + +/* + * ngtcp2_pv_del deallocates |pv|. This function frees memory |pv| + * points too. + */ +void ngtcp2_pv_del(ngtcp2_pv *pv); + +/* + * ngtcp2_pv_add_entry adds new entry with |data|. |expiry| is the + * expiry time of the entry. + */ +void ngtcp2_pv_add_entry(ngtcp2_pv *pv, const uint8_t *data, + ngtcp2_tstamp expiry, uint8_t flags, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_full returns nonzero if |pv| is full of ngtcp2_pv_entry. + */ +int ngtcp2_pv_full(ngtcp2_pv *pv); + +/* + * ngtcp2_pv_validate validates that the received |data| matches the + * one of the existing entry. The flag of ngtcp2_pv_entry that + * matches |data| is assigned to |*pflags| if this function succeeds. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_PATH_VALIDATION_FAILED + * path validation has failed and must be abandoned + * NGTCP2_ERR_INVALID_STATE + * |pv| includes no entry + * NGTCP2_ERR_INVALID_ARGUMENT + * |pv| does not have an entry which has |data| and |path| + */ +int ngtcp2_pv_validate(ngtcp2_pv *pv, uint8_t *pflags, const uint8_t *data); + +/* + * ngtcp2_pv_handle_entry_expiry checks expiry of existing entries. + */ +void ngtcp2_pv_handle_entry_expiry(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_should_send_probe returns nonzero if new entry can be + * added by ngtcp2_pv_add_entry. + */ +int ngtcp2_pv_should_send_probe(ngtcp2_pv *pv); + +/* + * ngtcp2_pv_validation_timed_out returns nonzero if the path + * validation fails because of timeout. + */ +int ngtcp2_pv_validation_timed_out(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +/* + * ngtcp2_pv_next_expiry returns the earliest expiry. + */ +ngtcp2_tstamp ngtcp2_pv_next_expiry(ngtcp2_pv *pv); + +/* + * ngtcp2_pv_cancel_expired_timer cancels the expired timer. + */ +void ngtcp2_pv_cancel_expired_timer(ngtcp2_pv *pv, ngtcp2_tstamp ts); + +#endif /* NGTCP2_PV_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c new file mode 100644 index 00000000000000..7c31ab64af6955 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.c @@ -0,0 +1,1108 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_qlog.h" + +#include <assert.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" + +void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, + ngtcp2_tstamp ts, void *user_data) { + qlog->write = write; + qlog->ts = qlog->last_ts = ts; + qlog->user_data = user_data; +} + +#define write_verbatim(DEST, S) ngtcp2_cpymem((DEST), (S), sizeof(S) - 1) + +static uint8_t *write_string_impl(uint8_t *p, const uint8_t *data, + size_t datalen) { + *p++ = '"'; + if (datalen) { + p = ngtcp2_cpymem(p, data, datalen); + } + *p++ = '"'; + return p; +} + +#define write_string(DEST, S) \ + write_string_impl((DEST), (const uint8_t *)(S), sizeof(S) - 1) + +#define NGTCP2_LOWER_XDIGITS "0123456789abcdef" + +static uint8_t *write_hex(uint8_t *p, const uint8_t *data, size_t datalen) { + const uint8_t *b = data, *end = data + datalen; + *p++ = '"'; + for (; b != end; ++b) { + *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b >> 4]; + *p++ = (uint8_t)NGTCP2_LOWER_XDIGITS[*b & 0xf]; + } + *p++ = '"'; + return p; +} + +static uint8_t *write_cid(uint8_t *p, const ngtcp2_cid *cid) { + return write_hex(p, cid->data, cid->datalen); +} + +static uint8_t *write_number(uint8_t *p, uint64_t n) { + size_t nlen = 0; + uint64_t t; + uint8_t *res; + + if (n == 0) { + *p++ = '0'; + return p; + } + for (t = n; t; t /= 10, ++nlen) + ; + p += nlen; + res = p; + for (; n; n /= 10) { + *--p = (uint8_t)((n % 10) + '0'); + } + return res; +} + +static uint8_t *write_tstamp(uint8_t *p, ngtcp2_tstamp ts) { + return write_number(p, ts / NGTCP2_MILLISECONDS); +} + +static uint8_t *write_duration(uint8_t *p, ngtcp2_duration duration) { + return write_number(p, duration / NGTCP2_MILLISECONDS); +} + +static uint8_t *write_bool(uint8_t *p, int b) { + if (b) { + return ngtcp2_cpymem(p, "true", sizeof("true") - 1); + } + return ngtcp2_cpymem(p, "false", sizeof("false") - 1); +} + +static uint8_t *write_pair_impl(uint8_t *p, const uint8_t *name, size_t namelen, + const ngtcp2_vec *value) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_string_impl(p, value->base, value->len); +} + +#define write_pair(DEST, NAME, VALUE) \ + write_pair_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, (VALUE)) + +static uint8_t *write_pair_hex_impl(uint8_t *p, const uint8_t *name, + size_t namelen, const uint8_t *value, + size_t valuelen) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_hex(p, value, valuelen); +} + +#define write_pair_hex(DEST, NAME, VALUE, VALUELEN) \ + write_pair_hex_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \ + (VALUE), (VALUELEN)) + +static uint8_t *write_pair_number_impl(uint8_t *p, const uint8_t *name, + size_t namelen, uint64_t value) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_number(p, value); +} + +#define write_pair_number(DEST, NAME, VALUE) \ + write_pair_number_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \ + (VALUE)) + +static uint8_t *write_pair_duration_impl(uint8_t *p, const uint8_t *name, + size_t namelen, + ngtcp2_duration duration) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_duration(p, duration); +} + +#define write_pair_duration(DEST, NAME, VALUE) \ + write_pair_duration_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \ + (VALUE)) + +static uint8_t *write_pair_tstamp_impl(uint8_t *p, const uint8_t *name, + size_t namelen, ngtcp2_tstamp ts) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_tstamp(p, ts); +} + +#define write_pair_tstamp(DEST, NAME, VALUE) \ + write_pair_tstamp_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \ + (VALUE)) + +static uint8_t *write_pair_bool_impl(uint8_t *p, const uint8_t *name, + size_t namelen, int b) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_bool(p, b); +} + +#define write_pair_bool(DEST, NAME, VALUE) \ + write_pair_bool_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \ + (VALUE)) + +static uint8_t *write_pair_cid_impl(uint8_t *p, const uint8_t *name, + size_t namelen, const ngtcp2_cid *cid) { + p = write_string_impl(p, name, namelen); + *p++ = ':'; + return write_cid(p, cid); +} + +#define write_pair_cid(DEST, NAME, VALUE) \ + write_pair_cid_impl((DEST), (const uint8_t *)(NAME), sizeof(NAME) - 1, \ + (VALUE)) + +#define ngtcp2_make_vec_lit(S) \ + { (uint8_t *)(S), sizeof((S)) - 1 } + +static uint8_t *write_common_fields(uint8_t *p, const ngtcp2_cid *odcid) { + p = write_verbatim( + p, "\"common_fields\":{\"protocol_type\":\"QUIC_HTTP3\",\"time_format\":" + "\"relative\",\"reference_time\":\"0\",\"group_id\":"); + p = write_cid(p, odcid); + *p++ = '}'; + return p; +} + +static uint8_t *write_trace(uint8_t *p, int server, const ngtcp2_cid *odcid) { + p = write_verbatim( + p, "\"trace\":{\"vantage_point\":{\"name\":\"ngtcp2\",\"type\":"); + if (server) { + p = write_string(p, "server"); + } else { + p = write_string(p, "client"); + } + p = write_verbatim(p, "},"); + p = write_common_fields(p, odcid); + *p++ = '}'; + return p; +} + +void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server) { + uint8_t buf[1024]; + uint8_t *p = buf; + + if (!qlog->write) { + return; + } + + p = write_verbatim( + p, "{\"qlog_format\":\"NDJSON\",\"qlog_version\":\"draft-02\","); + p = write_trace(p, server, odcid); + p = write_verbatim(p, "}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, + (size_t)(p - buf)); +} + +void ngtcp2_qlog_end(ngtcp2_qlog *qlog) { + uint8_t buf[1] = {0}; + + if (!qlog->write) { + return; + } + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_FIN, &buf, 0); +} + +static ngtcp2_vec vec_pkt_type_initial = ngtcp2_make_vec_lit("initial"); +static ngtcp2_vec vec_pkt_type_handshake = ngtcp2_make_vec_lit("handshake"); +static ngtcp2_vec vec_pkt_type_0rtt = ngtcp2_make_vec_lit("0RTT"); +static ngtcp2_vec vec_pkt_type_1rtt = ngtcp2_make_vec_lit("1RTT"); +static ngtcp2_vec vec_pkt_type_retry = ngtcp2_make_vec_lit("retry"); +static ngtcp2_vec vec_pkt_type_unknown = ngtcp2_make_vec_lit("unknown"); + +static const ngtcp2_vec *qlog_pkt_type(const ngtcp2_pkt_hd *hd) { + if (hd->flags & NGTCP2_PKT_FLAG_LONG_FORM) { + switch (hd->type) { + case NGTCP2_PKT_INITIAL: + return &vec_pkt_type_initial; + case NGTCP2_PKT_HANDSHAKE: + return &vec_pkt_type_handshake; + case NGTCP2_PKT_0RTT: + return &vec_pkt_type_0rtt; + case NGTCP2_PKT_RETRY: + return &vec_pkt_type_retry; + default: + return &vec_pkt_type_unknown; + } + } + + return &vec_pkt_type_1rtt; +} + +static uint8_t *write_pkt_hd(uint8_t *p, const ngtcp2_pkt_hd *hd) { + /* + * {"packet_type":"version_negotiation","packet_number":"0000000000000000000"} + */ +#define NGTCP2_QLOG_PKT_HD_OVERHEAD 75 + + *p++ = '{'; + p = write_pair(p, "packet_type", qlog_pkt_type(hd)); + *p++ = ','; + p = write_pair_number(p, "packet_number", (uint64_t)hd->pkt_num); + /* TODO Write DCIL and DCID */ + /* TODO Write SCIL and SCID */ + *p++ = '}'; + return p; +} + +static uint8_t *write_padding_frame(uint8_t *p, const ngtcp2_padding *fr) { + (void)fr; + + /* {"frame_type":"padding"} */ +#define NGTCP2_QLOG_PADDING_FRAME_OVERHEAD 24 + + return write_verbatim(p, "{\"frame_type\":\"padding\"}"); +} + +static uint8_t *write_ping_frame(uint8_t *p, const ngtcp2_ping *fr) { + (void)fr; + + /* {"frame_type":"ping"} */ +#define NGTCP2_QLOG_PING_FRAME_OVERHEAD 21 + + return write_verbatim(p, "{\"frame_type\":\"ping\"}"); +} + +static uint8_t *write_ack_frame(uint8_t *p, const ngtcp2_ack *fr) { + int64_t largest_ack, min_ack; + size_t i; + const ngtcp2_ack_blk *blk; + + /* + * {"frame_type":"ack","ack_delay":0000000000000000000,"acked_ranges":[]} + * + * each range: + * [0000000000000000000,0000000000000000000], + * + * ecn: + * ,"ect1":0000000000000000000,"ect0":0000000000000000000,"ce":0000000000000000000 + */ +#define NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD 70 +#define NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD 42 +#define NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD 79 + + p = write_verbatim(p, "{\"frame_type\":\"ack\","); + p = write_pair_duration(p, "ack_delay", fr->ack_delay_unscaled); + *p++ = ','; + p = write_string(p, "acked_ranges"); + *p++ = ':'; + *p++ = '['; + + largest_ack = fr->largest_ack; + min_ack = fr->largest_ack - (int64_t)fr->first_ack_blklen; + + *p++ = '['; + p = write_number(p, (uint64_t)min_ack); + if (largest_ack != min_ack) { + *p++ = ','; + p = write_number(p, (uint64_t)largest_ack); + } + *p++ = ']'; + + for (i = 0; i < fr->num_blks; ++i) { + blk = &fr->blks[i]; + largest_ack = min_ack - (int64_t)blk->gap - 2; + min_ack = largest_ack - (int64_t)blk->blklen; + *p++ = ','; + *p++ = '['; + p = write_number(p, (uint64_t)min_ack); + if (largest_ack != min_ack) { + *p++ = ','; + p = write_number(p, (uint64_t)largest_ack); + } + *p++ = ']'; + } + + *p++ = ']'; + + if (fr->type == NGTCP2_FRAME_ACK_ECN) { + *p++ = ','; + p = write_pair_number(p, "ect1", fr->ecn.ect1); + *p++ = ','; + p = write_pair_number(p, "ect0", fr->ecn.ect0); + *p++ = ','; + p = write_pair_number(p, "ce", fr->ecn.ce); + } + + *p++ = '}'; + + return p; +} + +static uint8_t *write_reset_stream_frame(uint8_t *p, + const ngtcp2_reset_stream *fr) { + /* + * {"frame_type":"reset_stream","stream_id":0000000000000000000,"error_code":0000000000000000000,"final_size":0000000000000000000} + */ +#define NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD 127 + + p = write_verbatim(p, "{\"frame_type\":\"reset_stream\","); + p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_number(p, "error_code", fr->app_error_code); + *p++ = ','; + p = write_pair_number(p, "final_size", fr->final_size); + *p++ = '}'; + + return p; +} + +static uint8_t *write_stop_sending_frame(uint8_t *p, + const ngtcp2_stop_sending *fr) { + /* + * {"frame_type":"stop_sending","stream_id":0000000000000000000,"error_code":0000000000000000000} + */ +#define NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD 94 + + p = write_verbatim(p, "{\"frame_type\":\"stop_sending\","); + p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_number(p, "error_code", fr->app_error_code); + *p++ = '}'; + + return p; +} + +static uint8_t *write_crypto_frame(uint8_t *p, const ngtcp2_crypto *fr) { + /* + * {"frame_type":"crypto","offset":0000000000000000000,"length":0000000000000000000} + */ +#define NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD 81 + + p = write_verbatim(p, "{\"frame_type\":\"crypto\","); + p = write_pair_number(p, "offset", fr->offset); + *p++ = ','; + p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt)); + *p++ = '}'; + + return p; +} + +static uint8_t *write_new_token_frame(uint8_t *p, const ngtcp2_new_token *fr) { + /* + * {"frame_type":"new_token","length":0000000000000000000,"token":""} + */ +#define NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD 66 + + p = write_verbatim(p, "{\"frame_type\":\"new_token\","); + p = write_pair_number(p, "length", fr->token.len); + *p++ = ','; + p = write_pair_hex(p, "token", fr->token.base, fr->token.len); + *p++ = '}'; + + return p; +} + +static uint8_t *write_stream_frame(uint8_t *p, const ngtcp2_stream *fr) { + /* + * {"frame_type":"stream","stream_id":0000000000000000000,"offset":0000000000000000000,"length":0000000000000000000,"fin":true} + */ +#define NGTCP2_QLOG_STREAM_FRAME_OVERHEAD 124 + + p = write_verbatim(p, "{\"frame_type\":\"stream\","); + p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_number(p, "offset", fr->offset); + *p++ = ','; + p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt)); + if (fr->fin) { + *p++ = ','; + p = write_pair_bool(p, "fin", 1); + } + *p++ = '}'; + + return p; +} + +static uint8_t *write_max_data_frame(uint8_t *p, const ngtcp2_max_data *fr) { + /* + * {"frame_type":"max_data","maximum":0000000000000000000} + */ +#define NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD 55 + + p = write_verbatim(p, "{\"frame_type\":\"max_data\","); + p = write_pair_number(p, "maximum", fr->max_data); + *p++ = '}'; + + return p; +} + +static uint8_t *write_max_stream_data_frame(uint8_t *p, + const ngtcp2_max_stream_data *fr) { + /* + * {"frame_type":"max_stream_data","stream_id":0000000000000000000,"maximum":0000000000000000000} + */ +#define NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD 94 + + p = write_verbatim(p, "{\"frame_type\":\"max_stream_data\","); + p = write_pair_number(p, "stream_id", (uint64_t)fr->stream_id); + *p++ = ','; + p = write_pair_number(p, "maximum", fr->max_stream_data); + *p++ = '}'; + + return p; +} + +static uint8_t *write_max_streams_frame(uint8_t *p, + const ngtcp2_max_streams *fr) { + /* + * {"frame_type":"max_streams","stream_type":"unidirectional","maximum":0000000000000000000} + */ +#define NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD 89 + + p = write_verbatim(p, "{\"frame_type\":\"max_streams\","); + p = write_string(p, "stream_type"); + *p++ = ':'; + if (fr->type == NGTCP2_FRAME_MAX_STREAMS_BIDI) { + p = write_string(p, "bidirectional"); + } else { + p = write_string(p, "unidirectional"); + } + *p++ = ','; + p = write_pair_number(p, "maximum", fr->max_streams); + *p++ = '}'; + + return p; +} + +static uint8_t *write_data_blocked_frame(uint8_t *p, + const ngtcp2_data_blocked *fr) { + (void)fr; + + /* + * {"frame_type":"data_blocked"} + */ +#define NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD 29 + + /* TODO log limit */ + + return write_verbatim(p, "{\"frame_type\":\"data_blocked\"}"); +} + +static uint8_t * +write_stream_data_blocked_frame(uint8_t *p, + const ngtcp2_stream_data_blocked *fr) { + (void)fr; + + /* + * {"frame_type":"stream_data_blocked"} + */ +#define NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD 36 + + /* TODO log limit */ + + return write_verbatim(p, "{\"frame_type\":\"stream_data_blocked\"}"); +} + +static uint8_t *write_streams_blocked_frame(uint8_t *p, + const ngtcp2_streams_blocked *fr) { + (void)fr; + + /* + * {"frame_type":"streams_blocked"} + */ +#define NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD 32 + + /* TODO Log stream_type and limit */ + + return write_verbatim(p, "{\"frame_type\":\"streams_blocked\"}"); +} + +static uint8_t * +write_new_connection_id_frame(uint8_t *p, const ngtcp2_new_connection_id *fr) { + /* + * {"frame_type":"new_connection_id","sequence_number":0000000000000000000,"retire_prior_to":0000000000000000000,"connection_id_length":0000000000000000000,"connection_id":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","stateless_reset_token":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} + */ +#define NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD 271 + + p = write_verbatim(p, "{\"frame_type\":\"new_connection_id\","); + p = write_pair_number(p, "sequence_number", fr->seq); + *p++ = ','; + p = write_pair_number(p, "retire_prior_to", fr->retire_prior_to); + *p++ = ','; + p = write_pair_number(p, "connection_id_length", fr->cid.datalen); + *p++ = ','; + p = write_pair_cid(p, "connection_id", &fr->cid); + *p++ = ','; + p = write_pair_hex(p, "stateless_reset_token", fr->stateless_reset_token, + sizeof(fr->stateless_reset_token)); + *p++ = '}'; + + return p; +} + +static uint8_t * +write_retire_connection_id_frame(uint8_t *p, + const ngtcp2_retire_connection_id *fr) { + /* + * {"frame_type":"retire_connection_id","sequence_number":0000000000000000000} + */ +#define NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD 75 + + p = write_verbatim(p, "{\"frame_type\":\"retire_connection_id\","); + p = write_pair_number(p, "sequence_number", fr->seq); + *p++ = '}'; + + return p; +} + +static uint8_t *write_path_challenge_frame(uint8_t *p, + const ngtcp2_path_challenge *fr) { + /* + * {"frame_type":"path_challenge","data":"xxxxxxxxxxxxxxxx"} + */ +#define NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD 57 + + p = write_verbatim(p, "{\"frame_type\":\"path_challenge\","); + p = write_pair_hex(p, "data", fr->data, sizeof(fr->data)); + *p++ = '}'; + + return p; +} + +static uint8_t *write_path_response_frame(uint8_t *p, + const ngtcp2_path_response *fr) { + /* + * {"frame_type":"path_response","data":"xxxxxxxxxxxxxxxx"} + */ +#define NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD 56 + + p = write_verbatim(p, "{\"frame_type\":\"path_response\","); + p = write_pair_hex(p, "data", fr->data, sizeof(fr->data)); + *p++ = '}'; + + return p; +} + +static uint8_t * +write_connection_close_frame(uint8_t *p, const ngtcp2_connection_close *fr) { + /* + * {"frame_type":"connection_close","error_space":"application","error_code":0000000000000000000,"raw_error_code":0000000000000000000} + */ +#define NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD 131 + + p = write_verbatim(p, "{\"frame_type\":\"connection_close\","); + p = write_string(p, "error_space"); + *p++ = ':'; + if (fr->type == NGTCP2_FRAME_CONNECTION_CLOSE) { + p = write_string(p, "transport"); + } else { + p = write_string(p, "application"); + } + *p++ = ','; + p = write_pair_number(p, "error_code", fr->error_code); + *p++ = ','; + p = write_pair_number(p, "raw_error_code", fr->error_code); + /* TODO Write reason by escaping non-printables */ + /* TODO Write trigger_frame_type */ + *p++ = '}'; + + return p; +} + +static uint8_t *write_handshake_done_frame(uint8_t *p, + const ngtcp2_handshake_done *fr) { + (void)fr; + + /* + * {"frame_type":"handshake_done"} + */ +#define NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD 31 + + return write_verbatim(p, "{\"frame_type\":\"handshake_done\"}"); +} + +static uint8_t *write_datagram_frame(uint8_t *p, const ngtcp2_datagram *fr) { + /* + * {"frame_type":"datagram","length":0000000000000000000} + */ +#define NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD 54 + + p = write_verbatim(p, "{\"frame_type\":\"datagram\","); + p = write_pair_number(p, "length", ngtcp2_vec_len(fr->data, fr->datacnt)); + *p++ = '}'; + + return p; +} + +static uint8_t *qlog_write_time(ngtcp2_qlog *qlog, uint8_t *p) { + return write_pair_tstamp(p, "time", qlog->last_ts - qlog->ts); +} + +static void qlog_pkt_write_start(ngtcp2_qlog *qlog, int sent) { + uint8_t *p; + + if (!qlog->write) { + return; + } + + ngtcp2_buf_reset(&qlog->buf); + p = qlog->buf.last; + + *p++ = '{'; + p = qlog_write_time(qlog, p); + p = write_verbatim(p, ",\"name\":"); + if (sent) { + p = write_string(p, "transport:packet_sent"); + } else { + p = write_string(p, "transport:packet_received"); + } + p = write_verbatim(p, ",\"data\":{\"frames\":["); + qlog->buf.last = p; +} + +static void qlog_pkt_write_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + uint8_t *p = qlog->buf.last; + + if (!qlog->write) { + return; + } + + /* + * ],"header":,"raw":{"packet_size":0000000000000000000}}} + * + * plus, terminating LF + */ +#define NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD \ + (1 + 55 + NGTCP2_QLOG_PKT_HD_OVERHEAD) + + assert(ngtcp2_buf_left(&qlog->buf) >= NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD); + assert(ngtcp2_buf_len(&qlog->buf)); + + /* Eat last ',' */ + if (*(p - 1) == ',') { + --p; + } + + p = write_verbatim(p, "],\"header\":"); + p = write_pkt_hd(p, hd); + p = write_verbatim(p, ",\"raw\":{\"packet_size\":"); + p = write_number(p, pktlen); + p = write_verbatim(p, "}}}\n"); + + qlog->buf.last = p; + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, qlog->buf.pos, + ngtcp2_buf_len(&qlog->buf)); +} + +void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr) { + uint8_t *p = qlog->buf.last; + + if (!qlog->write) { + return; + } + + switch (fr->type) { + case NGTCP2_FRAME_PADDING: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PADDING_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_padding_frame(p, &fr->padding); + break; + case NGTCP2_FRAME_PING: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PING_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_ping_frame(p, &fr->ping); + break; + case NGTCP2_FRAME_ACK: + case NGTCP2_FRAME_ACK_ECN: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_ACK_FRAME_BASE_OVERHEAD + + (size_t)(fr->type == NGTCP2_FRAME_ACK_ECN + ? NGTCP2_QLOG_ACK_FRAME_ECN_OVERHEAD + : 0) + + NGTCP2_QLOG_ACK_FRAME_RANGE_OVERHEAD * (1 + fr->ack.num_blks) + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_ack_frame(p, &fr->ack); + break; + case NGTCP2_FRAME_RESET_STREAM: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_RESET_STREAM_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_reset_stream_frame(p, &fr->reset_stream); + break; + case NGTCP2_FRAME_STOP_SENDING: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STOP_SENDING_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_stop_sending_frame(p, &fr->stop_sending); + break; + case NGTCP2_FRAME_CRYPTO: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_CRYPTO_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_crypto_frame(p, &fr->crypto); + break; + case NGTCP2_FRAME_NEW_TOKEN: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_NEW_TOKEN_FRAME_OVERHEAD + + fr->new_token.token.len * 2 + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_new_token_frame(p, &fr->new_token); + break; + case NGTCP2_FRAME_STREAM: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_STREAM_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_stream_frame(p, &fr->stream); + break; + case NGTCP2_FRAME_MAX_DATA: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_DATA_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_max_data_frame(p, &fr->max_data); + break; + case NGTCP2_FRAME_MAX_STREAM_DATA: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_MAX_STREAM_DATA_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_max_stream_data_frame(p, &fr->max_stream_data); + break; + case NGTCP2_FRAME_MAX_STREAMS_BIDI: + case NGTCP2_FRAME_MAX_STREAMS_UNI: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_MAX_STREAMS_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_max_streams_frame(p, &fr->max_streams); + break; + case NGTCP2_FRAME_DATA_BLOCKED: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATA_BLOCKED_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_data_blocked_frame(p, &fr->data_blocked); + break; + case NGTCP2_FRAME_STREAM_DATA_BLOCKED: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STREAM_DATA_BLOCKED_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_stream_data_blocked_frame(p, &fr->stream_data_blocked); + break; + case NGTCP2_FRAME_STREAMS_BLOCKED_BIDI: + case NGTCP2_FRAME_STREAMS_BLOCKED_UNI: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_STREAMS_BLOCKED_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_streams_blocked_frame(p, &fr->streams_blocked); + break; + case NGTCP2_FRAME_NEW_CONNECTION_ID: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_NEW_CONNECTION_ID_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_new_connection_id_frame(p, &fr->new_connection_id); + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_RETIRE_CONNECTION_ID_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_retire_connection_id_frame(p, &fr->retire_connection_id); + break; + case NGTCP2_FRAME_PATH_CHALLENGE: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_PATH_CHALLENGE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_path_challenge_frame(p, &fr->path_challenge); + break; + case NGTCP2_FRAME_PATH_RESPONSE: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_PATH_RESPONSE_FRAME_OVERHEAD + + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_path_response_frame(p, &fr->path_response); + break; + case NGTCP2_FRAME_CONNECTION_CLOSE: + case NGTCP2_FRAME_CONNECTION_CLOSE_APP: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_CONNECTION_CLOSE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_connection_close_frame(p, &fr->connection_close); + break; + case NGTCP2_FRAME_HANDSHAKE_DONE: + if (ngtcp2_buf_left(&qlog->buf) < + NGTCP2_QLOG_HANDSHAKE_DONE_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_handshake_done_frame(p, &fr->handshake_done); + break; + case NGTCP2_FRAME_DATAGRAM: + case NGTCP2_FRAME_DATAGRAM_LEN: + if (ngtcp2_buf_left(&qlog->buf) < NGTCP2_QLOG_DATAGRAM_FRAME_OVERHEAD + 1 + + NGTCP2_QLOG_PKT_WRITE_END_OVERHEAD) { + return; + } + p = write_datagram_frame(p, &fr->datagram); + break; + default: + assert(0); + } + + *p++ = ','; + + qlog->buf.last = p; +} + +void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog) { + qlog_pkt_write_start(qlog, /* sent = */ 0); +} + +void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + qlog_pkt_write_end(qlog, hd, pktlen); +} + +void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog) { + qlog_pkt_write_start(qlog, /* sent = */ 1); +} + +void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen) { + qlog_pkt_write_end(qlog, hd, pktlen); +} + +void ngtcp2_qlog_parameters_set_transport_params( + ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server, + ngtcp2_qlog_side side) { + uint8_t buf[1024]; + uint8_t *p = buf; + const ngtcp2_preferred_addr *paddr; + + if (!qlog->write) { + return; + } + + *p++ = '{'; + p = qlog_write_time(qlog, p); + p = write_verbatim( + p, ",\"name\":\"transport:parameters_set\",\"data\":{\"owner\":"); + + if (side == NGTCP2_QLOG_SIDE_LOCAL) { + p = write_string(p, "local"); + } else { + p = write_string(p, "remote"); + } + + *p++ = ','; + p = write_pair_cid(p, "initial_source_connection_id", ¶ms->initial_scid); + *p++ = ','; + if (side == (server ? NGTCP2_QLOG_SIDE_LOCAL : NGTCP2_QLOG_SIDE_REMOTE)) { + p = write_pair_cid(p, "original_destination_connection_id", + ¶ms->original_dcid); + *p++ = ','; + } + if (params->retry_scid_present) { + p = write_pair_cid(p, "retry_source_connection_id", ¶ms->retry_scid); + *p++ = ','; + } + if (params->stateless_reset_token_present) { + p = write_pair_hex(p, "stateless_reset_token", + params->stateless_reset_token, + sizeof(params->stateless_reset_token)); + *p++ = ','; + } + p = write_pair_bool(p, "disable_active_migration", + params->disable_active_migration); + *p++ = ','; + p = write_pair_duration(p, "max_idle_timeout", params->max_idle_timeout); + *p++ = ','; + p = write_pair_number(p, "max_udp_payload_size", + params->max_udp_payload_size); + *p++ = ','; + p = write_pair_number(p, "ack_delay_exponent", params->ack_delay_exponent); + *p++ = ','; + p = write_pair_duration(p, "max_ack_delay", params->max_ack_delay); + *p++ = ','; + p = write_pair_number(p, "active_connection_id_limit", + params->active_connection_id_limit); + *p++ = ','; + p = write_pair_number(p, "initial_max_data", params->initial_max_data); + *p++ = ','; + p = write_pair_number(p, "initial_max_stream_data_bidi_local", + params->initial_max_stream_data_bidi_local); + *p++ = ','; + p = write_pair_number(p, "initial_max_stream_data_bidi_remote", + params->initial_max_stream_data_bidi_remote); + *p++ = ','; + p = write_pair_number(p, "initial_max_stream_data_uni", + params->initial_max_stream_data_uni); + *p++ = ','; + p = write_pair_number(p, "initial_max_streams_bidi", + params->initial_max_streams_bidi); + *p++ = ','; + p = write_pair_number(p, "initial_max_streams_uni", + params->initial_max_streams_uni); + if (params->preferred_address_present) { + *p++ = ','; + paddr = ¶ms->preferred_address; + p = write_string(p, "preferred_address"); + *p++ = ':'; + *p++ = '{'; + p = write_pair_hex(p, "ip_v4", paddr->ipv4_addr, sizeof(paddr->ipv4_addr)); + *p++ = ','; + p = write_pair_number(p, "port_v4", paddr->ipv4_port); + *p++ = ','; + p = write_pair_hex(p, "ip_v6", paddr->ipv6_addr, sizeof(paddr->ipv6_addr)); + *p++ = ','; + p = write_pair_number(p, "port_v6", paddr->ipv6_port); + *p++ = ','; + p = write_pair_cid(p, "connection_id", &paddr->cid); + *p++ = ','; + p = write_pair_hex(p, "stateless_reset_token", paddr->stateless_reset_token, + sizeof(paddr->stateless_reset_token)); + *p++ = '}'; + } + *p++ = ','; + p = write_pair_number(p, "max_datagram_frame_size", + params->max_datagram_frame_size); + p = write_verbatim(p, "}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, + (size_t)(p - buf)); +} + +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, + const ngtcp2_conn_stat *cstat) { + uint8_t buf[1024]; + uint8_t *p = buf; + + if (!qlog->write) { + return; + } + + *p++ = '{'; + p = qlog_write_time(qlog, p); + p = write_verbatim(p, ",\"name\":\"recovery:metrics_updated\",\"data\":{"); + + if (cstat->min_rtt != UINT64_MAX) { + p = write_pair_duration(p, "min_rtt", cstat->min_rtt); + *p++ = ','; + } + p = write_pair_duration(p, "smoothed_rtt", cstat->smoothed_rtt); + *p++ = ','; + p = write_pair_duration(p, "latest_rtt", cstat->latest_rtt); + *p++ = ','; + p = write_pair_duration(p, "rtt_variance", cstat->rttvar); + *p++ = ','; + p = write_pair_number(p, "pto_count", cstat->pto_count); + *p++ = ','; + p = write_pair_number(p, "congestion_window", cstat->cwnd); + *p++ = ','; + p = write_pair_number(p, "bytes_in_flight", cstat->bytes_in_flight); + if (cstat->ssthresh != UINT64_MAX) { + *p++ = ','; + p = write_pair_number(p, "ssthresh", cstat->ssthresh); + } + + p = write_verbatim(p, "}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, + (size_t)(p - buf)); +} + +void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent) { + uint8_t buf[256]; + uint8_t *p = buf; + ngtcp2_pkt_hd hd = {0}; + + if (!qlog->write) { + return; + } + + *p++ = '{'; + p = qlog_write_time(qlog, p); + p = write_verbatim( + p, ",\"name\":\"recovery:packet_lost\",\"data\":{\"header\":"); + + hd.type = ent->hd.type; + hd.flags = ent->hd.flags; + hd.pkt_num = ent->hd.pkt_num; + + p = write_pkt_hd(p, &hd); + p = write_verbatim(p, "}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, + (size_t)(p - buf)); +} + +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, + const ngtcp2_pkt_hd *hd) { + uint8_t buf[256]; + uint8_t *p = buf; + + if (!qlog->write) { + return; + } + + *p++ = '{'; + p = qlog_write_time(qlog, p); + p = write_verbatim( + p, ",\"name\":\"transport:packet_received\",\"data\":{\"header\":"); + p = write_pkt_hd(p, hd); + p = write_verbatim(p, "}}\n"); + + qlog->write(qlog->user_data, NGTCP2_QLOG_WRITE_FLAG_NONE, buf, + (size_t)(p - buf)); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h new file mode 100644 index 00000000000000..cb3c2063f766c8 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_qlog.h @@ -0,0 +1,144 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_QLOG_H +#define NGTCP2_QLOG_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_buf.h" +#include "ngtcp2_rtb.h" + +/* NGTCP2_QLOG_BUFLEN is the length of heap allocated buffer for + qlog. */ +#define NGTCP2_QLOG_BUFLEN 4096 + +typedef enum ngtcp2_qlog_side { + NGTCP2_QLOG_SIDE_LOCAL, + NGTCP2_QLOG_SIDE_REMOTE, +} ngtcp2_qlog_side; + +typedef struct ngtcp2_qlog { + /* write is a callback function to write qlog. */ + ngtcp2_qlog_write write; + /* ts is the initial timestamp */ + ngtcp2_tstamp ts; + /* last_ts is the timestamp observed last time. */ + ngtcp2_tstamp last_ts; + /* buf is a heap allocated buffer to write exclusively + packet_received and packet_sent. */ + ngtcp2_buf buf; + /* user_data is an opaque pointer which is passed to write + callback. */ + void *user_data; +} ngtcp2_qlog; + +/* + * ngtcp2_qlog_init initializes |qlog|. + */ +void ngtcp2_qlog_init(ngtcp2_qlog *qlog, ngtcp2_qlog_write write, + ngtcp2_tstamp ts, void *user_data); + +/* + * ngtcp2_qlog_start writes qlog preamble. + */ +void ngtcp2_qlog_start(ngtcp2_qlog *qlog, const ngtcp2_cid *odcid, int server); + +/* + * ngtcp2_qlog_end writes closing part of qlog. + */ +void ngtcp2_qlog_end(ngtcp2_qlog *qlog); + +/* + * ngtcp2_qlog_write_frame writes |fr| to qlog->buf. + * ngtcp2_qlog_pkt_received_start or ngtcp2_qlog_pkt_sent_start must + * be called before calling this function. + */ +void ngtcp2_qlog_write_frame(ngtcp2_qlog *qlog, const ngtcp2_frame *fr); + +/* + * ngtcp2_qlog_pkt_received_start starts to write packet_received + * event. It initializes qlog->buf. It writes qlog to qlog->buf. + * ngtcp2_qlog_pkt_received_end will flush the content of qlog->buf to + * write callback. + */ +void ngtcp2_qlog_pkt_received_start(ngtcp2_qlog *qlog); + +/* + * ngtcp2_qlog_pkt_received_end ends packet_received event and sends + * the content of qlog->buf to qlog->write callback. + */ +void ngtcp2_qlog_pkt_received_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen); + +/* + * ngtcp2_qlog_pkt_sent_start starts to write packet_sent event. It + * initializes qlog->buf. It writes qlog to qlog->buf. + * ngtcp2_qlog_pkt_sent_end will flush the content of qlog->buf to + * write callback. + */ +void ngtcp2_qlog_pkt_sent_start(ngtcp2_qlog *qlog); + +/* + * ngtcp2_qlog_pkt_sent_end ends packet_sent event and sends the + * content of qlog->buf to qlog->write callback. + */ +void ngtcp2_qlog_pkt_sent_end(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd, + size_t pktlen); + +/* + * ngtcp2_qlog_parameters_set_transport_params writes |params| to qlog + * as parameters_set event. |server| is nonzero if the local endpoint + * is server. If |local| is nonzero, it is "owner" field becomes + * "local", otherwise "remote". + */ +void ngtcp2_qlog_parameters_set_transport_params( + ngtcp2_qlog *qlog, const ngtcp2_transport_params *params, int server, + ngtcp2_qlog_side side); + +/* + * ngtcp2_qlog_metrics_updated writes metrics_updated event of + * recovery category. + */ +void ngtcp2_qlog_metrics_updated(ngtcp2_qlog *qlog, + const ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_qlog_pkt_lost writes packet_lost event. + */ +void ngtcp2_qlog_pkt_lost(ngtcp2_qlog *qlog, ngtcp2_rtb_entry *ent); + +/* + * ngtcp2_qlog_retry_pkt_received writes packet_received event for a + * received Retry packet. + */ +void ngtcp2_qlog_retry_pkt_received(ngtcp2_qlog *qlog, const ngtcp2_pkt_hd *hd); + +#endif /* NGTCP2_QLOG_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c new file mode 100644 index 00000000000000..9379496b7d4b53 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.c @@ -0,0 +1,61 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_range.h" +#include "ngtcp2_macro.h" + +void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end) { + r->begin = begin; + r->end = end; +} + +ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, + const ngtcp2_range *b) { + ngtcp2_range r = {0, 0}; + uint64_t begin = ngtcp2_max(a->begin, b->begin); + uint64_t end = ngtcp2_min(a->end, b->end); + if (begin < end) { + ngtcp2_range_init(&r, begin, end); + } + return r; +} + +uint64_t ngtcp2_range_len(const ngtcp2_range *r) { return r->end - r->begin; } + +int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b) { + return a->begin == b->begin && a->end == b->end; +} + +void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right, + const ngtcp2_range *a, const ngtcp2_range *b) { + /* Assume that b is included in a */ + left->begin = a->begin; + left->end = b->begin; + right->begin = b->end; + right->end = a->end; +} + +int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b) { + return a->end <= b->end; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h new file mode 100644 index 00000000000000..a776c4ec4768ce --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_range.h @@ -0,0 +1,80 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_RANGE_H +#define NGTCP2_RANGE_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +/* + * ngtcp2_range represents half-closed range [begin, end). + */ +typedef struct ngtcp2_range { + uint64_t begin; + uint64_t end; +} ngtcp2_range; + +/* + * ngtcp2_range_init initializes |r| with the range [|begin|, |end|). + */ +void ngtcp2_range_init(ngtcp2_range *r, uint64_t begin, uint64_t end); + +/* + * ngtcp2_range_intersect returns the intersection of |a| and |b|. If + * they do not overlap, it returns empty range. + */ +ngtcp2_range ngtcp2_range_intersect(const ngtcp2_range *a, + const ngtcp2_range *b); + +/* + * ngtcp2_range_len returns the length of |r|. + */ +uint64_t ngtcp2_range_len(const ngtcp2_range *r); + +/* + * ngtcp2_range_eq returns nonzero if |a| equals |b|, such that + * a->begin == b->begin, and a->end == b->end hold. + */ +int ngtcp2_range_eq(const ngtcp2_range *a, const ngtcp2_range *b); + +/* + * ngtcp2_range_cut returns the left and right range after removing + * |b| from |a|. This function assumes that |a| completely includes + * |b|. In other words, a->begin <= b->begin and b->end <= a->end + * hold. + */ +void ngtcp2_range_cut(ngtcp2_range *left, ngtcp2_range *right, + const ngtcp2_range *a, const ngtcp2_range *b); + +/* + * ngtcp2_range_not_after returns nonzero if the right edge of |a| + * does not go beyond of the right edge of |b|. + */ +int ngtcp2_range_not_after(const ngtcp2_range *a, const ngtcp2_range *b); + +#endif /* NGTCP2_RANGE_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h new file mode 100644 index 00000000000000..e392c34ebfedb7 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rcvry.h @@ -0,0 +1,42 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_RCVRY_H +#define NGTCP2_RCVRY_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +/* NGTCP2_PKT_THRESHOLD is kPacketThreshold described in + draft-ietf-quic-recovery-22. */ +#define NGTCP2_PKT_THRESHOLD 3 + +/* NGTCP2_GRANULARITY is kGranularity described in + draft-ietf-quic-recovery-17. */ +#define NGTCP2_GRANULARITY NGTCP2_MILLISECONDS + +#endif /* NGTCP2_RCVRY_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c new file mode 100644 index 00000000000000..e4deab1ff76b83 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.c @@ -0,0 +1,114 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_ringbuf.h" + +#include <assert.h> +#ifdef WIN32 +# include <intrin.h> +#endif + +#include "ngtcp2_macro.h" + +#if defined(_MSC_VER) && defined(_M_ARM64) +unsigned int __popcnt(unsigned int x) { + unsigned int c = 0; + for (; x; ++c) { + x &= x - 1; + } + return c; +} +#endif + +int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + const ngtcp2_mem *mem) { +#ifdef WIN32 + assert(1 == __popcnt((unsigned int)nmemb)); +#else + assert(1 == __builtin_popcount((unsigned int)nmemb)); +#endif + + rb->buf = ngtcp2_mem_malloc(mem, nmemb * size); + if (rb->buf == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rb->mem = mem; + rb->nmemb = nmemb; + rb->size = size; + rb->first = 0; + rb->len = 0; + + return 0; +} + +void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb) { + if (rb == NULL) { + return; + } + + ngtcp2_mem_free(rb->mem, rb->buf); +} + +void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb) { + rb->first = (rb->first - 1) & (rb->nmemb - 1); + rb->len = ngtcp2_min(rb->nmemb, rb->len + 1); + + return (void *)&rb->buf[rb->first * rb->size]; +} + +void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb) { + size_t offset = (rb->first + rb->len) & (rb->nmemb - 1); + + if (rb->len == rb->nmemb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + } else { + ++rb->len; + } + + return (void *)&rb->buf[offset * rb->size]; +} + +void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb) { + rb->first = (rb->first + 1) & (rb->nmemb - 1); + --rb->len; +} + +void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb) { + assert(rb->len); + --rb->len; +} + +void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len) { + assert(len <= rb->nmemb); + rb->len = len; +} + +void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset) { + assert(offset < rb->len); + offset = (rb->first + offset) & (rb->nmemb - 1); + return &rb->buf[offset * rb->size]; +} + +int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb) { return rb->len == rb->nmemb; } diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h new file mode 100644 index 00000000000000..c6e1421518342b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_ringbuf.h @@ -0,0 +1,110 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_RINGBUF_H +#define NGTCP2_RINGBUF_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +typedef struct ngtcp2_ringbuf { + /* buf points to the underlying buffer. */ + uint8_t *buf; + const ngtcp2_mem *mem; + /* nmemb is the number of elements that can be stored in this ring + buffer. */ + size_t nmemb; + /* size is the size of each element. */ + size_t size; + /* first is the offset to the first element. */ + size_t first; + /* len is the number of elements actually stored. */ + size_t len; +} ngtcp2_ringbuf; + +/* + * ngtcp2_ringbuf_init initializes |rb|. |nmemb| is the number of + * elements that can be stored in this buffer. |size| is the size of + * each element. |size| must be power of 2. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_ringbuf_init(ngtcp2_ringbuf *rb, size_t nmemb, size_t size, + const ngtcp2_mem *mem); + +/* + * ngtcp2_ringbuf_free frees resources allocated for |rb|. This + * function does not free the memory pointed by |rb|. + */ +void ngtcp2_ringbuf_free(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_push_front moves the offset to the first element in + the buffer backward, and returns the pointer to the element. + Caller can store data to the buffer pointed by the returned + pointer. If this action exceeds the capacity of the ring buffer, + the last element is silently overwritten, and rb->len remains + unchanged. */ +void *ngtcp2_ringbuf_push_front(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_push_back moves the offset to the last element in + the buffer forward, and returns the pointer to the element. Caller + can store data to the buffer pointed by the returned pointer. If + this action exceeds the capacity of the ring buffer, the first + element is silently overwritten, and rb->len remains unchanged. */ +void *ngtcp2_ringbuf_push_back(ngtcp2_ringbuf *rb); + +/* + * ngtcp2_ringbuf_pop_front removes first element in |rb|. + */ +void ngtcp2_ringbuf_pop_front(ngtcp2_ringbuf *rb); + +/* + * ngtcp2_ringbuf_pop_back removes the last element in |rb|. + */ +void ngtcp2_ringbuf_pop_back(ngtcp2_ringbuf *rb); + +/* ngtcp2_ringbuf_resize changes the number of elements stored. This + does not change the capacity of the underlying buffer. */ +void ngtcp2_ringbuf_resize(ngtcp2_ringbuf *rb, size_t len); + +/* ngtcp2_ringbuf_get returns the pointer to the element at + |offset|. */ +void *ngtcp2_ringbuf_get(ngtcp2_ringbuf *rb, size_t offset); + +/* ngtcp2_ringbuf_len returns the number of elements stored. */ +#define ngtcp2_ringbuf_len(RB) ((RB)->len) + +/* ngtcp2_ringbuf_full returns nonzero if |rb| is full. */ +int ngtcp2_ringbuf_full(ngtcp2_ringbuf *rb); + +#endif /* NGTCP2_RINGBUF_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c new file mode 100644 index 00000000000000..499c07ec6be247 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.c @@ -0,0 +1,327 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_rob.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_macro.h" + +int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, + const ngtcp2_mem *mem) { + *pg = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_gap)); + if (*pg == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pg)->range.begin = begin; + (*pg)->range.end = end; + + return 0; +} + +void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, g); +} + +int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, + const ngtcp2_mem *mem) { + *pd = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_rob_data) + chunk); + if (*pd == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pd)->range.begin = offset; + (*pd)->range.end = offset + chunk; + (*pd)->begin = (uint8_t *)(*pd) + sizeof(ngtcp2_rob_data); + (*pd)->end = (*pd)->begin + chunk; + + return 0; +} + +void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem) { + ngtcp2_mem_free(mem, d); +} + +int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem) { + int rv; + ngtcp2_rob_gap *g; + + rv = ngtcp2_ksl_init(&rob->gapksl, ngtcp2_ksl_range_compar, + sizeof(ngtcp2_range), mem); + if (rv != 0) { + goto fail_gapksl_ksl_init; + } + + rv = ngtcp2_rob_gap_new(&g, 0, UINT64_MAX, mem); + if (rv != 0) { + goto fail_rob_gap_new; + } + + rv = ngtcp2_ksl_insert(&rob->gapksl, NULL, &g->range, g); + if (rv != 0) { + goto fail_gapksl_ksl_insert; + } + + rv = ngtcp2_ksl_init(&rob->dataksl, ngtcp2_ksl_range_compar, + sizeof(ngtcp2_range), mem); + if (rv != 0) { + goto fail_dataksl_ksl_init; + } + + rob->chunk = chunk; + rob->mem = mem; + + return 0; + +fail_dataksl_ksl_init: +fail_gapksl_ksl_insert: + ngtcp2_rob_gap_del(g, mem); +fail_rob_gap_new: + ngtcp2_ksl_free(&rob->gapksl); +fail_gapksl_ksl_init: + return rv; +} + +void ngtcp2_rob_free(ngtcp2_rob *rob) { + ngtcp2_ksl_it it; + + if (rob == NULL) { + return; + } + + for (it = ngtcp2_ksl_begin(&rob->dataksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_rob_data_del(ngtcp2_ksl_it_get(&it), rob->mem); + } + + for (it = ngtcp2_ksl_begin(&rob->gapksl); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_rob_gap_del(ngtcp2_ksl_it_get(&it), rob->mem); + } + + ngtcp2_ksl_free(&rob->dataksl); + ngtcp2_ksl_free(&rob->gapksl); +} + +static int rob_write_data(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, + size_t len) { + size_t n; + int rv; + ngtcp2_rob_data *d; + ngtcp2_range range = {offset, offset + len}; + ngtcp2_ksl_it it; + + for (it = ngtcp2_ksl_lower_bound_compar(&rob->dataksl, &range, + ngtcp2_ksl_range_exclusive_compar); + len; ngtcp2_ksl_it_next(&it)) { + if (ngtcp2_ksl_it_end(&it)) { + d = NULL; + } else { + d = ngtcp2_ksl_it_get(&it); + } + + if (d == NULL || offset < d->range.begin) { + rv = ngtcp2_rob_data_new(&d, (offset / rob->chunk) * rob->chunk, + rob->chunk, rob->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_ksl_insert(&rob->dataksl, &it, &d->range, d); + if (rv != 0) { + ngtcp2_rob_data_del(d, rob->mem); + return rv; + } + } + + n = (size_t)ngtcp2_min((uint64_t)len, d->range.begin + rob->chunk - offset); + memcpy(d->begin + (offset - d->range.begin), data, n); + offset += n; + data += n; + len -= n; + } + + return 0; +} + +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_ksl_it it; + + it = ngtcp2_ksl_lower_bound_compar(&rob->gapksl, &q, + ngtcp2_ksl_range_exclusive_compar); + + for (; !ngtcp2_ksl_it_end(&it);) { + g = ngtcp2_ksl_it_get(&it); + + m = ngtcp2_range_intersect(&q, &g->range); + if (!ngtcp2_range_len(&m)) { + break; + } + if (ngtcp2_range_eq(&g->range, &m)) { + ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); + ngtcp2_rob_gap_del(g, rob->mem); + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + (size_t)ngtcp2_range_len(&m)); + if (rv != 0) { + return rv; + } + + continue; + } + ngtcp2_range_cut(&l, &r, &g->range, &m); + if (ngtcp2_range_len(&l)) { + ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &l); + g->range = l; + + if (ngtcp2_range_len(&r)) { + ngtcp2_rob_gap *ng; + rv = ngtcp2_rob_gap_new(&ng, r.begin, r.end, rob->mem); + if (rv != 0) { + return rv; + } + rv = ngtcp2_ksl_insert(&rob->gapksl, &it, &ng->range, ng); + if (rv != 0) { + ngtcp2_rob_gap_del(ng, rob->mem); + return rv; + } + } + } else if (ngtcp2_range_len(&r)) { + ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r); + g->range = r; + } + rv = rob_write_data(rob, m.begin, data + (m.begin - offset), + (size_t)ngtcp2_range_len(&m)); + if (rv != 0) { + return rv; + } + ngtcp2_ksl_it_next(&it); + } + return 0; +} + +int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset) { + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_ksl_it it; + + it = ngtcp2_ksl_begin(&rob->gapksl); + + for (; !ngtcp2_ksl_it_end(&it);) { + g = ngtcp2_ksl_it_get(&it); + if (offset <= g->range.begin) { + break; + } + if (offset < g->range.end) { + ngtcp2_range r = {offset, g->range.end}; + ngtcp2_ksl_update_key(&rob->gapksl, &g->range, &r); + g->range.begin = offset; + break; + } + ngtcp2_ksl_remove(&rob->gapksl, &it, &g->range); + ngtcp2_rob_gap_del(g, rob->mem); + } + + it = ngtcp2_ksl_begin(&rob->dataksl); + + for (; !ngtcp2_ksl_it_end(&it);) { + d = ngtcp2_ksl_it_get(&it); + if (offset < d->range.begin + rob->chunk) { + return 0; + } + ngtcp2_ksl_remove(&rob->dataksl, &it, &d->range); + ngtcp2_rob_data_del(d, rob->mem); + } + + return 0; +} + +size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, + uint64_t offset) { + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_ksl_it it; + + it = ngtcp2_ksl_begin(&rob->gapksl); + if (ngtcp2_ksl_it_end(&it)) { + return 0; + } + + g = ngtcp2_ksl_it_get(&it); + + if (g->range.begin <= offset) { + return 0; + } + + it = ngtcp2_ksl_begin(&rob->dataksl); + d = ngtcp2_ksl_it_get(&it); + + assert(d); + assert(d->range.begin <= offset); + assert(offset < d->range.begin + rob->chunk); + + *pdest = d->begin + (offset - d->range.begin); + + return (size_t)(ngtcp2_min(g->range.begin, d->range.begin + rob->chunk) - + offset); +} + +void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len) { + ngtcp2_ksl_it it; + ngtcp2_rob_data *d; + + it = ngtcp2_ksl_begin(&rob->dataksl); + d = ngtcp2_ksl_it_get(&it); + + assert(d); + + if (offset + len < d->range.begin + rob->chunk) { + return; + } + + ngtcp2_ksl_remove(&rob->dataksl, NULL, &d->range); + ngtcp2_rob_data_del(d, rob->mem); +} + +uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob) { + ngtcp2_ksl_it it = ngtcp2_ksl_begin(&rob->gapksl); + ngtcp2_rob_gap *g; + + if (ngtcp2_ksl_it_end(&it)) { + return UINT64_MAX; + } + + g = ngtcp2_ksl_it_get(&it); + + return g->range.begin; +} + +int ngtcp2_rob_data_buffered(ngtcp2_rob *rob) { + return ngtcp2_ksl_len(&rob->dataksl) != 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h new file mode 100644 index 00000000000000..c7688df4542956 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rob.h @@ -0,0 +1,197 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_ROB_H +#define NGTCP2_ROB_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" +#include "ngtcp2_range.h" +#include "ngtcp2_ksl.h" + +/* + * ngtcp2_rob_gap represents the gap, which is the range of stream + * data that is not received yet. + */ +typedef struct ngtcp2_rob_gap { + /* range is the range of this gap. */ + ngtcp2_range range; +} ngtcp2_rob_gap; + +/* + * ngtcp2_rob_gap_new allocates new ngtcp2_rob_gap object, and assigns + * its pointer to |*pg|. The caller should call ngtcp2_rob_gap_del to + * delete it when it is no longer used. The range of the gap is + * [begin, end). |mem| is custom memory allocator to allocate memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rob_gap_new(ngtcp2_rob_gap **pg, uint64_t begin, uint64_t end, + const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_gap_del deallocates |g|. It deallocates the memory + * pointed by |g| it self. |mem| is custom memory allocator to + * deallocate memory. + */ +void ngtcp2_rob_gap_del(ngtcp2_rob_gap *g, const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_data holds the buffered stream data. + */ +typedef struct ngtcp2_rob_data { + /* range is the range of this gap. */ + ngtcp2_range range; + /* begin points to the buffer. */ + uint8_t *begin; + /* end points to the one beyond of the last byte of the buffer */ + uint8_t *end; +} ngtcp2_rob_data; + +/* + * ngtcp2_rob_data_new allocates new ngtcp2_rob_data object, and + * assigns its pointer to |*pd|. The caller should call + * ngtcp2_rob_data_del to delete it when it is no longer used. + * |offset| is the stream offset of the first byte of this data. + * |chunk| is the size of the buffer. |offset| must be multiple of + * |chunk|. |mem| is custom memory allocator to allocate memory. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rob_data_new(ngtcp2_rob_data **pd, uint64_t offset, size_t chunk, + const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_data_del deallocates |d|. It deallocates the memory + * pointed by |d| itself. |mem| is custom memory allocator to + * deallocate memory. + */ +void ngtcp2_rob_data_del(ngtcp2_rob_data *d, const ngtcp2_mem *mem); + +/* + * ngtcp2_rob is the reorder buffer which reassembles stream data + * received in out of order. + */ +typedef struct ngtcp2_rob { + /* gapksl maintains the range of offset which is not received + yet. Initially, its range is [0, UINT64_MAX). */ + ngtcp2_ksl gapksl; + /* dataksl maintains the list of buffers which store received data + ordered by stream offset. */ + ngtcp2_ksl dataksl; + /* mem is custom memory allocator */ + const ngtcp2_mem *mem; + /* chunk is the size of each buffer in data field */ + size_t chunk; +} ngtcp2_rob; + +/* + * ngtcp2_rob_init initializes |rob|. |chunk| is the size of buffer + * per chunk. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rob_init(ngtcp2_rob *rob, size_t chunk, const ngtcp2_mem *mem); + +/* + * ngtcp2_rob_free frees resources allocated for |rob|. + */ +void ngtcp2_rob_free(ngtcp2_rob *rob); + +/* + * ngtcp2_rob_push adds new data of length |datalen| at the stream + * offset |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rob_push(ngtcp2_rob *rob, uint64_t offset, const uint8_t *data, + size_t datalen); + +/* + * ngtcp2_rob_remove_prefix removes gap up to |offset|, exclusive. It + * also removes data buffer if it is completely included in |offset|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rob_remove_prefix(ngtcp2_rob *rob, uint64_t offset); + +/* + * ngtcp2_rob_data_at stores the pointer to the buffer of stream + * offset |offset| to |*pdest| if it is available, and returns the + * valid length of available data. If no data is available, it + * returns 0. + */ +size_t ngtcp2_rob_data_at(ngtcp2_rob *rob, const uint8_t **pdest, + uint64_t offset); + +/* + * ngtcp2_rob_pop clears data at stream offset |offset| of length + * |len|. + * + * |offset| must be the offset given in ngtcp2_rob_data_at. |len| + * must be the return value of ngtcp2_rob_data_at when |offset| is + * passed. + * + * Caller should call this function from offset 0 in non-decreasing + * order. + */ +void ngtcp2_rob_pop(ngtcp2_rob *rob, uint64_t offset, size_t len); + +/* + * ngtcp2_rob_first_gap_offset returns the offset to the first gap. + * If there is no gap, it returns UINT64_MAX. + */ +uint64_t ngtcp2_rob_first_gap_offset(ngtcp2_rob *rob); + +/* + * ngtcp2_rob_data_buffered returns nonzero if any data is buffered. + */ +int ngtcp2_rob_data_buffered(ngtcp2_rob *rob); + +#endif /* NGTCP2_ROB_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c new file mode 100644 index 00000000000000..e546fdf85c623b --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.c @@ -0,0 +1,107 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_rst.h" +#include "ngtcp2_rtb.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_macro.h" + +void ngtcp2_rs_init(ngtcp2_rs *rs) { + rs->interval = UINT64_MAX; + rs->delivered = 0; + rs->prior_delivered = 0; + rs->prior_ts = 0; + rs->send_elapsed = 0; + rs->ack_elapsed = 0; + rs->is_app_limited = 0; +} + +void ngtcp2_rst_init(ngtcp2_rst *rst) { + ngtcp2_rs_init(&rst->rs); + rst->delivered = 0; + rst->delivered_ts = 0; + rst->first_sent_ts = 0; + rst->app_limited = 0; +} + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, + const ngtcp2_conn_stat *cstat) { + if (cstat->bytes_in_flight == 0) { + rst->first_sent_ts = rst->delivered_ts = ent->ts; + } + ent->rst.first_sent_ts = rst->first_sent_ts; + ent->rst.delivered_ts = rst->delivered_ts; + ent->rst.delivered = rst->delivered; + ent->rst.is_app_limited = rst->app_limited != 0; +} + +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { + ngtcp2_rs *rs = &rst->rs; + + if (rst->app_limited && rst->delivered > rst->app_limited) { + rst->app_limited = 0; + } + + if (rs->prior_ts == 0) { + return 0; + } + + rs->interval = ngtcp2_max(rs->send_elapsed, rs->ack_elapsed); + + rs->delivered = rst->delivered - rs->prior_delivered; + + if (rs->interval < cstat->min_rtt) { + rs->interval = UINT64_MAX; + return 0; + } + + if (rs->interval) { + cstat->delivery_rate_sec = rs->delivered * NGTCP2_SECONDS / rs->interval; + } + + return 0; +} + +void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, + ngtcp2_tstamp ts) { + ngtcp2_rs *rs = &rst->rs; + + rst->delivered += ent->pktlen; + rst->delivered_ts = ts; + + if (ent->rst.delivered > rs->prior_delivered) { + rs->prior_delivered = ent->rst.delivered; + rs->prior_ts = ent->rst.delivered_ts; + rs->is_app_limited = ent->rst.is_app_limited; + rs->send_elapsed = ent->ts - ent->rst.first_sent_ts; + rs->ack_elapsed = rst->delivered_ts - ent->rst.delivered_ts; + rst->first_sent_ts = ent->ts; + } +} + +void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat) { + (void)rst; + (void)cstat; + /* TODO Not implemented */ +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h new file mode 100644 index 00000000000000..14aae998ebdf65 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rst.h @@ -0,0 +1,74 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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_RST_H +#define NGTCP2_RST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; + +/** + * @struct + * + * ngtcp2_rs contains connection state for delivery rate estimation. + */ +typedef struct ngtcp2_rs { + ngtcp2_duration interval; + uint64_t delivered; + uint64_t prior_delivered; + ngtcp2_tstamp prior_ts; + ngtcp2_duration send_elapsed; + ngtcp2_duration ack_elapsed; + int is_app_limited; +} ngtcp2_rs; + +void ngtcp2_rs_init(ngtcp2_rs *rs); + +/* + * ngtcp2_rst implements delivery rate estimation described in + * https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00 + */ +typedef struct ngtcp2_rst { + ngtcp2_rs rs; + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + uint64_t app_limited; +} ngtcp2_rst; + +void ngtcp2_rst_init(ngtcp2_rst *rst); + +void ngtcp2_rst_on_pkt_sent(ngtcp2_rst *rst, ngtcp2_rtb_entry *ent, + const ngtcp2_conn_stat *cstat); +int ngtcp2_rst_on_ack_recv(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); +void ngtcp2_rst_update_rate_sample(ngtcp2_rst *rst, const ngtcp2_rtb_entry *ent, + ngtcp2_tstamp ts); +void ngtcp2_rst_update_app_limited(ngtcp2_rst *rst, ngtcp2_conn_stat *cstat); + +#endif /* NGTCP2_RST_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c new file mode 100644 index 00000000000000..1ef67ccbc6af5d --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.c @@ -0,0 +1,1301 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_rtb.h" + +#include <assert.h> +#include <string.h> + +#include "ngtcp2_macro.h" +#include "ngtcp2_conn.h" +#include "ngtcp2_log.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_cc.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_rst.h" + +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_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, + const ngtcp2_mem *mem) { + *pfrc = ngtcp2_mem_malloc(mem, sizeof(ngtcp2_frame_chain) + extralen); + if (*pfrc == NULL) { + return NGTCP2_ERR_NOMEM; + } + + ngtcp2_frame_chain_init(*pfrc); + + return 0; +} + +int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem) { + size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); + size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_stream); + + if (datacnt > 0 && need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } + + return ngtcp2_frame_chain_new(pfrc, mem); +} + +int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem) { + size_t need = sizeof(ngtcp2_vec) * (datacnt - 1); + size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_crypto); + + if (datacnt > 0 && need > avail) { + return ngtcp2_frame_chain_extralen_new(pfrc, need - avail, mem); + } + + return ngtcp2_frame_chain_new(pfrc, mem); +} + +int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + const ngtcp2_mem *mem) { + size_t avail = sizeof(ngtcp2_frame) - sizeof(ngtcp2_new_token); + int rv; + uint8_t *p; + ngtcp2_frame *fr; + + if (token->len > avail) { + rv = ngtcp2_frame_chain_extralen_new(pfrc, token->len - avail, mem); + } else { + rv = ngtcp2_frame_chain_new(pfrc, mem); + } + if (rv != 0) { + return rv; + } + + fr = &(*pfrc)->fr; + fr->type = NGTCP2_FRAME_NEW_TOKEN; + + p = (uint8_t *)(*pfrc) + sizeof(ngtcp2_new_token); + memcpy(p, token->base, token->len); + + ngtcp2_vec_init(&fr->new_token.token, p, token->len); + + return 0; +} + +void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem) { + ngtcp2_frame_chain_binder *binder; + + if (frc == NULL) { + return; + } + + binder = frc->binder; + if (binder && --binder->refcount == 0) { + ngtcp2_mem_free(mem, binder); + } + + ngtcp2_mem_free(mem, frc); +} + +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc) { + frc->next = NULL; + frc->binder = NULL; +} + +void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain *next; + + for (; frc;) { + next = frc->next; + ngtcp2_frame_chain_del(frc, mem); + frc = next; + } +} + +int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder, + const ngtcp2_mem *mem) { + *pbinder = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_frame_chain_binder)); + if (*pbinder == NULL) { + return NGTCP2_ERR_NOMEM; + } + + return 0; +} + +int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain_binder *binder; + int rv; + + assert(b->binder == NULL); + + if (a->binder == NULL) { + rv = ngtcp2_frame_chain_binder_new(&binder, mem); + if (rv != 0) { + return rv; + } + + a->binder = binder; + ++a->binder->refcount; + } + + b->binder = a->binder; + ++b->binder->refcount; + + return 0; +} + +int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint8_t flags, const ngtcp2_mem *mem) { + (*pent) = ngtcp2_mem_calloc(mem, 1, sizeof(ngtcp2_rtb_entry)); + if (*pent == NULL) { + return NGTCP2_ERR_NOMEM; + } + + (*pent)->hd.pkt_num = hd->pkt_num; + (*pent)->hd.type = hd->type; + (*pent)->hd.flags = hd->flags; + (*pent)->frc = frc; + (*pent)->ts = ts; + (*pent)->lost_ts = UINT64_MAX; + (*pent)->pktlen = pktlen; + (*pent)->flags = flags; + (*pent)->next = NULL; + + return 0; +} + +void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem) { + if (ent == NULL) { + return; + } + + ngtcp2_frame_chain_list_del(ent->frc, mem); + + ngtcp2_mem_free(mem, ent); +} + +static int greater(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *(int64_t *)lhs > *(int64_t *)rhs; +} + +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, + ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, + ngtcp2_log *log, ngtcp2_qlog *qlog, + const ngtcp2_mem *mem) { + ngtcp2_ksl_init(&rtb->ents, greater, sizeof(int64_t), mem); + rtb->crypto = crypto; + rtb->rst = rst; + rtb->cc = cc; + rtb->log = log; + rtb->qlog = qlog; + rtb->mem = mem; + rtb->largest_acked_tx_pkt_num = -1; + rtb->num_ack_eliciting = 0; + rtb->num_retransmittable = 0; + rtb->probe_pkt_left = 0; + rtb->pktns_id = pktns_id; + rtb->cc_pkt_num = 0; + rtb->cc_bytes_in_flight = 0; + rtb->persistent_congestion_start_ts = UINT64_MAX; + rtb->num_lost_pkts = 0; +} + +void ngtcp2_rtb_free(ngtcp2_rtb *rtb) { + ngtcp2_ksl_it it; + + if (rtb == NULL) { + return; + } + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ngtcp2_rtb_entry_del(ngtcp2_ksl_it_get(&it), rtb->mem); + } + + ngtcp2_ksl_free(&rtb->ents); +} + +static void rtb_on_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat) { + ngtcp2_rst_on_pkt_sent(rtb->rst, ent, cstat); + + assert(rtb->cc_pkt_num <= ent->hd.pkt_num); + + cstat->bytes_in_flight += ent->pktlen; + rtb->cc_bytes_in_flight += ent->pktlen; + + ngtcp2_rst_update_app_limited(rtb->rst, cstat); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + ++rtb->num_ack_eliciting; + } + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) { + ++rtb->num_retransmittable; + } +} + +static void rtb_on_remove(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat) { + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { + assert(rtb->num_lost_pkts); + --rtb->num_lost_pkts; + return; + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + assert(rtb->num_ack_eliciting); + --rtb->num_ack_eliciting; + } + + if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE) && + !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) { + assert(rtb->num_retransmittable); + --rtb->num_retransmittable; + } + + if (rtb->cc_pkt_num <= ent->hd.pkt_num) { + assert(cstat->bytes_in_flight >= ent->pktlen); + cstat->bytes_in_flight -= ent->pktlen; + + assert(rtb->cc_bytes_in_flight >= ent->pktlen); + rtb->cc_bytes_in_flight -= ent->pktlen; + } +} + +/* + * rtb_reclaim_frame queues unacknowledged frames included in |ent| + * for retransmission. The re-queued frames are not deleted from + * |ent|. It returns the number of frames queued. + */ +static ngtcp2_ssize rtb_reclaim_frame(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_rtb_entry *ent) { + ngtcp2_frame_chain *frc, *nfrc, **pfrc = &pktns->tx.frq; + ngtcp2_frame *fr; + ngtcp2_strm *strm; + ngtcp2_range gap, range; + size_t num_reclaimed = 0; + int rv; + + /* PADDING only (or PADDING + ACK ) packets will have NULL + ent->frc. */ + /* TODO Reconsider the order of pfrc */ + for (frc = ent->frc; frc; frc = frc->next) { + fr = &frc->fr; + /* Check that a late ACK acknowledged this frame. */ + if (frc->binder && + (frc->binder->flags & NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK)) { + continue; + } + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + strm = ngtcp2_conn_find_stream(conn, fr->stream.stream_id); + if (strm == NULL) { + continue; + } + + gap = ngtcp2_strm_get_unacked_range_after(strm, fr->stream.offset); + + range.begin = fr->stream.offset; + range.end = fr->stream.offset + + ngtcp2_vec_len(fr->stream.data, fr->stream.datacnt); + range = ngtcp2_range_intersect(&range, &gap); + if (ngtcp2_range_len(&range) == 0 && + (!fr->stream.fin || (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED))) { + continue; + } + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->stream.datacnt, + rtb->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr = *fr; + ngtcp2_vec_copy(nfrc->fr.stream.data, fr->stream.data, + fr->stream.datacnt); + + rv = ngtcp2_strm_streamfrq_push(strm, nfrc); + if (rv != 0) { + ngtcp2_frame_chain_del(nfrc, conn->mem); + return rv; + } + if (!ngtcp2_strm_is_tx_queued(strm)) { + strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn); + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + return rv; + } + } + + ++num_reclaimed; + + continue; + case NGTCP2_FRAME_CRYPTO: + /* Don't resend CRYPTO frame if the whole region it contains has + been acknowledged */ + gap = ngtcp2_strm_get_unacked_range_after(rtb->crypto, fr->crypto.offset); + + range.begin = fr->crypto.offset; + range.end = fr->crypto.offset + + ngtcp2_vec_len(fr->crypto.data, fr->crypto.datacnt); + range = ngtcp2_range_intersect(&range, &gap); + if (ngtcp2_range_len(&range) == 0) { + continue; + } + + rv = ngtcp2_frame_chain_crypto_datacnt_new(&nfrc, fr->crypto.datacnt, + rtb->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr = *fr; + ngtcp2_vec_copy(nfrc->fr.crypto.data, fr->crypto.data, + fr->crypto.datacnt); + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + &nfrc->fr.crypto.offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, conn->mem); + return rv; + } + + ++num_reclaimed; + + continue; + case NGTCP2_FRAME_NEW_TOKEN: + rv = ngtcp2_frame_chain_new_token_new(&nfrc, &fr->new_token.token, + rtb->mem); + if (rv != 0) { + return rv; + } + + rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem); + if (rv != 0) { + return rv; + } + + break; + default: + rv = ngtcp2_frame_chain_new(&nfrc, rtb->mem); + if (rv != 0) { + return rv; + } + + nfrc->fr = *fr; + + rv = ngtcp2_bind_frame_chains(frc, nfrc, rtb->mem); + if (rv != 0) { + return rv; + } + + break; + } + + ++num_reclaimed; + + nfrc->next = *pfrc; + *pfrc = nfrc; + pfrc = &nfrc->next; + } + + return (ngtcp2_ssize)num_reclaimed; +} + +static int rtb_on_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, + ngtcp2_rtb_entry *ent, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_tstamp ts) { + int rv; + ngtcp2_ssize reclaimed; + + ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, + ent->ts); + + if (rtb->qlog) { + ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + } + + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE)) { + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); + + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; + + ++rtb->num_lost_pkts; + + ngtcp2_ksl_it_next(it); + + return 0; + } + + if (ent->frc) { + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(UINT64_MAX == ent->lost_ts); + + reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); + if (reclaimed < 0) { + return (int)reclaimed; + } + + if (reclaimed) { + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = ts; + + ++rtb->num_lost_pkts; + + ngtcp2_ksl_it_next(it); + + return 0; + } + } + } else { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 + " is a probe packet, no retransmission is necessary", + ent->hd.pkt_num); + } + + rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); + assert(0 == rv); + + ngtcp2_rtb_entry_del(ent, rtb->mem); + + return 0; +} + +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat) { + int rv; + + rv = ngtcp2_ksl_insert(&rtb->ents, NULL, &ent->hd.pkt_num, ent); + if (rv != 0) { + return rv; + } + + rtb_on_add(rtb, ent, cstat); + + return 0; +} + +ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb) { + return ngtcp2_ksl_begin(&rtb->ents); +} + +static void rtb_remove(ngtcp2_rtb *rtb, ngtcp2_ksl_it *it, + ngtcp2_rtb_entry **pent, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat) { + int rv; + rv = ngtcp2_ksl_remove(&rtb->ents, it, &ent->hd.pkt_num); + assert(0 == rv); + rtb_on_remove(rtb, ent, cstat); + + assert(ent->next == NULL); + + ngtcp2_list_insert(ent, pent); +} + +static int rtb_process_acked_pkt(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn *conn) { + ngtcp2_frame_chain *frc; + uint64_t prev_stream_offset, stream_offset; + ngtcp2_strm *strm; + int rv; + uint64_t datalen; + ngtcp2_strm *crypto = rtb->crypto; + ngtcp2_crypto_level crypto_level; + + for (frc = ent->frc; frc; frc = frc->next) { + if (frc->binder) { + frc->binder->flags |= NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK; + } + + switch (frc->fr.type) { + case NGTCP2_FRAME_STREAM: + strm = ngtcp2_conn_find_stream(conn, frc->fr.stream.stream_id); + if (strm == NULL) { + break; + } + + if (frc->fr.stream.fin) { + strm->flags |= NGTCP2_STRM_FLAG_FIN_ACKED; + } + + prev_stream_offset = ngtcp2_strm_get_acked_offset(strm); + rv = ngtcp2_strm_ack_data( + strm, frc->fr.stream.offset, + ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); + if (rv != 0) { + return rv; + } + + if (conn->callbacks.acked_stream_data_offset) { + stream_offset = ngtcp2_strm_get_acked_offset(strm); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0 && !frc->fr.stream.fin) { + break; + } + + rv = conn->callbacks.acked_stream_data_offset( + conn, strm->stream_id, prev_stream_offset, datalen, conn->user_data, + strm->stream_user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_CRYPTO: + prev_stream_offset = ngtcp2_strm_get_acked_offset(crypto); + rv = ngtcp2_strm_ack_data( + crypto, frc->fr.crypto.offset, + ngtcp2_vec_len(frc->fr.crypto.data, frc->fr.crypto.datacnt)); + if (rv != 0) { + return rv; + } + + if (conn->callbacks.acked_crypto_offset) { + stream_offset = ngtcp2_strm_get_acked_offset(crypto); + datalen = stream_offset - prev_stream_offset; + if (datalen == 0) { + break; + } + + switch (rtb->pktns_id) { + case NGTCP2_PKTNS_ID_INITIAL: + crypto_level = NGTCP2_CRYPTO_LEVEL_INITIAL; + break; + case NGTCP2_PKTNS_ID_HANDSHAKE: + crypto_level = NGTCP2_CRYPTO_LEVEL_HANDSHAKE; + break; + case NGTCP2_PKTNS_ID_APPLICATION: + crypto_level = NGTCP2_CRYPTO_LEVEL_APPLICATION; + break; + default: + assert(0); + } + + rv = conn->callbacks.acked_crypto_offset( + conn, crypto_level, prev_stream_offset, datalen, conn->user_data); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + } + break; + case NGTCP2_FRAME_RESET_STREAM: + strm = ngtcp2_conn_find_stream(conn, frc->fr.reset_stream.stream_id); + if (strm == NULL) { + break; + } + strm->flags |= NGTCP2_STRM_FLAG_RST_ACKED; + rv = ngtcp2_conn_close_stream_if_shut_rdwr(conn, strm, NGTCP2_NO_ERROR); + if (rv != 0) { + return rv; + } + break; + case NGTCP2_FRAME_RETIRE_CONNECTION_ID: + assert(conn->dcid.num_retire_queued); + --conn->dcid.num_retire_queued; + break; + } + } + return 0; +} + +static void rtb_on_pkt_acked(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat, ngtcp2_tstamp ts) { + ngtcp2_cc *cc = rtb->cc; + ngtcp2_cc_pkt pkt; + + ngtcp2_rst_update_rate_sample(rtb->rst, ent, ts); + + cc->on_pkt_acked(cc, cstat, + ngtcp2_cc_pkt_init(&pkt, ent->hd.pkt_num, ent->pktlen, + rtb->pktns_id, ent->ts), + ts); + + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) && + (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING)) { + cstat->pto_count = 0; + } +} + +static void conn_verify_ecn(ngtcp2_conn *conn, ngtcp2_pktns *pktns, + ngtcp2_cc *cc, ngtcp2_conn_stat *cstat, + const ngtcp2_ack *fr, size_t ecn_acked, + ngtcp2_tstamp largest_acked_sent_ts, + ngtcp2_tstamp ts) { + if (conn->tx.ecn.state == NGTCP2_ECN_STATE_FAILED) { + return; + } + + 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) < + ecn_acked || + fr->ecn.ect0 > pktns->tx.ecn.ect0 || fr->ecn.ect1))) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, + "path is not ECN capable"); + conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED; + return; + } + + if (conn->tx.ecn.state != NGTCP2_ECN_STATE_CAPABLE && ecn_acked) { + ngtcp2_log_info(&conn->log, NGTCP2_LOG_EVENT_CON, "path is ECN capable"); + conn->tx.ecn.state = NGTCP2_ECN_STATE_CAPABLE; + } + + if (fr->type == NGTCP2_FRAME_ACK_ECN) { + if (largest_acked_sent_ts != UINT64_MAX && + fr->ecn.ce > pktns->rx.ecn.ack.ce) { + cc->congestion_event(cc, cstat, largest_acked_sent_ts, 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; + } +} + +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, + ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *ent; + int64_t largest_ack = fr->largest_ack, min_ack; + size_t i; + int rv; + ngtcp2_ksl_it it; + ngtcp2_ssize num_acked = 0; + ngtcp2_tstamp largest_pkt_sent_ts = UINT64_MAX; + ngtcp2_tstamp largest_acked_sent_ts = UINT64_MAX; + int64_t pkt_num; + ngtcp2_cc *cc = rtb->cc; + ngtcp2_rtb_entry *acked_ent = NULL; + int ack_eliciting_pkt_acked = 0; + size_t ecn_acked = 0; + int verify_ecn = 0; + + if (conn && (conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED) && + largest_ack >= conn->pktns.crypto.tx.ckm->pkt_num) { + conn->flags &= (uint16_t)~NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED; + conn->crypto.key_update.confirmed_ts = ts; + + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_CRY, "key update confirmed"); + } + + if (rtb->largest_acked_tx_pkt_num < largest_ack) { + rtb->largest_acked_tx_pkt_num = largest_ack; + verify_ecn = 1; + } + + /* Assume that ngtcp2_pkt_validate_ack(fr) returns 0 */ + it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); + if (ngtcp2_ksl_it_end(&it)) { + if (verify_ecn) { + conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, + largest_acked_sent_ts, ts); + } + return 0; + } + + min_ack = largest_ack - (int64_t)fr->first_ack_blklen; + + for (; !ngtcp2_ksl_it_end(&it);) { + pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it); + + assert(pkt_num <= largest_ack); + + if (pkt_num < min_ack) { + break; + } + + ent = ngtcp2_ksl_it_get(&it); + + if (largest_ack == pkt_num) { + largest_pkt_sent_ts = ent->ts; + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + ack_eliciting_pkt_acked = 1; + } + + rtb_remove(rtb, &it, &acked_ent, ent, cstat); + ++num_acked; + } + + for (i = 0; i < fr->num_blks;) { + largest_ack = min_ack - (int64_t)fr->blks[i].gap - 2; + min_ack = largest_ack - (int64_t)fr->blks[i].blklen; + + it = ngtcp2_ksl_lower_bound(&rtb->ents, &largest_ack); + if (ngtcp2_ksl_it_end(&it)) { + break; + } + + for (; !ngtcp2_ksl_it_end(&it);) { + pkt_num = *(int64_t *)ngtcp2_ksl_it_key(&it); + if (pkt_num < min_ack) { + break; + } + ent = ngtcp2_ksl_it_get(&it); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING) { + ack_eliciting_pkt_acked = 1; + } + + rtb_remove(rtb, &it, &acked_ent, ent, cstat); + ++num_acked; + } + + ++i; + } + + if (largest_pkt_sent_ts != UINT64_MAX && ack_eliciting_pkt_acked) { + ngtcp2_conn_update_rtt(conn, pkt_ts - largest_pkt_sent_ts, + fr->ack_delay_unscaled, ts); + if (cc->new_rtt_sample) { + cc->new_rtt_sample(cc, cstat, ts); + } + } + + ngtcp2_rst_on_ack_recv(rtb->rst, cstat); + + if (conn) { + for (ent = acked_ent; ent; ent = acked_ent) { + if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num && + (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) { + ++ecn_acked; + } + + assert(largest_acked_sent_ts == UINT64_MAX || + largest_acked_sent_ts <= ent->ts); + + largest_acked_sent_ts = ent->ts; + + rv = rtb_process_acked_pkt(rtb, ent, conn); + if (rv != 0) { + goto fail; + } + + rtb_on_pkt_acked(rtb, ent, cstat, ts); + acked_ent = ent->next; + ngtcp2_rtb_entry_del(ent, rtb->mem); + } + + if (verify_ecn) { + conn_verify_ecn(conn, pktns, rtb->cc, cstat, fr, ecn_acked, + largest_acked_sent_ts, ts); + } + } else { + /* For unit tests */ + for (ent = acked_ent; ent; ent = acked_ent) { + rtb_on_pkt_acked(rtb, ent, cstat, ts); + acked_ent = ent->next; + ngtcp2_rtb_entry_del(ent, rtb->mem); + } + } + + cc->on_ack_recv(cc, cstat, ts); + + return num_acked; + +fail: + for (ent = acked_ent; ent; ent = acked_ent) { + acked_ent = ent->next; + ngtcp2_rtb_entry_del(ent, rtb->mem); + } + + return rv; +} + +static int rtb_pkt_lost(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat, + const ngtcp2_rtb_entry *ent, uint64_t loss_delay, + ngtcp2_tstamp lost_send_time, uint64_t pkt_thres) { + ngtcp2_tstamp loss_time; + + if (ent->ts <= lost_send_time || + rtb->largest_acked_tx_pkt_num >= ent->hd.pkt_num + (int64_t)pkt_thres) { + return 1; + } + + loss_time = cstat->loss_time[rtb->pktns_id]; + + if (loss_time == UINT64_MAX) { + loss_time = ent->ts + loss_delay; + } else { + loss_time = ngtcp2_min(loss_time, ent->ts + loss_delay); + } + + cstat->loss_time[rtb->pktns_id] = loss_time; + + return 0; +} + +/* + * rtb_compute_pkt_loss_delay computes loss delay. + */ +static ngtcp2_duration compute_pkt_loss_delay(const ngtcp2_conn_stat *cstat) { + /* 9/8 is kTimeThreshold */ + ngtcp2_duration loss_delay = + ngtcp2_max(cstat->latest_rtt, cstat->smoothed_rtt) * 9 / 8; + return ngtcp2_max(loss_delay, NGTCP2_GRANULARITY); +} + +/* + * conn_all_ecn_pkt_lost returns nonzero if all ECN QUIC packets are + * lost during validation period. + */ +static int conn_all_ecn_pkt_lost(ngtcp2_conn *conn) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + ngtcp2_pktns *pktns = &conn->pktns; + + return (!in_pktns || in_pktns->tx.ecn.validation_pkt_sent == + in_pktns->tx.ecn.validation_pkt_lost) && + (!hs_pktns || hs_pktns->tx.ecn.validation_pkt_sent == + hs_pktns->tx.ecn.validation_pkt_lost) && + pktns->tx.ecn.validation_pkt_sent == pktns->tx.ecn.validation_pkt_lost; +} + +int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, + ngtcp2_duration pto, ngtcp2_tstamp ts) { + ngtcp2_rtb_entry *ent; + ngtcp2_duration loss_delay; + ngtcp2_tstamp lost_send_time; + ngtcp2_ksl_it it; + ngtcp2_tstamp latest_ts, oldest_ts; + int64_t last_lost_pkt_num; + ngtcp2_duration loss_window, congestion_period; + ngtcp2_cc *cc = rtb->cc; + int rv; + uint64_t pkt_thres = + rtb->cc_bytes_in_flight / cstat->max_udp_payload_size / 2; + size_t ecn_pkt_lost = 0; + ngtcp2_tstamp start_ts; + + pkt_thres = ngtcp2_max(pkt_thres, NGTCP2_PKT_THRESHOLD); + cstat->loss_time[rtb->pktns_id] = UINT64_MAX; + loss_delay = compute_pkt_loss_delay(cstat); + lost_send_time = ts - loss_delay; + + it = ngtcp2_ksl_lower_bound(&rtb->ents, &rtb->largest_acked_tx_pkt_num); + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { + break; + } + + if (rtb_pkt_lost(rtb, cstat, ent, loss_delay, lost_send_time, pkt_thres)) { + /* All entries from ent are considered to be lost. */ + latest_ts = oldest_ts = ent->ts; + last_lost_pkt_num = ent->hd.pkt_num; + + congestion_period = (cstat->smoothed_rtt + + ngtcp2_max(4 * cstat->rttvar, NGTCP2_GRANULARITY) + + conn->remote.transport_params.max_ack_delay) * + NGTCP2_PERSISTENT_CONGESTION_THRESHOLD; + + start_ts = ngtcp2_max(rtb->persistent_congestion_start_ts, + cstat->first_rtt_sample_ts); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + if (last_lost_pkt_num == ent->hd.pkt_num + 1 && ent->ts >= start_ts) { + last_lost_pkt_num = ent->hd.pkt_num; + oldest_ts = ent->ts; + } else { + last_lost_pkt_num = -1; + } + + if ((ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) { + if (rtb->pktns_id != NGTCP2_PKTNS_ID_APPLICATION || + last_lost_pkt_num == -1 || + latest_ts - oldest_ts >= congestion_period) { + break; + } + ngtcp2_ksl_it_next(&it); + continue; + } + + if (ent->hd.pkt_num >= pktns->tx.ecn.start_pkt_num && + (ent->flags & NGTCP2_RTB_ENTRY_FLAG_ECN)) { + ++ecn_pkt_lost; + } + + rtb_on_remove(rtb, ent, cstat); + rv = rtb_on_pkt_lost(rtb, &it, ent, conn, pktns, ts); + if (rv != 0) { + return rv; + } + } + + switch (conn->tx.ecn.state) { + case NGTCP2_ECN_STATE_TESTING: + if (conn->tx.ecn.validation_start_ts == UINT64_MAX) { + break; + } + if (ts - conn->tx.ecn.validation_start_ts < 3 * pto) { + pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost; + assert(pktns->tx.ecn.validation_pkt_sent >= + pktns->tx.ecn.validation_pkt_lost); + break; + } + conn->tx.ecn.state = NGTCP2_ECN_STATE_UNKNOWN; + /* fall through */ + case NGTCP2_ECN_STATE_UNKNOWN: + pktns->tx.ecn.validation_pkt_lost += ecn_pkt_lost; + assert(pktns->tx.ecn.validation_pkt_sent >= + pktns->tx.ecn.validation_pkt_lost); + if (conn_all_ecn_pkt_lost(conn)) { + conn->tx.ecn.state = NGTCP2_ECN_STATE_FAILED; + } + break; + default: + break; + } + + cc->congestion_event(cc, cstat, latest_ts, ts); + + loss_window = latest_ts - oldest_ts; + /* Persistent congestion situation is only evaluated for app + * packet number space and for the packets sent after handshake + * is confirmed. During handshake, there is not much packets + * sent and also people seem to do lots of effort not to trigger + * persistent congestion there, then it is a lot easier to just + * not enable it during handshake. + */ + if (rtb->pktns_id == NGTCP2_PKTNS_ID_APPLICATION && loss_window > 0) { + if (loss_window >= congestion_period) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "persistent congestion loss_window=%" PRIu64 + " congestion_period=%" PRIu64, + loss_window, congestion_period); + + /* Reset min_rtt, srtt, and rttvar here. Next new RTT + sample will be used to recalculate these values. */ + cstat->min_rtt = UINT64_MAX; + cstat->smoothed_rtt = conn->local.settings.initial_rtt; + cstat->rttvar = conn->local.settings.initial_rtt / 2; + cstat->first_rtt_sample_ts = UINT64_MAX; + + cc->on_persistent_congestion(cc, cstat, ts); + } + } + + break; + } + } + + ngtcp2_rtb_remove_excessive_lost_pkt(rtb, pkt_thres); + + return 0; +} + +void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n) { + ngtcp2_ksl_it it = ngtcp2_ksl_end(&rtb->ents); + ngtcp2_rtb_entry *ent; + int rv; + + for (; rtb->num_lost_pkts > n;) { + assert(ngtcp2_ksl_it_end(&it)); + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + assert(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED); + + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); + + --rtb->num_lost_pkts; + rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + assert(0 == rv); + ngtcp2_rtb_entry_del(ent, rtb->mem); + } +} + +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, + ngtcp2_tstamp ts) { + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + int rv; + + if (ngtcp2_ksl_len(&rtb->ents) == 0) { + return; + } + + it = ngtcp2_ksl_end(&rtb->ents); + + for (;;) { + assert(ngtcp2_ksl_it_end(&it)); + + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) || + ts - ent->lost_ts < pto) { + return; + } + + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "removing stale lost pkn=%" PRId64, ent->hd.pkt_num); + + --rtb->num_lost_pkts; + rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + assert(0 == rv); + ngtcp2_rtb_entry_del(ent, rtb->mem); + + if (ngtcp2_ksl_len(&rtb->ents) == 0) { + return; + } + } +} + +ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb) { + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + + if (ngtcp2_ksl_len(&rtb->ents) == 0) { + return UINT64_MAX; + } + + it = ngtcp2_ksl_end(&rtb->ents); + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + if (!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)) { + return UINT64_MAX; + } + + return ent->lost_ts; +} + +static int rtb_on_pkt_lost_resched_move(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, + ngtcp2_rtb_entry *ent) { + ngtcp2_frame_chain **pfrc, *frc; + ngtcp2_stream *sfr; + ngtcp2_strm *strm; + int rv; + + ngtcp2_log_pkt_lost(rtb->log, ent->hd.pkt_num, ent->hd.type, ent->hd.flags, + ent->ts); + + if (rtb->qlog) { + ngtcp2_qlog_pkt_lost(rtb->qlog, ent); + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 + " is a probe packet, no retransmission is necessary", + ent->hd.pkt_num); + return 0; + } + + if (!ent->frc) { + /* PADDING only (or PADDING + ACK ) packets will have NULL + ent->frc. */ + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED)); + assert(!(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)); + return 0; + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { + --rtb->num_lost_pkts; + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 + " was declared lost and has already been retransmitted", + ent->hd.pkt_num); + return 0; + } + + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ngtcp2_log_info(rtb->log, NGTCP2_LOG_EVENT_RCV, + "pkn=%" PRId64 " has already been reclaimed on PTO", + ent->hd.pkt_num); + return 0; + } + + pfrc = &ent->frc; + + for (; *pfrc;) { + switch ((*pfrc)->fr.type) { + case NGTCP2_FRAME_STREAM: + frc = *pfrc; + + *pfrc = frc->next; + frc->next = NULL; + sfr = &frc->fr.stream; + + strm = ngtcp2_conn_find_stream(conn, sfr->stream_id); + if (!strm) { + ngtcp2_frame_chain_del(frc, conn->mem); + break; + } + rv = ngtcp2_strm_streamfrq_push(strm, frc); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + if (!ngtcp2_strm_is_tx_queued(strm)) { + strm->cycle = ngtcp2_conn_tx_strmq_first_cycle(conn); + rv = ngtcp2_conn_tx_strmq_push(conn, strm); + if (rv != 0) { + return rv; + } + } + break; + case NGTCP2_FRAME_CRYPTO: + frc = *pfrc; + + *pfrc = frc->next; + frc->next = NULL; + + rv = ngtcp2_ksl_insert(&pktns->crypto.tx.frq, NULL, + &frc->fr.crypto.offset, frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, conn->mem); + return rv; + } + break; + default: + pfrc = &(*pfrc)->next; + } + } + + *pfrc = pktns->tx.frq; + pktns->tx.frq = ent->frc; + ent->frc = NULL; + + return 0; +} + +int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat) { + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + int rv; + + it = ngtcp2_ksl_begin(&rtb->ents); + + for (; !ngtcp2_ksl_it_end(&it);) { + ent = ngtcp2_ksl_it_get(&it); + + rtb_on_remove(rtb, ent, cstat); + rv = ngtcp2_ksl_remove(&rtb->ents, &it, &ent->hd.pkt_num); + assert(0 == rv); + + rv = rtb_on_pkt_lost_resched_move(rtb, conn, pktns, ent); + ngtcp2_rtb_entry_del(ent, rtb->mem); + if (rv != 0) { + return rv; + } + } + + return 0; +} + +int ngtcp2_rtb_empty(ngtcp2_rtb *rtb) { + return ngtcp2_ksl_len(&rtb->ents) == 0; +} + +void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num) { + rtb->cc_pkt_num = cc_pkt_num; + rtb->cc_bytes_in_flight = 0; +} + +ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, size_t num_pkts) { + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + ngtcp2_ssize reclaimed; + size_t atmost = num_pkts; + + it = ngtcp2_ksl_end(&rtb->ents); + for (; !ngtcp2_ksl_it_begin(&it) && num_pkts >= 1;) { + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + + if ((ent->flags & (NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED | + NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED)) || + !(ent->flags & NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE)) { + continue; + } + + assert(ent->frc); + + reclaimed = rtb_reclaim_frame(rtb, conn, pktns, ent); + if (reclaimed < 0) { + return reclaimed; + } + + /* Mark reclaimed even if reclaimed == 0 so that we can skip it in + the next run. */ + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED; + + assert(rtb->num_retransmittable); + --rtb->num_retransmittable; + + if (reclaimed) { + --num_pkts; + } + } + + return (ngtcp2_ssize)(atmost - num_pkts); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h new file mode 100644 index 00000000000000..70f43ffd92416a --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_rtb.h @@ -0,0 +1,402 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_RTB_H +#define NGTCP2_RTB_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" + +typedef struct ngtcp2_conn ngtcp2_conn; +typedef struct ngtcp2_pktns ngtcp2_pktns; +typedef struct ngtcp2_log ngtcp2_log; +typedef struct ngtcp2_qlog ngtcp2_qlog; +typedef struct ngtcp2_strm ngtcp2_strm; +typedef struct ngtcp2_rst ngtcp2_rst; + +/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE indicates that no flag is + set. */ +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_NONE 0x00 +/* NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK indicates that an information + which a frame carries has been acknowledged. */ +#define NGTCP2_FRAME_CHAIN_BINDER_FLAG_ACK 0x01 + +/* + * ngtcp2_frame_chain_binder binds 2 or more of ngtcp2_frame_chain to + * share the acknowledgement state. In general, all + * ngtcp2_frame_chains bound to the same binder must have the same + * information. + */ +typedef struct ngtcp2_frame_chain_binder { + size_t refcount; + /* flags is bitwise OR of zero or more of + NGTCP2_FRAME_CHAIN_BINDER_FLAG_*. */ + uint32_t flags; +} ngtcp2_frame_chain_binder; + +int ngtcp2_frame_chain_binder_new(ngtcp2_frame_chain_binder **pbinder, + const ngtcp2_mem *mem); + +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* + * ngtcp2_frame_chain chains frames in a single packet. + */ +struct ngtcp2_frame_chain { + ngtcp2_frame_chain *next; + ngtcp2_frame_chain_binder *binder; + ngtcp2_frame fr; +}; + +/* + * ngtcp2_bind_frame_chains binds two frame chains |a| and |b| using + * new or existing ngtcp2_frame_chain_binder. |a| might have non-NULL + * a->binder. |b| must not have non-NULL b->binder. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_bind_frame_chains(ngtcp2_frame_chain *a, ngtcp2_frame_chain *b, + const ngtcp2_mem *mem); + +/* NGTCP2_MAX_STREAM_DATACNT is the maximum number of ngtcp2_vec that + a ngtcp2_stream can include. */ +#define NGTCP2_MAX_STREAM_DATACNT 256 + +/* NGTCP2_MAX_CRYPTO_DATACNT is the maximum number of ngtcp2_vec that + a ngtcp2_crypto can include. */ +#define NGTCP2_MAX_CRYPTO_DATACNT 8 + +/* + * 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_extralen_new works like ngtcp2_frame_chain_new, + * but it allocates extra memory |extralen| in order to extend + * ngtcp2_frame. + */ +int ngtcp2_frame_chain_extralen_new(ngtcp2_frame_chain **pfrc, size_t extralen, + const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_stream_datacnt_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 |datacnt| equals to 1, ngtcp2_frame_chain_new is called + * internally. + */ +int ngtcp2_frame_chain_stream_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_crypto_datacnt_new works like + * ngtcp2_frame_chain_new, but it allocates enough data to store + * additional |datacnt| - 1 ngtcp2_vec object after ngtcp2_crypto + * object. If |datacnt| equals to 1, ngtcp2_frame_chain_new is called + * internally. + */ +int ngtcp2_frame_chain_crypto_datacnt_new(ngtcp2_frame_chain **pfrc, + size_t datacnt, + const ngtcp2_mem *mem); + +int ngtcp2_frame_chain_new_token_new(ngtcp2_frame_chain **pfrc, + const ngtcp2_vec *token, + const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_del deallocates |frc|. It also deallocates the + * memory pointed by |frc|. + */ +void ngtcp2_frame_chain_del(ngtcp2_frame_chain *frc, const ngtcp2_mem *mem); + +/* + * ngtcp2_frame_chain_init initializes |frc|. + */ +void ngtcp2_frame_chain_init(ngtcp2_frame_chain *frc); + +/* + * ngtcp2_frame_chain_list_del deletes |frc|, and all objects + * connected by next field. + */ +void ngtcp2_frame_chain_list_del(ngtcp2_frame_chain *frc, + const ngtcp2_mem *mem); + +/* NGTCP2_RTB_ENTRY_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_RTB_ENTRY_FLAG_NONE 0x00 +/* NGTCP2_RTB_ENTRY_FLAG_PROBE indicates that the entry includes a + probe packet. */ +#define NGTCP2_RTB_ENTRY_FLAG_PROBE 0x01 +/* NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE indicates that the entry + includes a frame which must be retransmitted until it is + acknowledged. In most cases, this flag is used along with + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING. We have these 2 flags because + NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE triggers PTO, but just + NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING does not. */ +#define NGTCP2_RTB_ENTRY_FLAG_RETRANSMITTABLE 0x02 +/* NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING indicates that the entry + elicits acknowledgement. */ +#define NGTCP2_RTB_ENTRY_FLAG_ACK_ELICITING 0x04 +/* NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED indicates that the packet has + been reclaimed on PTO. It is not marked lost yet and still + consumes congestion window. */ +#define NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED 0x08 +/* NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED indicates that the entry + has been marked lost and scheduled to retransmit. */ +#define NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED 0x10 +/* NGTCP2_RTB_ENTRY_FLAG_ECN indicates that the entry is included in a + UDP datagram with ECN marking. */ +#define NGTCP2_RTB_ENTRY_FLAG_ECN 0x20 + +typedef struct ngtcp2_rtb_entry ngtcp2_rtb_entry; + +/* + * ngtcp2_rtb_entry is an object stored in ngtcp2_rtb. It corresponds + * to the one packet which is waiting for its ACK. + */ +struct ngtcp2_rtb_entry { + ngtcp2_rtb_entry *next; + + struct { + int64_t pkt_num; + uint8_t type; + uint8_t flags; + } hd; + ngtcp2_frame_chain *frc; + /* ts is the time point when a packet included in this entry is sent + to a peer. */ + ngtcp2_tstamp ts; + /* lost_ts is the time when this entry is marked lost. */ + ngtcp2_tstamp lost_ts; + /* pktlen is the length of QUIC packet */ + size_t pktlen; + struct { + uint64_t delivered; + ngtcp2_tstamp delivered_ts; + ngtcp2_tstamp first_sent_ts; + int is_app_limited; + } rst; + /* flags is bitwise-OR of zero or more of + NGTCP2_RTB_ENTRY_FLAG_*. */ + uint8_t flags; +}; + +/* + * ngtcp2_rtb_entry_new allocates ngtcp2_rtb_entry object, and assigns + * its pointer to |*pent|. On success, |*pent| takes ownership of + * |frc|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory. + */ +int ngtcp2_rtb_entry_new(ngtcp2_rtb_entry **pent, const ngtcp2_pkt_hd *hd, + ngtcp2_frame_chain *frc, ngtcp2_tstamp ts, + size_t pktlen, uint8_t flags, const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb_entry_del deallocates |ent|. It also frees memory + * pointed by |ent|. + */ +void ngtcp2_rtb_entry_del(ngtcp2_rtb_entry *ent, const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb tracks sent packets, and its ACK timeout for + * retransmission. + */ +typedef struct ngtcp2_rtb { + /* ents includes ngtcp2_rtb_entry sorted by decreasing order of + packet number. */ + ngtcp2_ksl ents; + /* crypto is CRYPTO stream. */ + ngtcp2_strm *crypto; + ngtcp2_rst *rst; + ngtcp2_cc *cc; + ngtcp2_log *log; + ngtcp2_qlog *qlog; + const ngtcp2_mem *mem; + /* largest_acked_tx_pkt_num is the largest packet number + acknowledged by the peer. */ + int64_t largest_acked_tx_pkt_num; + /* num_ack_eliciting is the number of ACK eliciting entries. */ + size_t num_ack_eliciting; + /* num_retransmittable is the number of packets which contain frames + that must be retransmitted on loss. */ + size_t num_retransmittable; + /* probe_pkt_left is the number of probe packet to send */ + size_t probe_pkt_left; + /* pktns_id is the identifier of packet number space. */ + ngtcp2_pktns_id pktns_id; + /* cc_pkt_num is the smallest packet number that is contributed to + ngtcp2_conn_stat.bytes_in_flight. */ + int64_t cc_pkt_num; + /* cc_bytes_in_flight is the number of in-flight bytes that is + contributed to ngtcp2_conn_stat.bytes_in_flight. It only + includes the bytes after congestion state is reset. */ + 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; +} ngtcp2_rtb; + +/* + * ngtcp2_rtb_init initializes |rtb|. + */ +void ngtcp2_rtb_init(ngtcp2_rtb *rtb, ngtcp2_pktns_id pktns_id, + ngtcp2_strm *crypto, ngtcp2_rst *rst, ngtcp2_cc *cc, + ngtcp2_log *log, ngtcp2_qlog *qlog, const ngtcp2_mem *mem); + +/* + * ngtcp2_rtb_free deallocates resources allocated for |rtb|. + */ +void ngtcp2_rtb_free(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_add adds |ent| to |rtb|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_rtb_add(ngtcp2_rtb *rtb, ngtcp2_rtb_entry *ent, + ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_rtb_head returns the iterator which points to the entry + * which has the largest packet number. If there is no entry, + * returned value satisfies ngtcp2_ksl_it_end(&it) != 0. + */ +ngtcp2_ksl_it ngtcp2_rtb_head(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_recv_ack removes acked ngtcp2_rtb_entry from |rtb|. + * |pkt_num| is a packet number which includes |fr|. |pkt_ts| is the + * timestamp when packet is received. |ts| should be the current + * time. Usually they are the same, but for buffered packets, + * |pkt_ts| would be earlier than |ts|. + * + * This function returns the number of newly acknowledged packets if + * it succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_CALLBACK_FAILURE + * User callback failed + * NGTCP2_ERR_NOMEM + * Out of memory + */ +ngtcp2_ssize ngtcp2_rtb_recv_ack(ngtcp2_rtb *rtb, const ngtcp2_ack *fr, + ngtcp2_conn_stat *cstat, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_tstamp pkt_ts, + ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_detect_lost_pkt detects lost packets and prepends the + * frames contained them to |*pfrc|. Even when this function fails, + * some frames might be prepended to |*pfrc| and the caller should + * handle them. + */ +int ngtcp2_rtb_detect_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat, + ngtcp2_duration pto, ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_remove_expired_lost_pkt removes expired lost packet. + */ +void ngtcp2_rtb_remove_expired_lost_pkt(ngtcp2_rtb *rtb, ngtcp2_duration pto, + ngtcp2_tstamp ts); + +/* + * ngtcp2_rtb_lost_pkt_ts returns the earliest time when the still + * retained packet was lost. It returns UINT64_MAX if no such packet + * exists. + */ +ngtcp2_tstamp ngtcp2_rtb_lost_pkt_ts(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_remove_all removes all packets from |rtb| and prepends + * all frames to |*pfrc|. Even when this function fails, some frames + * might be prepended to |*pfrc| and the caller should handle them. + */ +int ngtcp2_rtb_remove_all(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, ngtcp2_conn_stat *cstat); + +/* + * ngtcp2_rtb_empty returns nonzero if |rtb| have no entry. + */ +int ngtcp2_rtb_empty(ngtcp2_rtb *rtb); + +/* + * ngtcp2_rtb_reset_cc_state resets congestion state in |rtb|. + * |cc_pkt_num| is the next outbound packet number which is sent under + * new congestion state. + */ +void ngtcp2_rtb_reset_cc_state(ngtcp2_rtb *rtb, int64_t cc_pkt_num); + +/* + * ngtcp2_rtb_remove_expired_lost_pkt ensures that the number of lost + * packets at most |n|. + */ +void ngtcp2_rtb_remove_excessive_lost_pkt(ngtcp2_rtb *rtb, size_t n); + +/* + * ngtcp2_rtb_reclaim_on_pto reclaims up to |num_pkts| packets which + * are in-flight and not marked lost to send them in PTO probe. The + * reclaimed frames are chained to |*pfrc|. + * + * This function returns the number of packets reclaimed if it + * succeeds, or one of the following negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +ngtcp2_ssize ngtcp2_rtb_reclaim_on_pto(ngtcp2_rtb *rtb, ngtcp2_conn *conn, + ngtcp2_pktns *pktns, size_t num_pkts); + +#endif /* NGTCP2_RTB_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c new file mode 100644 index 00000000000000..3118955b248902 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.c @@ -0,0 +1,242 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_str.h" + +#include <string.h> + +#include "ngtcp2_macro.h" + +void *ngtcp2_cpymem(void *dest, const void *src, size_t n) { + memcpy(dest, src, n); + return (uint8_t *)dest + n; +} + +uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n) { + memset(dest, b, n); + return dest + n; +} + +#define LOWER_XDIGITS "0123456789abcdef" + +uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len) { + size_t i; + uint8_t *p = dest; + + for (i = 0; i < len; ++i) { + *p++ = (uint8_t)LOWER_XDIGITS[data[i] >> 4]; + *p++ = (uint8_t)LOWER_XDIGITS[data[i] & 0xf]; + } + + *p = '\0'; + + return dest; +} + +char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, + size_t len) { + size_t i; + char *p = dest; + uint8_t c; + + for (i = 0; i < len; ++i) { + c = data[i]; + if (0x20 <= c && c <= 0x7e) { + *p++ = (char)c; + } else { + *p++ = '.'; + } + } + + *p = '\0'; + + return dest; +} + +/* + * write_uint writes |n| to the buffer pointed by |p| in decimal + * representation. It returns |p| plus the number of bytes written. + * The function assumes that the buffer has enough capacity to contain + * a string. + */ +static uint8_t *write_uint(uint8_t *p, uint64_t n) { + size_t nlen = 0; + uint64_t t; + uint8_t *res; + + if (n == 0) { + *p++ = '0'; + return p; + } + for (t = n; t; t /= 10, ++nlen) + ; + p += nlen; + res = p; + for (; n; n /= 10) { + *--p = (uint8_t)((n % 10) + '0'); + } + return res; +} + +uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr) { + size_t i; + uint8_t *p = dest; + + p = write_uint(p, addr[0]); + + for (i = 1; i < 4; ++i) { + *p++ = '.'; + p = write_uint(p, addr[i]); + } + + *p = '\0'; + + return dest; +} + +/* + * write_hex_zsup writes the content of buffer pointed by |data| of + * length |len| to |dest| in hex string. Any leading zeros are + * suppressed. It returns |dest| plus the number of bytes written. + */ +static uint8_t *write_hex_zsup(uint8_t *dest, const uint8_t *data, size_t len) { + size_t i; + uint8_t *p = dest; + uint8_t d; + + for (i = 0; i < len; ++i) { + d = data[i]; + if (d >> 4) { + break; + } + + d &= 0xf; + + if (d) { + *p++ = (uint8_t)LOWER_XDIGITS[d]; + ++i; + break; + } + } + + if (p == dest && i == len) { + *p++ = '0'; + return p; + } + + for (; i < len; ++i) { + d = data[i]; + *p++ = (uint8_t)LOWER_XDIGITS[d >> 4]; + *p++ = (uint8_t)LOWER_XDIGITS[d & 0xf]; + } + + return p; +} + +uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr) { + uint16_t blks[8]; + size_t i; + size_t zlen, zoff; + size_t max_zlen = 0, max_zoff = 8; + uint8_t *p = dest; + + for (i = 0; i < 16; i += sizeof(uint16_t)) { + /* Copy in network byte order. */ + memcpy(&blks[i / sizeof(uint16_t)], addr + i, sizeof(uint16_t)); + } + + for (i = 0; i < 8;) { + if (blks[i]) { + ++i; + continue; + } + + zlen = 1; + zoff = i; + + ++i; + for (; i < 8 && blks[i] == 0; ++i, ++zlen) + ; + if (zlen > max_zlen) { + max_zlen = zlen; + max_zoff = zoff; + } + } + + /* Do not suppress a single '0' block */ + if (max_zlen == 1) { + max_zoff = 8; + } + + if (max_zoff != 0) { + p = write_hex_zsup(p, (const uint8_t *)blks, sizeof(uint16_t)); + + for (i = 1; i < max_zoff; ++i) { + *p++ = ':'; + p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t)); + } + } + + if (max_zoff != 8) { + *p++ = ':'; + + if (max_zoff + max_zlen == 8) { + *p++ = ':'; + } else { + for (i = max_zoff + max_zlen; i < 8; ++i) { + *p++ = ':'; + p = write_hex_zsup(p, (const uint8_t *)(blks + i), sizeof(uint16_t)); + } + } + } + + *p = '\0'; + + return dest; +} + +int ngtcp2_verify_stateless_reset_token(const uint8_t *want, + const uint8_t *got) { + return !ngtcp2_check_invalid_stateless_reset_token(got) && + ngtcp2_cmemeq(want, got, NGTCP2_STATELESS_RESET_TOKENLEN) + ? 0 + : NGTCP2_ERR_INVALID_ARGUMENT; +} + +int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token) { + static uint8_t invalid_token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0}; + + return 0 == memcmp(invalid_token, token, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n) { + size_t i; + int rv = 0; + + for (i = 0; i < n; ++i) { + rv |= a[i] ^ b[i]; + } + + return rv == 0; +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h new file mode 100644 index 00000000000000..bd0145747c8f54 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_str.h @@ -0,0 +1,106 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_STR_H +#define NGTCP2_STR_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +void *ngtcp2_cpymem(void *dest, const void *src, size_t n); + +/* + * ngtcp2_setmem writes a string of length |n| consisting only |b| to + * the buffer pointed by |dest|. It returns dest + n; + */ +uint8_t *ngtcp2_setmem(uint8_t *dest, uint8_t b, size_t n); +/* + * ngtcp2_encode_hex encodes |data| of length |len| in hex string. It + * writes additional NULL bytes at the end of the buffer. The buffer + * pointed by |dest| must have at least |len| * 2 + 1 bytes space. + * This function returns |dest|. + */ +uint8_t *ngtcp2_encode_hex(uint8_t *dest, const uint8_t *data, size_t len); + +/* + * ngtcp2_encode_ipv4 encodes binary form IPv4 address stored in + * |addr| to human readable text form in the buffer pointed by |dest|. + * The capacity of buffer must have enough length to store a text form + * plus a terminating NULL byte. The resulting text form ends with + * NULL byte. The function returns |dest|. + */ +uint8_t *ngtcp2_encode_ipv4(uint8_t *dest, const uint8_t *addr); + +/* + * ngtcp2_encode_ipv6 encodes binary form IPv6 address stored in + * |addr| to human readable text form in the buffer pointed by |dest|. + * The capacity of buffer must have enough length to store a text form + * plus a terminating NULL byte. The resulting text form ends with + * NULL byte. The function produces the canonical form of IPv6 text + * representation described in + * https://tools.ietf.org/html/rfc5952#section-4. The function + * returns |dest|. + */ +uint8_t *ngtcp2_encode_ipv6(uint8_t *dest, const uint8_t *addr); + +/* + * ngtcp2_encode_printable_ascii encodes |data| of length |len| in + * |dest| in the following manner: printable ascii characters are + * copied as is. The other characters are converted to ".". It + * writes additional NULL bytes at the end of the buffer. |dest| must + * have at least |len| + 1 bytes. This function returns |dest|. + */ +char *ngtcp2_encode_printable_ascii(char *dest, const uint8_t *data, + size_t len); + +/* + * ngtcp2_verify_stateless_reset_token verifies stateless reset token + * |want| and |got|. This function returns 0 if |want| equals |got| + * and |got| is not all zero, or one of the following negative error + * codes: + * + * NGTCP2_ERR_INVALID_ARGUMENT + * Token does not match; or token is all zero. + */ +int ngtcp2_verify_stateless_reset_token(const uint8_t *want, + const uint8_t *got); + +/* + * ngtcp2_check_invalid_stateless_reset_token returns nonzero if + * |token| is invalid stateless reset token. Currently, token which + * consists of all zeros is considered invalid. + */ +int ngtcp2_check_invalid_stateless_reset_token(const uint8_t *token); + +/* + * ngtcp2_cmemeq returns nonzero if the first |n| bytes of the buffers + * pointed by |a| and |b| are equal. The comparison is done in a + * constant time manner. + */ +int ngtcp2_cmemeq(const uint8_t *a, const uint8_t *b, size_t n); + +#endif /* NGTCP2_STR_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c new file mode 100644 index 00000000000000..8e8eef0c9c9c93 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.c @@ -0,0 +1,675 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_strm.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_rtb.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_vec.h" + +static int offset_less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *(int64_t *)lhs < *(int64_t *)rhs; +} + +int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, const ngtcp2_mem *mem) { + strm->cycle = 0; + strm->tx.acked_offset = NULL; + strm->tx.cont_acked_offset = 0; + strm->tx.streamfrq = NULL; + strm->tx.offset = 0; + strm->tx.max_offset = max_tx_offset; + strm->tx.last_max_stream_data_ts = UINT64_MAX; + strm->rx.rob = NULL; + strm->rx.cont_offset = 0; + strm->rx.last_offset = 0; + strm->stream_id = stream_id; + strm->flags = flags; + strm->stream_user_data = stream_user_data; + strm->rx.window = strm->rx.max_offset = strm->rx.unsent_max_offset = + max_rx_offset; + strm->me.key = (uint64_t)stream_id; + strm->pe.index = NGTCP2_PQ_BAD_INDEX; + strm->mem = mem; + strm->app_error_code = 0; + + return 0; +} + +void ngtcp2_strm_free(ngtcp2_strm *strm) { + ngtcp2_ksl_it it; + + if (strm == NULL) { + return; + } + + if (strm->tx.streamfrq) { + for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + ngtcp2_frame_chain_del(ngtcp2_ksl_it_get(&it), strm->mem); + } + + ngtcp2_ksl_free(strm->tx.streamfrq); + ngtcp2_mem_free(strm->mem, strm->tx.streamfrq); + } + + ngtcp2_rob_free(strm->rx.rob); + ngtcp2_mem_free(strm->mem, strm->rx.rob); + ngtcp2_gaptr_free(strm->tx.acked_offset); + ngtcp2_mem_free(strm->mem, strm->tx.acked_offset); +} + +static int strm_rob_init(ngtcp2_strm *strm) { + int rv; + ngtcp2_rob *rob = ngtcp2_mem_malloc(strm->mem, sizeof(*rob)); + + if (rob == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_rob_init(rob, 8 * 1024, strm->mem); + if (rv != 0) { + ngtcp2_mem_free(strm->mem, rob); + return rv; + } + + strm->rx.rob = rob; + + return 0; +} + +uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm) { + if (strm->rx.rob == NULL) { + return strm->rx.cont_offset; + } + return ngtcp2_rob_first_gap_offset(strm->rx.rob); +} + +/* strm_rob_heavily_fragmented returns nonzero if the number of gaps + in |rob| exceeds the limit. */ +static int strm_rob_heavily_fragmented(ngtcp2_rob *rob) { + return ngtcp2_ksl_len(&rob->gapksl) >= 1000; +} + +int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, + size_t datalen, uint64_t offset) { + int rv; + + if (strm->rx.rob == NULL) { + rv = strm_rob_init(strm); + if (rv != 0) { + return rv; + } + + if (strm->rx.cont_offset) { + rv = ngtcp2_rob_remove_prefix(strm->rx.rob, strm->rx.cont_offset); + if (rv != 0) { + return rv; + } + } + } + + if (strm_rob_heavily_fragmented(strm->rx.rob)) { + return NGTCP2_ERR_INTERNAL; + } + + return ngtcp2_rob_push(strm->rx.rob, offset, data, datalen); +} + +int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset) { + if (strm->rx.rob == NULL) { + strm->rx.cont_offset = offset; + return 0; + } + + return ngtcp2_rob_remove_prefix(strm->rx.rob, offset); +} + +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags) { + strm->flags |= flags & NGTCP2_STRM_FLAG_SHUT_RDWR; +} + +static int strm_streamfrq_init(ngtcp2_strm *strm) { + int rv; + ngtcp2_ksl *streamfrq = ngtcp2_mem_malloc(strm->mem, sizeof(*streamfrq)); + if (streamfrq == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_ksl_init(streamfrq, offset_less, sizeof(uint64_t), strm->mem); + if (rv != 0) { + ngtcp2_mem_free(strm->mem, streamfrq); + return rv; + } + + strm->tx.streamfrq = streamfrq; + + return 0; +} + +int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc) { + int rv; + + assert(frc->fr.type == NGTCP2_FRAME_STREAM); + assert(frc->next == NULL); + + if (strm->tx.streamfrq == NULL) { + rv = strm_streamfrq_init(strm); + if (rv != 0) { + return rv; + } + } + + return ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &frc->fr.stream.offset, + frc); +} + +static int strm_streamfrq_unacked_pop(ngtcp2_strm *strm, + ngtcp2_frame_chain **pfrc) { + ngtcp2_frame_chain *frc, *nfrc; + ngtcp2_stream *fr, *nfr; + uint64_t offset, end_offset; + size_t idx, end_idx; + uint64_t base_offset, end_base_offset; + ngtcp2_range gap; + ngtcp2_vec *v; + int rv; + ngtcp2_ksl_it it; + + *pfrc = NULL; + + assert(strm->tx.streamfrq); + assert(ngtcp2_ksl_len(strm->tx.streamfrq)); + + for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it);) { + frc = ngtcp2_ksl_it_get(&it); + fr = &frc->fr.stream; + + ngtcp2_ksl_remove(strm->tx.streamfrq, &it, &fr->offset); + + idx = 0; + offset = fr->offset; + base_offset = 0; + + gap = ngtcp2_strm_get_unacked_range_after(strm, offset); + if (gap.begin < offset) { + gap.begin = offset; + } + + for (; idx < fr->datacnt && offset < gap.begin; ++idx) { + v = &fr->data[idx]; + if (offset + v->len > gap.begin) { + base_offset = gap.begin - offset; + break; + } + + offset += v->len; + } + + if (idx == fr->datacnt) { + if (fr->fin) { + if (strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED) { + ngtcp2_frame_chain_del(frc, strm->mem); + assert(ngtcp2_ksl_len(strm->tx.streamfrq) == 0); + return 0; + } + + fr->offset = fr->offset + ngtcp2_vec_len(fr->data, fr->datacnt); + fr->datacnt = 0; + + *pfrc = frc; + + return 0; + } + ngtcp2_frame_chain_del(frc, strm->mem); + continue; + } + + assert(gap.begin == offset + base_offset); + + end_idx = idx; + end_offset = offset; + end_base_offset = 0; + + for (; end_idx < fr->datacnt; ++end_idx) { + v = &fr->data[end_idx]; + if (end_offset + v->len > gap.end) { + end_base_offset = gap.end - end_offset; + break; + } + + end_offset += v->len; + } + + if (fr->offset == offset && base_offset == 0 && fr->datacnt == end_idx) { + *pfrc = frc; + return 0; + } + + if (fr->datacnt == end_idx) { + memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + + assert(fr->data[0].len > base_offset); + + fr->offset = offset + base_offset; + fr->datacnt = end_idx - idx; + fr->data[0].base += base_offset; + fr->data[0].len -= (size_t)base_offset; + + *pfrc = frc; + return 0; + } + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, fr->datacnt - end_idx, + strm->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + memcpy(nfr->data, fr->data + end_idx, + sizeof(nfr->data[0]) * (fr->datacnt - end_idx)); + + assert(nfr->data[0].len > end_base_offset); + + nfr->type = NGTCP2_FRAME_STREAM; + nfr->flags = 0; + nfr->fin = fr->fin; + nfr->stream_id = fr->stream_id; + nfr->offset = end_offset + end_base_offset; + nfr->datacnt = fr->datacnt - end_idx; + nfr->data[0].base += end_base_offset; + nfr->data[0].len -= (size_t)end_base_offset; + + rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + if (end_base_offset) { + ++end_idx; + } + + memmove(fr->data, fr->data + idx, sizeof(fr->data[0]) * (end_idx - idx)); + + assert(fr->data[0].len > base_offset); + + fr->fin = 0; + fr->offset = offset + base_offset; + fr->datacnt = end_idx - idx; + if (end_base_offset) { + assert(fr->data[fr->datacnt - 1].len > end_base_offset); + fr->data[fr->datacnt - 1].len = (size_t)end_base_offset; + } + fr->data[0].base += base_offset; + fr->data[0].len -= (size_t)base_offset; + + *pfrc = frc; + return 0; + } + + return 0; +} + +int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, + size_t left) { + ngtcp2_stream *fr, *nfr; + ngtcp2_frame_chain *frc, *nfrc; + int rv; + size_t nmerged; + size_t datalen; + ngtcp2_vec a[NGTCP2_MAX_STREAM_DATACNT]; + ngtcp2_vec b[NGTCP2_MAX_STREAM_DATACNT]; + size_t acnt, bcnt; + uint64_t unacked_offset; + + if (strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0) { + *pfrc = NULL; + return 0; + } + + rv = strm_streamfrq_unacked_pop(strm, &frc); + if (rv != 0) { + return rv; + } + if (frc == NULL) { + *pfrc = NULL; + return 0; + } + + fr = &frc->fr.stream; + datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + if (left == 0) { + /* datalen could be zero if 0 length STREAM has been sent */ + if (datalen || ngtcp2_ksl_len(strm->tx.streamfrq) > 1) { + rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &fr->offset, frc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + *pfrc = NULL; + return 0; + } + } + + if (datalen > left) { + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + bcnt = 0; + ngtcp2_vec_split(a, &acnt, b, &bcnt, left, NGTCP2_MAX_STREAM_DATACNT); + + assert(acnt > 0); + assert(bcnt > 0); + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, bcnt, strm->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + nfr->type = NGTCP2_FRAME_STREAM; + nfr->flags = 0; + nfr->fin = fr->fin; + nfr->stream_id = fr->stream_id; + nfr->offset = fr->offset + left; + nfr->datacnt = bcnt; + ngtcp2_vec_copy(nfr->data, b, bcnt); + + rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + *nfr = *fr; + nfr->fin = 0; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, strm->mem); + + *pfrc = nfrc; + + return 0; + } + + left -= datalen; + + ngtcp2_vec_copy(a, fr->data, fr->datacnt); + acnt = fr->datacnt; + + for (; left && ngtcp2_ksl_len(strm->tx.streamfrq);) { + unacked_offset = ngtcp2_strm_streamfrq_unacked_offset(strm); + if (unacked_offset != fr->offset + datalen) { + assert(fr->offset + datalen < unacked_offset); + break; + } + + rv = strm_streamfrq_unacked_pop(strm, &nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + if (nfrc == NULL) { + break; + } + + nfr = &nfrc->fr.stream; + + if (nfr->fin && nfr->datacnt == 0) { + fr->fin = 1; + ngtcp2_frame_chain_del(nfrc, strm->mem); + break; + } + + nmerged = ngtcp2_vec_merge(a, &acnt, nfr->data, &nfr->datacnt, left, + NGTCP2_MAX_STREAM_DATACNT); + if (nmerged == 0) { + rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + assert(ngtcp2_err_is_fatal(rv)); + ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + break; + } + + datalen += nmerged; + left -= nmerged; + + if (nfr->datacnt == 0) { + fr->fin = nfr->fin; + ngtcp2_frame_chain_del(nfrc, strm->mem); + continue; + } + + nfr->offset += nmerged; + + rv = ngtcp2_ksl_insert(strm->tx.streamfrq, NULL, &nfr->offset, nfrc); + if (rv != 0) { + ngtcp2_frame_chain_del(nfrc, strm->mem); + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + break; + } + + if (acnt == fr->datacnt) { + if (acnt > 0) { + fr->data[acnt - 1] = a[acnt - 1]; + } + + *pfrc = frc; + return 0; + } + + assert(acnt > fr->datacnt); + + rv = ngtcp2_frame_chain_stream_datacnt_new(&nfrc, acnt, strm->mem); + if (rv != 0) { + ngtcp2_frame_chain_del(frc, strm->mem); + return rv; + } + + nfr = &nfrc->fr.stream; + *nfr = *fr; + nfr->datacnt = acnt; + ngtcp2_vec_copy(nfr->data, a, acnt); + + ngtcp2_frame_chain_del(frc, strm->mem); + + *pfrc = nfrc; + + return 0; +} + +uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm) { + ngtcp2_frame_chain *frc; + ngtcp2_stream *fr; + ngtcp2_range gap; + ngtcp2_ksl_it it; + size_t datalen; + + assert(strm->tx.streamfrq); + assert(ngtcp2_ksl_len(strm->tx.streamfrq)); + + for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + fr = &frc->fr.stream; + + gap = ngtcp2_strm_get_unacked_range_after(strm, fr->offset); + + datalen = ngtcp2_vec_len(fr->data, fr->datacnt); + + if (gap.begin <= fr->offset) { + return fr->offset; + } + if (gap.begin < fr->offset + datalen) { + return gap.begin; + } + if (fr->offset + datalen == gap.begin && fr->fin && + !(strm->flags & NGTCP2_STRM_FLAG_FIN_ACKED)) { + return fr->offset + datalen; + } + } + + return (uint64_t)-1; +} + +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm) { + ngtcp2_ksl_it it; + + assert(strm->tx.streamfrq); + assert(ngtcp2_ksl_len(strm->tx.streamfrq)); + + it = ngtcp2_ksl_begin(strm->tx.streamfrq); + return ngtcp2_ksl_it_get(&it); +} + +int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm) { + return strm->tx.streamfrq == NULL || ngtcp2_ksl_len(strm->tx.streamfrq) == 0; +} + +void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm) { + ngtcp2_frame_chain *frc; + ngtcp2_ksl_it it; + + if (strm->tx.streamfrq == NULL) { + return; + } + + for (it = ngtcp2_ksl_begin(strm->tx.streamfrq); !ngtcp2_ksl_it_end(&it); + ngtcp2_ksl_it_next(&it)) { + frc = ngtcp2_ksl_it_get(&it); + ngtcp2_frame_chain_del(frc, strm->mem); + } + ngtcp2_ksl_clear(strm->tx.streamfrq); +} + +int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm) { + return strm->pe.index != NGTCP2_PQ_BAD_INDEX; +} + +int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm) { + if (strm->tx.acked_offset == NULL) { + return strm->tx.cont_acked_offset == strm->tx.offset; + } + + return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset) == + strm->tx.offset; +} + +ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, + uint64_t offset) { + ngtcp2_ksl_it gapit; + ngtcp2_range gap; + + if (strm->tx.acked_offset == NULL) { + gap.begin = strm->tx.cont_acked_offset; + gap.end = UINT64_MAX; + return gap; + } + + gapit = ngtcp2_gaptr_get_first_gap_after(strm->tx.acked_offset, offset); + return *(ngtcp2_range *)ngtcp2_ksl_it_key(&gapit); +} + +uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm) { + if (strm->tx.acked_offset == NULL) { + return strm->tx.cont_acked_offset; + } + + return ngtcp2_gaptr_first_gap_offset(strm->tx.acked_offset); +} + +static int strm_acked_offset_init(ngtcp2_strm *strm) { + int rv; + ngtcp2_gaptr *acked_offset = + ngtcp2_mem_malloc(strm->mem, sizeof(*acked_offset)); + + if (acked_offset == NULL) { + return NGTCP2_ERR_NOMEM; + } + + rv = ngtcp2_gaptr_init(acked_offset, strm->mem); + if (rv != 0) { + ngtcp2_mem_free(strm->mem, acked_offset); + return rv; + } + + strm->tx.acked_offset = acked_offset; + + return 0; +} + +int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len) { + int rv; + + if (strm->tx.acked_offset == NULL) { + if (strm->tx.cont_acked_offset == offset) { + strm->tx.cont_acked_offset += len; + return 0; + } + + rv = strm_acked_offset_init(strm); + if (rv != 0) { + return rv; + } + + rv = + ngtcp2_gaptr_push(strm->tx.acked_offset, 0, strm->tx.cont_acked_offset); + if (rv != 0) { + return rv; + } + } + + return ngtcp2_gaptr_push(strm->tx.acked_offset, offset, len); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h new file mode 100644 index 00000000000000..6b7418706c760e --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_strm.h @@ -0,0 +1,268 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 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_STRM_H +#define NGTCP2_STRM_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_rob.h" +#include "ngtcp2_map.h" +#include "ngtcp2_gaptr.h" +#include "ngtcp2_ksl.h" +#include "ngtcp2_pq.h" + +typedef struct ngtcp2_frame_chain ngtcp2_frame_chain; + +/* NGTCP2_STRM_FLAG_NONE indicates that no flag is set. */ +#define NGTCP2_STRM_FLAG_NONE 0x00 +/* NGTCP2_STRM_FLAG_SHUT_RD indicates that further reception of stream + data is not allowed. */ +#define NGTCP2_STRM_FLAG_SHUT_RD 0x01 +/* NGTCP2_STRM_FLAG_SHUT_WR indicates that further transmission of + stream data is not allowed. */ +#define NGTCP2_STRM_FLAG_SHUT_WR 0x02 +#define NGTCP2_STRM_FLAG_SHUT_RDWR \ + (NGTCP2_STRM_FLAG_SHUT_RD | NGTCP2_STRM_FLAG_SHUT_WR) +/* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is sent from + the local endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_WR is also + set. */ +#define NGTCP2_STRM_FLAG_SENT_RST 0x04 +/* NGTCP2_STRM_FLAG_SENT_RST indicates that RST_STREAM is received + from the remote endpoint. In this case, NGTCP2_STRM_FLAG_SHUT_RD + is also set. */ +#define NGTCP2_STRM_FLAG_RECV_RST 0x08 +/* NGTCP2_STRM_FLAG_STOP_SENDING indicates that STOP_SENDING is sent + from the local endpoint. */ +#define NGTCP2_STRM_FLAG_STOP_SENDING 0x10 +/* NGTCP2_STRM_FLAG_RST_ACKED indicates that the outgoing RST_STREAM + is acknowledged by peer. */ +#define NGTCP2_STRM_FLAG_RST_ACKED 0x20 +/* NGTCP2_STRM_FLAG_FIN_ACKED indicates that a STREAM with FIN bit set + is acknowledged by a remote endpoint. */ +#define NGTCP2_STRM_FLAG_FIN_ACKED 0x40 + +typedef struct ngtcp2_strm ngtcp2_strm; + +struct ngtcp2_strm { + ngtcp2_map_entry me; + ngtcp2_pq_entry pe; + uint64_t cycle; + + struct { + /* acked_offset tracks acknowledged outgoing data. */ + ngtcp2_gaptr *acked_offset; + /* cont_acked_offset is the offset that all data up to this offset + is acknowledged by a remote endpoint. It is used until the + remote endpoint acknowledges data in out-of-order. After that, + acked_offset is used instead. */ + uint64_t cont_acked_offset; + /* streamfrq contains STREAM frame for retransmission. The flow + control credits have been paid when they are transmitted first + time. There are no restriction regarding flow control for + retransmission. */ + ngtcp2_ksl *streamfrq; + /* offset is the next offset of outgoing data. In other words, it + is the number of bytes sent in this stream without + duplication. */ + uint64_t offset; + /* max_tx_offset is the maximum offset that local endpoint can + send for this stream. */ + uint64_t max_offset; + /* last_max_stream_data_ts is the timestamp when last + MAX_STREAM_DATA frame is sent. */ + ngtcp2_tstamp last_max_stream_data_ts; + } tx; + + struct { + /* rob is the reorder buffer for incoming stream data. The data + received in out of order is buffered and sorted by its offset + in this object. */ + ngtcp2_rob *rob; + /* cont_offset is the largest offset of consecutive data. It is + used until the endpoint receives out-of-order data. After + that, rob is used to track the offset and data. */ + uint64_t cont_offset; + /* last_offset is the largest offset of stream data received for + this stream. */ + uint64_t last_offset; + /* max_offset is the maximum offset that remote endpoint can send + to this stream. */ + uint64_t max_offset; + /* unsent_max_offset is the maximum offset that remote endpoint + can send to this stream, and it is not notified to the remote + endpoint. unsent_max_offset >= max_offset must be hold. */ + uint64_t unsent_max_offset; + /* window is the stream-level flow control window size. */ + uint64_t window; + } rx; + + const ngtcp2_mem *mem; + int64_t stream_id; + void *stream_user_data; + /* flags is bit-wise OR of zero or more of NGTCP2_STRM_FLAG_*. */ + uint32_t flags; + /* app_error_code is an error code the local endpoint sent in + RST_STREAM or STOP_SENDING. */ + uint64_t app_error_code; +}; + +/* + * ngtcp2_strm_init initializes |strm|. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_init(ngtcp2_strm *strm, int64_t stream_id, uint32_t flags, + uint64_t max_rx_offset, uint64_t max_tx_offset, + void *stream_user_data, const ngtcp2_mem *mem); + +/* + * ngtcp2_strm_free deallocates memory allocated for |strm|. This + * function does not free the memory pointed by |strm| itself. + */ +void ngtcp2_strm_free(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_rx_offset returns the minimum offset of stream data + * which is not received yet. + */ +uint64_t ngtcp2_strm_rx_offset(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_recv_reordering handles reordered data. + * + * It returns 0 if it succeeds, or one of the following negative error + * codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_recv_reordering(ngtcp2_strm *strm, const uint8_t *data, + size_t datalen, uint64_t offset); + +/* + * ngtcp2_strm_update_rx_offset tells that data up to offset bytes are + * received in order. + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_update_rx_offset(ngtcp2_strm *strm, uint64_t offset); + +/* + * ngtcp2_strm_shutdown shutdowns |strm|. |flags| should be + * NGTCP2_STRM_FLAG_SHUT_RD, and/or NGTCP2_STRM_FLAG_SHUT_WR. + */ +void ngtcp2_strm_shutdown(ngtcp2_strm *strm, uint32_t flags); + +/* + * ngtcp2_strm_streamfrq_push pushes |frc| to streamfrq for + * retransmission. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_streamfrq_push(ngtcp2_strm *strm, ngtcp2_frame_chain *frc); + +/* + * ngtcp2_strm_streamfrq_pop pops the first ngtcp2_frame_chain and + * assigns it to |*pfrc|. This function splits into or merges several + * ngtcp2_frame_chain objects so that the returned ngtcp2_frame_chain + * has at most |left| data length. If there is no frames to send, + * this function returns 0 and |*pfrc| is NULL. + * + * This function returns 0 if it succeeds, or one of the following + * negative error codes: + * + * NGTCP2_ERR_NOMEM + * Out of memory + */ +int ngtcp2_strm_streamfrq_pop(ngtcp2_strm *strm, ngtcp2_frame_chain **pfrc, + size_t left); + +/* + * ngtcp2_strm_streamfrq_unacked_offset returns the smallest offset of + * unacknowledged stream data held in strm->tx.streamfrq. + */ +uint64_t ngtcp2_strm_streamfrq_unacked_offset(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_top returns the first ngtcp2_frame_chain. + * The queue must not be empty. + */ +ngtcp2_frame_chain *ngtcp2_strm_streamfrq_top(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_empty returns nonzero if streamfrq is empty. + */ +int ngtcp2_strm_streamfrq_empty(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_streamfrq_clear removes all frames from streamfrq. + */ +void ngtcp2_strm_streamfrq_clear(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_is_tx_queued returns nonzero if |strm| is queued. + */ +int ngtcp2_strm_is_tx_queued(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_is_all_tx_data_acked returns nonzero if all outgoing + * data for |strm| which have sent so far have been acknowledged. + */ +int ngtcp2_strm_is_all_tx_data_acked(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_get_unacked_range_after returns the range that is not + * acknowledged yet and intersects or comes after |offset|. + */ +ngtcp2_range ngtcp2_strm_get_unacked_range_after(ngtcp2_strm *strm, + uint64_t offset); + +/* + * ngtcp2_strm_get_acked_offset returns offset, that is the data up to + * this offset have been acknowledged by a remote endpoint. It + * returns 0 if no data is acknowledged. + */ +uint64_t ngtcp2_strm_get_acked_offset(ngtcp2_strm *strm); + +/* + * ngtcp2_strm_ack_data tells |strm| that the data [offset, + * offset+len) is acknowledged by a remote endpoint. + */ +int ngtcp2_strm_ack_data(ngtcp2_strm *strm, uint64_t offset, uint64_t len); + +#endif /* NGTCP2_STRM_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c new file mode 100644 index 00000000000000..7a6f8afa051f20 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.c @@ -0,0 +1,232 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_vec.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_str.h" + +ngtcp2_vec *ngtcp2_vec_init(ngtcp2_vec *vec, const uint8_t *base, size_t len) { + vec->base = (uint8_t *)base; + vec->len = 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); +} + +size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n) { + size_t i; + size_t res = 0; + + for (i = 0; i < n; ++i) { + res += vec[i].len; + } + + return res; +} + +ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, + size_t *pdstcnt, size_t left, size_t maxcnt) { + size_t i; + size_t srccnt = *psrccnt; + size_t nmove; + size_t extra = 0; + + for (i = 0; i < srccnt; ++i) { + if (left >= src[i].len) { + left -= src[i].len; + continue; + } + + if (*pdstcnt && src[srccnt - 1].base + src[srccnt - 1].len == dst[0].base) { + if (*pdstcnt + srccnt - i - 1 > maxcnt) { + return -1; + } + + dst[0].len += src[srccnt - 1].len; + dst[0].base = src[srccnt - 1].base; + extra = src[srccnt - 1].len; + --srccnt; + } else if (*pdstcnt + srccnt - i > maxcnt) { + return -1; + } + + if (left == 0) { + *psrccnt = i; + } else { + *psrccnt = i + 1; + } + + nmove = srccnt - i; + if (nmove) { + memmove(dst + nmove, dst, sizeof(ngtcp2_vec) * (*pdstcnt)); + *pdstcnt += nmove; + memcpy(dst, src + i, sizeof(ngtcp2_vec) * nmove); + } + + dst[0].len -= left; + dst[0].base += left; + src[i].len = left; + + if (nmove == 0) { + extra -= left; + } + + return (ngtcp2_ssize)(ngtcp2_vec_len(dst, nmove) + extra); + } + + return 0; +} + +size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, + size_t *psrccnt, size_t left, size_t maxcnt) { + size_t orig_left = left; + size_t i; + ngtcp2_vec *a, *b; + + assert(maxcnt); + + if (*pdstcnt == 0) { + if (*psrccnt == 0) { + return 0; + } + + a = &dst[0]; + b = &src[0]; + + if (left >= b->len) { + *a = *b; + ++*pdstcnt; + left -= b->len; + i = 1; + } else { + a->len = left; + a->base = b->base; + + b->len -= left; + b->base += left; + + return left; + } + } else { + i = 0; + } + + for (; left && i < *psrccnt; ++i) { + a = &dst[*pdstcnt - 1]; + b = &src[i]; + + if (left >= b->len) { + if (a->base + a->len == b->base) { + a->len += b->len; + } else if (*pdstcnt == maxcnt) { + break; + } else { + dst[(*pdstcnt)++] = *b; + } + left -= b->len; + continue; + } + + if (a->base + a->len == b->base) { + a->len += left; + } else if (*pdstcnt == maxcnt) { + break; + } else { + dst[*pdstcnt].len = left; + dst[*pdstcnt].base = b->base; + ++*pdstcnt; + } + + b->len -= left; + b->base += left; + left = 0; + + break; + } + + memmove(src, src + i, sizeof(ngtcp2_vec) * (*psrccnt - i)); + *psrccnt -= i; + + return orig_left - left; +} + +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, + size_t dstcnt, const ngtcp2_vec *src, + size_t srccnt, size_t left) { + size_t i, j; + size_t len = left; + + *pnwritten = 0; + + for (i = 0, j = 0; left > 0 && i < srccnt && j < dstcnt;) { + if (src[i].len == 0) { + ++i; + continue; + } + dst[j] = src[i]; + if (dst[j].len > left) { + dst[j].len = left; + *pnwritten = len; + return j + 1; + } + left -= dst[j].len; + ++i; + ++j; + } + + *pnwritten = len - left; + + return j; +} + +void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt) { + memcpy(dst, src, sizeof(ngtcp2_vec) * cnt); +} diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h new file mode 100644 index 00000000000000..077820a9efed23 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_vec.h @@ -0,0 +1,114 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 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_VEC_H +#define NGTCP2_VEC_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_mem.h" + +/* + * ngtcp2_vec_lit is a convenient macro to fill the object pointed by + * |DEST| with the literal string |LIT|. + */ +#define ngtcp2_vec_lit(DEST, LIT) \ + ((DEST)->base = (uint8_t *)(LIT), (DEST)->len = sizeof(LIT) - 1, (DEST)) + +/* + * ngtcp2_vec_init initializes |vec| with the given parameters. It + * returns |vec|. + */ +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. + */ +size_t ngtcp2_vec_len(const ngtcp2_vec *vec, size_t n); + +/* + * ngtcp2_vec_split splits |src| to |dst| so that the sum of the + * length in |src| does not exceed |left| bytes. The |maxcnt| is the + * maximum number of elements which |dst| array can contain. The + * caller must set |*psrccnt| to the number of elements of |src|. + * Similarly, the caller must set |*pdstcnt| to the number of elements + * of |dst|. The split does not necessarily occur at the boundary of + * ngtcp2_vec object. After split has done, this function updates + * |*psrccnt| and |*pdstcnt|. This function returns the number of + * bytes moved from |src| to |dst|. If split cannot be made because + * doing so exceeds |maxcnt|, this function returns -1. + */ +ngtcp2_ssize ngtcp2_vec_split(ngtcp2_vec *src, size_t *psrccnt, ngtcp2_vec *dst, + size_t *pdstcnt, size_t left, size_t maxcnt); + +/* + * ngtcp2_vec_merge merges |src| into |dst| by moving at most |left| + * bytes from |src|. The |maxcnt| is the maximum number of elements + * which |dst| array can contain. The caller must set |*pdstcnt| to + * the number of elements of |dst|. Similarly, the caller must set + * |*psrccnt| to the number of elements of |src|. After merge has + * done, this function updates |*psrccnt| and |*pdstcnt|. This + * function returns the number of bytes moved from |src| to |dst|. + */ +size_t ngtcp2_vec_merge(ngtcp2_vec *dst, size_t *pdstcnt, ngtcp2_vec *src, + size_t *psrccnt, size_t left, size_t maxcnt); + +/* + * ngtcp2_vec_copy_at_most copies |src| of length |srccnt| to |dst| of + * length |dstcnt|. The total number of bytes which the copied + * ngtcp2_vec refers to is at most |left| and is assigned to + * |*pnwritten|. The empty elements in |src| are ignored. This + * function returns the number of elements copied. + */ +size_t ngtcp2_vec_copy_at_most(ngtcp2_vec *dst, size_t *pnwritten, + size_t dstcnt, const ngtcp2_vec *src, + size_t srccnt, size_t left); + +/* + * ngtcp2_vec_copy copies |src| of length |cnt| to |dst|. |dst| must + * have sufficient capacity. + */ +void ngtcp2_vec_copy(ngtcp2_vec *dst, const ngtcp2_vec *src, size_t cnt); + +#endif /* NGTCP2_VEC_H */ diff --git a/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c new file mode 100644 index 00000000000000..40f3ae3f9eade4 --- /dev/null +++ b/deps/ngtcp2/ngtcp2/lib/ngtcp2_version.c @@ -0,0 +1,39 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 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. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +static ngtcp2_info version = {NGTCP2_VERSION_AGE, NGTCP2_VERSION_NUM, + NGTCP2_VERSION}; + +ngtcp2_info *ngtcp2_version(int least_version) { + if (least_version > NGTCP2_VERSION_NUM) { + return NULL; + } + return &version; +} diff --git a/node.gypi b/node.gypi index f9dba2d4bdd4f4..dde4f8332d542e 100644 --- a/node.gypi +++ b/node.gypi @@ -326,6 +326,9 @@ # For tests './deps/openssl/openssl.gyp:openssl-cli', ], + # Set 1.0.0 as the API compability level to avoid the + # deprecation warnings when using OpenSSL 3.0. + 'defines': ['OPENSSL_API_COMPAT=0x10000000L'], 'conditions': [ # -force_load or --whole-archive are not applicable for # the static library @@ -360,16 +363,17 @@ }], ], }], - ], - }, { - # Set 1.0.0 as the API compability level to avoid the - # deprecation warnings when using OpenSSL 3.0. - 'defines': ['OPENSSL_API_COMPAT=0x10000000L'], - }]] - + ] + }], + [ 'openssl_quic=="true" and node_shared_ngtcp2=="false"', { + 'dependencies': [ './deps/ngtcp2/ngtcp2.gyp:ngtcp2' ] + }], + [ 'openssl_quic=="true" and node_shared_nghttp3=="false"', { + 'dependencies': [ './deps/ngtcp2/ngtcp2.gyp:nghttp3' ] + }] + ] }, { 'defines': [ 'HAVE_OPENSSL=0' ] }], - ], } diff --git a/src/node_metadata.cc b/src/node_metadata.cc index 8d0a725de45421..46d9be0dfcfdcf 100644 --- a/src/node_metadata.cc +++ b/src/node_metadata.cc @@ -13,6 +13,11 @@ #include <openssl/opensslv.h> #endif // HAVE_OPENSSL +#ifdef OPENSSL_INFO_QUIC +#include <ngtcp2/version.h> +#include <nghttp3/version.h> +#endif + #ifdef NODE_HAVE_I18N_SUPPORT #include <unicode/timezone.h> #include <unicode/ulocdata.h> @@ -95,6 +100,11 @@ Metadata::Versions::Versions() { icu = U_ICU_VERSION; unicode = U_UNICODE_VERSION; #endif // NODE_HAVE_I18N_SUPPORT + +#ifdef OPENSSL_INFO_QUIC + ngtcp2 = NGTCP2_VERSION; + nghttp3 = NGHTTP3_VERSION; +#endif } Metadata::Release::Release() : name(NODE_RELEASE) { diff --git a/src/node_metadata.h b/src/node_metadata.h index bf7e5d3ff4e811..4486d5af2c1622 100644 --- a/src/node_metadata.h +++ b/src/node_metadata.h @@ -6,6 +6,10 @@ #include <string> #include "node_version.h" +#if HAVE_OPENSSL +#include <openssl/crypto.h> +#endif // HAVE_OPENSSL + namespace node { // if this is a release build and no explicit base has been set @@ -48,10 +52,19 @@ namespace node { #define NODE_VERSIONS_KEY_INTL(V) #endif // NODE_HAVE_I18N_SUPPORT +#ifdef OPENSSL_INFO_QUIC +#define NODE_VERSIONS_KEY_QUIC(V) \ + V(ngtcp2) \ + V(nghttp3) +#else +#define NODE_VERSIONS_KEY_QUIC(V) +#endif + #define NODE_VERSIONS_KEYS(V) \ NODE_VERSIONS_KEYS_BASE(V) \ NODE_VERSIONS_KEY_CRYPTO(V) \ - NODE_VERSIONS_KEY_INTL(V) + NODE_VERSIONS_KEY_INTL(V) \ + NODE_VERSIONS_KEY_QUIC(V) class Metadata { public: diff --git a/test/common/index.js b/test/common/index.js index 8190be9565e742..3967b521c12659 100644 --- a/test/common/index.js +++ b/test/common/index.js @@ -54,6 +54,8 @@ const hasCrypto = Boolean(process.versions.openssl) && const hasOpenSSL3 = hasCrypto && require('crypto').constants.OPENSSL_VERSION_NUMBER >= 805306368; +const hasQuic = hasCrypto && !!process.config.variables.openssl_quic; + // Check for flags. Skip this for workers (both, the `cluster` module and // `worker_threads`) and child processes. // If the binary was built without-ssl then the crypto flags are @@ -730,6 +732,7 @@ const common = { hasIntl, hasCrypto, hasOpenSSL3, + hasQuic, hasMultiLocalhost, invalidArgTypeHelper, isAIX, diff --git a/test/parallel/test-process-versions.js b/test/parallel/test-process-versions.js index 666c8ead3b15d2..464a8a4ecec58a 100644 --- a/test/parallel/test-process-versions.js +++ b/test/parallel/test-process-versions.js @@ -2,13 +2,28 @@ const common = require('../common'); const assert = require('assert'); -const expected_keys = ['ares', 'brotli', 'modules', 'node', - 'uv', 'v8', 'zlib', 'nghttp2', 'napi', 'llhttp']; +const expected_keys = [ + 'ares', + 'brotli', + 'modules', + 'node', + 'uv', + 'v8', + 'zlib', + 'nghttp2', + 'napi', + 'llhttp' +]; if (common.hasCrypto) { expected_keys.push('openssl'); } +if (common.hasQuic) { + expected_keys.push('ngtcp2'); + expected_keys.push('nghttp3'); +} + if (common.hasIntl) { expected_keys.push('icu'); expected_keys.push('cldr'); diff --git a/tools/getsharedopensslhasquic.py b/tools/getsharedopensslhasquic.py index 549a0eacdaee94..00d4b4b424ee5d 100644 --- a/tools/getsharedopensslhasquic.py +++ b/tools/getsharedopensslhasquic.py @@ -3,17 +3,18 @@ import re def get_has_quic(include_path): - openssl_crypto_h = os.path.join( - include_path, - 'openssl', - 'crypto.h') + if include_path: + openssl_crypto_h = os.path.join( + include_path, + 'openssl', + 'crypto.h') - f = open(openssl_crypto_h) + f = open(openssl_crypto_h) - regex = '^#\s*define OPENSSL_INFO_QUIC' + regex = '^#\s*define OPENSSL_INFO_QUIC' - for line in f: - if (re.match(regex, line)): - return True + for line in f: + if (re.match(regex, line)): + return True return False diff --git a/tools/license-builder.sh b/tools/license-builder.sh index d1d173943c1283..05a15471c7e77f 100755 --- a/tools/license-builder.sh +++ b/tools/license-builder.sh @@ -113,5 +113,7 @@ addlicense "rimraf" "lib/internal/fs/rimraf.js" \ "$(curl -sL https://raw.githubusercontent.com/isaacs/rimraf/0e365ac4e4d64a25aa2a3cc026348f13410210e1/LICENSE)" addlicense "uvwasi" "deps/uvwasi" "$(cat "${rootdir}"/deps/uvwasi/LICENSE)" +addlicense "ngtcp2" "deps/ngtcp2/ngtcp2/" "$(cat "${rootdir}"/deps/ngtcp2/LICENSE_ngtcp2)" +addlicense "nghttp3" "deps/ngtcp2/nghttp3/" "$(cat "${rootdir}"/deps/ngtcp2/LICENSE_nghttp3)" mv "$tmplicense" "$licensefile" From 4700042a9bd588d250be7ce1d4cd67fd517a8e40 Mon Sep 17 00:00:00 2001 From: Nitzan Uziely <linkgoron@gmail.com> Date: Fri, 19 Mar 2021 19:21:41 +0200 Subject: [PATCH 18/85] doc: add @linkgoron to collaborators fixes: https://github.com/nodejs/node/issues/37619 PR-URL: https://github.com/nodejs/node/pull/37817 Fixes: https://github.com/nodejs/node/issues/37619 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Myles Borins <myles.borins@gmail.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a051128fe30edf..6319043ca5e2e1 100644 --- a/README.md +++ b/README.md @@ -353,6 +353,8 @@ For information about the governance of the Node.js project, see **Chengzhong Wu** <legendecas@gmail.com> (he/him) * [Leko](https://github.com/Leko) - **Shingo Inoue** <leko.noor@gmail.com> (he/him) +* [linkgoron](https://github.com/linkgoron) - +**Nitzan Uziely** <linkgoron@gmail.com> * [lpinca](https://github.com/lpinca) - **Luigi Pinca** <luigipinca@gmail.com> (he/him) * [lundibundi](https://github.com/lundibundi) - From af7489cb6c02ef3b4d120c67f0f9f22e1b3d4196 Mon Sep 17 00:00:00 2001 From: Mattias Buelens <mattias@buelens.com> Date: Thu, 11 Mar 2021 23:25:45 +0100 Subject: [PATCH 19/85] lib: add brand checks to AbortController and AbortSignal PR-URL: https://github.com/nodejs/node/pull/37720 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> --- lib/internal/abort_controller.js | 32 ++++++++++++-- test/parallel/test-abortcontroller.js | 60 +++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/lib/internal/abort_controller.js b/lib/internal/abort_controller.js index 5a16a56a4a7eb8..6c80aa7bf4f2b3 100644 --- a/lib/internal/abort_controller.js +++ b/lib/internal/abort_controller.js @@ -23,6 +23,11 @@ const { customInspectSymbol, } = require('internal/util'); const { inspect } = require('internal/util/inspect'); +const { + codes: { + ERR_INVALID_THIS, + } +} = require('internal/errors'); const kAborted = Symbol('kAborted'); @@ -37,13 +42,21 @@ function customInspect(self, obj, depth, options) { return `${self.constructor.name} ${inspect(obj, opts)}`; } +function validateAbortSignal(obj) { + if (obj?.[kAborted] === undefined) + throw new ERR_INVALID_THIS('AbortSignal'); +} + class AbortSignal extends EventTarget { constructor() { // eslint-disable-next-line no-restricted-syntax throw new TypeError('Illegal constructor'); } - get aborted() { return !!this[kAborted]; } + get aborted() { + validateAbortSignal(this); + return !!this[kAborted]; + } [customInspectSymbol](depth, options) { return customInspect(this, { @@ -89,13 +102,26 @@ function abortSignal(signal) { // initializers for now: // https://bugs.chromium.org/p/v8/issues/detail?id=10704 const kSignal = Symbol('signal'); + +function validateAbortController(obj) { + if (obj?.[kSignal] === undefined) + throw new ERR_INVALID_THIS('AbortController'); +} + class AbortController { constructor() { this[kSignal] = createAbortSignal(); } - get signal() { return this[kSignal]; } - abort() { abortSignal(this[kSignal]); } + get signal() { + validateAbortController(this); + return this[kSignal]; + } + + abort() { + validateAbortController(this); + abortSignal(this[kSignal]); + } [customInspectSymbol](depth, options) { return customInspect(this, { diff --git a/test/parallel/test-abortcontroller.js b/test/parallel/test-abortcontroller.js index 2b36da332e44aa..d26ec8641a671e 100644 --- a/test/parallel/test-abortcontroller.js +++ b/test/parallel/test-abortcontroller.js @@ -72,3 +72,63 @@ const { ok, strictEqual, throws } = require('assert'); const signal = AbortSignal.abort(); ok(signal.aborted); } + +{ + // Test that AbortController properties and methods validate the receiver + const acSignalGet = Object.getOwnPropertyDescriptor( + AbortController.prototype, + 'signal' + ).get; + const acAbort = AbortController.prototype.abort; + + const goodController = new AbortController(); + ok(acSignalGet.call(goodController)); + acAbort.call(goodController); + + const badAbortControllers = [ + null, + undefined, + 0, + NaN, + true, + 'AbortController', + Object.create(AbortController.prototype) + ]; + for (const badController of badAbortControllers) { + throws( + () => acSignalGet.call(badController), + { code: 'ERR_INVALID_THIS', name: 'TypeError' } + ); + throws( + () => acAbort.call(badController), + { code: 'ERR_INVALID_THIS', name: 'TypeError' } + ); + } +} + +{ + // Test that AbortSignal properties validate the receiver + const signalAbortedGet = Object.getOwnPropertyDescriptor( + AbortSignal.prototype, + 'aborted' + ).get; + + const goodSignal = new AbortController().signal; + strictEqual(signalAbortedGet.call(goodSignal), false); + + const badAbortSignals = [ + null, + undefined, + 0, + NaN, + true, + 'AbortSignal', + Object.create(AbortSignal.prototype) + ]; + for (const badSignal of badAbortSignals) { + throws( + () => signalAbortedGet.call(badSignal), + { code: 'ERR_INVALID_THIS', name: 'TypeError' } + ); + } +} From c37806d0ba866a0cbefedc1bb2c99c9760108627 Mon Sep 17 00:00:00 2001 From: Darshan Sen <raisinten@gmail.com> Date: Sun, 14 Mar 2021 22:04:11 +0530 Subject: [PATCH 20/85] crypto: use macro map for NodeCryptoError PR-URL: https://github.com/nodejs/node/pull/37758 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- src/crypto/crypto_util.h | 39 +++++++++++++++------------------------ 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/crypto/crypto_util.h b/src/crypto/crypto_util.h index 1a7bef77a40930..928c0cd38969be 100644 --- a/src/crypto/crypto_util.h +++ b/src/crypto/crypto_util.h @@ -159,13 +159,18 @@ void Decode(const v8::FunctionCallbackInfo<v8::Value>& args, } } +#define NODE_CRYPTO_ERROR_CODES_MAP(V) \ + V(CIPHER_JOB_FAILED, "Cipher job failed") \ + V(DERIVING_BITS_FAILED, "Deriving bits failed") \ + V(ENGINE_NOT_FOUND, "Engine \"%s\" was not found") \ + V(INVALID_KEY_TYPE, "Invalid key type") \ + V(KEY_GENERATION_JOB_FAILED, "Key generation job failed") \ + V(OK, "Ok") \ + enum class NodeCryptoError { - CIPHER_JOB_FAILED, - DERIVING_BITS_FAILED, - ENGINE_NOT_FOUND, - INVALID_KEY_TYPE, - KEY_GENERATION_JOB_FAILED, - OK +#define V(CODE, DESCRIPTION) CODE, + NODE_CRYPTO_ERROR_CODES_MAP(V) +#undef V }; // Utility struct used to harvest error information from openssl's error stack @@ -194,24 +199,10 @@ template <typename... Args> void CryptoErrorStore::Insert(const NodeCryptoError error, Args&&... args) { const char* error_string = nullptr; switch (error) { - case NodeCryptoError::CIPHER_JOB_FAILED: - error_string = "Cipher job failed"; - break; - case NodeCryptoError::DERIVING_BITS_FAILED: - error_string = "Deriving bits failed"; - break; - case NodeCryptoError::ENGINE_NOT_FOUND: - error_string = "Engine \"%s\" was not found"; - break; - case NodeCryptoError::INVALID_KEY_TYPE: - error_string = "Invalid key type"; - break; - case NodeCryptoError::KEY_GENERATION_JOB_FAILED: - error_string = "Key generation failed"; - break; - case NodeCryptoError::OK: - error_string = "Ok"; - break; +#define V(CODE, DESCRIPTION) \ + case NodeCryptoError::CODE: error_string = DESCRIPTION; break; + NODE_CRYPTO_ERROR_CODES_MAP(V) +#undef V } errors_.emplace_back(SPrintF(error_string, std::forward<Args>(args)...)); From 9d160daa893c083dba8998441b6d36a14be08b39 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Wed, 17 Mar 2021 12:31:43 -0700 Subject: [PATCH 21/85] doc: add legacy status to stability index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37784 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> --- doc/api/documentation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/api/documentation.md b/doc/api/documentation.md index a7a591d3733853..019040bdb61099 100644 --- a/doc/api/documentation.md +++ b/doc/api/documentation.md @@ -37,6 +37,12 @@ The stability indices are as follows: > Stability: 2 - Stable. Compatibility with the npm ecosystem is a high > priority. +<!-- separator --> + +> Stability: 3 - Legacy. The feature is no longer recommended for use. While it +likely will not be removed, and is still covered by semantic-versioning +guarantees, use of the feature should be avoided. + Use caution when making use of Experimental features, particularly within modules. Users may not be aware that experimental features are being used. Bugs or behavior changes may surprise users when Experimental API From 185d4cd4aae9825db492e2f283383da12633da82 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Wed, 17 Mar 2021 12:40:58 -0700 Subject: [PATCH 22/85] doc: revoke deprecation of legacy url, change status to legacy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: James M Snell <jasnell@gmail.com> Fixes: https://github.com/nodejs/node/issues/25099 PR-URL: https://github.com/nodejs/node/pull/37784 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> --- doc/api/deprecations.md | 5 ++++- doc/api/url.md | 30 +++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index ac0f99bf4fcf95..13f0d9eb40c5a0 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2169,12 +2169,15 @@ future release. ### DEP0116: Legacy URL API <!-- YAML changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37784 + description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 pr-url: https://github.com/nodejs/node/pull/22715 description: Documentation-only deprecation. --> -Type: Documentation-only +Type: Deprecation revoked The [Legacy URL API][] is deprecated. This includes [`url.format()`][], [`url.parse()`][], [`url.resolve()`][], and the [legacy `urlObject`][]. Please diff --git a/doc/api/url.md b/doc/api/url.md index e78b15d09d28d1..f93cf741473794 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -1075,18 +1075,29 @@ console.log(urlToHttpOptions(myUrl)); ## Legacy URL API <!-- YAML -deprecated: v11.0.0 +changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37784 + description: Deprecation revoked. Status changed to "Legacy". + - version: v11.0.0 + pr-url: https://github.com/nodejs/node/pull/22715 + description: This API is deprecated. --> +> Stability: 3 - Legacy: Use the WHATWG URL API instead. + ### Legacy `urlObject` <!-- YAML changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37784 + description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 pr-url: https://github.com/nodejs/node/pull/22715 description: The Legacy URL API is deprecated. Use the WHATWG URL API. --> -> Stability: 0 - Deprecated: Use the WHATWG URL API instead. +> Stability: 3 - Legacy: Use the WHATWG URL API instead. The legacy `urlObject` (`require('url').Url`) is created and returned by the `url.parse()` function. @@ -1192,6 +1203,9 @@ forward-slash characters (`/`) are required following the colon in the <!-- YAML added: v0.1.25 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37784 + description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 pr-url: https://github.com/nodejs/node/pull/22715 description: The Legacy URL API is deprecated. Use the WHATWG URL API. @@ -1203,7 +1217,7 @@ changes: times. --> -> Stability: 0 - Deprecated: Use the WHATWG URL API instead. +> Stability: 3 - Legacy: Use the WHATWG URL API instead. * `urlObject` {Object|string} A URL object (as returned by `url.parse()` or constructed otherwise). If a string, it is converted to an object by passing @@ -1285,6 +1299,9 @@ The formatting process operates as follows: <!-- YAML added: v0.1.25 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37784 + description: Deprecation revoked. Status changed to "Legacy". - version: v11.14.0 pr-url: https://github.com/nodejs/node/pull/26941 description: The `pathname` property on the returned URL object is now `/` @@ -1299,7 +1316,7 @@ changes: when no query string is present. --> -> Stability: 0 - Deprecated: Use the WHATWG URL API instead. +> Stability: 3 - Legacy: Use the WHATWG URL API instead. * `urlString` {string} The URL string to parse. * `parseQueryString` {boolean} If `true`, the `query` property will always @@ -1329,6 +1346,9 @@ incorrect handling of usernames and passwords have been identified. <!-- YAML added: v0.1.25 changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/37784 + description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 pr-url: https://github.com/nodejs/node/pull/22715 description: The Legacy URL API is deprecated. Use the WHATWG URL API. @@ -1347,7 +1367,7 @@ changes: contains a hostname. --> -> Stability: 0 - Deprecated: Use the WHATWG URL API instead. +> Stability: 3 - Legacy: Use the WHATWG URL API instead. * `from` {string} The Base URL being resolved against. * `to` {string} The HREF URL being resolved. From 6da0a0e85aca38897bb0a288a7b20e016f638f42 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Fri, 19 Mar 2021 10:54:53 -0700 Subject: [PATCH 23/85] doc: apply style for legacy status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37784 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Vladimir de Turckheim <vlad2t@hotmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> --- doc/api_assets/style.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index fb34bcf12a2195..189015268bcb72 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -4,6 +4,7 @@ --black1: #090c15; --black2: #2c3437; --black3: #0d111d; + --blue1: #00f; --white: #ffffff; --white-smoke: #f2f2f2; --grey-smoke: #e9edf0; @@ -263,6 +264,10 @@ ol.version-picker li:last-child a { background-color: var(--green2); } +.api_stability_3 { + background-color: var(--blue1); +} + .module_stability { vertical-align: middle; } @@ -372,7 +377,7 @@ dd + dt.pre { #apicontent { padding-top: 1rem; } - + #apicontent section { content-visibility: auto; contain-intrinsic-size: 1px 5000px; From b09d03268368394592799e9521e574e928dedf88 Mon Sep 17 00:00:00 2001 From: Derek Lewis <DerekNonGeneric@inf.is> Date: Fri, 12 Mar 2021 03:33:35 -0500 Subject: [PATCH 24/85] doc: move Derek Lewis back to collaborators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Conclude winter break and resume collaboratorship at start of spring. Refs: https://github.com/nodejs/node/pull/36514 PR-URL: https://github.com/nodejs/node/pull/37726 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Geoffrey Booth <webmaster@geoffreybooth.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6319043ca5e2e1..bc7383fcbf444c 100644 --- a/README.md +++ b/README.md @@ -285,6 +285,8 @@ For information about the governance of the Node.js project, see **Danielle Adams** <adamzdanielle@gmail.com> (she/her) * [davisjam](https://github.com/davisjam) - **Jamie Davis** <davisjam@vt.edu> (he/him) +* [DerekNonGeneric](https://github.com/DerekNonGeneric) - +**Derek Lewis** <DerekNonGeneric@inf.is> (he/him) * [devnexen](https://github.com/devnexen) - **David Carlier** <devnexen@gmail.com> * [devsnek](https://github.com/devsnek) - @@ -478,8 +480,6 @@ For information about the governance of the Node.js project, see **Claudio Rodriguez** <cjrodr@yahoo.com> * [DavidCai1993](https://github.com/DavidCai1993) - **David Cai** <davidcai1993@yahoo.com> (he/him) -* [DerekNonGeneric](https://github.com/DerekNonGeneric) - -**Derek Lewis** <DerekNonGeneric@inf.is> (he/him) * [digitalinfinity](https://github.com/digitalinfinity) - **Hitesh Kanwathirtha** <digitalinfinity@gmail.com> (he/him) * [eljefedelrodeodeljefe](https://github.com/eljefedelrodeodeljefe) - From b6be4724566ab7704321be6db97109b10e26dd5b Mon Sep 17 00:00:00 2001 From: Jiawen Geng <technicalcute@gmail.com> Date: Thu, 18 Mar 2021 06:59:30 +0000 Subject: [PATCH 25/85] tools: update gitignore for CMake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37793 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index c5ae6ea68d82fa..f2d8c226a698f1 100644 --- a/.gitignore +++ b/.gitignore @@ -139,6 +139,15 @@ tools/*/*.i.tmp # === Rules for Windows vcbuild.bat === /temp-vcbuild +# === Rules for CMake === +cmake-build-debug/ +CMakeCache.txt +CMakeFiles +CTestTestfile.cmake +cmake_install.cmake +install_manifest.txt +*.cbp + # === Global Rules === # Keep last to avoid being excluded *.pyc From 3376051a0e25e8126a31220fa2ad03a0082fbe2a Mon Sep 17 00:00:00 2001 From: Antoine du Hamel <duhamelantoine1995@gmail.com> Date: Wed, 17 Mar 2021 23:44:34 +0100 Subject: [PATCH 26/85] doc: fix JS flavor selection PR-URL: https://github.com/nodejs/node/pull/37791 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- doc/api_assets/style.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api_assets/style.css b/doc/api_assets/style.css index 189015268bcb72..04299595ca80d6 100644 --- a/doc/api_assets/style.css +++ b/doc/api_assets/style.css @@ -782,7 +782,7 @@ kbd { .js-flavor-selector:checked { background-image: url(./js-flavor-esm.svg); } -.js-flavor-selector:not(:checked) ~ .esm, +.js-flavor-selector:not(:checked) ~ .mjs, .js-flavor-selector:checked ~ .cjs { display: none; } From e85f311cf2784522a88291e7f4e28953cd057616 Mon Sep 17 00:00:00 2001 From: Wassim Chegham <github@wassim.dev> Date: Thu, 18 Mar 2021 17:52:08 +0100 Subject: [PATCH 27/85] test: refactor code to use AbortSignal.abort() PR-URL: https://github.com/nodejs/node/pull/37798 Refs: https://github.com/whatwg/dom/pull/960 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> Reviewed-By: Zeyu Yang <himself65@outlook.com> --- ...child-process-exec-abortcontroller-promisified.js | 4 +--- ...d-process-execFile-promisified-abortController.js | 4 +--- test/parallel/test-child-process-execfile.js | 4 +--- .../parallel/test-child-process-fork-abort-signal.js | 8 ++------ test/parallel/test-child-process-spawn-controller.js | 5 +---- test/parallel/test-dgram-close-signal.js | 4 +--- test/parallel/test-event-on-async-iterator.js | 10 ++++------ test/parallel/test-events-once.js | 10 ++++------ .../test-fs-promises-file-handle-readFile.js | 4 +--- test/parallel/test-fs-promises-readfile.js | 4 +--- test/parallel/test-fs-readfile.js | 4 +--- test/parallel/test-fs-watch-abort-signal.js | 4 +--- .../test-net-server-listen-options-signal.js | 5 ++--- test/parallel/test-stream-pipeline.js | 4 +--- test/parallel/test-stream-writable-destroy.js | 5 ++--- test/parallel/test-timers-promisified.js | 12 +++--------- 16 files changed, 27 insertions(+), 64 deletions(-) diff --git a/test/parallel/test-child-process-exec-abortcontroller-promisified.js b/test/parallel/test-child-process-exec-abortcontroller-promisified.js index ac77d39938d8b6..a19b797ba6671d 100644 --- a/test/parallel/test-child-process-exec-abortcontroller-promisified.js +++ b/test/parallel/test-child-process-exec-abortcontroller-promisified.js @@ -37,9 +37,7 @@ const waitCommand = common.isLinux ? } { - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); // Abort in advance const promise = execPromisifed(waitCommand, { signal }); assert.rejects(promise, /AbortError/, 'pre aborted signal failed') diff --git a/test/parallel/test-child-process-execFile-promisified-abortController.js b/test/parallel/test-child-process-execFile-promisified-abortController.js index ccfcafc5d51e58..38c177eb33389c 100644 --- a/test/parallel/test-child-process-execFile-promisified-abortController.js +++ b/test/parallel/test-child-process-execFile-promisified-abortController.js @@ -29,9 +29,7 @@ const invalidArgTypeError = { { // Verify that the signal option works properly when already aborted - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); assert.rejects( promisified(process.execPath, [echoFixture, 0], { signal }), diff --git a/test/parallel/test-child-process-execfile.js b/test/parallel/test-child-process-execfile.js index a8f0cabacc2860..40cb8cd3afab01 100644 --- a/test/parallel/test-child-process-execfile.js +++ b/test/parallel/test-child-process-execfile.js @@ -69,9 +69,7 @@ const execOpts = { encoding: 'utf8', shell: true }; { // Verify that does not spawn a child if already aborted - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); const check = common.mustCall((err) => { assert.strictEqual(err.code, 'ABORT_ERR'); diff --git a/test/parallel/test-child-process-fork-abort-signal.js b/test/parallel/test-child-process-fork-abort-signal.js index 93a968366270cc..9982444c429552 100644 --- a/test/parallel/test-child-process-fork-abort-signal.js +++ b/test/parallel/test-child-process-fork-abort-signal.js @@ -23,9 +23,7 @@ const { fork } = require('child_process'); } { // Test passing an already aborted signal to a forked child_process - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { signal }); @@ -40,9 +38,7 @@ const { fork } = require('child_process'); { // Test passing a different kill signal - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); const cp = fork(fixtures.path('child-process-stay-alive-forever.js'), { signal, killSignal: 'SIGKILL', diff --git a/test/parallel/test-child-process-spawn-controller.js b/test/parallel/test-child-process-spawn-controller.js index 36c4112cbe00da..5199c7a09557c9 100644 --- a/test/parallel/test-child-process-spawn-controller.js +++ b/test/parallel/test-child-process-spawn-controller.js @@ -29,10 +29,7 @@ const aliveScript = fixtures.path('child-process-stay-alive-forever.js'); { // Verify that passing an already-aborted signal works. - const controller = new AbortController(); - const { signal } = controller; - - controller.abort(); + const signal = AbortSignal.abort(); const cp = spawn(process.execPath, [aliveScript], { signal, diff --git a/test/parallel/test-dgram-close-signal.js b/test/parallel/test-dgram-close-signal.js index f1008b599dbd18..26de38b504dd4a 100644 --- a/test/parallel/test-dgram-close-signal.js +++ b/test/parallel/test-dgram-close-signal.js @@ -25,9 +25,7 @@ const dgram = require('dgram'); { // Test close with pre-aborted signal. - const controller = new AbortController(); - controller.abort(); - const { signal } = controller; + const signal = AbortSignal.abort(); const server = dgram.createSocket({ type: 'udp4', signal }); server.on('close', common.mustCall()); } diff --git a/test/parallel/test-event-on-async-iterator.js b/test/parallel/test-event-on-async-iterator.js index 276a6f62d37e33..9ad319136fa493 100644 --- a/test/parallel/test-event-on-async-iterator.js +++ b/test/parallel/test-event-on-async-iterator.js @@ -248,28 +248,26 @@ async function nodeEventTarget() { async function abortableOnBefore() { const ee = new EventEmitter(); - const ac = new AbortController(); - ac.abort(); + const abortedSignal = AbortSignal.abort(); [1, {}, null, false, 'hi'].forEach((signal) => { assert.throws(() => on(ee, 'foo', { signal }), { code: 'ERR_INVALID_ARG_TYPE' }); }); - assert.throws(() => on(ee, 'foo', { signal: ac.signal }), { + assert.throws(() => on(ee, 'foo', { signal: abortedSignal }), { name: 'AbortError' }); } async function eventTargetAbortableOnBefore() { const et = new EventTarget(); - const ac = new AbortController(); - ac.abort(); + const abortedSignal = AbortSignal.abort(); [1, {}, null, false, 'hi'].forEach((signal) => { assert.throws(() => on(et, 'foo', { signal }), { code: 'ERR_INVALID_ARG_TYPE' }); }); - assert.throws(() => on(et, 'foo', { signal: ac.signal }), { + assert.throws(() => on(et, 'foo', { signal: abortedSignal }), { name: 'AbortError' }); } diff --git a/test/parallel/test-events-once.js b/test/parallel/test-events-once.js index 14b8ea5815a61a..56042b1ecee4f9 100644 --- a/test/parallel/test-events-once.js +++ b/test/parallel/test-events-once.js @@ -133,9 +133,8 @@ async function prioritizesEventEmitter() { async function abortSignalBefore() { const ee = new EventEmitter(); - const ac = new AbortController(); ee.on('error', common.mustNotCall()); - ac.abort(); + const abortedSignal = AbortSignal.abort(); await Promise.all([1, {}, 'hi', null, false].map((signal) => { return rejects(once(ee, 'foo', { signal }), { @@ -143,7 +142,7 @@ async function abortSignalBefore() { }); })); - return rejects(once(ee, 'foo', { signal: ac.signal }), { + return rejects(once(ee, 'foo', { signal: abortedSignal }), { name: 'AbortError' }); } @@ -184,8 +183,7 @@ async function abortSignalRemoveListener() { async function eventTargetAbortSignalBefore() { const et = new EventTarget(); - const ac = new AbortController(); - ac.abort(); + const abortedSignal = AbortSignal.abort(); await Promise.all([1, {}, 'hi', null, false].map((signal) => { return rejects(once(et, 'foo', { signal }), { @@ -193,7 +191,7 @@ async function eventTargetAbortSignalBefore() { }); })); - return rejects(once(et, 'foo', { signal: ac.signal }), { + return rejects(once(et, 'foo', { signal: abortedSignal }), { name: 'AbortError' }); } diff --git a/test/parallel/test-fs-promises-file-handle-readFile.js b/test/parallel/test-fs-promises-file-handle-readFile.js index 66dc8f0fb0e0a1..fc28dd23328df7 100644 --- a/test/parallel/test-fs-promises-file-handle-readFile.js +++ b/test/parallel/test-fs-promises-file-handle-readFile.js @@ -58,9 +58,7 @@ async function doReadAndCancel() { const fileHandle = await open(filePathForHandle, 'w+'); const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8'); fs.writeFileSync(filePathForHandle, buffer); - const controller = new AbortController(); - const { signal } = controller; - controller.abort(); + const signal = AbortSignal.abort(); await assert.rejects(readFile(fileHandle, { signal }), { name: 'AbortError' }); diff --git a/test/parallel/test-fs-promises-readfile.js b/test/parallel/test-fs-promises-readfile.js index d1c79580b835b6..a7fc46e5a6544a 100644 --- a/test/parallel/test-fs-promises-readfile.js +++ b/test/parallel/test-fs-promises-readfile.js @@ -43,9 +43,7 @@ async function validateReadFileProc() { } function validateReadFileAbortLogicBefore() { - const controller = new AbortController(); - const signal = controller.signal; - controller.abort(); + const signal = AbortSignal.abort(); assert.rejects(readFile(fn, { signal }), { name: 'AbortError' }); diff --git a/test/parallel/test-fs-readfile.js b/test/parallel/test-fs-readfile.js index b7d798a5db8e9b..a081eec099890d 100644 --- a/test/parallel/test-fs-readfile.js +++ b/test/parallel/test-fs-readfile.js @@ -54,9 +54,7 @@ for (const e of fileInfo) { } { // Test cancellation, before - const controller = new AbortController(); - const signal = controller.signal; - controller.abort(); + const signal = AbortSignal.abort(); fs.readFile(fileInfo[0].name, { signal }, common.mustCall((err, buf) => { assert.strictEqual(err.name, 'AbortError'); })); diff --git a/test/parallel/test-fs-watch-abort-signal.js b/test/parallel/test-fs-watch-abort-signal.js index cbe9a1457dc9ae..33936d2d491f8b 100644 --- a/test/parallel/test-fs-watch-abort-signal.js +++ b/test/parallel/test-fs-watch-abort-signal.js @@ -24,9 +24,7 @@ const fixtures = require('../common/fixtures'); { // Signal aborted before creating the watcher const file = fixtures.path('empty.js'); - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); const watcher = fs.watch(file, { signal }); watcher.once('close', common.mustCall()); } diff --git a/test/parallel/test-net-server-listen-options-signal.js b/test/parallel/test-net-server-listen-options-signal.js index 80efaf1700cd75..60e78fa00c5080 100644 --- a/test/parallel/test-net-server-listen-options-signal.js +++ b/test/parallel/test-net-server-listen-options-signal.js @@ -26,8 +26,7 @@ const net = require('net'); { // Test close with pre-aborted signal. const server = net.createServer(); - const controller = new AbortController(); - controller.abort(); + const signal = AbortSignal.abort(); server.on('close', common.mustCall()); - server.listen({ port: 0, signal: controller.signal }); + server.listen({ port: 0, signal }); } diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index 86f9006d83c430..7940d3a90d1828 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -521,9 +521,7 @@ const net = require('net'); // Check pre-aborted signal const pipelinePromise = promisify(pipeline); async function run() { - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); + const signal = AbortSignal.abort(); async function* producer() { yield '5'; await Promise.resolve(); diff --git a/test/parallel/test-stream-writable-destroy.js b/test/parallel/test-stream-writable-destroy.js index 9859c59e446c95..7cd800b0938138 100644 --- a/test/parallel/test-stream-writable-destroy.js +++ b/test/parallel/test-stream-writable-destroy.js @@ -448,11 +448,10 @@ const assert = require('assert'); } { - const ac = new AbortController(); - ac.abort(); + const signal = AbortSignal.abort(); const write = new Writable({ - signal: ac.signal, + signal, write(chunk, enc, cb) { cb(); } }); diff --git a/test/parallel/test-timers-promisified.js b/test/parallel/test-timers-promisified.js index 04c3ff96149352..45da5036727061 100644 --- a/test/parallel/test-timers-promisified.js +++ b/test/parallel/test-timers-promisified.js @@ -98,9 +98,7 @@ process.on('multipleResolves', common.mustNotCall()); } { - const ac = new AbortController(); - const signal = ac.signal; - ac.abort(); // Abort in advance + const signal = AbortSignal.abort(); // Abort in advance assert.rejects(setPromiseTimeout(10, undefined, { signal }), /AbortError/) .then(common.mustCall()); } @@ -114,17 +112,13 @@ process.on('multipleResolves', common.mustNotCall()); } { - const ac = new AbortController(); - const signal = ac.signal; - ac.abort(); // Abort in advance + const signal = AbortSignal.abort(); // Abort in advance assert.rejects(setPromiseImmediate(10, { signal }), /AbortError/) .then(common.mustCall()); } { - const ac = new AbortController(); - const { signal } = ac; - ac.abort(); // Abort in advance + const signal = AbortSignal.abort(); // Abort in advance const iterable = setInterval(1, undefined, { signal }); const iterator = iterable[Symbol.asyncIterator](); From a5205819d83dcf7f422546072f0397a01f0fc3f0 Mon Sep 17 00:00:00 2001 From: simov <simeonvelichkov@gmail.com> Date: Mon, 8 Mar 2021 21:27:49 +0200 Subject: [PATCH 28/85] http: add http.ClientRequest.getRawHeaderNames() Fixes: https://github.com/nodejs/node/issues/37641 PR-URL: https://github.com/nodejs/node/pull/37660 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- doc/api/deprecations.md | 1 + doc/api/http.md | 18 ++++++++++++++++++ lib/_http_outgoing.js | 19 +++++++++++++++++++ test/parallel/test-http-mutable-headers.js | 15 ++++++++++++++- 4 files changed, 52 insertions(+), 1 deletion(-) diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 13f0d9eb40c5a0..0a539db586bb85 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -1347,6 +1347,7 @@ The `http` module `OutgoingMessage.prototype._headers` and the public methods (e.g. `OutgoingMessage.prototype.getHeader()`, `OutgoingMessage.prototype.getHeaders()`, `OutgoingMessage.prototype.getHeaderNames()`, +`OutgoingMessage.prototype.getRawHeaderNames()`, `OutgoingMessage.prototype.hasHeader()`, `OutgoingMessage.prototype.removeHeader()`, `OutgoingMessage.prototype.setHeader()`) for working with outgoing headers. diff --git a/doc/api/http.md b/doc/api/http.md index e51ac128b84603..65fb0f47bf8023 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -763,6 +763,24 @@ const cookie = request.getHeader('Cookie'); // 'cookie' is of type string[] ``` +### `request.getRawHeaderNames()` +<!-- YAML +added: REPLACEME +--> + +* Returns: {string[]} + +Returns an array containing the unique names of the current outgoing raw +headers. Header names are returned with their exact casing being set. + +```js +request.setHeader('Foo', 'bar'); +request.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']); + +const headerNames = request.getRawHeaderNames(); +// headerNames === ['Foo', 'Set-Cookie'] +``` + ### `request.maxHeadersCount` * {number} **Default:** `2000` diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index 37ef44f2f2eee2..b7f0a9d5e67a15 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -22,6 +22,7 @@ 'use strict'; const { + Array, ArrayIsArray, ArrayPrototypeForEach, ArrayPrototypeJoin, @@ -35,6 +36,7 @@ const { ObjectCreate, ObjectDefineProperty, ObjectKeys, + ObjectValues, ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, RegExpPrototypeTest, @@ -602,6 +604,23 @@ OutgoingMessage.prototype.getHeaderNames = function getHeaderNames() { }; +// Returns an array of the names of the current outgoing raw headers. +OutgoingMessage.prototype.getRawHeaderNames = function getRawHeaderNames() { + const headersMap = this[kOutHeaders]; + if (headersMap === null) return []; + + const values = ObjectValues(headersMap); + const headers = Array(values.length); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0, l = values.length; i < l; i++) { + headers[i] = values[i][0]; + } + + return headers; +}; + + // Returns a shallow copy of the current outgoing headers. OutgoingMessage.prototype.getHeaders = function getHeaders() { const headers = this[kOutHeaders]; diff --git a/test/parallel/test-http-mutable-headers.js b/test/parallel/test-http-mutable-headers.js index c9f63acf4433f4..755a4dd66159e8 100644 --- a/test/parallel/test-http-mutable-headers.js +++ b/test/parallel/test-http-mutable-headers.js @@ -108,6 +108,10 @@ const s = http.createServer(common.mustCall((req, res) => { ['x-test-header', 'x-test-header2', 'set-cookie', 'x-test-array-header']); + assert.deepStrictEqual(res.getRawHeaderNames(), + ['x-test-header', 'X-TEST-HEADER2', + 'set-cookie', 'x-test-array-header']); + assert.strictEqual(res.hasHeader('x-test-header2'), true); assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), true); assert.strictEqual(res.hasHeader('X-Test-Header2'), true); @@ -171,7 +175,10 @@ function nextTest() { let bufferedResponse = ''; - http.get({ port: s.address().port }, common.mustCall((response) => { + const req = http.get({ + port: s.address().port, + headers: { 'X-foo': 'bar' } + }, common.mustCall((response) => { switch (test) { case 'headers': assert.strictEqual(response.statusCode, 201); @@ -214,4 +221,10 @@ function nextTest() { common.mustCall(nextTest)(); })); })); + + assert.deepStrictEqual(req.getHeaderNames(), + ['x-foo', 'host']); + + assert.deepStrictEqual(req.getRawHeaderNames(), + ['X-foo', 'Host']); } From 9f61cbd1fdc4d439071ffc60973cf07e2403d8b3 Mon Sep 17 00:00:00 2001 From: Joyee Cheung <joyeec9h3@gmail.com> Date: Tue, 16 Mar 2021 03:44:04 +0800 Subject: [PATCH 29/85] test: account for OOM risks in heapsnapshot-near-heap-limit tests PR-URL: https://github.com/nodejs/node/pull/37761 Fixes: https://github.com/nodejs/node/issues/36961 Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- ...test-heapsnapshot-near-heap-limit-worker.js | 17 ++++++++++------- .../test-heapsnapshot-near-heap-limit.js | 18 ++++++++++++++---- .../test-heapsnapshot-near-heap-limit-big.js | 11 +++++------ 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/test/parallel/test-heapsnapshot-near-heap-limit-worker.js b/test/parallel/test-heapsnapshot-near-heap-limit-worker.js index edbe2f6c617505..48845bdad00a48 100644 --- a/test/parallel/test-heapsnapshot-near-heap-limit-worker.js +++ b/test/parallel/test-heapsnapshot-near-heap-limit-worker.js @@ -26,11 +26,14 @@ const env = { console.log(child.stdout.toString()); const stderr = child.stderr.toString(); console.log(stderr); - // There should be one snapshot taken and then after the - // snapshot heap limit callback is popped, the OOM callback - // becomes effective. - assert(stderr.includes('ERR_WORKER_OUT_OF_MEMORY')); - const list = fs.readdirSync(tmpdir.path) - .filter((file) => file.endsWith('.heapsnapshot')); - assert.strictEqual(list.length, 1); + const risky = /Not generating snapshots because it's too risky/.test(stderr); + if (!risky) { + // There should be one snapshot taken and then after the + // snapshot heap limit callback is popped, the OOM callback + // becomes effective. + assert(stderr.includes('ERR_WORKER_OUT_OF_MEMORY')); + const list = fs.readdirSync(tmpdir.path) + .filter((file) => file.endsWith('.heapsnapshot')); + assert.strictEqual(list.length, 1); + } } diff --git a/test/parallel/test-heapsnapshot-near-heap-limit.js b/test/parallel/test-heapsnapshot-near-heap-limit.js index 42b66871d429c2..92e06cb25e44e4 100644 --- a/test/parallel/test-heapsnapshot-near-heap-limit.js +++ b/test/parallel/test-heapsnapshot-near-heap-limit.js @@ -58,12 +58,17 @@ const env = { env, }); console.log(child.stdout.toString()); - console.log(child.stderr.toString()); + const stderr = child.stderr.toString(); + console.log(stderr); assert(common.nodeProcessAborted(child.status, child.signal), 'process should have aborted, but did not'); const list = fs.readdirSync(tmpdir.path) .filter((file) => file.endsWith('.heapsnapshot')); - assert.strictEqual(list.length, 1); + const risky = [...stderr.matchAll( + /Not generating snapshots because it's too risky/g)].length; + assert(list.length + risky > 0 && list.length <= 3, + `Generated ${list.length} snapshots ` + + `and ${risky} was too risky`); } { @@ -79,10 +84,15 @@ const env = { env, }); console.log(child.stdout.toString()); - console.log(child.stderr.toString()); + const stderr = child.stderr.toString(); + console.log(stderr); assert(common.nodeProcessAborted(child.status, child.signal), 'process should have aborted, but did not'); const list = fs.readdirSync(tmpdir.path) .filter((file) => file.endsWith('.heapsnapshot')); - assert(list.length > 0 && list.length <= 3); + const risky = [...stderr.matchAll( + /Not generating snapshots because it's too risky/g)].length; + assert(list.length + risky > 0 && list.length <= 3, + `Generated ${list.length} snapshots ` + + `and ${risky} was too risky`); } diff --git a/test/pummel/test-heapsnapshot-near-heap-limit-big.js b/test/pummel/test-heapsnapshot-near-heap-limit-big.js index f70d562c1d1ee4..3442ae068be06f 100644 --- a/test/pummel/test-heapsnapshot-near-heap-limit-big.js +++ b/test/pummel/test-heapsnapshot-near-heap-limit-big.js @@ -34,10 +34,9 @@ if (!common.enoughTestMem) 'process should have aborted, but did not'); const list = fs.readdirSync(tmpdir.path) .filter((file) => file.endsWith('.heapsnapshot')); - if (list.length === 0) { - assert(stderr.includes( - 'Not generating snapshots because it\'s too risky')); - } else { - assert(list.length > 0 && list.length <= 3); - } + const risky = [...stderr.matchAll( + /Not generating snapshots because it's too risky/g)].length; + assert(list.length + risky > 0 && list.length <= 3, + `Generated ${list.length} snapshots ` + + `and ${risky} was too risky`); } From e9c161ce129fa75e618fb74cfa3493591728d80d Mon Sep 17 00:00:00 2001 From: Nitzan Uziely <linkgoron@gmail.com> Date: Fri, 12 Mar 2021 20:24:00 +0200 Subject: [PATCH 30/85] http: fix double AbortSignal registration Fix an issue where AbortSignals are registered twice PR-URL: https://github.com/nodejs/node/pull/37730 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- lib/_http_client.js | 17 +++-- .../test-http-agent-abort-controller.js | 73 +++++++++++++++++++ .../test-http-client-abort-destroy.js | 52 ++++++++++++- test/parallel/test-http2-client-destroy.js | 15 ++-- test/parallel/test-https-abortcontroller.js | 3 +- 5 files changed, 143 insertions(+), 17 deletions(-) create mode 100644 test/parallel/test-http-agent-abort-controller.js diff --git a/lib/_http_client.js b/lib/_http_client.js index 70c8a26f3fc17f..57a36183405170 100644 --- a/lib/_http_client.js +++ b/lib/_http_client.js @@ -304,14 +304,20 @@ function ClientRequest(input, options, cb) { options.headers); } + let optsWithoutSignal = options; + if (optsWithoutSignal.signal) { + optsWithoutSignal = ObjectAssign({}, options); + delete optsWithoutSignal.signal; + } + // initiate connection if (this.agent) { - this.agent.addRequest(this, options); + this.agent.addRequest(this, optsWithoutSignal); } else { // No agent, default to Connection:close. this._last = true; this.shouldKeepAlive = false; - if (typeof options.createConnection === 'function') { + if (typeof optsWithoutSignal.createConnection === 'function') { const oncreate = once((err, socket) => { if (err) { process.nextTick(() => this.emit('error', err)); @@ -321,7 +327,8 @@ function ClientRequest(input, options, cb) { }); try { - const newSocket = options.createConnection(options, oncreate); + const newSocket = optsWithoutSignal.createConnection(optsWithoutSignal, + oncreate); if (newSocket) { oncreate(null, newSocket); } @@ -329,8 +336,8 @@ function ClientRequest(input, options, cb) { oncreate(err); } } else { - debug('CLIENT use net.createConnection', options); - this.onSocket(net.createConnection(options)); + debug('CLIENT use net.createConnection', optsWithoutSignal); + this.onSocket(net.createConnection(optsWithoutSignal)); } } } diff --git a/test/parallel/test-http-agent-abort-controller.js b/test/parallel/test-http-agent-abort-controller.js new file mode 100644 index 00000000000000..c5ece3ab353bf0 --- /dev/null +++ b/test/parallel/test-http-agent-abort-controller.js @@ -0,0 +1,73 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const http = require('http'); +const Agent = http.Agent; +const { getEventListeners, once } = require('events'); +const agent = new Agent(); +const server = http.createServer(); + +server.listen(0, common.mustCall(async () => { + const port = server.address().port; + const host = 'localhost'; + const options = { + port: port, + host: host, + _agentKey: agent.getName({ port, host }) + }; + + async function postCreateConnection() { + const ac = new AbortController(); + const { signal } = ac; + const connection = agent.createConnection({ ...options, signal }); + assert.strictEqual(getEventListeners(signal, 'abort').length, 1); + ac.abort(); + const [err] = await once(connection, 'error'); + assert.strictEqual(err?.name, 'AbortError'); + } + + async function preCreateConnection() { + const ac = new AbortController(); + const { signal } = ac; + ac.abort(); + const connection = agent.createConnection({ ...options, signal }); + const [err] = await once(connection, 'error'); + assert.strictEqual(err?.name, 'AbortError'); + } + + async function agentAsParam() { + const ac = new AbortController(); + const { signal } = ac; + const request = http.get({ + port: server.address().port, + path: '/hello', + agent: agent, + signal, + }); + assert.strictEqual(getEventListeners(signal, 'abort').length, 1); + ac.abort(); + const [err] = await once(request, 'error'); + assert.strictEqual(err?.name, 'AbortError'); + } + + async function agentAsParamPreAbort() { + const ac = new AbortController(); + const { signal } = ac; + ac.abort(); + const request = http.get({ + port: server.address().port, + path: '/hello', + agent: agent, + signal, + }); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); + const [err] = await once(request, 'error'); + assert.strictEqual(err?.name, 'AbortError'); + } + + await postCreateConnection(); + await preCreateConnection(); + await agentAsParam(); + await agentAsParamPreAbort(); + server.close(); +})); diff --git a/test/parallel/test-http-client-abort-destroy.js b/test/parallel/test-http-client-abort-destroy.js index 7af63a1ef408c0..11b1390a839b68 100644 --- a/test/parallel/test-http-client-abort-destroy.js +++ b/test/parallel/test-http-client-abort-destroy.js @@ -2,6 +2,7 @@ const common = require('../common'); const http = require('http'); const assert = require('assert'); +const { getEventListeners } = require('events'); { // abort @@ -71,19 +72,20 @@ const assert = require('assert'); { - // Destroy with AbortSignal + // Destroy post-abort sync with AbortSignal const server = http.createServer(common.mustNotCall()); const controller = new AbortController(); - + const { signal } = controller; server.listen(0, common.mustCall(() => { - const options = { port: server.address().port, signal: controller.signal }; + const options = { port: server.address().port, signal }; const req = http.get(options, common.mustNotCall()); req.on('error', common.mustCall((err) => { assert.strictEqual(err.code, 'ABORT_ERR'); assert.strictEqual(err.name, 'AbortError'); server.close(); })); + assert.strictEqual(getEventListeners(signal, 'abort').length, 1); assert.strictEqual(req.aborted, false); assert.strictEqual(req.destroyed, false); controller.abort(); @@ -91,3 +93,47 @@ const assert = require('assert'); assert.strictEqual(req.destroyed, true); })); } + +{ + // Use post-abort async AbortSignal + const server = http.createServer(common.mustNotCall()); + const controller = new AbortController(); + const { signal } = controller; + server.listen(0, common.mustCall(() => { + const options = { port: server.address().port, signal }; + const req = http.get(options, common.mustNotCall()); + req.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + })); + + req.on('close', common.mustCall(() => { + assert.strictEqual(req.aborted, false); + assert.strictEqual(req.destroyed, true); + server.close(); + })); + + assert.strictEqual(getEventListeners(signal, 'abort').length, 1); + process.nextTick(() => controller.abort()); + })); +} + +{ + // Use pre-aborted AbortSignal + const server = http.createServer(common.mustNotCall()); + const controller = new AbortController(); + const { signal } = controller; + server.listen(0, common.mustCall(() => { + controller.abort(); + const options = { port: server.address().port, signal }; + const req = http.get(options, common.mustNotCall()); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); + req.on('error', common.mustCall((err) => { + assert.strictEqual(err.code, 'ABORT_ERR'); + assert.strictEqual(err.name, 'AbortError'); + server.close(); + })); + assert.strictEqual(req.aborted, false); + assert.strictEqual(req.destroyed, true); + })); +} diff --git a/test/parallel/test-http2-client-destroy.js b/test/parallel/test-http2-client-destroy.js index f257cf6f0006fa..52b0744cd2190d 100644 --- a/test/parallel/test-http2-client-destroy.js +++ b/test/parallel/test-http2-client-destroy.js @@ -8,9 +8,8 @@ if (!common.hasCrypto) const assert = require('assert'); const h2 = require('http2'); const { kSocket } = require('internal/http2/util'); -const { kEvents } = require('internal/event_target'); const Countdown = require('../common/countdown'); - +const { getEventListeners } = require('events'); { const server = h2.createServer(); server.listen(0, common.mustCall(() => { @@ -180,11 +179,11 @@ const Countdown = require('../common/countdown'); client.on('close', common.mustCall()); const { signal } = controller; - assert.strictEqual(signal[kEvents].get('abort'), undefined); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); client.on('error', common.mustCall(() => { // After underlying stream dies, signal listener detached - assert.strictEqual(signal[kEvents].get('abort'), undefined); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); })); const req = client.request({}, { signal }); @@ -198,7 +197,7 @@ const Countdown = require('../common/countdown'); assert.strictEqual(req.aborted, false); assert.strictEqual(req.destroyed, false); // Signal listener attached - assert.strictEqual(signal[kEvents].get('abort').size, 1); + assert.strictEqual(getEventListeners(signal, 'abort').length, 1); controller.abort(); @@ -219,16 +218,16 @@ const Countdown = require('../common/countdown'); const { signal } = controller; controller.abort(); - assert.strictEqual(signal[kEvents].get('abort'), undefined); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); client.on('error', common.mustCall(() => { // After underlying stream dies, signal listener detached - assert.strictEqual(signal[kEvents].get('abort'), undefined); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); })); const req = client.request({}, { signal }); // Signal already aborted, so no event listener attached. - assert.strictEqual(signal[kEvents].get('abort'), undefined); + assert.strictEqual(getEventListeners(signal, 'abort').length, 0); assert.strictEqual(req.aborted, false); // Destroyed on same tick as request made diff --git a/test/parallel/test-https-abortcontroller.js b/test/parallel/test-https-abortcontroller.js index 80821285acf95e..19aa9a6fca2bdf 100644 --- a/test/parallel/test-https-abortcontroller.js +++ b/test/parallel/test-https-abortcontroller.js @@ -7,7 +7,7 @@ if (!common.hasCrypto) const fixtures = require('../common/fixtures'); const https = require('https'); const assert = require('assert'); -const { once } = require('events'); +const { once, getEventListeners } = require('events'); const options = { key: fixtures.readKey('agent1-key.pem'), @@ -29,6 +29,7 @@ const options = { rejectUnauthorized: false, signal: ac.signal, }); + assert.strictEqual(getEventListeners(ac.signal, 'abort').length, 1); process.nextTick(() => ac.abort()); const [ err ] = await once(req, 'error'); assert.strictEqual(err.name, 'AbortError'); From 8f18133de078ac12ba9124e4f05f950fc3666efe Mon Sep 17 00:00:00 2001 From: marsonya <akhil.marsonya27@gmail.com> Date: Thu, 11 Mar 2021 00:01:44 +0530 Subject: [PATCH 31/85] doc: use sentence case in issues.md headers PR-URL: https://github.com/nodejs/node/pull/37537 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- doc/guides/contributing/issues.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/guides/contributing/issues.md b/doc/guides/contributing/issues.md index fdb30c6f71c43d..10aa28201d9e0b 100644 --- a/doc/guides/contributing/issues.md +++ b/doc/guides/contributing/issues.md @@ -1,11 +1,11 @@ # Issues -* [Asking for General Help](#asking-for-general-help) +* [Asking for general help](#asking-for-general-help) * [Discussing non-technical topics](#discussing-non-technical-topics) -* [Submitting a Bug Report](#submitting-a-bug-report) -* [Triaging a Bug Report](#triaging-a-bug-report) +* [Submitting a bug report](#submitting-a-bug-report) +* [Triaging a bug report](#triaging-a-bug-report) -## Asking for General Help +## Asking for general help Because the level of activity in the `nodejs/node` repository is so high, questions or requests for general help using Node.js should be directed at @@ -16,7 +16,7 @@ the [Node.js help repository][]. Discussion of non-technical topics (such as intellectual property and trademark) should be directed to the [Technical Steering Committee (TSC) repository][]. -## Submitting a Bug Report +## Submitting a bug report When opening a new issue in the `nodejs/node` issue tracker, users will be presented with a choice of issue templates. If you believe that you have @@ -37,7 +37,7 @@ Node.js changed that broke the module. See [How to create a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve). -## Triaging a Bug Report +## Triaging a bug report Once an issue has been opened, it is common for there to be discussion around it. Some contributors may have differing opinions about the issue, From 11d45855cdeb71abe625f9053dbbb86e817309b2 Mon Sep 17 00:00:00 2001 From: Jiawen Geng <technicalcute@gmail.com> Date: Thu, 18 Mar 2021 06:47:03 +0000 Subject: [PATCH 32/85] crypto: fix header name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37792 Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- node.gyp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node.gyp b/node.gyp index 6691de8422e940..2d76366e330075 100644 --- a/node.gyp +++ b/node.gyp @@ -963,7 +963,7 @@ 'src/crypto/crypto_tls.h', 'src/crypto/crypto_clienthello.h', 'src/crypto/crypto_context.h', - 'src/crypto/crypto_ecdh.h', + 'src/crypto/crypto_ec.h', 'src/crypto/crypto_hkdf.h', 'src/crypto/crypto_pbkdf2.h', 'src/crypto/crypto_sig.h', From 71fde0727465f8a7728f6377049467e1fc5029d8 Mon Sep 17 00:00:00 2001 From: Qingyu Deng <bitdqy@hotmail.com> Date: Wed, 17 Mar 2021 20:25:07 +0800 Subject: [PATCH 33/85] doc: add Ayase-252 to triagers I believe that the best way to master a tech is practicing as much as possible. I think becoming a triager can provide many different perspective to approach Node. PR-URL: https://github.com/nodejs/node/pull/37781 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index bc7383fcbf444c..ccc71e9014320d 100644 --- a/README.md +++ b/README.md @@ -593,6 +593,8 @@ maintaining the Node.js project. ### Triagers +* [Ayase-252](https://github.com/Ayase-252) - +**Qingyu Deng** <i@ayase-lab.com> * [marsonya](https://github.com/marsonya) - **Akhil Marsonya** <akhil.marsonya27@gmail.com> (he/him) * [PoojaDurgad](https://github.com/PoojaDurgad) - From f07428ae51116c4feac06a7416e424091a562158 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 18 Mar 2021 22:22:19 -0700 Subject: [PATCH 34/85] test: remove skip for fixed test-benchmark-fs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37803 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/benchmark/benchmark.status | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/benchmark/benchmark.status b/test/benchmark/benchmark.status index e65d001234038a..6a966743aab26b 100644 --- a/test/benchmark/benchmark.status +++ b/test/benchmark/benchmark.status @@ -19,5 +19,3 @@ prefix benchmark [$system==aix] [$arch==arm] -# https://github.com/nodejs/node/issues/34266 -test-benchmark-fs: SKIP From 43c3b43ea3f0e535bdca0680ae246900aa5349da Mon Sep 17 00:00:00 2001 From: wwwzbwcom <zbwhome@outlook.com> Date: Fri, 5 Mar 2021 19:55:00 +0800 Subject: [PATCH 35/85] stream: make Readable.from performance better PR-URL: https://github.com/nodejs/node/pull/37609 Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> --- benchmark/streams/readable-from.js | 26 +++++++++++++++ lib/internal/streams/from.js | 51 +++++++++++++++++++----------- 2 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 benchmark/streams/readable-from.js diff --git a/benchmark/streams/readable-from.js b/benchmark/streams/readable-from.js new file mode 100644 index 00000000000000..2dcf10ffef2732 --- /dev/null +++ b/benchmark/streams/readable-from.js @@ -0,0 +1,26 @@ +'use strict'; + +const common = require('../common'); +const Readable = require('stream').Readable; + +const bench = common.createBenchmark(main, { + n: [1e7], +}); + +async function main({ n }) { + const arr = []; + for (let i = 0; i < n; i++) { + arr.push(`${i}`); + } + + const s = new Readable.from(arr); + + bench.start(); + s.on('data', (data) => { + // eslint-disable-next-line no-unused-expressions + data; + }); + s.on('close', () => { + bench.end(n); + }); +} diff --git a/lib/internal/streams/from.js b/lib/internal/streams/from.js index 6798411fa4b038..906ddcf9af844b 100644 --- a/lib/internal/streams/from.js +++ b/lib/internal/streams/from.js @@ -3,7 +3,7 @@ const { PromisePrototypeThen, SymbolAsyncIterator, - SymbolIterator + SymbolIterator, } = primordials; const { Buffer } = require('buffer'); @@ -25,18 +25,22 @@ function from(Readable, iterable, opts) { }); } - if (iterable && iterable[SymbolAsyncIterator]) + let isAsync = false; + if (iterable && iterable[SymbolAsyncIterator]) { + isAsync = true; iterator = iterable[SymbolAsyncIterator](); - else if (iterable && iterable[SymbolIterator]) + } else if (iterable && iterable[SymbolIterator]) { + isAsync = false; iterator = iterable[SymbolIterator](); - else + } else { throw new ERR_INVALID_ARG_TYPE('iterable', ['Iterable'], iterable); + } const readable = new Readable({ objectMode: true, highWaterMark: 1, // TODO(ronag): What options should be allowed? - ...opts + ...opts, }); // Flag to protect against _read @@ -75,23 +79,32 @@ function from(Readable, iterable, opts) { } async function next() { - try { - const { value, done } = await iterator.next(); - if (done) { - readable.push(null); - } else { - const res = await value; - if (res === null) { - reading = false; - throw new ERR_STREAM_NULL_VALUES(); - } else if (readable.push(res)) { - next(); + for (;;) { + try { + const { value, done } = isAsync ? + await iterator.next() : + iterator.next(); + + if (done) { + readable.push(null); } else { - reading = false; + const res = (value && + typeof value.then === 'function') ? + await value : + value; + if (res === null) { + reading = false; + throw new ERR_STREAM_NULL_VALUES(); + } else if (readable.push(res)) { + continue; + } else { + reading = false; + } } + } catch (err) { + readable.destroy(err); } - } catch (err) { - readable.destroy(err); + break; } } return readable; From 9bc6fe7eb378fbacecb33efd2ad549f04f7b447e Mon Sep 17 00:00:00 2001 From: Richard Lau <rlau@redhat.com> Date: Fri, 19 Mar 2021 22:37:29 -0400 Subject: [PATCH 36/85] test: remove references to unsupported AIX versions The `filehandle.utimes()` and `fs.futimes()` APIs work on all versions of AIX that are supported by this version of Node.js. PR-URL: https://github.com/nodejs/node/pull/37826 Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Stewart X Addison <sxa@redhat.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Ash Cripps <acripps@redhat.com> --- doc/api/fs.md | 6 ------ test/parallel/test-trace-events-fs-sync.js | 7 ------- 2 files changed, 13 deletions(-) diff --git a/doc/api/fs.md b/doc/api/fs.md index d87f73a15f593f..e01a33940f19bc 100644 --- a/doc/api/fs.md +++ b/doc/api/fs.md @@ -420,9 +420,6 @@ added: v10.0.0 Change the file system timestamps of the object referenced by the {FileHandle} then resolves the promise with no arguments upon success. -This function does not work on AIX versions before 7.1, it will reject the -promise with an error using code `UV_ENOSYS`. - #### `filehandle.write(buffer[, offset[, length[, position]]])` <!-- YAML added: v10.0.0 @@ -2326,9 +2323,6 @@ changes: Change the file system timestamps of the object referenced by the supplied file descriptor. See [`fs.utimes()`][]. -This function does not work on AIX versions before 7.1, it will return the -error `UV_ENOSYS`. - ### `fs.lchmod(path, mode, callback)` <!-- YAML deprecated: v0.4.7 diff --git a/test/parallel/test-trace-events-fs-sync.js b/test/parallel/test-trace-events-fs-sync.js index d8e9ca30a8ebd3..69a0ae2778245a 100644 --- a/test/parallel/test-trace-events-fs-sync.js +++ b/test/parallel/test-trace-events-fs-sync.js @@ -124,13 +124,6 @@ for (const tr in tests) { '--trace-event-categories', 'node.fs.sync', '-e', tests[tr] ], { cwd: tmpdir.path, encoding: 'utf8' }); - // Some AIX versions don't support futimes or utimes, so skip. - if (common.isAIX && proc.status !== 0 && tr === 'fs.sync.futimes') { - continue; - } - if (common.isAIX && proc.status !== 0 && tr === 'fs.sync.utimes') { - continue; - } // Make sure the operation is successful. // Don't use assert with a custom message here. Otherwise the From f97a5dd22ffeed345b3684eba9395f81fd035343 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 06:03:12 -0700 Subject: [PATCH 37/85] doc: use sentence-style capitlaztion in template header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the level one header for all the HTML documents use consistent capitalization style as the documentation itself. PR-URL: https://github.com/nodejs/node/pull/37837 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- doc/template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/template.html b/doc/template.html index f57bf50bb67334..5e70b6054e080b 100644 --- a/doc/template.html +++ b/doc/template.html @@ -24,7 +24,7 @@ <div id="column1" data-id="__ID__" class="interior"> <header> <div class="header-container"> - <h1>Node.js __VERSION__ Documentation</h1> + <h1>Node.js __VERSION__ documentation</h1> <button class="theme-toggle-btn" id="theme-toggle-btn" title="Toggle dark mode/light mode" aria-label="Toggle dark mode/light mode" hidden> <svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" height="24" width="24"> <path fill="none" d="M0 0h24v24H0z" /> From 9defe103717aa447bb4c0f27c786d3fda6b872a3 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 06:27:48 -0700 Subject: [PATCH 38/85] doc: fix legacy stability indicator display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37838 Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> --- doc/api/documentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/api/documentation.md b/doc/api/documentation.md index 019040bdb61099..d0a05262cc1f27 100644 --- a/doc/api/documentation.md +++ b/doc/api/documentation.md @@ -40,8 +40,8 @@ The stability indices are as follows: <!-- separator --> > Stability: 3 - Legacy. The feature is no longer recommended for use. While it -likely will not be removed, and is still covered by semantic-versioning -guarantees, use of the feature should be avoided. +> likely will not be removed, and is still covered by semantic-versioning +> guarantees, use of the feature should be avoided. Use caution when making use of Experimental features, particularly within modules. Users may not be aware that experimental features are being used. From cdfc1c86923332a6993e0d01455f19228646f987 Mon Sep 17 00:00:00 2001 From: Nitzan Uziely <linkgoron@gmail.com> Date: Sat, 20 Mar 2021 01:02:18 +0200 Subject: [PATCH 39/85] child_process: cleanup AbortSignal duplication cleanup AbortSignal child_process code duplication PR-URL: https://github.com/nodejs/node/pull/37823 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/child_process.js | 65 +++++++++++--------------------------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/lib/child_process.js b/lib/child_process.js index 18996f8a5643ce..da2f9628ce0808 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -605,23 +605,6 @@ function spawn(file, args, options) { const killSignal = sanitizeKillSignal(options.killSignal); const child = new ChildProcess(); - if (options.signal) { - const signal = options.signal; - if (signal.aborted) { - onAbortListener(); - } else { - signal.addEventListener('abort', onAbortListener, { once: true }); - child.once('exit', - () => signal.removeEventListener('abort', onAbortListener)); - } - - function onAbortListener() { - process.nextTick(() => { - abortChildProcess(child, killSignal); - }); - } - } - debug('spawn', options); child.spawn(options); @@ -645,6 +628,21 @@ function spawn(file, args, options) { }); } + if (options.signal) { + const signal = options.signal; + if (signal.aborted) { + process.nextTick(onAbortListener); + } else { + signal.addEventListener('abort', onAbortListener, { once: true }); + child.once('exit', + () => signal.removeEventListener('abort', onAbortListener)); + } + + function onAbortListener() { + abortChildProcess(child, killSignal); + } + } + return child; } @@ -778,37 +776,6 @@ function sanitizeKillSignal(killSignal) { } } -// This level of indirection is here because the other child_process methods -// call spawn internally but should use different cancellation logic. -function spawnWithSignal(file, args, options) { - // Remove signal from options to spawn - // to avoid double emitting of AbortError - const opts = options && typeof options === 'object' && ('signal' in options) ? - { ...options, signal: undefined } : - options; - - if (options?.signal) { - // Validate signal, if present - validateAbortSignal(options.signal, 'options.signal'); - } - const child = spawn(file, args, opts); - - if (options?.signal) { - const killSignal = sanitizeKillSignal(options.killSignal); - - function kill() { - abortChildProcess(child, killSignal); - } - if (options.signal.aborted) { - process.nextTick(kill); - } else { - options.signal.addEventListener('abort', kill, { once: true }); - const remove = () => options.signal.removeEventListener('abort', kill); - child.once('exit', remove); - } - } - return child; -} module.exports = { _forkChild, ChildProcess, @@ -817,6 +784,6 @@ module.exports = { execFileSync, execSync, fork, - spawn: spawnWithSignal, + spawn, spawnSync }; From 44490af948a0b1c3e76d575bd487a16d0cc9af92 Mon Sep 17 00:00:00 2001 From: Richard Lau <rlau@redhat.com> Date: Fri, 19 Mar 2021 21:43:00 -0400 Subject: [PATCH 40/85] test: relax Y2K38 check in test-fs-utimes-y2K38 On some platforms `date` may not support the `-r` option. Optimistically allow the test to proceed in that case as the previous `touch` had succeeded -- we were just not able to easily validate the file date. PR-URL: https://github.com/nodejs/node/pull/37825 Refs: https://github.com/nodejs/node/pull/37707 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/parallel/test-fs-utimes-y2K38.js | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/test/parallel/test-fs-utimes-y2K38.js b/test/parallel/test-fs-utimes-y2K38.js index 06c5060c65d2fc..9e42e90feb1fd6 100644 --- a/test/parallel/test-fs-utimes-y2K38.js +++ b/test/parallel/test-fs-utimes-y2K38.js @@ -1,20 +1,15 @@ 'use strict'; const common = require('../common'); -if (common.isIBMi) { - common.skip('fs.utimesSync() currently fails on IBM i with Y2K38 values'); -} - const tmpdir = require('../common/tmpdir'); tmpdir.refresh(); const assert = require('assert'); const fs = require('fs'); -// Check for Y2K38 support. For Windows and AIX, assume it's there. Windows +// Check for Y2K38 support. For Windows, assume it's there. Windows // doesn't have `touch` and `date -r` which are used in the check for support. -// AIX lacks `date -r`. -if (!common.isWindows && !common.isAIX) { +if (!common.isWindows) { const testFilePath = `${tmpdir.path}/y2k38-test`; const testFileDate = '204001020304'; const { spawnSync } = require('child_process'); @@ -25,13 +20,21 @@ if (!common.isWindows && !common.isAIX) { common.skip('File system appears to lack Y2K38 support (touch failed)'); } + // On some file systems that lack Y2K38 support, `touch` will succeed but + // the time will be incorrect. const dateResult = spawnSync('date', ['-r', testFilePath, '+%Y%m%d%H%M'], { encoding: 'utf8' }); - - assert.strictEqual(dateResult.status, 0); - if (dateResult.stdout.trim() !== testFileDate) { - common.skip('File system appears to lack Y2k38 support (date failed)'); + if (dateResult.status === 0) { + if (dateResult.stdout.trim() !== testFileDate) { + common.skip('File system appears to lack Y2k38 support (date failed)'); + } + } else { + // On some platforms `date` may not support the `-r` option. Usually + // this will result in a non-zero status and usage information printed. + // In this case optimistically proceed -- the earlier `touch` succeeded + // but validation that the file has the correct time is not easily possible. + assert.match(dateResult.stderr, /[Uu]sage:/); } } From 0f2e1429464c6640e55181b36deea7a1166bed9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com> Date: Sat, 20 Mar 2021 12:43:08 +0100 Subject: [PATCH 41/85] tools: make genv8constants.py Python3-compatible PR-URL: https://github.com/nodejs/node/pull/37835 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- tools/genv8constants.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/genv8constants.py b/tools/genv8constants.py index f750ca4de7523f..01aa37cc715de0 100755 --- a/tools/genv8constants.py +++ b/tools/genv8constants.py @@ -73,6 +73,7 @@ def out_define(): out_reset() for line in pipe: + line = line.decode('utf-8') if curr_sym != None: # # This bit of code has nasty knowledge of the objdump text output From 9557dda2eb44482f693974032ba41f77fba548e9 Mon Sep 17 00:00:00 2001 From: Nitzan Uziely <linkgoron@gmail.com> Date: Sat, 13 Mar 2021 16:44:48 +0200 Subject: [PATCH 42/85] stream: pipeline accept Buffer as a valid first argument change isStream to also check existence of on, so it wont mistake buffers as Streams. fixes: https://github.com/nodejs/node/issues/37731 PR-URL: https://github.com/nodejs/node/pull/37739 Fixes: https://github.com/nodejs/node/issues/37731 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/internal/streams/utils.js | 6 ++++-- test/parallel/test-stream-pipeline.js | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/internal/streams/utils.js b/lib/internal/streams/utils.js index 08c196802780b8..b6e744250799c6 100644 --- a/lib/internal/streams/utils.js +++ b/lib/internal/streams/utils.js @@ -6,11 +6,13 @@ const { } = primordials; function isReadable(obj) { - return !!(obj && typeof obj.pipe === 'function'); + return !!(obj && typeof obj.pipe === 'function' && + typeof obj.on === 'function'); } function isWritable(obj) { - return !!(obj && typeof obj.write === 'function'); + return !!(obj && typeof obj.write === 'function' && + typeof obj.on === 'function'); } function isStream(obj) { diff --git a/test/parallel/test-stream-pipeline.js b/test/parallel/test-stream-pipeline.js index 7940d3a90d1828..ed5a3d9a0b54f4 100644 --- a/test/parallel/test-stream-pipeline.js +++ b/test/parallel/test-stream-pipeline.js @@ -1371,3 +1371,19 @@ const net = require('net'); assert.strictEqual(res, '123'); })); } + +{ + const content = 'abc'; + pipeline(Buffer.from(content), PassThrough({ objectMode: true }), + common.mustSucceed(() => {})); + + let res = ''; + pipeline(Buffer.from(content), async function*(previous) { + for await (const val of previous) { + res += String.fromCharCode(val); + yield val; + } + }, common.mustSucceed(() => { + assert.strictEqual(res, content); + })); +} From c0a424f3e971287b4484dd24e4606a7a8ad74a75 Mon Sep 17 00:00:00 2001 From: Matthew Francis Brunetti <zenflow87@gmail.com> Date: Sat, 20 Mar 2021 05:14:04 -0400 Subject: [PATCH 43/85] doc: clarify when child process 'spawn' event is *not* emitted Making this clarification in response to a comment on GitHub: https://github.com/nodejs/node/issues/35288#issuecomment-802811813 PR-URL: https://github.com/nodejs/node/pull/37833 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/child_process.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api/child_process.md b/doc/api/child_process.md index 77467cc6573064..aace7566e49764 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -1162,6 +1162,8 @@ added: v15.1.0 --> The `'spawn'` event is emitted once the child process has spawned successfully. +If the child process does not spawn successfully, the `'spawn'` event is not +emitted and the `'error'` event is emitted instead. If emitted, the `'spawn'` event comes before all other events and before any data is received via `stdout` or `stderr`. From 5d4c6107277e399abc593295a7f9315d4544ce4e Mon Sep 17 00:00:00 2001 From: himself65 <himself65@outlook.com> Date: Mon, 22 Mar 2021 01:29:43 +0800 Subject: [PATCH 44/85] test: remove duplicated test for eventtarget PR-URL: https://github.com/nodejs/node/pull/37853 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/parallel/test-eventtarget.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/parallel/test-eventtarget.js b/test/parallel/test-eventtarget.js index 63c166fc1be421..4eeea407b196af 100644 --- a/test/parallel/test-eventtarget.js +++ b/test/parallel/test-eventtarget.js @@ -254,13 +254,6 @@ let asyncTest = Promise.resolve(); {}, // No handleEvent function false ].forEach((i) => throws(() => target.addEventListener('foo', i), err(i))); - - [ - 'foo', - 1, - {}, // No handleEvent function - false - ].forEach((i) => throws(() => target.addEventListener('foo', i), err(i))); } { From 30bc2e43e47499a93447e73c6befd9d0521f3cc2 Mon Sep 17 00:00:00 2001 From: James M Snell <jasnell@gmail.com> Date: Fri, 19 Mar 2021 14:43:18 -0700 Subject: [PATCH 45/85] doc: add examples for WHATWG URL objects Signed-off-by: James M Snell <jasnell@gmail.com> PR-URL: https://github.com/nodejs/node/pull/37822 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> --- doc/api/url.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/api/url.md b/doc/api/url.md index f93cf741473794..8ad7c5eed5019a 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -67,6 +67,31 @@ const myURL = url.parse('https://user:pass@sub.example.com:8080/p/a/t/h?query=string#hash'); ``` +### Constructing a URL from component parts and getting the constructed string + +It is possible to construct a WHATWG URL from component parts using either the +property setters or a template literal string: + +```js +const myURL = new URL('https://example.org'); +myURL.pathname = '/a/b/c'; +myURL.search = '?d=e'; +myURL.hash = '#fgh'; +``` + +```js +const pathname = '/a/b/c'; +const search = '?d=e'; +const hash = '#fgh'; +const myURL = new URL(`https://example.org${pathname}${search}${hash}`); +``` + +To get the constructed URL string, use the `href` property accessor: + +```js +console.log(myURL.href); +``` + ## The WHATWG URL API ### Class: `URL` From d9ab1d56cef1438126036f55a7ff1d2e0138cce7 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 09:47:24 -0700 Subject: [PATCH 46/85] test: fix flaky test-vm-timeout-escape-promise-module-2 Test is reliable in sequential and not in parallel, so move it there. Fixes: https://github.com/nodejs/node/issues/36627 PR-URL: https://github.com/nodejs/node/pull/37842 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- .../test-vm-timeout-escape-promise-module-2.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{parallel => sequential}/test-vm-timeout-escape-promise-module-2.js (100%) diff --git a/test/parallel/test-vm-timeout-escape-promise-module-2.js b/test/sequential/test-vm-timeout-escape-promise-module-2.js similarity index 100% rename from test/parallel/test-vm-timeout-escape-promise-module-2.js rename to test/sequential/test-vm-timeout-escape-promise-module-2.js From aa529b73b78dcb001b2b19b54514c82ddea6230b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= <tniessen@users.noreply.github.com> Date: Fri, 19 Mar 2021 22:35:44 +0100 Subject: [PATCH 47/85] test: fix ibmi skip message PR-URL: https://github.com/nodejs/node/pull/37821 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/async-hooks/test-fseventwrap.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/async-hooks/test-fseventwrap.js b/test/async-hooks/test-fseventwrap.js index 60928562872549..12a439f8033cbc 100644 --- a/test/async-hooks/test-fseventwrap.js +++ b/test/async-hooks/test-fseventwrap.js @@ -11,7 +11,7 @@ if (!common.isMainThread) common.skip('Worker bootstrapping works differently -> different async IDs'); if (common.isIBMi) - common.skip('IBMi does not suppport fs.watch()'); + common.skip('IBMi does not support fs.watch()'); const hooks = initHooks(); From 3452618905eef45975ce457695c8f32be38ea4a1 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel <duhamelantoine1995@gmail.com> Date: Fri, 19 Mar 2021 10:48:02 +0100 Subject: [PATCH 48/85] tty: validate file descriptor to avoid int32 overflow Fixes: https://github.com/nodejs/node/issues/37805 PR-URL: https://github.com/nodejs/node/pull/37809 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> --- lib/tty.js | 3 ++- test/pseudo-tty/test-tty-isatty.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/tty.js b/lib/tty.js index 106f8cc423ddf6..e61a5c3ac3f905 100644 --- a/lib/tty.js +++ b/lib/tty.js @@ -40,7 +40,8 @@ const { let readline; function isatty(fd) { - return NumberIsInteger(fd) && fd >= 0 && isTTY(fd); + return NumberIsInteger(fd) && fd >= 0 && fd <= 2147483647 && + isTTY(fd); } function ReadStream(fd, options) { diff --git a/test/pseudo-tty/test-tty-isatty.js b/test/pseudo-tty/test-tty-isatty.js index 3a7b2940311221..ad81a4c6eff92b 100644 --- a/test/pseudo-tty/test-tty-isatty.js +++ b/test/pseudo-tty/test-tty-isatty.js @@ -10,6 +10,7 @@ strictEqual(isatty(2), true, 'stderr reported to not be a tty, but it is'); strictEqual(isatty(-1), false, '-1 reported to be a tty, but it is not'); strictEqual(isatty(55555), false, '55555 reported to be a tty, but it is not'); +strictEqual(isatty(2 ** 31), false, '2^31 reported to be a tty, but it is not'); strictEqual(isatty(1.1), false, '1.1 reported to be a tty, but it is not'); strictEqual(isatty('1'), false, '\'1\' reported to be a tty, but it is not'); strictEqual(isatty({}), false, '{} reported to be a tty, but it is not'); From 79259389a198185ec8ef4d74fa38eb3adc99477c Mon Sep 17 00:00:00 2001 From: Antoine du Hamel <duhamelantoine1995@gmail.com> Date: Fri, 19 Mar 2021 12:06:50 +0100 Subject: [PATCH 49/85] crypto: fix DiffieHellman argument validation Fixes: https://github.com/nodejs/node/issues/37808 PR-URL: https://github.com/nodejs/node/pull/37810 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> --- lib/internal/crypto/diffiehellman.js | 14 +++++++++++--- test/parallel/test-crypto-dh.js | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/internal/crypto/diffiehellman.js b/lib/internal/crypto/diffiehellman.js index 1132b090d36d16..e2106b211ba01b 100644 --- a/lib/internal/crypto/diffiehellman.js +++ b/lib/internal/crypto/diffiehellman.js @@ -118,12 +118,20 @@ function DiffieHellman(sizeOrKey, keyEncoding, generator, genEncoding) { if (typeof sizeOrKey !== 'number') sizeOrKey = toBuf(sizeOrKey, keyEncoding); - if (!generator) + if (!generator) { generator = DH_GENERATOR; - else if (typeof generator === 'number') + } else if (typeof generator === 'number') { validateInt32(generator, 'generator'); - else + } else if (generator !== true) { generator = toBuf(generator, genEncoding); + } else { + throw new ERR_INVALID_ARG_TYPE( + 'generator', + ['number', 'string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'], + generator + ); + } + this[kHandle] = new _DiffieHellman(sizeOrKey, generator); ObjectDefineProperty(this, 'verifyError', { diff --git a/test/parallel/test-crypto-dh.js b/test/parallel/test-crypto-dh.js index d08d07de4f8063..92687b448c4ea1 100644 --- a/test/parallel/test-crypto-dh.js +++ b/test/parallel/test-crypto-dh.js @@ -489,3 +489,9 @@ assert.throws( 'crypto.getDiffieHellman(\'modp1\').setPublicKey(\'\') ' + 'failed to throw the expected error.' ); +assert.throws( + () => crypto.createDiffieHellman('', true), + { + code: 'ERR_INVALID_ARG_TYPE' + } +); From 86bf341a3509920b2ec8433b6b8c93177da744ac Mon Sep 17 00:00:00 2001 From: Antoine du Hamel <duhamelantoine1995@gmail.com> Date: Mon, 22 Mar 2021 11:11:30 +0100 Subject: [PATCH 50/85] test: fix deprecation warning in test-doctool-html PR-URL: https://github.com/nodejs/node/pull/37858 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- test/doctool/test-doctool-html.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index 3129dcc5d75e79..e3d0d4251dcc7d 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -44,7 +44,7 @@ function toHTML({ input, filename, nodeVersion, versions }) { .use(html.preprocessText, { nodeVersion }) .use(html.preprocessElements, { filename }) .use(html.buildToc, { filename, apilinks: {} }) - .use(remark2rehype, { allowDangerousHTML: true }) + .use(remark2rehype, { allowDangerousHtml: true }) .use(raw) .use(htmlStringify) .processSync(input); From dbc48044683aefe754d9e51edd8b33b1c0b82abb Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 14:57:31 -0700 Subject: [PATCH 51/85] tools: simplify eslint comma-dangle configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove comma-dangle settings in benchmark/.eslintrc.yaml as they are the same as what is set in .eslintrc.js. PR-URL: https://github.com/nodejs/node/pull/37850 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- benchmark/.eslintrc.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/benchmark/.eslintrc.yaml b/benchmark/.eslintrc.yaml index 8ce0f9f6e148c1..6871299adece7b 100644 --- a/benchmark/.eslintrc.yaml +++ b/benchmark/.eslintrc.yaml @@ -6,10 +6,4 @@ env: rules: no-var: error - comma-dangle: - - error - - arrays: 'always-multiline' - objects: 'only-multiline' - imports: 'only-multiline' - exports: 'only-multiline' prefer-arrow-callback: error From b5879efef1028e9a04641072198b62e301636fd5 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 11:53:47 -0700 Subject: [PATCH 52/85] tools: improve macos-firewall.sh output The output of tools/macos-firewall.sh can cause people to think it didn't work. Update things slightly to make the output mildly more informative. Refs: https://github.com/nodejs/node/issues/37233#issuecomment-802201046 PR-URL: https://github.com/nodejs/node/pull/37846 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- tools/macos-firewall.sh | 45 ++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/tools/macos-firewall.sh b/tools/macos-firewall.sh index 5a5ad52c285e71..b6050aaf3450bd 100755 --- a/tools/macos-firewall.sh +++ b/tools/macos-firewall.sh @@ -21,36 +21,25 @@ CCTEST_DEBUG="$OUTDIR/Debug/cctest" OPENSSL_CLI_RELEASE="$OUTDIR/Release/openssl-cli" OPENSSL_CLI_DEBUG="$OUTDIR/Debug/openssl-cli" +add_and_unblock () { + if [ -e "$1" ] + then + echo Processing "$1" + $SFW --remove "$1" >/dev/null + $SFW --add "$1" + $SFW --unblock "$1" + fi +} + if [ -f $SFW ]; then - # Duplicating these commands on purpose as the symbolic link node might be - # linked to either out/Debug/node or out/Release/node depending on the - # BUILDTYPE. - $SFW --remove "$NODE_DEBUG" - $SFW --remove "$NODE_DEBUG" - $SFW --remove "$NODE_RELEASE" - $SFW --remove "$NODE_RELEASE" - $SFW --remove "$NODE_LINK" - $SFW --remove "$CCTEST_DEBUG" - $SFW --remove "$CCTEST_RELEASE" - $SFW --remove "$OPENSSL_CLI_DEBUG" - $SFW --remove "$OPENSSL_CLI_RELEASE" - - $SFW --add "$NODE_DEBUG" - $SFW --add "$NODE_RELEASE" - $SFW --add "$NODE_LINK" - $SFW --add "$CCTEST_DEBUG" - $SFW --add "$CCTEST_RELEASE" - $SFW --add "$OPENSSL_CLI_DEBUG" - $SFW --add "$OPENSSL_CLI_RELEASE" - - $SFW --unblock "$NODE_DEBUG" - $SFW --unblock "$NODE_RELEASE" - $SFW --unblock "$NODE_LINK" - $SFW --unblock "$CCTEST_DEBUG" - $SFW --unblock "$CCTEST_RELEASE" - $SFW --unblock "$OPENSSL_CLI_DEBUG" - $SFW --unblock "$OPENSSL_CLI_RELEASE" + add_and_unblock "$NODE_DEBUG" + add_and_unblock "$NODE_RELEASE" + add_and_unblock "$NODE_LINK" + add_and_unblock "$CCTEST_DEBUG" + add_and_unblock "$CCTEST_RELEASE" + add_and_unblock "$OPENSSL_CLI_DEBUG" + add_and_unblock "$OPENSSL_CLI_RELEASE" else echo "SocketFirewall not found in location: $SFW" fi From d5b472b70dd12ccb7804c63aa1375f2db8805605 Mon Sep 17 00:00:00 2001 From: Ruy Adorno <ruyadorno@hotmail.com> Date: Tue, 23 Mar 2021 14:58:11 -0400 Subject: [PATCH 53/85] deps: upgrade npm to 7.7.0 PR-URL: https://github.com/nodejs/node/pull/37879 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- deps/npm/AUTHORS | 6 + deps/npm/CHANGELOG.md | 76 + deps/npm/CONTRIBUTING.md | 2 +- deps/npm/README.md | 1 + deps/npm/bin/npx-cli.js | 8 +- deps/npm/docs/content/commands/npm-adduser.md | 2 + deps/npm/docs/content/commands/npm-bin.md | 2 + deps/npm/docs/content/commands/npm-cache.md | 2 + .../docs/content/commands/npm-completion.md | 2 + deps/npm/docs/content/commands/npm-config.md | 2 + deps/npm/docs/content/commands/npm-dedupe.md | 5 +- .../docs/content/commands/npm-deprecate.md | 2 + deps/npm/docs/content/commands/npm-doctor.md | 2 + deps/npm/docs/content/commands/npm-edit.md | 2 + deps/npm/docs/content/commands/npm-exec.md | 87 + deps/npm/docs/content/commands/npm-explore.md | 2 + .../docs/content/commands/npm-find-dupes.md | 23 + .../docs/content/commands/npm-help-search.md | 2 + deps/npm/docs/content/commands/npm-help.md | 2 + deps/npm/docs/content/commands/npm-hook.md | 2 + deps/npm/docs/content/commands/npm-init.md | 2 +- deps/npm/docs/content/commands/npm-logout.md | 2 + deps/npm/docs/content/commands/npm-org.md | 2 + deps/npm/docs/content/commands/npm-owner.md | 2 + deps/npm/docs/content/commands/npm-ping.md | 2 + deps/npm/docs/content/commands/npm-prefix.md | 2 + deps/npm/docs/content/commands/npm-profile.md | 2 + .../docs/content/commands/npm-run-script.md | 85 + deps/npm/docs/content/commands/npm-search.md | 2 + .../docs/content/commands/npm-shrinkwrap.md | 2 + deps/npm/docs/content/commands/npm-star.md | 2 + deps/npm/docs/content/commands/npm-stars.md | 2 + deps/npm/docs/content/commands/npm-team.md | 2 + deps/npm/docs/content/commands/npm-token.md | 4 +- deps/npm/docs/content/commands/npm-unstar.md | 2 + deps/npm/docs/content/commands/npm-whoami.md | 2 + .../content/configuring-npm/package-json.md | 5 +- deps/npm/docs/content/using-npm/config.md | 1408 ++++++----- deps/npm/docs/content/using-npm/workspaces.md | 46 + .../npm/docs/output/commands/npm-adduser.html | 1 + deps/npm/docs/output/commands/npm-bin.html | 1 + deps/npm/docs/output/commands/npm-cache.html | 1 + .../docs/output/commands/npm-completion.html | 1 + deps/npm/docs/output/commands/npm-config.html | 1 + deps/npm/docs/output/commands/npm-dedupe.html | 5 +- .../docs/output/commands/npm-deprecate.html | 1 + deps/npm/docs/output/commands/npm-doctor.html | 1 + deps/npm/docs/output/commands/npm-edit.html | 1 + deps/npm/docs/output/commands/npm-exec.html | 69 +- .../npm/docs/output/commands/npm-explore.html | 1 + .../docs/output/commands/npm-find-dupes.html | 174 ++ .../docs/output/commands/npm-help-search.html | 1 + deps/npm/docs/output/commands/npm-help.html | 1 + deps/npm/docs/output/commands/npm-hook.html | 1 + deps/npm/docs/output/commands/npm-init.html | 2 +- deps/npm/docs/output/commands/npm-logout.html | 1 + deps/npm/docs/output/commands/npm-ls.html | 2 +- deps/npm/docs/output/commands/npm-org.html | 1 + deps/npm/docs/output/commands/npm-owner.html | 1 + deps/npm/docs/output/commands/npm-ping.html | 1 + deps/npm/docs/output/commands/npm-prefix.html | 1 + .../npm/docs/output/commands/npm-profile.html | 1 + .../docs/output/commands/npm-run-script.html | 66 +- deps/npm/docs/output/commands/npm-search.html | 1 + .../docs/output/commands/npm-shrinkwrap.html | 1 + deps/npm/docs/output/commands/npm-star.html | 1 + deps/npm/docs/output/commands/npm-stars.html | 1 + deps/npm/docs/output/commands/npm-team.html | 1 + deps/npm/docs/output/commands/npm-token.html | 1 + deps/npm/docs/output/commands/npm-unstar.html | 1 + deps/npm/docs/output/commands/npm-whoami.html | 1 + deps/npm/docs/output/commands/npm.html | 2 +- .../output/configuring-npm/package-json.html | 4 +- deps/npm/docs/output/using-npm/config.html | 1321 ++++++----- .../npm/docs/output/using-npm/workspaces.html | 32 +- deps/npm/lib/access.js | 6 +- deps/npm/lib/adduser.js | 12 +- deps/npm/lib/audit.js | 32 +- deps/npm/lib/base-command.js | 19 + deps/npm/lib/bin.js | 10 +- deps/npm/lib/birthday.js | 15 +- deps/npm/lib/bugs.js | 4 + deps/npm/lib/cache.js | 9 +- deps/npm/lib/ci.js | 10 +- deps/npm/lib/cli.js | 22 +- deps/npm/lib/completion.js | 18 +- deps/npm/lib/config.js | 23 +- deps/npm/lib/dedupe.js | 12 +- deps/npm/lib/deprecate.js | 4 + deps/npm/lib/diff.js | 10 +- deps/npm/lib/dist-tag.js | 6 +- deps/npm/lib/docs.js | 18 +- deps/npm/lib/doctor.js | 7 +- deps/npm/lib/edit.js | 4 + deps/npm/lib/exec.js | 110 +- deps/npm/lib/explain.js | 4 + deps/npm/lib/explore.js | 4 + deps/npm/lib/find-dupes.js | 5 + deps/npm/lib/fund.js | 44 +- deps/npm/lib/get.js | 5 + deps/npm/lib/help-search.js | 28 +- deps/npm/lib/help.js | 177 +- deps/npm/lib/hook.js | 4 + deps/npm/lib/init.js | 7 +- deps/npm/lib/install-ci-test.js | 4 + deps/npm/lib/install-test.js | 4 + deps/npm/lib/install.js | 20 +- deps/npm/lib/link.js | 13 +- deps/npm/lib/logout.js | 17 +- deps/npm/lib/ls.js | 27 +- deps/npm/lib/npm.js | 57 +- deps/npm/lib/org.js | 4 + deps/npm/lib/outdated.js | 37 +- deps/npm/lib/owner.js | 4 + deps/npm/lib/pack.js | 16 +- deps/npm/lib/ping.js | 19 +- deps/npm/lib/prefix.js | 5 + deps/npm/lib/profile.js | 54 +- deps/npm/lib/prune.js | 12 +- deps/npm/lib/publish.js | 24 +- deps/npm/lib/rebuild.js | 7 +- deps/npm/lib/repo.js | 5 + deps/npm/lib/restart.js | 5 + deps/npm/lib/root.js | 9 +- deps/npm/lib/run-script.js | 147 +- deps/npm/lib/search.js | 19 +- deps/npm/lib/set-script.js | 5 + deps/npm/lib/set.js | 4 + deps/npm/lib/shrinkwrap.js | 7 +- deps/npm/lib/star.js | 6 +- deps/npm/lib/stars.js | 5 + deps/npm/lib/start.js | 5 + deps/npm/lib/stop.js | 5 + deps/npm/lib/team.js | 4 + deps/npm/lib/test.js | 5 + deps/npm/lib/token.js | 4 + deps/npm/lib/uninstall.js | 14 +- deps/npm/lib/unpublish.js | 10 +- deps/npm/lib/unstar.js | 5 + deps/npm/lib/update.js | 16 +- deps/npm/lib/utils/config.js | 394 ---- deps/npm/lib/utils/config/definition.js | 185 ++ deps/npm/lib/utils/config/definitions.js | 2054 +++++++++++++++++ deps/npm/lib/utils/config/describe-all.js | 16 + deps/npm/lib/utils/config/flatten.js | 32 + deps/npm/lib/utils/config/index.js | 52 + deps/npm/lib/utils/did-you-mean.js | 35 +- deps/npm/lib/utils/flat-options.js | 254 -- deps/npm/lib/utils/lifecycle-cmd.js | 4 + deps/npm/lib/utils/npm-usage.js | 25 +- deps/npm/lib/utils/ping.js | 4 +- deps/npm/lib/utils/read-local-package.js | 7 +- deps/npm/lib/version.js | 8 +- deps/npm/lib/view.js | 36 +- deps/npm/lib/whoami.js | 19 +- deps/npm/lib/workspaces/get-workspaces.js | 33 + deps/npm/man/man1/npm-adduser.1 | 2 + deps/npm/man/man1/npm-bin.1 | 2 + deps/npm/man/man1/npm-cache.1 | 2 + deps/npm/man/man1/npm-completion.1 | 2 + deps/npm/man/man1/npm-config.1 | 2 + deps/npm/man/man1/npm-dedupe.1 | 6 +- deps/npm/man/man1/npm-deprecate.1 | 2 + deps/npm/man/man1/npm-doctor.1 | 2 + deps/npm/man/man1/npm-edit.1 | 2 + deps/npm/man/man1/npm-exec.1 | 109 + deps/npm/man/man1/npm-explore.1 | 2 + deps/npm/man/man1/npm-find-dupes.1 | 26 + deps/npm/man/man1/npm-help-search.1 | 2 + deps/npm/man/man1/npm-help.1 | 2 + deps/npm/man/man1/npm-hook.1 | 2 + deps/npm/man/man1/npm-init.1 | 2 +- deps/npm/man/man1/npm-logout.1 | 2 + deps/npm/man/man1/npm-ls.1 | 2 +- deps/npm/man/man1/npm-org.1 | 2 + deps/npm/man/man1/npm-owner.1 | 2 + deps/npm/man/man1/npm-ping.1 | 2 + deps/npm/man/man1/npm-prefix.1 | 2 + deps/npm/man/man1/npm-profile.1 | 2 + deps/npm/man/man1/npm-run-script.1 | 106 + deps/npm/man/man1/npm-search.1 | 2 + deps/npm/man/man1/npm-shrinkwrap.1 | 2 + deps/npm/man/man1/npm-star.1 | 2 + deps/npm/man/man1/npm-stars.1 | 2 + deps/npm/man/man1/npm-team.1 | 2 + deps/npm/man/man1/npm-token.1 | 2 + deps/npm/man/man1/npm-unstar.1 | 2 + deps/npm/man/man1/npm-whoami.1 | 2 + deps/npm/man/man1/npm.1 | 2 +- deps/npm/man/man5/package-json.5 | 5 +- deps/npm/man/man7/config.7 | 1591 +++++++------ deps/npm/man/man7/workspaces.7 | 54 + .../@npmcli/arborist/lib/add-rm-pkg-deps.js | 2 +- .../@npmcli/arborist/lib/arborist/reify.js | 2 +- .../@npmcli/arborist/package.json | 4 +- .../@npmcli/config/lib/get-user-agent.js | 13 - .../node_modules/@npmcli/config/lib/index.js | 115 +- .../@npmcli/config/lib/set-envs.js | 6 + .../node_modules/@npmcli/config/package.json | 2 +- .../@npmcli/run-script/lib/make-spawn-args.js | 20 +- .../@npmcli/run-script/package.json | 3 +- deps/npm/node_modules/cacache/CHANGELOG.md | 2 + deps/npm/node_modules/cacache/package.json | 4 +- .../node_modules/hosted-git-info/README.md | 4 +- .../hosted-git-info/git-host-info.js | 211 +- .../node_modules/hosted-git-info/git-host.js | 222 +- .../npm/node_modules/hosted-git-info/index.js | 299 ++- .../node_modules/hosted-git-info/package.json | 26 +- .../node_modules/libnpmversion/lib/index.js | 2 +- .../libnpmversion/lib/read-json.js | 7 + .../node_modules/libnpmversion/lib/version.js | 2 +- .../node_modules/libnpmversion/package.json | 4 +- .../normalize-package-data/README.md | 4 +- .../node_modules/hosted-git-info/CHANGELOG.md | 185 ++ .../node_modules/hosted-git-info/LICENSE | 13 + .../node_modules/hosted-git-info/README.md | 133 ++ .../hosted-git-info/git-host-info.js | 154 ++ .../node_modules/hosted-git-info/git-host.js | 110 + .../node_modules/hosted-git-info/index.js | 237 ++ .../node_modules/hosted-git-info/package.json | 52 + .../normalize-package-data/package.json | 13 +- .../node_modules/hosted-git-info/CHANGELOG.md | 185 ++ .../node_modules/hosted-git-info/LICENSE | 13 + .../node_modules/hosted-git-info/README.md | 133 ++ .../hosted-git-info/git-host-info.js | 154 ++ .../node_modules/hosted-git-info/git-host.js | 110 + .../node_modules/hosted-git-info/index.js | 237 ++ .../node_modules/hosted-git-info/package.json | 52 + .../node_modules/npm-package-arg/package.json | 8 +- .../npm-pick-manifest/CHANGELOG.md | 6 +- .../node_modules/npm-pick-manifest/index.js | 4 +- .../npm-pick-manifest/package.json | 19 +- .../pacote/lib/util/tar-create-options.js | 8 +- deps/npm/node_modules/pacote/package.json | 2 +- deps/npm/node_modules/puka/CHANGELOG.md | 31 - deps/npm/node_modules/puka/LICENSE.txt | 18 - deps/npm/node_modules/puka/README.md | 411 ---- deps/npm/node_modules/puka/index.js | 804 ------- deps/npm/node_modules/puka/package.json | 38 - deps/npm/node_modules/semver/package.json | 2 +- deps/npm/node_modules/semver/ranges/subset.js | 74 +- deps/npm/package.json | 28 +- .../test-lib-dist-tag.js-TAP.test.js | 10 + .../test-lib-publish.js-TAP.test.js | 7 +- ...lib-utils-config-definition.js-TAP.test.js | 264 +++ ...ib-utils-config-definitions.js-TAP.test.js | 156 ++ ...b-utils-config-describe-all.js-TAP.test.js | 1377 +++++++++++ ...test-lib-utils-config-index.js-TAP.test.js | 133 ++ .../test-lib-utils-config.js-TAP.test.js | 1110 --------- ...test-lib-utils-flat-options.js-TAP.test.js | 129 -- .../test-lib-utils-npm-usage.js-TAP.test.js | 284 ++- .../test-lib-utils-tar.js-TAP.test.js | 6 +- deps/npm/test/fixtures/mock-npm.js | 22 + deps/npm/test/lib/audit.js | 20 +- deps/npm/test/lib/bin.js | 19 +- deps/npm/test/lib/birthday.js | 19 +- deps/npm/test/lib/cache.js | 31 +- deps/npm/test/lib/ci.js | 36 +- deps/npm/test/lib/cli.js | 127 +- deps/npm/test/lib/completion.js | 13 +- deps/npm/test/lib/config.js | 59 +- deps/npm/test/lib/dedupe.js | 31 +- deps/npm/test/lib/diff.js | 190 +- deps/npm/test/lib/dist-tag.js | 48 +- deps/npm/test/lib/exec.js | 222 +- deps/npm/test/lib/fund.js | 108 +- deps/npm/test/lib/help-search.js | 71 +- deps/npm/test/lib/help.js | 88 +- deps/npm/test/lib/init.js | 48 +- deps/npm/test/lib/install.js | 35 +- deps/npm/test/lib/link.js | 15 +- deps/npm/test/lib/load-all-commands.js | 54 +- deps/npm/test/lib/logout.js | 72 +- deps/npm/test/lib/ls.js | 522 ++--- deps/npm/test/lib/npm.js | 104 +- deps/npm/test/lib/outdated.js | 72 +- deps/npm/test/lib/pack.js | 29 +- deps/npm/test/lib/ping.js | 39 +- deps/npm/test/lib/profile.js | 105 +- deps/npm/test/lib/publish.js | 241 +- deps/npm/test/lib/rebuild.js | 20 +- deps/npm/test/lib/run-script.js | 528 ++++- deps/npm/test/lib/search.js | 20 +- deps/npm/test/lib/set-script.js | 5 + deps/npm/test/lib/shrinkwrap.js | 32 +- deps/npm/test/lib/star.js | 24 +- deps/npm/test/lib/uninstall.js | 21 +- deps/npm/test/lib/unpublish.js | 43 +- deps/npm/test/lib/update.js | 20 +- deps/npm/test/lib/utils/config.js | 143 -- deps/npm/test/lib/utils/config/definition.js | 185 ++ deps/npm/test/lib/utils/config/definitions.js | 697 ++++++ .../npm/test/lib/utils/config/describe-all.js | 6 + deps/npm/test/lib/utils/config/flatten.js | 34 + deps/npm/test/lib/utils/config/index.js | 24 + deps/npm/test/lib/utils/did-you-mean.js | 42 +- deps/npm/test/lib/utils/flat-options.js | 359 --- deps/npm/test/lib/utils/lifecycle-cmd.js | 6 +- deps/npm/test/lib/utils/npm-usage.js | 63 +- deps/npm/test/lib/utils/read-local-package.js | 21 +- deps/npm/test/lib/utils/tar.js | 4 +- deps/npm/test/lib/version.js | 20 +- deps/npm/test/lib/view.js | 90 +- deps/npm/test/lib/whoami.js | 14 +- .../npm/test/lib/workspaces/get-workspaces.js | 199 ++ 305 files changed, 14585 insertions(+), 7996 deletions(-) create mode 100644 deps/npm/docs/content/commands/npm-find-dupes.md create mode 100644 deps/npm/docs/output/commands/npm-find-dupes.html delete mode 100644 deps/npm/lib/utils/config.js create mode 100644 deps/npm/lib/utils/config/definition.js create mode 100644 deps/npm/lib/utils/config/definitions.js create mode 100644 deps/npm/lib/utils/config/describe-all.js create mode 100644 deps/npm/lib/utils/config/flatten.js create mode 100644 deps/npm/lib/utils/config/index.js delete mode 100644 deps/npm/lib/utils/flat-options.js create mode 100644 deps/npm/lib/workspaces/get-workspaces.js create mode 100644 deps/npm/man/man1/npm-find-dupes.1 delete mode 100644 deps/npm/node_modules/@npmcli/config/lib/get-user-agent.js create mode 100644 deps/npm/node_modules/libnpmversion/lib/read-json.js create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js create mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js create mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json delete mode 100644 deps/npm/node_modules/puka/CHANGELOG.md delete mode 100644 deps/npm/node_modules/puka/LICENSE.txt delete mode 100644 deps/npm/node_modules/puka/README.md delete mode 100644 deps/npm/node_modules/puka/index.js delete mode 100644 deps/npm/node_modules/puka/package.json create mode 100644 deps/npm/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js create mode 100644 deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js create mode 100644 deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js create mode 100644 deps/npm/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js delete mode 100644 deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js delete mode 100644 deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js create mode 100644 deps/npm/test/fixtures/mock-npm.js delete mode 100644 deps/npm/test/lib/utils/config.js create mode 100644 deps/npm/test/lib/utils/config/definition.js create mode 100644 deps/npm/test/lib/utils/config/definitions.js create mode 100644 deps/npm/test/lib/utils/config/describe-all.js create mode 100644 deps/npm/test/lib/utils/config/flatten.js create mode 100644 deps/npm/test/lib/utils/config/index.js delete mode 100644 deps/npm/test/lib/utils/flat-options.js create mode 100644 deps/npm/test/lib/workspaces/get-workspaces.js diff --git a/deps/npm/AUTHORS b/deps/npm/AUTHORS index 9e5ac7fcaff39c..9c67cf88ef2505 100644 --- a/deps/npm/AUTHORS +++ b/deps/npm/AUTHORS @@ -759,3 +759,9 @@ Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Bjørn Johansen <bjjohans@microsoft.com> Fraqe <f@fraqe.ca> Edward Grech <dwardu@gmail.com> +Kenrick <kenrick95@gmail.com> +Karthik Sundari <karthik_sundari@comcast.com> +Jan Sepke <625043+jansepke@users.noreply.github.com> +Augusto Moura <augusto.borgesm@gmail.com> +Eric Chow <eric.zjp.chow@gmail.com> +kbayrhammer <klaus.bayrhammer@redbull.com> diff --git a/deps/npm/CHANGELOG.md b/deps/npm/CHANGELOG.md index 0b20696a89b952..c11a44734ae8eb 100644 --- a/deps/npm/CHANGELOG.md +++ b/deps/npm/CHANGELOG.md @@ -1,3 +1,79 @@ +## v7.7.0 (2021-03-23) + +### FEATURES + +* [`33c4189f9`](https://github.com/npm/cli/commit/33c4189f939aebdfaf85ea419e6ea01d0977b79d) + [#2864](https://github.com/npm/cli/issues/2864) + add `npm run-script` workspaces support + ([@ruyadorno](https://github.com/ruyadorno)) +* [`e1b3b318f`](https://github.com/npm/cli/commit/e1b3b318f095a7e1a7cc4b131907de4955275d9d) + [#2886](https://github.com/npm/cli/issues/2886) + add `npm exec` workspaces support + ([@ruyadorno](https://github.com/ruyadorno)) +* [`41facf643`](https://github.com/npm/cli/commit/41facf6435ced4e416d74111d9c3ff00ee19ab7d) + [#2859](https://github.com/npm/cli/issues/2859) + expanded "Did you mean?" suggestions for missing cmds and scripts + ([@wraithgar](https://github.com/wraithgar)) + +### BUG FIXES + +* [`8cce4282f`](https://github.com/npm/cli/commit/8cce4282f7bef11aeeb73cffd532b477b241985e) + [#2865](https://github.com/npm/cli/issues/2865) + `npm publish`: handle case where multiple config list is present + ([@kenrick95](https://github.com/kenrick95)) +* [`6598bfe86`](https://github.com/npm/cli/commit/6598bfe8697439e827d84981f8504febca64a55a) + mark deprecated configs + ([@isaacs](https://github.com/isaacs)) +* [`8a38afe77`](https://github.com/npm/cli/commit/8a38afe779ce71a10178ed62b13709d06adf7a66) + [#2881](https://github.com/npm/cli/issues/2881) + docs(package-json): document default main behavior + ([@klausbayrhammer](https://github.com/klausbayrhammer)) +* [`93a061d73`](https://github.com/npm/cli/commit/93a061d737dc769663652368e8586e4202267b9e) + [#2917](https://github.com/npm/cli/issues/2917) + add action items to `npm run` error output + ([@wraithgar](https://github.com/wraithgar)) + +### DOCUMENTATION + +* [`ad65bd910`](https://github.com/npm/cli/commit/ad65bd9101aa8e8b94bc1e48df3ef93deca6d30c) + [#2860](https://github.com/npm/cli/issues/2860) + fix link in configuring-npm + ([@varmakarthik12](https://github.com/varmakarthik12)) +* [`b419bfb02`](https://github.com/npm/cli/commit/b419bfb0259596fb338d45b2eaeab25a7a0d1f1e) + [#2876](https://github.com/npm/cli/issues/2876) + fix test-coverage command in contributing guide + ([@chowkapow](https://github.com/chowkapow)) + +### DEPENDENCIES + +* [`7b5606b93`](https://github.com/npm/cli/commit/7b5606b931083e8a70f5ea094c2b46f0b7a38a18) + `@npmcli/arborist@2.2.9` + * [#254](https://github.com/npm/arborist/pull/254) Honor explicit + prefix when saving dependencies + ([@jameschensmith](https://github.com/jameschensmith)) + * [#255](https://github.com/npm/arborist/pull/255) Never save to + `bundleDependencies` when saving a `peer` or `peerOptional` + dependency. ([@isaacs](https://github.com/isaacs)) +* [`f76e7c21f`](https://github.com/npm/cli/commit/f76e7c21ffd87b08593d8c396a78ab9c5fa790bd) + `pacote@11.3.1` + * increases tarball compression level +* [`4928512bc`](https://github.com/npm/cli/commit/4928512bcefd8448ff5852978cfc7f903e3ae996) + `semver@7.3.5` + * fix handling prereleases/ANY ranges in subset +* [`1924eb457`](https://github.com/npm/cli/commit/1924eb457aea7c93dfaf4a911355a63d84d66eee) + `libnpmversion@1.0.12` + * fix removing undescored-prefixed package.json properties in `npm version` +* [`916623056`](https://github.com/npm/cli/commit/91662305643509eebd2f79ed7e3ff01562aa4968) + `@npmcli/run-script@1.8.4` + * fix expanding windows-style environment variables +* [`a8d0751e4`](https://github.com/npm/cli/commit/a8d0751e4b7c7d8b808c8a49f288fc7272f729b0) + `npm-pick-manifest@6.1.1` + * fix running packages with a single executable binary with `npm exec` +* [`af7eaac50`](https://github.com/npm/cli/commit/af7eaac5018ed821d72d43d08f1d7e49e7491453) + `hosted-git-info@4.0.1` +* [`f52c51db1`](https://github.com/npm/cli/commit/f52c51db13c39cfbaed18dbd13ba7302a4b6a0d9) + `@npmcli/config@2.0.0` + ## v7.6.3 (2021-03-11) ### DOCUMENTATION diff --git a/deps/npm/CONTRIBUTING.md b/deps/npm/CONTRIBUTING.md index 5198918f010dfa..4116f4e71d057c 100644 --- a/deps/npm/CONTRIBUTING.md +++ b/deps/npm/CONTRIBUTING.md @@ -38,7 +38,7 @@ We expect that every new feature or bug fix comes with corresponding tests that **You can find out what the current test coverage percentage is by running...** ```bash -$ npm run test-coverage +$ npm run check-coverage ``` ## Performance & Benchmarks diff --git a/deps/npm/README.md b/deps/npm/README.md index 8a649847ecaab6..01de9e8f694456 100644 --- a/deps/npm/README.md +++ b/deps/npm/README.md @@ -28,6 +28,7 @@ If you're looking to manage multiple versions of **`node`** &/or **`npm`**, cons * [**`n`**](https://github.com/tj/n) * [**`volta`**](https://github.com/volta-cli/volta) * [**`nodenv`**](https://github.com/nodenv/nodenv) +* [**`asdf-nodejs`**](https://github.com/asdf-vm/asdf-nodejs) ### Usage diff --git a/deps/npm/bin/npx-cli.js b/deps/npm/bin/npx-cli.js index f4a419972f7cf4..a495090c64c784 100755 --- a/deps/npm/bin/npx-cli.js +++ b/deps/npm/bin/npx-cli.js @@ -24,11 +24,11 @@ const removed = new Set([ ...removedOpts ]) -const { types, shorthands } = require('../lib/utils/config.js') -const npmSwitches = Object.entries(types) - .filter(([key, type]) => type === Boolean || +const { definitions, shorthands } = require('../lib/utils/config/index.js') +const npmSwitches = Object.entries(definitions) + .filter(([key, {type}]) => type === Boolean || (Array.isArray(type) && type.includes(Boolean))) - .map(([key, type]) => key) + .map(([key]) => key) // things that don't take a value const switches = new Set([ diff --git a/deps/npm/docs/content/commands/npm-adduser.md b/deps/npm/docs/content/commands/npm-adduser.md index 7960869ad33cce..d0ddd68c2529a4 100644 --- a/deps/npm/docs/content/commands/npm-adduser.md +++ b/deps/npm/docs/content/commands/npm-adduser.md @@ -12,6 +12,8 @@ npm adduser [--registry=url] [--scope=@orgname] [--always-auth] [--auth-type=leg aliases: login, add-user ``` +Note: This command is unaware of workspaces. + ### Description Create or verify a user named `<username>` in the specified registry, and diff --git a/deps/npm/docs/content/commands/npm-bin.md b/deps/npm/docs/content/commands/npm-bin.md index 4303040e78dacc..c835784f675a05 100644 --- a/deps/npm/docs/content/commands/npm-bin.md +++ b/deps/npm/docs/content/commands/npm-bin.md @@ -10,6 +10,8 @@ description: Display npm bin folder npm bin [-g|--global] ``` +Note: This command is unaware of workspaces. + ### Description Print the folder where npm will install executables. diff --git a/deps/npm/docs/content/commands/npm-cache.md b/deps/npm/docs/content/commands/npm-cache.md index 13386f2c4a439c..bcc2989b7d3c3b 100644 --- a/deps/npm/docs/content/commands/npm-cache.md +++ b/deps/npm/docs/content/commands/npm-cache.md @@ -18,6 +18,8 @@ aliases: npm cache clear, npm cache rm npm cache verify ``` +Note: This command is unaware of workspaces. + ### Description Used to add, list, or clean the npm cache folder. diff --git a/deps/npm/docs/content/commands/npm-completion.md b/deps/npm/docs/content/commands/npm-completion.md index 53737c8033194b..9dbd960913f270 100644 --- a/deps/npm/docs/content/commands/npm-completion.md +++ b/deps/npm/docs/content/commands/npm-completion.md @@ -10,6 +10,8 @@ description: Tab Completion for npm source <(npm completion) ``` +Note: This command is unaware of workspaces. + ### Description Enables tab-completion in all npm commands. diff --git a/deps/npm/docs/content/commands/npm-config.md b/deps/npm/docs/content/commands/npm-config.md index 51caa5a61b6075..31629a6b7d7a21 100644 --- a/deps/npm/docs/content/commands/npm-config.md +++ b/deps/npm/docs/content/commands/npm-config.md @@ -18,6 +18,8 @@ npm get [<key> [<key> ...]] alias: c ``` +Note: This command is unaware of workspaces. + ### Description npm gets its config settings from the command line, environment diff --git a/deps/npm/docs/content/commands/npm-dedupe.md b/deps/npm/docs/content/commands/npm-dedupe.md index 9b14e99dd14f02..c6d26126d30775 100644 --- a/deps/npm/docs/content/commands/npm-dedupe.md +++ b/deps/npm/docs/content/commands/npm-dedupe.md @@ -1,7 +1,7 @@ --- title: npm-dedupe section: 1 -description: Reduce duplication +description: Reduce duplication in the package tree --- ### Synopsis @@ -10,7 +10,7 @@ description: Reduce duplication npm dedupe npm ddp -aliases: find-dupes, ddp +aliases: ddp ``` ### Description @@ -74,6 +74,7 @@ Using `npm find-dupes` will run the command in `--dry-run` mode. ### See Also +* [npm find-dupes](/cli-commands/find-dupes) * [npm ls](/cli-commands/ls) * [npm update](/cli-commands/update) * [npm install](/cli-commands/install) diff --git a/deps/npm/docs/content/commands/npm-deprecate.md b/deps/npm/docs/content/commands/npm-deprecate.md index 139441856bb068..06037976610558 100644 --- a/deps/npm/docs/content/commands/npm-deprecate.md +++ b/deps/npm/docs/content/commands/npm-deprecate.md @@ -10,6 +10,8 @@ description: Deprecate a version of a package npm deprecate <pkg>[@<version range>] <message> ``` +Note: This command is unaware of workspaces. + ### Description This command will update the npm registry entry for a package, providing a diff --git a/deps/npm/docs/content/commands/npm-doctor.md b/deps/npm/docs/content/commands/npm-doctor.md index 2aceee23903316..9416818a40aafb 100644 --- a/deps/npm/docs/content/commands/npm-doctor.md +++ b/deps/npm/docs/content/commands/npm-doctor.md @@ -10,6 +10,8 @@ description: Check your npm environment npm doctor ``` +Note: This command is unaware of workspaces. + ### Description `npm doctor` runs a set of checks to ensure that your npm installation has diff --git a/deps/npm/docs/content/commands/npm-edit.md b/deps/npm/docs/content/commands/npm-edit.md index 40fac0408529a2..20788aafb6d6a8 100644 --- a/deps/npm/docs/content/commands/npm-edit.md +++ b/deps/npm/docs/content/commands/npm-edit.md @@ -10,6 +10,8 @@ description: Edit an installed package npm edit <pkg> ``` +Note: This command is unaware of workspaces. + ### Description Selects a dependency in the current project and opens the package folder in diff --git a/deps/npm/docs/content/commands/npm-exec.md b/deps/npm/docs/content/commands/npm-exec.md index cb3e51c8255d4b..88b98e3bce466a 100644 --- a/deps/npm/docs/content/commands/npm-exec.md +++ b/deps/npm/docs/content/commands/npm-exec.md @@ -11,6 +11,7 @@ npm exec -- <pkg>[@<version>] [args...] npm exec --package=<pkg>[@<version>] -- <cmd> [args...] npm exec -c '<cmd> [args...]' npm exec --package=foo -c '<cmd> [args...]' +npm exec [-ws] [-w <workspace-name] [args...] npx <pkg>[@<specifier>] [args...] npx -p <pkg>[@<specifier>] <cmd> [args...] @@ -145,6 +146,68 @@ $ npm x -c 'eslint && say "hooray, lint passed"' $ npx -c 'eslint && say "hooray, lint passed"' ``` +### Workspaces support + +You may use the `workspace` or `workspaces` configs in order to run an +arbitrary command from an npm package (either one installed locally, or fetched +remotely) in the context of the specified workspaces. +If no positional argument or `--call` option is provided, it will open an +interactive subshell in the context of each of these configured workspaces one +at a time. + +Given a project with configured workspaces, e.g: + +``` +. ++-- package.json +`-- packages + +-- a + | `-- package.json + +-- b + | `-- package.json + `-- c + `-- package.json +``` + +Assuming the workspace configuration is properly set up at the root level +`package.json` file. e.g: + +``` +{ + "workspaces": [ "./packages/*" ] +} +``` + +You can execute an arbitrary command from a package in the context of each of +the configured workspaces when using the `workspaces` configuration options, +in this example we're using **eslint** to lint any js file found within each +workspace folder: + +``` +npm exec -ws -- eslint ./*.js +``` + +#### Filtering workspaces + +It's also possible to execute a command in a single workspace using the +`workspace` config along with a name or directory path: + +``` +npm exec --workspace=a -- eslint ./*.js +``` + +The `workspace` config can also be specified multiple times in order to run a +specific script in the context of multiple workspaces. When defining values for +the `workspace` config in the command line, it also possible to use `-w` as a +shorthand, e.g: + +``` +npm exec -w a -w b -- eslint ./*.js +``` + +This last command will run the `eslint` command in both `./packages/a` and +`./packages/b` folders. + ### Compatibility with Older npx Versions The `npx` binary was rewritten in npm v7.0.0, and the standalone `npx` @@ -195,6 +258,30 @@ requested from the server. To force full offline mode, use `offline`. Forces full offline mode. Any packages not locally cached will result in an error. +#### workspace + +* Alias: `-w` +* Type: Array +* Default: `[]` + +Enable running scripts in the context of workspaces while also filtering by +the provided names or paths provided. + +Valid values for the `workspace` config are either: +- Workspace names +- Path to a workspace directory +- Path to a parent workspace directory (will result to selecting all of the +children workspaces) + +#### workspaces + +* Alias: `-ws` +* Type: Boolean +* Default: `false` + +Run scripts in the context of all configured workspaces for the current +project. + ### See Also * [npm run-script](/commands/npm-run-script) diff --git a/deps/npm/docs/content/commands/npm-explore.md b/deps/npm/docs/content/commands/npm-explore.md index e467a755753b4f..7e2004b84c0414 100644 --- a/deps/npm/docs/content/commands/npm-explore.md +++ b/deps/npm/docs/content/commands/npm-explore.md @@ -10,6 +10,8 @@ description: Browse an installed package npm explore <pkg> [ -- <command>] ``` +Note: This command is unaware of workspaces. + ### Description Spawn a subshell in the directory of the installed package specified. diff --git a/deps/npm/docs/content/commands/npm-find-dupes.md b/deps/npm/docs/content/commands/npm-find-dupes.md new file mode 100644 index 00000000000000..e098cf47f49915 --- /dev/null +++ b/deps/npm/docs/content/commands/npm-find-dupes.md @@ -0,0 +1,23 @@ +--- +title: npm-find-dupes +section: 1 +description: Find duplication in the package tree +--- + +### Synopsis + +```bash +npm find-dupes +``` + +### Description + +Runs `npm dedupe` in `--dry-run` mode, making npm only output the +duplications, without actually changing the package tree. + +### See Also + +* [npm dedupe](/cli-commands/dedupe) +* [npm ls](/cli-commands/ls) +* [npm update](/cli-commands/update) +* [npm install](/cli-commands/install) diff --git a/deps/npm/docs/content/commands/npm-help-search.md b/deps/npm/docs/content/commands/npm-help-search.md index e10638efa07d95..51c7b43fb54f11 100644 --- a/deps/npm/docs/content/commands/npm-help-search.md +++ b/deps/npm/docs/content/commands/npm-help-search.md @@ -10,6 +10,8 @@ description: Search npm help documentation npm help-search <text> ``` +Note: This command is unaware of workspaces. + ### Description This command will search the npm markdown documentation files for the terms diff --git a/deps/npm/docs/content/commands/npm-help.md b/deps/npm/docs/content/commands/npm-help.md index 56e46645522baf..57c5efc8ed5eb9 100644 --- a/deps/npm/docs/content/commands/npm-help.md +++ b/deps/npm/docs/content/commands/npm-help.md @@ -10,6 +10,8 @@ description: Get help on npm npm help <term> [<terms..>] ``` +Note: This command is unaware of workspaces. + ### Description If supplied a topic, then show the appropriate documentation page. diff --git a/deps/npm/docs/content/commands/npm-hook.md b/deps/npm/docs/content/commands/npm-hook.md index 2ac548ada0c21f..6effc9b7d223ba 100644 --- a/deps/npm/docs/content/commands/npm-hook.md +++ b/deps/npm/docs/content/commands/npm-hook.md @@ -13,6 +13,8 @@ npm hook update <id> <url> [secret] npm hook rm <id> ``` +Note: This command is unaware of workspaces. + ### Description Allows you to manage [npm diff --git a/deps/npm/docs/content/commands/npm-init.md b/deps/npm/docs/content/commands/npm-init.md index 8a40d90e8354d5..4b0b8c4c43e73e 100644 --- a/deps/npm/docs/content/commands/npm-init.md +++ b/deps/npm/docs/content/commands/npm-init.md @@ -1,7 +1,7 @@ --- title: npm-init section: 1 -description: create a package.json file +description: Create a package.json file --- ### Synopsis diff --git a/deps/npm/docs/content/commands/npm-logout.md b/deps/npm/docs/content/commands/npm-logout.md index 7fa858a99993d2..1172a3f0f560a7 100644 --- a/deps/npm/docs/content/commands/npm-logout.md +++ b/deps/npm/docs/content/commands/npm-logout.md @@ -10,6 +10,8 @@ description: Log out of the registry npm logout [--registry=<url>] [--scope=<@scope>] ``` +Note: This command is unaware of workspaces. + ### Description When logged into a registry that supports token-based authentication, tell diff --git a/deps/npm/docs/content/commands/npm-org.md b/deps/npm/docs/content/commands/npm-org.md index 18047d109cc0b0..384f5b99fd42e3 100644 --- a/deps/npm/docs/content/commands/npm-org.md +++ b/deps/npm/docs/content/commands/npm-org.md @@ -12,6 +12,8 @@ npm org rm <orgname> <username> npm org ls <orgname> [<username>] ``` +Note: This command is unaware of workspaces. + ### Example Add a new developer to an org: diff --git a/deps/npm/docs/content/commands/npm-owner.md b/deps/npm/docs/content/commands/npm-owner.md index 69eba56afd97d5..b30bbc8dc68ef2 100644 --- a/deps/npm/docs/content/commands/npm-owner.md +++ b/deps/npm/docs/content/commands/npm-owner.md @@ -14,6 +14,8 @@ npm owner ls [<@scope>/]<pkg> aliases: author ``` +Note: This command is unaware of workspaces. + ### Description Manage ownership of published packages. diff --git a/deps/npm/docs/content/commands/npm-ping.md b/deps/npm/docs/content/commands/npm-ping.md index 8de06aa1848361..f640bf060c7501 100644 --- a/deps/npm/docs/content/commands/npm-ping.md +++ b/deps/npm/docs/content/commands/npm-ping.md @@ -10,6 +10,8 @@ description: Ping npm registry npm ping [--registry <registry>] ``` +Note: This command is unaware of workspaces. + ### Description Ping the configured or given npm registry and verify authentication. diff --git a/deps/npm/docs/content/commands/npm-prefix.md b/deps/npm/docs/content/commands/npm-prefix.md index 9c33bb18901ef4..4e3edf19023013 100644 --- a/deps/npm/docs/content/commands/npm-prefix.md +++ b/deps/npm/docs/content/commands/npm-prefix.md @@ -10,6 +10,8 @@ description: Display prefix npm prefix [-g] ``` +Note: This command is unaware of workspaces. + ### Description Print the local prefix to standard output. This is the closest parent directory diff --git a/deps/npm/docs/content/commands/npm-profile.md b/deps/npm/docs/content/commands/npm-profile.md index 88edf26d87c410..b4e2fdaee6cb11 100644 --- a/deps/npm/docs/content/commands/npm-profile.md +++ b/deps/npm/docs/content/commands/npm-profile.md @@ -14,6 +14,8 @@ npm profile enable-2fa [auth-and-writes|auth-only] npm profile disable-2fa ``` +Note: This command is unaware of workspaces. + ### Description Change your profile information on the registry. Note that this command diff --git a/deps/npm/docs/content/commands/npm-run-script.md b/deps/npm/docs/content/commands/npm-run-script.md index 8b89435e1a97be..076dfd7addcc35 100644 --- a/deps/npm/docs/content/commands/npm-run-script.md +++ b/deps/npm/docs/content/commands/npm-run-script.md @@ -8,6 +8,8 @@ description: Run arbitrary package scripts ```bash npm run-script <command> [--if-present] [--silent] [-- <args>] +npm run-script <command> [--workspace=<workspace-name>] +npm run-script <command> [--workspaces] aliases: run, rum, urn ``` @@ -78,6 +80,65 @@ If you try to run a script without having a `node_modules` directory and it fails, you will be given a warning to run `npm install`, just in case you've forgotten. +### Workspaces support + +You may use the `workspace` or `workspaces` configs in order to run an +arbitrary command from a package's `"scripts"` object in the context of the +specified workspaces. If no `"command"` is provided, it will list the available +scripts for each of these configured workspaces. + +Given a project with configured workspaces, e.g: + +``` +. ++-- package.json +`-- packages + +-- a + | `-- package.json + +-- b + | `-- package.json + `-- c + `-- package.json +``` + +Assuming the workspace configuration is properly set up at the root level +`package.json` file. e.g: + +``` +{ + "workspaces": [ "./packages/*" ] +} +``` + +And that each of the configured workspaces has a configured `test` script, +we can run tests in all of them using the `workspaces` config: + +``` +npm test --workspaces +``` + +#### Filtering workspaces + +It's also possible to run a script in a single workspace using the `workspace` +config along with a name or directory path: + +``` +npm test --workspace=a +``` + +The `workspace` config can also be specified multiple times in order to run a +specific script in the context of multiple workspaces. When defining values for +the `workspace` config in the command line, it also possible to use `-w` as a +shorthand, e.g: + +``` +npm test -w a -w b +``` + +This last command will run `test` in both `./packages/a` and `./packages/b` +packages. + + ### Configuration #### if-present @@ -111,6 +172,30 @@ to `/bin/sh` on Unix, defaults to `env.comspec` or `cmd.exe` on Windows. You can use the `--silent` flag to prevent showing `npm ERR!` output on error. +#### workspace + +* Alias: `-w` +* Type: Array +* Default: `[]` + +Enable running scripts in the context of workspaces while also filtering by +the provided names or paths provided. + +Valid values for the `workspace` config are either: +- Workspace names +- Path to a workspace directory +- Path to a parent workspace directory (will result to selecting all of the +children workspaces) + +#### workspaces + +* Alias: `-ws` +* Type: Boolean +* Default: `false` + +Run scripts in the context of all configured workspaces for the current +project. + ### See Also * [npm scripts](/using-npm/scripts) diff --git a/deps/npm/docs/content/commands/npm-search.md b/deps/npm/docs/content/commands/npm-search.md index 35178bcb0a580a..046c9334ff062d 100644 --- a/deps/npm/docs/content/commands/npm-search.md +++ b/deps/npm/docs/content/commands/npm-search.md @@ -12,6 +12,8 @@ npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms . aliases: s, se, find ``` +Note: This command is unaware of workspaces. + ### Description Search the registry for packages matching the search terms. `npm search` diff --git a/deps/npm/docs/content/commands/npm-shrinkwrap.md b/deps/npm/docs/content/commands/npm-shrinkwrap.md index dce50b7843bc3e..6786229469d2ce 100644 --- a/deps/npm/docs/content/commands/npm-shrinkwrap.md +++ b/deps/npm/docs/content/commands/npm-shrinkwrap.md @@ -10,6 +10,8 @@ description: Lock down dependency versions for publication npm shrinkwrap ``` +Note: This command is unaware of workspaces. + ### Description This command repurposes `package-lock.json` into a publishable diff --git a/deps/npm/docs/content/commands/npm-star.md b/deps/npm/docs/content/commands/npm-star.md index aab6e107747fd9..e624b92480f918 100644 --- a/deps/npm/docs/content/commands/npm-star.md +++ b/deps/npm/docs/content/commands/npm-star.md @@ -10,6 +10,8 @@ description: Mark your favorite packages npm star [<pkg>...] ``` +Note: This command is unaware of workspaces. + ### Description "Starring" a package means that you have some interest in it. It's diff --git a/deps/npm/docs/content/commands/npm-stars.md b/deps/npm/docs/content/commands/npm-stars.md index dab11bc669d1a7..80217ee044aa8b 100644 --- a/deps/npm/docs/content/commands/npm-stars.md +++ b/deps/npm/docs/content/commands/npm-stars.md @@ -9,6 +9,8 @@ description: View packages marked as favorites npm stars [<user>] ``` +Note: This command is unaware of workspaces. + ### Description If you have starred a lot of neat things and want to find them again diff --git a/deps/npm/docs/content/commands/npm-team.md b/deps/npm/docs/content/commands/npm-team.md index 96aacd8ae95f22..04e1d7f9eb1a51 100644 --- a/deps/npm/docs/content/commands/npm-team.md +++ b/deps/npm/docs/content/commands/npm-team.md @@ -16,6 +16,8 @@ npm team rm <scope:team> <user> npm team ls <scope>|<scope:team> ``` +Note: This command is unaware of workspaces. + ### Description Used to manage teams in organizations, and change team memberships. Does not diff --git a/deps/npm/docs/content/commands/npm-token.md b/deps/npm/docs/content/commands/npm-token.md index 652079453702e7..bafc7fc45c677a 100644 --- a/deps/npm/docs/content/commands/npm-token.md +++ b/deps/npm/docs/content/commands/npm-token.md @@ -9,7 +9,9 @@ description: Manage your authentication tokens npm token list [--json|--parseable] npm token create [--read-only] [--cidr=1.1.1.1/24,2.2.2.2/16] npm token revoke <id|token> - ``` +``` + +Note: This command is unaware of workspaces. ### Description diff --git a/deps/npm/docs/content/commands/npm-unstar.md b/deps/npm/docs/content/commands/npm-unstar.md index 5471d908004e16..bad1917593841f 100644 --- a/deps/npm/docs/content/commands/npm-unstar.md +++ b/deps/npm/docs/content/commands/npm-unstar.md @@ -10,6 +10,8 @@ description: Remove an item from your favorite packages npm unstar [<pkg>...] ``` +Note: This command is unaware of workspaces. + ### Description "Unstarring" a package is the opposite of [`npm star`](/commands/npm-star), diff --git a/deps/npm/docs/content/commands/npm-whoami.md b/deps/npm/docs/content/commands/npm-whoami.md index 43b301c51707a4..892adeea3db7c8 100644 --- a/deps/npm/docs/content/commands/npm-whoami.md +++ b/deps/npm/docs/content/commands/npm-whoami.md @@ -10,6 +10,8 @@ description: Display npm username npm whoami [--registry <registry>] ``` +Note: This command is unaware of workspaces. + ### Description Print the `username` config to standard output. diff --git a/deps/npm/docs/content/configuring-npm/package-json.md b/deps/npm/docs/content/configuring-npm/package-json.md index 4b3fd2ba934591..f3a186f436e029 100644 --- a/deps/npm/docs/content/configuring-npm/package-json.md +++ b/deps/npm/docs/content/configuring-npm/package-json.md @@ -325,6 +325,8 @@ This should be a module relative to the root of your package folder. For most modules, it makes the most sense to have a main script and often not much else. +If `main` is not set it defaults to `index.js` in the packages root folder. + ### browser If your module is meant to be used client-side the browser field should be @@ -562,8 +564,7 @@ tarball or git URL. **Please do not put test harnesses or transpilers or other "development" time tools in your `dependencies` object.** See `devDependencies`, below. -See [semver]([/using-npm/semver](https://github.com/npm/node-semver#versions)) -for more details about specifying version ranges. +See [semver](/using-npm/semver#versions) for more details about specifying version ranges. * `version` Must match `version` exactly * `>version` Must be greater than `version` diff --git a/deps/npm/docs/content/using-npm/config.md b/deps/npm/docs/content/using-npm/config.md index 1032adafbeb222..da69ad46325305 100644 --- a/deps/npm/docs/content/using-npm/config.md +++ b/deps/npm/docs/content/using-npm/config.md @@ -59,30 +59,48 @@ internal to npm, and are defaults if nothing else is specified. The following shorthands are parsed on the command-line: -* `-v`: `--version` -* `-h`, `-?`, `--help`, `-H`: `--usage` -* `-s`, `--silent`: `--loglevel silent` -* `-q`, `--quiet`: `--loglevel warn` -* `-d`: `--loglevel info` -* `-dd`, `--verbose`: `--loglevel verbose` -* `-ddd`: `--loglevel silly` +<!-- AUTOGENERATED CONFIG SHORTHANDS START --> +<!-- automatically generated, do not edit manually --> +* `-a`: `--all` +* `--enjoy-by`: `--before` +* `-c`: `--call` +* `--desc`: `--description` +* `-f`: `--force` * `-g`: `--global` -* `-C`: `--prefix` +* `-d`: `--loglevel info` +* `-s`: `--loglevel silent` +* `--silent`: `--loglevel silent` +* `--ddd`: `--loglevel silly` +* `--dd`: `--loglevel verbose` +* `--verbose`: `--loglevel verbose` +* `-q`: `--loglevel warn` +* `--quiet`: `--loglevel warn` * `-l`: `--long` * `-m`: `--message` -* `-p`, `--porcelain`: `--parseable` -* `-reg`: `--registry` -* `-f`: `--force` -* `-desc`: `--description` +* `--local`: `--no-global` +* `-n`: `--no-yes` +* `--no`: `--no-yes` +* `-p`: `--parseable` +* `--porcelain`: `--parseable` +* `-C`: `--prefix` +* `--readonly`: `--read-only` +* `--reg`: `--registry` * `-S`: `--save` -* `-P`: `--save-prod` -* `-D`: `--save-dev` -* `-O`: `--save-optional` * `-B`: `--save-bundle` +* `-D`: `--save-dev` * `-E`: `--save-exact` +* `-O`: `--save-optional` +* `-P`: `--save-prod` +* `-?`: `--usage` +* `-h`: `--usage` +* `-H`: `--usage` +* `--help`: `--usage` +* `-v`: `--version` +* `-w`: `--workspace` +* `--ws`: `--workspaces` * `-y`: `--yes` -* `-n`: `--yes false` -* `ll` and `la` commands: `ls --long` + +<!-- AUTOGENERATED CONFIG SHORTHANDS END --> If the specified configuration param resolves unambiguously to a known configuration parameter, then it is expanded to that configuration @@ -107,26 +125,39 @@ npm ls --global --parseable --long --loglevel info ### Config Settings -#### access +<!-- AUTOGENERATED CONFIG DESCRIPTIONS START --> +<!-- automatically generated, do not edit manually --> +#### `_auth` + +* Default: null +* Type: null or String + +A basic-auth string to use when authenticating against the npm registry. + +Warning: This should generally not be set via a command-line option. It is +safer to use a registry-provided authentication bearer token stored in the +~/.npmrc file by running `npm login`. -* Default: `restricted` -* Type: Access +#### `access` -When publishing scoped packages, the access level defaults to `restricted`. If -you want your scoped package to be publicly viewable (and installable) set -`--access=public`. The only valid values for `access` are `public` and +* Default: 'restricted' for scoped packages, 'public' for unscoped packages +* Type: null, "restricted", or "public" + +When publishing scoped packages, the access level defaults to `restricted`. +If you want your scoped package to be publicly viewable (and installable) +set `--access=public`. The only valid values for `access` are `public` and `restricted`. Unscoped packages _always_ have an access level of `public`. -#### all +#### `all` -* Default: `false` +* Default: false * Type: Boolean When running `npm outdated` and `npm ls`, setting `--all` will show all outdated or installed packages, rather than only those directly depended upon by the current project. -#### allow-same-version +#### `allow-same-version` * Default: false * Type: Boolean @@ -134,78 +165,62 @@ upon by the current project. Prevents throwing an error when `npm version` is used to set the new version to the same value as the current version. -#### always-auth +#### `always-auth` * Default: false * Type: Boolean -Force npm to always require authentication when accessing the registry, -even for `GET` requests. - -#### also - -* Default: null -* Type: String - -When "dev" or "development" and running local `npm shrinkwrap`, -`npm outdated`, or `npm update`, is an alias for `--dev`. +Force npm to always require authentication when accessing the registry, even +for `GET` requests. -#### audit +#### `audit` * Default: true * Type: Boolean When "true" submit audit reports alongside `npm install` runs to the default -registry and all registries configured for scopes. See the documentation -for [`npm audit`](/commands/npm-audit) for details on what is submitted. - -#### audit-level - -* Default: `"low"` -* Type: `'low'`, `'moderate'`, `'high'`, `'critical'` - -The minimum level of vulnerability for `npm audit` to exit with -a non-zero exit code. +registry and all registries configured for scopes. See the documentation for +[`npm audit`](/commands/npm-audit) for details on what is submitted. -#### auth-type +#### `audit-level` -* Default: `'legacy'` -* Type: `'legacy'`, `'sso'`, `'saml'`, `'oauth'` +* Default: null +* Type: "low", "moderate", "high", "critical", "none", or null -What authentication strategy to use with `adduser`/`login`. +The minimum level of vulnerability for `npm audit` to exit with a non-zero +exit code. -#### before +#### `before` -* Alias: enjoy-by * Default: null -* Type: Date +* Type: null or Date -If passed to `npm install`, will rebuild the npm tree such that only versions -that were available **on or before** the `--before` time get installed. -If there's no versions available for the current set of direct dependencies, the -command will error. +If passed to `npm install`, will rebuild the npm tree such that only +versions that were available **on or before** the `--before` time get +installed. If there's no versions available for the current set of direct +dependencies, the command will error. If the requested version is a `dist-tag` and the given tag does not pass the -`--before` filter, the most recent version less than or equal to that tag will -be used. For example, `foo@latest` might install `foo@1.2` even though `latest` -is `2.0`. +`--before` filter, the most recent version less than or equal to that tag +will be used. For example, `foo@latest` might install `foo@1.2` even though +`latest` is `2.0`. -#### bin-links +#### `bin-links` -* Default: `true` +* Default: true * Type: Boolean Tells npm to create symlinks (or `.cmd` shims on Windows) for package executables. -Set to false to have it not do this. This can be used to work around -the fact that some file systems don't support symlinks, even on -ostensibly Unix systems. +Set to false to have it not do this. This can be used to work around the +fact that some file systems don't support symlinks, even on ostensibly Unix +systems. -#### browser +#### `browser` * Default: OS X: `"open"`, Windows: `"start"`, Others: `"xdg-open"` -* Type: String or Boolean +* Type: null, Boolean, or String The browser that is called by npm commands to open websites. @@ -214,87 +229,50 @@ terminal. Set to `true` to use default system URL opener. -#### ca +#### `ca` -* Default: The npm CA certificate -* Type: String, Array or null +* Default: null +* Type: null or String (can be set multiple times) The Certificate Authority signing certificate that is trusted for SSL -connections to the registry. Values should be in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with newlines -replaced by the string "\n". For example: +connections to the registry. Values should be in PEM format (Windows calls +it "Base-64 encoded X.509 (.CER)") with newlines replaced by the string +"\n". For example: -```bash +```ini ca="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" ``` -Set to `null` to only allow "known" registrars, or to a specific CA cert -to trust only that specific signing authority. +Set to `null` to only allow "known" registrars, or to a specific CA cert to +trust only that specific signing authority. Multiple CAs can be trusted by specifying an array of certificates: -```bash +```ini ca[]="..." ca[]="..." ``` See also the `strict-ssl` config. -#### cafile - -* Default: `null` -* Type: path - -A path to a file containing one or multiple Certificate Authority signing -certificates. Similar to the `ca` setting, but allows for multiple CA's, as -well as for the CA information to be stored in a file on disk. - -#### cache - -* Default: Windows: `%AppData%\npm-cache`, Posix: `~/.npm` -* Type: path - -The location of npm's cache directory. See [`npm cache`](/commands/npm-cache) - -#### cache-lock-stale - -* Default: 60000 (1 minute) -* Type: Number - -The number of ms before cache folder lockfiles are considered stale. - -#### cache-lock-retries - -* Default: 10 -* Type: Number +#### `cache` -Number of times to retry to acquire a lock on cache folder lockfiles. +* Default: Windows: `%LocalAppData%\npm-cache`, Posix: `~/.npm` +* Type: Path -#### cache-lock-wait +The location of npm's cache directory. See [`npm +cache`](/commands/npm-cache) -* Default: 10000 (10 seconds) -* Type: Number +#### `cafile` -Number of ms to wait for cache lock files to expire. - -#### cache-max - -* Default: Infinity -* Type: Number - -**DEPRECATED**: This option has been deprecated in favor of `--prefer-online`. - -`--cache-max=0` is an alias for `--prefer-online`. - -#### cache-min - -* Default: 10 -* Type: Number - -**DEPRECATED**: This option has been deprecated in favor of `--prefer-offline`. +* Default: null +* Type: Path -`--cache-min=9999 (or bigger)` is an alias for `--prefer-offline`. +A path to a file containing one or multiple Certificate Authority signing +certificates. Similar to the `ca` setting, but allows for multiple CA's, as +well as for the CA information to be stored in a file on disk. -#### call +#### `call` * Default: "" * Type: String @@ -306,154 +284,206 @@ custom command to be run along with the installed packages. npm exec --package yo --package generator-node --call "yo node" ``` -#### cert -* Default: `null` -* Type: String +#### `cert` + +* Default: null +* Type: null or String -A client certificate to pass when accessing the registry. Values should be in -PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with newlines replaced by the string "\n". For example: +A client certificate to pass when accessing the registry. Values should be +in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with +newlines replaced by the string "\n". For example: -```bash +```ini cert="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" ``` -It is _not_ the path to a certificate file (and there is no "certfile" option). +It is _not_ the path to a certificate file (and there is no "certfile" +option). -#### cidr +#### `ci-name` -* Default: `null` -* Type: String, Array, null +* Default: The name of the current CI system, or `null` when not on a known CI + platform. +* Type: null or String -This is a list of CIDR address to be used when configuring limited access tokens with the `npm token create` command. +The name of a continuous integration system. If not set explicitly, npm will +detect the current CI environment using the +[`@npmcli/ci-detect`](http://npm.im/@npmcli/ci-detect) module. -#### commit-hooks +#### `cidr` -* Default: `true` -* Type: Boolean +* Default: null +* Type: null or String (can be set multiple times) -Run git commit hooks when using the `npm version` command. +This is a list of CIDR address to be used when configuring limited access +tokens with the `npm token create` command. -#### color +#### `color` -* Default: true -* Type: Boolean or `"always"` +* Default: true unless the NO_COLOR environ is set to something other than '0' +* Type: "always" or Boolean -If false, never shows colors. If `"always"` then always shows colors. -If true, then only prints color codes for tty file descriptors. +If false, never shows colors. If `"always"` then always shows colors. If +true, then only prints color codes for tty file descriptors. -This option can also be changed using the environment: colors are -disabled when the environment variable `NO_COLOR` is set to any value. +#### `commit-hooks` -#### depth +* Default: true +* Type: Boolean -* Default: null +Run git commit hooks when using the `npm version` command. + +#### `depth` + +* Default: `Infinity` if `--all` is set, otherwise `1` * Type: null or Number The depth to go when recursing packages for `npm ls`. -To make this default to `Infinity` instead of `null`, set `--all`. +If not set, `npm ls` will show only the immediate dependencies of the root +project. If `--all` is set, then npm will show all dependencies by default. -#### description +#### `description` * Default: true * Type: Boolean Show the description in `npm search` -#### dev +#### `diff` + +* Default: +* Type: String (can be set multiple times) + +Define arguments to compare in `npm diff`. + +#### `diff-dst-prefix` + +* Default: "b/" +* Type: String + +Destination prefix to be used in `npm diff` output. + +#### `diff-ignore-all-space` * Default: false * Type: Boolean -\[Deprecated\] Install `dev-dependencies` along with packages. +Ignore whitespace when comparing lines in `npm diff`. -#### dry-run +#### `diff-name-only` * Default: false * Type: Boolean -Indicates that you don't want npm to make any changes and that it should -only report what it would have done. This can be passed into any of the -commands that modify your local installation, eg, `install`, `update`, -`dedupe`, `uninstall`. This is NOT currently honored by some network related -commands, eg `dist-tags`, `owner`, etc. +Prints only filenames when using `npm diff`. -#### diff +#### `diff-no-prefix` -* Default: null -* Type: String, Array, null +* Default: false +* Type: Boolean -Define arguments to compare in `npm diff`. +Do not show any source or destination prefix in `npm diff` output. + +Note: this causes `npm diff` to ignore the `--diff-src-prefix` and +`--diff-dst-prefix` configs. + +#### `diff-src-prefix` + +* Default: "a/" +* Type: String + +Source prefix to be used in `npm diff` output. -#### diff-name-only +#### `diff-text` * Default: false * Type: Boolean -Prints only filenames when using `npm diff`. +Treat all files as text in `npm diff`. -#### diff-unified +#### `diff-unified` -* Type: number -* Default: `3` +* Default: 3 +* Type: Number The number of lines of context to print in `npm diff`. -#### diff-ignore-all-space +#### `dry-run` -* Type: Boolean * Default: false +* Type: Boolean -Ignore whitespace when comparing lines in `npm diff. +Indicates that you don't want npm to make any changes and that it should +only report what it would have done. This can be passed into any of the +commands that modify your local installation, eg, `install`, `update`, +`dedupe`, `uninstall`, as well as `pack` and `publish`. -#### diff-no-prefix +Note: This is NOT honored by other network related commands, eg `dist-tags`, +`owner`, etc. + +#### `editor` + +* Default: The EDITOR or VISUAL environment variables, or 'notepad.exe' on + Windows, or 'vim' on Unix systems +* Type: String + +The command to run for `npm edit` and `npm config edit`. + +#### `engine-strict` -* Type: Boolean * Default: false +* Type: Boolean -Do not show any source or destination prefix in `npm diff` output. +If set to true, then npm will stubbornly refuse to install (or even consider +installing) any package that claims to not be compatible with the current +Node.js version. -#### diff-src-prefix +This can be overridden by setting the `--force` flag. -* Type: String -* Default: `"a/"` +#### `fetch-retries` -Source prefix to be used in `npm diff` output. +* Default: 2 +* Type: Number -#### diff-dst-prefix +The "retries" config for the `retry` module to use when fetching packages +from the registry. -* Type: String -* Default: `"b/"` +npm will retry idempotent read requests to the registry in the case of +network failures or 5xx HTTP errors. -Destination prefix to be used in `npm diff` output. +#### `fetch-retry-factor` -#### diff-text +* Default: 10 +* Type: Number -* Alias: `-a` -* Type: Boolean -* Default: false +The "factor" config for the `retry` module to use when fetching packages. -Treat all files as text in `npm diff`. +#### `fetch-retry-maxtimeout` -#### editor +* Default: 60000 (1 minute) +* Type: Number -* Default: `EDITOR` environment variable if set, or `"vi"` on Posix, - or `"notepad"` on Windows. -* Type: path +The "maxTimeout" config for the `retry` module to use when fetching +packages. -The command to run for `npm edit` or `npm config edit`. +#### `fetch-retry-mintimeout` -#### engine-strict +* Default: 10000 (10 seconds) +* Type: Number -* Default: false -* Type: Boolean +The "minTimeout" config for the `retry` module to use when fetching +packages. + +#### `fetch-timeout` -If set to true, then npm will stubbornly refuse to install (or even -consider installing) any package that claims to not be compatible with -the current Node.js version. +* Default: 300000 (5 minutes) +* Type: Number + +The maximum amount of time to wait for HTTP requests to complete. -#### force +#### `force` * Default: false * Type: Boolean @@ -467,17 +497,16 @@ mistakes, unnecessary performance degradation, and malicious input. * Allow installing packages that have an `engines` declaration requiring a different version of npm. * Allow installing packages that have an `engines` declaration requiring a - different version of `node`, even if `--engines-strict` is enabled. + different version of `node`, even if `--engine-strict` is enabled. * Allow `npm audit fix` to install modules outside your stated dependency range (including SemVer-major changes). -* Allow a module to be installed as a direct dependency of itself. * Allow unpublishing all versions of a published package. * Allow conflicting peerDependencies to be installed in the root project. If you don't have a clear idea of what you want to do, it is strongly recommended that you do not use this option! -#### foreground-scripts +#### `foreground-scripts` * Default: false * Type: Boolean @@ -486,249 +515,217 @@ Run all build scripts (ie, `preinstall`, `install`, and `postinstall`) scripts for installed packages in the foreground process, sharing standard input, output, and error with the main npm process. -Note that this will generally make installs run slower, and be much -noisier, but can be useful for debugging. +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. -#### format-package-lock +#### `format-package-lock` * Default: true * Type: Boolean -Format `package-lock.json` or `npm-shrinkwrap.json` as a human readable file. +Format `package-lock.json` or `npm-shrinkwrap.json` as a human readable +file. -#### fund +#### `fund` * Default: true * Type: Boolean When "true" displays the message at the end of each `npm install` -acknowledging the number of dependencies looking for funding. -See [`npm fund`](/commands/npm-fund) for details. +acknowledging the number of dependencies looking for funding. See [`npm +fund`](/commands/npm-fund) for details. -#### fetch-retries +#### `git` -* Default: 2 -* Type: Number - -The "retries" config for the `retry` module to use when fetching -packages from the registry. - -#### fetch-retry-factor - -* Default: 10 -* Type: Number - -The "factor" config for the `retry` module to use when fetching -packages. - -#### fetch-retry-mintimeout - -* Default: 10000 (10 seconds) -* Type: Number - -The "minTimeout" config for the `retry` module to use when fetching -packages. - -#### fetch-retry-maxtimeout - -* Default: 60000 (1 minute) -* Type: Number - -The "maxTimeout" config for the `retry` module to use when fetching -packages. - -#### fetch-timeout - -* Default: 300000 (5 minutes) -* Type: Number - -The maximum amount of time to wait for HTTP requests to complete. - -#### git - -* Default: `"git"` +* Default: "git" * Type: String -The command to use for git commands. If git is installed on the -computer, but is not in the `PATH`, then set this to the full path to -the git binary. +The command to use for git commands. If git is installed on the computer, +but is not in the `PATH`, then set this to the full path to the git binary. -#### git-tag-version +#### `git-tag-version` -* Default: `true` +* Default: true * Type: Boolean Tag the commit when using the `npm version` command. -#### global +#### `global` * Default: false * Type: Boolean -Operates in "global" mode, so that packages are installed into the -`prefix` folder instead of the current working directory. See +Operates in "global" mode, so that packages are installed into the `prefix` +folder instead of the current working directory. See [folders](/configuring-npm/folders) for more on the differences in behavior. -* packages are installed into the `{prefix}/lib/node_modules` folder, instead of the - current working directory. +* packages are installed into the `{prefix}/lib/node_modules` folder, instead + of the current working directory. * bin files are linked to `{prefix}/bin` * man pages are linked to `{prefix}/share/man` -#### globalconfig - -* Default: {prefix}/etc/npmrc -* Type: path - -The config file to read for global config options. - -#### global-style +#### `global-style` * Default: false * Type: Boolean Causes npm to install the package into your local `node_modules` folder with -the same layout it uses with the global `node_modules` folder. Only your +the same layout it uses with the global `node_modules` folder. Only your direct dependencies will show in `node_modules` and everything they depend -on will be flattened in their `node_modules` folders. This obviously will -eliminate some deduping. If used with `legacy-bundling`, `legacy-bundling` will be -preferred. +on will be flattened in their `node_modules` folders. This obviously will +eliminate some deduping. If used with `legacy-bundling`, `legacy-bundling` +will be preferred. + +#### `globalconfig` + +* Default: The global --prefix setting plus 'etc/npmrc'. For example, + '/usr/local/etc/npmrc' +* Type: Path -#### heading +The config file to read for global config options. + +#### `heading` -* Default: `"npm"` +* Default: "npm" * Type: String The string that starts all the debugging log output. -#### https-proxy +#### `https-proxy` * Default: null -* Type: url +* Type: null or URL A proxy to use for outgoing https requests. If the `HTTPS_PROXY` or `https_proxy` or `HTTP_PROXY` or `http_proxy` environment variables are set, -proxy settings will be honored by the underlying `request` library. +proxy settings will be honored by the underlying `make-fetch-happen` +library. -#### if-present +#### `if-present` * Default: false * Type: Boolean -If true, npm will not exit with an error code when `run-script` is invoked for -a script that isn't defined in the `scripts` section of `package.json`. This -option can be used when it's desirable to optionally run a script when it's -present and fail if the script fails. This is useful, for example, when running -scripts that may only apply for some builds in an otherwise generic CI setup. +If true, npm will not exit with an error code when `run-script` is invoked +for a script that isn't defined in the `scripts` section of `package.json`. +This option can be used when it's desirable to optionally run a script when +it's present and fail if the script fails. This is useful, for example, when +running scripts that may only apply for some builds in an otherwise generic +CI setup. -#### ignore-prepublish +#### `ignore-scripts` * Default: false * Type: Boolean -If true, npm will not run `prepublish` scripts. +If true, npm does not run scripts specified in package.json files. -#### ignore-scripts +#### `include` -* Default: false -* Type: Boolean +* Default: +* Type: "prod", "dev", "optional", or "peer" (can be set multiple times) -If true, npm does not run scripts specified in package.json files. +Option that allows for defining which types of dependencies to install. -#### include +This is the inverse of `--omit=<type>`. -* Default: `[prod|dev|optional|peer]` -* Type: Array +Dependency types specified in `--include` will not be omitted, regardless of +the order in which omit/include are specified on the command-line. -Option that allows for defining which types of dependencies to install. +#### `include-staged` -#### init-module +* Default: false +* Type: Boolean -* Alias: `init.module` -* Default: ~/.npm-init.js -* Type: path +Allow installing "staged" published packages, as defined by [npm RFC PR +#92](https://github.com/npm/rfcs/pull/92). -A module that will be loaded by the `npm init` command. See the -documentation for the -[init-package-json](https://github.com/npm/init-package-json) module -for more information, or [npm init](/commands/npm-init). +This is experimental, and not implemented by the npm public registry. -#### init-author-name +#### `init-author-email` -* Alias: `init.author.name` * Default: "" * Type: String -The value `npm init` should use by default for the package author's name. +The value `npm init` should use by default for the package author's email. -#### init-author-email +#### `init-author-name` -* Alias: `init.author.email` * Default: "" * Type: String -The value `npm init` should use by default for the package author's email. +The value `npm init` should use by default for the package author's name. -#### init-author-url +#### `init-author-url` -* Alias: `init.author.url` * Default: "" -* Type: String +* Type: "" or URL -The value `npm init` should use by default for the package author's homepage. +The value `npm init` should use by default for the package author's +homepage. -#### init-license +#### `init-license` -* Alias: `init.license` * Default: "ISC" * Type: String The value `npm init` should use by default for the package license. -#### init-version +#### `init-module` + +* Default: "~/.npm-init.js" +* Type: Path + +A module that will be loaded by the `npm init` command. See the +documentation for the +[init-package-json](https://github.com/npm/init-package-json) module for +more information, or [npm init](/commands/npm-init). + +#### `init-version` -* Alias: `init.version` * Default: "1.0.0" -* Type: semver +* Type: SemVer string -The value that `npm init` should use by default for the package -version number, if not already set in package.json. +The value that `npm init` should use by default for the package version +number, if not already set in package.json. -#### json +#### `json` * Default: false * Type: Boolean Whether or not to output JSON data, rather than the normal output. -This feature is currently experimental, and the output data structures for many -commands is either not implemented in JSON yet, or subject to change. Only the -output from `npm ls --json` and `npm search --json` are currently valid. +This feature is currently experimental, and the output data structures for +many commands is either not implemented in JSON yet, or subject to change. +Only the output from `npm ls --json` and `npm search --json` are currently +valid. -#### key +#### `key` -* Default: `null` -* Type: String +* Default: null +* Type: null or String -A client key to pass when accessing the registry. Values should be in PEM +A client key to pass when accessing the registry. Values should be in PEM format with newlines replaced by the string "\n". For example: -```json +```ini key="-----BEGIN PRIVATE KEY-----\nXXXX\nXXXX\n-----END PRIVATE KEY-----" ``` It is _not_ the path to a key file (and there is no "keyfile" option). -#### legacy-bundling +#### `legacy-bundling` * Default: false * Type: Boolean Causes npm to install the package such that versions of npm prior to 1.4, -such as the one included with node 0.8, can install the package. This +such as the one included with node 0.8, can install the package. This eliminates all automatic deduping. If used with `global-style` this option will be preferred. -#### legacy-peer-deps +#### `legacy-peer-deps` * Default: false * Type: Boolean @@ -736,9 +733,8 @@ will be preferred. Causes npm to completely ignore `peerDependencies` when building a package tree, as in npm versions 3 through 6. -If a package cannot be installed because of overly strict -`peerDependencies` that collide, it provides a way to move forward -resolving the situation. +If a package cannot be installed because of overly strict `peerDependencies` +that collide, it provides a way to move forward resolving the situation. This differs from `--omit=peer`, in that `--omit=peer` will avoid unpacking `peerDependencies` on disk, but will still design a tree such that @@ -747,7 +743,7 @@ This differs from `--omit=peer`, in that `--omit=peer` will avoid unpacking Use of `legacy-peer-deps` is not recommended, as it will not enforce the `peerDependencies` contract that meta-dependencies may rely on. -#### link +#### `link` * Default: false * Type: Boolean @@ -755,57 +751,57 @@ Use of `legacy-peer-deps` is not recommended, as it will not enforce the If true, then local installs will link if there is a suitable globally installed package. -Note that this means that local installs can cause things to be -installed into the global space at the same time. The link is only done -if one of the two conditions are met: +Note that this means that local installs can cause things to be installed +into the global space at the same time. The link is only done if one of the +two conditions are met: * The package is not already installed globally, or -* the globally installed version is identical to the version that is - being installed locally. +* the globally installed version is identical to the version that is being + installed locally. -#### local-address +#### `local-address` -* Default: undefined +* Default: null * Type: IP Address -The IP address of the local interface to use when making connections -to the npm registry. Must be IPv4 in versions of Node prior to 0.12. +The IP address of the local interface to use when making connections to the +npm registry. Must be IPv4 in versions of Node prior to 0.12. -#### loglevel +#### `loglevel` * Default: "notice" -* Type: String -* Values: "silent", "error", "warn", "notice", "http", "timing", "info", - "verbose", "silly" +* Type: "silent", "error", "warn", "notice", "http", "timing", "info", + "verbose", or "silly" -What level of logs to report. On failure, *all* logs are written to +What level of logs to report. On failure, *all* logs are written to `npm-debug.log` in the current working directory. -Any logs of a higher level than the setting are shown. The default is "notice". +Any logs of a higher level than the setting are shown. The default is +"notice". -#### logs-max +#### `logs-max` * Default: 10 * Type: Number The maximum number of log files to store. -#### long +#### `long` * Default: false * Type: Boolean Show extended information in `npm ls` and `npm search`. -#### maxsockets +#### `maxsockets` -* Default: 50 +* Default: Infinity * Type: Number The maximum number of connections to use per origin (protocol/host/port -combination). Passed to the `http` `Agent` used to make the request. +combination). -#### message +#### `message` * Default: "%s" * Type: String @@ -814,73 +810,83 @@ Commit message which is used by `npm version` when creating version commit. Any "%s" in the message will be replaced with the version number. -#### node-options +#### `node-options` * Default: null -* Type: String +* Type: null or String Options to pass through to Node.js via the `NODE_OPTIONS` environment -variable. This does not impact how npm itself is executed but it does -impact how lifecycle scripts are called. +variable. This does not impact how npm itself is executed but it does impact +how lifecycle scripts are called. -#### node-version +#### `node-version` -* Default: process.version -* Type: semver or false +* Default: Node.js `process.version` value +* Type: SemVer string -The node version to use when checking a package's `engines` map. +The node version to use when checking a package's `engines` setting. -#### noproxy +#### `noproxy` -* Default: null -* Type: String or Array +* Default: The value of the NO_PROXY environment variable +* Type: String (can be set multiple times) -A comma-separated string or an array of domain extensions that a proxy should not be used for. +Domain extensions that should bypass any proxies. -#### offline +Also accepts a comma-delimited string. -* Default: false -* Type: Boolean +#### `npm-version` -Force offline mode: no network requests will be done during install. To allow -the CLI to fill in missing cache data, see `--prefer-offline`. +* Default: Output of `npm --version` +* Type: SemVer string -#### only +The npm version to use when checking a package's `engines` setting. -* Default: null -* Type: String +#### `offline` -When "dev" or "development" and running local `npm install` without any -arguments, only devDependencies (and their dependencies) are installed. +* Default: false +* Type: Boolean -When "dev" or "development" and running local `npm ls`, `npm outdated`, or -`npm update`, is an alias for `--dev`. +Force offline mode: no network requests will be done during install. To +allow the CLI to fill in missing cache data, see `--prefer-offline`. -When "prod" or "production" and running local `npm install` without any -arguments, only non-devDependencies (and their dependencies) are -installed. +#### `omit` -When "prod" or "production" and running local `npm ls`, `npm outdated`, or -`npm update`, is an alias for `--production`. +* Default: 'dev' if the NODE_ENV environment variable is set to 'production', + otherwise empty. +* Type: "dev", "optional", or "peer" (can be set multiple times) -#### optional +Dependency types to omit from the installation tree on disk. -* Default: true -* Type: Boolean +Note that these dependencies _are_ still resolved and added to the +`package-lock.json` or `npm-shrinkwrap.json` file. They are just not +physically installed on disk. + +If a package type appears in both the `--include` and `--omit` lists, then +it will be included. -Attempt to install packages in the `optionalDependencies` object. Note -that if these packages fail to install, the overall installation -process is not aborted. +If the resulting omit list includes `'dev'`, then the `NODE_ENV` environment +variable will be set to `'production'` for all lifecycle scripts. -#### otp +#### `otp` * Default: null -* Type: Number +* Type: null or String -This is a one-time password from a two-factor authenticator. It's needed +This is a one-time password from a two-factor authenticator. It's needed when publishing or changing package permissions with `npm access`. -#### package-lock +If not set, and a registry response fails with a challenge for a one-time +password, npm will prompt on the command line for one. + +#### `package` + +* Default: +* Type: String (can be set multiple times) + +The package to install for [`npm exec`](/commands/npm-exec) + +#### `package-lock` * Default: true * Type: Boolean @@ -889,75 +895,63 @@ If set to false, then ignore `package-lock.json` files when installing. This will also prevent _writing_ `package-lock.json` if `save` is true. When package package-locks are disabled, automatic pruning of extraneous -modules will also be disabled. To remove extraneous modules with +modules will also be disabled. To remove extraneous modules with package-locks disabled use `npm prune`. -This option is an alias for `--shrinkwrap`. - -#### package-lock-only +#### `package-lock-only` * Default: false * Type: Boolean -If set to true, it will update only the `package-lock.json`, -instead of checking `node_modules` and downloading dependencies. +If set to true, it will update only the `package-lock.json`, instead of +checking `node_modules` and downloading dependencies. -#### parseable +#### `parseable` * Default: false * Type: Boolean -Output parseable results from commands that write to -standard output. For `npm search`, this will be tab-separated table format. +Output parseable results from commands that write to standard output. For +`npm search`, this will be tab-separated table format. -#### prefer-offline +#### `prefer-offline` * Default: false * Type: Boolean If true, staleness checks for cached data will be bypassed, but missing data -will be requested from the server. To force full offline mode, use `--offline`. - -This option is effectively equivalent to `--cache-min=9999999`. +will be requested from the server. To force full offline mode, use +`--offline`. -#### prefer-online +#### `prefer-online` * Default: false * Type: Boolean -If true, staleness checks for cached data will be forced, making the CLI look -for updates immediately even for fresh package data. +If true, staleness checks for cached data will be forced, making the CLI +look for updates immediately even for fresh package data. -#### prefix +#### `prefix` -* Default: see [folders](/configuring-npm/folders) -* Type: path +* Default: In global mode, the folder where the node executable is installed. + In local mode, the nearest parent folder containing either a package.json + file or a node_modules folder. +* Type: Path -The location to install global items. If set on the command line, then -it forces non-global commands to run in the specified folder. +The location to install global items. If set on the command line, then it +forces non-global commands to run in the specified folder. -#### preid +#### `preid` * Default: "" * Type: String -The "prerelease identifier" to use as a prefix for the "prerelease" part of a -semver. Like the `rc` in `1.2.0-rc.8`. +The "prerelease identifier" to use as a prefix for the "prerelease" part of +a semver. Like the `rc` in `1.2.0-rc.8`. -#### production +#### `progress` -* Default: false -* Type: Boolean - -Set to true to run in "production" mode. - -1. devDependencies are not installed at the topmost level when running - local `npm install` without any arguments. -2. Set the NODE_ENV="production" for lifecycle scripts. - -#### progress - -* Default: true, unless TRAVIS or CI env vars set. +* Default: `true` unless running in a known CI system * Type: Boolean When set to `true`, npm will display a progress bar during time intensive @@ -965,56 +959,48 @@ operations, if `process.stderr` is a TTY. Set to `false` to suppress the progress bar. -#### proxy +#### `proxy` * Default: null -* Type: url +* Type: null, false, or URL A proxy to use for outgoing http requests. If the `HTTP_PROXY` or -`http_proxy` environment variables are set, proxy settings will be -honored by the underlying `request` library. +`http_proxy` environment variables are set, proxy settings will be honored +by the underlying `request` library. -#### read-only +#### `read-only` * Default: false * Type: Boolean -This is used to mark a token as unable to publish when configuring limited access tokens with the `npm token create` command. +This is used to mark a token as unable to publish when configuring limited +access tokens with the `npm token create` command. -#### rebuild-bundle +#### `rebuild-bundle` * Default: true * Type: Boolean Rebuild bundled dependencies after installation. -#### registry +#### `registry` -* Default: https://registry.npmjs.org/ -* Type: url +* Default: "https://registry.npmjs.org/" +* Type: URL -The base URL of the npm package registry. +The base URL of the npm registry. -#### rollback - -* Default: true -* Type: Boolean - -Remove failed installs. - -#### save +#### `save` * Default: true * Type: Boolean Save installed packages to a package.json file as dependencies. -When used with the `npm rm` command, it removes it from the `dependencies` -object. - -Only works if there is already a package.json file present. +When used with the `npm rm` command, removes the dependency from +package.json. -#### save-bundle +#### `save-bundle` * Default: false * Type: Boolean @@ -1023,120 +1009,94 @@ If a package would be saved at install time by the use of `--save`, `--save-dev`, or `--save-optional`, then also put it in the `bundleDependencies` list. -When used with the `npm rm` command, it removes it from the -bundledDependencies list. +Ignore if `--save-peer` is set, since peerDependencies cannot be bundled. -#### save-prod +#### `save-dev` * Default: false * Type: Boolean -Makes sure that a package will be saved into `dependencies` specifically. This -is useful if a package already exists in `devDependencies` or -`optionalDependencies`, but you want to move it to be a production dep. This is -also the default behavior if `--save` is true, and neither `--save-dev` or -`--save-optional` are true. +Save installed packages to a package.json file as `devDependencies`. -#### save-dev +#### `save-exact` * Default: false * Type: Boolean -Save installed packages to a package.json file as `devDependencies`. - -When used with the `npm rm` command, it removes it from the -`devDependencies` object. - -Only works if there is already a package.json file present. +Dependencies saved to package.json will be configured with an exact version +rather than using npm's default semver range operator. -#### save-exact +#### `save-optional` * Default: false * Type: Boolean -Dependencies saved to package.json using `--save`, `--save-dev` or -`--save-optional` will be configured with an exact version rather than -using npm's default semver range operator. +Save installed packages to a package.json file as `optionalDependencies`. -#### save-optional +#### `save-peer` * Default: false * Type: Boolean -Save installed packages to a package.json file as -optionalDependencies. +Save installed packages. to a package.json file as `peerDependencies` -When used with the `npm rm` command, it removes it from the -`devDependencies` object. +#### `save-prefix` -Only works if there is already a package.json file present. - -#### save-prefix - -* Default: '^' +* Default: "^" * Type: String Configure how versions of packages installed to a package.json file via `--save` or `--save-dev` get prefixed. -For example if a package has version `1.2.3`, by default its version is -set to `^1.2.3` which allows minor upgrades for that package, but after -`npm config set save-prefix='~'` it would be set to `~1.2.3` which only allows +For example if a package has version `1.2.3`, by default its version is set +to `^1.2.3` which allows minor upgrades for that package, but after `npm +config set save-prefix='~'` it would be set to `~1.2.3` which only allows patch upgrades. -#### scope - -* Default: the scope of the current project, if any, or "" -* Type: String +#### `save-prod` -Associate an operation with a scope for a scoped registry. Useful when logging -in to a private registry for the first time: -`npm login --scope=@organization --registry=registry.organization.com`, which -will cause `@organization` to be mapped to the registry for future installation -of packages specified according to the pattern `@organization/package`. - -#### script-shell +* Default: false +* Type: Boolean -* Default: `null` -* Type: path +Save installed packages into `dependencies` specifically. This is useful if +a package already exists in `devDependencies` or `optionalDependencies`, but +you want to move it to be a non-optional production dependency. -The shell to use for scripts run with the `npm run` command. +This is the default behavior if `--save` is true, and neither `--save-dev` +or `--save-optional` are true. -#### scripts-prepend-node-path +#### `scope` -* Default: "warn-only" -* Type: Boolean, `"auto"` or `"warn-only"` +* Default: the scope of the current project, if any, or "" +* Type: String -If set to `true`, add the directory in which the current `node` executable -resides to the `PATH` environment variable when running scripts, -even if that means that `npm` will invoke a different `node` executable than -the one which it is running. +Associate an operation with a scope for a scoped registry. -If set to `false`, never modify `PATH` with that. +Useful when logging in to a private registry for the first time: -If set to `"warn-only"`, never modify `PATH` but print a warning if `npm` thinks -that you may want to run it with `true`, e.g. because the `node` executable -in the `PATH` is not the one `npm` was invoked with. +```bash +npm login --scope=@mycorp --registry=https://registry.mycorp.com +``` -If set to `auto`, only add that directory to the `PATH` environment variable -if the `node` executable with which `npm` was invoked and the one that is found -first on the `PATH` are different. +This will cause `@mycorp` to be mapped to the registry for future +installation of packages specified according to the pattern +`@mycorp/package`. -#### searchexclude +#### `script-shell` -* Default: "" -* Type: String +* Default: '/bin/sh' on POSIX systems, 'cmd.exe' on Windows +* Type: null or String -Space-separated options that limit the results from search. +The shell to use for scripts run with the `npm run` command. -#### searchopts +#### `searchexclude` * Default: "" * Type: String -Space-separated options that are always passed to search. +Space-separated options that limit the results from search. -#### searchlimit +#### `searchlimit` * Default: 20 * Type: Number @@ -1144,33 +1104,30 @@ Space-separated options that are always passed to search. Number of items to limit search results to. Will not apply at all to legacy searches. -#### searchstaleness +#### `searchopts` + +* Default: "" +* Type: String + +Space-separated options that are always passed to search. + +#### `searchstaleness` -* Default: 900 (15 minutes) +* Default: 900 * Type: Number The age of the cache, in seconds, before another registry request is made if using legacy search endpoint. -#### shell +#### `shell` -* Default: SHELL environment variable, or "bash" on Posix, or "cmd" on +* Default: SHELL environment variable, or "bash" on Posix, or "cmd.exe" on Windows -* Type: path +* Type: String The shell to run for the `npm explore` command. -#### shrinkwrap - -* Default: true -* Type: Boolean - -If set to false, then ignore `npm-shrinkwrap.json` files when installing. This -will also prevent _writing_ `npm-shrinkwrap.json` if `save` is true. - -This option is an alias for `--package-lock`. - -#### sign-git-commit +#### `sign-git-commit` * Default: false * Type: Boolean @@ -1178,36 +1135,21 @@ This option is an alias for `--package-lock`. If set to true, then the `npm version` command will commit the new package version using `-S` to add a signature. -Note that git requires you to have set up GPG keys in your git configs -for this to work properly. +Note that git requires you to have set up GPG keys in your git configs for +this to work properly. -#### sign-git-tag +#### `sign-git-tag` * Default: false * Type: Boolean -If set to true, then the `npm version` command will tag the version -using `-s` to add a signature. +If set to true, then the `npm version` command will tag the version using +`-s` to add a signature. -Note that git requires you to have set up GPG keys in your git configs -for this to work properly. - -#### sso-poll-frequency - -* Default: 500 -* Type: Number - -When used with SSO-enabled `auth-type`s, configures how regularly the registry -should be polled while the user is completing authentication. - -#### sso-type - -* Default: 'oauth' -* Type: 'oauth', 'saml', or null - -If `--auth-type=sso`, the type of SSO type to use. +Note that git requires you to have set up GPG keys in your git configs for +this to work properly. -#### strict-peer-deps +#### `strict-peer-deps` * Default: false * Type: Boolean @@ -1217,145 +1159,345 @@ conflicting `peerDependencies` will be treated as an install failure, even if npm could reasonably guess the appropriate resolution based on non-peer dependency relationships. -By default, conflicting `peerDependencies` in the dependency graph will be -resolved using the nearest non-peer dependency specification, even if doing -so will result in some packages receiving a peer dependency outside the -range set in their package's `peerDependencies` object. When such and -override is performed, a warning is printed, explaining the conflict and -the packages involved. If `--strict-peer-deps` is set, then the warning is -treated as a failure. +By default, conflicting `peerDependencies` deep in the dependency graph will +be resolved using the nearest non-peer dependency specification, even if +doing so will result in some packages receiving a peer dependency outside +the range set in their package's `peerDependencies` object. -#### strict-ssl +When such and override is performed, a warning is printed, explaining the +conflict and the packages involved. If `--strict-peer-deps` is set, then +this warning is treated as a failure. + +#### `strict-ssl` * Default: true * Type: Boolean -Whether or not to do SSL key validation when making requests to the -registry via https. +Whether or not to do SSL key validation when making requests to the registry +via https. See also the `ca` config. -#### tag +#### `tag` -* Default: latest +* Default: "latest" * Type: String -If you ask npm to install a package and don't tell it a specific version, then -it will install the specified tag. +If you ask npm to install a package and don't tell it a specific version, +then it will install the specified tag. -Also the tag that is added to the package@version specified by the `npm -tag` command, if no explicit tag is given. +Also the tag that is added to the package@version specified by the `npm tag` +command, if no explicit tag is given. -#### tag-version-prefix +#### `tag-version-prefix` -* Default: `"v"` +* Default: "v" * Type: String If set, alters the prefix used when tagging a new version when performing a -version increment using `npm-version`. To remove the prefix altogether, set it -to the empty string: `""`. +version increment using `npm-version`. To remove the prefix altogether, set +it to the empty string: `""`. -Because other tools may rely on the convention that npm version tags look like -`v1.0.0`, _only use this property if it is absolutely necessary_. In +Because other tools may rely on the convention that npm version tags look +like `v1.0.0`, _only use this property if it is absolutely necessary_. In particular, use care when overriding this setting for public packages. -#### timing +#### `timing` -* Default: `false` +* Default: false * Type: Boolean If true, writes an `npm-debug` log to `_logs` and timing information to -`_timing.json`, both in your cache. `_timing.json` is a newline delimited -list of JSON objects. You can quickly view it with this -[json](https://www.npmjs.com/package/json) command line: -`json -g < ~/.npm/_timing.json`. +`_timing.json`, both in your cache, even if the command completes +successfully. `_timing.json` is a newline delimited list of JSON objects. + +You can quickly view it with this [json](https://npm.im/json) command line: +`npm exec -- json -g < ~/.npm/_timing.json`. + +#### `umask` + +* Default: 0 +* Type: Octal numeric string in range 0000..0777 (0..511) -#### tmp +The "umask" value to use when setting the file creation mode on files and +folders. -* Default: TMPDIR environment variable, or "/tmp" -* Type: path +Folders and executables are given a mode which is `0o777` masked against +this value. Other files are given a mode which is `0o666` masked against +this value. -Where to store temporary files and folders. All temp files are deleted -on success, but left behind on failure for forensic purposes. +Note that the underlying system will _also_ apply its own umask value to +files and folders that are created, and npm does not circumvent this, but +rather adds the `--umask` config to it. -#### unicode +Thus, the effective default umask value on most POSIX systems is 0o22, +meaning that folders and executables are created with a mode of 0o755 and +other files are created with a mode of 0o644. -* Default: false on windows, true on mac/unix systems with a unicode locale +#### `unicode` + +* Default: false on windows, true on mac/unix systems with a unicode locale, + as defined by the LC_ALL, LC_CTYPE, or LANG environment variables. * Type: Boolean -When set to true, npm uses unicode characters in the tree output. When -false, it uses ascii characters to draw trees. +When set to true, npm uses unicode characters in the tree output. When +false, it uses ascii characters instead of unicode glyphs. -#### update-notifier +#### `update-notifier` * Default: true * Type: Boolean -Set to false to suppress the update notification when using an older -version of npm than the latest. +Set to false to suppress the update notification when using an older version +of npm than the latest. -#### usage +#### `usage` * Default: false * Type: Boolean -Set to show short usage output (like the -H output) -instead of complete help when doing [`npm help`](/commands/npm-help). - -#### userconfig - -* Default: ~/.npmrc -* Type: path +Show short usage output about the command specified. -The location of user-level configuration settings. +#### `user-agent` -#### umask +* Default: "npm/{npm-version} node/{node-version} {platform} {arch} {ci}" +* Type: String -* Default: 022 -* Type: Octal numeric string in range 0000..0777 (0..511) +Sets the User-Agent request header. The following fields are replaced with +their actual counterparts: -The "umask" value to use when setting the file creation mode on files -and folders. +* `{npm-version}` - The npm version in use +* `{node-version}` - The Node.js version in use +* `{platform}` - The value of `process.platform` +* `{arch}` - The value of `process.arch` +* `{ci}` - The value of the `ci-name` config, if set, prefixed with `ci/`, or + an empty string if `ci-name` is empty. -Folders and executables are given a mode which is `0777` masked against -this value. Other files are given a mode which is `0666` masked against -this value. Thus, the defaults are `0755` and `0644` respectively. +#### `userconfig` -#### user-agent +* Default: "~/.npmrc" +* Type: Path -* Default: node/{process.version} {process.platform} {process.arch} -* Type: String +The location of user-level configuration settings. -Sets a User-Agent to the request header +This may be overridden by the `npm_config_userconfig` environment variable +or the `--userconfig` command line option, but may _not_ be overridden by +settings in the `globalconfig` file. -#### version +#### `version` * Default: false -* Type: boolean +* Type: Boolean If true, output the npm version and exit successfully. Only relevant when specified explicitly on the command line. -#### versions +#### `versions` * Default: false -* Type: boolean +* Type: Boolean -If true, output the npm version as well as node's `process.versions` map, and -exit successfully. +If true, output the npm version as well as node's `process.versions` map and +the version in the current working directory's `package.json` file if one +exists, and exit successfully. Only relevant when specified explicitly on the command line. -#### viewer +#### `viewer` * Default: "man" on Posix, "browser" on Windows -* Type: path +* Type: String The program to use to view help content. Set to `"browser"` to view html help content in the default web browser. +#### `which` + +* Default: null +* Type: null or Number + +If there are multiple funding sources, which 1-indexed source URL to open. + +#### `workspace` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the `workspace` config are either: - Workspace names - Path +to a workspace directory - Path to a parent workspace directory (will result +to selecting all of the nested workspaces) + +#### `workspaces` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +#### `yes` + +* Default: null +* Type: null or Boolean + +Automatically answer "yes" to any prompts that npm might print on the +command line. + +#### `also` + +* Default: null +* Type: null, "dev", or "development" +* DEPRECATED: Please use --include=dev instead. + +When set to `dev` or `development`, this is an alias for `--include=dev`. + +#### `auth-type` + +* Default: "legacy" +* Type: "legacy", "sso", "saml", or "oauth" +* DEPRECATED: This method of SSO/SAML/OAuth is deprecated and will be removed + in a future version of npm in favor of web-based login. + +What authentication strategy to use with `adduser`/`login`. + +#### `cache-max` + +* Default: Infinity +* Type: Number +* DEPRECATED: This option has been deprecated in favor of `--prefer-online` + +`--cache-max=0` is an alias for `--prefer-online` + +#### `cache-min` + +* Default: 0 +* Type: Number +* DEPRECATED: This option has been deprecated in favor of `--prefer-offline`. + +`--cache-min=9999 (or bigger)` is an alias for `--prefer-offline`. + +#### `init.author.email` + +* Default: "" +* Type: String +* DEPRECATED: Use `--init-author-email` instead. + +Alias for `--init-author-email` + +#### `init.author.name` + +* Default: "" +* Type: String +* DEPRECATED: Use `--init-author-name` instead. + +Alias for `--init-author-name` + +#### `init.author.url` + +* Default: "" +* Type: "" or URL +* DEPRECATED: Use `--init-author-url` instead. + +Alias for `--init-author-url` + +#### `init.license` + +* Default: "ISC" +* Type: String +* DEPRECATED: Use `--init-license` instead. + +Alias for `--init-license` + +#### `init.module` + +* Default: "~/.npm-init.js" +* Type: Path +* DEPRECATED: Use `--init-module` instead. + +Alias for `--init-module` + +#### `init.version` + +* Default: "1.0.0" +* Type: SemVer string +* DEPRECATED: Use `--init-version` instead. + +Alias for `--init-version` + +#### `only` + +* Default: null +* Type: null, "prod", or "production" +* DEPRECATED: Use `--omit=dev` to omit dev dependencies from the install. + +When set to `prod` or `production`, this is an alias for `--omit=dev`. + +#### `optional` + +* Default: null +* Type: null or Boolean +* DEPRECATED: Use `--omit=optional` to exclude optional dependencies, or + `--include=optional` to include them. + +Default value does install optional deps unless otherwise omitted. + +Alias for --include=optional or --omit=optional + +#### `production` + +* Default: false +* Type: Boolean +* DEPRECATED: Use `--omit=dev` instead. + +Alias for `--omit=dev` + +#### `shrinkwrap` + +* Default: true +* Type: Boolean +* DEPRECATED: Use the --package-lock setting instead. + +Alias for --package-lock + +#### `sso-poll-frequency` + +* Default: 500 +* Type: Number +* DEPRECATED: The --auth-type method of SSO/SAML/OAuth will be removed in a + future version of npm in favor of web-based login. + +When used with SSO-enabled `auth-type`s, configures how regularly the +registry should be polled while the user is completing authentication. + +#### `sso-type` + +* Default: "oauth" +* Type: null, "oauth", or "saml" +* DEPRECATED: The --auth-type method of SSO/SAML/OAuth will be removed in a + future version of npm in favor of web-based login. + +If `--auth-type=sso`, the type of SSO type to use. + +#### `tmp` + +* Default: The value returned by the Node.js `os.tmpdir()` method + <https://nodejs.org/api/os.html#os_os_tmpdir> +* Type: Path +* DEPRECATED: This setting is no longer used. npm stores temporary files in a + special location in the cache, and they are managed by + [`cacache`](http://npm.im/cacache). + +Historically, the location where temporary files were stored. No longer +relevant. + +<!-- AUTOGENERATED CONFIG DESCRIPTIONS END --> + ### See also * [npm config](/commands/npm-config) diff --git a/deps/npm/docs/content/using-npm/workspaces.md b/deps/npm/docs/content/using-npm/workspaces.md index 2024627c758679..28fccd2200c322 100644 --- a/deps/npm/docs/content/using-npm/workspaces.md +++ b/deps/npm/docs/content/using-npm/workspaces.md @@ -88,8 +88,54 @@ This demonstrates how the nature of `node_modules` resolution allows for in such a way that is also easy to [publish](/commands/npm-publish) these nested workspaces to be consumed elsewhere. +### Running commands in the context of workspaces + +You man use the `workspace` configuration option to run commands in the context +of a configured workspace. + +Following is a quick example on how to use the `npm run` command in the context +of nested workspaces. For a project containing multiple workspaces, e.g: + +``` +. ++-- package.json +`-- packages + +-- a + | `-- package.json + `-- b + `-- package.json +``` + +By running a command using the `workspace` option, it's possible to run the +given command in the context of that specific workspace. e.g: + +``` +npm run test --workspace=a +``` + +This will run the `test` script defined within the +`./packages/a/package.json` file. + +Please note that you can also specify this argument multiple times in the +command-line in order to target multiple workspaces, e.g: + +``` +npm run test --workspace=a --workspace=b +``` + +It's also possible to use the `workspaces` (plural) configuration option to +enable the same behavior but running that command in the context of **all** +configured workspaces. e.g: + +``` +npm run test --workspaces +``` + +Will run the `test` script in both `./packages/a` and `./packages/b`. + ### See also * [npm install](/commands/npm-install) * [npm publish](/commands/npm-publish) +* [npm run-script](/commands/npm-run-script) diff --git a/deps/npm/docs/output/commands/npm-adduser.html b/deps/npm/docs/output/commands/npm-adduser.html index 95f0cdaa7112ec..a079dfed71e291 100644 --- a/deps/npm/docs/output/commands/npm-adduser.html +++ b/deps/npm/docs/output/commands/npm-adduser.html @@ -149,6 +149,7 @@ <h2 id="table-of-contents">Table of contents</h2> aliases: login, add-user </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Create or verify a user named <code><username></code> in the specified registry, and save the credentials to the <code>.npmrc</code> file. If no registry is specified, diff --git a/deps/npm/docs/output/commands/npm-bin.html b/deps/npm/docs/output/commands/npm-bin.html index 703c9e9243b358..614552025945a3 100644 --- a/deps/npm/docs/output/commands/npm-bin.html +++ b/deps/npm/docs/output/commands/npm-bin.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm bin [-g|--global] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Print the folder where npm will install executables.</p> <h3 id="see-also">See Also</h3> diff --git a/deps/npm/docs/output/commands/npm-cache.html b/deps/npm/docs/output/commands/npm-cache.html index 9dbf83b31a8ae2..22ca6f1999ba06 100644 --- a/deps/npm/docs/output/commands/npm-cache.html +++ b/deps/npm/docs/output/commands/npm-cache.html @@ -155,6 +155,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm cache verify </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Used to add, list, or clean the npm cache folder.</p> <ul> diff --git a/deps/npm/docs/output/commands/npm-completion.html b/deps/npm/docs/output/commands/npm-completion.html index 4f7016e4ac9933..b99d52b420db9e 100644 --- a/deps/npm/docs/output/commands/npm-completion.html +++ b/deps/npm/docs/output/commands/npm-completion.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>source <(npm completion) </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Enables tab-completion in all npm commands.</p> <p>The synopsis above diff --git a/deps/npm/docs/output/commands/npm-config.html b/deps/npm/docs/output/commands/npm-config.html index 924562822781b1..55f6e5ffe640cd 100644 --- a/deps/npm/docs/output/commands/npm-config.html +++ b/deps/npm/docs/output/commands/npm-config.html @@ -155,6 +155,7 @@ <h2 id="table-of-contents">Table of contents</h2> alias: c </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>npm gets its config settings from the command line, environment variables, <code>npmrc</code> files, and in some cases, the <code>package.json</code> file.</p> diff --git a/deps/npm/docs/output/commands/npm-dedupe.html b/deps/npm/docs/output/commands/npm-dedupe.html index 8a0ec7e0f77feb..ccd9a03f4d828e 100644 --- a/deps/npm/docs/output/commands/npm-dedupe.html +++ b/deps/npm/docs/output/commands/npm-dedupe.html @@ -136,7 +136,7 @@ <section id="content"> <header class="title"> <h1 id="npm-dedupe">npm-dedupe</h1> -<span class="description">Reduce duplication</span> +<span class="description">Reduce duplication in the package tree</span> </header> <section id="table_of_contents"> @@ -148,7 +148,7 @@ <h2 id="table-of-contents">Table of contents</h2> <pre lang="bash"><code>npm dedupe npm ddp -aliases: find-dupes, ddp +aliases: ddp </code></pre> <h3 id="description">Description</h3> <p>Searches the local package tree and attempts to simplify the overall @@ -193,6 +193,7 @@ <h3 id="description">Description</h3> <p>Using <code>npm find-dupes</code> will run the command in <code>--dry-run</code> mode.</p> <h3 id="see-also">See Also</h3> <ul> +<li><a href="../cli-commands/find-dupes.html">npm find-dupes</a></li> <li><a href="../cli-commands/ls.html">npm ls</a></li> <li><a href="../cli-commands/update.html">npm update</a></li> <li><a href="../cli-commands/install.html">npm install</a></li> diff --git a/deps/npm/docs/output/commands/npm-deprecate.html b/deps/npm/docs/output/commands/npm-deprecate.html index 949be43d9be171..88299a80bcfdaf 100644 --- a/deps/npm/docs/output/commands/npm-deprecate.html +++ b/deps/npm/docs/output/commands/npm-deprecate.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm deprecate <pkg>[@<version range>] <message> </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>This command will update the npm registry entry for a package, providing a deprecation warning to all who attempt to install it.</p> diff --git a/deps/npm/docs/output/commands/npm-doctor.html b/deps/npm/docs/output/commands/npm-doctor.html index a9b2001543d7dc..9ae140575529ef 100644 --- a/deps/npm/docs/output/commands/npm-doctor.html +++ b/deps/npm/docs/output/commands/npm-doctor.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm doctor </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p><code>npm doctor</code> runs a set of checks to ensure that your npm installation has what it needs to manage your JavaScript packages. npm is mostly a diff --git a/deps/npm/docs/output/commands/npm-edit.html b/deps/npm/docs/output/commands/npm-edit.html index 7ebb443a2f6bb4..8c4ee5ed1a762f 100644 --- a/deps/npm/docs/output/commands/npm-edit.html +++ b/deps/npm/docs/output/commands/npm-edit.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm edit <pkg> </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Selects a dependency in the current project and opens the package folder in the default editor (or whatever you’ve configured as the npm <code>editor</code> diff --git a/deps/npm/docs/output/commands/npm-exec.html b/deps/npm/docs/output/commands/npm-exec.html index 03059c52052a50..f8f882a2c54a48 100644 --- a/deps/npm/docs/output/commands/npm-exec.html +++ b/deps/npm/docs/output/commands/npm-exec.html @@ -141,7 +141,7 @@ <h1 id="npm-exec">npm-exec</h1> <section id="table_of_contents"> <h2 id="table-of-contents">Table of contents</h2> -<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#npx-vs-npm-exec"><code>npx</code> vs <code>npm exec</code></a></li><li><a href="#examples">Examples</a></li><li><a href="#compatibility-with-older-npx-versions">Compatibility with Older npx Versions</a></li><li><a href="#a-note-on-caching">A note on caching</a></li><ul><li><a href="#prefer-online">prefer-online</a></li><li><a href="#prefer-offline">prefer-offline</a></li><li><a href="#offline">offline</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div> +<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#npx-vs-npm-exec"><code>npx</code> vs <code>npm exec</code></a></li><li><a href="#examples">Examples</a></li><li><a href="#workspaces-support">Workspaces support</a></li><ul><li><a href="#filtering-workspaces">Filtering workspaces</a></li></ul><li><a href="#compatibility-with-older-npx-versions">Compatibility with Older npx Versions</a></li><li><a href="#a-note-on-caching">A note on caching</a></li><ul><li><a href="#prefer-online">prefer-online</a></li><li><a href="#prefer-offline">prefer-offline</a></li><li><a href="#offline">offline</a></li><li><a href="#workspace">workspace</a></li><li><a href="#workspaces">workspaces</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div> </section> <div id="_content"><h3 id="synopsis">Synopsis</h3> @@ -149,6 +149,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm exec --package=<pkg>[@<version>] -- <cmd> [args...] npm exec -c '<cmd> [args...]' npm exec --package=foo -c '<cmd> [args...]' +npm exec [-ws] [-w <workspace-name] [args...] npx <pkg>[@<specifier>] [args...] npx -p <pkg>[@<specifier>] <cmd> [args...] @@ -247,6 +248,49 @@ <h3 id="examples">Examples</h3> <pre><code>$ npm x -c 'eslint && say "hooray, lint passed"' $ npx -c 'eslint && say "hooray, lint passed"' </code></pre> +<h3 id="workspaces-support">Workspaces support</h3> +<p>You may use the <code>workspace</code> or <code>workspaces</code> configs in order to run an +arbitrary command from an npm package (either one installed locally, or fetched +remotely) in the context of the specified workspaces. +If no positional argument or <code>--call</code> option is provided, it will open an +interactive subshell in the context of each of these configured workspaces one +at a time.</p> +<p>Given a project with configured workspaces, e.g:</p> +<pre><code>. ++-- package.json +`-- packages + +-- a + | `-- package.json + +-- b + | `-- package.json + `-- c + `-- package.json +</code></pre> +<p>Assuming the workspace configuration is properly set up at the root level +<code>package.json</code> file. e.g:</p> +<pre><code>{ + "workspaces": [ "./packages/*" ] +} +</code></pre> +<p>You can execute an arbitrary command from a package in the context of each of +the configured workspaces when using the <code>workspaces</code> configuration options, +in this example we’re using <strong>eslint</strong> to lint any js file found within each +workspace folder:</p> +<pre><code>npm exec -ws -- eslint ./*.js +</code></pre> +<h4 id="filtering-workspaces">Filtering workspaces</h4> +<p>It’s also possible to execute a command in a single workspace using the +<code>workspace</code> config along with a name or directory path:</p> +<pre><code>npm exec --workspace=a -- eslint ./*.js +</code></pre> +<p>The <code>workspace</code> config can also be specified multiple times in order to run a +specific script in the context of multiple workspaces. When defining values for +the <code>workspace</code> config in the command line, it also possible to use <code>-w</code> as a +shorthand, e.g:</p> +<pre><code>npm exec -w a -w b -- eslint ./*.js +</code></pre> +<p>This last command will run the <code>eslint</code> command in both <code>./packages/a</code> and +<code>./packages/b</code> folders.</p> <h3 id="compatibility-with-older-npx-versions">Compatibility with Older npx Versions</h3> <p>The <code>npx</code> binary was rewritten in npm v7.0.0, and the standalone <code>npx</code> package deprecated at that time. <code>npx</code> uses the <code>npm exec</code> @@ -287,6 +331,29 @@ <h4 id="prefer-offline">prefer-offline</h4> <h4 id="offline">offline</h4> <p>Forces full offline mode. Any packages not locally cached will result in an error.</p> +<h4 id="workspace">workspace</h4> +<ul> +<li>Alias: <code>-w</code></li> +<li>Type: Array</li> +<li>Default: <code>[]</code></li> +</ul> +<p>Enable running scripts in the context of workspaces while also filtering by +the provided names or paths provided.</p> +<p>Valid values for the <code>workspace</code> config are either:</p> +<ul> +<li>Workspace names</li> +<li>Path to a workspace directory</li> +<li>Path to a parent workspace directory (will result to selecting all of the +children workspaces)</li> +</ul> +<h4 id="workspaces">workspaces</h4> +<ul> +<li>Alias: <code>-ws</code></li> +<li>Type: Boolean</li> +<li>Default: <code>false</code></li> +</ul> +<p>Run scripts in the context of all configured workspaces for the current +project.</p> <h3 id="see-also">See Also</h3> <ul> <li><a href="../commands/npm-run-script.html">npm run-script</a></li> diff --git a/deps/npm/docs/output/commands/npm-explore.html b/deps/npm/docs/output/commands/npm-explore.html index 0bb350502264f2..cd52ff96a68486 100644 --- a/deps/npm/docs/output/commands/npm-explore.html +++ b/deps/npm/docs/output/commands/npm-explore.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm explore <pkg> [ -- <command>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Spawn a subshell in the directory of the installed package specified.</p> <p>If a command is specified, then it is run in the subshell, which then diff --git a/deps/npm/docs/output/commands/npm-find-dupes.html b/deps/npm/docs/output/commands/npm-find-dupes.html new file mode 100644 index 00000000000000..32d8993b99cc4d --- /dev/null +++ b/deps/npm/docs/output/commands/npm-find-dupes.html @@ -0,0 +1,174 @@ +<html><head> +<title>npm-find-dupes</title> +<style> +body { + background-color: #ffffff; + color: #24292e; + + margin: 0; + + line-height: 1.5; + + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; +} +#rainbar { + height: 10px; + background-image: linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff); +} + +a { + text-decoration: none; + color: #0366d6; +} +a:hover { + text-decoration: underline; +} + +pre { + margin: 1em 0px; + padding: 1em; + border: solid 1px #e1e4e8; + border-radius: 6px; + + display: block; + overflow: auto; + + white-space: pre; + + background-color: #f6f8fa; + color: #393a34; +} +code { + font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 85%; + padding: 0.2em 0.4em; + background-color: #f6f8fa; + color: #393a34; +} +pre > code { + padding: 0; + background-color: inherit; + color: inherit; +} +h1, h2, h3 { + font-weight: 600; +} + +#logobar { + background-color: #333333; + margin: 0 auto; + padding: 1em 4em; +} +#logobar .logo { + float: left; +} +#logobar .title { + font-weight: 600; + color: #dddddd; + float: left; + margin: 5px 0 0 1em; +} +#logobar:after { + content: ""; + display: block; + clear: both; +} + +#content { + margin: 0 auto; + padding: 0 4em; +} + +#table_of_contents > h2 { + font-size: 1.17em; +} +#table_of_contents ul:first-child { + border: solid 1px #e1e4e8; + border-radius: 6px; + padding: 1em; + background-color: #f6f8fa; + color: #393a34; +} +#table_of_contents ul { + list-style-type: none; + padding-left: 1.5em; +} +#table_of_contents li { + font-size: 0.9em; +} +#table_of_contents li a { + color: #000000; +} + +header.title { + border-bottom: solid 1px #e1e4e8; +} +header.title > h1 { + margin-bottom: 0.25em; +} +header.title > .description { + display: block; + margin-bottom: 0.5em; + line-height: 1; +} + +footer#edit { + border-top: solid 1px #e1e4e8; + margin: 3em 0 4em 0; + padding-top: 2em; +} +</style> +</head> +<body> +<div id="banner"> +<div id="rainbar"></div> +<div id="logobar"> +<svg class="logo" role="img" height="32" width="32" viewBox="0 0 700 700"> +<polygon fill="#cb0000" points="0,700 700,700 700,0 0,0"></polygon> +<polygon fill="#ffffff" points="150,550 350,550 350,250 450,250 450,550 550,550 550,150 150,150"></polygon> +</svg> +<div class="title"> +npm command-line interface +</div> +</div> +</div> + +<section id="content"> +<header class="title"> +<h1 id="npm-find-dupes">npm-find-dupes</h1> +<span class="description">Find duplication in the package tree</span> +</header> + +<section id="table_of_contents"> +<h2 id="table-of-contents">Table of contents</h2> +<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#see-also">See Also</a></li></ul></div> +</section> + +<div id="_content"><h3 id="synopsis">Synopsis</h3> +<pre lang="bash"><code>npm find-dupes +</code></pre> +<h3 id="description">Description</h3> +<p>Runs <code>npm dedupe</code> in <code>--dry-run</code> mode, making npm only output the +duplications, without actually changing the package tree.</p> +<h3 id="see-also">See Also</h3> +<ul> +<li><a href="../cli-commands/dedupe.html">npm dedupe</a></li> +<li><a href="../cli-commands/ls.html">npm ls</a></li> +<li><a href="../cli-commands/update.html">npm update</a></li> +<li><a href="../cli-commands/install.html">npm install</a></li> +</ul> +</div> + +<footer id="edit"> +<a href="https://github.com/npm/cli/edit/latest/docs/content/commands/npm-find-dupes.md"> +<svg role="img" viewBox="0 0 16 16" width="16" height="16" fill="currentcolor" style="vertical-align: text-bottom; margin-right: 0.3em;"> +<path fill-rule="evenodd" d="M11.013 1.427a1.75 1.75 0 012.474 0l1.086 1.086a1.75 1.75 0 010 2.474l-8.61 8.61c-.21.21-.47.364-.756.445l-3.251.93a.75.75 0 01-.927-.928l.929-3.25a1.75 1.75 0 01.445-.758l8.61-8.61zm1.414 1.06a.25.25 0 00-.354 0L10.811 3.75l1.439 1.44 1.263-1.263a.25.25 0 000-.354l-1.086-1.086zM11.189 6.25L9.75 4.81l-6.286 6.287a.25.25 0 00-.064.108l-.558 1.953 1.953-.558a.249.249 0 00.108-.064l6.286-6.286z"></path> +</svg> +Edit this page on GitHub +</a> +</footer> +</section> + + + +</body></html> \ No newline at end of file diff --git a/deps/npm/docs/output/commands/npm-help-search.html b/deps/npm/docs/output/commands/npm-help-search.html index 00ebe9bf6ca919..77456ef5493294 100644 --- a/deps/npm/docs/output/commands/npm-help-search.html +++ b/deps/npm/docs/output/commands/npm-help-search.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm help-search <text> </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>This command will search the npm markdown documentation files for the terms provided, and then list the results, sorted by relevance.</p> diff --git a/deps/npm/docs/output/commands/npm-help.html b/deps/npm/docs/output/commands/npm-help.html index e4f4226a5d23b2..0327d1788f12d8 100644 --- a/deps/npm/docs/output/commands/npm-help.html +++ b/deps/npm/docs/output/commands/npm-help.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm help <term> [<terms..>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>If supplied a topic, then show the appropriate documentation page.</p> <p>If the topic does not exist, or if multiple terms are provided, then npm diff --git a/deps/npm/docs/output/commands/npm-hook.html b/deps/npm/docs/output/commands/npm-hook.html index dd7015df1b425d..2921e7e12c0361 100644 --- a/deps/npm/docs/output/commands/npm-hook.html +++ b/deps/npm/docs/output/commands/npm-hook.html @@ -150,6 +150,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm hook update <id> <url> [secret] npm hook rm <id> </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Allows you to manage <a href="https://blog.npmjs.org/post/145260155635/introducing-hooks-get-notifications-of-npm">npm hooks</a>, diff --git a/deps/npm/docs/output/commands/npm-init.html b/deps/npm/docs/output/commands/npm-init.html index 3abf8e967b4649..62dc20093b2e94 100644 --- a/deps/npm/docs/output/commands/npm-init.html +++ b/deps/npm/docs/output/commands/npm-init.html @@ -136,7 +136,7 @@ <section id="content"> <header class="title"> <h1 id="npm-init">npm-init</h1> -<span class="description">create a package.json file</span> +<span class="description">Create a package.json file</span> </header> <section id="table_of_contents"> diff --git a/deps/npm/docs/output/commands/npm-logout.html b/deps/npm/docs/output/commands/npm-logout.html index 02de412c7310e0..4e19888ee84d25 100644 --- a/deps/npm/docs/output/commands/npm-logout.html +++ b/deps/npm/docs/output/commands/npm-logout.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm logout [--registry=<url>] [--scope=<@scope>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>When logged into a registry that supports token-based authentication, tell the server to end this token’s session. This will invalidate the token diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index ae1e1a84c72a4f..454c5567442657 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -159,7 +159,7 @@ <h3 id="description">Description</h3> the results to only the paths to the packages named. Note that nested packages will <em>also</em> show the paths to the specified packages. For example, running <code>npm ls promzard</code> in npm’s source tree will show:</p> -<pre lang="bash"><code>npm@7.6.3 /path/to/npm +<pre lang="bash"><code>npm@7.7.0 /path/to/npm └─┬ init-package-json@0.0.4 └── promzard@0.1.5 </code></pre> diff --git a/deps/npm/docs/output/commands/npm-org.html b/deps/npm/docs/output/commands/npm-org.html index d97f638b22d0b8..50c0995650557c 100644 --- a/deps/npm/docs/output/commands/npm-org.html +++ b/deps/npm/docs/output/commands/npm-org.html @@ -149,6 +149,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm org rm <orgname> <username> npm org ls <orgname> [<username>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="example">Example</h3> <p>Add a new developer to an org:</p> <pre lang="bash"><code>$ npm org set my-org @mx-smith diff --git a/deps/npm/docs/output/commands/npm-owner.html b/deps/npm/docs/output/commands/npm-owner.html index 77f072da40b2ee..ada60ea0951e25 100644 --- a/deps/npm/docs/output/commands/npm-owner.html +++ b/deps/npm/docs/output/commands/npm-owner.html @@ -151,6 +151,7 @@ <h2 id="table-of-contents">Table of contents</h2> aliases: author </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Manage ownership of published packages.</p> <ul> diff --git a/deps/npm/docs/output/commands/npm-ping.html b/deps/npm/docs/output/commands/npm-ping.html index e00fb30a01c351..ca124a18595da1 100644 --- a/deps/npm/docs/output/commands/npm-ping.html +++ b/deps/npm/docs/output/commands/npm-ping.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm ping [--registry <registry>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Ping the configured or given npm registry and verify authentication. If it works it will output something like:</p> diff --git a/deps/npm/docs/output/commands/npm-prefix.html b/deps/npm/docs/output/commands/npm-prefix.html index a330dfd1c373cc..493a1f8950b59b 100644 --- a/deps/npm/docs/output/commands/npm-prefix.html +++ b/deps/npm/docs/output/commands/npm-prefix.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm prefix [-g] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Print the local prefix to standard output. This is the closest parent directory to contain a <code>package.json</code> file or <code>node_modules</code> directory, unless <code>-g</code> is diff --git a/deps/npm/docs/output/commands/npm-profile.html b/deps/npm/docs/output/commands/npm-profile.html index 91013567a206e1..f103446479c10e 100644 --- a/deps/npm/docs/output/commands/npm-profile.html +++ b/deps/npm/docs/output/commands/npm-profile.html @@ -151,6 +151,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm profile enable-2fa [auth-and-writes|auth-only] npm profile disable-2fa </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Change your profile information on the registry. Note that this command depends on the registry implementation, so third-party registries may not diff --git a/deps/npm/docs/output/commands/npm-run-script.html b/deps/npm/docs/output/commands/npm-run-script.html index a78a434760bc05..6fc343dfbaf5f9 100644 --- a/deps/npm/docs/output/commands/npm-run-script.html +++ b/deps/npm/docs/output/commands/npm-run-script.html @@ -141,11 +141,13 @@ <h1 id="npm-run-script">npm-run-script</h1> <section id="table_of_contents"> <h2 id="table-of-contents">Table of contents</h2> -<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#configuration">Configuration</a></li><ul><li><a href="#if-present">if-present</a></li><li><a href="#ignore-scripts">ignore-scripts</a></li><li><a href="#script-shell">script-shell</a></li><li><a href="#silent">silent</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div> +<div id="_table_of_contents"><ul><li><a href="#synopsis">Synopsis</a></li><li><a href="#description">Description</a></li><li><a href="#workspaces-support">Workspaces support</a></li><ul><li><a href="#filtering-workspaces">Filtering workspaces</a></li></ul><li><a href="#configuration">Configuration</a></li><ul><li><a href="#if-present">if-present</a></li><li><a href="#ignore-scripts">ignore-scripts</a></li><li><a href="#script-shell">script-shell</a></li><li><a href="#silent">silent</a></li><li><a href="#workspace">workspace</a></li><li><a href="#workspaces">workspaces</a></li></ul><li><a href="#see-also">See Also</a></li></ul></div> </section> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm run-script <command> [--if-present] [--silent] [-- <args>] +npm run-script <command> [--workspace=<workspace-name>] +npm run-script <command> [--workspaces] aliases: run, rum, urn </code></pre> @@ -196,6 +198,45 @@ <h3 id="description">Description</h3> <p>If you try to run a script without having a <code>node_modules</code> directory and it fails, you will be given a warning to run <code>npm install</code>, just in case you’ve forgotten.</p> +<h3 id="workspaces-support">Workspaces support</h3> +<p>You may use the <code>workspace</code> or <code>workspaces</code> configs in order to run an +arbitrary command from a package’s <code>"scripts"</code> object in the context of the +specified workspaces. If no <code>"command"</code> is provided, it will list the available +scripts for each of these configured workspaces.</p> +<p>Given a project with configured workspaces, e.g:</p> +<pre><code>. ++-- package.json +`-- packages + +-- a + | `-- package.json + +-- b + | `-- package.json + `-- c + `-- package.json +</code></pre> +<p>Assuming the workspace configuration is properly set up at the root level +<code>package.json</code> file. e.g:</p> +<pre><code>{ + "workspaces": [ "./packages/*" ] +} +</code></pre> +<p>And that each of the configured workspaces has a configured <code>test</code> script, +we can run tests in all of them using the <code>workspaces</code> config:</p> +<pre><code>npm test --workspaces +</code></pre> +<h4 id="filtering-workspaces">Filtering workspaces</h4> +<p>It’s also possible to run a script in a single workspace using the <code>workspace</code> +config along with a name or directory path:</p> +<pre><code>npm test --workspace=a +</code></pre> +<p>The <code>workspace</code> config can also be specified multiple times in order to run a +specific script in the context of multiple workspaces. When defining values for +the <code>workspace</code> config in the command line, it also possible to use <code>-w</code> as a +shorthand, e.g:</p> +<pre><code>npm test -w a -w b +</code></pre> +<p>This last command will run <code>test</code> in both <code>./packages/a</code> and <code>./packages/b</code> +packages.</p> <h3 id="configuration">Configuration</h3> <h4 id="if-present">if-present</h4> <ul> @@ -224,6 +265,29 @@ <h4 id="silent">silent</h4> <li>Default: false</li> </ul> <p>You can use the <code>--silent</code> flag to prevent showing <code>npm ERR!</code> output on error.</p> +<h4 id="workspace">workspace</h4> +<ul> +<li>Alias: <code>-w</code></li> +<li>Type: Array</li> +<li>Default: <code>[]</code></li> +</ul> +<p>Enable running scripts in the context of workspaces while also filtering by +the provided names or paths provided.</p> +<p>Valid values for the <code>workspace</code> config are either:</p> +<ul> +<li>Workspace names</li> +<li>Path to a workspace directory</li> +<li>Path to a parent workspace directory (will result to selecting all of the +children workspaces)</li> +</ul> +<h4 id="workspaces">workspaces</h4> +<ul> +<li>Alias: <code>-ws</code></li> +<li>Type: Boolean</li> +<li>Default: <code>false</code></li> +</ul> +<p>Run scripts in the context of all configured workspaces for the current +project.</p> <h3 id="see-also">See Also</h3> <ul> <li><a href="../using-npm/scripts.html">npm scripts</a></li> diff --git a/deps/npm/docs/output/commands/npm-search.html b/deps/npm/docs/output/commands/npm-search.html index bd1522305035ec..974c163ae16e9d 100644 --- a/deps/npm/docs/output/commands/npm-search.html +++ b/deps/npm/docs/output/commands/npm-search.html @@ -149,6 +149,7 @@ <h2 id="table-of-contents">Table of contents</h2> aliases: s, se, find </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Search the registry for packages matching the search terms. <code>npm search</code> performs a linear, incremental, lexically-ordered search through package diff --git a/deps/npm/docs/output/commands/npm-shrinkwrap.html b/deps/npm/docs/output/commands/npm-shrinkwrap.html index 63057cbe952b17..e2c09e81762c8d 100644 --- a/deps/npm/docs/output/commands/npm-shrinkwrap.html +++ b/deps/npm/docs/output/commands/npm-shrinkwrap.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm shrinkwrap </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>This command repurposes <code>package-lock.json</code> into a publishable <code>npm-shrinkwrap.json</code> or simply creates a new one. The file created and diff --git a/deps/npm/docs/output/commands/npm-star.html b/deps/npm/docs/output/commands/npm-star.html index 1f92bc2697d437..8589a50c026be7 100644 --- a/deps/npm/docs/output/commands/npm-star.html +++ b/deps/npm/docs/output/commands/npm-star.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm star [<pkg>...] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>“Starring” a package means that you have some interest in it. It’s a vaguely positive way to show that you care.</p> diff --git a/deps/npm/docs/output/commands/npm-stars.html b/deps/npm/docs/output/commands/npm-stars.html index fb80fc0cdee12f..c4aa2a194c4ec5 100644 --- a/deps/npm/docs/output/commands/npm-stars.html +++ b/deps/npm/docs/output/commands/npm-stars.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm stars [<user>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>If you have starred a lot of neat things and want to find them again quickly this command lets you do just that.</p> diff --git a/deps/npm/docs/output/commands/npm-team.html b/deps/npm/docs/output/commands/npm-team.html index bf29f4a5eaf633..586fd52721efb7 100644 --- a/deps/npm/docs/output/commands/npm-team.html +++ b/deps/npm/docs/output/commands/npm-team.html @@ -153,6 +153,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm team ls <scope>|<scope:team> </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Used to manage teams in organizations, and change team memberships. Does not handle permissions for packages.</p> diff --git a/deps/npm/docs/output/commands/npm-token.html b/deps/npm/docs/output/commands/npm-token.html index de92432475e248..d913acdb54789c 100644 --- a/deps/npm/docs/output/commands/npm-token.html +++ b/deps/npm/docs/output/commands/npm-token.html @@ -149,6 +149,7 @@ <h2 id="table-of-contents">Table of contents</h2> npm token create [--read-only] [--cidr=1.1.1.1/24,2.2.2.2/16] npm token revoke <id|token> </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>This lets you list, create and revoke authentication tokens.</p> <ul> diff --git a/deps/npm/docs/output/commands/npm-unstar.html b/deps/npm/docs/output/commands/npm-unstar.html index 77c7cd3495c4b1..3da3d28cbba798 100644 --- a/deps/npm/docs/output/commands/npm-unstar.html +++ b/deps/npm/docs/output/commands/npm-unstar.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm unstar [<pkg>...] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>“Unstarring” a package is the opposite of <a href="../commands/npm-star.html"><code>npm star</code></a>, it removes an item from your list of favorite packages.</p> diff --git a/deps/npm/docs/output/commands/npm-whoami.html b/deps/npm/docs/output/commands/npm-whoami.html index e703e1665f996c..869e1351bcc6f7 100644 --- a/deps/npm/docs/output/commands/npm-whoami.html +++ b/deps/npm/docs/output/commands/npm-whoami.html @@ -147,6 +147,7 @@ <h2 id="table-of-contents">Table of contents</h2> <div id="_content"><h3 id="synopsis">Synopsis</h3> <pre lang="bash"><code>npm whoami [--registry <registry>] </code></pre> +<p>Note: This command is unaware of workspaces.</p> <h3 id="description">Description</h3> <p>Print the <code>username</code> config to standard output.</p> <h3 id="see-also">See Also</h3> diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index c2d07cf5caf1f8..f55659d9ee82a1 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -148,7 +148,7 @@ <h2 id="table-of-contents">Table of contents</h2> <pre lang="bash"><code>npm <command> [args] </code></pre> <h3 id="version">Version</h3> -<p>7.6.3</p> +<p>7.7.0</p> <h3 id="description">Description</h3> <p>npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency diff --git a/deps/npm/docs/output/configuring-npm/package-json.html b/deps/npm/docs/output/configuring-npm/package-json.html index 114c18d61ebb25..9c58ebf85f6a59 100644 --- a/deps/npm/docs/output/configuring-npm/package-json.html +++ b/deps/npm/docs/output/configuring-npm/package-json.html @@ -392,6 +392,7 @@ <h3 id="main">main</h3> <p>This should be a module relative to the root of your package folder.</p> <p>For most modules, it makes the most sense to have a main script and often not much else.</p> +<p>If <code>main</code> is not set it defaults to <code>index.js</code> in the packages root folder.</p> <h3 id="browser">browser</h3> <p>If your module is meant to be used client-side the browser field should be used instead of the main field. This is helpful to hint users that it might @@ -565,8 +566,7 @@ <h3 id="dependencies">dependencies</h3> tarball or git URL.</p> <p><strong>Please do not put test harnesses or transpilers or other “development” time tools in your <code>dependencies</code> object.</strong> See <code>devDependencies</code>, below.</p> -<p>See <a href="%5B/using-npm/semver%5D(https://github.com/npm/node-semver#versions)">semver</a> -for more details about specifying version ranges.</p> +<p>See <a href="../using-npm/semver#versions.html">semver</a> for more details about specifying version ranges.</p> <ul> <li><code>version</code> Must match <code>version</code> exactly</li> <li><code>>version</code> Must be greater than <code>version</code></li> diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html index 9d6ba9b5a6c3c1..dc50eb72b71ca4 100644 --- a/deps/npm/docs/output/using-npm/config.html +++ b/deps/npm/docs/output/using-npm/config.html @@ -141,7 +141,7 @@ <h1 id="config">config</h1> <section id="table_of_contents"> <h2 id="table-of-contents">Table of contents</h2> -<div id="_table_of_contents"><ul><li><a href="#description">Description</a></li><ul><li><a href="#command-line-flags">Command Line Flags</a></li><li><a href="#environment-variables">Environment Variables</a></li><li><a href="#npmrc-files">npmrc Files</a></li><li><a href="#default-configs">Default Configs</a></li></ul><li><a href="#shorthands-and-other-cli-niceties">Shorthands and Other CLI Niceties</a></li><li><a href="#config-settings">Config Settings</a></li><ul><li><a href="#access">access</a></li><li><a href="#all">all</a></li><li><a href="#allow-same-version">allow-same-version</a></li><li><a href="#always-auth">always-auth</a></li><li><a href="#also">also</a></li><li><a href="#audit">audit</a></li><li><a href="#audit-level">audit-level</a></li><li><a href="#auth-type">auth-type</a></li><li><a href="#before">before</a></li><li><a href="#bin-links">bin-links</a></li><li><a href="#browser">browser</a></li><li><a href="#ca">ca</a></li><li><a href="#cafile">cafile</a></li><li><a href="#cache">cache</a></li><li><a href="#cache-lock-stale">cache-lock-stale</a></li><li><a href="#cache-lock-retries">cache-lock-retries</a></li><li><a href="#cache-lock-wait">cache-lock-wait</a></li><li><a href="#cache-max">cache-max</a></li><li><a href="#cache-min">cache-min</a></li><li><a href="#call">call</a></li><li><a href="#cert">cert</a></li><li><a href="#cidr">cidr</a></li><li><a href="#commit-hooks">commit-hooks</a></li><li><a href="#color">color</a></li><li><a href="#depth">depth</a></li><li><a href="#description2">description</a></li><li><a href="#dev">dev</a></li><li><a href="#dry-run">dry-run</a></li><li><a href="#diff">diff</a></li><li><a href="#diff-name-only">diff-name-only</a></li><li><a href="#diff-unified">diff-unified</a></li><li><a href="#diff-ignore-all-space">diff-ignore-all-space</a></li><li><a href="#diff-no-prefix">diff-no-prefix</a></li><li><a href="#diff-src-prefix">diff-src-prefix</a></li><li><a href="#diff-dst-prefix">diff-dst-prefix</a></li><li><a href="#diff-text">diff-text</a></li><li><a href="#editor">editor</a></li><li><a href="#engine-strict">engine-strict</a></li><li><a href="#force">force</a></li><li><a href="#foreground-scripts">foreground-scripts</a></li><li><a href="#format-package-lock">format-package-lock</a></li><li><a href="#fund">fund</a></li><li><a href="#fetch-retries">fetch-retries</a></li><li><a href="#fetch-retry-factor">fetch-retry-factor</a></li><li><a href="#fetch-retry-mintimeout">fetch-retry-mintimeout</a></li><li><a href="#fetch-retry-maxtimeout">fetch-retry-maxtimeout</a></li><li><a href="#fetch-timeout">fetch-timeout</a></li><li><a href="#git">git</a></li><li><a href="#git-tag-version">git-tag-version</a></li><li><a href="#global">global</a></li><li><a href="#globalconfig">globalconfig</a></li><li><a href="#global-style">global-style</a></li><li><a href="#heading">heading</a></li><li><a href="#https-proxy">https-proxy</a></li><li><a href="#if-present">if-present</a></li><li><a href="#ignore-prepublish">ignore-prepublish</a></li><li><a href="#ignore-scripts">ignore-scripts</a></li><li><a href="#include">include</a></li><li><a href="#init-module">init-module</a></li><li><a href="#init-author-name">init-author-name</a></li><li><a href="#init-author-email">init-author-email</a></li><li><a href="#init-author-url">init-author-url</a></li><li><a href="#init-license">init-license</a></li><li><a href="#init-version">init-version</a></li><li><a href="#json">json</a></li><li><a href="#key">key</a></li><li><a href="#legacy-bundling">legacy-bundling</a></li><li><a href="#legacy-peer-deps">legacy-peer-deps</a></li><li><a href="#link">link</a></li><li><a href="#local-address">local-address</a></li><li><a href="#loglevel">loglevel</a></li><li><a href="#logs-max">logs-max</a></li><li><a href="#long">long</a></li><li><a href="#maxsockets">maxsockets</a></li><li><a href="#message">message</a></li><li><a href="#node-options">node-options</a></li><li><a href="#node-version">node-version</a></li><li><a href="#noproxy">noproxy</a></li><li><a href="#offline">offline</a></li><li><a href="#only">only</a></li><li><a href="#optional">optional</a></li><li><a href="#otp">otp</a></li><li><a href="#package-lock">package-lock</a></li><li><a href="#package-lock-only">package-lock-only</a></li><li><a href="#parseable">parseable</a></li><li><a href="#prefer-offline">prefer-offline</a></li><li><a href="#prefer-online">prefer-online</a></li><li><a href="#prefix">prefix</a></li><li><a href="#preid">preid</a></li><li><a href="#production">production</a></li><li><a href="#progress">progress</a></li><li><a href="#proxy">proxy</a></li><li><a href="#read-only">read-only</a></li><li><a href="#rebuild-bundle">rebuild-bundle</a></li><li><a href="#registry">registry</a></li><li><a href="#rollback">rollback</a></li><li><a href="#save">save</a></li><li><a href="#save-bundle">save-bundle</a></li><li><a href="#save-prod">save-prod</a></li><li><a href="#save-dev">save-dev</a></li><li><a href="#save-exact">save-exact</a></li><li><a href="#save-optional">save-optional</a></li><li><a href="#save-prefix">save-prefix</a></li><li><a href="#scope">scope</a></li><li><a href="#script-shell">script-shell</a></li><li><a href="#scripts-prepend-node-path">scripts-prepend-node-path</a></li><li><a href="#searchexclude">searchexclude</a></li><li><a href="#searchopts">searchopts</a></li><li><a href="#searchlimit">searchlimit</a></li><li><a href="#searchstaleness">searchstaleness</a></li><li><a href="#shell">shell</a></li><li><a href="#shrinkwrap">shrinkwrap</a></li><li><a href="#sign-git-commit">sign-git-commit</a></li><li><a href="#sign-git-tag">sign-git-tag</a></li><li><a href="#sso-poll-frequency">sso-poll-frequency</a></li><li><a href="#sso-type">sso-type</a></li><li><a href="#strict-peer-deps">strict-peer-deps</a></li><li><a href="#strict-ssl">strict-ssl</a></li><li><a href="#tag">tag</a></li><li><a href="#tag-version-prefix">tag-version-prefix</a></li><li><a href="#timing">timing</a></li><li><a href="#tmp">tmp</a></li><li><a href="#unicode">unicode</a></li><li><a href="#update-notifier">update-notifier</a></li><li><a href="#usage">usage</a></li><li><a href="#userconfig">userconfig</a></li><li><a href="#umask">umask</a></li><li><a href="#user-agent">user-agent</a></li><li><a href="#version">version</a></li><li><a href="#versions">versions</a></li><li><a href="#viewer">viewer</a></li></ul><li><a href="#see-also">See also</a></li></ul></div> +<div id="_table_of_contents"><ul><li><a href="#description">Description</a></li><ul><li><a href="#command-line-flags">Command Line Flags</a></li><li><a href="#environment-variables">Environment Variables</a></li><li><a href="#npmrc-files">npmrc Files</a></li><li><a href="#default-configs">Default Configs</a></li></ul><li><a href="#shorthands-and-other-cli-niceties">Shorthands and Other CLI Niceties</a></li><li><a href="#config-settings">Config Settings</a></li><ul><li><a href="#auth"><code>_auth</code></a></li><li><a href="#access"><code>access</code></a></li><li><a href="#all"><code>all</code></a></li><li><a href="#allow-same-version"><code>allow-same-version</code></a></li><li><a href="#always-auth"><code>always-auth</code></a></li><li><a href="#audit"><code>audit</code></a></li><li><a href="#audit-level"><code>audit-level</code></a></li><li><a href="#before"><code>before</code></a></li><li><a href="#bin-links"><code>bin-links</code></a></li><li><a href="#browser"><code>browser</code></a></li><li><a href="#ca"><code>ca</code></a></li><li><a href="#cache"><code>cache</code></a></li><li><a href="#cafile"><code>cafile</code></a></li><li><a href="#call"><code>call</code></a></li><li><a href="#cert"><code>cert</code></a></li><li><a href="#ci-name"><code>ci-name</code></a></li><li><a href="#cidr"><code>cidr</code></a></li><li><a href="#color"><code>color</code></a></li><li><a href="#commit-hooks"><code>commit-hooks</code></a></li><li><a href="#depth"><code>depth</code></a></li><li><a href="#description2"><code>description</code></a></li><li><a href="#diff"><code>diff</code></a></li><li><a href="#diff-dst-prefix"><code>diff-dst-prefix</code></a></li><li><a href="#diff-ignore-all-space"><code>diff-ignore-all-space</code></a></li><li><a href="#diff-name-only"><code>diff-name-only</code></a></li><li><a href="#diff-no-prefix"><code>diff-no-prefix</code></a></li><li><a href="#diff-src-prefix"><code>diff-src-prefix</code></a></li><li><a href="#diff-text"><code>diff-text</code></a></li><li><a href="#diff-unified"><code>diff-unified</code></a></li><li><a href="#dry-run"><code>dry-run</code></a></li><li><a href="#editor"><code>editor</code></a></li><li><a href="#engine-strict"><code>engine-strict</code></a></li><li><a href="#fetch-retries"><code>fetch-retries</code></a></li><li><a href="#fetch-retry-factor"><code>fetch-retry-factor</code></a></li><li><a href="#fetch-retry-maxtimeout"><code>fetch-retry-maxtimeout</code></a></li><li><a href="#fetch-retry-mintimeout"><code>fetch-retry-mintimeout</code></a></li><li><a href="#fetch-timeout"><code>fetch-timeout</code></a></li><li><a href="#force"><code>force</code></a></li><li><a href="#foreground-scripts"><code>foreground-scripts</code></a></li><li><a href="#format-package-lock"><code>format-package-lock</code></a></li><li><a href="#fund"><code>fund</code></a></li><li><a href="#git"><code>git</code></a></li><li><a href="#git-tag-version"><code>git-tag-version</code></a></li><li><a href="#global"><code>global</code></a></li><li><a href="#global-style"><code>global-style</code></a></li><li><a href="#globalconfig"><code>globalconfig</code></a></li><li><a href="#heading"><code>heading</code></a></li><li><a href="#https-proxy"><code>https-proxy</code></a></li><li><a href="#if-present"><code>if-present</code></a></li><li><a href="#ignore-scripts"><code>ignore-scripts</code></a></li><li><a href="#include"><code>include</code></a></li><li><a href="#include-staged"><code>include-staged</code></a></li><li><a href="#init-author-email"><code>init-author-email</code></a></li><li><a href="#init-author-name"><code>init-author-name</code></a></li><li><a href="#init-author-url"><code>init-author-url</code></a></li><li><a href="#init-license"><code>init-license</code></a></li><li><a href="#init-module"><code>init-module</code></a></li><li><a href="#init-version"><code>init-version</code></a></li><li><a href="#json"><code>json</code></a></li><li><a href="#key"><code>key</code></a></li><li><a href="#legacy-bundling"><code>legacy-bundling</code></a></li><li><a href="#legacy-peer-deps"><code>legacy-peer-deps</code></a></li><li><a href="#link"><code>link</code></a></li><li><a href="#local-address"><code>local-address</code></a></li><li><a href="#loglevel"><code>loglevel</code></a></li><li><a href="#logs-max"><code>logs-max</code></a></li><li><a href="#long"><code>long</code></a></li><li><a href="#maxsockets"><code>maxsockets</code></a></li><li><a href="#message"><code>message</code></a></li><li><a href="#node-options"><code>node-options</code></a></li><li><a href="#node-version"><code>node-version</code></a></li><li><a href="#noproxy"><code>noproxy</code></a></li><li><a href="#npm-version"><code>npm-version</code></a></li><li><a href="#offline"><code>offline</code></a></li><li><a href="#omit"><code>omit</code></a></li><li><a href="#otp"><code>otp</code></a></li><li><a href="#package"><code>package</code></a></li><li><a href="#package-lock"><code>package-lock</code></a></li><li><a href="#package-lock-only"><code>package-lock-only</code></a></li><li><a href="#parseable"><code>parseable</code></a></li><li><a href="#prefer-offline"><code>prefer-offline</code></a></li><li><a href="#prefer-online"><code>prefer-online</code></a></li><li><a href="#prefix"><code>prefix</code></a></li><li><a href="#preid"><code>preid</code></a></li><li><a href="#progress"><code>progress</code></a></li><li><a href="#proxy"><code>proxy</code></a></li><li><a href="#read-only"><code>read-only</code></a></li><li><a href="#rebuild-bundle"><code>rebuild-bundle</code></a></li><li><a href="#registry"><code>registry</code></a></li><li><a href="#save"><code>save</code></a></li><li><a href="#save-bundle"><code>save-bundle</code></a></li><li><a href="#save-dev"><code>save-dev</code></a></li><li><a href="#save-exact"><code>save-exact</code></a></li><li><a href="#save-optional"><code>save-optional</code></a></li><li><a href="#save-peer"><code>save-peer</code></a></li><li><a href="#save-prefix"><code>save-prefix</code></a></li><li><a href="#save-prod"><code>save-prod</code></a></li><li><a href="#scope"><code>scope</code></a></li><li><a href="#script-shell"><code>script-shell</code></a></li><li><a href="#searchexclude"><code>searchexclude</code></a></li><li><a href="#searchlimit"><code>searchlimit</code></a></li><li><a href="#searchopts"><code>searchopts</code></a></li><li><a href="#searchstaleness"><code>searchstaleness</code></a></li><li><a href="#shell"><code>shell</code></a></li><li><a href="#sign-git-commit"><code>sign-git-commit</code></a></li><li><a href="#sign-git-tag"><code>sign-git-tag</code></a></li><li><a href="#strict-peer-deps"><code>strict-peer-deps</code></a></li><li><a href="#strict-ssl"><code>strict-ssl</code></a></li><li><a href="#tag"><code>tag</code></a></li><li><a href="#tag-version-prefix"><code>tag-version-prefix</code></a></li><li><a href="#timing"><code>timing</code></a></li><li><a href="#umask"><code>umask</code></a></li><li><a href="#unicode"><code>unicode</code></a></li><li><a href="#update-notifier"><code>update-notifier</code></a></li><li><a href="#usage"><code>usage</code></a></li><li><a href="#user-agent"><code>user-agent</code></a></li><li><a href="#userconfig"><code>userconfig</code></a></li><li><a href="#version"><code>version</code></a></li><li><a href="#versions"><code>versions</code></a></li><li><a href="#viewer"><code>viewer</code></a></li><li><a href="#which"><code>which</code></a></li><li><a href="#workspace"><code>workspace</code></a></li><li><a href="#workspaces"><code>workspaces</code></a></li><li><a href="#yes"><code>yes</code></a></li><li><a href="#also"><code>also</code></a></li><li><a href="#auth-type"><code>auth-type</code></a></li><li><a href="#cache-max"><code>cache-max</code></a></li><li><a href="#cache-min"><code>cache-min</code></a></li><li><a href="#initauthoremail"><code>init.author.email</code></a></li><li><a href="#initauthorname"><code>init.author.name</code></a></li><li><a href="#initauthorurl"><code>init.author.url</code></a></li><li><a href="#initlicense"><code>init.license</code></a></li><li><a href="#initmodule"><code>init.module</code></a></li><li><a href="#initversion"><code>init.version</code></a></li><li><a href="#only"><code>only</code></a></li><li><a href="#optional"><code>optional</code></a></li><li><a href="#production"><code>production</code></a></li><li><a href="#shrinkwrap"><code>shrinkwrap</code></a></li><li><a href="#sso-poll-frequency"><code>sso-poll-frequency</code></a></li><li><a href="#sso-type"><code>sso-type</code></a></li><li><a href="#tmp"><code>tmp</code></a></li></ul><li><a href="#see-also">See also</a></li></ul></div> </section> <div id="_content"><h3 id="description">Description</h3> @@ -185,32 +185,49 @@ <h4 id="default-configs">Default Configs</h4> internal to npm, and are defaults if nothing else is specified.</p> <h3 id="shorthands-and-other-cli-niceties">Shorthands and Other CLI Niceties</h3> <p>The following shorthands are parsed on the command-line:</p> +<!-- raw HTML omitted --> +<!-- raw HTML omitted --> <ul> -<li><code>-v</code>: <code>--version</code></li> -<li><code>-h</code>, <code>-?</code>, <code>--help</code>, <code>-H</code>: <code>--usage</code></li> -<li><code>-s</code>, <code>--silent</code>: <code>--loglevel silent</code></li> -<li><code>-q</code>, <code>--quiet</code>: <code>--loglevel warn</code></li> -<li><code>-d</code>: <code>--loglevel info</code></li> -<li><code>-dd</code>, <code>--verbose</code>: <code>--loglevel verbose</code></li> -<li><code>-ddd</code>: <code>--loglevel silly</code></li> +<li><code>-a</code>: <code>--all</code></li> +<li><code>--enjoy-by</code>: <code>--before</code></li> +<li><code>-c</code>: <code>--call</code></li> +<li><code>--desc</code>: <code>--description</code></li> +<li><code>-f</code>: <code>--force</code></li> <li><code>-g</code>: <code>--global</code></li> -<li><code>-C</code>: <code>--prefix</code></li> +<li><code>-d</code>: <code>--loglevel info</code></li> +<li><code>-s</code>: <code>--loglevel silent</code></li> +<li><code>--silent</code>: <code>--loglevel silent</code></li> +<li><code>--ddd</code>: <code>--loglevel silly</code></li> +<li><code>--dd</code>: <code>--loglevel verbose</code></li> +<li><code>--verbose</code>: <code>--loglevel verbose</code></li> +<li><code>-q</code>: <code>--loglevel warn</code></li> +<li><code>--quiet</code>: <code>--loglevel warn</code></li> <li><code>-l</code>: <code>--long</code></li> <li><code>-m</code>: <code>--message</code></li> -<li><code>-p</code>, <code>--porcelain</code>: <code>--parseable</code></li> -<li><code>-reg</code>: <code>--registry</code></li> -<li><code>-f</code>: <code>--force</code></li> -<li><code>-desc</code>: <code>--description</code></li> +<li><code>--local</code>: <code>--no-global</code></li> +<li><code>-n</code>: <code>--no-yes</code></li> +<li><code>--no</code>: <code>--no-yes</code></li> +<li><code>-p</code>: <code>--parseable</code></li> +<li><code>--porcelain</code>: <code>--parseable</code></li> +<li><code>-C</code>: <code>--prefix</code></li> +<li><code>--readonly</code>: <code>--read-only</code></li> +<li><code>--reg</code>: <code>--registry</code></li> <li><code>-S</code>: <code>--save</code></li> -<li><code>-P</code>: <code>--save-prod</code></li> -<li><code>-D</code>: <code>--save-dev</code></li> -<li><code>-O</code>: <code>--save-optional</code></li> <li><code>-B</code>: <code>--save-bundle</code></li> +<li><code>-D</code>: <code>--save-dev</code></li> <li><code>-E</code>: <code>--save-exact</code></li> +<li><code>-O</code>: <code>--save-optional</code></li> +<li><code>-P</code>: <code>--save-prod</code></li> +<li><code>-?</code>: <code>--usage</code></li> +<li><code>-h</code>: <code>--usage</code></li> +<li><code>-H</code>: <code>--usage</code></li> +<li><code>--help</code>: <code>--usage</code></li> +<li><code>-v</code>: <code>--version</code></li> +<li><code>-w</code>: <code>--workspace</code></li> +<li><code>--ws</code>: <code>--workspaces</code></li> <li><code>-y</code>: <code>--yes</code></li> -<li><code>-n</code>: <code>--yes false</code></li> -<li><code>ll</code> and <code>la</code> commands: <code>ls --long</code></li> </ul> +<!-- raw HTML omitted --> <p>If the specified configuration param resolves unambiguously to a known configuration parameter, then it is expanded to that configuration parameter. For example:</p> @@ -227,162 +244,128 @@ <h3 id="shorthands-and-other-cli-niceties">Shorthands and Other CLI Niceties</h3 npm ls --global --parseable --long --loglevel info </code></pre> <h3 id="config-settings">Config Settings</h3> -<h4 id="access">access</h4> +<!-- raw HTML omitted --> +<!-- raw HTML omitted --> +<h4 id="auth"><code>_auth</code></h4> <ul> -<li>Default: <code>restricted</code></li> -<li>Type: Access</li> +<li>Default: null</li> +<li>Type: null or String</li> </ul> -<p>When publishing scoped packages, the access level defaults to <code>restricted</code>. If -you want your scoped package to be publicly viewable (and installable) set -<code>--access=public</code>. The only valid values for <code>access</code> are <code>public</code> and +<p>A basic-auth string to use when authenticating against the npm registry.</p> +<p>Warning: This should generally not be set via a command-line option. It is +safer to use a registry-provided authentication bearer token stored in the +~/.npmrc file by running <code>npm login</code>.</p> +<h4 id="access"><code>access</code></h4> +<ul> +<li>Default: ‘restricted’ for scoped packages, ‘public’ for unscoped packages</li> +<li>Type: null, “restricted”, or “public”</li> +</ul> +<p>When publishing scoped packages, the access level defaults to <code>restricted</code>. +If you want your scoped package to be publicly viewable (and installable) +set <code>--access=public</code>. The only valid values for <code>access</code> are <code>public</code> and <code>restricted</code>. Unscoped packages <em>always</em> have an access level of <code>public</code>.</p> -<h4 id="all">all</h4> +<h4 id="all"><code>all</code></h4> <ul> -<li>Default: <code>false</code></li> +<li>Default: false</li> <li>Type: Boolean</li> </ul> <p>When running <code>npm outdated</code> and <code>npm ls</code>, setting <code>--all</code> will show all outdated or installed packages, rather than only those directly depended upon by the current project.</p> -<h4 id="allow-same-version">allow-same-version</h4> +<h4 id="allow-same-version"><code>allow-same-version</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>Prevents throwing an error when <code>npm version</code> is used to set the new version to the same value as the current version.</p> -<h4 id="always-auth">always-auth</h4> +<h4 id="always-auth"><code>always-auth</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Force npm to always require authentication when accessing the registry, -even for <code>GET</code> requests.</p> -<h4 id="also">also</h4> -<ul> -<li>Default: null</li> -<li>Type: String</li> -</ul> -<p>When “dev” or “development” and running local <code>npm shrinkwrap</code>, -<code>npm outdated</code>, or <code>npm update</code>, is an alias for <code>--dev</code>.</p> -<h4 id="audit">audit</h4> +<p>Force npm to always require authentication when accessing the registry, even +for <code>GET</code> requests.</p> +<h4 id="audit"><code>audit</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> <p>When “true” submit audit reports alongside <code>npm install</code> runs to the default -registry and all registries configured for scopes. See the documentation -for <a href="../commands/npm-audit.html"><code>npm audit</code></a> for details on what is submitted.</p> -<h4 id="audit-level">audit-level</h4> -<ul> -<li>Default: <code>"low"</code></li> -<li>Type: <code>'low'</code>, <code>'moderate'</code>, <code>'high'</code>, <code>'critical'</code></li> -</ul> -<p>The minimum level of vulnerability for <code>npm audit</code> to exit with -a non-zero exit code.</p> -<h4 id="auth-type">auth-type</h4> +registry and all registries configured for scopes. See the documentation for +<a href="../commands/npm-audit.html"><code>npm audit</code></a> for details on what is submitted.</p> +<h4 id="audit-level"><code>audit-level</code></h4> <ul> -<li>Default: <code>'legacy'</code></li> -<li>Type: <code>'legacy'</code>, <code>'sso'</code>, <code>'saml'</code>, <code>'oauth'</code></li> +<li>Default: null</li> +<li>Type: “low”, “moderate”, “high”, “critical”, “none”, or null</li> </ul> -<p>What authentication strategy to use with <code>adduser</code>/<code>login</code>.</p> -<h4 id="before">before</h4> +<p>The minimum level of vulnerability for <code>npm audit</code> to exit with a non-zero +exit code.</p> +<h4 id="before"><code>before</code></h4> <ul> -<li>Alias: enjoy-by</li> <li>Default: null</li> -<li>Type: Date</li> +<li>Type: null or Date</li> </ul> -<p>If passed to <code>npm install</code>, will rebuild the npm tree such that only versions -that were available <strong>on or before</strong> the <code>--before</code> time get installed. -If there’s no versions available for the current set of direct dependencies, the -command will error.</p> +<p>If passed to <code>npm install</code>, will rebuild the npm tree such that only +versions that were available <strong>on or before</strong> the <code>--before</code> time get +installed. If there’s no versions available for the current set of direct +dependencies, the command will error.</p> <p>If the requested version is a <code>dist-tag</code> and the given tag does not pass the -<code>--before</code> filter, the most recent version less than or equal to that tag will -be used. For example, <code>foo@latest</code> might install <code>foo@1.2</code> even though <code>latest</code> -is <code>2.0</code>.</p> -<h4 id="bin-links">bin-links</h4> +<code>--before</code> filter, the most recent version less than or equal to that tag +will be used. For example, <code>foo@latest</code> might install <code>foo@1.2</code> even though +<code>latest</code> is <code>2.0</code>.</p> +<h4 id="bin-links"><code>bin-links</code></h4> <ul> -<li>Default: <code>true</code></li> +<li>Default: true</li> <li>Type: Boolean</li> </ul> <p>Tells npm to create symlinks (or <code>.cmd</code> shims on Windows) for package executables.</p> -<p>Set to false to have it not do this. This can be used to work around -the fact that some file systems don’t support symlinks, even on -ostensibly Unix systems.</p> -<h4 id="browser">browser</h4> +<p>Set to false to have it not do this. This can be used to work around the +fact that some file systems don’t support symlinks, even on ostensibly Unix +systems.</p> +<h4 id="browser"><code>browser</code></h4> <ul> <li>Default: OS X: <code>"open"</code>, Windows: <code>"start"</code>, Others: <code>"xdg-open"</code></li> -<li>Type: String or Boolean</li> +<li>Type: null, Boolean, or String</li> </ul> <p>The browser that is called by npm commands to open websites.</p> <p>Set to <code>false</code> to suppress browser behavior and instead print urls to terminal.</p> <p>Set to <code>true</code> to use default system URL opener.</p> -<h4 id="ca">ca</h4> +<h4 id="ca"><code>ca</code></h4> <ul> -<li>Default: The npm CA certificate</li> -<li>Type: String, Array or null</li> +<li>Default: null</li> +<li>Type: null or String (can be set multiple times)</li> </ul> <p>The Certificate Authority signing certificate that is trusted for SSL -connections to the registry. Values should be in PEM format (Windows calls it “Base-64 encoded X.509 (.CER)”) with newlines -replaced by the string “\n”. For example:</p> -<pre lang="bash"><code>ca="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" +connections to the registry. Values should be in PEM format (Windows calls +it “Base-64 encoded X.509 (.CER)”) with newlines replaced by the string +“\n”. For example:</p> +<pre lang="ini"><code>ca="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" </code></pre> -<p>Set to <code>null</code> to only allow “known” registrars, or to a specific CA cert -to trust only that specific signing authority.</p> +<p>Set to <code>null</code> to only allow “known” registrars, or to a specific CA cert to +trust only that specific signing authority.</p> <p>Multiple CAs can be trusted by specifying an array of certificates:</p> -<pre lang="bash"><code>ca[]="..." +<pre lang="ini"><code>ca[]="..." ca[]="..." </code></pre> <p>See also the <code>strict-ssl</code> config.</p> -<h4 id="cafile">cafile</h4> +<h4 id="cache"><code>cache</code></h4> <ul> -<li>Default: <code>null</code></li> -<li>Type: path</li> +<li>Default: Windows: <code>%LocalAppData%\npm-cache</code>, Posix: <code>~/.npm</code></li> +<li>Type: Path</li> +</ul> +<p>The location of npm’s cache directory. See <a href="../commands/npm-cache.html"><code>npm cache</code></a></p> +<h4 id="cafile"><code>cafile</code></h4> +<ul> +<li>Default: null</li> +<li>Type: Path</li> </ul> <p>A path to a file containing one or multiple Certificate Authority signing certificates. Similar to the <code>ca</code> setting, but allows for multiple CA’s, as well as for the CA information to be stored in a file on disk.</p> -<h4 id="cache">cache</h4> -<ul> -<li>Default: Windows: <code>%AppData%\npm-cache</code>, Posix: <code>~/.npm</code></li> -<li>Type: path</li> -</ul> -<p>The location of npm’s cache directory. See <a href="../commands/npm-cache.html"><code>npm cache</code></a></p> -<h4 id="cache-lock-stale">cache-lock-stale</h4> -<ul> -<li>Default: 60000 (1 minute)</li> -<li>Type: Number</li> -</ul> -<p>The number of ms before cache folder lockfiles are considered stale.</p> -<h4 id="cache-lock-retries">cache-lock-retries</h4> -<ul> -<li>Default: 10</li> -<li>Type: Number</li> -</ul> -<p>Number of times to retry to acquire a lock on cache folder lockfiles.</p> -<h4 id="cache-lock-wait">cache-lock-wait</h4> -<ul> -<li>Default: 10000 (10 seconds)</li> -<li>Type: Number</li> -</ul> -<p>Number of ms to wait for cache lock files to expire.</p> -<h4 id="cache-max">cache-max</h4> -<ul> -<li>Default: Infinity</li> -<li>Type: Number</li> -</ul> -<p><strong>DEPRECATED</strong>: This option has been deprecated in favor of <code>--prefer-online</code>.</p> -<p><code>--cache-max=0</code> is an alias for <code>--prefer-online</code>.</p> -<h4 id="cache-min">cache-min</h4> -<ul> -<li>Default: 10</li> -<li>Type: Number</li> -</ul> -<p><strong>DEPRECATED</strong>: This option has been deprecated in favor of <code>--prefer-offline</code>.</p> -<p><code>--cache-min=9999 (or bigger)</code> is an alias for <code>--prefer-offline</code>.</p> -<h4 id="call">call</h4> +<h4 id="call"><code>call</code></h4> <ul> <li>Default: “”</li> <li>Type: String</li> @@ -391,131 +374,174 @@ <h4 id="call">call</h4> custom command to be run along with the installed packages.</p> <pre lang="bash"><code>npm exec --package yo --package generator-node --call "yo node" </code></pre> -<h4 id="cert">cert</h4> +<h4 id="cert"><code>cert</code></h4> <ul> -<li>Default: <code>null</code></li> -<li>Type: String</li> +<li>Default: null</li> +<li>Type: null or String</li> </ul> -<p>A client certificate to pass when accessing the registry. Values should be in -PEM format (Windows calls it “Base-64 encoded X.509 (.CER)”) with newlines replaced by the string “\n”. For example:</p> -<pre lang="bash"><code>cert="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" +<p>A client certificate to pass when accessing the registry. Values should be +in PEM format (Windows calls it “Base-64 encoded X.509 (.CER)”) with +newlines replaced by the string “\n”. For example:</p> +<pre lang="ini"><code>cert="-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----" </code></pre> -<p>It is <em>not</em> the path to a certificate file (and there is no “certfile” option).</p> -<h4 id="cidr">cidr</h4> +<p>It is <em>not</em> the path to a certificate file (and there is no “certfile” +option).</p> +<h4 id="ci-name"><code>ci-name</code></h4> <ul> -<li>Default: <code>null</code></li> -<li>Type: String, Array, null</li> +<li>Default: The name of the current CI system, or <code>null</code> when not on a known CI +platform.</li> +<li>Type: null or String</li> </ul> -<p>This is a list of CIDR address to be used when configuring limited access tokens with the <code>npm token create</code> command.</p> -<h4 id="commit-hooks">commit-hooks</h4> +<p>The name of a continuous integration system. If not set explicitly, npm will +detect the current CI environment using the +<a href="http://npm.im/@npmcli/ci-detect"><code>@npmcli/ci-detect</code></a> module.</p> +<h4 id="cidr"><code>cidr</code></h4> <ul> -<li>Default: <code>true</code></li> -<li>Type: Boolean</li> +<li>Default: null</li> +<li>Type: null or String (can be set multiple times)</li> </ul> -<p>Run git commit hooks when using the <code>npm version</code> command.</p> -<h4 id="color">color</h4> +<p>This is a list of CIDR address to be used when configuring limited access +tokens with the <code>npm token create</code> command.</p> +<h4 id="color"><code>color</code></h4> +<ul> +<li>Default: true unless the NO_COLOR environ is set to something other than ‘0’</li> +<li>Type: “always” or Boolean</li> +</ul> +<p>If false, never shows colors. If <code>"always"</code> then always shows colors. If +true, then only prints color codes for tty file descriptors.</p> +<h4 id="commit-hooks"><code>commit-hooks</code></h4> <ul> <li>Default: true</li> -<li>Type: Boolean or <code>"always"</code></li> +<li>Type: Boolean</li> </ul> -<p>If false, never shows colors. If <code>"always"</code> then always shows colors. -If true, then only prints color codes for tty file descriptors.</p> -<p>This option can also be changed using the environment: colors are -disabled when the environment variable <code>NO_COLOR</code> is set to any value.</p> -<h4 id="depth">depth</h4> +<p>Run git commit hooks when using the <code>npm version</code> command.</p> +<h4 id="depth"><code>depth</code></h4> <ul> -<li>Default: null</li> +<li>Default: <code>Infinity</code> if <code>--all</code> is set, otherwise <code>1</code></li> <li>Type: null or Number</li> </ul> <p>The depth to go when recursing packages for <code>npm ls</code>.</p> -<p>To make this default to <code>Infinity</code> instead of <code>null</code>, set <code>--all</code>.</p> -<h4 id="description2">description</h4> +<p>If not set, <code>npm ls</code> will show only the immediate dependencies of the root +project. If <code>--all</code> is set, then npm will show all dependencies by default.</p> +<h4 id="description2"><code>description</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> <p>Show the description in <code>npm search</code></p> -<h4 id="dev">dev</h4> +<h4 id="diff"><code>diff</code></h4> +<ul> +<li>Default:</li> +<li>Type: String (can be set multiple times)</li> +</ul> +<p>Define arguments to compare in <code>npm diff</code>.</p> +<h4 id="diff-dst-prefix"><code>diff-dst-prefix</code></h4> +<ul> +<li>Default: “b/”</li> +<li>Type: String</li> +</ul> +<p>Destination prefix to be used in <code>npm diff</code> output.</p> +<h4 id="diff-ignore-all-space"><code>diff-ignore-all-space</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>[Deprecated] Install <code>dev-dependencies</code> along with packages.</p> -<h4 id="dry-run">dry-run</h4> +<p>Ignore whitespace when comparing lines in <code>npm diff</code>.</p> +<h4 id="diff-name-only"><code>diff-name-only</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Indicates that you don’t want npm to make any changes and that it should -only report what it would have done. This can be passed into any of the -commands that modify your local installation, eg, <code>install</code>, <code>update</code>, -<code>dedupe</code>, <code>uninstall</code>. This is NOT currently honored by some network related -commands, eg <code>dist-tags</code>, <code>owner</code>, etc.</p> -<h4 id="diff">diff</h4> +<p>Prints only filenames when using <code>npm diff</code>.</p> +<h4 id="diff-no-prefix"><code>diff-no-prefix</code></h4> <ul> -<li>Default: null</li> -<li>Type: String, Array, null</li> +<li>Default: false</li> +<li>Type: Boolean</li> </ul> -<p>Define arguments to compare in <code>npm diff</code>.</p> -<h4 id="diff-name-only">diff-name-only</h4> +<p>Do not show any source or destination prefix in <code>npm diff</code> output.</p> +<p>Note: this causes <code>npm diff</code> to ignore the <code>--diff-src-prefix</code> and +<code>--diff-dst-prefix</code> configs.</p> +<h4 id="diff-src-prefix"><code>diff-src-prefix</code></h4> +<ul> +<li>Default: “a/”</li> +<li>Type: String</li> +</ul> +<p>Source prefix to be used in <code>npm diff</code> output.</p> +<h4 id="diff-text"><code>diff-text</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Prints only filenames when using <code>npm diff</code>.</p> -<h4 id="diff-unified">diff-unified</h4> +<p>Treat all files as text in <code>npm diff</code>.</p> +<h4 id="diff-unified"><code>diff-unified</code></h4> <ul> -<li>Type: number</li> -<li>Default: <code>3</code></li> +<li>Default: 3</li> +<li>Type: Number</li> </ul> <p>The number of lines of context to print in <code>npm diff</code>.</p> -<h4 id="diff-ignore-all-space">diff-ignore-all-space</h4> +<h4 id="dry-run"><code>dry-run</code></h4> <ul> -<li>Type: Boolean</li> <li>Default: false</li> +<li>Type: Boolean</li> </ul> -<p>Ignore whitespace when comparing lines in `npm diff.</p> -<h4 id="diff-no-prefix">diff-no-prefix</h4> +<p>Indicates that you don’t want npm to make any changes and that it should +only report what it would have done. This can be passed into any of the +commands that modify your local installation, eg, <code>install</code>, <code>update</code>, +<code>dedupe</code>, <code>uninstall</code>, as well as <code>pack</code> and <code>publish</code>.</p> +<p>Note: This is NOT honored by other network related commands, eg <code>dist-tags</code>, +<code>owner</code>, etc.</p> +<h4 id="editor"><code>editor</code></h4> +<ul> +<li>Default: The EDITOR or VISUAL environment variables, or ‘notepad.exe’ on +Windows, or ‘vim’ on Unix systems</li> +<li>Type: String</li> +</ul> +<p>The command to run for <code>npm edit</code> and <code>npm config edit</code>.</p> +<h4 id="engine-strict"><code>engine-strict</code></h4> <ul> -<li>Type: Boolean</li> <li>Default: false</li> +<li>Type: Boolean</li> </ul> -<p>Do not show any source or destination prefix in <code>npm diff</code> output.</p> -<h4 id="diff-src-prefix">diff-src-prefix</h4> +<p>If set to true, then npm will stubbornly refuse to install (or even consider +installing) any package that claims to not be compatible with the current +Node.js version.</p> +<p>This can be overridden by setting the <code>--force</code> flag.</p> +<h4 id="fetch-retries"><code>fetch-retries</code></h4> <ul> -<li>Type: String</li> -<li>Default: <code>"a/"</code></li> +<li>Default: 2</li> +<li>Type: Number</li> </ul> -<p>Source prefix to be used in <code>npm diff</code> output.</p> -<h4 id="diff-dst-prefix">diff-dst-prefix</h4> +<p>The “retries” config for the <code>retry</code> module to use when fetching packages +from the registry.</p> +<p>npm will retry idempotent read requests to the registry in the case of +network failures or 5xx HTTP errors.</p> +<h4 id="fetch-retry-factor"><code>fetch-retry-factor</code></h4> <ul> -<li>Type: String</li> -<li>Default: <code>"b/"</code></li> +<li>Default: 10</li> +<li>Type: Number</li> </ul> -<p>Destination prefix to be used in <code>npm diff</code> output.</p> -<h4 id="diff-text">diff-text</h4> +<p>The “factor” config for the <code>retry</code> module to use when fetching packages.</p> +<h4 id="fetch-retry-maxtimeout"><code>fetch-retry-maxtimeout</code></h4> <ul> -<li>Alias: <code>-a</code></li> -<li>Type: Boolean</li> -<li>Default: false</li> +<li>Default: 60000 (1 minute)</li> +<li>Type: Number</li> </ul> -<p>Treat all files as text in <code>npm diff</code>.</p> -<h4 id="editor">editor</h4> +<p>The “maxTimeout” config for the <code>retry</code> module to use when fetching +packages.</p> +<h4 id="fetch-retry-mintimeout"><code>fetch-retry-mintimeout</code></h4> <ul> -<li>Default: <code>EDITOR</code> environment variable if set, or <code>"vi"</code> on Posix, -or <code>"notepad"</code> on Windows.</li> -<li>Type: path</li> +<li>Default: 10000 (10 seconds)</li> +<li>Type: Number</li> </ul> -<p>The command to run for <code>npm edit</code> or <code>npm config edit</code>.</p> -<h4 id="engine-strict">engine-strict</h4> +<p>The “minTimeout” config for the <code>retry</code> module to use when fetching +packages.</p> +<h4 id="fetch-timeout"><code>fetch-timeout</code></h4> <ul> -<li>Default: false</li> -<li>Type: Boolean</li> +<li>Default: 300000 (5 minutes)</li> +<li>Type: Number</li> </ul> -<p>If set to true, then npm will stubbornly refuse to install (or even -consider installing) any package that claims to not be compatible with -the current Node.js version.</p> -<h4 id="force">force</h4> +<p>The maximum amount of time to wait for HTTP requests to complete.</p> +<h4 id="force"><code>force</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> @@ -529,16 +555,15 @@ <h4 id="force">force</h4> <li>Allow installing packages that have an <code>engines</code> declaration requiring a different version of npm.</li> <li>Allow installing packages that have an <code>engines</code> declaration requiring a -different version of <code>node</code>, even if <code>--engines-strict</code> is enabled.</li> +different version of <code>node</code>, even if <code>--engine-strict</code> is enabled.</li> <li>Allow <code>npm audit fix</code> to install modules outside your stated dependency range (including SemVer-major changes).</li> -<li>Allow a module to be installed as a direct dependency of itself.</li> <li>Allow unpublishing all versions of a published package.</li> <li>Allow conflicting peerDependencies to be installed in the root project.</li> </ul> <p>If you don’t have a clear idea of what you want to do, it is strongly recommended that you do not use this option!</p> -<h4 id="foreground-scripts">foreground-scripts</h4> +<h4 id="foreground-scripts"><code>foreground-scripts</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> @@ -546,347 +571,322 @@ <h4 id="foreground-scripts">foreground-scripts</h4> <p>Run all build scripts (ie, <code>preinstall</code>, <code>install</code>, and <code>postinstall</code>) scripts for installed packages in the foreground process, sharing standard input, output, and error with the main npm process.</p> -<p>Note that this will generally make installs run slower, and be much -noisier, but can be useful for debugging.</p> -<h4 id="format-package-lock">format-package-lock</h4> +<p>Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging.</p> +<h4 id="format-package-lock"><code>format-package-lock</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> -<p>Format <code>package-lock.json</code> or <code>npm-shrinkwrap.json</code> as a human readable file.</p> -<h4 id="fund">fund</h4> +<p>Format <code>package-lock.json</code> or <code>npm-shrinkwrap.json</code> as a human readable +file.</p> +<h4 id="fund"><code>fund</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> <p>When “true” displays the message at the end of each <code>npm install</code> -acknowledging the number of dependencies looking for funding. -See <a href="../commands/npm-fund.html"><code>npm fund</code></a> for details.</p> -<h4 id="fetch-retries">fetch-retries</h4> -<ul> -<li>Default: 2</li> -<li>Type: Number</li> -</ul> -<p>The “retries” config for the <code>retry</code> module to use when fetching -packages from the registry.</p> -<h4 id="fetch-retry-factor">fetch-retry-factor</h4> -<ul> -<li>Default: 10</li> -<li>Type: Number</li> -</ul> -<p>The “factor” config for the <code>retry</code> module to use when fetching -packages.</p> -<h4 id="fetch-retry-mintimeout">fetch-retry-mintimeout</h4> -<ul> -<li>Default: 10000 (10 seconds)</li> -<li>Type: Number</li> -</ul> -<p>The “minTimeout” config for the <code>retry</code> module to use when fetching -packages.</p> -<h4 id="fetch-retry-maxtimeout">fetch-retry-maxtimeout</h4> -<ul> -<li>Default: 60000 (1 minute)</li> -<li>Type: Number</li> -</ul> -<p>The “maxTimeout” config for the <code>retry</code> module to use when fetching -packages.</p> -<h4 id="fetch-timeout">fetch-timeout</h4> +acknowledging the number of dependencies looking for funding. See <a href="../commands/npm-fund.html"><code>npm fund</code></a> for details.</p> +<h4 id="git"><code>git</code></h4> <ul> -<li>Default: 300000 (5 minutes)</li> -<li>Type: Number</li> -</ul> -<p>The maximum amount of time to wait for HTTP requests to complete.</p> -<h4 id="git">git</h4> -<ul> -<li>Default: <code>"git"</code></li> +<li>Default: “git”</li> <li>Type: String</li> </ul> -<p>The command to use for git commands. If git is installed on the -computer, but is not in the <code>PATH</code>, then set this to the full path to -the git binary.</p> -<h4 id="git-tag-version">git-tag-version</h4> +<p>The command to use for git commands. If git is installed on the computer, +but is not in the <code>PATH</code>, then set this to the full path to the git binary.</p> +<h4 id="git-tag-version"><code>git-tag-version</code></h4> <ul> -<li>Default: <code>true</code></li> +<li>Default: true</li> <li>Type: Boolean</li> </ul> <p>Tag the commit when using the <code>npm version</code> command.</p> -<h4 id="global">global</h4> +<h4 id="global"><code>global</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Operates in “global” mode, so that packages are installed into the -<code>prefix</code> folder instead of the current working directory. See +<p>Operates in “global” mode, so that packages are installed into the <code>prefix</code> +folder instead of the current working directory. See <a href="../configuring-npm/folders.html">folders</a> for more on the differences in behavior.</p> <ul> -<li>packages are installed into the <code>{prefix}/lib/node_modules</code> folder, instead of the -current working directory.</li> +<li>packages are installed into the <code>{prefix}/lib/node_modules</code> folder, instead +of the current working directory.</li> <li>bin files are linked to <code>{prefix}/bin</code></li> <li>man pages are linked to <code>{prefix}/share/man</code></li> </ul> -<h4 id="globalconfig">globalconfig</h4> -<ul> -<li>Default: {prefix}/etc/npmrc</li> -<li>Type: path</li> -</ul> -<p>The config file to read for global config options.</p> -<h4 id="global-style">global-style</h4> +<h4 id="global-style"><code>global-style</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>Causes npm to install the package into your local <code>node_modules</code> folder with -the same layout it uses with the global <code>node_modules</code> folder. Only your +the same layout it uses with the global <code>node_modules</code> folder. Only your direct dependencies will show in <code>node_modules</code> and everything they depend -on will be flattened in their <code>node_modules</code> folders. This obviously will -eliminate some deduping. If used with <code>legacy-bundling</code>, <code>legacy-bundling</code> will be -preferred.</p> -<h4 id="heading">heading</h4> +on will be flattened in their <code>node_modules</code> folders. This obviously will +eliminate some deduping. If used with <code>legacy-bundling</code>, <code>legacy-bundling</code> +will be preferred.</p> +<h4 id="globalconfig"><code>globalconfig</code></h4> <ul> -<li>Default: <code>"npm"</code></li> +<li>Default: The global –prefix setting plus ‘etc/npmrc’. For example, +‘/usr/local/etc/npmrc’</li> +<li>Type: Path</li> +</ul> +<p>The config file to read for global config options.</p> +<h4 id="heading"><code>heading</code></h4> +<ul> +<li>Default: “npm”</li> <li>Type: String</li> </ul> <p>The string that starts all the debugging log output.</p> -<h4 id="https-proxy">https-proxy</h4> +<h4 id="https-proxy"><code>https-proxy</code></h4> <ul> <li>Default: null</li> -<li>Type: url</li> +<li>Type: null or URL</li> </ul> <p>A proxy to use for outgoing https requests. If the <code>HTTPS_PROXY</code> or <code>https_proxy</code> or <code>HTTP_PROXY</code> or <code>http_proxy</code> environment variables are set, -proxy settings will be honored by the underlying <code>request</code> library.</p> -<h4 id="if-present">if-present</h4> -<ul> -<li>Default: false</li> -<li>Type: Boolean</li> -</ul> -<p>If true, npm will not exit with an error code when <code>run-script</code> is invoked for -a script that isn’t defined in the <code>scripts</code> section of <code>package.json</code>. This -option can be used when it’s desirable to optionally run a script when it’s -present and fail if the script fails. This is useful, for example, when running -scripts that may only apply for some builds in an otherwise generic CI setup.</p> -<h4 id="ignore-prepublish">ignore-prepublish</h4> +proxy settings will be honored by the underlying <code>make-fetch-happen</code> +library.</p> +<h4 id="if-present"><code>if-present</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>If true, npm will not run <code>prepublish</code> scripts.</p> -<h4 id="ignore-scripts">ignore-scripts</h4> +<p>If true, npm will not exit with an error code when <code>run-script</code> is invoked +for a script that isn’t defined in the <code>scripts</code> section of <code>package.json</code>. +This option can be used when it’s desirable to optionally run a script when +it’s present and fail if the script fails. This is useful, for example, when +running scripts that may only apply for some builds in an otherwise generic +CI setup.</p> +<h4 id="ignore-scripts"><code>ignore-scripts</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>If true, npm does not run scripts specified in package.json files.</p> -<h4 id="include">include</h4> +<h4 id="include"><code>include</code></h4> <ul> -<li>Default: <code>[prod|dev|optional|peer]</code></li> -<li>Type: Array</li> +<li>Default:</li> +<li>Type: “prod”, “dev”, “optional”, or “peer” (can be set multiple times)</li> </ul> <p>Option that allows for defining which types of dependencies to install.</p> -<h4 id="init-module">init-module</h4> +<p>This is the inverse of <code>--omit=<type></code>.</p> +<p>Dependency types specified in <code>--include</code> will not be omitted, regardless of +the order in which omit/include are specified on the command-line.</p> +<h4 id="include-staged"><code>include-staged</code></h4> <ul> -<li>Alias: <code>init.module</code></li> -<li>Default: ~/.npm-init.js</li> -<li>Type: path</li> +<li>Default: false</li> +<li>Type: Boolean</li> </ul> -<p>A module that will be loaded by the <code>npm init</code> command. See the -documentation for the -<a href="https://github.com/npm/init-package-json">init-package-json</a> module -for more information, or <a href="../commands/npm-init.html">npm init</a>.</p> -<h4 id="init-author-name">init-author-name</h4> +<p>Allow installing “staged” published packages, as defined by <a href="https://github.com/npm/rfcs/pull/92">npm RFC PR +#92</a>.</p> +<p>This is experimental, and not implemented by the npm public registry.</p> +<h4 id="init-author-email"><code>init-author-email</code></h4> <ul> -<li>Alias: <code>init.author.name</code></li> <li>Default: “”</li> <li>Type: String</li> </ul> -<p>The value <code>npm init</code> should use by default for the package author’s name.</p> -<h4 id="init-author-email">init-author-email</h4> +<p>The value <code>npm init</code> should use by default for the package author’s email.</p> +<h4 id="init-author-name"><code>init-author-name</code></h4> <ul> -<li>Alias: <code>init.author.email</code></li> <li>Default: “”</li> <li>Type: String</li> </ul> -<p>The value <code>npm init</code> should use by default for the package author’s email.</p> -<h4 id="init-author-url">init-author-url</h4> +<p>The value <code>npm init</code> should use by default for the package author’s name.</p> +<h4 id="init-author-url"><code>init-author-url</code></h4> <ul> -<li>Alias: <code>init.author.url</code></li> <li>Default: “”</li> -<li>Type: String</li> +<li>Type: “” or URL</li> </ul> -<p>The value <code>npm init</code> should use by default for the package author’s homepage.</p> -<h4 id="init-license">init-license</h4> +<p>The value <code>npm init</code> should use by default for the package author’s +homepage.</p> +<h4 id="init-license"><code>init-license</code></h4> <ul> -<li>Alias: <code>init.license</code></li> <li>Default: “ISC”</li> <li>Type: String</li> </ul> <p>The value <code>npm init</code> should use by default for the package license.</p> -<h4 id="init-version">init-version</h4> +<h4 id="init-module"><code>init-module</code></h4> +<ul> +<li>Default: “~/.npm-init.js”</li> +<li>Type: Path</li> +</ul> +<p>A module that will be loaded by the <code>npm init</code> command. See the +documentation for the +<a href="https://github.com/npm/init-package-json">init-package-json</a> module for +more information, or <a href="../commands/npm-init.html">npm init</a>.</p> +<h4 id="init-version"><code>init-version</code></h4> <ul> -<li>Alias: <code>init.version</code></li> <li>Default: “1.0.0”</li> -<li>Type: semver</li> +<li>Type: SemVer string</li> </ul> -<p>The value that <code>npm init</code> should use by default for the package -version number, if not already set in package.json.</p> -<h4 id="json">json</h4> +<p>The value that <code>npm init</code> should use by default for the package version +number, if not already set in package.json.</p> +<h4 id="json"><code>json</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>Whether or not to output JSON data, rather than the normal output.</p> -<p>This feature is currently experimental, and the output data structures for many -commands is either not implemented in JSON yet, or subject to change. Only the -output from <code>npm ls --json</code> and <code>npm search --json</code> are currently valid.</p> -<h4 id="key">key</h4> +<p>This feature is currently experimental, and the output data structures for +many commands is either not implemented in JSON yet, or subject to change. +Only the output from <code>npm ls --json</code> and <code>npm search --json</code> are currently +valid.</p> +<h4 id="key"><code>key</code></h4> <ul> -<li>Default: <code>null</code></li> -<li>Type: String</li> +<li>Default: null</li> +<li>Type: null or String</li> </ul> -<p>A client key to pass when accessing the registry. Values should be in PEM +<p>A client key to pass when accessing the registry. Values should be in PEM format with newlines replaced by the string “\n”. For example:</p> -<pre lang="json"><code>key="-----BEGIN PRIVATE KEY-----\nXXXX\nXXXX\n-----END PRIVATE KEY-----" +<pre lang="ini"><code>key="-----BEGIN PRIVATE KEY-----\nXXXX\nXXXX\n-----END PRIVATE KEY-----" </code></pre> <p>It is <em>not</em> the path to a key file (and there is no “keyfile” option).</p> -<h4 id="legacy-bundling">legacy-bundling</h4> +<h4 id="legacy-bundling"><code>legacy-bundling</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>Causes npm to install the package such that versions of npm prior to 1.4, -such as the one included with node 0.8, can install the package. This +such as the one included with node 0.8, can install the package. This eliminates all automatic deduping. If used with <code>global-style</code> this option will be preferred.</p> -<h4 id="legacy-peer-deps">legacy-peer-deps</h4> +<h4 id="legacy-peer-deps"><code>legacy-peer-deps</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>Causes npm to completely ignore <code>peerDependencies</code> when building a package tree, as in npm versions 3 through 6.</p> -<p>If a package cannot be installed because of overly strict -<code>peerDependencies</code> that collide, it provides a way to move forward -resolving the situation.</p> +<p>If a package cannot be installed because of overly strict <code>peerDependencies</code> +that collide, it provides a way to move forward resolving the situation.</p> <p>This differs from <code>--omit=peer</code>, in that <code>--omit=peer</code> will avoid unpacking <code>peerDependencies</code> on disk, but will still design a tree such that <code>peerDependencies</code> <em>could</em> be unpacked in a correct place.</p> <p>Use of <code>legacy-peer-deps</code> is not recommended, as it will not enforce the <code>peerDependencies</code> contract that meta-dependencies may rely on.</p> -<h4 id="link">link</h4> +<h4 id="link"><code>link</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>If true, then local installs will link if there is a suitable globally installed package.</p> -<p>Note that this means that local installs can cause things to be -installed into the global space at the same time. The link is only done -if one of the two conditions are met:</p> +<p>Note that this means that local installs can cause things to be installed +into the global space at the same time. The link is only done if one of the +two conditions are met:</p> <ul> <li>The package is not already installed globally, or</li> -<li>the globally installed version is identical to the version that is -being installed locally.</li> +<li>the globally installed version is identical to the version that is being +installed locally.</li> </ul> -<h4 id="local-address">local-address</h4> +<h4 id="local-address"><code>local-address</code></h4> <ul> -<li>Default: undefined</li> +<li>Default: null</li> <li>Type: IP Address</li> </ul> -<p>The IP address of the local interface to use when making connections -to the npm registry. Must be IPv4 in versions of Node prior to 0.12.</p> -<h4 id="loglevel">loglevel</h4> +<p>The IP address of the local interface to use when making connections to the +npm registry. Must be IPv4 in versions of Node prior to 0.12.</p> +<h4 id="loglevel"><code>loglevel</code></h4> <ul> <li>Default: “notice”</li> -<li>Type: String</li> -<li>Values: “silent”, “error”, “warn”, “notice”, “http”, “timing”, “info”, -“verbose”, “silly”</li> +<li>Type: “silent”, “error”, “warn”, “notice”, “http”, “timing”, “info”, +“verbose”, or “silly”</li> </ul> -<p>What level of logs to report. On failure, <em>all</em> logs are written to +<p>What level of logs to report. On failure, <em>all</em> logs are written to <code>npm-debug.log</code> in the current working directory.</p> -<p>Any logs of a higher level than the setting are shown. The default is “notice”.</p> -<h4 id="logs-max">logs-max</h4> +<p>Any logs of a higher level than the setting are shown. The default is +“notice”.</p> +<h4 id="logs-max"><code>logs-max</code></h4> <ul> <li>Default: 10</li> <li>Type: Number</li> </ul> <p>The maximum number of log files to store.</p> -<h4 id="long">long</h4> +<h4 id="long"><code>long</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>Show extended information in <code>npm ls</code> and <code>npm search</code>.</p> -<h4 id="maxsockets">maxsockets</h4> +<h4 id="maxsockets"><code>maxsockets</code></h4> <ul> -<li>Default: 50</li> +<li>Default: Infinity</li> <li>Type: Number</li> </ul> <p>The maximum number of connections to use per origin (protocol/host/port -combination). Passed to the <code>http</code> <code>Agent</code> used to make the request.</p> -<h4 id="message">message</h4> +combination).</p> +<h4 id="message"><code>message</code></h4> <ul> <li>Default: “%s”</li> <li>Type: String</li> </ul> <p>Commit message which is used by <code>npm version</code> when creating version commit.</p> <p>Any “%s” in the message will be replaced with the version number.</p> -<h4 id="node-options">node-options</h4> +<h4 id="node-options"><code>node-options</code></h4> <ul> <li>Default: null</li> -<li>Type: String</li> +<li>Type: null or String</li> </ul> <p>Options to pass through to Node.js via the <code>NODE_OPTIONS</code> environment -variable. This does not impact how npm itself is executed but it does -impact how lifecycle scripts are called.</p> -<h4 id="node-version">node-version</h4> +variable. This does not impact how npm itself is executed but it does impact +how lifecycle scripts are called.</p> +<h4 id="node-version"><code>node-version</code></h4> <ul> -<li>Default: process.version</li> -<li>Type: semver or false</li> +<li>Default: Node.js <code>process.version</code> value</li> +<li>Type: SemVer string</li> </ul> -<p>The node version to use when checking a package’s <code>engines</code> map.</p> -<h4 id="noproxy">noproxy</h4> +<p>The node version to use when checking a package’s <code>engines</code> setting.</p> +<h4 id="noproxy"><code>noproxy</code></h4> <ul> -<li>Default: null</li> -<li>Type: String or Array</li> +<li>Default: The value of the NO_PROXY environment variable</li> +<li>Type: String (can be set multiple times)</li> </ul> -<p>A comma-separated string or an array of domain extensions that a proxy should not be used for.</p> -<h4 id="offline">offline</h4> +<p>Domain extensions that should bypass any proxies.</p> +<p>Also accepts a comma-delimited string.</p> +<h4 id="npm-version"><code>npm-version</code></h4> <ul> -<li>Default: false</li> -<li>Type: Boolean</li> +<li>Default: Output of <code>npm --version</code></li> +<li>Type: SemVer string</li> </ul> -<p>Force offline mode: no network requests will be done during install. To allow -the CLI to fill in missing cache data, see <code>--prefer-offline</code>.</p> -<h4 id="only">only</h4> +<p>The npm version to use when checking a package’s <code>engines</code> setting.</p> +<h4 id="offline"><code>offline</code></h4> <ul> -<li>Default: null</li> -<li>Type: String</li> +<li>Default: false</li> +<li>Type: Boolean</li> </ul> -<p>When “dev” or “development” and running local <code>npm install</code> without any -arguments, only devDependencies (and their dependencies) are installed.</p> -<p>When “dev” or “development” and running local <code>npm ls</code>, <code>npm outdated</code>, or -<code>npm update</code>, is an alias for <code>--dev</code>.</p> -<p>When “prod” or “production” and running local <code>npm install</code> without any -arguments, only non-devDependencies (and their dependencies) are -installed.</p> -<p>When “prod” or “production” and running local <code>npm ls</code>, <code>npm outdated</code>, or -<code>npm update</code>, is an alias for <code>--production</code>.</p> -<h4 id="optional">optional</h4> +<p>Force offline mode: no network requests will be done during install. To +allow the CLI to fill in missing cache data, see <code>--prefer-offline</code>.</p> +<h4 id="omit"><code>omit</code></h4> <ul> -<li>Default: true</li> -<li>Type: Boolean</li> +<li>Default: ‘dev’ if the NODE_ENV environment variable is set to ‘production’, +otherwise empty.</li> +<li>Type: “dev”, “optional”, or “peer” (can be set multiple times)</li> </ul> -<p>Attempt to install packages in the <code>optionalDependencies</code> object. Note -that if these packages fail to install, the overall installation -process is not aborted.</p> -<h4 id="otp">otp</h4> +<p>Dependency types to omit from the installation tree on disk.</p> +<p>Note that these dependencies <em>are</em> still resolved and added to the +<code>package-lock.json</code> or <code>npm-shrinkwrap.json</code> file. They are just not +physically installed on disk.</p> +<p>If a package type appears in both the <code>--include</code> and <code>--omit</code> lists, then +it will be included.</p> +<p>If the resulting omit list includes <code>'dev'</code>, then the <code>NODE_ENV</code> environment +variable will be set to <code>'production'</code> for all lifecycle scripts.</p> +<h4 id="otp"><code>otp</code></h4> <ul> <li>Default: null</li> -<li>Type: Number</li> +<li>Type: null or String</li> </ul> -<p>This is a one-time password from a two-factor authenticator. It’s needed +<p>This is a one-time password from a two-factor authenticator. It’s needed when publishing or changing package permissions with <code>npm access</code>.</p> -<h4 id="package-lock">package-lock</h4> +<p>If not set, and a registry response fails with a challenge for a one-time +password, npm will prompt on the command line for one.</p> +<h4 id="package"><code>package</code></h4> +<ul> +<li>Default:</li> +<li>Type: String (can be set multiple times)</li> +</ul> +<p>The package to install for <a href="../commands/npm-exec.html"><code>npm exec</code></a></p> +<h4 id="package-lock"><code>package-lock</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> @@ -894,113 +894,97 @@ <h4 id="package-lock">package-lock</h4> <p>If set to false, then ignore <code>package-lock.json</code> files when installing. This will also prevent <em>writing</em> <code>package-lock.json</code> if <code>save</code> is true.</p> <p>When package package-locks are disabled, automatic pruning of extraneous -modules will also be disabled. To remove extraneous modules with +modules will also be disabled. To remove extraneous modules with package-locks disabled use <code>npm prune</code>.</p> -<p>This option is an alias for <code>--shrinkwrap</code>.</p> -<h4 id="package-lock-only">package-lock-only</h4> +<h4 id="package-lock-only"><code>package-lock-only</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>If set to true, it will update only the <code>package-lock.json</code>, -instead of checking <code>node_modules</code> and downloading dependencies.</p> -<h4 id="parseable">parseable</h4> +<p>If set to true, it will update only the <code>package-lock.json</code>, instead of +checking <code>node_modules</code> and downloading dependencies.</p> +<h4 id="parseable"><code>parseable</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Output parseable results from commands that write to -standard output. For <code>npm search</code>, this will be tab-separated table format.</p> -<h4 id="prefer-offline">prefer-offline</h4> +<p>Output parseable results from commands that write to standard output. For +<code>npm search</code>, this will be tab-separated table format.</p> +<h4 id="prefer-offline"><code>prefer-offline</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>If true, staleness checks for cached data will be bypassed, but missing data -will be requested from the server. To force full offline mode, use <code>--offline</code>.</p> -<p>This option is effectively equivalent to <code>--cache-min=9999999</code>.</p> -<h4 id="prefer-online">prefer-online</h4> +will be requested from the server. To force full offline mode, use +<code>--offline</code>.</p> +<h4 id="prefer-online"><code>prefer-online</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>If true, staleness checks for cached data will be forced, making the CLI look -for updates immediately even for fresh package data.</p> -<h4 id="prefix">prefix</h4> +<p>If true, staleness checks for cached data will be forced, making the CLI +look for updates immediately even for fresh package data.</p> +<h4 id="prefix"><code>prefix</code></h4> <ul> -<li>Default: see <a href="../configuring-npm/folders.html">folders</a></li> -<li>Type: path</li> +<li>Default: In global mode, the folder where the node executable is installed. +In local mode, the nearest parent folder containing either a package.json +file or a node_modules folder.</li> +<li>Type: Path</li> </ul> -<p>The location to install global items. If set on the command line, then -it forces non-global commands to run in the specified folder.</p> -<h4 id="preid">preid</h4> +<p>The location to install global items. If set on the command line, then it +forces non-global commands to run in the specified folder.</p> +<h4 id="preid"><code>preid</code></h4> <ul> <li>Default: “”</li> <li>Type: String</li> </ul> -<p>The “prerelease identifier” to use as a prefix for the “prerelease” part of a -semver. Like the <code>rc</code> in <code>1.2.0-rc.8</code>.</p> -<h4 id="production">production</h4> -<ul> -<li>Default: false</li> -<li>Type: Boolean</li> -</ul> -<p>Set to true to run in “production” mode.</p> -<ol> -<li>devDependencies are not installed at the topmost level when running -local <code>npm install</code> without any arguments.</li> -<li>Set the NODE_ENV=“production” for lifecycle scripts.</li> -</ol> -<h4 id="progress">progress</h4> +<p>The “prerelease identifier” to use as a prefix for the “prerelease” part of +a semver. Like the <code>rc</code> in <code>1.2.0-rc.8</code>.</p> +<h4 id="progress"><code>progress</code></h4> <ul> -<li>Default: true, unless TRAVIS or CI env vars set.</li> +<li>Default: <code>true</code> unless running in a known CI system</li> <li>Type: Boolean</li> </ul> <p>When set to <code>true</code>, npm will display a progress bar during time intensive operations, if <code>process.stderr</code> is a TTY.</p> <p>Set to <code>false</code> to suppress the progress bar.</p> -<h4 id="proxy">proxy</h4> +<h4 id="proxy"><code>proxy</code></h4> <ul> <li>Default: null</li> -<li>Type: url</li> +<li>Type: null, false, or URL</li> </ul> <p>A proxy to use for outgoing http requests. If the <code>HTTP_PROXY</code> or -<code>http_proxy</code> environment variables are set, proxy settings will be -honored by the underlying <code>request</code> library.</p> -<h4 id="read-only">read-only</h4> +<code>http_proxy</code> environment variables are set, proxy settings will be honored +by the underlying <code>request</code> library.</p> +<h4 id="read-only"><code>read-only</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>This is used to mark a token as unable to publish when configuring limited access tokens with the <code>npm token create</code> command.</p> -<h4 id="rebuild-bundle">rebuild-bundle</h4> +<p>This is used to mark a token as unable to publish when configuring limited +access tokens with the <code>npm token create</code> command.</p> +<h4 id="rebuild-bundle"><code>rebuild-bundle</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> <p>Rebuild bundled dependencies after installation.</p> -<h4 id="registry">registry</h4> -<ul> -<li>Default: <a href="https://registry.npmjs.org/">https://registry.npmjs.org/</a></li> -<li>Type: url</li> -</ul> -<p>The base URL of the npm package registry.</p> -<h4 id="rollback">rollback</h4> +<h4 id="registry"><code>registry</code></h4> <ul> -<li>Default: true</li> -<li>Type: Boolean</li> +<li>Default: “<a href="https://registry.npmjs.org/">https://registry.npmjs.org/</a>”</li> +<li>Type: URL</li> </ul> -<p>Remove failed installs.</p> -<h4 id="save">save</h4> +<p>The base URL of the npm registry.</p> +<h4 id="save"><code>save</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> <p>Save installed packages to a package.json file as dependencies.</p> -<p>When used with the <code>npm rm</code> command, it removes it from the <code>dependencies</code> -object.</p> -<p>Only works if there is already a package.json file present.</p> -<h4 id="save-bundle">save-bundle</h4> +<p>When used with the <code>npm rm</code> command, removes the dependency from +package.json.</p> +<h4 id="save-bundle"><code>save-bundle</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> @@ -1008,161 +992,122 @@ <h4 id="save-bundle">save-bundle</h4> <p>If a package would be saved at install time by the use of <code>--save</code>, <code>--save-dev</code>, or <code>--save-optional</code>, then also put it in the <code>bundleDependencies</code> list.</p> -<p>When used with the <code>npm rm</code> command, it removes it from the -bundledDependencies list.</p> -<h4 id="save-prod">save-prod</h4> +<p>Ignore if <code>--save-peer</code> is set, since peerDependencies cannot be bundled.</p> +<h4 id="save-dev"><code>save-dev</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Makes sure that a package will be saved into <code>dependencies</code> specifically. This -is useful if a package already exists in <code>devDependencies</code> or -<code>optionalDependencies</code>, but you want to move it to be a production dep. This is -also the default behavior if <code>--save</code> is true, and neither <code>--save-dev</code> or -<code>--save-optional</code> are true.</p> -<h4 id="save-dev">save-dev</h4> +<p>Save installed packages to a package.json file as <code>devDependencies</code>.</p> +<h4 id="save-exact"><code>save-exact</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Save installed packages to a package.json file as <code>devDependencies</code>.</p> -<p>When used with the <code>npm rm</code> command, it removes it from the -<code>devDependencies</code> object.</p> -<p>Only works if there is already a package.json file present.</p> -<h4 id="save-exact">save-exact</h4> +<p>Dependencies saved to package.json will be configured with an exact version +rather than using npm’s default semver range operator.</p> +<h4 id="save-optional"><code>save-optional</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Dependencies saved to package.json using <code>--save</code>, <code>--save-dev</code> or -<code>--save-optional</code> will be configured with an exact version rather than -using npm’s default semver range operator.</p> -<h4 id="save-optional">save-optional</h4> +<p>Save installed packages to a package.json file as <code>optionalDependencies</code>.</p> +<h4 id="save-peer"><code>save-peer</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Save installed packages to a package.json file as -optionalDependencies.</p> -<p>When used with the <code>npm rm</code> command, it removes it from the -<code>devDependencies</code> object.</p> -<p>Only works if there is already a package.json file present.</p> -<h4 id="save-prefix">save-prefix</h4> +<p>Save installed packages. to a package.json file as <code>peerDependencies</code></p> +<h4 id="save-prefix"><code>save-prefix</code></h4> <ul> -<li>Default: ‘^’</li> +<li>Default: “^”</li> <li>Type: String</li> </ul> <p>Configure how versions of packages installed to a package.json file via <code>--save</code> or <code>--save-dev</code> get prefixed.</p> -<p>For example if a package has version <code>1.2.3</code>, by default its version is -set to <code>^1.2.3</code> which allows minor upgrades for that package, but after -<code>npm config set save-prefix='~'</code> it would be set to <code>~1.2.3</code> which only allows +<p>For example if a package has version <code>1.2.3</code>, by default its version is set +to <code>^1.2.3</code> which allows minor upgrades for that package, but after <code>npm config set save-prefix='~'</code> it would be set to <code>~1.2.3</code> which only allows patch upgrades.</p> -<h4 id="scope">scope</h4> +<h4 id="save-prod"><code>save-prod</code></h4> +<ul> +<li>Default: false</li> +<li>Type: Boolean</li> +</ul> +<p>Save installed packages into <code>dependencies</code> specifically. This is useful if +a package already exists in <code>devDependencies</code> or <code>optionalDependencies</code>, but +you want to move it to be a non-optional production dependency.</p> +<p>This is the default behavior if <code>--save</code> is true, and neither <code>--save-dev</code> +or <code>--save-optional</code> are true.</p> +<h4 id="scope"><code>scope</code></h4> <ul> <li>Default: the scope of the current project, if any, or “”</li> <li>Type: String</li> </ul> -<p>Associate an operation with a scope for a scoped registry. Useful when logging -in to a private registry for the first time: -<code>npm login --scope=@organization --registry=registry.organization.com</code>, which -will cause <code>@organization</code> to be mapped to the registry for future installation -of packages specified according to the pattern <code>@organization/package</code>.</p> -<h4 id="script-shell">script-shell</h4> +<p>Associate an operation with a scope for a scoped registry.</p> +<p>Useful when logging in to a private registry for the first time:</p> +<pre lang="bash"><code>npm login --scope=@mycorp --registry=https://registry.mycorp.com +</code></pre> +<p>This will cause <code>@mycorp</code> to be mapped to the registry for future +installation of packages specified according to the pattern +<code>@mycorp/package</code>.</p> +<h4 id="script-shell"><code>script-shell</code></h4> <ul> -<li>Default: <code>null</code></li> -<li>Type: path</li> +<li>Default: ‘/bin/sh’ on POSIX systems, ‘cmd.exe’ on Windows</li> +<li>Type: null or String</li> </ul> <p>The shell to use for scripts run with the <code>npm run</code> command.</p> -<h4 id="scripts-prepend-node-path">scripts-prepend-node-path</h4> -<ul> -<li>Default: “warn-only”</li> -<li>Type: Boolean, <code>"auto"</code> or <code>"warn-only"</code></li> -</ul> -<p>If set to <code>true</code>, add the directory in which the current <code>node</code> executable -resides to the <code>PATH</code> environment variable when running scripts, -even if that means that <code>npm</code> will invoke a different <code>node</code> executable than -the one which it is running.</p> -<p>If set to <code>false</code>, never modify <code>PATH</code> with that.</p> -<p>If set to <code>"warn-only"</code>, never modify <code>PATH</code> but print a warning if <code>npm</code> thinks -that you may want to run it with <code>true</code>, e.g. because the <code>node</code> executable -in the <code>PATH</code> is not the one <code>npm</code> was invoked with.</p> -<p>If set to <code>auto</code>, only add that directory to the <code>PATH</code> environment variable -if the <code>node</code> executable with which <code>npm</code> was invoked and the one that is found -first on the <code>PATH</code> are different.</p> -<h4 id="searchexclude">searchexclude</h4> +<h4 id="searchexclude"><code>searchexclude</code></h4> <ul> <li>Default: “”</li> <li>Type: String</li> </ul> <p>Space-separated options that limit the results from search.</p> -<h4 id="searchopts">searchopts</h4> -<ul> -<li>Default: “”</li> -<li>Type: String</li> -</ul> -<p>Space-separated options that are always passed to search.</p> -<h4 id="searchlimit">searchlimit</h4> +<h4 id="searchlimit"><code>searchlimit</code></h4> <ul> <li>Default: 20</li> <li>Type: Number</li> </ul> <p>Number of items to limit search results to. Will not apply at all to legacy searches.</p> -<h4 id="searchstaleness">searchstaleness</h4> +<h4 id="searchopts"><code>searchopts</code></h4> +<ul> +<li>Default: “”</li> +<li>Type: String</li> +</ul> +<p>Space-separated options that are always passed to search.</p> +<h4 id="searchstaleness"><code>searchstaleness</code></h4> <ul> -<li>Default: 900 (15 minutes)</li> +<li>Default: 900</li> <li>Type: Number</li> </ul> <p>The age of the cache, in seconds, before another registry request is made if using legacy search endpoint.</p> -<h4 id="shell">shell</h4> +<h4 id="shell"><code>shell</code></h4> <ul> -<li>Default: SHELL environment variable, or “bash” on Posix, or “cmd” on +<li>Default: SHELL environment variable, or “bash” on Posix, or “cmd.exe” on Windows</li> -<li>Type: path</li> +<li>Type: String</li> </ul> <p>The shell to run for the <code>npm explore</code> command.</p> -<h4 id="shrinkwrap">shrinkwrap</h4> -<ul> -<li>Default: true</li> -<li>Type: Boolean</li> -</ul> -<p>If set to false, then ignore <code>npm-shrinkwrap.json</code> files when installing. This -will also prevent <em>writing</em> <code>npm-shrinkwrap.json</code> if <code>save</code> is true.</p> -<p>This option is an alias for <code>--package-lock</code>.</p> -<h4 id="sign-git-commit">sign-git-commit</h4> +<h4 id="sign-git-commit"><code>sign-git-commit</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> <p>If set to true, then the <code>npm version</code> command will commit the new package version using <code>-S</code> to add a signature.</p> -<p>Note that git requires you to have set up GPG keys in your git configs -for this to work properly.</p> -<h4 id="sign-git-tag">sign-git-tag</h4> +<p>Note that git requires you to have set up GPG keys in your git configs for +this to work properly.</p> +<h4 id="sign-git-tag"><code>sign-git-tag</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>If set to true, then the <code>npm version</code> command will tag the version -using <code>-s</code> to add a signature.</p> -<p>Note that git requires you to have set up GPG keys in your git configs -for this to work properly.</p> -<h4 id="sso-poll-frequency">sso-poll-frequency</h4> -<ul> -<li>Default: 500</li> -<li>Type: Number</li> -</ul> -<p>When used with SSO-enabled <code>auth-type</code>s, configures how regularly the registry -should be polled while the user is completing authentication.</p> -<h4 id="sso-type">sso-type</h4> -<ul> -<li>Default: ‘oauth’</li> -<li>Type: ‘oauth’, ‘saml’, or null</li> -</ul> -<p>If <code>--auth-type=sso</code>, the type of SSO type to use.</p> -<h4 id="strict-peer-deps">strict-peer-deps</h4> +<p>If set to true, then the <code>npm version</code> command will tag the version using +<code>-s</code> to add a signature.</p> +<p>Note that git requires you to have set up GPG keys in your git configs for +this to work properly.</p> +<h4 id="strict-peer-deps"><code>strict-peer-deps</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> @@ -1171,122 +1116,296 @@ <h4 id="strict-peer-deps">strict-peer-deps</h4> conflicting <code>peerDependencies</code> will be treated as an install failure, even if npm could reasonably guess the appropriate resolution based on non-peer dependency relationships.</p> -<p>By default, conflicting <code>peerDependencies</code> in the dependency graph will be -resolved using the nearest non-peer dependency specification, even if doing -so will result in some packages receiving a peer dependency outside the -range set in their package’s <code>peerDependencies</code> object. When such and -override is performed, a warning is printed, explaining the conflict and -the packages involved. If <code>--strict-peer-deps</code> is set, then the warning is -treated as a failure.</p> -<h4 id="strict-ssl">strict-ssl</h4> +<p>By default, conflicting <code>peerDependencies</code> deep in the dependency graph will +be resolved using the nearest non-peer dependency specification, even if +doing so will result in some packages receiving a peer dependency outside +the range set in their package’s <code>peerDependencies</code> object.</p> +<p>When such and override is performed, a warning is printed, explaining the +conflict and the packages involved. If <code>--strict-peer-deps</code> is set, then +this warning is treated as a failure.</p> +<h4 id="strict-ssl"><code>strict-ssl</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> -<p>Whether or not to do SSL key validation when making requests to the -registry via https.</p> +<p>Whether or not to do SSL key validation when making requests to the registry +via https.</p> <p>See also the <code>ca</code> config.</p> -<h4 id="tag">tag</h4> +<h4 id="tag"><code>tag</code></h4> <ul> -<li>Default: latest</li> +<li>Default: “latest”</li> <li>Type: String</li> </ul> -<p>If you ask npm to install a package and don’t tell it a specific version, then -it will install the specified tag.</p> -<p>Also the tag that is added to the package@version specified by the <code>npm tag</code> command, if no explicit tag is given.</p> -<h4 id="tag-version-prefix">tag-version-prefix</h4> +<p>If you ask npm to install a package and don’t tell it a specific version, +then it will install the specified tag.</p> +<p>Also the tag that is added to the package@version specified by the <code>npm tag</code> +command, if no explicit tag is given.</p> +<h4 id="tag-version-prefix"><code>tag-version-prefix</code></h4> <ul> -<li>Default: <code>"v"</code></li> +<li>Default: “v”</li> <li>Type: String</li> </ul> <p>If set, alters the prefix used when tagging a new version when performing a -version increment using <code>npm-version</code>. To remove the prefix altogether, set it -to the empty string: <code>""</code>.</p> -<p>Because other tools may rely on the convention that npm version tags look like -<code>v1.0.0</code>, <em>only use this property if it is absolutely necessary</em>. In +version increment using <code>npm-version</code>. To remove the prefix altogether, set +it to the empty string: <code>""</code>.</p> +<p>Because other tools may rely on the convention that npm version tags look +like <code>v1.0.0</code>, <em>only use this property if it is absolutely necessary</em>. In particular, use care when overriding this setting for public packages.</p> -<h4 id="timing">timing</h4> +<h4 id="timing"><code>timing</code></h4> <ul> -<li>Default: <code>false</code></li> +<li>Default: false</li> <li>Type: Boolean</li> </ul> <p>If true, writes an <code>npm-debug</code> log to <code>_logs</code> and timing information to -<code>_timing.json</code>, both in your cache. <code>_timing.json</code> is a newline delimited -list of JSON objects. You can quickly view it with this -<a href="https://www.npmjs.com/package/json">json</a> command line: -<code>json -g < ~/.npm/_timing.json</code>.</p> -<h4 id="tmp">tmp</h4> +<code>_timing.json</code>, both in your cache, even if the command completes +successfully. <code>_timing.json</code> is a newline delimited list of JSON objects.</p> +<p>You can quickly view it with this <a href="https://npm.im/json">json</a> command line: +<code>npm exec -- json -g < ~/.npm/_timing.json</code>.</p> +<h4 id="umask"><code>umask</code></h4> <ul> -<li>Default: TMPDIR environment variable, or “/tmp”</li> -<li>Type: path</li> +<li>Default: 0</li> +<li>Type: Octal numeric string in range 0000..0777 (0..511)</li> </ul> -<p>Where to store temporary files and folders. All temp files are deleted -on success, but left behind on failure for forensic purposes.</p> -<h4 id="unicode">unicode</h4> -<ul> -<li>Default: false on windows, true on mac/unix systems with a unicode locale</li> +<p>The “umask” value to use when setting the file creation mode on files and +folders.</p> +<p>Folders and executables are given a mode which is <code>0o777</code> masked against +this value. Other files are given a mode which is <code>0o666</code> masked against +this value.</p> +<p>Note that the underlying system will <em>also</em> apply its own umask value to +files and folders that are created, and npm does not circumvent this, but +rather adds the <code>--umask</code> config to it.</p> +<p>Thus, the effective default umask value on most POSIX systems is 0o22, +meaning that folders and executables are created with a mode of 0o755 and +other files are created with a mode of 0o644.</p> +<h4 id="unicode"><code>unicode</code></h4> +<ul> +<li>Default: false on windows, true on mac/unix systems with a unicode locale, +as defined by the LC_ALL, LC_CTYPE, or LANG environment variables.</li> <li>Type: Boolean</li> </ul> -<p>When set to true, npm uses unicode characters in the tree output. When -false, it uses ascii characters to draw trees.</p> -<h4 id="update-notifier">update-notifier</h4> +<p>When set to true, npm uses unicode characters in the tree output. When +false, it uses ascii characters instead of unicode glyphs.</p> +<h4 id="update-notifier"><code>update-notifier</code></h4> <ul> <li>Default: true</li> <li>Type: Boolean</li> </ul> -<p>Set to false to suppress the update notification when using an older -version of npm than the latest.</p> -<h4 id="usage">usage</h4> +<p>Set to false to suppress the update notification when using an older version +of npm than the latest.</p> +<h4 id="usage"><code>usage</code></h4> <ul> <li>Default: false</li> <li>Type: Boolean</li> </ul> -<p>Set to show short usage output (like the -H output) -instead of complete help when doing <a href="../commands/npm-help.html"><code>npm help</code></a>.</p> -<h4 id="userconfig">userconfig</h4> +<p>Show short usage output about the command specified.</p> +<h4 id="user-agent"><code>user-agent</code></h4> <ul> -<li>Default: ~/.npmrc</li> -<li>Type: path</li> +<li>Default: “npm/{npm-version} node/{node-version} {platform} {arch} {ci}”</li> +<li>Type: String</li> </ul> -<p>The location of user-level configuration settings.</p> -<h4 id="umask">umask</h4> +<p>Sets the User-Agent request header. The following fields are replaced with +their actual counterparts:</p> <ul> -<li>Default: 022</li> -<li>Type: Octal numeric string in range 0000..0777 (0..511)</li> +<li><code>{npm-version}</code> - The npm version in use</li> +<li><code>{node-version}</code> - The Node.js version in use</li> +<li><code>{platform}</code> - The value of <code>process.platform</code></li> +<li><code>{arch}</code> - The value of <code>process.arch</code></li> +<li><code>{ci}</code> - The value of the <code>ci-name</code> config, if set, prefixed with <code>ci/</code>, or +an empty string if <code>ci-name</code> is empty.</li> </ul> -<p>The “umask” value to use when setting the file creation mode on files -and folders.</p> -<p>Folders and executables are given a mode which is <code>0777</code> masked against -this value. Other files are given a mode which is <code>0666</code> masked against -this value. Thus, the defaults are <code>0755</code> and <code>0644</code> respectively.</p> -<h4 id="user-agent">user-agent</h4> +<h4 id="userconfig"><code>userconfig</code></h4> <ul> -<li>Default: node/{process.version} {process.platform} {process.arch}</li> -<li>Type: String</li> +<li>Default: “~/.npmrc”</li> +<li>Type: Path</li> </ul> -<p>Sets a User-Agent to the request header</p> -<h4 id="version">version</h4> +<p>The location of user-level configuration settings.</p> +<p>This may be overridden by the <code>npm_config_userconfig</code> environment variable +or the <code>--userconfig</code> command line option, but may <em>not</em> be overridden by +settings in the <code>globalconfig</code> file.</p> +<h4 id="version"><code>version</code></h4> <ul> <li>Default: false</li> -<li>Type: boolean</li> +<li>Type: Boolean</li> </ul> <p>If true, output the npm version and exit successfully.</p> <p>Only relevant when specified explicitly on the command line.</p> -<h4 id="versions">versions</h4> +<h4 id="versions"><code>versions</code></h4> <ul> <li>Default: false</li> -<li>Type: boolean</li> +<li>Type: Boolean</li> </ul> -<p>If true, output the npm version as well as node’s <code>process.versions</code> map, and -exit successfully.</p> +<p>If true, output the npm version as well as node’s <code>process.versions</code> map and +the version in the current working directory’s <code>package.json</code> file if one +exists, and exit successfully.</p> <p>Only relevant when specified explicitly on the command line.</p> -<h4 id="viewer">viewer</h4> +<h4 id="viewer"><code>viewer</code></h4> <ul> <li>Default: “man” on Posix, “browser” on Windows</li> -<li>Type: path</li> +<li>Type: String</li> </ul> <p>The program to use to view help content.</p> <p>Set to <code>"browser"</code> to view html help content in the default web browser.</p> +<h4 id="which"><code>which</code></h4> +<ul> +<li>Default: null</li> +<li>Type: null or Number</li> +</ul> +<p>If there are multiple funding sources, which 1-indexed source URL to open.</p> +<h4 id="workspace"><code>workspace</code></h4> +<ul> +<li>Default:</li> +<li>Type: String (can be set multiple times)</li> +</ul> +<p>Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option.</p> +<p>Valid values for the <code>workspace</code> config are either: - Workspace names - Path +to a workspace directory - Path to a parent workspace directory (will result +to selecting all of the nested workspaces)</p> +<h4 id="workspaces"><code>workspaces</code></h4> +<ul> +<li>Default: false</li> +<li>Type: Boolean</li> +</ul> +<p>Enable running a command in the context of <strong>all</strong> the configured +workspaces.</p> +<h4 id="yes"><code>yes</code></h4> +<ul> +<li>Default: null</li> +<li>Type: null or Boolean</li> +</ul> +<p>Automatically answer “yes” to any prompts that npm might print on the +command line.</p> +<h4 id="also"><code>also</code></h4> +<ul> +<li>Default: null</li> +<li>Type: null, “dev”, or “development”</li> +<li>DEPRECATED: Please use –include=dev instead.</li> +</ul> +<p>When set to <code>dev</code> or <code>development</code>, this is an alias for <code>--include=dev</code>.</p> +<h4 id="auth-type"><code>auth-type</code></h4> +<ul> +<li>Default: “legacy”</li> +<li>Type: “legacy”, “sso”, “saml”, or “oauth”</li> +<li>DEPRECATED: This method of SSO/SAML/OAuth is deprecated and will be removed +in a future version of npm in favor of web-based login.</li> +</ul> +<p>What authentication strategy to use with <code>adduser</code>/<code>login</code>.</p> +<h4 id="cache-max"><code>cache-max</code></h4> +<ul> +<li>Default: Infinity</li> +<li>Type: Number</li> +<li>DEPRECATED: This option has been deprecated in favor of <code>--prefer-online</code></li> +</ul> +<p><code>--cache-max=0</code> is an alias for <code>--prefer-online</code></p> +<h4 id="cache-min"><code>cache-min</code></h4> +<ul> +<li>Default: 0</li> +<li>Type: Number</li> +<li>DEPRECATED: This option has been deprecated in favor of <code>--prefer-offline</code>.</li> +</ul> +<p><code>--cache-min=9999 (or bigger)</code> is an alias for <code>--prefer-offline</code>.</p> +<h4 id="initauthoremail"><code>init.author.email</code></h4> +<ul> +<li>Default: “”</li> +<li>Type: String</li> +<li>DEPRECATED: Use <code>--init-author-email</code> instead.</li> +</ul> +<p>Alias for <code>--init-author-email</code></p> +<h4 id="initauthorname"><code>init.author.name</code></h4> +<ul> +<li>Default: “”</li> +<li>Type: String</li> +<li>DEPRECATED: Use <code>--init-author-name</code> instead.</li> +</ul> +<p>Alias for <code>--init-author-name</code></p> +<h4 id="initauthorurl"><code>init.author.url</code></h4> +<ul> +<li>Default: “”</li> +<li>Type: “” or URL</li> +<li>DEPRECATED: Use <code>--init-author-url</code> instead.</li> +</ul> +<p>Alias for <code>--init-author-url</code></p> +<h4 id="initlicense"><code>init.license</code></h4> +<ul> +<li>Default: “ISC”</li> +<li>Type: String</li> +<li>DEPRECATED: Use <code>--init-license</code> instead.</li> +</ul> +<p>Alias for <code>--init-license</code></p> +<h4 id="initmodule"><code>init.module</code></h4> +<ul> +<li>Default: “~/.npm-init.js”</li> +<li>Type: Path</li> +<li>DEPRECATED: Use <code>--init-module</code> instead.</li> +</ul> +<p>Alias for <code>--init-module</code></p> +<h4 id="initversion"><code>init.version</code></h4> +<ul> +<li>Default: “1.0.0”</li> +<li>Type: SemVer string</li> +<li>DEPRECATED: Use <code>--init-version</code> instead.</li> +</ul> +<p>Alias for <code>--init-version</code></p> +<h4 id="only"><code>only</code></h4> +<ul> +<li>Default: null</li> +<li>Type: null, “prod”, or “production”</li> +<li>DEPRECATED: Use <code>--omit=dev</code> to omit dev dependencies from the install.</li> +</ul> +<p>When set to <code>prod</code> or <code>production</code>, this is an alias for <code>--omit=dev</code>.</p> +<h4 id="optional"><code>optional</code></h4> +<ul> +<li>Default: null</li> +<li>Type: null or Boolean</li> +<li>DEPRECATED: Use <code>--omit=optional</code> to exclude optional dependencies, or +<code>--include=optional</code> to include them.</li> +</ul> +<p>Default value does install optional deps unless otherwise omitted.</p> +<p>Alias for –include=optional or –omit=optional</p> +<h4 id="production"><code>production</code></h4> +<ul> +<li>Default: false</li> +<li>Type: Boolean</li> +<li>DEPRECATED: Use <code>--omit=dev</code> instead.</li> +</ul> +<p>Alias for <code>--omit=dev</code></p> +<h4 id="shrinkwrap"><code>shrinkwrap</code></h4> +<ul> +<li>Default: true</li> +<li>Type: Boolean</li> +<li>DEPRECATED: Use the –package-lock setting instead.</li> +</ul> +<p>Alias for –package-lock</p> +<h4 id="sso-poll-frequency"><code>sso-poll-frequency</code></h4> +<ul> +<li>Default: 500</li> +<li>Type: Number</li> +<li>DEPRECATED: The –auth-type method of SSO/SAML/OAuth will be removed in a +future version of npm in favor of web-based login.</li> +</ul> +<p>When used with SSO-enabled <code>auth-type</code>s, configures how regularly the +registry should be polled while the user is completing authentication.</p> +<h4 id="sso-type"><code>sso-type</code></h4> +<ul> +<li>Default: “oauth”</li> +<li>Type: null, “oauth”, or “saml”</li> +<li>DEPRECATED: The –auth-type method of SSO/SAML/OAuth will be removed in a +future version of npm in favor of web-based login.</li> +</ul> +<p>If <code>--auth-type=sso</code>, the type of SSO type to use.</p> +<h4 id="tmp"><code>tmp</code></h4> +<ul> +<li>Default: The value returned by the Node.js <code>os.tmpdir()</code> method +<a href="https://nodejs.org/api/os.html#os_os_tmpdir">https://nodejs.org/api/os.html#os_os_tmpdir</a></li> +<li>Type: Path</li> +<li>DEPRECATED: This setting is no longer used. npm stores temporary files in a +special location in the cache, and they are managed by +<a href="http://npm.im/cacache"><code>cacache</code></a>.</li> +</ul> +<p>Historically, the location where temporary files were stored. No longer +relevant.</p> +<!-- raw HTML omitted --> <h3 id="see-also">See also</h3> <ul> <li><a href="../commands/npm-config.html">npm config</a></li> diff --git a/deps/npm/docs/output/using-npm/workspaces.html b/deps/npm/docs/output/using-npm/workspaces.html index ffec6467c4a515..0fb1391c7970c7 100644 --- a/deps/npm/docs/output/using-npm/workspaces.html +++ b/deps/npm/docs/output/using-npm/workspaces.html @@ -141,7 +141,7 @@ <h1 id="workspaces">workspaces</h1> <section id="table_of_contents"> <h2 id="table-of-contents">Table of contents</h2> -<div id="_table_of_contents"><ul><li><a href="#description">Description</a></li><li><a href="#installing-workspaces">Installing workspaces</a></li><li><a href="#using-workspaces">Using workspaces</a></li><li><a href="#see-also">See also</a></li></ul></div> +<div id="_table_of_contents"><ul><li><a href="#description">Description</a></li><li><a href="#installing-workspaces">Installing workspaces</a></li><li><a href="#using-workspaces">Using workspaces</a></li><li><a href="#running-commands-in-the-context-of-workspaces">Running commands in the context of workspaces</a></li><li><a href="#see-also">See also</a></li></ul></div> </section> <div id="_content"><h3 id="description">Description</h3> @@ -206,10 +206,40 @@ <h3 id="using-workspaces">Using workspaces</h3> <strong>workspaces</strong> to enable a portable workflow for requiring each <strong>workspace</strong> in such a way that is also easy to <a href="../commands/npm-publish.html">publish</a> these nested workspaces to be consumed elsewhere.</p> +<h3 id="running-commands-in-the-context-of-workspaces">Running commands in the context of workspaces</h3> +<p>You man use the <code>workspace</code> configuration option to run commands in the context +of a configured workspace.</p> +<p>Following is a quick example on how to use the <code>npm run</code> command in the context +of nested workspaces. For a project containing multiple workspaces, e.g:</p> +<pre><code>. ++-- package.json +`-- packages + +-- a + | `-- package.json + `-- b + `-- package.json +</code></pre> +<p>By running a command using the <code>workspace</code> option, it’s possible to run the +given command in the context of that specific workspace. e.g:</p> +<pre><code>npm run test --workspace=a +</code></pre> +<p>This will run the <code>test</code> script defined within the +<code>./packages/a/package.json</code> file.</p> +<p>Please note that you can also specify this argument multiple times in the +command-line in order to target multiple workspaces, e.g:</p> +<pre><code>npm run test --workspace=a --workspace=b +</code></pre> +<p>It’s also possible to use the <code>workspaces</code> (plural) configuration option to +enable the same behavior but running that command in the context of <strong>all</strong> +configured workspaces. e.g:</p> +<pre><code>npm run test --workspaces +</code></pre> +<p>Will run the <code>test</code> script in both <code>./packages/a</code> and <code>./packages/b</code>.</p> <h3 id="see-also">See also</h3> <ul> <li><a href="../commands/npm-install.html">npm install</a></li> <li><a href="../commands/npm-publish.html">npm publish</a></li> +<li><a href="../commands/npm-run-script.html">npm run-script</a></li> </ul> </div> diff --git a/deps/npm/lib/access.js b/deps/npm/lib/access.js index 0df36beeac15f3..3df611e5640c7a 100644 --- a/deps/npm/lib/access.js +++ b/deps/npm/lib/access.js @@ -20,6 +20,10 @@ const subcommands = [ ] class Access extends BaseCommand { + static get description () { + return 'Set access level on published packages' + } + static get name () { return 'access' } @@ -75,7 +79,7 @@ class Access extends BaseCommand { if (!subcommands.includes(cmd) || !this[cmd]) throw this.usageError(`${cmd} is not a recognized subcommand.`) - return this[cmd](args, { ...this.npm.flatOptions }) + return this[cmd](args, this.npm.flatOptions) } public ([pkg], opts) { diff --git a/deps/npm/lib/adduser.js b/deps/npm/lib/adduser.js index da318a1f3feb85..f35b9829fe946e 100644 --- a/deps/npm/lib/adduser.js +++ b/deps/npm/lib/adduser.js @@ -9,12 +9,20 @@ const authTypes = { } class AddUser extends BaseCommand { + static get description () { + return 'Add a registry user account' + } + static get name () { return 'adduser' } - static get usage () { - return ['[--registry=url] [--scope=@orgname] [--always-auth]'] + static get params () { + return [ + 'registry', + 'scope', + 'always-auth', + ] } exec (args, cb) { diff --git a/deps/npm/lib/audit.js b/deps/npm/lib/audit.js index 6e64987b612ae5..f990e1fa5efaa9 100644 --- a/deps/npm/lib/audit.js +++ b/deps/npm/lib/audit.js @@ -5,19 +5,32 @@ const auditError = require('./utils/audit-error.js') const BaseCommand = require('./base-command.js') class Audit extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Run a security audit' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'audit' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get usage () { + static get params () { return [ - '[--json] [--production]', - 'fix [--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)]', + 'dry-run', + 'force', + 'json', + 'package-lock-only', + 'production', ] } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get usage () { + return ['[fix]'] + } + async completion (opts) { const argv = opts.conf.argv.remain @@ -37,11 +50,14 @@ class Audit extends BaseCommand { } async audit (args) { - const arb = new Arborist({ + const reporter = this.npm.config.get('json') ? 'json' : 'detail' + const opts = { ...this.npm.flatOptions, audit: true, path: this.npm.prefix, - }) + reporter, + } + const arb = new Arborist(opts) const fix = args[0] === 'fix' await arb.audit({ fix }) if (fix) @@ -49,11 +65,7 @@ class Audit extends BaseCommand { else { // will throw if there's an error, because this is an audit command auditError(this.npm, arb.auditReport) - const reporter = this.npm.flatOptions.json ? 'json' : 'detail' - const result = auditReport(arb.auditReport, { - ...this.npm.flatOptions, - reporter, - }) + const result = auditReport(arb.auditReport, opts) process.exitCode = process.exitCode || result.exitCode this.npm.output(result.report) } diff --git a/deps/npm/lib/base-command.js b/deps/npm/lib/base-command.js index 8e48caa2ef4c72..7a9e4b8ee377eb 100644 --- a/deps/npm/lib/base-command.js +++ b/deps/npm/lib/base-command.js @@ -1,11 +1,20 @@ // Base class for npm.commands[cmd] const usageUtil = require('./utils/usage.js') +const ConfigDefinitions = require('./utils/config/definitions.js') class BaseCommand { constructor (npm) { this.npm = npm } + get name () { + return this.constructor.name + } + + get description () { + return this.constructor.description + } + get usage () { let usage = `npm ${this.constructor.name}\n\n` if (this.constructor.description) @@ -17,6 +26,9 @@ class BaseCommand { else usage = `${usage}${this.constructor.usage.map(u => `npm ${this.constructor.name} ${u}`).join('\n')}` + if (this.constructor.params) + usage = `${usage}\n\nOptions:\n[${this.constructor.params.map(p => ConfigDefinitions[p].usage).join('] [')}]` + // Mostly this just appends aliases, this could be more clear usage = usageUtil(this.constructor.name, usage) usage = `${usage}\n\nRun "npm help ${this.constructor.name}" for more info` @@ -34,5 +46,12 @@ class BaseCommand { code: 'EUSAGE', }) } + + execWorkspaces (args, filters, cb) { + throw Object.assign( + new Error('This command does not support workspaces.'), + { code: 'ENOWORKSPACES' } + ) + } } module.exports = BaseCommand diff --git a/deps/npm/lib/bin.js b/deps/npm/lib/bin.js index 1450fb539bffaf..20e13f160f2761 100644 --- a/deps/npm/lib/bin.js +++ b/deps/npm/lib/bin.js @@ -2,12 +2,16 @@ const envPath = require('./utils/path.js') const BaseCommand = require('./base-command.js') class Bin extends BaseCommand { + static get description () { + return 'Display npm bin folder' + } + static get name () { return 'bin' } - static get usage () { - return ['[-g]'] + static get params () { + return ['global'] } exec (args, cb) { @@ -17,7 +21,7 @@ class Bin extends BaseCommand { async bin (args) { const b = this.npm.bin this.npm.output(b) - if (this.npm.flatOptions.global && !envPath.includes(b)) + if (this.npm.config.get('global') && !envPath.includes(b)) console.error('(not in PATH env variable)') } } diff --git a/deps/npm/lib/birthday.js b/deps/npm/lib/birthday.js index 5ea855512f9f69..92b1dd1c2e5fe8 100644 --- a/deps/npm/lib/birthday.js +++ b/deps/npm/lib/birthday.js @@ -1,16 +1,9 @@ -class Birthday { - constructor (npm) { - this.npm = npm - Object.defineProperty(this.npm, 'flatOptions', { - value: { - ...npm.flatOptions, - package: ['@npmcli/npm-birthday'], - yes: true, - }, - }) - } +const BaseCommand = require('./base-command.js') +class Birthday extends BaseCommand { exec (args, cb) { + this.npm.config.set('package', ['@npmcli/npm-birthday']) + this.npm.config.set('yes', true) return this.npm.commands.exec(['npm-birthday'], cb) } } diff --git a/deps/npm/lib/bugs.js b/deps/npm/lib/bugs.js index 1814dd7bc461e1..a0cef4c5ec4faf 100644 --- a/deps/npm/lib/bugs.js +++ b/deps/npm/lib/bugs.js @@ -5,6 +5,10 @@ const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js') const BaseCommand = require('./base-command.js') class Bugs extends BaseCommand { + static get description () { + return 'Report bugs for a package in a web browser' + } + static get name () { return 'bugs' } diff --git a/deps/npm/lib/cache.js b/deps/npm/lib/cache.js index 80a5c68ebc0e99..43902f43bbee19 100644 --- a/deps/npm/lib/cache.js +++ b/deps/npm/lib/cache.js @@ -7,6 +7,10 @@ const rimraf = promisify(require('rimraf')) const BaseCommand = require('./base-command.js') class Cache extends BaseCommand { + static get description () { + return 'Manipulates packages cache' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'cache' @@ -63,7 +67,7 @@ class Cache extends BaseCommand { throw new Error('npm cache clear does not accept arguments') const cachePath = path.join(this.npm.cache, '_cacache') - if (!this.npm.flatOptions.force) { + if (!this.npm.config.get('force')) { throw new Error(`As of npm@5, the npm cache self-heals from corruption issues by treating integrity mismatches as cache misses. As a result, data extracted from the cache is guaranteed to be valid. If you @@ -100,7 +104,6 @@ with --force.`) throw Object.assign(new Error(usage), { code: 'EUSAGE' }) log.silly('cache add', 'spec', spec) - const opts = { ...this.npm.flatOptions } // we ask pacote for the thing, and then just throw the data // away so that it tee-pipes it into the cache like it does @@ -108,7 +111,7 @@ with --force.`) await pacote.tarball.stream(spec, stream => { stream.resume() return stream.promise() - }, opts) + }, this.npm.flatOptions) } async verify () { diff --git a/deps/npm/lib/ci.js b/deps/npm/lib/ci.js index 3ea19937616e65..b73b3a85911148 100644 --- a/deps/npm/lib/ci.js +++ b/deps/npm/lib/ci.js @@ -20,6 +20,11 @@ const removeNodeModules = async where => { const BaseCommand = require('./base-command.js') class CI extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Install a project with a clean slate' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'ci' @@ -30,14 +35,13 @@ class CI extends BaseCommand { } async ci () { - if (this.npm.flatOptions.global) { + if (this.npm.config.get('global')) { const err = new Error('`npm ci` does not work for global packages') err.code = 'ECIGLOBAL' throw err } const where = this.npm.prefix - const { scriptShell, ignoreScripts } = this.npm.flatOptions const arb = new Arborist({ ...this.npm.flatOptions, path: where }) await Promise.all([ @@ -54,6 +58,7 @@ class CI extends BaseCommand { // npm ci should never modify the lockfile or package.json await arb.reify({ ...this.npm.flatOptions, save: false }) + const ignoreScripts = this.npm.config.get('ignore-scripts') // run the same set of scripts that `npm install` runs. if (!ignoreScripts) { const scripts = [ @@ -65,6 +70,7 @@ class CI extends BaseCommand { 'prepare', 'postprepare', ] + const scriptShell = this.npm.config.get('script-shell') || undefined for (const event of scripts) { await runScript({ path: where, diff --git a/deps/npm/lib/cli.js b/deps/npm/lib/cli.js index 910b674eaa790f..46859f150e3b9d 100644 --- a/deps/npm/lib/cli.js +++ b/deps/npm/lib/cli.js @@ -40,11 +40,14 @@ module.exports = (process) => { npm.load(async er => { if (er) return errorHandler(er) + + // npm --version=cli if (npm.config.get('version', 'cli')) { - console.log(npm.version) + npm.output(npm.version) return errorHandler.exit(0) } + // npm --versions=cli if (npm.config.get('versions', 'cli')) { npm.argv = ['version'] npm.config.set('usage', false, 'cli') @@ -57,9 +60,20 @@ module.exports = (process) => { if (impl) impl(npm.argv, errorHandler) else { - npm.config.set('usage', false) - npm.argv.unshift(cmd) - npm.commands.help(npm.argv, errorHandler) + try { + // I don't know why this is needed but we get a cb() not called if we + // omit it + npm.log.level = 'silent' + if (cmd) { + const didYouMean = require('./utils/did-you-mean.js') + const suggestions = await didYouMean(npm, npm.localPrefix, cmd) + npm.output(`Unknown command: "${cmd}"${suggestions}\n\nTo see a list of supported npm commands, run:\n npm help`) + } else + npm.output(npm.usage) + process.exitCode = 1 + } catch (err) { + errorHandler(err) + } } }) } diff --git a/deps/npm/lib/completion.js b/deps/npm/lib/completion.js index 3ee68cdacaf95e..fa3b5f2dd36ccc 100644 --- a/deps/npm/lib/completion.js +++ b/deps/npm/lib/completion.js @@ -29,13 +29,13 @@ // as an array. // -const { types, shorthands } = require('./utils/config.js') +const { definitions, shorthands } = require('./utils/config/index.js') const deref = require('./utils/deref-command.js') const { aliases, cmdList, plumbing } = require('./utils/cmd-list.js') const aliasNames = Object.keys(aliases) const fullList = cmdList.concat(aliasNames).filter(c => !plumbing.includes(c)) const nopt = require('nopt') -const configNames = Object.keys(types) +const configNames = Object.keys(definitions) const shorthandNames = Object.keys(shorthands) const allConfs = configNames.concat(shorthandNames) const isWindowsShell = require('./utils/is-windows-shell.js') @@ -46,13 +46,13 @@ const BaseCommand = require('./base-command.js') class Completion extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get name () { - return 'completion' + static get description () { + return 'Tab Completion for npm' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get description () { - return 'npm command completion script. save to ~/.bashrc or ~/.zshrc' + static get name () { + return 'completion' } // completion for the completion command @@ -147,6 +147,10 @@ class Completion extends BaseCommand { // take a little shortcut and use npm's arg parsing logic. // don't have to worry about the last arg being implicitly // boolean'ed, since the last block will catch that. + const types = Object.entries(definitions).reduce((types, [key, def]) => { + types[key] = def.type + return types + }, {}) const parsed = opts.conf = nopt(types, shorthands, partialWords.slice(0, -1), 0) // check if there's a command already. @@ -256,7 +260,7 @@ const isFlag = word => { const split = word.match(/^(-*)((?:no-)+)?(.*)$/) const no = split[2] const conf = split[3] - const type = types[conf] + const {type} = definitions[conf] return no || type === Boolean || (Array.isArray(type) && type.includes(Boolean)) || diff --git a/deps/npm/lib/config.js b/deps/npm/lib/config.js index c29253e430a33a..d5ef6ec50a5e60 100644 --- a/deps/npm/lib/config.js +++ b/deps/npm/lib/config.js @@ -1,4 +1,5 @@ -const { defaults, types } = require('./utils/config.js') +// don't expand so that we only assemble the set of defaults when needed +const configDefs = require('./utils/config/index.js') const mkdirp = require('mkdirp-infer-owner') const { dirname } = require('path') @@ -29,6 +30,10 @@ const publicVar = k => !/^(\/\/[^:]+:)?_/.test(k) const BaseCommand = require('./base-command.js') class Config extends BaseCommand { + static get description () { + return 'Manage the npm configuration files' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'config' @@ -70,7 +75,7 @@ class Config extends BaseCommand { case 'get': case 'delete': case 'rm': - return Object.keys(types) + return Object.keys(configDefs.definitions) case 'edit': case 'list': case 'ls': @@ -100,7 +105,7 @@ class Config extends BaseCommand { break case 'list': case 'ls': - await (this.npm.flatOptions.json ? this.listJson() : this.list()) + await (this.npm.config.get('json') ? this.listJson() : this.list()) break case 'edit': await this.edit() @@ -117,7 +122,7 @@ class Config extends BaseCommand { if (!args.length) throw this.usageError() - const where = this.npm.flatOptions.global ? 'global' : 'user' + const where = this.npm.config.get('global') ? 'global' : 'user' for (const [key, val] of Object.entries(keyValues(args))) { this.npm.log.info('config', 'set %j %j', key, val) this.npm.config.set(key, val || '', where) @@ -147,14 +152,15 @@ class Config extends BaseCommand { if (!keys.length) throw this.usageError() - const where = this.npm.flatOptions.global ? 'global' : 'user' + const where = this.npm.config.get('global') ? 'global' : 'user' for (const key of keys) this.npm.config.delete(key, where) await this.npm.config.save(where) } async edit () { - const { editor: e, global } = this.npm.flatOptions + const global = this.npm.config.get('global') + const e = this.npm.config.get('editor') const where = global ? 'global' : 'user' const file = this.npm.config.data.get(where).source @@ -165,7 +171,8 @@ class Config extends BaseCommand { const data = ( await readFile(file, 'utf8').catch(() => '') ).replace(/\r\n/g, '\n') - const defData = Object.entries(defaults).reduce((str, [key, val]) => { + const entries = Object.entries(configDefs.defaults) + const defData = entries.reduce((str, [key, val]) => { const obj = { [key]: val } const i = ini.stringify(obj) .replace(/\r\n/g, '\n') // normalizes output from ini.stringify @@ -210,7 +217,7 @@ ${defData} async list () { const msg = [] - const { long } = this.npm.flatOptions + const long = this.npm.config.get('long') for (const [where, { data, source }] of this.npm.config.data.entries()) { if (where === 'default' && !long) continue diff --git a/deps/npm/lib/dedupe.js b/deps/npm/lib/dedupe.js index 50a56211fc8479..b80a777fcc2f75 100644 --- a/deps/npm/lib/dedupe.js +++ b/deps/npm/lib/dedupe.js @@ -5,6 +5,11 @@ const reifyFinish = require('./utils/reify-finish.js') const BaseCommand = require('./base-command.js') class Dedupe extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Reduce duplication in the package tree' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'dedupe' @@ -23,12 +28,13 @@ class Dedupe extends BaseCommand { const dryRun = this.npm.config.get('dry-run') const where = this.npm.prefix - const arb = new Arborist({ + const opts = { ...this.npm.flatOptions, path: where, dryRun, - }) - await arb.dedupe(this.npm.flatOptions) + } + const arb = new Arborist(opts) + await arb.dedupe(opts) await reifyFinish(this.npm, arb) } } diff --git a/deps/npm/lib/deprecate.js b/deps/npm/lib/deprecate.js index a0c67f805d2f92..c640bcfe09538a 100644 --- a/deps/npm/lib/deprecate.js +++ b/deps/npm/lib/deprecate.js @@ -7,6 +7,10 @@ const libaccess = require('libnpmaccess') const BaseCommand = require('./base-command.js') class Deprecate extends BaseCommand { + static get description () { + return 'Deprecate a version of a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'deprecate' diff --git a/deps/npm/lib/diff.js b/deps/npm/lib/diff.js index 0e322ec6438494..c5e4b403efdfa0 100644 --- a/deps/npm/lib/diff.js +++ b/deps/npm/lib/diff.js @@ -12,6 +12,10 @@ const readLocalPkg = require('./utils/read-local-package.js') const BaseCommand = require('./base-command.js') class Diff extends BaseCommand { + static get description () { + return 'The registry diff command' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'diff' @@ -30,7 +34,7 @@ class Diff extends BaseCommand { get where () { const globalTop = resolve(this.npm.globalDir, '..') - const { global } = this.npm.flatOptions + const global = this.npm.config.get('global') return global ? globalTop : this.npm.prefix } @@ -39,7 +43,7 @@ class Diff extends BaseCommand { } async diff (args) { - const specs = this.npm.flatOptions.diff.filter(d => d) + const specs = this.npm.config.get('diff').filter(d => d) if (specs.length > 2) { throw new TypeError( 'Can\'t use more than two --diff arguments.\n\n' + @@ -91,7 +95,7 @@ class Diff extends BaseCommand { } return [ - `${pkgName}@${this.npm.flatOptions.defaultTag}`, + `${pkgName}@${this.npm.config.get('tag')}`, `file:${this.npm.prefix}`, ] } diff --git a/deps/npm/lib/dist-tag.js b/deps/npm/lib/dist-tag.js index cdc95ac6f0cd7a..13ec37fd8cb1d4 100644 --- a/deps/npm/lib/dist-tag.js +++ b/deps/npm/lib/dist-tag.js @@ -8,6 +8,10 @@ const readLocalPkgName = require('./utils/read-local-package.js') const BaseCommand = require('./base-command.js') class DistTag extends BaseCommand { + static get description () { + return 'Modify package distribution tags' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'dist-tag' @@ -61,7 +65,7 @@ class DistTag extends BaseCommand { async add (spec, tag, opts) { spec = npa(spec || '') const version = spec.rawSpec - const defaultTag = tag || opts.defaultTag + const defaultTag = tag || this.npm.config.get('tag') log.verbose('dist-tag add', defaultTag, 'to', spec.name + '@' + version) diff --git a/deps/npm/lib/docs.js b/deps/npm/lib/docs.js index 2dad7a26db4e74..089d77eb046d93 100644 --- a/deps/npm/lib/docs.js +++ b/deps/npm/lib/docs.js @@ -1,17 +1,23 @@ const log = require('npmlog') const pacote = require('pacote') const openUrl = require('./utils/open-url.js') -const usageUtil = require('./utils/usage.js') const hostedFromMani = require('./utils/hosted-git-info-from-manifest.js') -class Docs { - constructor (npm) { - this.npm = npm +const BaseCommand = require('./base-command.js') +class Docs extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Open documentation for a package in a web browser' + } + + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'docs' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - get usage () { - return usageUtil('docs', 'npm docs [<pkgname> [<pkgname> ...]]') + static get usage () { + return ['[<pkgname> [<pkgname> ...]]'] } exec (args, cb) { diff --git a/deps/npm/lib/doctor.js b/deps/npm/lib/doctor.js index fbe44714140aad..99beb25279cbc3 100644 --- a/deps/npm/lib/doctor.js +++ b/deps/npm/lib/doctor.js @@ -11,7 +11,7 @@ const { promisify } = require('util') const ansiTrim = require('./utils/ansi-trim.js') const isWindows = require('./utils/is-windows.js') const ping = require('./utils/ping.js') -const { defaults: { registry: defaultRegistry } } = require('./utils/config.js') +const { registry: { default: defaultRegistry } } = require('./utils/config/definitions.js') const lstat = promisify(fs.lstat) const readdir = promisify(fs.readdir) const access = promisify(fs.access) @@ -32,6 +32,11 @@ const maskLabel = mask => { const BaseCommand = require('./base-command.js') class Doctor extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Check your npm environment' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'doctor' diff --git a/deps/npm/lib/edit.js b/deps/npm/lib/edit.js index 1dbe8e4c103ad5..79d41462eb47f4 100644 --- a/deps/npm/lib/edit.js +++ b/deps/npm/lib/edit.js @@ -9,6 +9,10 @@ const completion = require('./utils/completion/installed-shallow.js') const BaseCommand = require('./base-command.js') class Edit extends BaseCommand { + static get description () { + return 'Edit an installed package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'edit' diff --git a/deps/npm/lib/exec.js b/deps/npm/lib/exec.js index b2443b17accd2f..0a61de7c1200cd 100644 --- a/deps/npm/lib/exec.js +++ b/deps/npm/lib/exec.js @@ -1,5 +1,6 @@ const { promisify } = require('util') const read = promisify(require('read')) +const chalk = require('chalk') const mkdirp = require('mkdirp-infer-owner') const readPackageJson = require('read-package-json-fast') const Arborist = require('@npmcli/arborist') @@ -12,6 +13,7 @@ const npa = require('npm-package-arg') const fileExists = require('./utils/file-exists.js') const PATH = require('./utils/path.js') const BaseCommand = require('./base-command.js') +const getWorkspaces = require('./workspaces/get-workspaces.js') // it's like this: // @@ -24,7 +26,7 @@ const BaseCommand = require('./base-command.js') // // npm x -p pkg@version -- foo --registry=/dev/null // -// const pkg = npm.flatOptions.package || getPackageFrom(args[0]) +// const pkg = npm.config.get('package') || getPackageFrom(args[0]) // const cmd = getCommand(pkg, args[0]) // --> npm x -c 'cmd ...args.slice(1)' // @@ -38,15 +40,22 @@ const BaseCommand = require('./base-command.js') // runScript({ pkg, event: 'npx', ... }) // process.env.npm_lifecycle_event = 'npx' +const nocolor = { + reset: s => s, + bold: s => s, + dim: s => s, + green: s => s, +} + class Exec extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get name () { - return 'exec' + static get description () { + return 'Run a command from a local or remote npm package' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get description () { - return 'Run a command from a local or remote npm package.' + static get name () { + return 'exec' } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -60,17 +69,27 @@ class Exec extends BaseCommand { } exec (args, cb) { - this._exec(args).then(() => cb()).catch(cb) + const path = this.npm.localPrefix + const runPath = process.cwd() + this._exec(args, { path, runPath }).then(() => cb()).catch(cb) + } + + execWorkspaces (args, filters, cb) { + this._execWorkspaces(args, filters).then(() => cb()).catch(cb) } // When commands go async and we can dump the boilerplate exec methods this // can be named correctly - async _exec (args) { - const { package: packages, call, shell } = this.npm.flatOptions + async _exec (_args, { locationMsg, path, runPath }) { + const call = this.npm.config.get('call') + const shell = this.npm.config.get('shell') + // dereferenced because we manipulate it later + const packages = [...this.npm.config.get('package')] - if (call && args.length) + if (call && _args.length) throw this.usage + const args = [..._args] const pathArr = [...PATH] // nothing to maybe install, skip the arborist dance @@ -78,8 +97,11 @@ class Exec extends BaseCommand { return await this.run({ args, call, + locationMsg, shell, + path, pathArr, + runPath, }) } @@ -102,7 +124,10 @@ class Exec extends BaseCommand { return await this.run({ args, call, + locationMsg, + path, pathArr, + runPath, shell, }) } @@ -117,11 +142,11 @@ class Exec extends BaseCommand { // node_modules/${name}/package.json, and only pacote fetch if // that fails. const manis = await Promise.all(packages.map(async p => { - const spec = npa(p, this.npm.localPrefix) + const spec = npa(p, path) if (spec.type === 'tag' && spec.rawSpec === '') { // fall through to the pacote.manifest() approach try { - const pj = resolve(this.npm.localPrefix, 'node_modules', spec.name) + const pj = resolve(path, 'node_modules', spec.name) return await readPackageJson(pj) } catch (er) {} } @@ -140,7 +165,7 @@ class Exec extends BaseCommand { // figure out whether we need to install stuff, or if local is fine const localArb = new Arborist({ ...this.npm.flatOptions, - path: this.npm.localPrefix, + path, }) const tree = await localArb.loadActual() @@ -165,10 +190,10 @@ class Exec extends BaseCommand { // no need to install if already present if (add.length) { - if (!this.npm.flatOptions.yes) { + if (!this.npm.config.get('yes')) { // set -n to always say no - if (this.npm.flatOptions.yes === false) - throw 'canceled' + if (this.npm.config.get('yes') === false) + throw new Error('canceled') if (!process.stdin.isTTY || ciDetect()) { this.npm.log.warn('exec', `The following package${ @@ -184,7 +209,7 @@ class Exec extends BaseCommand { }Ok to proceed? ` const confirm = await read({ prompt, default: 'y' }) if (confirm.trim().toLowerCase().charAt(0) !== 'y') - throw 'canceled' + throw new Error('canceled') } } await arb.reify({ ...this.npm.flatOptions, add }) @@ -192,16 +217,24 @@ class Exec extends BaseCommand { pathArr.unshift(resolve(installDir, 'node_modules/.bin')) } - return await this.run({ args, call, pathArr, shell }) + return await this.run({ + args, + call, + locationMsg, + path, + pathArr, + runPath, + shell, + }) } - async run ({ args, call, pathArr, shell }) { + async run ({ args, call, locationMsg, path, pathArr, runPath, shell }) { // turn list of args into command string const script = call || args.shift() || shell // do the fakey runScript dance // still should work if no package.json in cwd - const realPkg = await readPackageJson(`${this.npm.localPrefix}/package.json`) + const realPkg = await readPackageJson(`${path}/package.json`) .catch(() => ({})) const pkg = { ...realPkg, @@ -217,7 +250,19 @@ class Exec extends BaseCommand { if (process.stdin.isTTY) { if (ciDetect()) return this.npm.log.warn('exec', 'Interactive mode disabled in CI environment') - this.npm.output(`\nEntering npm script environment\nType 'exit' or ^D when finished\n`) + + const color = this.npm.config.get('color') + const colorize = color ? chalk : nocolor + + locationMsg = locationMsg || ` at location:\n${colorize.dim(runPath)}` + + this.npm.output(`${ + colorize.reset('\nEntering npm script environment') + }${ + colorize.reset(locationMsg) + }${ + colorize.bold('\nType \'exit\' or ^D when finished\n') + }`) } } return await runScript({ @@ -225,7 +270,7 @@ class Exec extends BaseCommand { pkg, banner: false, // we always run in cwd, not --prefix - path: process.cwd(), + path: runPath, stdioString: true, event: 'npx', args, @@ -285,5 +330,28 @@ class Exec extends BaseCommand { .digest('hex') .slice(0, 16) } + + async workspaces (filters) { + return getWorkspaces(filters, { path: this.npm.localPrefix }) + } + + async _execWorkspaces (args, filters) { + const workspaces = await this.workspaces(filters) + const getLocationMsg = async path => { + const color = this.npm.config.get('color') + const colorize = color ? chalk : nocolor + const { _id } = await readPackageJson(`${path}/package.json`) + return ` in workspace ${colorize.green(_id)} at location:\n${colorize.dim(path)}` + } + + for (const workspacePath of workspaces.values()) { + const locationMsg = await getLocationMsg(workspacePath) + await this._exec(args, { + locationMsg, + path: workspacePath, + runPath: workspacePath, + }) + } + } } module.exports = Exec diff --git a/deps/npm/lib/explain.js b/deps/npm/lib/explain.js index 6af7611867786d..4c2908df68918d 100644 --- a/deps/npm/lib/explain.js +++ b/deps/npm/lib/explain.js @@ -8,6 +8,10 @@ const validName = require('validate-npm-package-name') const BaseCommand = require('./base-command.js') class Explain extends BaseCommand { + static get description () { + return 'Explain installed packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'explain' diff --git a/deps/npm/lib/explore.js b/deps/npm/lib/explore.js index 34f6d10793c7ea..5f9820c2162b6e 100644 --- a/deps/npm/lib/explore.js +++ b/deps/npm/lib/explore.js @@ -8,6 +8,10 @@ const completion = require('./utils/completion/installed-shallow.js') const BaseCommand = require('./base-command.js') class Explore extends BaseCommand { + static get description () { + return 'Browse an installed package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'explore' diff --git a/deps/npm/lib/find-dupes.js b/deps/npm/lib/find-dupes.js index ecb945f47bb41d..8037876f5bbb68 100644 --- a/deps/npm/lib/find-dupes.js +++ b/deps/npm/lib/find-dupes.js @@ -2,6 +2,11 @@ const BaseCommand = require('./base-command.js') class FindDupes extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Find duplication in the package tree' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'find-dupes' diff --git a/deps/npm/lib/fund.js b/deps/npm/lib/fund.js index a723c62d2c3c51..25d3462f638693 100644 --- a/deps/npm/lib/fund.js +++ b/deps/npm/lib/fund.js @@ -22,14 +22,29 @@ const getPrintableName = ({ name, version }) => { const BaseCommand = require('./base-command.js') class Fund extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Retrieve funding information' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'fund' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return [ + 'json', + 'browser', + 'unicode', + 'which', + ] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[--json] [--browser] [--unicode] [[<@scope>/]<pkg> [--which=<fundingSourceNumber>]'] + return ['[[<@scope>/]<pkg>]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -42,14 +57,13 @@ class Fund extends BaseCommand { } async fund (args) { - const opts = this.npm.flatOptions const spec = args[0] - const numberArg = opts.which + const numberArg = this.npm.config.get('which') const fundingSourceNumber = numberArg && parseInt(numberArg, 10) const badFundingSourceNumber = - numberArg !== undefined && + numberArg !== null && (String(fundingSourceNumber) !== numberArg || fundingSourceNumber < 1) if (badFundingSourceNumber) { @@ -58,14 +72,14 @@ class Fund extends BaseCommand { throw err } - if (opts.global) { + if (this.npm.config.get('global')) { const err = new Error('`npm fund` does not support global packages') err.code = 'EFUNDGLOBAL' throw err } const where = this.npm.prefix - const arb = new Arborist({ ...opts, path: where }) + const arb = new Arborist({ ...this.npm.flatOptions, path: where }) const tree = await arb.loadActual() if (spec) { @@ -78,23 +92,19 @@ class Fund extends BaseCommand { return } - const print = opts.json - ? this.printJSON - : this.printHuman - - this.npm.output( - print( - getFundingInfo(tree), - opts - ) - ) + if (this.npm.config.get('json')) + this.npm.output(this.printJSON(getFundingInfo(tree))) + else + this.npm.output(this.printHuman(getFundingInfo(tree))) } printJSON (fundingInfo) { return JSON.stringify(fundingInfo, null, 2) } - printHuman (fundingInfo, { color, unicode }) { + printHuman (fundingInfo) { + const color = !!this.npm.color + const unicode = this.npm.config.get('unicode') const seenUrls = new Map() const tree = obj => diff --git a/deps/npm/lib/get.js b/deps/npm/lib/get.js index a5d58accc83070..8cfb259a81323b 100644 --- a/deps/npm/lib/get.js +++ b/deps/npm/lib/get.js @@ -1,6 +1,11 @@ const BaseCommand = require('./base-command.js') class Get extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Get a value from the npm configuration' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'get' diff --git a/deps/npm/lib/help-search.js b/deps/npm/lib/help-search.js index 4e727c3e72954a..c1ceae4414c2fb 100644 --- a/deps/npm/lib/help-search.js +++ b/deps/npm/lib/help-search.js @@ -1,15 +1,16 @@ const fs = require('fs') const path = require('path') const color = require('ansicolors') -const npmUsage = require('./utils/npm-usage.js') const { promisify } = require('util') const glob = promisify(require('glob')) const readFile = promisify(fs.readFile) -const didYouMean = require('./utils/did-you-mean.js') -const { cmdList } = require('./utils/cmd-list.js') const BaseCommand = require('./base-command.js') class HelpSearch extends BaseCommand { + static get description () { + return 'Search npm help documentation' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'help-search' @@ -26,28 +27,17 @@ class HelpSearch extends BaseCommand { async helpSearch (args) { if (!args.length) - throw this.usage + return this.npm.output(this.usage) const docPath = path.resolve(__dirname, '..', 'docs/content') - const files = await glob(`${docPath}/*/*.md`) const data = await this.readFiles(files) const results = await this.searchFiles(args, data, files) - // if only one result, then just show that help section. - if (results.length === 1) { - return this.npm.commands.help([path.basename(results[0].file, '.md')], er => { - if (er) - throw er - }) - } - const formatted = this.formatResults(args, results) if (!formatted.trim()) - npmUsage(this.npm, false) - else { + this.npm.output(`No matches in help for: ${args.join(' ')}\n`) + else this.npm.output(formatted) - this.npm.output(didYouMean(args[0], cmdList)) - } } async readFiles (files) { @@ -166,7 +156,7 @@ class HelpSearch extends BaseCommand { out.push(' '.repeat((Math.max(1, cols - out.join(' ').length - r.length - 1)))) out.push(r) - if (!this.npm.flatOptions.long) + if (!this.npm.config.get('long')) return out.join('') out.unshift('\n\n') @@ -198,7 +188,7 @@ class HelpSearch extends BaseCommand { return out.join('') }).join('\n') - const finalOut = results.length && !this.npm.flatOptions.long + const finalOut = results.length && !this.npm.config.get('long') ? 'Top hits for ' + (args.map(JSON.stringify).join(' ')) + '\n' + '—'.repeat(cols - 1) + '\n' + out + '\n' + diff --git a/deps/npm/lib/help.js b/deps/npm/lib/help.js index 93abf878ba26fa..b9ff1c95760c6a 100644 --- a/deps/npm/lib/help.js +++ b/deps/npm/lib/help.js @@ -1,13 +1,22 @@ -const npmUsage = require('./utils/npm-usage.js') const { spawn } = require('child_process') const path = require('path') -const log = require('npmlog') const openUrl = require('./utils/open-url.js') -const glob = require('glob') +const { promisify } = require('util') +const glob = promisify(require('glob')) const BaseCommand = require('./base-command.js') +// Strips out the number from foo.7 or foo.7. or foo.7.tgz +// We don't currently compress our man pages but if we ever did this would +// seemlessly continue supporting it +const manNumberRegex = /\.(\d+)(\..*)?$/ + class Help extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Get help on npm' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'help' @@ -22,13 +31,7 @@ class Help extends BaseCommand { if (opts.conf.argv.remain.length > 2) return [] const g = path.resolve(__dirname, '../man/man[0-9]/*.[0-9]') - const files = await new Promise((resolve, reject) => { - glob(g, function (er, files) { - if (er) - return reject(er) - resolve(files) - }) - }) + const files = await glob(g) return Object.keys(files.reduce(function (acc, file) { file = path.basename(file).replace(/\.[0-9]+$/, '') @@ -43,81 +46,45 @@ class Help extends BaseCommand { } async help (args) { - const argv = this.npm.config.parsedArgv.cooked + // By default we search all of our man subdirectories, but if the user has + // asked for a specific one we limit the search to just there + let manSearch = 'man*' + if (/^\d+$/.test(args[0])) + manSearch = `man${args.shift()}` - let argnum = 0 - if (args.length === 2 && ~~args[0]) - argnum = ~~args.shift() + if (!args.length) + return this.npm.output(this.npm.usage) // npm help foo bar baz: search topics - if (args.length > 1 && args[0]) + if (args.length > 1) return this.helpSearch(args) - const affordances = { - 'find-dupes': 'dedupe', - } - let section = affordances[args[0]] || this.npm.deref(args[0]) || args[0] - - // npm help <noargs>: show basic usage - if (!section) { - npmUsage(this.npm, argv[0] === 'help') - return - } - - // npm <command> -h: show command usage - if (this.npm.config.get('usage') && - this.npm.commands[section] && - this.npm.commands[section].usage) { - this.npm.config.set('loglevel', 'silent') - log.level = 'silent' - this.npm.output(this.npm.commands[section].usage) - return - } + let section = this.npm.deref(args[0]) || args[0] - let pref = [1, 5, 7] - if (argnum) - pref = [argnum].concat(pref.filter(n => n !== argnum)) + // support `npm help package.json` + section = section.replace('.json', '-json') - // npm help <section>: Try to find the path const manroot = path.resolve(__dirname, '..', 'man') + // find either section.n or npm-section.n + const f = `${manroot}/${manSearch}/?(npm-)${section}.[0-9]*` + let mans = await glob(f) + mans = mans.sort((a, b) => { + // Because of the glob we know the manNumberRegex will pass + const aManNumber = a.match(manNumberRegex)[1] + const bManNumber = b.match(manNumberRegex)[1] - // legacy - if (section === 'global') - section = 'folders' - else if (section.match(/.*json/)) - section = section.replace('.json', '-json') - - // find either /section.n or /npm-section.n - // The glob is used in the glob. The regexp is used much - // further down. Globs and regexps are different - const compextglob = '.+(gz|bz2|lzma|[FYzZ]|xz)' - const compextre = '\\.(gz|bz2|lzma|[FYzZ]|xz)$' - const f = '+(npm-' + section + '|' + section + ').[0-9]?(' + compextglob + ')' - return new Promise((resolve, reject) => { - glob(manroot + '/*/' + f, async (er, mans) => { - if (er) - return reject(er) - - if (!mans.length) { - this.helpSearch(args).then(resolve).catch(reject) - return - } - - mans = mans.map((man) => { - const ext = path.extname(man) - if (man.match(new RegExp(compextre))) - man = path.basename(man, ext) - - return man - }) - - this.viewMan(this.pickMan(mans, pref), (err) => { - if (err) - return reject(err) - return resolve() - }) - }) + // man number sort first so that 1 aka commands are preferred + if (aManNumber !== bManNumber) + return aManNumber - bManNumber + + return a.localeCompare(b) }) + const man = mans[0] + + if (man) + await this.viewMan(man) + else + return this.helpSearch(args) } helpSearch (args) { @@ -133,32 +100,11 @@ class Help extends BaseCommand { }) } - pickMan (mans, pref_) { - const nre = /([0-9]+)$/ - const pref = {} - pref_.forEach((sect, i) => pref[sect] = i) - mans = mans.sort((a, b) => { - const an = a.match(nre)[1] - const bn = b.match(nre)[1] - return an === bn ? (a > b ? -1 : 1) - : pref[an] < pref[bn] ? -1 - : 1 - }) - return mans[0] - } - - viewMan (man, cb) { - const nre = /([0-9]+)$/ - const num = man.match(nre)[1] - const section = path.basename(man, '.' + num) - - // at this point, we know that the specified man page exists - const manpath = path.join(__dirname, '..', 'man') + async viewMan (man) { const env = {} Object.keys(process.env).forEach(function (i) { env[i] = process.env[i] }) - env.MANPATH = manpath const viewer = this.npm.config.get('viewer') const opts = { @@ -175,48 +121,39 @@ class Help extends BaseCommand { break case 'browser': - bin = false - try { - const url = this.htmlMan(man) - openUrl(this.npm, url, 'help available at the following URL').then( - () => cb() - ).catch(cb) - } catch (err) { - cb(err) - } - break + await openUrl(this.npm, this.htmlMan(man), 'help available at the following URL') + return default: - args.push(num, section) + args.push(man) break } - if (bin) { - const proc = spawn(bin, args, opts) + const proc = spawn(bin, args, opts) + return new Promise((resolve, reject) => { proc.on('exit', (code) => { if (code) - return cb(new Error(`help process exited with code: ${code}`)) + return reject(new Error(`help process exited with code: ${code}`)) - return cb() + return resolve() }) - } + }) } + // Returns the path to the html version of the man page htmlMan (man) { - let sect = +man.match(/([0-9]+)$/)[1] - const f = path.basename(man).replace(/[.]([0-9]+)$/, '') + let sect = man.match(manNumberRegex)[1] + const f = path.basename(man).replace(manNumberRegex, '') switch (sect) { - case 1: + case '1': sect = 'commands' break - case 5: + case '5': sect = 'configuring-npm' break - case 7: + case '7': sect = 'using-npm' break - default: - throw new Error('invalid man section: ' + sect) } return 'file://' + path.resolve(__dirname, '..', 'docs', 'output', sect, f + '.html') } diff --git a/deps/npm/lib/hook.js b/deps/npm/lib/hook.js index 6cda3504f43d72..64b1201cbd0deb 100644 --- a/deps/npm/lib/hook.js +++ b/deps/npm/lib/hook.js @@ -5,6 +5,10 @@ const Table = require('cli-table3') const BaseCommand = require('./base-command.js') class Hook extends BaseCommand { + static get description () { + return 'Manage registry hooks' + } + static get name () { return 'hook' } diff --git a/deps/npm/lib/init.js b/deps/npm/lib/init.js index 42b02dfdc6a77d..81c6733885a68e 100644 --- a/deps/npm/lib/init.js +++ b/deps/npm/lib/init.js @@ -4,6 +4,11 @@ const npa = require('npm-package-arg') const BaseCommand = require('./base-command.js') class Init extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Create a package.json file' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'init' @@ -59,7 +64,7 @@ class Init extends BaseCommand { this.npm.log.pause() this.npm.log.disableProgress() const initFile = this.npm.config.get('init-module') - if (!this.npm.flatOptions.yes && !this.npm.flatOptions.force) { + if (!this.npm.config.get('yes') && !this.npm.config.get('force')) { this.npm.output([ 'This utility will walk you through creating a package.json file.', 'It only covers the most common items, and tries to guess sensible defaults.', diff --git a/deps/npm/lib/install-ci-test.js b/deps/npm/lib/install-ci-test.js index c52b5c9e8073f5..871f24b2f32d6a 100644 --- a/deps/npm/lib/install-ci-test.js +++ b/deps/npm/lib/install-ci-test.js @@ -4,6 +4,10 @@ const CI = require('./ci.js') class InstallCITest extends CI { + static get description () { + return 'Install a project with a clean slate and run tests' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'install-ci-test' diff --git a/deps/npm/lib/install-test.js b/deps/npm/lib/install-test.js index 76c6f367dd3c81..d5664119df5ce2 100644 --- a/deps/npm/lib/install-test.js +++ b/deps/npm/lib/install-test.js @@ -4,6 +4,10 @@ const Install = require('./install.js') class InstallTest extends Install { + static get description () { + return 'Install package(s) and run tests' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'install-test' diff --git a/deps/npm/lib/install.js b/deps/npm/lib/install.js index 8df63a219ef741..54ea6d82510512 100644 --- a/deps/npm/lib/install.js +++ b/deps/npm/lib/install.js @@ -11,11 +11,24 @@ const runScript = require('@npmcli/run-script') const BaseCommand = require('./base-command.js') class Install extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Install a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'install' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return [ + 'save', + 'save-exact', + ] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { return [ @@ -28,7 +41,7 @@ class Install extends BaseCommand { '<tarball file>', '<tarball url>', '<git:// url>', - '<github username>/<github project> [--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save]', + '<github username>/<github project>', ] } @@ -98,7 +111,8 @@ class Install extends BaseCommand { async install (args) { // the /path/to/node_modules/.. const globalTop = resolve(this.npm.globalDir, '..') - const { ignoreScripts, global: isGlobalInstall } = this.npm.flatOptions + const ignoreScripts = this.npm.config.get('ignore-scripts') + const isGlobalInstall = this.npm.config.get('global') const where = isGlobalInstall ? globalTop : this.npm.prefix // don't try to install the prefix into itself @@ -122,7 +136,7 @@ class Install extends BaseCommand { add: args, }) if (!args.length && !isGlobalInstall && !ignoreScripts) { - const { scriptShell } = this.npm.flatOptions + const scriptShell = this.npm.config.get('script-shell') || undefined const scripts = [ 'preinstall', 'install', diff --git a/deps/npm/lib/link.js b/deps/npm/lib/link.js index 66f83d9f5b0a77..fe9cfd3a6b254a 100644 --- a/deps/npm/lib/link.js +++ b/deps/npm/lib/link.js @@ -12,6 +12,11 @@ const reifyFinish = require('./utils/reify-finish.js') const BaseCommand = require('./base-command.js') class Link extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Symlink a package folder' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'link' @@ -96,7 +101,13 @@ class Link extends BaseCommand { // npm link should not save=true by default unless you're // using any of --save-dev or other types const save = - Boolean(this.npm.config.find('save') !== 'default' || this.npm.flatOptions.saveType) + Boolean( + this.npm.config.find('save') !== 'default' || + this.npm.config.get('save-optional') || + this.npm.config.get('save-peer') || + this.npm.config.get('save-dev') || + this.npm.config.get('save-prod') + ) // create a new arborist instance for the local prefix and // reify all the pending names as symlinks there diff --git a/deps/npm/lib/logout.js b/deps/npm/lib/logout.js index b3f64f671d3266..adc19a923af9ab 100644 --- a/deps/npm/lib/logout.js +++ b/deps/npm/lib/logout.js @@ -4,14 +4,22 @@ const npmFetch = require('npm-registry-fetch') const BaseCommand = require('./base-command.js') class Logout extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Log out of the registry' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'logout' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get usage () { - return ['[--registry=<url>] [--scope=<@scope>]'] + static get params () { + return [ + 'registry', + 'scope', + ] } exec (args, cb) { @@ -19,9 +27,10 @@ class Logout extends BaseCommand { } async logout (args) { - const { registry, scope } = this.npm.flatOptions + const registry = this.npm.config.get('registry') + const scope = this.npm.config.get('scope') const regRef = scope ? `${scope}:registry` : 'registry' - const reg = this.npm.flatOptions[regRef] || registry + const reg = this.npm.config.get(regRef) || registry const auth = getAuth(reg, this.npm.flatOptions) diff --git a/deps/npm/lib/ls.js b/deps/npm/lib/ls.js index 9ff2761c2f9285..65b3ddfe7611b7 100644 --- a/deps/npm/lib/ls.js +++ b/deps/npm/lib/ls.js @@ -23,6 +23,11 @@ const _type = Symbol('type') const BaseCommand = require('./base-command.js') class LS extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'List installed packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'ls' @@ -43,24 +48,22 @@ class LS extends BaseCommand { } async ls (args) { - const { - all, - color, - depth, - json, - long, - global, - parseable, - prefix, - unicode, - } = this.npm.flatOptions - const path = global ? resolve(this.npm.globalDir, '..') : prefix + const all = this.npm.config.get('all') + const color = !!this.npm.color + const depth = this.npm.config.get('depth') const dev = this.npm.config.get('dev') const development = this.npm.config.get('development') + const global = this.npm.config.get('global') + const json = this.npm.config.get('json') const link = this.npm.config.get('link') + const long = this.npm.config.get('long') const only = this.npm.config.get('only') + const parseable = this.npm.config.get('parseable') const prod = this.npm.config.get('prod') const production = this.npm.config.get('production') + const unicode = this.npm.config.get('unicode') + + const path = global ? resolve(this.npm.globalDir, '..') : this.npm.prefix const arb = new Arborist({ global, diff --git a/deps/npm/lib/npm.js b/deps/npm/lib/npm.js index 0534e630606e45..42541a90ffb793 100644 --- a/deps/npm/lib/npm.js +++ b/deps/npm/lib/npm.js @@ -36,24 +36,19 @@ const proxyCmds = new Proxy({}, { }, }) -const { types, defaults, shorthands } = require('./utils/config.js') +const { definitions, flatten, shorthands } = require('./utils/config/index.js') const { shellouts } = require('./utils/cmd-list.js') +const usage = require('./utils/npm-usage.js') let warnedNonDashArg = false const _runCmd = Symbol('_runCmd') const _load = Symbol('_load') -const _flatOptions = Symbol('_flatOptions') const _tmpFolder = Symbol('_tmpFolder') const _title = Symbol('_title') const npm = module.exports = new class extends EventEmitter { constructor () { super() require('./utils/perf.js') - this.modes = { - exec: 0o755, - file: 0o644, - umask: 0o22, - } this.started = Date.now() this.command = null this.commands = proxyCmds @@ -62,8 +57,8 @@ const npm = module.exports = new class extends EventEmitter { this.version = require('../package.json').version this.config = new Config({ npmPath: dirname(__dirname), - types, - defaults, + definitions, + flatten, shorthands, }) this[_title] = process.title @@ -105,9 +100,18 @@ const npm = module.exports = new class extends EventEmitter { }) } + const workspacesEnabled = this.config.get('workspaces') + const workspacesFilters = this.config.get('workspace') + const filterByWorkspaces = workspacesEnabled || workspacesFilters.length > 0 + if (this.config.get('usage')) { - console.log(impl.usage) + this.output(impl.usage) cb() + } else if (filterByWorkspaces) { + impl.execWorkspaces(args, this.config.get('workspace'), er => { + process.emit('timeEnd', `command:${cmd}`) + cb(er) + }) } else { impl.exec(args, er => { process.emit('timeEnd', `command:${cmd}`) @@ -140,9 +144,6 @@ const npm = module.exports = new class extends EventEmitter { if (!er && this.config.get('force')) this.log.warn('using --force', 'Recommended protections disabled.') - if (!er && !this[_flatOptions]) - this[_flatOptions] = require('./utils/flat-options.js')(this) - process.emit('timeEnd', 'npm:load') this.emit('load', er) }) @@ -162,14 +163,19 @@ const npm = module.exports = new class extends EventEmitter { } async [_load] () { + process.emit('time', 'npm:load:whichnode') const node = await which(process.argv[0]).catch(er => null) + process.emit('timeEnd', 'npm:load:whichnode') if (node && node.toUpperCase() !== process.execPath.toUpperCase()) { log.verbose('node symlink', node) process.execPath = node this.config.execPath = node } + process.emit('time', 'npm:load:configload') await this.config.load() + process.emit('timeEnd', 'npm:load:configload') + this.argv = this.config.parsedArgv.remain // note: this MUST be shorter than the actual argv length, because it // uses the same memory, so node will truncate it if it's too long. @@ -177,33 +183,40 @@ const npm = module.exports = new class extends EventEmitter { // don't show that. (Regrettable historical choice to put it there.) // Any other secrets are configs only, so showing only the positional // args keeps those from being leaked. + process.emit('time', 'npm:load:setTitle') const tokrev = deref(this.argv[0]) === 'token' && this.argv[1] === 'revoke' this.title = tokrev ? 'npm token revoke' + (this.argv[2] ? ' ***' : '') : ['npm', ...this.argv].join(' ') + process.emit('timeEnd', 'npm:load:setTitle') + process.emit('time', 'npm:load:setupLog') this.color = setupLog(this.config) + process.emit('timeEnd', 'npm:load:setupLog') process.env.COLOR = this.color ? '1' : '0' + process.emit('time', 'npm:load:cleanupLog') cleanUpLogFiles(this.cache, this.config.get('logs-max'), log.warn) + process.emit('timeEnd', 'npm:load:cleanupLog') log.resume() - const umask = this.config.get('umask') - this.modes = { - exec: 0o777 & (~umask), - file: 0o666 & (~umask), - umask, - } + process.emit('time', 'npm:load:configScope') const configScope = this.config.get('scope') if (configScope && !/^@/.test(configScope)) this.config.set('scope', `@${configScope}`, this.config.find('scope')) + process.emit('timeEnd', 'npm:load:configScope') + process.emit('time', 'npm:load:projectScope') this.projectScope = this.config.get('scope') || getProjectScope(this.prefix) + process.emit('timeEnd', 'npm:load:projectScope') } get flatOptions () { - return this[_flatOptions] + const { flat } = this.config + if (this.command) + flat.npmCommand = this.command + return flat } get lockfileVersion () { @@ -274,6 +287,10 @@ const npm = module.exports = new class extends EventEmitter { this[k] = r } + get usage () { + return usage(this) + } + // XXX add logging to see if we actually use this get tmp () { if (!this[_tmpFolder]) { diff --git a/deps/npm/lib/org.js b/deps/npm/lib/org.js index b9f84b060f8a83..ddd2b03daee185 100644 --- a/deps/npm/lib/org.js +++ b/deps/npm/lib/org.js @@ -4,6 +4,10 @@ const Table = require('cli-table3') const BaseCommand = require('./base-command.js') class Org extends BaseCommand { + static get description () { + return 'Manage orgs' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'org' diff --git a/deps/npm/lib/outdated.js b/deps/npm/lib/outdated.js index 7225577ea42d47..9b656d2aeede47 100644 --- a/deps/npm/lib/outdated.js +++ b/deps/npm/lib/outdated.js @@ -13,6 +13,11 @@ const ansiTrim = require('./utils/ansi-trim.js') const BaseCommand = require('./base-command.js') class Outdated extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Check for outdated packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'outdated' @@ -28,15 +33,13 @@ class Outdated extends BaseCommand { } async outdated (args) { - this.opts = this.npm.flatOptions - const global = path.resolve(this.npm.globalDir, '..') - const where = this.opts.global + const where = this.npm.config.get('global') ? global : this.npm.prefix const arb = new Arborist({ - ...this.opts, + ...this.npm.flatOptions, path: where, }) @@ -51,7 +54,7 @@ class Outdated extends BaseCommand { this.getEdges(nodes, 'edgesIn') } } else { - if (this.opts.all) { + if (this.npm.config.get('all')) { // all deps in tree const nodes = this.tree.inventory.values() this.getEdges(nodes, 'edgesOut') @@ -68,13 +71,13 @@ class Outdated extends BaseCommand { const outdated = this.list.sort((a, b) => a.name.localeCompare(b.name)) // return if no outdated packages - if (outdated.length === 0 && !this.opts.json) + if (outdated.length === 0 && !this.npm.config.get('json')) return // display results - if (this.opts.json) + if (this.npm.config.get('json')) this.npm.output(this.makeJSON(outdated)) - else if (this.opts.parseable) + else if (this.npm.config.get('parseable')) this.npm.output(this.makeParseable(outdated)) else { const outList = outdated.map(x => this.makePretty(x)) @@ -86,11 +89,11 @@ class Outdated extends BaseCommand { 'Depended by', ] - if (this.opts.long) + if (this.npm.config.get('long')) outHead.push('Package Type', 'Homepage') const outTable = [outHead].concat(outList) - if (this.opts.color) + if (this.npm.color) outTable[0] = outTable[0].map(heading => styles.underline(heading)) const tableOpts = { @@ -117,7 +120,7 @@ class Outdated extends BaseCommand { } getEdgesOut (node) { - if (this.opts.global) { + if (this.npm.config.get('global')) { for (const child of node.children.values()) this.edges.add(child) } else { @@ -129,7 +132,7 @@ class Outdated extends BaseCommand { async getPackument (spec) { const packument = await pacote.packument(spec, { ...this.npm.flatOptions, - fullMetadata: this.npm.flatOptions.long, + fullMetadata: this.npm.config.get('long'), preferOnline: true, }) return packument @@ -146,7 +149,7 @@ class Outdated extends BaseCommand { : edge.dev ? 'devDependencies' : 'dependencies' - for (const omitType of this.opts.omit || []) { + for (const omitType of this.npm.config.get('omit') || []) { if (node[omitType]) return } @@ -213,12 +216,12 @@ class Outdated extends BaseCommand { const columns = [name, current, wanted, latest, location, dependent] - if (this.opts.long) { + if (this.npm.config.get('long')) { columns[6] = type columns[7] = homepage } - if (this.opts.color) { + if (this.npm.color) { columns[0] = color[current === wanted ? 'yellow' : 'red'](columns[0]) // current columns[2] = color.green(columns[2]) // wanted columns[3] = color.magenta(columns[3]) // latest @@ -248,7 +251,7 @@ class Outdated extends BaseCommand { name + '@' + latest, dependent, ] - if (this.opts.long) + if (this.npm.config.get('long')) out.push(type, homepage) return out.join(':') @@ -275,7 +278,7 @@ class Outdated extends BaseCommand { dependent, location: path, } - if (this.opts.long) { + if (this.npm.config.get('long')) { out[name].type = type out[name].homepage = homepage } diff --git a/deps/npm/lib/owner.js b/deps/npm/lib/owner.js index b62f125ac3bb68..e537d82d01d8db 100644 --- a/deps/npm/lib/owner.js +++ b/deps/npm/lib/owner.js @@ -8,6 +8,10 @@ const readLocalPkg = require('./utils/read-local-package.js') const BaseCommand = require('./base-command.js') class Owner extends BaseCommand { + static get description () { + return 'Manage package owners' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'owner' diff --git a/deps/npm/lib/pack.js b/deps/npm/lib/pack.js index 326fcc0cd1441f..92a2fbd7ac33ce 100644 --- a/deps/npm/lib/pack.js +++ b/deps/npm/lib/pack.js @@ -11,14 +11,24 @@ const writeFile = util.promisify(require('fs').writeFile) const BaseCommand = require('./base-command.js') class Pack extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Create a tarball from a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'pack' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return ['dry-run'] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[[<@scope>/]<pkg>...] [--dry-run]'] + return ['[[<@scope>/]<pkg>...]'] } exec (args, cb) { @@ -29,12 +39,12 @@ class Pack extends BaseCommand { if (args.length === 0) args = ['.'] - const { unicode } = this.npm.flatOptions + const unicode = this.npm.config.get('unicode') // clone the opts because pacote mutates it with resolved/integrity const tarballs = await Promise.all(args.map(async (arg) => { const spec = npa(arg) - const { dryRun } = this.npm.flatOptions + const dryRun = this.npm.config.get('dry-run') const manifest = await pacote.manifest(spec, this.npm.flatOptions) const filename = `${manifest.name}-${manifest.version}.tgz` .replace(/^@/, '').replace(/\//, '-') diff --git a/deps/npm/lib/ping.js b/deps/npm/lib/ping.js index e60b1f1debd80c..30379377482ec1 100644 --- a/deps/npm/lib/ping.js +++ b/deps/npm/lib/ping.js @@ -4,13 +4,18 @@ const BaseCommand = require('./base-command.js') class Ping extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get name () { - return 'ping' + static get description () { + return 'Ping npm registry' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get description () { - return 'ping registry' + static get params () { + return ['registry'] + } + + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get name () { + return 'ping' } exec (args, cb) { @@ -18,14 +23,14 @@ class Ping extends BaseCommand { } async ping (args) { - log.notice('PING', this.npm.flatOptions.registry) + log.notice('PING', this.npm.config.get('registry')) const start = Date.now() const details = await pingUtil(this.npm.flatOptions) const time = Date.now() - start log.notice('PONG', `${time / 1000}ms`) - if (this.npm.flatOptions.json) { + if (this.npm.config.get('json')) { this.npm.output(JSON.stringify({ - registry: this.npm.flatOptions.registry, + registry: this.npm.config.get('registry'), time, details, }, null, 2)) diff --git a/deps/npm/lib/prefix.js b/deps/npm/lib/prefix.js index 5ade87f6429f63..1298a3c5ba54bd 100644 --- a/deps/npm/lib/prefix.js +++ b/deps/npm/lib/prefix.js @@ -1,6 +1,11 @@ const BaseCommand = require('./base-command.js') class Prefix extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Display prefix' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'prefix' diff --git a/deps/npm/lib/profile.js b/deps/npm/lib/profile.js index 1c0df498885406..9789146fdee169 100644 --- a/deps/npm/lib/profile.js +++ b/deps/npm/lib/profile.js @@ -38,6 +38,10 @@ const writableProfileKeys = [ const BaseCommand = require('./base-command.js') class Profile extends BaseCommand { + static get description () { + return 'Change settings on your registry profile' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'profile' @@ -108,14 +112,14 @@ class Profile extends BaseCommand { async get (args) { const tfa = 'two-factor auth' - const conf = { ...this.npm.flatOptions } - - const info = await pulseTillDone.withPromise(npmProfile.get(conf)) + const info = await pulseTillDone.withPromise( + npmProfile.get(this.npm.flatOptions) + ) if (!info.cidr_whitelist) delete info.cidr_whitelist - if (conf.json) { + if (this.npm.config.get('json')) { this.npm.output(JSON.stringify(info, null, 2)) return } @@ -147,7 +151,7 @@ class Profile extends BaseCommand { .join('\t') this.npm.output(values) } else { - if (conf.parseable) { + if (this.npm.config.get('parseable')) { for (const key of Object.keys(info)) { if (key === 'tfa') this.npm.output(`${key}\t${cleaned[tfa]}`) @@ -165,7 +169,7 @@ class Profile extends BaseCommand { } async set (args) { - const conf = { ...this.npm.flatOptions } + const conf = this.npm.flatOptions const prop = (args[0] || '').toLowerCase().trim() let value = args.length > 1 ? args.slice(1).join(' ') : null @@ -214,9 +218,9 @@ class Profile extends BaseCommand { const result = await otplease(conf, conf => npmProfile.set(newUser, conf)) - if (conf.json) + if (this.npm.config.get('json')) this.npm.output(JSON.stringify({ [prop]: result[prop] }, null, 2)) - else if (conf.parseable) + else if (this.npm.config.get('parseable')) this.npm.output(prop + '\t' + result[prop]) else if (result[prop] != null) this.npm.output('Set', prop, 'to', result[prop]) @@ -239,11 +243,10 @@ class Profile extends BaseCommand { ) } - const conf = { ...this.npm.flatOptions } - if (conf.json || conf.parseable) { + if (this.npm.config.get('json') || this.npm.config.get('parseable')) { throw new Error( 'Enabling two-factor authentication is an interactive operation and ' + - (conf.json ? 'JSON' : 'parseable') + ' output mode is not available' + (this.npm.config.get('json') ? 'JSON' : 'parseable') + ' output mode is not available' ) } @@ -255,7 +258,7 @@ class Profile extends BaseCommand { // if they're using legacy auth currently then we have to // update them to a bearer token before continuing. - const creds = this.npm.config.getCredentialsByURI(conf.registry) + const creds = this.npm.config.getCredentialsByURI(this.npm.config.get('registry')) const auth = {} if (creds.token) @@ -267,32 +270,29 @@ class Profile extends BaseCommand { auth.basic = { username: basic[0], password: basic[1] } } - if (conf.otp) - auth.otp = conf.otp - if (!auth.basic && !auth.token) { throw new Error( 'You need to be logged in to registry ' + - `${conf.registry} in order to enable 2fa` + `${this.npm.config.get('registry')} in order to enable 2fa` ) } if (auth.basic) { log.info('profile', 'Updating authentication to bearer token') const result = await npmProfile.createToken( - auth.basic.password, false, [], conf + auth.basic.password, false, [], this.npm.flatOptions ) if (!result.token) { throw new Error( - `Your registry ${conf.registry} does not seem to ` + + `Your registry ${this.npm.config.get('registry')} does not seem to ` + 'support bearer tokens. Bearer tokens are required for ' + 'two-factor authentication' ) } this.npm.config.setCredentialsByURI( - conf.registry, + this.npm.config.get('registry'), { token: result.token } ) await this.npm.config.save('user') @@ -303,21 +303,21 @@ class Profile extends BaseCommand { info.tfa.password = password log.info('profile', 'Determine if tfa is pending') - const userInfo = await pulseTillDone.withPromise(npmProfile.get(conf)) + const userInfo = await pulseTillDone.withPromise( + npmProfile.get(this.npm.flatOptions) + ) + const conf = { ...this.npm.flatOptions } if (userInfo && userInfo.tfa && userInfo.tfa.pending) { log.info('profile', 'Resetting two-factor authentication') await pulseTillDone.withPromise( npmProfile.set({ tfa: { password, mode: 'disable' } }, conf) ) } else if (userInfo && userInfo.tfa) { - if (conf.otp) - conf.otp = conf.otp - else { - const otp = await readUserInfo.otp( + if (!conf.otp) { + conf.otp = await readUserInfo.otp( 'Enter one-time password from your authenticator app: ' ) - conf.otp = otp } } @@ -390,9 +390,9 @@ class Profile extends BaseCommand { tfa: { password: password, mode: 'disable' }, }, conf)) - if (conf.json) + if (this.npm.config.get('json')) this.npm.output(JSON.stringify({ tfa: false }, null, 2)) - else if (conf.parseable) + else if (this.npm.config.get('parseable')) this.npm.output('tfa\tfalse') else this.npm.output('Two factor authentication disabled.') diff --git a/deps/npm/lib/prune.js b/deps/npm/lib/prune.js index c2cddb1a22b335..1da86a3e821878 100644 --- a/deps/npm/lib/prune.js +++ b/deps/npm/lib/prune.js @@ -4,14 +4,24 @@ const reifyFinish = require('./utils/reify-finish.js') const BaseCommand = require('./base-command.js') class Prune extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Remove extraneous packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'prune' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return ['production'] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[[<@scope>/]<pkg>...] [--production]'] + return ['[[<@scope>/]<pkg>...]'] } exec (args, cb) { diff --git a/deps/npm/lib/publish.js b/deps/npm/lib/publish.js index f8e0eafe118864..b121cb3d36773a 100644 --- a/deps/npm/lib/publish.js +++ b/deps/npm/lib/publish.js @@ -8,7 +8,7 @@ const pacote = require('pacote') const npa = require('npm-package-arg') const npmFetch = require('npm-registry-fetch') -const { flatten } = require('./utils/flat-options.js') +const flatten = require('./utils/config/flatten.js') const otplease = require('./utils/otplease.js') const { getContents, logTar } = require('./utils/tar.js') @@ -19,15 +19,24 @@ const readJson = util.promisify(require('read-package-json')) const BaseCommand = require('./base-command.js') class Publish extends BaseCommand { + static get description () { + return 'Publish a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'publish' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return ['tag', 'access', 'dry-run'] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { return [ - '[<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run]', + '[<folder>]', ] } @@ -43,12 +52,16 @@ class Publish extends BaseCommand { log.verbose('publish', args) - const opts = { ...this.npm.flatOptions } - const { unicode, dryRun, json, defaultTag } = opts + const unicode = this.npm.config.get('unicode') + const dryRun = this.npm.config.get('dry-run') + const json = this.npm.config.get('json') + const defaultTag = this.npm.config.get('tag') if (semver.validRange(defaultTag)) throw new Error('Tag name must not be a valid SemVer range: ' + defaultTag.trim()) + const opts = { ...this.npm.flatOptions } + // you can publish name@version, ./foo.tgz, etc. // even though the default is the 'file:.' cwd. const spec = npa(args[0]) @@ -137,7 +150,8 @@ class Publish extends BaseCommand { publishConfigToOpts (publishConfig) { // create a new object that inherits from the config stack // then squash the css-case into camelCase opts, like we do - return flatten({...this.npm.config.list[0], ...publishConfig}) + // this is Object.assign()'ed onto the base npm.flatOptions + return flatten(publishConfig, {}) } } module.exports = Publish diff --git a/deps/npm/lib/rebuild.js b/deps/npm/lib/rebuild.js index 74f5ae5f6eba59..5910ab3d172dcd 100644 --- a/deps/npm/lib/rebuild.js +++ b/deps/npm/lib/rebuild.js @@ -6,6 +6,11 @@ const completion = require('./utils/completion/installed-deep.js') const BaseCommand = require('./base-command.js') class Rebuild extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Rebuild a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'rebuild' @@ -27,7 +32,7 @@ class Rebuild extends BaseCommand { async rebuild (args) { const globalTop = resolve(this.npm.globalDir, '..') - const where = this.npm.flatOptions.global ? globalTop : this.npm.prefix + const where = this.npm.config.get('global') ? globalTop : this.npm.prefix const arb = new Arborist({ ...this.npm.flatOptions, path: where, diff --git a/deps/npm/lib/repo.js b/deps/npm/lib/repo.js index aa07e07a819f74..5ab136abd73d8e 100644 --- a/deps/npm/lib/repo.js +++ b/deps/npm/lib/repo.js @@ -7,6 +7,11 @@ const openUrl = require('./utils/open-url.js') const BaseCommand = require('./base-command.js') class Repo extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Open package repository page in the browser' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'repo' diff --git a/deps/npm/lib/restart.js b/deps/npm/lib/restart.js index 1f3eb5af94f825..840eb20673b1ff 100644 --- a/deps/npm/lib/restart.js +++ b/deps/npm/lib/restart.js @@ -2,6 +2,11 @@ const LifecycleCmd = require('./utils/lifecycle-cmd.js') // This ends up calling run-script(['restart', ...args]) class Restart extends LifecycleCmd { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Restart a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'restart' diff --git a/deps/npm/lib/root.js b/deps/npm/lib/root.js index 1fe82c6fad773a..635a68e2563184 100644 --- a/deps/npm/lib/root.js +++ b/deps/npm/lib/root.js @@ -1,13 +1,18 @@ const BaseCommand = require('./base-command.js') class Root extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Display npm root' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'root' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get usage () { - return ['[-g]'] + static get params () { + return ['global'] } exec (args, cb) { diff --git a/deps/npm/lib/run-script.js b/deps/npm/lib/run-script.js index 3ea85b79ffd18f..054f0ae4a551f4 100644 --- a/deps/npm/lib/run-script.js +++ b/deps/npm/lib/run-script.js @@ -1,10 +1,12 @@ +const { resolve } = require('path') +const chalk = require('chalk') const runScript = require('@npmcli/run-script') const { isServerPackage } = runScript -const readJson = require('read-package-json-fast') -const { resolve } = require('path') +const rpj = require('read-package-json-fast') const log = require('npmlog') const didYouMean = require('./utils/did-you-mean.js') const isWindowsShell = require('./utils/is-windows-shell.js') +const getWorkspaces = require('./workspaces/get-workspaces.js') const cmdList = [ 'publish', @@ -17,8 +19,21 @@ const cmdList = [ 'version', ].reduce((l, p) => l.concat(['pre' + p, p, 'post' + p]), []) +const nocolor = { + reset: s => s, + bold: s => s, + dim: s => s, + blue: s => s, + green: s => s, +} + const BaseCommand = require('./base-command.js') class RunScript extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Run arbitrary package scripts' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'run-script' @@ -34,7 +49,7 @@ class RunScript extends BaseCommand { if (argv.length === 2) { // find the script name const json = resolve(this.npm.localPrefix, 'package.json') - const { scripts = {} } = await readJson(json).catch(er => ({})) + const { scripts = {} } = await rpj(json).catch(er => ({})) return Object.keys(scripts) } } @@ -46,12 +61,19 @@ class RunScript extends BaseCommand { this.list(args).then(() => cb()).catch(cb) } - async run (args) { - const path = this.npm.localPrefix - const event = args.shift() - const { scriptShell } = this.npm.flatOptions + execWorkspaces (args, filters, cb) { + if (args.length) + this.runWorkspaces(args, filters).then(() => cb()).catch(cb) + else + this.listWorkspaces(args, filters).then(() => cb()).catch(cb) + } + + async run ([event, ...args], { path = this.npm.localPrefix, pkg } = {}) { + // this || undefined is because runScript will be unhappy with the default + // null value + const scriptShell = this.npm.config.get('script-shell') || undefined - const pkg = await readJson(`${path}/package.json`) + pkg = pkg || (await rpj(`${path}/package.json`)) const { scripts = {} } = pkg if (event === 'restart' && !scripts.restart) @@ -68,14 +90,13 @@ class RunScript extends BaseCommand { if (this.npm.config.get('if-present')) return - const suggestions = didYouMean(event, Object.keys(scripts)) - throw new Error(`missing script: ${event}${ - suggestions ? `\n${suggestions}` : ''}`) + const suggestions = await didYouMean(this.npm, path, event) + throw new Error(`Missing script: "${event}"${suggestions}\n\nTo see a list of scripts, run:\n npm run`) } // positional args only added to the main event, not pre/post const events = [[event, args]] - if (!this.npm.flatOptions.ignoreScripts) { + if (!this.npm.config.get('ignore-scripts')) { if (scripts[`pre${event}`]) events.unshift([`pre${event}`, []]) @@ -102,9 +123,11 @@ class RunScript extends BaseCommand { } } - async list () { - const path = this.npm.localPrefix - const { scripts, name } = await readJson(`${path}/package.json`) + async list (args, path) { + path = path || this.npm.localPrefix + const { scripts, name, _id } = await rpj(`${path}/package.json`) + const pkgid = _id || name + const color = !!this.npm.color if (!scripts) return [] @@ -113,12 +136,12 @@ class RunScript extends BaseCommand { if (log.level === 'silent') return allScripts - if (this.npm.flatOptions.json) { + if (this.npm.config.get('json')) { this.npm.output(JSON.stringify(scripts, null, 2)) return allScripts } - if (this.npm.flatOptions.parseable) { + if (this.npm.config.get('parseable')) { for (const [script, cmd] of Object.entries(scripts)) this.npm.output(`${script}:${cmd}`) @@ -133,22 +156,96 @@ class RunScript extends BaseCommand { const list = cmdList.includes(script) ? cmds : runScripts list.push(script) } + const colorize = color ? chalk : nocolor - if (cmds.length) - this.npm.output(`Lifecycle scripts included in ${name}:`) + if (cmds.length) { + this.npm.output(`${ + colorize.reset(colorize.bold('Lifecycle scripts'))} included in ${ + colorize.green(pkgid)}:`) + } for (const script of cmds) - this.npm.output(prefix + script + indent + scripts[script]) + this.npm.output(prefix + script + indent + colorize.dim(scripts[script])) - if (!cmds.length && runScripts.length) - this.npm.output(`Scripts available in ${name} via \`npm run-script\`:`) - else if (runScripts.length) - this.npm.output('\navailable via `npm run-script`:') + if (!cmds.length && runScripts.length) { + this.npm.output(`${ + colorize.bold('Scripts') + } available in ${colorize.green(pkgid)} via \`${ + colorize.blue('npm run-script')}\`:`) + } else if (runScripts.length) + this.npm.output(`\navailable via \`${colorize.blue('npm run-script')}\`:`) for (const script of runScripts) - this.npm.output(prefix + script + indent + scripts[script]) + this.npm.output(prefix + script + indent + colorize.dim(scripts[script])) + this.npm.output('') return allScripts } + + async workspaces (filters) { + return getWorkspaces(filters, { path: this.npm.localPrefix }) + } + + async runWorkspaces (args, filters) { + const res = [] + const workspaces = await this.workspaces(filters) + + for (const workspacePath of workspaces.values()) { + const pkg = await rpj(`${workspacePath}/package.json`) + const runResult = await this.run(args, { + path: workspacePath, + pkg, + }).catch(err => { + log.error(`Lifecycle script \`${args[0]}\` failed with error:`) + log.error(err) + log.error(` in workspace: ${pkg._id || pkg.name}`) + log.error(` at location: ${workspacePath}`) + + const scriptMissing = err.message.startsWith('Missing script') + + // avoids exiting with error code in case there's scripts missing + // in some workspaces since other scripts might have succeeded + if (!scriptMissing) + process.exitCode = 1 + + return scriptMissing + }) + res.push(runResult) + } + + // in case **all** tests are missing, then it should exit with error code + if (res.every(Boolean)) + throw new Error(`Missing script: ${args[0]}`) + } + + async listWorkspaces (args, filters) { + const workspaces = await this.workspaces(filters) + + if (log.level === 'silent') + return + + if (this.npm.config.get('json')) { + const res = {} + for (const workspacePath of workspaces.values()) { + const { scripts, name } = await rpj(`${workspacePath}/package.json`) + res[name] = { ...scripts } + } + this.npm.output(JSON.stringify(res, null, 2)) + return + } + + if (this.npm.config.get('parseable')) { + for (const workspacePath of workspaces.values()) { + const { scripts, name } = await rpj(`${workspacePath}/package.json`) + for (const [script, cmd] of Object.entries(scripts || {})) + this.npm.output(`${name}:${script}:${cmd}`) + } + return + } + + for (const workspacePath of workspaces.values()) + await this.list(args, workspacePath) + } } + module.exports = RunScript diff --git a/deps/npm/lib/search.js b/deps/npm/lib/search.js index c24000156f67a7..bdd374ab6edf77 100644 --- a/deps/npm/lib/search.js +++ b/deps/npm/lib/search.js @@ -26,14 +26,29 @@ function prepareExcludes (searchexclude) { const BaseCommand = require('./base-command.js') class Search extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Search for pacakges' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'search' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return [ + 'long', + 'json', + 'parseable', + 'description', + ] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[-l|--long] [--json] [--parseable] [--no-description] [search terms ...]'] + return ['[search terms ...]'] } exec (args, cb) { @@ -84,7 +99,7 @@ class Search extends BaseCommand { }) await p.promise() - if (!anyOutput && !opts.json && !opts.parseable) + if (!anyOutput && !this.npm.config.get('json') && !this.npm.config.get('parseable')) this.npm.output('No matches found for ' + (args.map(JSON.stringify).join(' '))) log.silly('search', 'search completed') diff --git a/deps/npm/lib/set-script.js b/deps/npm/lib/set-script.js index 6241981323c4af..df101a0acb7090 100644 --- a/deps/npm/lib/set-script.js +++ b/deps/npm/lib/set-script.js @@ -5,6 +5,11 @@ const rpj = require('read-package-json-fast') const BaseCommand = require('./base-command.js') class SetScript extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Set tasks in the scripts section of package.json' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'set-script' diff --git a/deps/npm/lib/set.js b/deps/npm/lib/set.js index 787a8012c0b6a2..74a002cd638be2 100644 --- a/deps/npm/lib/set.js +++ b/deps/npm/lib/set.js @@ -1,6 +1,10 @@ const BaseCommand = require('./base-command.js') class Set extends BaseCommand { + static get description () { + return 'Set a value in the npm configuration' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'set' diff --git a/deps/npm/lib/shrinkwrap.js b/deps/npm/lib/shrinkwrap.js index b52cf41957b2d7..5d4a1ada982a49 100644 --- a/deps/npm/lib/shrinkwrap.js +++ b/deps/npm/lib/shrinkwrap.js @@ -7,6 +7,11 @@ const log = require('npmlog') const BaseCommand = require('./base-command.js') class Shrinkwrap extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Lock down dependency versions for publication' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'shrinkwrap' @@ -24,7 +29,7 @@ class Shrinkwrap extends BaseCommand { // // loadVirtual, fall back to loadActual // rename shrinkwrap file type, and tree.meta.save() - if (this.npm.flatOptions.global) { + if (this.npm.config.get('global')) { const er = new Error('`npm shrinkwrap` does not work for global packages') er.code = 'ESHRINKWRAPGLOBAL' throw er diff --git a/deps/npm/lib/star.js b/deps/npm/lib/star.js index 27a3041906a401..4c5cf4900c5198 100644 --- a/deps/npm/lib/star.js +++ b/deps/npm/lib/star.js @@ -6,6 +6,10 @@ const getIdentity = require('./utils/get-identity') const BaseCommand = require('./base-command.js') class Star extends BaseCommand { + static get description () { + return 'Mark your favorite packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'star' @@ -26,7 +30,7 @@ class Star extends BaseCommand { // if we're unstarring, then show an empty star image // otherwise, show the full star image - const { unicode } = this.npm.flatOptions + const unicode = this.npm.config.get('unicode') const unstar = this.npm.config.get('star.unstar') const full = unicode ? '\u2605 ' : '(*)' const empty = unicode ? '\u2606 ' : '( )' diff --git a/deps/npm/lib/stars.js b/deps/npm/lib/stars.js index 758a3130d423c0..db93d7b19610a5 100644 --- a/deps/npm/lib/stars.js +++ b/deps/npm/lib/stars.js @@ -5,6 +5,11 @@ const getIdentity = require('./utils/get-identity.js') const BaseCommand = require('./base-command.js') class Stars extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'View packages marked as favorites' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'stars' diff --git a/deps/npm/lib/start.js b/deps/npm/lib/start.js index 8987bc2931eb55..099b6e61b31fcd 100644 --- a/deps/npm/lib/start.js +++ b/deps/npm/lib/start.js @@ -2,6 +2,11 @@ const LifecycleCmd = require('./utils/lifecycle-cmd.js') // This ends up calling run-script(['start', ...args]) class Start extends LifecycleCmd { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Start a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'start' diff --git a/deps/npm/lib/stop.js b/deps/npm/lib/stop.js index a3857ab1360be2..766d9c01815cd4 100644 --- a/deps/npm/lib/stop.js +++ b/deps/npm/lib/stop.js @@ -2,6 +2,11 @@ const LifecycleCmd = require('./utils/lifecycle-cmd.js') // This ends up calling run-script(['stop', ...args]) class Stop extends LifecycleCmd { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Stop a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'stop' diff --git a/deps/npm/lib/team.js b/deps/npm/lib/team.js index f84660af4d19a6..5298bb3b2563e2 100644 --- a/deps/npm/lib/team.js +++ b/deps/npm/lib/team.js @@ -5,6 +5,10 @@ const otplease = require('./utils/otplease.js') const BaseCommand = require('./base-command.js') class Team extends BaseCommand { + static get description () { + return 'Manage organization teams and team memberships' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'team' diff --git a/deps/npm/lib/test.js b/deps/npm/lib/test.js index 991d1c873cbfbb..2be2b54af719e9 100644 --- a/deps/npm/lib/test.js +++ b/deps/npm/lib/test.js @@ -2,6 +2,11 @@ const LifecycleCmd = require('./utils/lifecycle-cmd.js') // This ends up calling run-script(['test', ...args]) class Test extends LifecycleCmd { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Test a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'test' diff --git a/deps/npm/lib/token.js b/deps/npm/lib/token.js index 3d7952ccfc292f..a80988531eebf8 100644 --- a/deps/npm/lib/token.js +++ b/deps/npm/lib/token.js @@ -10,6 +10,10 @@ const readUserInfo = require('./utils/read-user-info.js') const BaseCommand = require('./base-command.js') class Token extends BaseCommand { + static get description () { + return 'Manage your authentication tokens' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'token' diff --git a/deps/npm/lib/uninstall.js b/deps/npm/lib/uninstall.js index ee0f338e9fc0a8..11e65533a8e98d 100644 --- a/deps/npm/lib/uninstall.js +++ b/deps/npm/lib/uninstall.js @@ -7,14 +7,23 @@ const completion = require('./utils/completion/installed-shallow.js') const BaseCommand = require('./base-command.js') class Uninstall extends BaseCommand { + static get description () { + return 'Remove a package' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'uninstall' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return ['save'] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[<@scope>/]<pkg>[@<version>]... [-S|--save|--no-save]'] + return ['[<@scope>/]<pkg>...'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -28,7 +37,8 @@ class Uninstall extends BaseCommand { async uninstall (args) { // the /path/to/node_modules/.. - const { global, prefix } = this.npm.flatOptions + const global = this.npm.config.get('global') + const prefix = this.npm.config.get('prefix') const path = global ? resolve(this.npm.globalDir, '..') : prefix if (!args.length) { diff --git a/deps/npm/lib/unpublish.js b/deps/npm/lib/unpublish.js index 68a9a0ae64ee55..d49bb7ba4e3591 100644 --- a/deps/npm/lib/unpublish.js +++ b/deps/npm/lib/unpublish.js @@ -12,6 +12,10 @@ const getIdentity = require('./utils/get-identity.js') const BaseCommand = require('./base-command.js') class Unpublish extends BaseCommand { + static get description () { + return 'Remove a package from the registry' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'unpublish' @@ -63,8 +67,9 @@ class Unpublish extends BaseCommand { throw new Error(this.usage) const spec = args.length && npa(args[0]) - const opts = this.npm.flatOptions - const { force, silent, loglevel } = opts + const force = this.npm.config.get('force') + const silent = this.npm.config.get('silent') + const loglevel = this.npm.config.get('loglevel') let pkgName let pkgVersion @@ -79,6 +84,7 @@ class Unpublish extends BaseCommand { ) } + const opts = this.npm.flatOptions if (!spec || path.resolve(spec.name) === this.npm.localPrefix) { // if there's a package.json in the current folder, then // read the package name and version out of that. diff --git a/deps/npm/lib/unstar.js b/deps/npm/lib/unstar.js index 5786cfce60f73a..bc32ba617b46ab 100644 --- a/deps/npm/lib/unstar.js +++ b/deps/npm/lib/unstar.js @@ -1,6 +1,11 @@ const Star = require('./star.js') class Unstar extends Star { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Remove an item from your favorite packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'unstar' diff --git a/deps/npm/lib/update.js b/deps/npm/lib/update.js index 87540b96e07e54..6a87dd9ecddcfb 100644 --- a/deps/npm/lib/update.js +++ b/deps/npm/lib/update.js @@ -8,14 +8,24 @@ const completion = require('./utils/completion/installed-deep.js') const BaseCommand = require('./base-command.js') class Update extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'Update packages' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'update' } + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get params () { + return ['global'] + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get usage () { - return ['[-g] [<pkg>...]'] + return ['[<pkg>...]'] } /* istanbul ignore next - see test/lib/load-all-commands.js */ @@ -30,11 +40,11 @@ class Update extends BaseCommand { async update (args) { const update = args.length === 0 ? true : args const global = path.resolve(this.npm.globalDir, '..') - const where = this.npm.flatOptions.global + const where = this.npm.config.get('global') ? global : this.npm.prefix - if (this.npm.flatOptions.depth) { + if (this.npm.config.get('depth')) { log.warn('update', 'The --depth option no longer has any effect. See RFC0019.\n' + 'https://github.com/npm/rfcs/blob/latest/implemented/0019-remove-update-depth-option.md') } diff --git a/deps/npm/lib/utils/config.js b/deps/npm/lib/utils/config.js deleted file mode 100644 index 3ca9766132f02f..00000000000000 --- a/deps/npm/lib/utils/config.js +++ /dev/null @@ -1,394 +0,0 @@ -// defaults, types, and shorthands - -const { - typeDefs: { - semver: { type: semver }, - Umask: { type: Umask }, - url: { type: url }, - path: { type: path }, - }, -} = require('@npmcli/config') - -const { version: npmVersion } = require('../../package.json') - -const ciDetect = require('@npmcli/ci-detect') -const ciName = ciDetect() - -const isWindows = require('./is-windows.js') - -const editor = process.env.EDITOR || - process.env.VISUAL || - (isWindows ? 'notepad.exe' : 'vi') - -const shell = isWindows ? process.env.ComSpec || 'cmd' - : process.env.SHELL || 'sh' - -const { tmpdir, networkInterfaces } = require('os') -const getLocalAddresses = () => { - try { - return Object.values(networkInterfaces()).map( - int => int.map(({ address }) => address) - ).reduce((set, addrs) => set.concat(addrs), [undefined]) - } catch (e) { - return [undefined] - } -} - -const unicode = /UTF-?8$/i.test( - process.env.LC_ALL || process.env.LC_CTYPE || process.env.LANG -) - -// use LOCALAPPDATA on Windows, if set -// https://github.com/npm/cli/pull/899 -const cacheRoot = (isWindows && process.env.LOCALAPPDATA) || '~' -const cacheExtra = isWindows ? 'npm-cache' : '.npm' -const cache = `${cacheRoot}/${cacheExtra}` - -const defaults = { - access: null, - all: false, - 'allow-same-version': false, - also: null, - 'always-auth': false, - audit: true, - 'audit-level': null, - _auth: null, - 'auth-type': 'legacy', - before: null, - 'bin-links': true, - browser: null, - ca: null, - cache, - 'cache-lock-retries': 10, - 'cache-lock-stale': 60000, - 'cache-lock-wait': 10000, - 'cache-max': Infinity, - 'cache-min': 10, - cafile: null, - call: '', - cert: null, - 'ci-name': ciName || null, - cidr: null, - color: process.env.NO_COLOR == null, - 'commit-hooks': true, - depth: null, - description: true, - dev: false, - diff: [], - 'diff-unified': null, - 'diff-ignore-all-space': false, - 'diff-name-only': false, - 'diff-no-prefix': false, - 'diff-src-prefix': '', - 'diff-dst-prefix': '', - 'diff-text': false, - 'dry-run': false, - editor, - 'engine-strict': false, - 'fetch-retries': 2, - 'fetch-retry-factor': 10, - 'fetch-retry-maxtimeout': 60000, - 'fetch-retry-mintimeout': 10000, - 'fetch-timeout': 5 * 60 * 1000, - force: false, - 'foreground-script': false, - 'format-package-lock': true, - fund: true, - git: 'git', - 'git-tag-version': true, - global: false, - 'global-style': false, - // `globalconfig` has its default defined outside of this module - heading: 'npm', - 'https-proxy': null, - 'if-present': false, - 'ignore-prepublish': false, - 'ignore-scripts': false, - include: [], - 'include-staged': false, - 'init-author-email': '', - 'init-author-name': '', - 'init-author-url': '', - 'init-license': 'ISC', - 'init-module': '~/.npm-init.js', - 'init-version': '1.0.0', - 'init.author.email': '', - 'init.author.name': '', - 'init.author.url': '', - 'init.license': 'ISC', - 'init.module': '~/.npm-init.js', - 'init.version': '1.0.0', - json: false, - key: null, - 'legacy-bundling': false, - 'legacy-peer-deps': false, - link: false, - 'local-address': undefined, - loglevel: 'notice', - 'logs-max': 10, - long: false, - maxsockets: 50, - message: '%s', - 'node-options': null, - 'node-version': process.version, - noproxy: null, - 'npm-version': npmVersion, - offline: false, - omit: [], - only: null, - optional: true, - otp: null, - package: [], - 'package-lock': true, - 'package-lock-only': false, - parseable: false, - 'prefer-offline': false, - 'prefer-online': false, - // `prefix` has its default defined outside of this module - preid: '', - production: process.env.NODE_ENV === 'production', - progress: !ciName, - proxy: null, - 'read-only': false, - 'rebuild-bundle': true, - registry: 'https://registry.npmjs.org/', - rollback: true, - save: true, - 'save-bundle': false, - 'save-dev': false, - 'save-exact': false, - 'save-optional': false, - 'save-prefix': '^', - 'save-prod': false, - scope: '', - 'script-shell': null, - 'scripts-prepend-node-path': 'warn-only', - searchexclude: null, - searchlimit: 20, - searchopts: '', - searchstaleness: 15 * 60, - shell, - shrinkwrap: true, - 'sign-git-commit': false, - 'sign-git-tag': false, - 'sso-poll-frequency': 500, - 'sso-type': 'oauth', - 'strict-peer-deps': false, - 'strict-ssl': true, - tag: 'latest', - 'tag-version-prefix': 'v', - timing: false, - tmp: tmpdir(), - umask: 0, - unicode, - 'update-notifier': true, - usage: false, - 'user-agent': 'npm/{npm-version} ' + - 'node/{node-version} ' + - '{platform} ' + - '{arch} ' + - '{ci}', - userconfig: '~/.npmrc', - version: false, - versions: false, - viewer: isWindows ? 'browser' : 'man', -} - -const types = { - access: [null, 'restricted', 'public'], - all: Boolean, - 'allow-same-version': Boolean, - also: [null, 'dev', 'development'], - _auth: [null, String], - 'always-auth': Boolean, - audit: Boolean, - 'audit-level': ['low', 'moderate', 'high', 'critical', 'none', null], - 'auth-type': ['legacy', 'sso', 'saml', 'oauth'], - before: [null, Date], - 'bin-links': Boolean, - browser: [null, Boolean, String], - ca: [null, String, Array], - cache: path, - 'cache-lock-retries': Number, - 'cache-lock-stale': Number, - 'cache-lock-wait': Number, - 'cache-max': Number, - 'cache-min': Number, - cafile: path, - call: String, - cert: [null, String], - 'ci-name': [null, String], - cidr: [null, String, Array], - color: ['always', Boolean], - 'commit-hooks': Boolean, - depth: [null, Number], - description: Boolean, - dev: Boolean, - diff: [String, Array], - 'diff-unified': [null, Number], - 'diff-ignore-all-space': Boolean, - 'diff-name-only': Boolean, - 'diff-no-prefix': Boolean, - 'diff-src-prefix': String, - 'diff-dst-prefix': String, - 'diff-text': Boolean, - 'dry-run': Boolean, - editor: String, - 'engine-strict': Boolean, - 'fetch-retries': Number, - 'fetch-retry-factor': Number, - 'fetch-retry-maxtimeout': Number, - 'fetch-retry-mintimeout': Number, - 'fetch-timeout': Number, - force: Boolean, - 'foreground-script': Boolean, - 'format-package-lock': Boolean, - fund: Boolean, - git: String, - 'git-tag-version': Boolean, - global: Boolean, - 'global-style': Boolean, - globalconfig: path, - heading: String, - 'https-proxy': [null, url], - 'if-present': Boolean, - 'ignore-prepublish': Boolean, - 'ignore-scripts': Boolean, - include: [Array, 'prod', 'dev', 'optional', 'peer'], - 'include-staged': Boolean, - 'init-author-email': String, - 'init-author-name': String, - 'init-author-url': ['', url], - 'init-license': String, - 'init-module': path, - 'init-version': semver, - 'init.author.email': String, - 'init.author.name': String, - 'init.author.url': ['', url], - 'init.license': String, - 'init.module': path, - 'init.version': semver, - json: Boolean, - key: [null, String], - 'legacy-bundling': Boolean, - 'legacy-peer-deps': Boolean, - link: Boolean, - 'local-address': getLocalAddresses(), - loglevel: [ - 'silent', - 'error', - 'warn', - 'notice', - 'http', - 'timing', - 'info', - 'verbose', - 'silly', - ], - 'logs-max': Number, - long: Boolean, - maxsockets: Number, - message: String, - 'node-options': [null, String], - 'node-version': [null, semver], - noproxy: [null, String, Array], - 'npm-version': semver, - offline: Boolean, - omit: [Array, 'dev', 'optional', 'peer'], - only: [null, 'dev', 'development', 'prod', 'production'], - optional: Boolean, - otp: [null, String], - package: [String, Array], - 'package-lock': Boolean, - 'package-lock-only': Boolean, - parseable: Boolean, - 'prefer-offline': Boolean, - 'prefer-online': Boolean, - prefix: path, - preid: String, - production: Boolean, - progress: Boolean, - proxy: [null, false, url], // allow proxy to be disabled explicitly - 'read-only': Boolean, - 'rebuild-bundle': Boolean, - registry: [null, url], - rollback: Boolean, - save: Boolean, - 'save-bundle': Boolean, - 'save-dev': Boolean, - 'save-exact': Boolean, - 'save-optional': Boolean, - 'save-prefix': String, - 'save-prod': Boolean, - scope: String, - 'script-shell': [null, String], - 'scripts-prepend-node-path': [Boolean, 'auto', 'warn-only'], - searchexclude: [null, String], - searchlimit: Number, - searchopts: String, - searchstaleness: Number, - shell: String, - shrinkwrap: Boolean, - 'sign-git-commit': Boolean, - 'sign-git-tag': Boolean, - 'sso-poll-frequency': Number, - 'sso-type': [null, 'oauth', 'saml'], - 'strict-peer-deps': Boolean, - 'strict-ssl': Boolean, - tag: String, - 'tag-version-prefix': String, - timing: Boolean, - tmp: path, - umask: Umask, - unicode: Boolean, - 'update-notifier': Boolean, - usage: Boolean, - 'user-agent': String, - userconfig: path, - version: Boolean, - versions: Boolean, - viewer: String, -} - -const shorthands = { - '?': ['--usage'], - a: ['--all'], - B: ['--save-bundle'], - C: ['--prefix'], - c: ['--call'], - D: ['--save-dev'], - d: ['--loglevel', 'info'], - dd: ['--loglevel', 'verbose'], - ddd: ['--loglevel', 'silly'], - desc: ['--description'], - E: ['--save-exact'], - 'enjoy-by': ['--before'], - f: ['--force'], - g: ['--global'], - H: ['--usage'], - h: ['--usage'], - help: ['--usage'], - l: ['--long'], - local: ['--no-global'], - m: ['--message'], - n: ['--no-yes'], - 'no-desc': ['--no-description'], - 'no-reg': ['--no-registry'], - noreg: ['--no-registry'], - O: ['--save-optional'], - P: ['--save-prod'], - p: ['--parseable'], - porcelain: ['--parseable'], - q: ['--loglevel', 'warn'], - quiet: ['--loglevel', 'warn'], - readonly: ['--read-only'], - reg: ['--registry'], - S: ['--save'], - s: ['--loglevel', 'silent'], - silent: ['--loglevel', 'silent'], - v: ['--version'], - verbose: ['--loglevel', 'verbose'], - y: ['--yes'], -} - -module.exports = { defaults, types, shorthands } diff --git a/deps/npm/lib/utils/config/definition.js b/deps/npm/lib/utils/config/definition.js new file mode 100644 index 00000000000000..cb4eb78210c6ec --- /dev/null +++ b/deps/npm/lib/utils/config/definition.js @@ -0,0 +1,185 @@ +// class that describes a config key we know about +// this keeps us from defining a config key and not +// providing a default, description, etc. +// +// TODO: some kind of categorization system, so we can +// say "these are for registry access", "these are for +// version resolution" etc. + +const required = [ + 'type', + 'description', + 'default', + 'key', +] + +const allowed = [ + 'default', + 'defaultDescription', + 'deprecated', + 'description', + 'flatten', + 'hint', + 'key', + 'short', + 'type', + 'typeDescription', + 'usage', +] + +const { + typeDefs: { + semver: { type: semver }, + Umask: { type: Umask }, + url: { type: url }, + path: { type: path }, + }, +} = require('@npmcli/config') + +class Definition { + constructor (key, def) { + this.key = key + Object.assign(this, def) + this.validate() + if (!this.defaultDescription) + this.defaultDescription = describeValue(this.default) + if (!this.typeDescription) + this.typeDescription = describeType(this.type) + if (!this.hint) + this.hint = `<${this.key}>` + if (!this.usage) + this.usage = describeUsage(this) + } + + validate () { + for (const req of required) { + if (!Object.prototype.hasOwnProperty.call(this, req)) + throw new Error(`config lacks ${req}: ${this.key}`) + } + if (!this.key) + throw new Error(`config lacks key: ${this.key}`) + for (const field of Object.keys(this)) { + if (!allowed.includes(field)) + throw new Error(`config defines unknown field ${field}: ${this.key}`) + } + } + + // a textual description of this config, suitable for help output + describe () { + const description = unindent(this.description) + const deprecated = !this.deprecated ? '' + : `* DEPRECATED: ${unindent(this.deprecated)}\n` + return wrapAll(`#### \`${this.key}\` + +* Default: ${unindent(this.defaultDescription)} +* Type: ${unindent(this.typeDescription)} +${deprecated} +${description} +`) + } +} + +// Usage for a single param, abstracted because we have arrays of types in +// config definition +const paramUsage = (type, def) => { + let key = `--${def.key}` + if (def.short && typeof def.short === 'string') + key = `-${def.short}|${key}` + if (type === Boolean) + return `${key}` + else + return `${key} ${def.hint}` +} + +const describeUsage = (def) => { + if (Array.isArray(def.type)) { + if (!def.type.some(d => d !== null && typeof d !== 'string')) + return `--${def.key} <${def.type.filter(d => d).join('|')}>` + else + return def.type.filter(d => d).map((t) => paramUsage(t, def)).join('|') + } + return paramUsage(def.type, def) +} + +const describeType = type => { + if (Array.isArray(type)) { + const descriptions = type + .filter(t => t !== Array) + .map(t => describeType(t)) + + // [a] => "a" + // [a, b] => "a or b" + // [a, b, c] => "a, b, or c" + // [a, Array] => "a (can be set multiple times)" + // [a, Array, b] => "a or b (can be set multiple times)" + const last = descriptions.length > 1 ? [descriptions.pop()] : [] + const oxford = descriptions.length > 1 ? ', or ' : ' or ' + const words = [descriptions.join(', ')].concat(last).join(oxford) + const multiple = type.includes(Array) ? ' (can be set multiple times)' + : '' + return `${words}${multiple}` + } + + // Note: these are not quite the same as the description printed + // when validation fails. In that case, we want to give the user + // a bit more information to help them figure out what's wrong. + switch (type) { + case String: + return 'String' + case Number: + return 'Number' + case Umask: + return 'Octal numeric string in range 0000..0777 (0..511)' + case Boolean: + return 'Boolean' + case Date: + return 'Date' + case path: + return 'Path' + case semver: + return 'SemVer string' + case url: + return 'URL' + default: + return describeValue(type) + } +} + +// if it's a string, quote it. otherwise, just cast to string. +const describeValue = val => + typeof val === 'string' ? JSON.stringify(val) : String(val) + +const unindent = s => { + // get the first \n followed by a bunch of spaces, and pluck off + // that many spaces from the start of every line. + const match = s.match(/\n +/) + return !match ? s.trim() : s.split(match[0]).join('\n').trim() +} + +const wrap = (s) => { + const cols = Math.min(Math.max(20, process.stdout.columns) || 80, 80) - 5 + return unindent(s).split(/[ \n]+/).reduce((left, right) => { + const last = left.split('\n').pop() + const join = last.length && last.length + right.length > cols ? '\n' : ' ' + return left + join + right + }) +} + +const wrapAll = s => { + let inCodeBlock = false + return s.split('\n\n').map(block => { + if (inCodeBlock || block.startsWith('```')) { + inCodeBlock = !block.endsWith('```') + return block + } + + if (block.charAt(0) === '*') { + return '* ' + block.substr(1).trim().split('\n* ').map(li => { + return wrap(li).replace(/\n/g, '\n ') + }).join('\n* ') + } else + return wrap(block) + }).join('\n\n') +} + +module.exports = Definition diff --git a/deps/npm/lib/utils/config/definitions.js b/deps/npm/lib/utils/config/definitions.js new file mode 100644 index 00000000000000..a6ecbcd0c40a59 --- /dev/null +++ b/deps/npm/lib/utils/config/definitions.js @@ -0,0 +1,2054 @@ +const definitions = {} +module.exports = definitions + +const Definition = require('./definition.js') + +const { version: npmVersion } = require('../../../package.json') +const ciDetect = require('@npmcli/ci-detect') +const ciName = ciDetect() +const querystring = require('querystring') +const isWindows = require('../is-windows.js') +const { join } = require('path') + +// used by cafile flattening to flatOptions.ca +const fs = require('fs') +const maybeReadFile = file => { + try { + return fs.readFileSync(file, 'utf8') + } catch (er) { + if (er.code !== 'ENOENT') + throw er + return null + } +} + +const editor = process.env.EDITOR || + process.env.VISUAL || + (isWindows ? 'notepad.exe' : 'vi') + +const shell = isWindows ? process.env.ComSpec || 'cmd' + : process.env.SHELL || 'sh' + +const { tmpdir, networkInterfaces } = require('os') +const getLocalAddresses = () => { + try { + return Object.values(networkInterfaces()).map( + int => int.map(({ address }) => address) + ).reduce((set, addrs) => set.concat(addrs), [null]) + } catch (e) { + return [null] + } +} + +const unicode = /UTF-?8$/i.test( + process.env.LC_ALL || + process.env.LC_CTYPE || + process.env.LANG +) + +// use LOCALAPPDATA on Windows, if set +// https://github.com/npm/cli/pull/899 +const cacheRoot = (isWindows && process.env.LOCALAPPDATA) || '~' +const cacheExtra = isWindows ? 'npm-cache' : '.npm' +const cache = `${cacheRoot}/${cacheExtra}` + +const Config = require('@npmcli/config') + +// TODO: refactor these type definitions so that they are less +// weird to pull out of the config module. +// TODO: use better type definition/validation API, nopt's is so weird. +const { + typeDefs: { + semver: { type: semver }, + Umask: { type: Umask }, + url: { type: url }, + path: { type: path }, + }, +} = Config + +const define = (key, def) => { + /* istanbul ignore if - this should never happen, prevents mistakes below */ + if (definitions[key]) + throw new Error(`defining key more than once: ${key}`) + definitions[key] = new Definition(key, def) +} + +// basic flattening function, just copy it over camelCase +const flatten = (key, obj, flatOptions) => { + const camel = key.replace(/-([a-z])/g, (_0, _1) => _1.toUpperCase()) + flatOptions[camel] = obj[key] +} + +// TODO: +// Instead of having each definition provide a flatten method, +// provide the (?list of?) flat option field(s?) that it impacts. +// When that config is set, we mark the relevant flatOption fields +// dirty. Then, a getter for that field defines how we actually +// set it. +// +// So, `save-dev`, `save-optional`, `save-prod`, et al would indicate +// that they affect the `saveType` flat option. Then the config.flat +// object has a `get saveType () { ... }` that looks at the "real" +// config settings from files etc and returns the appropriate value. +// +// Getters will also (maybe?) give us a hook to audit flat option +// usage, so we can document and group these more appropriately. +// +// This will be a problem with cases where we currently do: +// const opts = { ...npm.flatOptions, foo: 'bar' }, but we can maybe +// instead do `npm.config.set('foo', 'bar')` prior to passing the +// config object down where it needs to go. +// +// This way, when we go hunting for "where does saveType come from anyway!?" +// while fixing some Arborist bug, we won't have to hunt through too +// many places. + +// Define all config keys we know about + +define('_auth', { + default: null, + type: [null, String], + description: ` + A basic-auth string to use when authenticating against the npm registry. + + Warning: This should generally not be set via a command-line option. It + is safer to use a registry-provided authentication bearer token stored in + the ~/.npmrc file by running \`npm login\`. + `, +}) + +define('access', { + default: null, + defaultDescription: ` + 'restricted' for scoped packages, 'public' for unscoped packages + `, + type: [null, 'restricted', 'public'], + description: ` + When publishing scoped packages, the access level defaults to + \`restricted\`. If you want your scoped package to be publicly viewable + (and installable) set \`--access=public\`. The only valid values for + \`access\` are \`public\` and \`restricted\`. Unscoped packages _always_ + have an access level of \`public\`. + `, + flatten, +}) + +define('all', { + default: false, + type: Boolean, + short: 'a', + description: ` + When running \`npm outdated\` and \`npm ls\`, setting \`--all\` will show + all outdated or installed packages, rather than only those directly + depended upon by the current project. + `, + flatten, +}) + +define('allow-same-version', { + default: false, + type: Boolean, + description: ` + Prevents throwing an error when \`npm version\` is used to set the new + version to the same value as the current version. + `, + flatten, +}) + +define('also', { + default: null, + type: [null, 'dev', 'development'], + description: ` + When set to \`dev\` or \`development\`, this is an alias for + \`--include=dev\`. + `, + deprecated: 'Please use --include=dev instead.', + flatten (key, obj, flatOptions) { + if (!/^dev(elopment)?$/.test(obj.also)) + return + + // add to include, and call the omit flattener + obj.include = obj.include || [] + obj.include.push('dev') + definitions.omit.flatten('omit', obj, flatOptions) + }, +}) + +define('always-auth', { + default: false, + type: Boolean, + description: ` + Force npm to always require authentication when accessing the registry, + even for \`GET\` requests. + `, + flatten, +}) + +define('audit', { + default: true, + type: Boolean, + description: ` + When "true" submit audit reports alongside \`npm install\` runs to the + default registry and all registries configured for scopes. See the + documentation for [\`npm audit\`](/commands/npm-audit) for details on + what is submitted. + `, + flatten, +}) + +define('audit-level', { + default: null, + type: ['low', 'moderate', 'high', 'critical', 'none', null], + description: ` + The minimum level of vulnerability for \`npm audit\` to exit with + a non-zero exit code. + `, + flatten, +}) + +define('auth-type', { + default: 'legacy', + type: ['legacy', 'sso', 'saml', 'oauth'], + deprecated: ` + This method of SSO/SAML/OAuth is deprecated and will be removed in + a future version of npm in favor of web-based login. + `, + description: ` + What authentication strategy to use with \`adduser\`/\`login\`. + `, + flatten, +}) + +define('before', { + default: null, + type: [null, Date], + description: ` + If passed to \`npm install\`, will rebuild the npm tree such that only + versions that were available **on or before** the \`--before\` time get + installed. If there's no versions available for the current set of + direct dependencies, the command will error. + + If the requested version is a \`dist-tag\` and the given tag does not + pass the \`--before\` filter, the most recent version less than or equal + to that tag will be used. For example, \`foo@latest\` might install + \`foo@1.2\` even though \`latest\` is \`2.0\`. + `, + flatten, +}) + +define('bin-links', { + default: true, + type: Boolean, + description: ` + Tells npm to create symlinks (or \`.cmd\` shims on Windows) for package + executables. + + Set to false to have it not do this. This can be used to work around the + fact that some file systems don't support symlinks, even on ostensibly + Unix systems. + `, + flatten, +}) + +define('browser', { + default: null, + defaultDescription: ` + OS X: \`"open"\`, Windows: \`"start"\`, Others: \`"xdg-open"\` + `, + type: [null, Boolean, String], + description: ` + The browser that is called by npm commands to open websites. + + Set to \`false\` to suppress browser behavior and instead print urls to + terminal. + + Set to \`true\` to use default system URL opener. + `, + flatten, +}) + +define('ca', { + default: null, + type: [null, String, Array], + description: ` + The Certificate Authority signing certificate that is trusted for SSL + connections to the registry. Values should be in PEM format (Windows + calls it "Base-64 encoded X.509 (.CER)") with newlines replaced by the + string "\\n". For example: + + \`\`\`ini + ca="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----" + \`\`\` + + Set to \`null\` to only allow "known" registrars, or to a specific CA + cert to trust only that specific signing authority. + + Multiple CAs can be trusted by specifying an array of certificates: + + \`\`\`ini + ca[]="..." + ca[]="..." + \`\`\` + + See also the \`strict-ssl\` config. + `, + flatten, +}) + +define('cache', { + default: cache, + defaultDescription: ` + Windows: \`%LocalAppData%\\npm-cache\`, Posix: \`~/.npm\` + `, + type: path, + description: ` + The location of npm's cache directory. See [\`npm + cache\`](/commands/npm-cache) + `, + flatten (key, obj, flatOptions) { + flatOptions.cache = join(obj.cache, '_cacache') + }, +}) + +define('cache-max', { + default: Infinity, + type: Number, + description: ` + \`--cache-max=0\` is an alias for \`--prefer-online\` + `, + deprecated: ` + This option has been deprecated in favor of \`--prefer-online\` + `, + flatten (key, obj, flatOptions) { + if (obj[key] <= 0) + flatOptions.preferOnline = true + }, +}) + +define('cache-min', { + default: 0, + type: Number, + description: ` + \`--cache-min=9999 (or bigger)\` is an alias for \`--prefer-offline\`. + `, + deprecated: ` + This option has been deprecated in favor of \`--prefer-offline\`. + `, + flatten (key, obj, flatOptions) { + if (obj[key] >= 9999) + flatOptions.preferOffline = true + }, +}) + +define('cafile', { + default: null, + type: path, + description: ` + A path to a file containing one or multiple Certificate Authority signing + certificates. Similar to the \`ca\` setting, but allows for multiple + CA's, as well as for the CA information to be stored in a file on disk. + `, + flatten (key, obj, flatOptions) { + // always set to null in defaults + if (!obj.cafile) + return + + const raw = maybeReadFile(obj.cafile) + if (!raw) + return + + const delim = '-----END CERTIFICATE-----' + flatOptions.ca = raw.replace(/\r\n/g, '\n').split(delim) + .filter(section => section.trim()) + .map(section => section.trimLeft() + delim) + }, +}) + +define('call', { + default: '', + type: String, + short: 'c', + description: ` + Optional companion option for \`npm exec\`, \`npx\` that allows for + specifying a custom command to be run along with the installed packages. + + \`\`\`bash + npm exec --package yo --package generator-node --call "yo node" + \`\`\` + `, + flatten, +}) + +define('cert', { + default: null, + type: [null, String], + description: ` + A client certificate to pass when accessing the registry. Values should + be in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with + newlines replaced by the string "\\n". For example: + + \`\`\`ini + cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----" + \`\`\` + + It is _not_ the path to a certificate file (and there is no "certfile" + option). + `, + flatten, +}) + +define('ci-name', { + default: ciName || null, + defaultDescription: ` + The name of the current CI system, or \`null\` when not on a known CI + platform. + `, + type: [null, String], + description: ` + The name of a continuous integration system. If not set explicitly, npm + will detect the current CI environment using the + [\`@npmcli/ci-detect\`](http://npm.im/@npmcli/ci-detect) module. + `, + flatten, +}) + +define('cidr', { + default: null, + type: [null, String, Array], + description: ` + This is a list of CIDR address to be used when configuring limited access + tokens with the \`npm token create\` command. + `, + flatten, +}) + +define('color', { + default: !process.env.NO_COLOR || process.env.NO_COLOR === '0', + defaultDescription: ` + true unless the NO_COLOR environ is set to something other than '0' + `, + type: ['always', Boolean], + description: ` + If false, never shows colors. If \`"always"\` then always shows colors. + If true, then only prints color codes for tty file descriptors. + `, + flatten (key, obj, flatOptions) { + flatOptions.color = !obj.color ? false + : obj.color === 'always' ? true + : process.stdout.isTTY + }, +}) + +define('commit-hooks', { + default: true, + type: Boolean, + description: ` + Run git commit hooks when using the \`npm version\` command. + `, + flatten, +}) + +define('depth', { + default: null, + defaultDescription: ` + \`Infinity\` if \`--all\` is set, otherwise \`1\` + `, + type: [null, Number], + description: ` + The depth to go when recursing packages for \`npm ls\`. + + If not set, \`npm ls\` will show only the immediate dependencies of the + root project. If \`--all\` is set, then npm will show all dependencies + by default. + `, + flatten, +}) + +define('description', { + default: true, + type: Boolean, + usage: '--no-description', + description: ` + Show the description in \`npm search\` + `, + flatten (key, obj, flatOptions) { + flatOptions.search = flatOptions.search || { limit: 20 } + flatOptions.search[key] = obj[key] + }, +}) + +define('diff', { + default: [], + type: [String, Array], + description: ` + Define arguments to compare in \`npm diff\`. + `, + flatten, +}) + +define('diff-ignore-all-space', { + default: false, + type: Boolean, + description: ` + Ignore whitespace when comparing lines in \`npm diff\`. + `, + flatten, +}) + +define('diff-name-only', { + default: false, + type: Boolean, + description: ` + Prints only filenames when using \`npm diff\`. + `, + flatten, +}) + +define('diff-no-prefix', { + default: false, + type: Boolean, + description: ` + Do not show any source or destination prefix in \`npm diff\` output. + + Note: this causes \`npm diff\` to ignore the \`--diff-src-prefix\` and + \`--diff-dst-prefix\` configs. + `, + flatten, +}) + +define('diff-dst-prefix', { + default: 'b/', + type: String, + description: ` + Destination prefix to be used in \`npm diff\` output. + `, + flatten, +}) + +define('diff-src-prefix', { + default: 'a/', + type: String, + description: ` + Source prefix to be used in \`npm diff\` output. + `, + flatten, +}) + +define('diff-text', { + default: false, + type: Boolean, + description: ` + Treat all files as text in \`npm diff\`. + `, + flatten, +}) + +define('diff-unified', { + default: 3, + type: Number, + description: ` + The number of lines of context to print in \`npm diff\`. + `, + flatten, +}) + +define('dry-run', { + default: false, + type: Boolean, + description: ` + Indicates that you don't want npm to make any changes and that it should + only report what it would have done. This can be passed into any of the + commands that modify your local installation, eg, \`install\`, + \`update\`, \`dedupe\`, \`uninstall\`, as well as \`pack\` and + \`publish\`. + + Note: This is NOT honored by other network related commands, eg + \`dist-tags\`, \`owner\`, etc. + `, + flatten, +}) + +define('editor', { + default: editor, + defaultDescription: ` + The EDITOR or VISUAL environment variables, or 'notepad.exe' on Windows, + or 'vim' on Unix systems + `, + type: String, + description: ` + The command to run for \`npm edit\` and \`npm config edit\`. + `, + flatten, +}) + +define('engine-strict', { + default: false, + type: Boolean, + description: ` + If set to true, then npm will stubbornly refuse to install (or even + consider installing) any package that claims to not be compatible with + the current Node.js version. + + This can be overridden by setting the \`--force\` flag. + `, + flatten, +}) + +define('fetch-retries', { + default: 2, + type: Number, + description: ` + The "retries" config for the \`retry\` module to use when fetching + packages from the registry. + + npm will retry idempotent read requests to the registry in the case + of network failures or 5xx HTTP errors. + `, + flatten (key, obj, flatOptions) { + flatOptions.retry = flatOptions.retry || {} + flatOptions.retry.retries = obj[key] + }, +}) + +define('fetch-retry-factor', { + default: 10, + type: Number, + description: ` + The "factor" config for the \`retry\` module to use when fetching + packages. + `, + flatten (key, obj, flatOptions) { + flatOptions.retry = flatOptions.retry || {} + flatOptions.retry.factor = obj[key] + }, +}) + +define('fetch-retry-maxtimeout', { + default: 60000, + defaultDescription: '60000 (1 minute)', + type: Number, + description: ` + The "maxTimeout" config for the \`retry\` module to use when fetching + packages. + `, + flatten (key, obj, flatOptions) { + flatOptions.retry = flatOptions.retry || {} + flatOptions.retry.maxTimeout = obj[key] + }, +}) + +define('fetch-retry-mintimeout', { + default: 10000, + defaultDescription: '10000 (10 seconds)', + type: Number, + description: ` + The "minTimeout" config for the \`retry\` module to use when fetching + packages. + `, + flatten (key, obj, flatOptions) { + flatOptions.retry = flatOptions.retry || {} + flatOptions.retry.minTimeout = obj[key] + }, +}) + +define('fetch-timeout', { + default: 5 * 60 * 1000, + defaultDescription: `${5 * 60 * 1000} (5 minutes)`, + type: Number, + description: ` + The maximum amount of time to wait for HTTP requests to complete. + `, + flatten (key, obj, flatOptions) { + flatOptions.timeout = obj[key] + }, +}) + +define('force', { + default: false, + type: Boolean, + short: 'f', + description: ` + Removes various protections against unfortunate side effects, common + mistakes, unnecessary performance degradation, and malicious input. + + * Allow clobbering non-npm files in global installs. + * Allow the \`npm version\` command to work on an unclean git repository. + * Allow deleting the cache folder with \`npm cache clean\`. + * Allow installing packages that have an \`engines\` declaration + requiring a different version of npm. + * Allow installing packages that have an \`engines\` declaration + requiring a different version of \`node\`, even if \`--engine-strict\` + is enabled. + * Allow \`npm audit fix\` to install modules outside your stated + dependency range (including SemVer-major changes). + * Allow unpublishing all versions of a published package. + * Allow conflicting peerDependencies to be installed in the root project. + + If you don't have a clear idea of what you want to do, it is strongly + recommended that you do not use this option! + `, + flatten, +}) + +define('foreground-scripts', { + default: false, + type: Boolean, + description: ` + Run all build scripts (ie, \`preinstall\`, \`install\`, and + \`postinstall\`) scripts for installed packages in the foreground + process, sharing standard input, output, and error with the main npm + process. + + Note that this will generally make installs run slower, and be much + noisier, but can be useful for debugging. + `, + flatten, +}) + +define('format-package-lock', { + default: true, + type: Boolean, + description: ` + Format \`package-lock.json\` or \`npm-shrinkwrap.json\` as a human + readable file. + `, + flatten, +}) + +define('fund', { + default: true, + type: Boolean, + description: ` + When "true" displays the message at the end of each \`npm install\` + acknowledging the number of dependencies looking for funding. + See [\`npm fund\`](/commands/npm-fund) for details. + `, + flatten, +}) + +define('git', { + default: 'git', + type: String, + description: ` + The command to use for git commands. If git is installed on the + computer, but is not in the \`PATH\`, then set this to the full path to + the git binary. + `, + flatten, +}) + +define('git-tag-version', { + default: true, + type: Boolean, + description: ` + Tag the commit when using the \`npm version\` command. + `, + flatten, +}) + +define('global', { + default: false, + type: Boolean, + short: 'g', + description: ` + Operates in "global" mode, so that packages are installed into the + \`prefix\` folder instead of the current working directory. See + [folders](/configuring-npm/folders) for more on the differences in + behavior. + + * packages are installed into the \`{prefix}/lib/node_modules\` folder, + instead of the current working directory. + * bin files are linked to \`{prefix}/bin\` + * man pages are linked to \`{prefix}/share/man\` + `, + flatten, +}) + +define('global-style', { + default: false, + type: Boolean, + description: ` + Causes npm to install the package into your local \`node_modules\` folder + with the same layout it uses with the global \`node_modules\` folder. + Only your direct dependencies will show in \`node_modules\` and + everything they depend on will be flattened in their \`node_modules\` + folders. This obviously will eliminate some deduping. If used with + \`legacy-bundling\`, \`legacy-bundling\` will be preferred. + `, + flatten, +}) + +// the globalconfig has its default defined outside of this module +define('globalconfig', { + type: path, + default: '', + defaultDescription: ` + The global --prefix setting plus 'etc/npmrc'. For example, + '/usr/local/etc/npmrc' + `, + description: ` + The config file to read for global config options. + `, + flatten, +}) + +define('heading', { + default: 'npm', + type: String, + description: ` + The string that starts all the debugging log output. + `, + flatten, +}) + +define('https-proxy', { + default: null, + type: [null, url], + description: ` + A proxy to use for outgoing https requests. If the \`HTTPS_PROXY\` or + \`https_proxy\` or \`HTTP_PROXY\` or \`http_proxy\` environment variables + are set, proxy settings will be honored by the underlying + \`make-fetch-happen\` library. + `, + flatten, +}) + +define('if-present', { + default: false, + type: Boolean, + description: ` + If true, npm will not exit with an error code when \`run-script\` is + invoked for a script that isn't defined in the \`scripts\` section of + \`package.json\`. This option can be used when it's desirable to + optionally run a script when it's present and fail if the script fails. + This is useful, for example, when running scripts that may only apply for + some builds in an otherwise generic CI setup. + `, + flatten, +}) + +define('ignore-scripts', { + default: false, + type: Boolean, + description: ` + If true, npm does not run scripts specified in package.json files. + `, + flatten, +}) + +define('include', { + default: [], + type: [Array, 'prod', 'dev', 'optional', 'peer'], + description: ` + Option that allows for defining which types of dependencies to install. + + This is the inverse of \`--omit=<type>\`. + + Dependency types specified in \`--include\` will not be omitted, + regardless of the order in which omit/include are specified on the + command-line. + `, + flatten (key, obj, flatOptions) { + // just call the omit flattener, it reads from obj.include + definitions.omit.flatten('omit', obj, flatOptions) + }, +}) + +define('include-staged', { + default: false, + type: Boolean, + description: ` + Allow installing "staged" published packages, as defined by [npm RFC PR + #92](https://github.com/npm/rfcs/pull/92). + + This is experimental, and not implemented by the npm public registry. + `, + flatten, +}) + +define('init-author-email', { + default: '', + type: String, + description: ` + The value \`npm init\` should use by default for the package author's + email. + `, +}) + +define('init-author-name', { + default: '', + type: String, + description: ` + The value \`npm init\` should use by default for the package author's name. + `, +}) + +define('init-author-url', { + default: '', + type: ['', url], + description: ` + The value \`npm init\` should use by default for the package author's homepage. + `, +}) + +define('init-license', { + default: 'ISC', + type: String, + description: ` + The value \`npm init\` should use by default for the package license. + `, +}) + +define('init-module', { + default: '~/.npm-init.js', + type: path, + description: ` + A module that will be loaded by the \`npm init\` command. See the + documentation for the + [init-package-json](https://github.com/npm/init-package-json) module for + more information, or [npm init](/commands/npm-init). + `, +}) + +define('init-version', { + default: '1.0.0', + type: semver, + description: ` + The value that \`npm init\` should use by default for the package + version number, if not already set in package.json. + `, +}) + +// these "aliases" are historically supported in .npmrc files, unfortunately +// They should be removed in a future npm version. +define('init.author.email', { + default: '', + type: String, + deprecated: ` + Use \`--init-author-email\` instead.`, + description: ` + Alias for \`--init-author-email\` + `, +}) + +define('init.author.name', { + default: '', + type: String, + deprecated: ` + Use \`--init-author-name\` instead. + `, + description: ` + Alias for \`--init-author-name\` + `, +}) + +define('init.author.url', { + default: '', + type: ['', url], + deprecated: ` + Use \`--init-author-url\` instead. + `, + description: ` + Alias for \`--init-author-url\` + `, +}) + +define('init.license', { + default: 'ISC', + type: String, + deprecated: ` + Use \`--init-license\` instead. + `, + description: ` + Alias for \`--init-license\` + `, +}) + +define('init.module', { + default: '~/.npm-init.js', + type: path, + deprecated: ` + Use \`--init-module\` instead. + `, + description: ` + Alias for \`--init-module\` + `, +}) + +define('init.version', { + default: '1.0.0', + type: semver, + deprecated: ` + Use \`--init-version\` instead. + `, + description: ` + Alias for \`--init-version\` + `, +}) + +define('json', { + default: false, + type: Boolean, + description: ` + Whether or not to output JSON data, rather than the normal output. + + This feature is currently experimental, and the output data structures + for many commands is either not implemented in JSON yet, or subject to + change. Only the output from \`npm ls --json\` and \`npm search --json\` + are currently valid. + `, + flatten, +}) + +define('key', { + default: null, + type: [null, String], + description: ` + A client key to pass when accessing the registry. Values should be in + PEM format with newlines replaced by the string "\\n". For example: + + \`\`\`ini + key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----" + \`\`\` + + It is _not_ the path to a key file (and there is no "keyfile" option). + `, + flatten, +}) + +define('legacy-bundling', { + default: false, + type: Boolean, + description: ` + Causes npm to install the package such that versions of npm prior to 1.4, + such as the one included with node 0.8, can install the package. This + eliminates all automatic deduping. If used with \`global-style\` this + option will be preferred. + `, + flatten, +}) + +define('legacy-peer-deps', { + default: false, + type: Boolean, + description: ` + Causes npm to completely ignore \`peerDependencies\` when building a + package tree, as in npm versions 3 through 6. + + If a package cannot be installed because of overly strict + \`peerDependencies\` that collide, it provides a way to move forward + resolving the situation. + + This differs from \`--omit=peer\`, in that \`--omit=peer\` will avoid + unpacking \`peerDependencies\` on disk, but will still design a tree such + that \`peerDependencies\` _could_ be unpacked in a correct place. + + Use of \`legacy-peer-deps\` is not recommended, as it will not enforce + the \`peerDependencies\` contract that meta-dependencies may rely on. + `, + flatten, +}) + +define('link', { + default: false, + type: Boolean, + description: ` + If true, then local installs will link if there is a suitable globally + installed package. + + Note that this means that local installs can cause things to be installed + into the global space at the same time. The link is only done if one of + the two conditions are met: + + * The package is not already installed globally, or + * the globally installed version is identical to the version that is + being installed locally. + `, +}) + +define('local-address', { + default: null, + type: getLocalAddresses(), + typeDescription: 'IP Address', + description: ` + The IP address of the local interface to use when making connections to + the npm registry. Must be IPv4 in versions of Node prior to 0.12. + `, + flatten, +}) + +define('loglevel', { + default: 'notice', + type: [ + 'silent', + 'error', + 'warn', + 'notice', + 'http', + 'timing', + 'info', + 'verbose', + 'silly', + ], + description: ` + What level of logs to report. On failure, *all* logs are written to + \`npm-debug.log\` in the current working directory. + + Any logs of a higher level than the setting are shown. The default is + "notice". + `, +}) + +define('logs-max', { + default: 10, + type: Number, + description: ` + The maximum number of log files to store. + `, +}) + +define('long', { + default: false, + type: Boolean, + short: 'l', + description: ` + Show extended information in \`npm ls\` and \`npm search\`. + `, +}) + +define('maxsockets', { + default: Infinity, + type: Number, + description: ` + The maximum number of connections to use per origin (protocol/host/port + combination). + `, + flatten (key, obj, flatOptions) { + flatOptions.maxSockets = obj[key] + }, +}) + +define('message', { + default: '%s', + type: String, + short: 'm', + description: ` + Commit message which is used by \`npm version\` when creating version commit. + + Any "%s" in the message will be replaced with the version number. + `, + flatten, +}) + +define('node-options', { + default: null, + type: [null, String], + description: ` + Options to pass through to Node.js via the \`NODE_OPTIONS\` environment + variable. This does not impact how npm itself is executed but it does + impact how lifecycle scripts are called. + `, +}) + +define('node-version', { + default: process.version, + defaultDescription: 'Node.js `process.version` value', + type: semver, + description: ` + The node version to use when checking a package's \`engines\` setting. + `, + flatten, +}) + +define('noproxy', { + default: '', + defaultDescription: ` + The value of the NO_PROXY environment variable + `, + type: [String, Array], + description: ` + Domain extensions that should bypass any proxies. + + Also accepts a comma-delimited string. + `, + flatten (key, obj, flatOptions) { + flatOptions.noProxy = obj[key].join(',') + }, +}) + +define('npm-version', { + default: npmVersion, + defaultDescription: 'Output of `npm --version`', + type: semver, + description: ` + The npm version to use when checking a package's \`engines\` setting. + `, + flatten, +}) + +define('offline', { + default: false, + type: Boolean, + description: ` + Force offline mode: no network requests will be done during install. To allow + the CLI to fill in missing cache data, see \`--prefer-offline\`. + `, + flatten, +}) + +define('omit', { + default: process.env.NODE_ENV === 'production' ? ['dev'] : [], + defaultDescription: ` + 'dev' if the NODE_ENV environment variable is set to 'production', + otherwise empty. + `, + type: [Array, 'dev', 'optional', 'peer'], + description: ` + Dependency types to omit from the installation tree on disk. + + Note that these dependencies _are_ still resolved and added to the + \`package-lock.json\` or \`npm-shrinkwrap.json\` file. They are just + not physically installed on disk. + + If a package type appears in both the \`--include\` and \`--omit\` + lists, then it will be included. + + If the resulting omit list includes \`'dev'\`, then the \`NODE_ENV\` + environment variable will be set to \`'production'\` for all lifecycle + scripts. + `, + flatten (key, obj, flatOptions) { + const include = obj.include || [] + const omit = flatOptions.omit || [] + flatOptions.omit = omit.concat(obj[key]) + .filter(type => type && !include.includes(type)) + }, +}) + +define('only', { + default: null, + type: [null, 'prod', 'production'], + deprecated: ` + Use \`--omit=dev\` to omit dev dependencies from the install. + `, + description: ` + When set to \`prod\` or \`production\`, this is an alias for + \`--omit=dev\`. + `, + flatten (key, obj, flatOptions) { + const value = obj[key] + if (!/^prod(uction)?$/.test(value)) + return + + obj.omit = obj.omit || [] + obj.omit.push('dev') + definitions.omit.flatten('omit', obj, flatOptions) + }, +}) + +define('optional', { + default: null, + type: [null, Boolean], + deprecated: ` + Use \`--omit=optional\` to exclude optional dependencies, or + \`--include=optional\` to include them. + + Default value does install optional deps unless otherwise omitted. + `, + description: ` + Alias for --include=optional or --omit=optional + `, + flatten (key, obj, flatOptions) { + const value = obj[key] + if (value === null) + return + else if (value === true) { + obj.include = obj.include || [] + obj.include.push('optional') + } else { + obj.omit = obj.omit || [] + obj.omit.push('optional') + } + definitions.omit.flatten('omit', obj, flatOptions) + }, +}) + +define('otp', { + default: null, + type: [null, String], + description: ` + This is a one-time password from a two-factor authenticator. It's needed + when publishing or changing package permissions with \`npm access\`. + + If not set, and a registry response fails with a challenge for a one-time + password, npm will prompt on the command line for one. + `, + flatten, +}) + +define('package', { + default: [], + hint: '<pkg>[@<version>]', + type: [String, Array], + description: ` + The package to install for [\`npm exec\`](/commands/npm-exec) + `, + flatten, +}) + +define('package-lock', { + default: true, + type: Boolean, + description: ` + If set to false, then ignore \`package-lock.json\` files when installing. + This will also prevent _writing_ \`package-lock.json\` if \`save\` is + true. + + When package package-locks are disabled, automatic pruning of extraneous + modules will also be disabled. To remove extraneous modules with + package-locks disabled use \`npm prune\`. + `, + flatten, +}) + +define('package-lock-only', { + default: false, + type: Boolean, + description: ` + If set to true, it will update only the \`package-lock.json\`, instead of + checking \`node_modules\` and downloading dependencies. + `, + flatten, +}) + +define('parseable', { + default: false, + type: Boolean, + short: 'p', + description: ` + Output parseable results from commands that write to standard output. For + \`npm search\`, this will be tab-separated table format. + `, + flatten, +}) + +define('prefer-offline', { + default: false, + type: Boolean, + description: ` + If true, staleness checks for cached data will be bypassed, but missing + data will be requested from the server. To force full offline mode, use + \`--offline\`. + `, + flatten, +}) + +define('prefer-online', { + default: false, + type: Boolean, + description: ` + If true, staleness checks for cached data will be forced, making the CLI + look for updates immediately even for fresh package data. + `, + flatten, +}) + +// `prefix` has its default defined outside of this module +define('prefix', { + type: path, + short: 'C', + default: '', + defaultDescription: ` + In global mode, the folder where the node executable is installed. In + local mode, the nearest parent folder containing either a package.json + file or a node_modules folder. + `, + description: ` + The location to install global items. If set on the command line, then + it forces non-global commands to run in the specified folder. + `, +}) + +define('preid', { + default: '', + type: String, + description: ` + The "prerelease identifier" to use as a prefix for the "prerelease" part + of a semver. Like the \`rc\` in \`1.2.0-rc.8\`. + `, + flatten, +}) + +define('production', { + default: false, + type: Boolean, + deprecated: 'Use `--omit=dev` instead.', + description: 'Alias for `--omit=dev`', + flatten (key, obj, flatOptions) { + const value = obj[key] + if (!value) + return + + obj.omit = obj.omit || [] + obj.omit.push('dev') + definitions.omit.flatten('omit', obj, flatOptions) + }, +}) + +define('progress', { + default: !ciName, + defaultDescription: ` + \`true\` unless running in a known CI system + `, + type: Boolean, + description: ` + When set to \`true\`, npm will display a progress bar during time + intensive operations, if \`process.stderr\` is a TTY. + + Set to \`false\` to suppress the progress bar. + `, +}) + +define('proxy', { + default: null, + type: [null, false, url], // allow proxy to be disabled explicitly + description: ` + A proxy to use for outgoing http requests. If the \`HTTP_PROXY\` or + \`http_proxy\` environment variables are set, proxy settings will be + honored by the underlying \`request\` library. + `, + flatten, +}) + +define('read-only', { + default: false, + type: Boolean, + description: ` + This is used to mark a token as unable to publish when configuring + limited access tokens with the \`npm token create\` command. + `, + flatten, +}) + +define('rebuild-bundle', { + default: true, + type: Boolean, + description: ` + Rebuild bundled dependencies after installation. + `, + flatten, +}) + +define('registry', { + default: 'https://registry.npmjs.org/', + type: url, + description: ` + The base URL of the npm registry. + `, + flatten, +}) + +define('save', { + default: true, + usage: '-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer', + type: Boolean, + short: 'S', + description: ` + Save installed packages to a package.json file as dependencies. + + When used with the \`npm rm\` command, removes the dependency from + package.json. + `, + flatten, +}) + +define('save-bundle', { + default: false, + type: Boolean, + short: 'B', + description: ` + If a package would be saved at install time by the use of \`--save\`, + \`--save-dev\`, or \`--save-optional\`, then also put it in the + \`bundleDependencies\` list. + + Ignore if \`--save-peer\` is set, since peerDependencies cannot be bundled. + `, + flatten (key, obj, flatOptions) { + // XXX update arborist to just ignore it if resulting saveType is peer + // otherwise this won't have the expected effect: + // + // npm config set save-peer true + // npm i foo --save-bundle --save-prod <-- should bundle + flatOptions.saveBundle = obj['save-bundle'] && !obj['save-peer'] + }, +}) + +// XXX: We should really deprecate all these `--save-blah` switches +// in favor of a single `--save-type` option. The unfortunate shortcut +// we took for `--save-peer --save-optional` being `--save-type=peerOptional` +// makes this tricky, and likely a breaking change. + +define('save-dev', { + default: false, + type: Boolean, + short: 'D', + description: ` + Save installed packages to a package.json file as \`devDependencies\`. + `, + flatten (key, obj, flatOptions) { + if (!obj[key]) { + if (flatOptions.saveType === 'dev') + delete flatOptions.saveType + return + } + + flatOptions.saveType = 'dev' + }, +}) + +define('save-exact', { + default: false, + type: Boolean, + short: 'E', + description: ` + Dependencies saved to package.json will be configured with an exact + version rather than using npm's default semver range operator. + `, + flatten, +}) + +define('save-optional', { + default: false, + type: Boolean, + short: 'O', + description: ` + Save installed packages to a package.json file as + \`optionalDependencies\`. + `, + flatten (key, obj, flatOptions) { + if (!obj[key]) { + if (flatOptions.saveType === 'optional') + delete flatOptions.saveType + else if (flatOptions.saveType === 'peerOptional') + flatOptions.saveType = 'peer' + return + } + + if (flatOptions.saveType === 'peerOptional') + return + + if (flatOptions.saveType === 'peer') + flatOptions.saveType = 'peerOptional' + else + flatOptions.saveType = 'optional' + }, +}) + +define('save-peer', { + default: false, + type: Boolean, + description: ` + Save installed packages. to a package.json file as \`peerDependencies\` + `, + flatten (key, obj, flatOptions) { + if (!obj[key]) { + if (flatOptions.saveType === 'peer') + delete flatOptions.saveType + else if (flatOptions.saveType === 'peerOptional') + flatOptions.saveType = 'optional' + return + } + + if (flatOptions.saveType === 'peerOptional') + return + + if (flatOptions.saveType === 'optional') + flatOptions.saveType = 'peerOptional' + else + flatOptions.saveType = 'peer' + }, +}) + +define('save-prefix', { + default: '^', + type: String, + description: ` + Configure how versions of packages installed to a package.json file via + \`--save\` or \`--save-dev\` get prefixed. + + For example if a package has version \`1.2.3\`, by default its version is + set to \`^1.2.3\` which allows minor upgrades for that package, but after + \`npm config set save-prefix='~'\` it would be set to \`~1.2.3\` which + only allows patch upgrades. + `, + flatten, +}) + +define('save-prod', { + default: false, + type: Boolean, + short: 'P', + description: ` + Save installed packages into \`dependencies\` specifically. This is + useful if a package already exists in \`devDependencies\` or + \`optionalDependencies\`, but you want to move it to be a non-optional + production dependency. + + This is the default behavior if \`--save\` is true, and neither + \`--save-dev\` or \`--save-optional\` are true. + `, + flatten (key, obj, flatOptions) { + if (!obj[key]) { + if (flatOptions.saveType === 'prod') + delete flatOptions.saveType + return + } + + flatOptions.saveType = 'prod' + }, +}) + +define('scope', { + default: '', + defaultDescription: ` + the scope of the current project, if any, or "" + `, + type: String, + hint: '<@scope>', + description: ` + Associate an operation with a scope for a scoped registry. + + Useful when logging in to a private registry for the first time: + + \`\`\`bash + npm login --scope=@mycorp --registry=https://registry.mycorp.com + \`\`\` + + This will cause \`@mycorp\` to be mapped to the registry for future + installation of packages specified according to the pattern + \`@mycorp/package\`. + `, + flatten (key, obj, flatOptions) { + const value = obj[key] + flatOptions.projectScope = value && !/^@/.test(value) ? `@${value}` : value + }, +}) + +define('script-shell', { + default: null, + defaultDescription: ` + '/bin/sh' on POSIX systems, 'cmd.exe' on Windows + `, + type: [null, String], + description: ` + The shell to use for scripts run with the \`npm run\` command. + `, + flatten (key, obj, flatOptions) { + flatOptions.scriptShell = obj[key] || undefined + }, +}) + +define('searchexclude', { + default: '', + type: String, + description: ` + Space-separated options that limit the results from search. + `, + flatten (key, obj, flatOptions) { + flatOptions.search = flatOptions.search || { limit: 20 } + flatOptions.search.exclude = obj[key] + }, +}) + +define('searchlimit', { + default: 20, + type: Number, + description: ` + Number of items to limit search results to. Will not apply at all to + legacy searches. + `, + flatten (key, obj, flatOptions) { + flatOptions.search = flatOptions.search || {} + flatOptions.search.limit = obj[key] + }, +}) + +define('searchopts', { + default: '', + type: String, + description: ` + Space-separated options that are always passed to search. + `, + flatten (key, obj, flatOptions) { + flatOptions.search = flatOptions.search || { limit: 20 } + flatOptions.search.opts = querystring.parse(obj[key]) + }, +}) + +define('searchstaleness', { + default: 15 * 60, + type: Number, + description: ` + The age of the cache, in seconds, before another registry request is made + if using legacy search endpoint. + `, + flatten (key, obj, flatOptions) { + flatOptions.search = flatOptions.search || { limit: 20 } + flatOptions.search.staleness = obj[key] + }, +}) + +define('shell', { + default: shell, + defaultDescription: ` + SHELL environment variable, or "bash" on Posix, or "cmd.exe" on Windows + `, + type: String, + description: ` + The shell to run for the \`npm explore\` command. + `, + flatten, +}) + +define('shrinkwrap', { + default: true, + type: Boolean, + deprecated: ` + Use the --package-lock setting instead. + `, + description: ` + Alias for --package-lock + `, + flatten (key, obj, flatOptions) { + obj['package-lock'] = obj.shrinkwrap + definitions['package-lock'].flatten('package-lock', obj, flatOptions) + }, +}) + +define('sign-git-commit', { + default: false, + type: Boolean, + description: ` + If set to true, then the \`npm version\` command will commit the new + package version using \`-S\` to add a signature. + + Note that git requires you to have set up GPG keys in your git configs + for this to work properly. + `, + flatten, +}) + +define('sign-git-tag', { + default: false, + type: Boolean, + description: ` + If set to true, then the \`npm version\` command will tag the version + using \`-s\` to add a signature. + + Note that git requires you to have set up GPG keys in your git configs + for this to work properly. + `, + flatten, +}) + +define('sso-poll-frequency', { + default: 500, + type: Number, + deprecated: ` + The --auth-type method of SSO/SAML/OAuth will be removed in a future + version of npm in favor of web-based login. + `, + description: ` + When used with SSO-enabled \`auth-type\`s, configures how regularly the + registry should be polled while the user is completing authentication. + `, + flatten, +}) + +define('sso-type', { + default: 'oauth', + type: [null, 'oauth', 'saml'], + deprecated: ` + The --auth-type method of SSO/SAML/OAuth will be removed in a future + version of npm in favor of web-based login. + `, + description: ` + If \`--auth-type=sso\`, the type of SSO type to use. + `, + flatten, +}) + +define('strict-peer-deps', { + default: false, + type: Boolean, + description: ` + If set to \`true\`, and \`--legacy-peer-deps\` is not set, then _any_ + conflicting \`peerDependencies\` will be treated as an install failure, + even if npm could reasonably guess the appropriate resolution based on + non-peer dependency relationships. + + By default, conflicting \`peerDependencies\` deep in the dependency graph + will be resolved using the nearest non-peer dependency specification, + even if doing so will result in some packages receiving a peer dependency + outside the range set in their package's \`peerDependencies\` object. + + When such and override is performed, a warning is printed, explaining the + conflict and the packages involved. If \`--strict-peer-deps\` is set, + then this warning is treated as a failure. + `, + flatten, +}) + +define('strict-ssl', { + default: true, + type: Boolean, + description: ` + Whether or not to do SSL key validation when making requests to the + registry via https. + + See also the \`ca\` config. + `, + flatten (key, obj, flatOptions) { + flatOptions.strictSSL = obj[key] + }, +}) + +define('tag', { + default: 'latest', + type: String, + description: ` + If you ask npm to install a package and don't tell it a specific version, + then it will install the specified tag. + + Also the tag that is added to the package@version specified by the \`npm + tag\` command, if no explicit tag is given. + `, + flatten (key, obj, flatOptions) { + flatOptions.defaultTag = obj[key] + }, +}) + +define('tag-version-prefix', { + default: 'v', + type: String, + description: ` + If set, alters the prefix used when tagging a new version when performing + a version increment using \`npm-version\`. To remove the prefix + altogether, set it to the empty string: \`""\`. + + Because other tools may rely on the convention that npm version tags look + like \`v1.0.0\`, _only use this property if it is absolutely necessary_. + In particular, use care when overriding this setting for public packages. + `, + flatten, +}) + +define('timing', { + default: false, + type: Boolean, + description: ` + If true, writes an \`npm-debug\` log to \`_logs\` and timing information + to \`_timing.json\`, both in your cache, even if the command completes + successfully. \`_timing.json\` is a newline delimited list of JSON + objects. + + You can quickly view it with this [json](https://npm.im/json) command + line: \`npm exec -- json -g < ~/.npm/_timing.json\`. + `, +}) + +define('tmp', { + default: tmpdir(), + defaultDescription: ` + The value returned by the Node.js \`os.tmpdir()\` method + <https://nodejs.org/api/os.html#os_os_tmpdir> + `, + type: path, + deprecated: ` + This setting is no longer used. npm stores temporary files in a special + location in the cache, and they are managed by + [\`cacache\`](http://npm.im/cacache). + `, + description: ` + Historically, the location where temporary files were stored. No longer + relevant. + `, +}) + +define('umask', { + default: 0, + type: Umask, + description: ` + The "umask" value to use when setting the file creation mode on files and + folders. + + Folders and executables are given a mode which is \`0o777\` masked + against this value. Other files are given a mode which is \`0o666\` + masked against this value. + + Note that the underlying system will _also_ apply its own umask value to + files and folders that are created, and npm does not circumvent this, but + rather adds the \`--umask\` config to it. + + Thus, the effective default umask value on most POSIX systems is 0o22, + meaning that folders and executables are created with a mode of 0o755 and + other files are created with a mode of 0o644. + `, + flatten, +}) + +define('unicode', { + default: unicode, + defaultDescription: ` + false on windows, true on mac/unix systems with a unicode locale, as + defined by the LC_ALL, LC_CTYPE, or LANG environment variables. + `, + type: Boolean, + description: ` + When set to true, npm uses unicode characters in the tree output. When + false, it uses ascii characters instead of unicode glyphs. + `, +}) + +define('update-notifier', { + default: true, + type: Boolean, + description: ` + Set to false to suppress the update notification when using an older + version of npm than the latest. + `, +}) + +define('usage', { + default: false, + type: Boolean, + short: ['?', 'H', 'h'], + description: ` + Show short usage output about the command specified. + `, +}) + +define('user-agent', { + default: 'npm/{npm-version} ' + + 'node/{node-version} ' + + '{platform} ' + + '{arch} ' + + '{ci}', + type: String, + description: ` + Sets the User-Agent request header. The following fields are replaced + with their actual counterparts: + + * \`{npm-version}\` - The npm version in use + * \`{node-version}\` - The Node.js version in use + * \`{platform}\` - The value of \`process.platform\` + * \`{arch}\` - The value of \`process.arch\` + * \`{ci}\` - The value of the \`ci-name\` config, if set, prefixed with + \`ci/\`, or an empty string if \`ci-name\` is empty. + `, + flatten (key, obj, flatOptions) { + const value = obj[key] + const ciName = obj['ci-name'] + flatOptions.userAgent = + value.replace(/\{node-version\}/gi, obj['node-version']) + .replace(/\{npm-version\}/gi, obj['npm-version']) + .replace(/\{platform\}/gi, process.platform) + .replace(/\{arch\}/gi, process.arch) + .replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '') + .trim() + }, +}) + +define('userconfig', { + default: '~/.npmrc', + type: path, + description: ` + The location of user-level configuration settings. + + This may be overridden by the \`npm_config_userconfig\` environment + variable or the \`--userconfig\` command line option, but may _not_ + be overridden by settings in the \`globalconfig\` file. + `, +}) + +define('version', { + default: false, + type: Boolean, + short: 'v', + description: ` + If true, output the npm version and exit successfully. + + Only relevant when specified explicitly on the command line. + `, +}) + +define('versions', { + default: false, + type: Boolean, + description: ` + If true, output the npm version as well as node's \`process.versions\` + map and the version in the current working directory's \`package.json\` + file if one exists, and exit successfully. + + Only relevant when specified explicitly on the command line. + `, +}) + +define('viewer', { + default: isWindows ? 'browser' : 'man', + defaultDescription: ` + "man" on Posix, "browser" on Windows + `, + type: String, + description: ` + The program to use to view help content. + + Set to \`"browser"\` to view html help content in the default web browser. + `, +}) + +define('which', { + default: null, + hint: '<fundingSourceNumber>', + type: [null, Number], + description: ` + If there are multiple funding sources, which 1-indexed source URL to open. + `, +}) + +define('workspace', { + default: [], + type: [String, Array], + short: 'w', + description: ` + Enable running a command in the context of the configured workspaces of the + current project while filtering by running only the workspaces defined by + this configuration option. + + Valid values for the \`workspace\` config are either: + - Workspace names + - Path to a workspace directory + - Path to a parent workspace directory (will result to selecting all of the + nested workspaces) + `, +}) + +define('workspaces', { + default: false, + type: Boolean, + short: 'ws', + description: ` + Enable running a command in the context of **all** the configured + workspaces. + `, +}) + +define('yes', { + default: null, + type: [null, Boolean], + short: 'y', + description: ` + Automatically answer "yes" to any prompts that npm might print on + the command line. + `, +}) diff --git a/deps/npm/lib/utils/config/describe-all.js b/deps/npm/lib/utils/config/describe-all.js new file mode 100644 index 00000000000000..ab3f3a63ea751c --- /dev/null +++ b/deps/npm/lib/utils/config/describe-all.js @@ -0,0 +1,16 @@ +const definitions = require('./definitions.js') +const describeAll = () => { + // sort not-deprecated ones to the top + /* istanbul ignore next - typically already sorted in the definitions file, + * but this is here so that our help doc will stay consistent if we decide + * to move them around. */ + const sort = ([keya, {deprecated: depa}], [keyb, {deprecated: depb}]) => { + return depa && !depb ? 1 + : !depa && depb ? -1 + : keya.localeCompare(keyb) + } + return Object.entries(definitions).sort(sort) + .map(([key, def]) => def.describe()) + .join('\n\n') +} +module.exports = describeAll diff --git a/deps/npm/lib/utils/config/flatten.js b/deps/npm/lib/utils/config/flatten.js new file mode 100644 index 00000000000000..f6d6124bddf7aa --- /dev/null +++ b/deps/npm/lib/utils/config/flatten.js @@ -0,0 +1,32 @@ +// use the defined flattening function, and copy over any scoped +// registries and registry-specific "nerfdart" configs verbatim +// +// TODO: make these getters so that we only have to make dirty +// the thing that changed, and then flatten the fields that +// could have changed when a config.set is called. +// +// TODO: move nerfdart auth stuff into a nested object that +// is only passed along to paths that end up calling npm-registry-fetch. +const definitions = require('./definitions.js') +const flatten = (obj, flat = {}) => { + for (const [key, val] of Object.entries(obj)) { + const def = definitions[key] + if (def && def.flatten) + def.flatten(key, obj, flat) + else if (/@.*:registry$/i.test(key) || /^\/\//.test(key)) + flat[key] = val + } + + // XXX make this the bin/npm-cli.js file explicitly instead + // otherwise using npm programmatically is a bit of a pain. + flat.npmBin = require.main ? require.main.filename + : /* istanbul ignore next - not configurable property */ undefined + flat.nodeBin = process.env.NODE || process.execPath + + // XXX should this be sha512? is it even relevant? + flat.hashAlgorithm = 'sha1' + + return flat +} + +module.exports = flatten diff --git a/deps/npm/lib/utils/config/index.js b/deps/npm/lib/utils/config/index.js new file mode 100644 index 00000000000000..a24f5865242bfa --- /dev/null +++ b/deps/npm/lib/utils/config/index.js @@ -0,0 +1,52 @@ +const flatten = require('./flatten.js') +const definitions = require('./definitions.js') +const describeAll = require('./describe-all.js') + +// aliases where they get expanded into a completely different thing +// these are NOT supported in the environment or npmrc files, only +// expanded on the CLI. +// TODO: when we switch off of nopt, use an arg parser that supports +// more reasonable aliasing and short opts right in the definitions set. +const shorthands = { + 'enjoy-by': ['--before'], + d: ['--loglevel', 'info'], + dd: ['--loglevel', 'verbose'], + ddd: ['--loglevel', 'silly'], + quiet: ['--loglevel', 'warn'], + q: ['--loglevel', 'warn'], + s: ['--loglevel', 'silent'], + silent: ['--loglevel', 'silent'], + verbose: ['--loglevel', 'verbose'], + desc: ['--description'], + help: ['--usage'], + local: ['--no-global'], + n: ['--no-yes'], + no: ['--no-yes'], + porcelain: ['--parseable'], + readonly: ['--read-only'], + reg: ['--registry'], +} + +for (const [key, {short}] of Object.entries(definitions)) { + if (!short) + continue + // can be either an array or string + for (const s of [].concat(short)) + shorthands[s] = [`--${key}`] +} + +module.exports = { + get defaults () { + // NB: 'default' is a reserved word + return Object.entries(definitions).map(([key, { default: def }]) => { + return [key, def] + }).reduce((defaults, [key, def]) => { + defaults[key] = def + return defaults + }, {}) + }, + definitions, + flatten, + shorthands, + describeAll, +} diff --git a/deps/npm/lib/utils/did-you-mean.js b/deps/npm/lib/utils/did-you-mean.js index c2bdf159dd1185..98133196e3c567 100644 --- a/deps/npm/lib/utils/did-you-mean.js +++ b/deps/npm/lib/utils/did-you-mean.js @@ -1,12 +1,33 @@ const leven = require('leven') +const readJson = require('read-package-json-fast') +const { cmdList } = require('./cmd-list.js') -const didYouMean = (scmd, commands) => { - const best = commands +const didYouMean = async (npm, path, scmd) => { + const bestCmd = cmdList + .filter(cmd => leven(scmd, cmd) < scmd.length * 0.4 && scmd !== cmd) + .map(str => ` npm ${str} # ${npm.commands[str].description}`) + + const pkg = await readJson(`${path}/package.json`) + const { scripts } = pkg + // We would already be suggesting this in `npm x` so omit them here + const runScripts = ['stop', 'start', 'test', 'restart'] + const bestRun = Object.keys(scripts || {}) + .filter(cmd => leven(scmd, cmd) < scmd.length * 0.4 && + !runScripts.includes(cmd)) + .map(str => ` npm run ${str} # run the "${str}" package script`) + + const { bin } = pkg + const bestBin = Object.keys(bin || {}) .filter(cmd => leven(scmd, cmd) < scmd.length * 0.4) - .map(str => ` ${str}`) - return best.length === 0 ? '' - : best.length === 1 ? `\nDid you mean this?\n${best[0]}` - : `\nDid you mean one of these?\n${best.slice(0, 3).join('\n')}` -} + .map(str => ` npm exec ${str} # run the "${str}" command from either this or a remote npm package`) + const best = [...bestCmd, ...bestRun, ...bestBin] + + if (best.length === 0) + return '' + + const suggestion = best.length === 1 ? `\n\nDid you mean this?\n${best[0]}` + : `\n\nDid you mean one of these?\n${best.slice(0, 3).join('\n')}` + return suggestion +} module.exports = didYouMean diff --git a/deps/npm/lib/utils/flat-options.js b/deps/npm/lib/utils/flat-options.js deleted file mode 100644 index c082e4137ab217..00000000000000 --- a/deps/npm/lib/utils/flat-options.js +++ /dev/null @@ -1,254 +0,0 @@ -// return a flattened config object with canonical names suitable for -// passing to dependencies like arborist, pacote, npm-registry-fetch, etc. - -const log = require('npmlog') -const crypto = require('crypto') -const querystring = require('querystring') -const npmSession = crypto.randomBytes(8).toString('hex') -log.verbose('npm-session', npmSession) -const { join } = require('path') - -const buildOmitList = obj => { - const include = obj.include || [] - const omit = new Set((obj.omit || []) - .filter(type => !include.includes(type))) - const only = obj.only - - if (/^prod(uction)?$/.test(only) || obj.production) - omit.add('dev') - - if (/dev/.test(obj.also)) - omit.delete('dev') - - if (obj.dev) - omit.delete('dev') - - if (obj.optional === false) - omit.add('optional') - - obj.omit = [...omit] - - // it would perhaps make more sense to put this in @npmcli/config, but - // since we can set dev to be omitted in multiple various legacy ways, - // it's better to set it here once it's all resolved. - if (obj.omit.includes('dev')) - process.env.NODE_ENV = 'production' - - return [...omit] -} - -// turn an object with npm-config style keys into an options object -// with camelCase values. This doesn't account for the stuff that is -// not pulled from the config keys, that's all handled only for the -// main function which acts on the npm object itself. Used by the -// flatOptions generator, and by the publishConfig handling logic. -const flatten = obj => ({ - includeStaged: obj['include-staged'], - preferDedupe: obj['prefer-dedupe'], - ignoreScripts: obj['ignore-scripts'], - nodeVersion: obj['node-version'], - cache: join(obj.cache, '_cacache'), - global: obj.global, - - registry: obj.registry, - scope: obj.scope, - access: obj.access, - alwaysAuth: obj['always-auth'], - audit: obj.audit, - auditLevel: obj['audit-level'], - _auth: obj._auth, - authType: obj['auth-type'], - ssoType: obj['sso-type'], - ssoPollFrequency: obj['sso-poll-frequency'], - before: obj.before, - browser: obj.browser, - ca: obj.ca, - cafile: obj.cafile, - cert: obj.cert, - key: obj.key, - - // token creation options - cidr: obj.cidr, - readOnly: obj['read-only'], - - // npm version options - preid: obj.preid, - tagVersionPrefix: obj['tag-version-prefix'], - allowSameVersion: obj['allow-same-version'], - - // npm version git options - message: obj.message, - commitHooks: obj['commit-hooks'], - gitTagVersion: obj['git-tag-version'], - signGitCommit: obj['sign-git-commit'], - signGitTag: obj['sign-git-tag'], - - // only used for npm ls in v7, not update - depth: obj.depth, - all: obj.all, - - // Output configs - unicode: obj.unicode, - json: obj.json, - long: obj.long, - parseable: obj.parseable, - - // options for npm search - search: { - description: obj.description, - exclude: obj.searchexclude, - limit: obj.searchlimit || 20, - opts: querystring.parse(obj.searchopts), - staleness: obj.searchstaleness, - }, - - diff: obj.diff, - diffUnified: obj['diff-unified'], - diffIgnoreAllSpace: obj['diff-ignore-all-space'], - diffNameOnly: obj['diff-name-only'], - diffNoPrefix: obj['diff-no-prefix'], - diffSrcPrefix: obj['diff-src-prefix'], - diffDstPrefix: obj['diff-dst-prefix'], - diffText: obj['diff-text'], - - dryRun: obj['dry-run'], - engineStrict: obj['engine-strict'], - - retry: { - retries: obj['fetch-retries'], - factor: obj['fetch-retry-factor'], - maxTimeout: obj['fetch-retry-maxtimeout'], - minTimeout: obj['fetch-retry-mintimeout'], - }, - - timeout: obj['fetch-timeout'], - - force: obj.force, - - formatPackageLock: obj['format-package-lock'], - fund: obj.fund, - - // binary locators - git: obj.git, - viewer: obj.viewer, - editor: obj.editor, - - // configs that affect how we build trees - binLinks: obj['bin-links'], - rebuildBundle: obj['rebuild-bundle'], - // --no-shrinkwrap is the same as --no-package-lock - packageLock: !(obj['package-lock'] === false || - obj.shrinkwrap === false), - packageLockOnly: obj['package-lock-only'], - globalStyle: obj['global-style'], - legacyBundling: obj['legacy-bundling'], - foregroundScripts: !!obj['foreground-scripts'], - scriptShell: obj['script-shell'] || undefined, - shell: obj.shell, - omit: buildOmitList(obj), - legacyPeerDeps: obj['legacy-peer-deps'], - strictPeerDeps: obj['strict-peer-deps'], - - // npx stuff - call: obj.call, - package: obj.package, - - // used to build up the appropriate {add:{...}} options to Arborist.reify - save: obj.save, - saveBundle: obj['save-bundle'] && !obj['save-peer'], - saveType: obj['save-optional'] && obj['save-peer'] - ? 'peerOptional' - : obj['save-optional'] ? 'optional' - : obj['save-dev'] ? 'dev' - : obj['save-peer'] ? 'peer' - : obj['save-prod'] ? 'prod' - : null, - savePrefix: obj['save-exact'] ? '' - : obj['save-prefix'], - - // configs for npm-registry-fetch - otp: obj.otp, - offline: obj.offline, - preferOffline: getPreferOffline(obj), - preferOnline: getPreferOnline(obj), - strictSSL: obj['strict-ssl'], - defaultTag: obj.tag, - userAgent: obj['user-agent'], - - // yes, it's fine, just do it, jeez, stop asking - yes: obj.yes, - - ...getScopesAndAuths(obj), - - // npm fund exclusive option to select an item from a funding list - which: obj.which, - - // socks proxy can be configured in https-proxy or proxy field - // note that the various (HTTPS_|HTTP_|]PROXY environs will be - // respected if this is not set. - proxy: obj['https-proxy'] || obj.proxy, - noProxy: obj.noproxy, -}) - -const flatOptions = npm => npm.flatOptions || Object.freeze({ - // flatten the config object - ...flatten(npm.config.list[0]), - - // Note that many of these do not come from configs or cli flags - // per se, though they may be implied or defined by them. - log, - npmSession, - dmode: npm.modes.exec, - fmode: npm.modes.file, - umask: npm.modes.umask, - hashAlgorithm: 'sha1', // XXX should this be sha512? - color: !!npm.color, - projectScope: npm.projectScope, - npmVersion: npm.version, - - // npm.command is not set until AFTER flatOptions are defined - // so we need to make this a getter. - get npmCommand () { - return npm.command - }, - - tmp: npm.tmp, - prefix: npm.prefix, - globalPrefix: npm.globalPrefix, - localPrefix: npm.localPrefix, - npmBin: require.main && require.main.filename, - nodeBin: process.env.NODE || process.execPath, - get tag () { - return npm.config.get('tag') - }, -}) - -const getPreferOnline = obj => { - const po = obj['prefer-online'] - if (po !== undefined) - return po - - return obj['cache-max'] <= 0 -} - -const getPreferOffline = obj => { - const po = obj['prefer-offline'] - if (po !== undefined) - return po - - return obj['cache-min'] >= 9999 -} - -// pull out all the @scope:<key> and //host:key config fields -// these are used by npm-registry-fetch for authing against registries -const getScopesAndAuths = obj => { - const scopesAndAuths = {} - // pull out all the @scope:... configs into a flat object. - for (const key in obj) { - if (/@.*:registry$/i.test(key) || /^\/\//.test(key)) - scopesAndAuths[key] = obj[key] - } - return scopesAndAuths -} - -module.exports = Object.assign(flatOptions, { flatten }) diff --git a/deps/npm/lib/utils/lifecycle-cmd.js b/deps/npm/lib/utils/lifecycle-cmd.js index 1917bef3678551..2c5b89dfcdd048 100644 --- a/deps/npm/lib/utils/lifecycle-cmd.js +++ b/deps/npm/lib/utils/lifecycle-cmd.js @@ -10,5 +10,9 @@ class LifecycleCmd extends BaseCommand { exec (args, cb) { this.npm.commands['run-script']([this.constructor.name, ...args], cb) } + + execWorkspaces (args, filters, cb) { + this.npm.commands['run-script']([this.constructor.name, ...args], cb) + } } module.exports = LifecycleCmd diff --git a/deps/npm/lib/utils/npm-usage.js b/deps/npm/lib/utils/npm-usage.js index b77bca7bec1a86..bc397cb4d95e66 100644 --- a/deps/npm/lib/utils/npm-usage.js +++ b/deps/npm/lib/utils/npm-usage.js @@ -1,14 +1,12 @@ -const didYouMean = require('./did-you-mean.js') const { dirname } = require('path') const { cmdList } = require('./cmd-list') -module.exports = (npm, valid = true) => { - npm.config.set('loglevel', 'silent') +module.exports = (npm) => { const usesBrowser = npm.config.get('viewer') === 'browser' ? ' (in a browser)' : '' - npm.log.level = 'silent' - npm.output(` -Usage: npm <command> + return `npm <command> + +Usage: npm install install all the dependencies in your project npm install <foo> add the <foo> dependency to your project @@ -20,7 +18,7 @@ npm help <term> search for help on <term>${usesBrowser} npm help npm more involved overview${usesBrowser} All commands: -${npm.config.get('long') ? usages(npm) : ('\n ' + wrap(cmdList))} +${allCommands(npm)} Specify configs in the ini-formatted file: ${npm.config.get('userconfig')} @@ -29,14 +27,13 @@ or on the command line via: npm <command> --key=value More configuration info: npm help config Configuration fields: npm help 7 config -npm@${npm.version} ${dirname(dirname(__dirname))} -`) - - if (npm.argv.length >= 1) - npm.output(didYouMean(npm.argv[0], cmdList)) +npm@${npm.version} ${dirname(dirname(__dirname))}` +} - if (!valid) - process.exitCode = 1 +const allCommands = (npm) => { + if (npm.config.get('long')) + return usages(npm) + return ('\n ' + wrap(cmdList)) } const wrap = (arr) => { diff --git a/deps/npm/lib/utils/ping.js b/deps/npm/lib/utils/ping.js index f5f7fcc6a62586..00956d0c1630ce 100644 --- a/deps/npm/lib/utils/ping.js +++ b/deps/npm/lib/utils/ping.js @@ -1,7 +1,7 @@ // ping the npm registry // used by the ping and doctor commands const fetch = require('npm-registry-fetch') -module.exports = async (opts) => { - const res = await fetch('/-/ping?write=true', opts) +module.exports = async (flatOptions) => { + const res = await fetch('/-/ping?write=true', flatOptions) return res.json().catch(() => ({})) } diff --git a/deps/npm/lib/utils/read-local-package.js b/deps/npm/lib/utils/read-local-package.js index c31bca994704ce..21506ca180a0f3 100644 --- a/deps/npm/lib/utils/read-local-package.js +++ b/deps/npm/lib/utils/read-local-package.js @@ -1,11 +1,12 @@ const { resolve } = require('path') const readJson = require('read-package-json-fast') async function readLocalPackageName (npm) { - if (npm.flatOptions.global) + if (npm.config.get('global')) return - const filepath = resolve(npm.flatOptions.prefix, 'package.json') - return (await readJson(filepath)).name + const filepath = resolve(npm.prefix, 'package.json') + const json = await readJson(filepath) + return json.name } module.exports = readLocalPackageName diff --git a/deps/npm/lib/version.js b/deps/npm/lib/version.js index 2eda9d11b737c4..18b7d7d6c5d840 100644 --- a/deps/npm/lib/version.js +++ b/deps/npm/lib/version.js @@ -2,6 +2,10 @@ const libversion = require('libnpmversion') const BaseCommand = require('./base-command.js') class Version extends BaseCommand { + static get description () { + return 'Bump a package version' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'version' @@ -45,7 +49,7 @@ class Version extends BaseCommand { } async change (args) { - const prefix = this.npm.flatOptions.tagVersionPrefix + const prefix = this.npm.config.get('tag-version-prefix') const version = await libversion(args[0], { ...this.npm.flatOptions, path: this.npm.prefix, @@ -71,7 +75,7 @@ class Version extends BaseCommand { for (const [key, version] of Object.entries(process.versions)) results[key] = version - if (this.npm.flatOptions.json) + if (this.npm.config.get('json')) this.npm.output(JSON.stringify(results, null, 2)) else this.npm.output(results) diff --git a/deps/npm/lib/view.js b/deps/npm/lib/view.js index 0a6688fc2b13d2..e0df1e231f9d8f 100644 --- a/deps/npm/lib/view.js +++ b/deps/npm/lib/view.js @@ -19,6 +19,11 @@ const readJson = async file => jsonParse(await readFile(file, 'utf8')) const BaseCommand = require('./base-command.js') class View extends BaseCommand { + /* istanbul ignore next - see test/lib/load-all-commands.js */ + static get description () { + return 'View registry info' + } + /* istanbul ignore next - see test/lib/load-all-commands.js */ static get name () { return 'view' @@ -41,9 +46,9 @@ class View extends BaseCommand { fullMetadata: true, preferOnline: true, } - const { defaultTag } = config const spec = npa(opts.conf.argv.remain[2]) const pckmnt = await packument(spec, config) + const defaultTag = this.npm.config.get('tag') const dv = pckmnt.versions[pckmnt['dist-tags'][defaultTag]] pckmnt.versions = Object.keys(pckmnt.versions).sort(semver.compareLoose) @@ -99,7 +104,7 @@ class View extends BaseCommand { const name = nv.name const local = (name === '.' || !name) - if (opts.global && local) + if (this.npm.config.get('global') && local) throw new Error('Cannot use view command in global mode.') if (local) { @@ -114,7 +119,7 @@ class View extends BaseCommand { } // get the data about this package - let version = nv.rawSpec || this.npm.flatOptions.defaultTag + let version = nv.rawSpec || this.npm.config.get('tag') const pckmnt = await packument(nv, opts) @@ -159,42 +164,43 @@ class View extends BaseCommand { } if ( - !opts.json && + !this.npm.config.get('json') && args.length === 1 && args[0] === '' ) { // general view pckmnt.version = version await Promise.all( - results.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]][''], opts)) + results.map((v) => this.prettyView(pckmnt, v[Object.keys(v)[0]][''])) ) return retval } else { // view by field name - await this.printData(retval, pckmnt._id, opts) + await this.printData(retval, pckmnt._id) return retval } } - async printData (data, name, opts) { + async printData (data, name) { const versions = Object.keys(data) let msg = '' let msgJson = [] const includeVersions = versions.length > 1 let includeFields + const json = this.npm.config.get('json') versions.forEach((v) => { const fields = Object.keys(data[v]) includeFields = includeFields || (fields.length > 1) - if (opts.json) + if (json) msgJson.push({}) fields.forEach((f) => { let d = cleanup(data[v][f]) - if (fields.length === 1 && opts.json) + if (fields.length === 1 && json) msgJson[msgJson.length - 1][f] = d if (includeVersions || includeFields || typeof d !== 'string') { - if (opts.json) + if (json) msgJson[msgJson.length - 1][f] = d else { d = inspect(d, { @@ -204,10 +210,10 @@ class View extends BaseCommand { maxArrayLength: null, }) } - } else if (typeof d === 'string' && opts.json) + } else if (typeof d === 'string' && json) d = JSON.stringify(d) - if (!opts.json) { + if (!json) { if (f && includeFields) f += ' = ' msg += (includeVersions ? name + '@' + v + ' ' : '') + @@ -216,7 +222,7 @@ class View extends BaseCommand { }) }) - if (opts.json) { + if (json) { if (msgJson.length && Object.keys(msgJson[0]).length === 1) { const k = Object.keys(msgJson[0])[0] msgJson = msgJson.map(m => m[k]) @@ -236,9 +242,9 @@ class View extends BaseCommand { console.log(msg.trim()) } - async prettyView (packument, manifest, opts) { + async prettyView (packument, manifest) { // More modern, pretty printing of default view - const unicode = opts.unicode + const unicode = this.npm.config.get('unicode') const tags = [] Object.keys(packument['dist-tags']).forEach((t) => { diff --git a/deps/npm/lib/whoami.js b/deps/npm/lib/whoami.js index 2322c5fd80e5da..82c4520d9e8830 100644 --- a/deps/npm/lib/whoami.js +++ b/deps/npm/lib/whoami.js @@ -3,18 +3,18 @@ const getIdentity = require('./utils/get-identity.js') const BaseCommand = require('./base-command.js') class Whoami extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get name () { - return 'whoami' + static get description () { + return 'Display npm username' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get description () { - return 'prints username according to given registry' + static get name () { + return 'whoami' } /* istanbul ignore next - see test/lib/load-all-commands.js */ - static get usage () { - return ['[--registry <registry>]'] + static get params () { + return ['registry'] } exec (args, cb) { @@ -22,9 +22,10 @@ class Whoami extends BaseCommand { } async whoami (args) { - const opts = this.npm.flatOptions - const username = await getIdentity(this.npm, opts) - this.npm.output(opts.json ? JSON.stringify(username) : username) + const username = await getIdentity(this.npm, this.npm.flatOptions) + this.npm.output( + this.npm.config.get('json') ? JSON.stringify(username) : username + ) } } module.exports = Whoami diff --git a/deps/npm/lib/workspaces/get-workspaces.js b/deps/npm/lib/workspaces/get-workspaces.js new file mode 100644 index 00000000000000..64812d5403576c --- /dev/null +++ b/deps/npm/lib/workspaces/get-workspaces.js @@ -0,0 +1,33 @@ +const { resolve } = require('path') +const mapWorkspaces = require('@npmcli/map-workspaces') +const minimatch = require('minimatch') +const rpj = require('read-package-json-fast') + +const getWorkspaces = async (filters, { path }) => { + const pkg = await rpj(resolve(path, 'package.json')) + const workspaces = await mapWorkspaces({ cwd: path, pkg }) + const res = filters.length ? new Map() : workspaces + + for (const filterArg of filters) { + for (const [workspaceName, workspacePath] of workspaces.entries()) { + if (filterArg === workspaceName + || resolve(path, filterArg) === workspacePath + || minimatch(workspacePath, `${resolve(path, filterArg)}/*`)) + res.set(workspaceName, workspacePath) + } + } + + if (!res.size) { + let msg = '!' + if (filters.length) { + msg = `:\n ${filters.reduce( + (res, filterArg) => `${res} --workspace=${filterArg}`, '')}` + } + + throw new Error(`No workspaces found${msg}`) + } + + return res +} + +module.exports = getWorkspaces diff --git a/deps/npm/man/man1/npm-adduser.1 b/deps/npm/man/man1/npm-adduser.1 index f690c1f53ddbe7..0701a16aeb50fe 100644 --- a/deps/npm/man/man1/npm-adduser.1 +++ b/deps/npm/man/man1/npm-adduser.1 @@ -10,6 +10,8 @@ npm adduser [\-\-registry=url] [\-\-scope=@orgname] [\-\-always\-auth] [\-\-auth aliases: login, add\-user .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Create or verify a user named \fB<username>\fP in the specified registry, and diff --git a/deps/npm/man/man1/npm-bin.1 b/deps/npm/man/man1/npm-bin.1 index cf1692e8943f12..d8dfc0542c3dad 100644 --- a/deps/npm/man/man1/npm-bin.1 +++ b/deps/npm/man/man1/npm-bin.1 @@ -8,6 +8,8 @@ npm bin [\-g|\-\-global] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Print the folder where npm will install executables\. diff --git a/deps/npm/man/man1/npm-cache.1 b/deps/npm/man/man1/npm-cache.1 index 4f0b0f19fc97fe..852c24a13d1d9c 100644 --- a/deps/npm/man/man1/npm-cache.1 +++ b/deps/npm/man/man1/npm-cache.1 @@ -16,6 +16,8 @@ aliases: npm cache clear, npm cache rm npm cache verify .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Used to add, list, or clean the npm cache folder\. diff --git a/deps/npm/man/man1/npm-completion.1 b/deps/npm/man/man1/npm-completion.1 index bc206f52421c5b..5be583ea6813e1 100644 --- a/deps/npm/man/man1/npm-completion.1 +++ b/deps/npm/man/man1/npm-completion.1 @@ -8,6 +8,8 @@ source <(npm completion) .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Enables tab\-completion in all npm commands\. diff --git a/deps/npm/man/man1/npm-config.1 b/deps/npm/man/man1/npm-config.1 index 3e29c71f507275..f47947aff1d9e9 100644 --- a/deps/npm/man/man1/npm-config.1 +++ b/deps/npm/man/man1/npm-config.1 @@ -16,6 +16,8 @@ npm get [<key> [<key> \.\.\.]] alias: c .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P npm gets its config settings from the command line, environment diff --git a/deps/npm/man/man1/npm-dedupe.1 b/deps/npm/man/man1/npm-dedupe.1 index 70267ff45ec6c0..915b7b110cfedf 100644 --- a/deps/npm/man/man1/npm-dedupe.1 +++ b/deps/npm/man/man1/npm-dedupe.1 @@ -1,6 +1,6 @@ .TH "NPM\-DEDUPE" "1" "March 2021" "" "" .SH "NAME" -\fBnpm-dedupe\fR \- Reduce duplication +\fBnpm-dedupe\fR \- Reduce duplication in the package tree .SS Synopsis .P .RS 2 @@ -8,7 +8,7 @@ npm dedupe npm ddp -aliases: find\-dupes, ddp +aliases: ddp .fi .RE .SS Description @@ -78,6 +78,8 @@ Using \fBnpm find\-dupes\fP will run the command in \fB\-\-dry\-run\fP mode\. .SS See Also .RS 0 .IP \(bu 2 +npm find\-dupes \fI/cli\-commands/find\-dupes\fR +.IP \(bu 2 npm ls \fI/cli\-commands/ls\fR .IP \(bu 2 npm update \fI/cli\-commands/update\fR diff --git a/deps/npm/man/man1/npm-deprecate.1 b/deps/npm/man/man1/npm-deprecate.1 index fa95af0a856c80..3d8876662f1986 100644 --- a/deps/npm/man/man1/npm-deprecate.1 +++ b/deps/npm/man/man1/npm-deprecate.1 @@ -8,6 +8,8 @@ npm deprecate <pkg>[@<version range>] <message> .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P This command will update the npm registry entry for a package, providing a diff --git a/deps/npm/man/man1/npm-doctor.1 b/deps/npm/man/man1/npm-doctor.1 index c45944c2b3e841..c317f03fae1246 100644 --- a/deps/npm/man/man1/npm-doctor.1 +++ b/deps/npm/man/man1/npm-doctor.1 @@ -8,6 +8,8 @@ npm doctor .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P \fBnpm doctor\fP runs a set of checks to ensure that your npm installation has diff --git a/deps/npm/man/man1/npm-edit.1 b/deps/npm/man/man1/npm-edit.1 index 3c8d1ecd105812..d0461afc8f1c97 100644 --- a/deps/npm/man/man1/npm-edit.1 +++ b/deps/npm/man/man1/npm-edit.1 @@ -8,6 +8,8 @@ npm edit <pkg> .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Selects a dependency in the current project and opens the package folder in diff --git a/deps/npm/man/man1/npm-exec.1 b/deps/npm/man/man1/npm-exec.1 index 9b21bfe438f42b..a9d9708c70035e 100644 --- a/deps/npm/man/man1/npm-exec.1 +++ b/deps/npm/man/man1/npm-exec.1 @@ -9,6 +9,7 @@ npm exec \-\- <pkg>[@<version>] [args\.\.\.] npm exec \-\-package=<pkg>[@<version>] \-\- <cmd> [args\.\.\.] npm exec \-c '<cmd> [args\.\.\.]' npm exec \-\-package=foo \-c '<cmd> [args\.\.\.]' +npm exec [\-ws] [\-w <workspace\-name] [args\.\.\.] npx <pkg>[@<specifier>] [args\.\.\.] npx \-p <pkg>[@<specifier>] <cmd> [args\.\.\.] @@ -161,6 +162,76 @@ $ npm x \-c 'eslint && say "hooray, lint passed"' $ npx \-c 'eslint && say "hooray, lint passed"' .fi .RE +.SS Workspaces support +.P +You may use the \fBworkspace\fP or \fBworkspaces\fP configs in order to run an +arbitrary command from an npm package (either one installed locally, or fetched +remotely) in the context of the specified workspaces\. +If no positional argument or \fB\-\-call\fP option is provided, it will open an +interactive subshell in the context of each of these configured workspaces one +at a time\. +.P +Given a project with configured workspaces, e\.g: +.P +.RS 2 +.nf +\|\. ++\-\- package\.json +`\-\- packages + +\-\- a + | `\-\- package\.json + +\-\- b + | `\-\- package\.json + `\-\- c + `\-\- package\.json +.fi +.RE +.P +Assuming the workspace configuration is properly set up at the root level +\fBpackage\.json\fP file\. e\.g: +.P +.RS 2 +.nf +{ + "workspaces": [ "\./packages/*" ] +} +.fi +.RE +.P +You can execute an arbitrary command from a package in the context of each of +the configured workspaces when using the \fBworkspaces\fP configuration options, +in this example we're using \fBeslint\fR to lint any js file found within each +workspace folder: +.P +.RS 2 +.nf +npm exec \-ws \-\- eslint \./*\.js +.fi +.RE +.SS Filtering workspaces +.P +It's also possible to execute a command in a single workspace using the +\fBworkspace\fP config along with a name or directory path: +.P +.RS 2 +.nf +npm exec \-\-workspace=a \-\- eslint \./*\.js +.fi +.RE +.P +The \fBworkspace\fP config can also be specified multiple times in order to run a +specific script in the context of multiple workspaces\. When defining values for +the \fBworkspace\fP config in the command line, it also possible to use \fB\-w\fP as a +shorthand, e\.g: +.P +.RS 2 +.nf +npm exec \-w a \-w b \-\- eslint \./*\.js +.fi +.RE +.P +This last command will run the \fBeslint\fP command in both \fB\|\./packages/a\fP and +\fB\|\./packages/b\fP folders\. .SS Compatibility with Older npx Versions .P The \fBnpx\fP binary was rewritten in npm v7\.0\.0, and the standalone \fBnpx\fP @@ -218,6 +289,44 @@ requested from the server\. To force full offline mode, use \fBoffline\fP\|\. .P Forces full offline mode\. Any packages not locally cached will result in an error\. +.SS workspace +.RS 0 +.IP \(bu 2 +Alias: \fB\-w\fP +.IP \(bu 2 +Type: Array +.IP \(bu 2 +Default: \fB[]\fP + +.RE +.P +Enable running scripts in the context of workspaces while also filtering by +the provided names or paths provided\. +.P +Valid values for the \fBworkspace\fP config are either: +.RS 0 +.IP \(bu 2 +Workspace names +.IP \(bu 2 +Path to a workspace directory +.IP \(bu 2 +Path to a parent workspace directory (will result to selecting all of the +children workspaces) + +.RE +.SS workspaces +.RS 0 +.IP \(bu 2 +Alias: \fB\-ws\fP +.IP \(bu 2 +Type: Boolean +.IP \(bu 2 +Default: \fBfalse\fP + +.RE +.P +Run scripts in the context of all configured workspaces for the current +project\. .SS See Also .RS 0 .IP \(bu 2 diff --git a/deps/npm/man/man1/npm-explore.1 b/deps/npm/man/man1/npm-explore.1 index 6b04f3be338741..3a0711e00c50af 100644 --- a/deps/npm/man/man1/npm-explore.1 +++ b/deps/npm/man/man1/npm-explore.1 @@ -8,6 +8,8 @@ npm explore <pkg> [ \-\- <command>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Spawn a subshell in the directory of the installed package specified\. diff --git a/deps/npm/man/man1/npm-find-dupes.1 b/deps/npm/man/man1/npm-find-dupes.1 new file mode 100644 index 00000000000000..6ecb4b90d58dbf --- /dev/null +++ b/deps/npm/man/man1/npm-find-dupes.1 @@ -0,0 +1,26 @@ +.TH "NPM\-FIND\-DUPES" "1" "March 2021" "" "" +.SH "NAME" +\fBnpm-find-dupes\fR \- Find duplication in the package tree +.SS Synopsis +.P +.RS 2 +.nf +npm find\-dupes +.fi +.RE +.SS Description +.P +Runs \fBnpm dedupe\fP in \fB\-\-dry\-run\fP mode, making npm only output the +duplications, without actually changing the package tree\. +.SS See Also +.RS 0 +.IP \(bu 2 +npm dedupe \fI/cli\-commands/dedupe\fR +.IP \(bu 2 +npm ls \fI/cli\-commands/ls\fR +.IP \(bu 2 +npm update \fI/cli\-commands/update\fR +.IP \(bu 2 +npm install \fI/cli\-commands/install\fR + +.RE diff --git a/deps/npm/man/man1/npm-help-search.1 b/deps/npm/man/man1/npm-help-search.1 index e0dc5419d3e86d..1a33f080d6f871 100644 --- a/deps/npm/man/man1/npm-help-search.1 +++ b/deps/npm/man/man1/npm-help-search.1 @@ -8,6 +8,8 @@ npm help\-search <text> .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P This command will search the npm markdown documentation files for the terms diff --git a/deps/npm/man/man1/npm-help.1 b/deps/npm/man/man1/npm-help.1 index 6e0ad91a6618bd..f6ca3a84a2ac7a 100644 --- a/deps/npm/man/man1/npm-help.1 +++ b/deps/npm/man/man1/npm-help.1 @@ -8,6 +8,8 @@ npm help <term> [<terms\.\.>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P If supplied a topic, then show the appropriate documentation page\. diff --git a/deps/npm/man/man1/npm-hook.1 b/deps/npm/man/man1/npm-hook.1 index f92de181308fb5..f4f2b3976211b5 100644 --- a/deps/npm/man/man1/npm-hook.1 +++ b/deps/npm/man/man1/npm-hook.1 @@ -11,6 +11,8 @@ npm hook update <id> <url> [secret] npm hook rm <id> .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Allows you to manage npm diff --git a/deps/npm/man/man1/npm-init.1 b/deps/npm/man/man1/npm-init.1 index 05aa13f0139013..b67899ce80fdfc 100644 --- a/deps/npm/man/man1/npm-init.1 +++ b/deps/npm/man/man1/npm-init.1 @@ -1,6 +1,6 @@ .TH "NPM\-INIT" "1" "March 2021" "" "" .SH "NAME" -\fBnpm-init\fR \- create a package\.json file +\fBnpm-init\fR \- Create a package\.json file .SS Synopsis .P .RS 2 diff --git a/deps/npm/man/man1/npm-logout.1 b/deps/npm/man/man1/npm-logout.1 index 777467cbe28f09..a917137bcbfa91 100644 --- a/deps/npm/man/man1/npm-logout.1 +++ b/deps/npm/man/man1/npm-logout.1 @@ -8,6 +8,8 @@ npm logout [\-\-registry=<url>] [\-\-scope=<@scope>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P When logged into a registry that supports token\-based authentication, tell diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index c427b6b780fffd..84bd11f2d86ac8 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@7\.6\.3 /path/to/npm +npm@7\.7\.0 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm-org.1 b/deps/npm/man/man1/npm-org.1 index 27f3fab580f34d..cb65355572262c 100644 --- a/deps/npm/man/man1/npm-org.1 +++ b/deps/npm/man/man1/npm-org.1 @@ -10,6 +10,8 @@ npm org rm <orgname> <username> npm org ls <orgname> [<username>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Example .P Add a new developer to an org: diff --git a/deps/npm/man/man1/npm-owner.1 b/deps/npm/man/man1/npm-owner.1 index bd0758d701f0bc..cbd61fd92597a0 100644 --- a/deps/npm/man/man1/npm-owner.1 +++ b/deps/npm/man/man1/npm-owner.1 @@ -12,6 +12,8 @@ npm owner ls [<@scope>/]<pkg> aliases: author .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Manage ownership of published packages\. diff --git a/deps/npm/man/man1/npm-ping.1 b/deps/npm/man/man1/npm-ping.1 index 9357497758146d..fa9a2f1ff5abb8 100644 --- a/deps/npm/man/man1/npm-ping.1 +++ b/deps/npm/man/man1/npm-ping.1 @@ -8,6 +8,8 @@ npm ping [\-\-registry <registry>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Ping the configured or given npm registry and verify authentication\. diff --git a/deps/npm/man/man1/npm-prefix.1 b/deps/npm/man/man1/npm-prefix.1 index 85178a2c06f30f..c0685305cafe5c 100644 --- a/deps/npm/man/man1/npm-prefix.1 +++ b/deps/npm/man/man1/npm-prefix.1 @@ -8,6 +8,8 @@ npm prefix [\-g] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Print the local prefix to standard output\. This is the closest parent directory diff --git a/deps/npm/man/man1/npm-profile.1 b/deps/npm/man/man1/npm-profile.1 index 470b8eaf71ec33..f17ad465630b1d 100644 --- a/deps/npm/man/man1/npm-profile.1 +++ b/deps/npm/man/man1/npm-profile.1 @@ -12,6 +12,8 @@ npm profile enable\-2fa [auth\-and\-writes|auth\-only] npm profile disable\-2fa .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Change your profile information on the registry\. Note that this command diff --git a/deps/npm/man/man1/npm-run-script.1 b/deps/npm/man/man1/npm-run-script.1 index e19a4c08ab62f4..b399e5d9d133fe 100644 --- a/deps/npm/man/man1/npm-run-script.1 +++ b/deps/npm/man/man1/npm-run-script.1 @@ -6,6 +6,8 @@ .RS 2 .nf npm run\-script <command> [\-\-if\-present] [\-\-silent] [\-\- <args>] +npm run\-script <command> [\-\-workspace=<workspace\-name>] +npm run\-script <command> [\-\-workspaces] aliases: run, rum, urn .fi @@ -81,6 +83,72 @@ found in the \fBPATH\fP\|\. If you try to run a script without having a \fBnode_modules\fP directory and it fails, you will be given a warning to run \fBnpm install\fP, just in case you've forgotten\. +.SS Workspaces support +.P +You may use the \fBworkspace\fP or \fBworkspaces\fP configs in order to run an +arbitrary command from a package's \fB"scripts"\fP object in the context of the +specified workspaces\. If no \fB"command"\fP is provided, it will list the available +scripts for each of these configured workspaces\. +.P +Given a project with configured workspaces, e\.g: +.P +.RS 2 +.nf +\|\. ++\-\- package\.json +`\-\- packages + +\-\- a + | `\-\- package\.json + +\-\- b + | `\-\- package\.json + `\-\- c + `\-\- package\.json +.fi +.RE +.P +Assuming the workspace configuration is properly set up at the root level +\fBpackage\.json\fP file\. e\.g: +.P +.RS 2 +.nf +{ + "workspaces": [ "\./packages/*" ] +} +.fi +.RE +.P +And that each of the configured workspaces has a configured \fBtest\fP script, +we can run tests in all of them using the \fBworkspaces\fP config: +.P +.RS 2 +.nf +npm test \-\-workspaces +.fi +.RE +.SS Filtering workspaces +.P +It's also possible to run a script in a single workspace using the \fBworkspace\fP +config along with a name or directory path: +.P +.RS 2 +.nf +npm test \-\-workspace=a +.fi +.RE +.P +The \fBworkspace\fP config can also be specified multiple times in order to run a +specific script in the context of multiple workspaces\. When defining values for +the \fBworkspace\fP config in the command line, it also possible to use \fB\-w\fP as a +shorthand, e\.g: +.P +.RS 2 +.nf +npm test \-w a \-w b +.fi +.RE +.P +This last command will run \fBtest\fP in both \fB\|\./packages/a\fP and \fB\|\./packages/b\fP +packages\. .SS Configuration .SS if\-present .RS 0 @@ -125,6 +193,44 @@ Default: false .RE .P You can use the \fB\-\-silent\fP flag to prevent showing \fBnpm ERR!\fP output on error\. +.SS workspace +.RS 0 +.IP \(bu 2 +Alias: \fB\-w\fP +.IP \(bu 2 +Type: Array +.IP \(bu 2 +Default: \fB[]\fP + +.RE +.P +Enable running scripts in the context of workspaces while also filtering by +the provided names or paths provided\. +.P +Valid values for the \fBworkspace\fP config are either: +.RS 0 +.IP \(bu 2 +Workspace names +.IP \(bu 2 +Path to a workspace directory +.IP \(bu 2 +Path to a parent workspace directory (will result to selecting all of the +children workspaces) + +.RE +.SS workspaces +.RS 0 +.IP \(bu 2 +Alias: \fB\-ws\fP +.IP \(bu 2 +Type: Boolean +.IP \(bu 2 +Default: \fBfalse\fP + +.RE +.P +Run scripts in the context of all configured workspaces for the current +project\. .SS See Also .RS 0 .IP \(bu 2 diff --git a/deps/npm/man/man1/npm-search.1 b/deps/npm/man/man1/npm-search.1 index 3dd493edb3c974..8253e86dca6479 100644 --- a/deps/npm/man/man1/npm-search.1 +++ b/deps/npm/man/man1/npm-search.1 @@ -10,6 +10,8 @@ npm search [\-l|\-\-long] [\-\-json] [\-\-parseable] [\-\-no\-description] [sear aliases: s, se, find .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Search the registry for packages matching the search terms\. \fBnpm search\fP diff --git a/deps/npm/man/man1/npm-shrinkwrap.1 b/deps/npm/man/man1/npm-shrinkwrap.1 index 75b4d3e2f7fe87..7800d2f53e4a0b 100644 --- a/deps/npm/man/man1/npm-shrinkwrap.1 +++ b/deps/npm/man/man1/npm-shrinkwrap.1 @@ -8,6 +8,8 @@ npm shrinkwrap .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P This command repurposes \fBpackage\-lock\.json\fP into a publishable diff --git a/deps/npm/man/man1/npm-star.1 b/deps/npm/man/man1/npm-star.1 index 8fdb41e9d5a068..5bd9d6a250135c 100644 --- a/deps/npm/man/man1/npm-star.1 +++ b/deps/npm/man/man1/npm-star.1 @@ -8,6 +8,8 @@ npm star [<pkg>\.\.\.] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P "Starring" a package means that you have some interest in it\. It's diff --git a/deps/npm/man/man1/npm-stars.1 b/deps/npm/man/man1/npm-stars.1 index 4d0d3d5fccc0f7..8e5f7b3b78a6c9 100644 --- a/deps/npm/man/man1/npm-stars.1 +++ b/deps/npm/man/man1/npm-stars.1 @@ -8,6 +8,8 @@ npm stars [<user>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P If you have starred a lot of neat things and want to find them again diff --git a/deps/npm/man/man1/npm-team.1 b/deps/npm/man/man1/npm-team.1 index 3331bb1b41c281..82f5ab3be787b7 100644 --- a/deps/npm/man/man1/npm-team.1 +++ b/deps/npm/man/man1/npm-team.1 @@ -14,6 +14,8 @@ npm team rm <scope:team> <user> npm team ls <scope>|<scope:team> .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Used to manage teams in organizations, and change team memberships\. Does not diff --git a/deps/npm/man/man1/npm-token.1 b/deps/npm/man/man1/npm-token.1 index 1da8ce9f7199b9..f98f37e6eb018a 100644 --- a/deps/npm/man/man1/npm-token.1 +++ b/deps/npm/man/man1/npm-token.1 @@ -10,6 +10,8 @@ npm token revoke <id|token> .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P This lets you list, create and revoke authentication tokens\. diff --git a/deps/npm/man/man1/npm-unstar.1 b/deps/npm/man/man1/npm-unstar.1 index a08ab4c5af1754..b3b7c698b3f5c4 100644 --- a/deps/npm/man/man1/npm-unstar.1 +++ b/deps/npm/man/man1/npm-unstar.1 @@ -8,6 +8,8 @@ npm unstar [<pkg>\.\.\.] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P "Unstarring" a package is the opposite of npm help \fBstar\fP, diff --git a/deps/npm/man/man1/npm-whoami.1 b/deps/npm/man/man1/npm-whoami.1 index 6ade4045c0ff90..3df11cb69d6efd 100644 --- a/deps/npm/man/man1/npm-whoami.1 +++ b/deps/npm/man/man1/npm-whoami.1 @@ -8,6 +8,8 @@ npm whoami [\-\-registry <registry>] .fi .RE +.P +Note: This command is unaware of workspaces\. .SS Description .P Print the \fBusername\fP config to standard output\. diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index c85c4759dc9c82..9b3b58ce87676d 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -10,7 +10,7 @@ npm <command> [args] .RE .SS Version .P -7\.6\.3 +7\.7\.0 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts diff --git a/deps/npm/man/man5/package-json.5 b/deps/npm/man/man5/package-json.5 index a244fa7e4dfb69..859c3de2d5197e 100644 --- a/deps/npm/man/man5/package-json.5 +++ b/deps/npm/man/man5/package-json.5 @@ -373,6 +373,8 @@ This should be a module relative to the root of your package folder\. .P For most modules, it makes the most sense to have a main script and often not much else\. +.P +If \fBmain\fP is not set it defaults to \fBindex\.js\fP in the packages root folder\. .SS browser .P If your module is meant to be used client\-side the browser field should be @@ -620,8 +622,7 @@ tarball or git URL\. \fBPlease do not put test harnesses or transpilers or other "development" time tools in your \fBdependencies\fP object\.\fR See \fBdevDependencies\fP, below\. .P -See semver \fI[/using\-npm/semver](https://github\.com/npm/node\-semver#versions)\fR -for more details about specifying version ranges\. +See npm help semver for more details about specifying version ranges\. .RS 0 .IP \(bu 2 \fBversion\fP Must match \fBversion\fP exactly diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index 6e826d0787ae17..a5f3095bcbd9e2 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -56,57 +56,89 @@ internal to npm, and are defaults if nothing else is specified\. .SS Shorthands and Other CLI Niceties .P The following shorthands are parsed on the command\-line: +<!\-\- AUTOGENERATED CONFIG SHORTHANDS START \-\-> +<!\-\- automatically generated, do not edit manually \-\-> .RS 0 .IP \(bu 2 -\fB\-v\fP: \fB\-\-version\fP +\fB\-a\fP: \fB\-\-all\fP +.IP \(bu 2 +\fB\-\-enjoy\-by\fP: \fB\-\-before\fP .IP \(bu 2 -\fB\-h\fP, \fB\-?\fP, \fB\-\-help\fP, \fB\-H\fP: \fB\-\-usage\fP +\fB\-c\fP: \fB\-\-call\fP .IP \(bu 2 -\fB\-s\fP, \fB\-\-silent\fP: \fB\-\-loglevel silent\fP +\fB\-\-desc\fP: \fB\-\-description\fP +.IP \(bu 2 +\fB\-f\fP: \fB\-\-force\fP .IP \(bu 2 -\fB\-q\fP, \fB\-\-quiet\fP: \fB\-\-loglevel warn\fP +\fB\-g\fP: \fB\-\-global\fP .IP \(bu 2 \fB\-d\fP: \fB\-\-loglevel info\fP .IP \(bu 2 -\fB\-dd\fP, \fB\-\-verbose\fP: \fB\-\-loglevel verbose\fP +\fB\-s\fP: \fB\-\-loglevel silent\fP .IP \(bu 2 -\fB\-ddd\fP: \fB\-\-loglevel silly\fP +\fB\-\-silent\fP: \fB\-\-loglevel silent\fP .IP \(bu 2 -\fB\-g\fP: \fB\-\-global\fP +\fB\-\-ddd\fP: \fB\-\-loglevel silly\fP .IP \(bu 2 -\fB\-C\fP: \fB\-\-prefix\fP +\fB\-\-dd\fP: \fB\-\-loglevel verbose\fP +.IP \(bu 2 +\fB\-\-verbose\fP: \fB\-\-loglevel verbose\fP +.IP \(bu 2 +\fB\-q\fP: \fB\-\-loglevel warn\fP +.IP \(bu 2 +\fB\-\-quiet\fP: \fB\-\-loglevel warn\fP .IP \(bu 2 \fB\-l\fP: \fB\-\-long\fP .IP \(bu 2 \fB\-m\fP: \fB\-\-message\fP .IP \(bu 2 -\fB\-p\fP, \fB\-\-porcelain\fP: \fB\-\-parseable\fP +\fB\-\-local\fP: \fB\-\-no\-global\fP .IP \(bu 2 -\fB\-reg\fP: \fB\-\-registry\fP +\fB\-n\fP: \fB\-\-no\-yes\fP .IP \(bu 2 -\fB\-f\fP: \fB\-\-force\fP +\fB\-\-no\fP: \fB\-\-no\-yes\fP +.IP \(bu 2 +\fB\-p\fP: \fB\-\-parseable\fP +.IP \(bu 2 +\fB\-\-porcelain\fP: \fB\-\-parseable\fP +.IP \(bu 2 +\fB\-C\fP: \fB\-\-prefix\fP +.IP \(bu 2 +\fB\-\-readonly\fP: \fB\-\-read\-only\fP .IP \(bu 2 -\fB\-desc\fP: \fB\-\-description\fP +\fB\-\-reg\fP: \fB\-\-registry\fP .IP \(bu 2 \fB\-S\fP: \fB\-\-save\fP .IP \(bu 2 -\fB\-P\fP: \fB\-\-save\-prod\fP +\fB\-B\fP: \fB\-\-save\-bundle\fP .IP \(bu 2 \fB\-D\fP: \fB\-\-save\-dev\fP .IP \(bu 2 +\fB\-E\fP: \fB\-\-save\-exact\fP +.IP \(bu 2 \fB\-O\fP: \fB\-\-save\-optional\fP .IP \(bu 2 -\fB\-B\fP: \fB\-\-save\-bundle\fP +\fB\-P\fP: \fB\-\-save\-prod\fP .IP \(bu 2 -\fB\-E\fP: \fB\-\-save\-exact\fP +\fB\-?\fP: \fB\-\-usage\fP .IP \(bu 2 -\fB\-y\fP: \fB\-\-yes\fP +\fB\-h\fP: \fB\-\-usage\fP .IP \(bu 2 -\fB\-n\fP: \fB\-\-yes false\fP +\fB\-H\fP: \fB\-\-usage\fP .IP \(bu 2 -\fBll\fP and \fBla\fP commands: \fBls \-\-long\fP +\fB\-\-help\fP: \fB\-\-usage\fP +.IP \(bu 2 +\fB\-v\fP: \fB\-\-version\fP +.IP \(bu 2 +\fB\-w\fP: \fB\-\-workspace\fP +.IP \(bu 2 +\fB\-\-ws\fP: \fB\-\-workspaces\fP +.IP \(bu 2 +\fB\-y\fP: \fB\-\-yes\fP .RE +<!\-\- AUTOGENERATED CONFIG SHORTHANDS END \-\-> + .P If the specified configuration param resolves unambiguously to a known configuration parameter, then it is expanded to that configuration @@ -133,23 +165,39 @@ npm ls \-\-global \-\-parseable \-\-long \-\-loglevel info .fi .RE .SS Config Settings -.SS access +<!\-\- AUTOGENERATED CONFIG DESCRIPTIONS START \-\-> +<!\-\- automatically generated, do not edit manually \-\-> +.SS \fB_auth\fP +.RS 0 +.IP \(bu 2 +Default: null +.IP \(bu 2 +Type: null or String + +.RE +.P +A basic\-auth string to use when authenticating against the npm registry\. +.P +Warning: This should generally not be set via a command\-line option\. It is +safer to use a registry\-provided authentication bearer token stored in the +~/\.npmrc file by running \fBnpm login\fP\|\. +.SS \fBaccess\fP .RS 0 .IP \(bu 2 -Default: \fBrestricted\fP +Default: 'restricted' for scoped packages, 'public' for unscoped packages .IP \(bu 2 -Type: Access +Type: null, "restricted", or "public" .RE .P -When publishing scoped packages, the access level defaults to \fBrestricted\fP\|\. If -you want your scoped package to be publicly viewable (and installable) set -\fB\-\-access=public\fP\|\. The only valid values for \fBaccess\fP are \fBpublic\fP and +When publishing scoped packages, the access level defaults to \fBrestricted\fP\|\. +If you want your scoped package to be publicly viewable (and installable) +set \fB\-\-access=public\fP\|\. The only valid values for \fBaccess\fP are \fBpublic\fP and \fBrestricted\fP\|\. Unscoped packages \fIalways\fR have an access level of \fBpublic\fP\|\. -.SS all +.SS \fBall\fP .RS 0 .IP \(bu 2 -Default: \fBfalse\fP +Default: false .IP \(bu 2 Type: Boolean @@ -158,7 +206,7 @@ Type: Boolean When running \fBnpm outdated\fP and \fBnpm ls\fP, setting \fB\-\-all\fP will show all outdated or installed packages, rather than only those directly depended upon by the current project\. -.SS allow\-same\-version +.SS \fBallow\-same\-version\fP .RS 0 .IP \(bu 2 Default: false @@ -169,7 +217,7 @@ Type: Boolean .P Prevents throwing an error when \fBnpm version\fP is used to set the new version to the same value as the current version\. -.SS always\-auth +.SS \fBalways\-auth\fP .RS 0 .IP \(bu 2 Default: false @@ -178,20 +226,9 @@ Type: Boolean .RE .P -Force npm to always require authentication when accessing the registry, -even for \fBGET\fP requests\. -.SS also -.RS 0 -.IP \(bu 2 -Default: null -.IP \(bu 2 -Type: String - -.RE -.P -When "dev" or "development" and running local \fBnpm shrinkwrap\fP, -\fBnpm outdated\fP, or \fBnpm update\fP, is an alias for \fB\-\-dev\fP\|\. -.SS audit +Force npm to always require authentication when accessing the registry, even +for \fBGET\fP requests\. +.SS \fBaudit\fP .RS 0 .IP \(bu 2 Default: true @@ -201,53 +238,41 @@ Type: Boolean .RE .P When "true" submit audit reports alongside \fBnpm install\fP runs to the default -registry and all registries configured for scopes\. See the documentation -for npm help \fBaudit\fP for details on what is submitted\. -.SS audit\-level +registry and all registries configured for scopes\. See the documentation for +npm help \fBaudit\fP for details on what is submitted\. +.SS \fBaudit\-level\fP .RS 0 .IP \(bu 2 -Default: \fB"low"\fP -.IP \(bu 2 -Type: \fB\|'low'\fP, \fB\|'moderate'\fP, \fB\|'high'\fP, \fB\|'critical'\fP - -.RE -.P -The minimum level of vulnerability for \fBnpm audit\fP to exit with -a non\-zero exit code\. -.SS auth\-type -.RS 0 -.IP \(bu 2 -Default: \fB\|'legacy'\fP +Default: null .IP \(bu 2 -Type: \fB\|'legacy'\fP, \fB\|'sso'\fP, \fB\|'saml'\fP, \fB\|'oauth'\fP +Type: "low", "moderate", "high", "critical", "none", or null .RE .P -What authentication strategy to use with \fBadduser\fP/\fBlogin\fP\|\. -.SS before +The minimum level of vulnerability for \fBnpm audit\fP to exit with a non\-zero +exit code\. +.SS \fBbefore\fP .RS 0 .IP \(bu 2 -Alias: enjoy\-by -.IP \(bu 2 Default: null .IP \(bu 2 -Type: Date +Type: null or Date .RE .P -If passed to \fBnpm install\fP, will rebuild the npm tree such that only versions -that were available \fBon or before\fR the \fB\-\-before\fP time get installed\. -If there's no versions available for the current set of direct dependencies, the -command will error\. +If passed to \fBnpm install\fP, will rebuild the npm tree such that only +versions that were available \fBon or before\fR the \fB\-\-before\fP time get +installed\. If there's no versions available for the current set of direct +dependencies, the command will error\. .P If the requested version is a \fBdist\-tag\fP and the given tag does not pass the -\fB\-\-before\fP filter, the most recent version less than or equal to that tag will -be used\. For example, \fBfoo@latest\fP might install \fBfoo@1\.2\fP even though \fBlatest\fP -is \fB2\.0\fP\|\. -.SS bin\-links +\fB\-\-before\fP filter, the most recent version less than or equal to that tag +will be used\. For example, \fBfoo@latest\fP might install \fBfoo@1\.2\fP even though +\fBlatest\fP is \fB2\.0\fP\|\. +.SS \fBbin\-links\fP .RS 0 .IP \(bu 2 -Default: \fBtrue\fP +Default: true .IP \(bu 2 Type: Boolean @@ -256,15 +281,15 @@ Type: Boolean Tells npm to create symlinks (or \fB\|\.cmd\fP shims on Windows) for package executables\. .P -Set to false to have it not do this\. This can be used to work around -the fact that some file systems don't support symlinks, even on -ostensibly Unix systems\. -.SS browser +Set to false to have it not do this\. This can be used to work around the +fact that some file systems don't support symlinks, even on ostensibly Unix +systems\. +.SS \fBbrowser\fP .RS 0 .IP \(bu 2 Default: OS X: \fB"open"\fP, Windows: \fB"start"\fP, Others: \fB"xdg\-open"\fP .IP \(bu 2 -Type: String or Boolean +Type: null, Boolean, or String .RE .P @@ -274,18 +299,19 @@ Set to \fBfalse\fP to suppress browser behavior and instead print urls to terminal\. .P Set to \fBtrue\fP to use default system URL opener\. -.SS ca +.SS \fBca\fP .RS 0 .IP \(bu 2 -Default: The npm CA certificate +Default: null .IP \(bu 2 -Type: String, Array or null +Type: null or String (can be set multiple times) .RE .P The Certificate Authority signing certificate that is trusted for SSL -connections to the registry\. Values should be in PEM format (Windows calls it "Base\-64 encoded X\.509 (\.CER)") with newlines -replaced by the string "\\n"\. For example: +connections to the registry\. Values should be in PEM format (Windows calls +it "Base\-64 encoded X\.509 (\.CER)") with newlines replaced by the string +"\\n"\. For example: .P .RS 2 .nf @@ -293,8 +319,8 @@ ca="\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-\\nXXXX\\nXXXX\\n\-\-\-\-\-END CERTIFIC .fi .RE .P -Set to \fBnull\fP to only allow "known" registrars, or to a specific CA cert -to trust only that specific signing authority\. +Set to \fBnull\fP to only allow "known" registrars, or to a specific CA cert to +trust only that specific signing authority\. .P Multiple CAs can be trusted by specifying an array of certificates: .P @@ -306,175 +332,166 @@ ca[]="\.\.\." .RE .P See also the \fBstrict\-ssl\fP config\. -.SS cafile +.SS \fBcache\fP .RS 0 .IP \(bu 2 -Default: \fBnull\fP +Default: Windows: \fB%LocalAppData%\\npm\-cache\fP, Posix: \fB~/\.npm\fP .IP \(bu 2 -Type: path +Type: Path .RE .P -A path to a file containing one or multiple Certificate Authority signing -certificates\. Similar to the \fBca\fP setting, but allows for multiple CA's, as -well as for the CA information to be stored in a file on disk\. -.SS cache +The location of npm's cache directory\. See npm help \fBnpm +cache\fP +.SS \fBcafile\fP .RS 0 .IP \(bu 2 -Default: Windows: \fB%AppData%\\npm\-cache\fP, Posix: \fB~/\.npm\fP +Default: null .IP \(bu 2 -Type: path +Type: Path .RE .P -The location of npm's cache directory\. See npm help \fBcache\fP -.SS cache\-lock\-stale +A path to a file containing one or multiple Certificate Authority signing +certificates\. Similar to the \fBca\fP setting, but allows for multiple CA's, as +well as for the CA information to be stored in a file on disk\. +.SS \fBcall\fP .RS 0 .IP \(bu 2 -Default: 60000 (1 minute) +Default: "" .IP \(bu 2 -Type: Number +Type: String .RE .P -The number of ms before cache folder lockfiles are considered stale\. -.SS cache\-lock\-retries +Optional companion option for \fBnpm exec\fP, \fBnpx\fP that allows for specifying a +custom command to be run along with the installed packages\. +.P +.RS 2 +.nf +npm exec \-\-package yo \-\-package generator\-node \-\-call "yo node" +.fi +.RE +.SS \fBcert\fP .RS 0 .IP \(bu 2 -Default: 10 +Default: null .IP \(bu 2 -Type: Number +Type: null or String .RE .P -Number of times to retry to acquire a lock on cache folder lockfiles\. -.SS cache\-lock\-wait -.RS 0 -.IP \(bu 2 -Default: 10000 (10 seconds) -.IP \(bu 2 -Type: Number - +A client certificate to pass when accessing the registry\. Values should be +in PEM format (Windows calls it "Base\-64 encoded X\.509 (\.CER)") with +newlines replaced by the string "\\n"\. For example: +.P +.RS 2 +.nf +cert="\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-\\nXXXX\\nXXXX\\n\-\-\-\-\-END CERTIFICATE\-\-\-\-\-" +.fi .RE .P -Number of ms to wait for cache lock files to expire\. -.SS cache\-max +It is \fInot\fR the path to a certificate file (and there is no "certfile" +option)\. +.SS \fBci\-name\fP .RS 0 .IP \(bu 2 -Default: Infinity +Default: The name of the current CI system, or \fBnull\fP when not on a known CI +platform\. .IP \(bu 2 -Type: Number +Type: null or String .RE .P -\fBDEPRECATED\fR: This option has been deprecated in favor of \fB\-\-prefer\-online\fP\|\. -.P -\fB\-\-cache\-max=0\fP is an alias for \fB\-\-prefer\-online\fP\|\. -.SS cache\-min +The name of a continuous integration system\. If not set explicitly, npm will +detect the current CI environment using the +\fB@npmcli/ci\-detect\fP \fIhttp://npm\.im/@npmcli/ci\-detect\fR module\. +.SS \fBcidr\fP .RS 0 .IP \(bu 2 -Default: 10 +Default: null .IP \(bu 2 -Type: Number +Type: null or String (can be set multiple times) .RE .P -\fBDEPRECATED\fR: This option has been deprecated in favor of \fB\-\-prefer\-offline\fP\|\. -.P -\fB\-\-cache\-min=9999 (or bigger)\fP is an alias for \fB\-\-prefer\-offline\fP\|\. -.SS call +This is a list of CIDR address to be used when configuring limited access +tokens with the \fBnpm token create\fP command\. +.SS \fBcolor\fP .RS 0 .IP \(bu 2 -Default: "" +Default: true unless the NO_COLOR environ is set to something other than '0' .IP \(bu 2 -Type: String +Type: "always" or Boolean .RE .P -Optional companion option for \fBnpm exec\fP, \fBnpx\fP that allows for specifying a -custom command to be run along with the installed packages\. -.P -.RS 2 -.nf -npm exec \-\-package yo \-\-package generator\-node \-\-call "yo node" -.fi -.RE -.SS cert +If false, never shows colors\. If \fB"always"\fP then always shows colors\. If +true, then only prints color codes for tty file descriptors\. +.SS \fBcommit\-hooks\fP .RS 0 .IP \(bu 2 -Default: \fBnull\fP +Default: true .IP \(bu 2 -Type: String +Type: Boolean .RE .P -A client certificate to pass when accessing the registry\. Values should be in -PEM format (Windows calls it "Base\-64 encoded X\.509 (\.CER)") with newlines replaced by the string "\\n"\. For example: -.P -.RS 2 -.nf -cert="\-\-\-\-\-BEGIN CERTIFICATE\-\-\-\-\-\\nXXXX\\nXXXX\\n\-\-\-\-\-END CERTIFICATE\-\-\-\-\-" -.fi -.RE -.P -It is \fInot\fR the path to a certificate file (and there is no "certfile" option)\. -.SS cidr +Run git commit hooks when using the \fBnpm version\fP command\. +.SS \fBdepth\fP .RS 0 .IP \(bu 2 -Default: \fBnull\fP +Default: \fBInfinity\fP if \fB\-\-all\fP is set, otherwise \fB1\fP .IP \(bu 2 -Type: String, Array, null +Type: null or Number .RE .P -This is a list of CIDR address to be used when configuring limited access tokens with the \fBnpm token create\fP command\. -.SS commit\-hooks +The depth to go when recursing packages for \fBnpm ls\fP\|\. +.P +If not set, \fBnpm ls\fP will show only the immediate dependencies of the root +project\. If \fB\-\-all\fP is set, then npm will show all dependencies by default\. +.SS \fBdescription\fP .RS 0 .IP \(bu 2 -Default: \fBtrue\fP +Default: true .IP \(bu 2 Type: Boolean .RE .P -Run git commit hooks when using the \fBnpm version\fP command\. -.SS color +Show the description in \fBnpm search\fP +.SS \fBdiff\fP .RS 0 .IP \(bu 2 -Default: true +Default: .IP \(bu 2 -Type: Boolean or \fB"always"\fP +Type: String (can be set multiple times) .RE .P -If false, never shows colors\. If \fB"always"\fP then always shows colors\. -If true, then only prints color codes for tty file descriptors\. -.P -This option can also be changed using the environment: colors are -disabled when the environment variable \fBNO_COLOR\fP is set to any value\. -.SS depth +Define arguments to compare in \fBnpm diff\fP\|\. +.SS \fBdiff\-dst\-prefix\fP .RS 0 .IP \(bu 2 -Default: null +Default: "b/" .IP \(bu 2 -Type: null or Number +Type: String .RE .P -The depth to go when recursing packages for \fBnpm ls\fP\|\. -.P -To make this default to \fBInfinity\fP instead of \fBnull\fP, set \fB\-\-all\fP\|\. -.SS description +Destination prefix to be used in \fBnpm diff\fP output\. +.SS \fBdiff\-ignore\-all\-space\fP .RS 0 .IP \(bu 2 -Default: true +Default: false .IP \(bu 2 Type: Boolean .RE .P -Show the description in \fBnpm search\fP -.SS dev +Ignore whitespace when comparing lines in \fBnpm diff\fP\|\. +.SS \fBdiff\-name\-only\fP .RS 0 .IP \(bu 2 Default: false @@ -483,8 +500,8 @@ Type: Boolean .RE .P -[Deprecated] Install \fBdev\-dependencies\fP along with packages\. -.SS dry\-run +Prints only filenames when using \fBnpm diff\fP\|\. +.SS \fBdiff\-no\-prefix\fP .RS 0 .IP \(bu 2 Default: false @@ -493,22 +510,21 @@ Type: Boolean .RE .P -Indicates that you don't want npm to make any changes and that it should -only report what it would have done\. This can be passed into any of the -commands that modify your local installation, eg, \fBinstall\fP, \fBupdate\fP, -\fBdedupe\fP, \fBuninstall\fP\|\. This is NOT currently honored by some network related -commands, eg \fBdist\-tags\fP, \fBowner\fP, etc\. -.SS diff +Do not show any source or destination prefix in \fBnpm diff\fP output\. +.P +Note: this causes \fBnpm diff\fP to ignore the \fB\-\-diff\-src\-prefix\fP and +\fB\-\-diff\-dst\-prefix\fP configs\. +.SS \fBdiff\-src\-prefix\fP .RS 0 .IP \(bu 2 -Default: null +Default: "a/" .IP \(bu 2 -Type: String, Array, null +Type: String .RE .P -Define arguments to compare in \fBnpm diff\fP\|\. -.SS diff\-name\-only +Source prefix to be used in \fBnpm diff\fP output\. +.SS \fBdiff\-text\fP .RS 0 .IP \(bu 2 Default: false @@ -517,93 +533,115 @@ Type: Boolean .RE .P -Prints only filenames when using \fBnpm diff\fP\|\. -.SS diff\-unified +Treat all files as text in \fBnpm diff\fP\|\. +.SS \fBdiff\-unified\fP .RS 0 .IP \(bu 2 -Type: number +Default: 3 .IP \(bu 2 -Default: \fB3\fP +Type: Number .RE .P The number of lines of context to print in \fBnpm diff\fP\|\. -.SS diff\-ignore\-all\-space +.SS \fBdry\-run\fP .RS 0 .IP \(bu 2 -Type: Boolean -.IP \(bu 2 Default: false +.IP \(bu 2 +Type: Boolean .RE .P -Ignore whitespace when comparing lines in `npm diff\. -.SS diff\-no\-prefix +Indicates that you don't want npm to make any changes and that it should +only report what it would have done\. This can be passed into any of the +commands that modify your local installation, eg, \fBinstall\fP, \fBupdate\fP, +\fBdedupe\fP, \fBuninstall\fP, as well as \fBpack\fP and \fBpublish\fP\|\. +.P +Note: This is NOT honored by other network related commands, eg \fBdist\-tags\fP, +\fBowner\fP, etc\. +.SS \fBeditor\fP .RS 0 .IP \(bu 2 -Type: Boolean +Default: The EDITOR or VISUAL environment variables, or 'notepad\.exe' on +Windows, or 'vim' on Unix systems .IP \(bu 2 -Default: false +Type: String .RE .P -Do not show any source or destination prefix in \fBnpm diff\fP output\. -.SS diff\-src\-prefix +The command to run for \fBnpm edit\fP and \fBnpm config edit\fP\|\. +.SS \fBengine\-strict\fP .RS 0 .IP \(bu 2 -Type: String +Default: false .IP \(bu 2 -Default: \fB"a/"\fP +Type: Boolean .RE .P -Source prefix to be used in \fBnpm diff\fP output\. -.SS diff\-dst\-prefix +If set to true, then npm will stubbornly refuse to install (or even consider +installing) any package that claims to not be compatible with the current +Node\.js version\. +.P +This can be overridden by setting the \fB\-\-force\fP flag\. +.SS \fBfetch\-retries\fP .RS 0 .IP \(bu 2 -Type: String +Default: 2 .IP \(bu 2 -Default: \fB"b/"\fP +Type: Number .RE .P -Destination prefix to be used in \fBnpm diff\fP output\. -.SS diff\-text +The "retries" config for the \fBretry\fP module to use when fetching packages +from the registry\. +.P +npm will retry idempotent read requests to the registry in the case of +network failures or 5xx HTTP errors\. +.SS \fBfetch\-retry\-factor\fP .RS 0 .IP \(bu 2 -Alias: \fB\-a\fP +Default: 10 .IP \(bu 2 -Type: Boolean +Type: Number + +.RE +.P +The "factor" config for the \fBretry\fP module to use when fetching packages\. +.SS \fBfetch\-retry\-maxtimeout\fP +.RS 0 .IP \(bu 2 -Default: false +Default: 60000 (1 minute) +.IP \(bu 2 +Type: Number .RE .P -Treat all files as text in \fBnpm diff\fP\|\. -.SS editor +The "maxTimeout" config for the \fBretry\fP module to use when fetching +packages\. +.SS \fBfetch\-retry\-mintimeout\fP .RS 0 .IP \(bu 2 -Default: \fBEDITOR\fP environment variable if set, or \fB"vi"\fP on Posix, -or \fB"notepad"\fP on Windows\. +Default: 10000 (10 seconds) .IP \(bu 2 -Type: path +Type: Number .RE .P -The command to run for \fBnpm edit\fP or \fBnpm config edit\fP\|\. -.SS engine\-strict +The "minTimeout" config for the \fBretry\fP module to use when fetching +packages\. +.SS \fBfetch\-timeout\fP .RS 0 .IP \(bu 2 -Default: false +Default: 300000 (5 minutes) .IP \(bu 2 -Type: Boolean +Type: Number .RE .P -If set to true, then npm will stubbornly refuse to install (or even -consider installing) any package that claims to not be compatible with -the current Node\.js version\. -.SS force +The maximum amount of time to wait for HTTP requests to complete\. +.SS \fBforce\fP .RS 0 .IP \(bu 2 Default: false @@ -626,13 +664,11 @@ Allow installing packages that have an \fBengines\fP declaration requiring a different version of npm\. .IP \(bu 2 Allow installing packages that have an \fBengines\fP declaration requiring a -different version of \fBnode\fP, even if \fB\-\-engines\-strict\fP is enabled\. +different version of \fBnode\fP, even if \fB\-\-engine\-strict\fP is enabled\. .IP \(bu 2 Allow \fBnpm audit fix\fP to install modules outside your stated dependency range (including SemVer\-major changes)\. .IP \(bu 2 -Allow a module to be installed as a direct dependency of itself\. -.IP \(bu 2 Allow unpublishing all versions of a published package\. .IP \(bu 2 Allow conflicting peerDependencies to be installed in the root project\. @@ -641,7 +677,7 @@ Allow conflicting peerDependencies to be installed in the root project\. .P If you don't have a clear idea of what you want to do, it is strongly recommended that you do not use this option! -.SS foreground\-scripts +.SS \fBforeground\-scripts\fP .RS 0 .IP \(bu 2 Default: false @@ -654,9 +690,9 @@ Run all build scripts (ie, \fBpreinstall\fP, \fBinstall\fP, and \fBpostinstall\f scripts for installed packages in the foreground process, sharing standard input, output, and error with the main npm process\. .P -Note that this will generally make installs run slower, and be much -noisier, but can be useful for debugging\. -.SS format\-package\-lock +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging\. +.SS \fBformat\-package\-lock\fP .RS 0 .IP \(bu 2 Default: true @@ -665,8 +701,9 @@ Type: Boolean .RE .P -Format \fBpackage\-lock\.json\fP or \fBnpm\-shrinkwrap\.json\fP as a human readable file\. -.SS fund +Format \fBpackage\-lock\.json\fP or \fBnpm\-shrinkwrap\.json\fP as a human readable +file\. +.SS \fBfund\fP .RS 0 .IP \(bu 2 Default: true @@ -676,85 +713,30 @@ Type: Boolean .RE .P When "true" displays the message at the end of each \fBnpm install\fP -acknowledging the number of dependencies looking for funding\. -See npm help \fBfund\fP for details\. -.SS fetch\-retries -.RS 0 -.IP \(bu 2 -Default: 2 -.IP \(bu 2 -Type: Number - -.RE -.P -The "retries" config for the \fBretry\fP module to use when fetching -packages from the registry\. -.SS fetch\-retry\-factor -.RS 0 -.IP \(bu 2 -Default: 10 -.IP \(bu 2 -Type: Number - -.RE -.P -The "factor" config for the \fBretry\fP module to use when fetching -packages\. -.SS fetch\-retry\-mintimeout -.RS 0 -.IP \(bu 2 -Default: 10000 (10 seconds) -.IP \(bu 2 -Type: Number - -.RE -.P -The "minTimeout" config for the \fBretry\fP module to use when fetching -packages\. -.SS fetch\-retry\-maxtimeout -.RS 0 -.IP \(bu 2 -Default: 60000 (1 minute) -.IP \(bu 2 -Type: Number - -.RE -.P -The "maxTimeout" config for the \fBretry\fP module to use when fetching -packages\. -.SS fetch\-timeout -.RS 0 -.IP \(bu 2 -Default: 300000 (5 minutes) -.IP \(bu 2 -Type: Number - -.RE -.P -The maximum amount of time to wait for HTTP requests to complete\. -.SS git +acknowledging the number of dependencies looking for funding\. See npm help \fBnpm +fund\fP for details\. +.SS \fBgit\fP .RS 0 .IP \(bu 2 -Default: \fB"git"\fP +Default: "git" .IP \(bu 2 Type: String .RE .P -The command to use for git commands\. If git is installed on the -computer, but is not in the \fBPATH\fP, then set this to the full path to -the git binary\. -.SS git\-tag\-version +The command to use for git commands\. If git is installed on the computer, +but is not in the \fBPATH\fP, then set this to the full path to the git binary\. +.SS \fBgit\-tag\-version\fP .RS 0 .IP \(bu 2 -Default: \fBtrue\fP +Default: true .IP \(bu 2 Type: Boolean .RE .P Tag the commit when using the \fBnpm version\fP command\. -.SS global +.SS \fBglobal\fP .RS 0 .IP \(bu 2 Default: false @@ -763,67 +745,69 @@ Type: Boolean .RE .P -Operates in "global" mode, so that packages are installed into the -\fBprefix\fP folder instead of the current working directory\. See +Operates in "global" mode, so that packages are installed into the \fBprefix\fP +folder instead of the current working directory\. See npm help folders for more on the differences in behavior\. .RS 0 .IP \(bu 2 -packages are installed into the \fB{prefix}/lib/node_modules\fP folder, instead of the -current working directory\. +packages are installed into the \fB{prefix}/lib/node_modules\fP folder, instead +of the current working directory\. .IP \(bu 2 bin files are linked to \fB{prefix}/bin\fP .IP \(bu 2 man pages are linked to \fB{prefix}/share/man\fP .RE -.SS globalconfig +.SS \fBglobal\-style\fP .RS 0 .IP \(bu 2 -Default: {prefix}/etc/npmrc +Default: false .IP \(bu 2 -Type: path +Type: Boolean .RE .P -The config file to read for global config options\. -.SS global\-style +Causes npm to install the package into your local \fBnode_modules\fP folder with +the same layout it uses with the global \fBnode_modules\fP folder\. Only your +direct dependencies will show in \fBnode_modules\fP and everything they depend +on will be flattened in their \fBnode_modules\fP folders\. This obviously will +eliminate some deduping\. If used with \fBlegacy\-bundling\fP, \fBlegacy\-bundling\fP +will be preferred\. +.SS \fBglobalconfig\fP .RS 0 .IP \(bu 2 -Default: false +Default: The global \-\-prefix setting plus 'etc/npmrc'\. For example, +\|'/usr/local/etc/npmrc' .IP \(bu 2 -Type: Boolean +Type: Path .RE .P -Causes npm to install the package into your local \fBnode_modules\fP folder with -the same layout it uses with the global \fBnode_modules\fP folder\. Only your -direct dependencies will show in \fBnode_modules\fP and everything they depend -on will be flattened in their \fBnode_modules\fP folders\. This obviously will -eliminate some deduping\. If used with \fBlegacy\-bundling\fP, \fBlegacy\-bundling\fP will be -preferred\. -.SS heading +The config file to read for global config options\. +.SS \fBheading\fP .RS 0 .IP \(bu 2 -Default: \fB"npm"\fP +Default: "npm" .IP \(bu 2 Type: String .RE .P The string that starts all the debugging log output\. -.SS https\-proxy +.SS \fBhttps\-proxy\fP .RS 0 .IP \(bu 2 Default: null .IP \(bu 2 -Type: url +Type: null or URL .RE .P A proxy to use for outgoing https requests\. If the \fBHTTPS_PROXY\fP or \fBhttps_proxy\fP or \fBHTTP_PROXY\fP or \fBhttp_proxy\fP environment variables are set, -proxy settings will be honored by the underlying \fBrequest\fP library\. -.SS if\-present +proxy settings will be honored by the underlying \fBmake\-fetch\-happen\fP +library\. +.SS \fBif\-present\fP .RS 0 .IP \(bu 2 Default: false @@ -832,12 +816,13 @@ Type: Boolean .RE .P -If true, npm will not exit with an error code when \fBrun\-script\fP is invoked for -a script that isn't defined in the \fBscripts\fP section of \fBpackage\.json\fP\|\. This -option can be used when it's desirable to optionally run a script when it's -present and fail if the script fails\. This is useful, for example, when running -scripts that may only apply for some builds in an otherwise generic CI setup\. -.SS ignore\-prepublish +If true, npm will not exit with an error code when \fBrun\-script\fP is invoked +for a script that isn't defined in the \fBscripts\fP section of \fBpackage\.json\fP\|\. +This option can be used when it's desirable to optionally run a script when +it's present and fail if the script fails\. This is useful, for example, when +running scripts that may only apply for some builds in an otherwise generic +CI setup\. +.SS \fBignore\-scripts\fP .RS 0 .IP \(bu 2 Default: false @@ -846,47 +831,48 @@ Type: Boolean .RE .P -If true, npm will not run \fBprepublish\fP scripts\. -.SS ignore\-scripts +If true, npm does not run scripts specified in package\.json files\. +.SS \fBinclude\fP .RS 0 .IP \(bu 2 -Default: false +Default: .IP \(bu 2 -Type: Boolean +Type: "prod", "dev", "optional", or "peer" (can be set multiple times) .RE .P -If true, npm does not run scripts specified in package\.json files\. -.SS include +Option that allows for defining which types of dependencies to install\. +.P +This is the inverse of \fB\-\-omit=<type>\fP\|\. +.P +Dependency types specified in \fB\-\-include\fP will not be omitted, regardless of +the order in which omit/include are specified on the command\-line\. +.SS \fBinclude\-staged\fP .RS 0 .IP \(bu 2 -Default: \fB[prod|dev|optional|peer]\fP +Default: false .IP \(bu 2 -Type: Array +Type: Boolean .RE .P -Option that allows for defining which types of dependencies to install\. -.SS init\-module +Allow installing "staged" published packages, as defined by npm RFC PR +#92 \fIhttps://github\.com/npm/rfcs/pull/92\fR\|\. +.P +This is experimental, and not implemented by the npm public registry\. +.SS \fBinit\-author\-email\fP .RS 0 .IP \(bu 2 -Alias: \fBinit\.module\fP -.IP \(bu 2 -Default: ~/\.npm\-init\.js +Default: "" .IP \(bu 2 -Type: path +Type: String .RE .P -A module that will be loaded by the \fBnpm init\fP command\. See the -documentation for the -init\-package\-json \fIhttps://github\.com/npm/init\-package\-json\fR module -for more information, or npm help init\. -.SS init\-author\-name +The value \fBnpm init\fP should use by default for the package author's email\. +.SS \fBinit\-author\-name\fP .RS 0 .IP \(bu 2 -Alias: \fBinit\.author\.name\fP -.IP \(bu 2 Default: "" .IP \(bu 2 Type: String @@ -894,56 +880,52 @@ Type: String .RE .P The value \fBnpm init\fP should use by default for the package author's name\. -.SS init\-author\-email +.SS \fBinit\-author\-url\fP .RS 0 .IP \(bu 2 -Alias: \fBinit\.author\.email\fP -.IP \(bu 2 Default: "" .IP \(bu 2 -Type: String +Type: "" or URL .RE .P -The value \fBnpm init\fP should use by default for the package author's email\. -.SS init\-author\-url +The value \fBnpm init\fP should use by default for the package author's +homepage\. +.SS \fBinit\-license\fP .RS 0 .IP \(bu 2 -Alias: \fBinit\.author\.url\fP -.IP \(bu 2 -Default: "" +Default: "ISC" .IP \(bu 2 Type: String .RE .P -The value \fBnpm init\fP should use by default for the package author's homepage\. -.SS init\-license +The value \fBnpm init\fP should use by default for the package license\. +.SS \fBinit\-module\fP .RS 0 .IP \(bu 2 -Alias: \fBinit\.license\fP -.IP \(bu 2 -Default: "ISC" +Default: "~/\.npm\-init\.js" .IP \(bu 2 -Type: String +Type: Path .RE .P -The value \fBnpm init\fP should use by default for the package license\. -.SS init\-version +A module that will be loaded by the \fBnpm init\fP command\. See the +documentation for the +init\-package\-json \fIhttps://github\.com/npm/init\-package\-json\fR module for +more information, or npm help init\. +.SS \fBinit\-version\fP .RS 0 .IP \(bu 2 -Alias: \fBinit\.version\fP -.IP \(bu 2 Default: "1\.0\.0" .IP \(bu 2 -Type: semver +Type: SemVer string .RE .P -The value that \fBnpm init\fP should use by default for the package -version number, if not already set in package\.json\. -.SS json +The value that \fBnpm init\fP should use by default for the package version +number, if not already set in package\.json\. +.SS \fBjson\fP .RS 0 .IP \(bu 2 Default: false @@ -954,19 +936,20 @@ Type: Boolean .P Whether or not to output JSON data, rather than the normal output\. .P -This feature is currently experimental, and the output data structures for many -commands is either not implemented in JSON yet, or subject to change\. Only the -output from \fBnpm ls \-\-json\fP and \fBnpm search \-\-json\fP are currently valid\. -.SS key +This feature is currently experimental, and the output data structures for +many commands is either not implemented in JSON yet, or subject to change\. +Only the output from \fBnpm ls \-\-json\fP and \fBnpm search \-\-json\fP are currently +valid\. +.SS \fBkey\fP .RS 0 .IP \(bu 2 -Default: \fBnull\fP +Default: null .IP \(bu 2 -Type: String +Type: null or String .RE .P -A client key to pass when accessing the registry\. Values should be in PEM +A client key to pass when accessing the registry\. Values should be in PEM format with newlines replaced by the string "\\n"\. For example: .P .RS 2 @@ -976,7 +959,7 @@ key="\-\-\-\-\-BEGIN PRIVATE KEY\-\-\-\-\-\\nXXXX\\nXXXX\\n\-\-\-\-\-END PRIVATE .RE .P It is \fInot\fR the path to a key file (and there is no "keyfile" option)\. -.SS legacy\-bundling +.SS \fBlegacy\-bundling\fP .RS 0 .IP \(bu 2 Default: false @@ -986,10 +969,10 @@ Type: Boolean .RE .P Causes npm to install the package such that versions of npm prior to 1\.4, -such as the one included with node 0\.8, can install the package\. This +such as the one included with node 0\.8, can install the package\. This eliminates all automatic deduping\. If used with \fBglobal\-style\fP this option will be preferred\. -.SS legacy\-peer\-deps +.SS \fBlegacy\-peer\-deps\fP .RS 0 .IP \(bu 2 Default: false @@ -1001,9 +984,8 @@ Type: Boolean Causes npm to completely ignore \fBpeerDependencies\fP when building a package tree, as in npm versions 3 through 6\. .P -If a package cannot be installed because of overly strict -\fBpeerDependencies\fP that collide, it provides a way to move forward -resolving the situation\. +If a package cannot be installed because of overly strict \fBpeerDependencies\fP +that collide, it provides a way to move forward resolving the situation\. .P This differs from \fB\-\-omit=peer\fP, in that \fB\-\-omit=peer\fP will avoid unpacking \fBpeerDependencies\fP on disk, but will still design a tree such that @@ -1011,7 +993,7 @@ This differs from \fB\-\-omit=peer\fP, in that \fB\-\-omit=peer\fP will avoid un .P Use of \fBlegacy\-peer\-deps\fP is not recommended, as it will not enforce the \fBpeerDependencies\fP contract that meta\-dependencies may rely on\. -.SS link +.SS \fBlink\fP .RS 0 .IP \(bu 2 Default: false @@ -1023,45 +1005,44 @@ Type: Boolean If true, then local installs will link if there is a suitable globally installed package\. .P -Note that this means that local installs can cause things to be -installed into the global space at the same time\. The link is only done -if one of the two conditions are met: +Note that this means that local installs can cause things to be installed +into the global space at the same time\. The link is only done if one of the +two conditions are met: .RS 0 .IP \(bu 2 The package is not already installed globally, or .IP \(bu 2 -the globally installed version is identical to the version that is -being installed locally\. +the globally installed version is identical to the version that is being +installed locally\. .RE -.SS local\-address +.SS \fBlocal\-address\fP .RS 0 .IP \(bu 2 -Default: undefined +Default: null .IP \(bu 2 Type: IP Address .RE .P -The IP address of the local interface to use when making connections -to the npm registry\. Must be IPv4 in versions of Node prior to 0\.12\. -.SS loglevel +The IP address of the local interface to use when making connections to the +npm registry\. Must be IPv4 in versions of Node prior to 0\.12\. +.SS \fBloglevel\fP .RS 0 .IP \(bu 2 Default: "notice" .IP \(bu 2 -Type: String -.IP \(bu 2 -Values: "silent", "error", "warn", "notice", "http", "timing", "info", -"verbose", "silly" +Type: "silent", "error", "warn", "notice", "http", "timing", "info", +"verbose", or "silly" .RE .P -What level of logs to report\. On failure, \fIall\fR logs are written to +What level of logs to report\. On failure, \fIall\fR logs are written to \fBnpm\-debug\.log\fP in the current working directory\. .P -Any logs of a higher level than the setting are shown\. The default is "notice"\. -.SS logs\-max +Any logs of a higher level than the setting are shown\. The default is +"notice"\. +.SS \fBlogs\-max\fP .RS 0 .IP \(bu 2 Default: 10 @@ -1071,7 +1052,7 @@ Type: Number .RE .P The maximum number of log files to store\. -.SS long +.SS \fBlong\fP .RS 0 .IP \(bu 2 Default: false @@ -1081,18 +1062,18 @@ Type: Boolean .RE .P Show extended information in \fBnpm ls\fP and \fBnpm search\fP\|\. -.SS maxsockets +.SS \fBmaxsockets\fP .RS 0 .IP \(bu 2 -Default: 50 +Default: Infinity .IP \(bu 2 Type: Number .RE .P The maximum number of connections to use per origin (protocol/host/port -combination)\. Passed to the \fBhttp\fP \fBAgent\fP used to make the request\. -.SS message +combination)\. +.SS \fBmessage\fP .RS 0 .IP \(bu 2 Default: "%s" @@ -1104,39 +1085,51 @@ Type: String Commit message which is used by \fBnpm version\fP when creating version commit\. .P Any "%s" in the message will be replaced with the version number\. -.SS node\-options +.SS \fBnode\-options\fP .RS 0 .IP \(bu 2 Default: null .IP \(bu 2 -Type: String +Type: null or String .RE .P Options to pass through to Node\.js via the \fBNODE_OPTIONS\fP environment -variable\. This does not impact how npm itself is executed but it does -impact how lifecycle scripts are called\. -.SS node\-version +variable\. This does not impact how npm itself is executed but it does impact +how lifecycle scripts are called\. +.SS \fBnode\-version\fP .RS 0 .IP \(bu 2 -Default: process\.version +Default: Node\.js \fBprocess\.version\fP value .IP \(bu 2 -Type: semver or false +Type: SemVer string .RE .P -The node version to use when checking a package's \fBengines\fP map\. -.SS noproxy +The node version to use when checking a package's \fBengines\fP setting\. +.SS \fBnoproxy\fP .RS 0 .IP \(bu 2 -Default: null +Default: The value of the NO_PROXY environment variable +.IP \(bu 2 +Type: String (can be set multiple times) + +.RE +.P +Domain extensions that should bypass any proxies\. +.P +Also accepts a comma\-delimited string\. +.SS \fBnpm\-version\fP +.RS 0 +.IP \(bu 2 +Default: Output of \fBnpm \-\-version\fP .IP \(bu 2 -Type: String or Array +Type: SemVer string .RE .P -A comma\-separated string or an array of domain extensions that a proxy should not be used for\. -.SS offline +The npm version to use when checking a package's \fBengines\fP setting\. +.SS \fBoffline\fP .RS 0 .IP \(bu 2 Default: false @@ -1145,53 +1138,54 @@ Type: Boolean .RE .P -Force offline mode: no network requests will be done during install\. To allow -the CLI to fill in missing cache data, see \fB\-\-prefer\-offline\fP\|\. -.SS only +Force offline mode: no network requests will be done during install\. To +allow the CLI to fill in missing cache data, see \fB\-\-prefer\-offline\fP\|\. +.SS \fBomit\fP .RS 0 .IP \(bu 2 -Default: null +Default: 'dev' if the NODE_ENV environment variable is set to 'production', +otherwise empty\. .IP \(bu 2 -Type: String +Type: "dev", "optional", or "peer" (can be set multiple times) .RE .P -When "dev" or "development" and running local \fBnpm install\fP without any -arguments, only devDependencies (and their dependencies) are installed\. +Dependency types to omit from the installation tree on disk\. .P -When "dev" or "development" and running local \fBnpm ls\fP, \fBnpm outdated\fP, or -\fBnpm update\fP, is an alias for \fB\-\-dev\fP\|\. +Note that these dependencies \fIare\fR still resolved and added to the +\fBpackage\-lock\.json\fP or \fBnpm\-shrinkwrap\.json\fP file\. They are just not +physically installed on disk\. .P -When "prod" or "production" and running local \fBnpm install\fP without any -arguments, only non\-devDependencies (and their dependencies) are -installed\. +If a package type appears in both the \fB\-\-include\fP and \fB\-\-omit\fP lists, then +it will be included\. .P -When "prod" or "production" and running local \fBnpm ls\fP, \fBnpm outdated\fP, or -\fBnpm update\fP, is an alias for \fB\-\-production\fP\|\. -.SS optional +If the resulting omit list includes \fB\|'dev'\fP, then the \fBNODE_ENV\fP environment +variable will be set to \fB\|'production'\fP for all lifecycle scripts\. +.SS \fBotp\fP .RS 0 .IP \(bu 2 -Default: true +Default: null .IP \(bu 2 -Type: Boolean +Type: null or String .RE .P -Attempt to install packages in the \fBoptionalDependencies\fP object\. Note -that if these packages fail to install, the overall installation -process is not aborted\. -.SS otp +This is a one\-time password from a two\-factor authenticator\. It's needed +when publishing or changing package permissions with \fBnpm access\fP\|\. +.P +If not set, and a registry response fails with a challenge for a one\-time +password, npm will prompt on the command line for one\. +.SS \fBpackage\fP .RS 0 .IP \(bu 2 -Default: null +Default: .IP \(bu 2 -Type: Number +Type: String (can be set multiple times) .RE .P -This is a one\-time password from a two\-factor authenticator\. It's needed -when publishing or changing package permissions with \fBnpm access\fP\|\. -.SS package\-lock +The package to install for npm help \fBexec\fP +.SS \fBpackage\-lock\fP .RS 0 .IP \(bu 2 Default: true @@ -1204,11 +1198,9 @@ If set to false, then ignore \fBpackage\-lock\.json\fP files when installing\. T will also prevent \fIwriting\fR \fBpackage\-lock\.json\fP if \fBsave\fP is true\. .P When package package\-locks are disabled, automatic pruning of extraneous -modules will also be disabled\. To remove extraneous modules with +modules will also be disabled\. To remove extraneous modules with package\-locks disabled use \fBnpm prune\fP\|\. -.P -This option is an alias for \fB\-\-shrinkwrap\fP\|\. -.SS package\-lock\-only +.SS \fBpackage\-lock\-only\fP .RS 0 .IP \(bu 2 Default: false @@ -1217,9 +1209,9 @@ Type: Boolean .RE .P -If set to true, it will update only the \fBpackage\-lock\.json\fP, -instead of checking \fBnode_modules\fP and downloading dependencies\. -.SS parseable +If set to true, it will update only the \fBpackage\-lock\.json\fP, instead of +checking \fBnode_modules\fP and downloading dependencies\. +.SS \fBparseable\fP .RS 0 .IP \(bu 2 Default: false @@ -1228,9 +1220,9 @@ Type: Boolean .RE .P -Output parseable results from commands that write to -standard output\. For \fBnpm search\fP, this will be tab\-separated table format\. -.SS prefer\-offline +Output parseable results from commands that write to standard output\. For +\fBnpm search\fP, this will be tab\-separated table format\. +.SS \fBprefer\-offline\fP .RS 0 .IP \(bu 2 Default: false @@ -1240,10 +1232,9 @@ Type: Boolean .RE .P If true, staleness checks for cached data will be bypassed, but missing data -will be requested from the server\. To force full offline mode, use \fB\-\-offline\fP\|\. -.P -This option is effectively equivalent to \fB\-\-cache\-min=9999999\fP\|\. -.SS prefer\-online +will be requested from the server\. To force full offline mode, use +\fB\-\-offline\fP\|\. +.SS \fBprefer\-online\fP .RS 0 .IP \(bu 2 Default: false @@ -1252,20 +1243,22 @@ Type: Boolean .RE .P -If true, staleness checks for cached data will be forced, making the CLI look -for updates immediately even for fresh package data\. -.SS prefix +If true, staleness checks for cached data will be forced, making the CLI +look for updates immediately even for fresh package data\. +.SS \fBprefix\fP .RS 0 .IP \(bu 2 -Default: see npm help folders +Default: In global mode, the folder where the node executable is installed\. +In local mode, the nearest parent folder containing either a package\.json +file or a node_modules folder\. .IP \(bu 2 -Type: path +Type: Path .RE .P -The location to install global items\. If set on the command line, then -it forces non\-global commands to run in the specified folder\. -.SS preid +The location to install global items\. If set on the command line, then it +forces non\-global commands to run in the specified folder\. +.SS \fBpreid\fP .RS 0 .IP \(bu 2 Default: "" @@ -1274,30 +1267,12 @@ Type: String .RE .P -The "prerelease identifier" to use as a prefix for the "prerelease" part of a -semver\. Like the \fBrc\fP in \fB1\.2\.0\-rc\.8\fP\|\. -.SS production +The "prerelease identifier" to use as a prefix for the "prerelease" part of +a semver\. Like the \fBrc\fP in \fB1\.2\.0\-rc\.8\fP\|\. +.SS \fBprogress\fP .RS 0 .IP \(bu 2 -Default: false -.IP \(bu 2 -Type: Boolean - -.RE -.P -Set to true to run in "production" mode\. -.RS 0 -.IP 1. 3 -devDependencies are not installed at the topmost level when running -local \fBnpm install\fP without any arguments\. -.IP 2. 3 -Set the NODE_ENV="production" for lifecycle scripts\. - -.RE -.SS progress -.RS 0 -.IP \(bu 2 -Default: true, unless TRAVIS or CI env vars set\. +Default: \fBtrue\fP unless running in a known CI system .IP \(bu 2 Type: Boolean @@ -1307,19 +1282,19 @@ When set to \fBtrue\fP, npm will display a progress bar during time intensive operations, if \fBprocess\.stderr\fP is a TTY\. .P Set to \fBfalse\fP to suppress the progress bar\. -.SS proxy +.SS \fBproxy\fP .RS 0 .IP \(bu 2 Default: null .IP \(bu 2 -Type: url +Type: null, false, or URL .RE .P A proxy to use for outgoing http requests\. If the \fBHTTP_PROXY\fP or -\fBhttp_proxy\fP environment variables are set, proxy settings will be -honored by the underlying \fBrequest\fP library\. -.SS read\-only +\fBhttp_proxy\fP environment variables are set, proxy settings will be honored +by the underlying \fBrequest\fP library\. +.SS \fBread\-only\fP .RS 0 .IP \(bu 2 Default: false @@ -1328,8 +1303,9 @@ Type: Boolean .RE .P -This is used to mark a token as unable to publish when configuring limited access tokens with the \fBnpm token create\fP command\. -.SS rebuild\-bundle +This is used to mark a token as unable to publish when configuring limited +access tokens with the \fBnpm token create\fP command\. +.SS \fBrebuild\-bundle\fP .RS 0 .IP \(bu 2 Default: true @@ -1339,27 +1315,17 @@ Type: Boolean .RE .P Rebuild bundled dependencies after installation\. -.SS registry -.RS 0 -.IP \(bu 2 -Default: https://registry\.npmjs\.org/ -.IP \(bu 2 -Type: url - -.RE -.P -The base URL of the npm package registry\. -.SS rollback +.SS \fBregistry\fP .RS 0 .IP \(bu 2 -Default: true +Default: "https://registry\.npmjs\.org/" .IP \(bu 2 -Type: Boolean +Type: URL .RE .P -Remove failed installs\. -.SS save +The base URL of the npm registry\. +.SS \fBsave\fP .RS 0 .IP \(bu 2 Default: true @@ -1370,11 +1336,9 @@ Type: Boolean .P Save installed packages to a package\.json file as dependencies\. .P -When used with the \fBnpm rm\fP command, it removes it from the \fBdependencies\fP -object\. -.P -Only works if there is already a package\.json file present\. -.SS save\-bundle +When used with the \fBnpm rm\fP command, removes the dependency from +package\.json\. +.SS \fBsave\-bundle\fP .RS 0 .IP \(bu 2 Default: false @@ -1387,9 +1351,8 @@ If a package would be saved at install time by the use of \fB\-\-save\fP, \fB\-\-save\-dev\fP, or \fB\-\-save\-optional\fP, then also put it in the \fBbundleDependencies\fP list\. .P -When used with the \fBnpm rm\fP command, it removes it from the -bundledDependencies list\. -.SS save\-prod +Ignore if \fB\-\-save\-peer\fP is set, since peerDependencies cannot be bundled\. +.SS \fBsave\-dev\fP .RS 0 .IP \(bu 2 Default: false @@ -1398,12 +1361,8 @@ Type: Boolean .RE .P -Makes sure that a package will be saved into \fBdependencies\fP specifically\. This -is useful if a package already exists in \fBdevDependencies\fP or -\fBoptionalDependencies\fP, but you want to move it to be a production dep\. This is -also the default behavior if \fB\-\-save\fP is true, and neither \fB\-\-save\-dev\fP or -\fB\-\-save\-optional\fP are true\. -.SS save\-dev +Save installed packages to a package\.json file as \fBdevDependencies\fP\|\. +.SS \fBsave\-exact\fP .RS 0 .IP \(bu 2 Default: false @@ -1412,13 +1371,9 @@ Type: Boolean .RE .P -Save installed packages to a package\.json file as \fBdevDependencies\fP\|\. -.P -When used with the \fBnpm rm\fP command, it removes it from the -\fBdevDependencies\fP object\. -.P -Only works if there is already a package\.json file present\. -.SS save\-exact +Dependencies saved to package\.json will be configured with an exact version +rather than using npm's default semver range operator\. +.SS \fBsave\-optional\fP .RS 0 .IP \(bu 2 Default: false @@ -1427,10 +1382,8 @@ Type: Boolean .RE .P -Dependencies saved to package\.json using \fB\-\-save\fP, \fB\-\-save\-dev\fP or -\fB\-\-save\-optional\fP will be configured with an exact version rather than -using npm's default semver range operator\. -.SS save\-optional +Save installed packages to a package\.json file as \fBoptionalDependencies\fP\|\. +.SS \fBsave\-peer\fP .RS 0 .IP \(bu 2 Default: false @@ -1439,17 +1392,11 @@ Type: Boolean .RE .P -Save installed packages to a package\.json file as -optionalDependencies\. -.P -When used with the \fBnpm rm\fP command, it removes it from the -\fBdevDependencies\fP object\. -.P -Only works if there is already a package\.json file present\. -.SS save\-prefix +Save installed packages\. to a package\.json file as \fBpeerDependencies\fP +.SS \fBsave\-prefix\fP .RS 0 .IP \(bu 2 -Default: '^' +Default: "^" .IP \(bu 2 Type: String @@ -1458,68 +1405,58 @@ Type: String Configure how versions of packages installed to a package\.json file via \fB\-\-save\fP or \fB\-\-save\-dev\fP get prefixed\. .P -For example if a package has version \fB1\.2\.3\fP, by default its version is -set to \fB^1\.2\.3\fP which allows minor upgrades for that package, but after -\fBnpm config set save\-prefix='~'\fP it would be set to \fB~1\.2\.3\fP which only allows +For example if a package has version \fB1\.2\.3\fP, by default its version is set +to \fB^1\.2\.3\fP which allows minor upgrades for that package, but after \fBnpm +config set save\-prefix='~'\fP it would be set to \fB~1\.2\.3\fP which only allows patch upgrades\. -.SS scope +.SS \fBsave\-prod\fP .RS 0 .IP \(bu 2 -Default: the scope of the current project, if any, or "" +Default: false .IP \(bu 2 -Type: String +Type: Boolean .RE .P -Associate an operation with a scope for a scoped registry\. Useful when logging -in to a private registry for the first time: -\fBnpm login \-\-scope=@organization \-\-registry=registry\.organization\.com\fP, which -will cause \fB@organization\fP to be mapped to the registry for future installation -of packages specified according to the pattern \fB@organization/package\fP\|\. -.SS script\-shell -.RS 0 -.IP \(bu 2 -Default: \fBnull\fP -.IP \(bu 2 -Type: path - -.RE +Save installed packages into \fBdependencies\fP specifically\. This is useful if +a package already exists in \fBdevDependencies\fP or \fBoptionalDependencies\fP, but +you want to move it to be a non\-optional production dependency\. .P -The shell to use for scripts run with the \fBnpm run\fP command\. -.SS scripts\-prepend\-node\-path +This is the default behavior if \fB\-\-save\fP is true, and neither \fB\-\-save\-dev\fP +or \fB\-\-save\-optional\fP are true\. +.SS \fBscope\fP .RS 0 .IP \(bu 2 -Default: "warn\-only" +Default: the scope of the current project, if any, or "" .IP \(bu 2 -Type: Boolean, \fB"auto"\fP or \fB"warn\-only"\fP +Type: String .RE .P -If set to \fBtrue\fP, add the directory in which the current \fBnode\fP executable -resides to the \fBPATH\fP environment variable when running scripts, -even if that means that \fBnpm\fP will invoke a different \fBnode\fP executable than -the one which it is running\. +Associate an operation with a scope for a scoped registry\. .P -If set to \fBfalse\fP, never modify \fBPATH\fP with that\. +Useful when logging in to a private registry for the first time: .P -If set to \fB"warn\-only"\fP, never modify \fBPATH\fP but print a warning if \fBnpm\fP thinks -that you may want to run it with \fBtrue\fP, e\.g\. because the \fBnode\fP executable -in the \fBPATH\fP is not the one \fBnpm\fP was invoked with\. +.RS 2 +.nf +npm login \-\-scope=@mycorp \-\-registry=https://registry\.mycorp\.com +.fi +.RE .P -If set to \fBauto\fP, only add that directory to the \fBPATH\fP environment variable -if the \fBnode\fP executable with which \fBnpm\fP was invoked and the one that is found -first on the \fBPATH\fP are different\. -.SS searchexclude +This will cause \fB@mycorp\fP to be mapped to the registry for future +installation of packages specified according to the pattern +\fB@mycorp/package\fP\|\. +.SS \fBscript\-shell\fP .RS 0 .IP \(bu 2 -Default: "" +Default: '/bin/sh' on POSIX systems, 'cmd\.exe' on Windows .IP \(bu 2 -Type: String +Type: null or String .RE .P -Space\-separated options that limit the results from search\. -.SS searchopts +The shell to use for scripts run with the \fBnpm run\fP command\. +.SS \fBsearchexclude\fP .RS 0 .IP \(bu 2 Default: "" @@ -1528,8 +1465,8 @@ Type: String .RE .P -Space\-separated options that are always passed to search\. -.SS searchlimit +Space\-separated options that limit the results from search\. +.SS \fBsearchlimit\fP .RS 0 .IP \(bu 2 Default: 20 @@ -1540,10 +1477,20 @@ Type: Number .P Number of items to limit search results to\. Will not apply at all to legacy searches\. -.SS searchstaleness +.SS \fBsearchopts\fP .RS 0 .IP \(bu 2 -Default: 900 (15 minutes) +Default: "" +.IP \(bu 2 +Type: String + +.RE +.P +Space\-separated options that are always passed to search\. +.SS \fBsearchstaleness\fP +.RS 0 +.IP \(bu 2 +Default: 900 .IP \(bu 2 Type: Number @@ -1551,31 +1498,32 @@ Type: Number .P The age of the cache, in seconds, before another registry request is made if using legacy search endpoint\. -.SS shell +.SS \fBshell\fP .RS 0 .IP \(bu 2 -Default: SHELL environment variable, or "bash" on Posix, or "cmd" on +Default: SHELL environment variable, or "bash" on Posix, or "cmd\.exe" on Windows .IP \(bu 2 -Type: path +Type: String .RE .P The shell to run for the \fBnpm explore\fP command\. -.SS shrinkwrap +.SS \fBsign\-git\-commit\fP .RS 0 .IP \(bu 2 -Default: true +Default: false .IP \(bu 2 Type: Boolean .RE .P -If set to false, then ignore \fBnpm\-shrinkwrap\.json\fP files when installing\. This -will also prevent \fIwriting\fR \fBnpm\-shrinkwrap\.json\fP if \fBsave\fP is true\. +If set to true, then the \fBnpm version\fP command will commit the new package +version using \fB\-S\fP to add a signature\. .P -This option is an alias for \fB\-\-package\-lock\fP\|\. -.SS sign\-git\-commit +Note that git requires you to have set up GPG keys in your git configs for +this to work properly\. +.SS \fBsign\-git\-tag\fP .RS 0 .IP \(bu 2 Default: false @@ -1584,12 +1532,12 @@ Type: Boolean .RE .P -If set to true, then the \fBnpm version\fP command will commit the new package -version using \fB\-S\fP to add a signature\. +If set to true, then the \fBnpm version\fP command will tag the version using +\fB\-s\fP to add a signature\. .P -Note that git requires you to have set up GPG keys in your git configs -for this to work properly\. -.SS sign\-git\-tag +Note that git requires you to have set up GPG keys in your git configs for +this to work properly\. +.SS \fBstrict\-peer\-deps\fP .RS 0 .IP \(bu 2 Default: false @@ -1598,33 +1546,63 @@ Type: Boolean .RE .P -If set to true, then the \fBnpm version\fP command will tag the version -using \fB\-s\fP to add a signature\. +If set to \fBtrue\fP, and \fB\-\-legacy\-peer\-deps\fP is not set, then \fIany\fR +conflicting \fBpeerDependencies\fP will be treated as an install failure, even +if npm could reasonably guess the appropriate resolution based on non\-peer +dependency relationships\. +.P +By default, conflicting \fBpeerDependencies\fP deep in the dependency graph will +be resolved using the nearest non\-peer dependency specification, even if +doing so will result in some packages receiving a peer dependency outside +the range set in their package's \fBpeerDependencies\fP object\. +.P +When such and override is performed, a warning is printed, explaining the +conflict and the packages involved\. If \fB\-\-strict\-peer\-deps\fP is set, then +this warning is treated as a failure\. +.SS \fBstrict\-ssl\fP +.RS 0 +.IP \(bu 2 +Default: true +.IP \(bu 2 +Type: Boolean + +.RE +.P +Whether or not to do SSL key validation when making requests to the registry +via https\. .P -Note that git requires you to have set up GPG keys in your git configs -for this to work properly\. -.SS sso\-poll\-frequency +See also the \fBca\fP config\. +.SS \fBtag\fP .RS 0 .IP \(bu 2 -Default: 500 +Default: "latest" .IP \(bu 2 -Type: Number +Type: String .RE .P -When used with SSO\-enabled \fBauth\-type\fPs, configures how regularly the registry -should be polled while the user is completing authentication\. -.SS sso\-type +If you ask npm to install a package and don't tell it a specific version, +then it will install the specified tag\. +.P +Also the tag that is added to the package@version specified by the \fBnpm tag\fP +command, if no explicit tag is given\. +.SS \fBtag\-version\-prefix\fP .RS 0 .IP \(bu 2 -Default: 'oauth' +Default: "v" .IP \(bu 2 -Type: 'oauth', 'saml', or null +Type: String .RE .P -If \fB\-\-auth\-type=sso\fP, the type of SSO type to use\. -.SS strict\-peer\-deps +If set, alters the prefix used when tagging a new version when performing a +version increment using \fBnpm\-version\fP\|\. To remove the prefix altogether, set +it to the empty string: \fB""\fP\|\. +.P +Because other tools may rely on the convention that npm version tags look +like \fBv1\.0\.0\fP, \fIonly use this property if it is absolutely necessary\fR\|\. In +particular, use care when overriding this setting for public packages\. +.SS \fBtiming\fP .RS 0 .IP \(bu 2 Default: false @@ -1633,19 +1611,48 @@ Type: Boolean .RE .P -If set to \fBtrue\fP, and \fB\-\-legacy\-peer\-deps\fP is not set, then \fIany\fR -conflicting \fBpeerDependencies\fP will be treated as an install failure, even -if npm could reasonably guess the appropriate resolution based on non\-peer -dependency relationships\. +If true, writes an \fBnpm\-debug\fP log to \fB_logs\fP and timing information to +\fB_timing\.json\fP, both in your cache, even if the command completes +successfully\. \fB_timing\.json\fP is a newline delimited list of JSON objects\. +.P +You can quickly view it with this json \fIhttps://npm\.im/json\fR command line: +\fBnpm exec \-\- json \-g < ~/\.npm/_timing\.json\fP\|\. +.SS \fBumask\fP +.RS 0 +.IP \(bu 2 +Default: 0 +.IP \(bu 2 +Type: Octal numeric string in range 0000\.\.0777 (0\.\.511) + +.RE +.P +The "umask" value to use when setting the file creation mode on files and +folders\. +.P +Folders and executables are given a mode which is \fB0o777\fP masked against +this value\. Other files are given a mode which is \fB0o666\fP masked against +this value\. +.P +Note that the underlying system will \fIalso\fR apply its own umask value to +files and folders that are created, and npm does not circumvent this, but +rather adds the \fB\-\-umask\fP config to it\. +.P +Thus, the effective default umask value on most POSIX systems is 0o22, +meaning that folders and executables are created with a mode of 0o755 and +other files are created with a mode of 0o644\. +.SS \fBunicode\fP +.RS 0 +.IP \(bu 2 +Default: false on windows, true on mac/unix systems with a unicode locale, +as defined by the LC_ALL, LC_CTYPE, or LANG environment variables\. +.IP \(bu 2 +Type: Boolean + +.RE .P -By default, conflicting \fBpeerDependencies\fP in the dependency graph will be -resolved using the nearest non\-peer dependency specification, even if doing -so will result in some packages receiving a peer dependency outside the -range set in their package's \fBpeerDependencies\fP object\. When such and -override is performed, a warning is printed, explaining the conflict and -the packages involved\. If \fB\-\-strict\-peer\-deps\fP is set, then the warning is -treated as a failure\. -.SS strict\-ssl +When set to true, npm uses unicode characters in the tree output\. When +false, it uses ascii characters instead of unicode glyphs\. +.SS \fBupdate\-notifier\fP .RS 0 .IP \(bu 2 Default: true @@ -1654,88 +1661,122 @@ Type: Boolean .RE .P -Whether or not to do SSL key validation when making requests to the -registry via https\. +Set to false to suppress the update notification when using an older version +of npm than the latest\. +.SS \fBusage\fP +.RS 0 +.IP \(bu 2 +Default: false +.IP \(bu 2 +Type: Boolean + +.RE .P -See also the \fBca\fP config\. -.SS tag +Show short usage output about the command specified\. +.SS \fBuser\-agent\fP .RS 0 .IP \(bu 2 -Default: latest +Default: "npm/{npm\-version} node/{node\-version} {platform} {arch} {ci}" .IP \(bu 2 Type: String .RE .P -If you ask npm to install a package and don't tell it a specific version, then -it will install the specified tag\. +Sets the User\-Agent request header\. The following fields are replaced with +their actual counterparts: +.RS 0 +.IP \(bu 2 +\fB{npm\-version}\fP \- The npm version in use +.IP \(bu 2 +\fB{node\-version}\fP \- The Node\.js version in use +.IP \(bu 2 +\fB{platform}\fP \- The value of \fBprocess\.platform\fP +.IP \(bu 2 +\fB{arch}\fP \- The value of \fBprocess\.arch\fP +.IP \(bu 2 +\fB{ci}\fP \- The value of the \fBci\-name\fP config, if set, prefixed with \fBci/\fP, or +an empty string if \fBci\-name\fP is empty\. + +.RE +.SS \fBuserconfig\fP +.RS 0 +.IP \(bu 2 +Default: "~/\.npmrc" +.IP \(bu 2 +Type: Path + +.RE +.P +The location of user\-level configuration settings\. .P -Also the tag that is added to the package@version specified by the \fBnpm -tag\fP command, if no explicit tag is given\. -.SS tag\-version\-prefix +This may be overridden by the \fBnpm_config_userconfig\fP environment variable +or the \fB\-\-userconfig\fP command line option, but may \fInot\fR be overridden by +settings in the \fBglobalconfig\fP file\. +.SS \fBversion\fP .RS 0 .IP \(bu 2 -Default: \fB"v"\fP +Default: false .IP \(bu 2 -Type: String +Type: Boolean .RE .P -If set, alters the prefix used when tagging a new version when performing a -version increment using \fBnpm\-version\fP\|\. To remove the prefix altogether, set it -to the empty string: \fB""\fP\|\. +If true, output the npm version and exit successfully\. .P -Because other tools may rely on the convention that npm version tags look like -\fBv1\.0\.0\fP, \fIonly use this property if it is absolutely necessary\fR\|\. In -particular, use care when overriding this setting for public packages\. -.SS timing +Only relevant when specified explicitly on the command line\. +.SS \fBversions\fP .RS 0 .IP \(bu 2 -Default: \fBfalse\fP +Default: false .IP \(bu 2 Type: Boolean .RE .P -If true, writes an \fBnpm\-debug\fP log to \fB_logs\fP and timing information to -\fB_timing\.json\fP, both in your cache\. \fB_timing\.json\fP is a newline delimited -list of JSON objects\. You can quickly view it with this -json \fIhttps://www\.npmjs\.com/package/json\fR command line: -\fBjson \-g < ~/\.npm/_timing\.json\fP\|\. -.SS tmp +If true, output the npm version as well as node's \fBprocess\.versions\fP map and +the version in the current working directory's \fBpackage\.json\fP file if one +exists, and exit successfully\. +.P +Only relevant when specified explicitly on the command line\. +.SS \fBviewer\fP .RS 0 .IP \(bu 2 -Default: TMPDIR environment variable, or "/tmp" +Default: "man" on Posix, "browser" on Windows .IP \(bu 2 -Type: path +Type: String .RE .P -Where to store temporary files and folders\. All temp files are deleted -on success, but left behind on failure for forensic purposes\. -.SS unicode +The program to use to view help content\. +.P +Set to \fB"browser"\fP to view html help content in the default web browser\. +.SS \fBwhich\fP .RS 0 .IP \(bu 2 -Default: false on windows, true on mac/unix systems with a unicode locale +Default: null .IP \(bu 2 -Type: Boolean +Type: null or Number .RE .P -When set to true, npm uses unicode characters in the tree output\. When -false, it uses ascii characters to draw trees\. -.SS update\-notifier +If there are multiple funding sources, which 1\-indexed source URL to open\. +.SS \fBworkspace\fP .RS 0 .IP \(bu 2 -Default: true +Default: .IP \(bu 2 -Type: Boolean +Type: String (can be set multiple times) .RE .P -Set to false to suppress the update notification when using an older -version of npm than the latest\. -.SS usage +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option\. +.P +Valid values for the \fBworkspace\fP config are either: \- Workspace names \- Path +to a workspace directory \- Path to a parent workspace directory (will result +to selecting all of the nested workspaces) +.SS \fBworkspaces\fP .RS 0 .IP \(bu 2 Default: false @@ -1744,80 +1785,236 @@ Type: Boolean .RE .P -Set to show short usage output (like the \-H output) -instead of complete help when doing npm help \fBhelp\fP\|\. -.SS userconfig +Enable running a command in the context of \fBall\fR the configured +workspaces\. +.SS \fByes\fP .RS 0 .IP \(bu 2 -Default: ~/\.npmrc +Default: null .IP \(bu 2 -Type: path +Type: null or Boolean .RE .P -The location of user\-level configuration settings\. -.SS umask +Automatically answer "yes" to any prompts that npm might print on the +command line\. +.SS \fBalso\fP .RS 0 .IP \(bu 2 -Default: 022 +Default: null .IP \(bu 2 -Type: Octal numeric string in range 0000\.\.0777 (0\.\.511) +Type: null, "dev", or "development" +.IP \(bu 2 +DEPRECATED: Please use \-\-include=dev instead\. .RE .P -The "umask" value to use when setting the file creation mode on files -and folders\. +When set to \fBdev\fP or \fBdevelopment\fP, this is an alias for \fB\-\-include=dev\fP\|\. +.SS \fBauth\-type\fP +.RS 0 +.IP \(bu 2 +Default: "legacy" +.IP \(bu 2 +Type: "legacy", "sso", "saml", or "oauth" +.IP \(bu 2 +DEPRECATED: This method of SSO/SAML/OAuth is deprecated and will be removed +in a future version of npm in favor of web\-based login\. + +.RE .P -Folders and executables are given a mode which is \fB0777\fP masked against -this value\. Other files are given a mode which is \fB0666\fP masked against -this value\. Thus, the defaults are \fB0755\fP and \fB0644\fP respectively\. -.SS user\-agent +What authentication strategy to use with \fBadduser\fP/\fBlogin\fP\|\. +.SS \fBcache\-max\fP .RS 0 .IP \(bu 2 -Default: node/{process\.version} {process\.platform} {process\.arch} +Default: Infinity +.IP \(bu 2 +Type: Number +.IP \(bu 2 +DEPRECATED: This option has been deprecated in favor of \fB\-\-prefer\-online\fP + +.RE +.P +\fB\-\-cache\-max=0\fP is an alias for \fB\-\-prefer\-online\fP +.SS \fBcache\-min\fP +.RS 0 +.IP \(bu 2 +Default: 0 +.IP \(bu 2 +Type: Number +.IP \(bu 2 +DEPRECATED: This option has been deprecated in favor of \fB\-\-prefer\-offline\fP\|\. + +.RE +.P +\fB\-\-cache\-min=9999 (or bigger)\fP is an alias for \fB\-\-prefer\-offline\fP\|\. +.SS \fBinit\.author\.email\fP +.RS 0 +.IP \(bu 2 +Default: "" .IP \(bu 2 Type: String +.IP \(bu 2 +DEPRECATED: Use \fB\-\-init\-author\-email\fP instead\. .RE .P -Sets a User\-Agent to the request header -.SS version +Alias for \fB\-\-init\-author\-email\fP +.SS \fBinit\.author\.name\fP .RS 0 .IP \(bu 2 -Default: false +Default: "" +.IP \(bu 2 +Type: String .IP \(bu 2 -Type: boolean +DEPRECATED: Use \fB\-\-init\-author\-name\fP instead\. .RE .P -If true, output the npm version and exit successfully\. +Alias for \fB\-\-init\-author\-name\fP +.SS \fBinit\.author\.url\fP +.RS 0 +.IP \(bu 2 +Default: "" +.IP \(bu 2 +Type: "" or URL +.IP \(bu 2 +DEPRECATED: Use \fB\-\-init\-author\-url\fP instead\. + +.RE .P -Only relevant when specified explicitly on the command line\. -.SS versions +Alias for \fB\-\-init\-author\-url\fP +.SS \fBinit\.license\fP +.RS 0 +.IP \(bu 2 +Default: "ISC" +.IP \(bu 2 +Type: String +.IP \(bu 2 +DEPRECATED: Use \fB\-\-init\-license\fP instead\. + +.RE +.P +Alias for \fB\-\-init\-license\fP +.SS \fBinit\.module\fP +.RS 0 +.IP \(bu 2 +Default: "~/\.npm\-init\.js" +.IP \(bu 2 +Type: Path +.IP \(bu 2 +DEPRECATED: Use \fB\-\-init\-module\fP instead\. + +.RE +.P +Alias for \fB\-\-init\-module\fP +.SS \fBinit\.version\fP +.RS 0 +.IP \(bu 2 +Default: "1\.0\.0" +.IP \(bu 2 +Type: SemVer string +.IP \(bu 2 +DEPRECATED: Use \fB\-\-init\-version\fP instead\. + +.RE +.P +Alias for \fB\-\-init\-version\fP +.SS \fBonly\fP +.RS 0 +.IP \(bu 2 +Default: null +.IP \(bu 2 +Type: null, "prod", or "production" +.IP \(bu 2 +DEPRECATED: Use \fB\-\-omit=dev\fP to omit dev dependencies from the install\. + +.RE +.P +When set to \fBprod\fP or \fBproduction\fP, this is an alias for \fB\-\-omit=dev\fP\|\. +.SS \fBoptional\fP +.RS 0 +.IP \(bu 2 +Default: null +.IP \(bu 2 +Type: null or Boolean +.IP \(bu 2 +DEPRECATED: Use \fB\-\-omit=optional\fP to exclude optional dependencies, or +\fB\-\-include=optional\fP to include them\. + +.RE +.P +Default value does install optional deps unless otherwise omitted\. +.P +Alias for \-\-include=optional or \-\-omit=optional +.SS \fBproduction\fP .RS 0 .IP \(bu 2 Default: false .IP \(bu 2 -Type: boolean +Type: Boolean +.IP \(bu 2 +DEPRECATED: Use \fB\-\-omit=dev\fP instead\. .RE .P -If true, output the npm version as well as node's \fBprocess\.versions\fP map, and -exit successfully\. +Alias for \fB\-\-omit=dev\fP +.SS \fBshrinkwrap\fP +.RS 0 +.IP \(bu 2 +Default: true +.IP \(bu 2 +Type: Boolean +.IP \(bu 2 +DEPRECATED: Use the \-\-package\-lock setting instead\. + +.RE .P -Only relevant when specified explicitly on the command line\. -.SS viewer +Alias for \-\-package\-lock +.SS \fBsso\-poll\-frequency\fP .RS 0 .IP \(bu 2 -Default: "man" on Posix, "browser" on Windows +Default: 500 +.IP \(bu 2 +Type: Number .IP \(bu 2 -Type: path +DEPRECATED: The \-\-auth\-type method of SSO/SAML/OAuth will be removed in a +future version of npm in favor of web\-based login\. .RE .P -The program to use to view help content\. +When used with SSO\-enabled \fBauth\-type\fPs, configures how regularly the +registry should be polled while the user is completing authentication\. +.SS \fBsso\-type\fP +.RS 0 +.IP \(bu 2 +Default: "oauth" +.IP \(bu 2 +Type: null, "oauth", or "saml" +.IP \(bu 2 +DEPRECATED: The \-\-auth\-type method of SSO/SAML/OAuth will be removed in a +future version of npm in favor of web\-based login\. + +.RE .P -Set to \fB"browser"\fP to view html help content in the default web browser\. +If \fB\-\-auth\-type=sso\fP, the type of SSO type to use\. +.SS \fBtmp\fP +.RS 0 +.IP \(bu 2 +Default: The value returned by the Node\.js \fBos\.tmpdir()\fP method +https://nodejs\.org/api/os\.html#os_os_tmpdir +.IP \(bu 2 +Type: Path +.IP \(bu 2 +DEPRECATED: This setting is no longer used\. npm stores temporary files in a +special location in the cache, and they are managed by +\fBcacache\fP \fIhttp://npm\.im/cacache\fR\|\. + +.RE +.P +Historically, the location where temporary files were stored\. No longer +relevant\. +<!\-\- AUTOGENERATED CONFIG DESCRIPTIONS END \-\-> + .SS See also .RS 0 .IP \(bu 2 diff --git a/deps/npm/man/man7/workspaces.7 b/deps/npm/man/man7/workspaces.7 index 61da2ce657c5c6..2fa6a9ccccdd9e 100644 --- a/deps/npm/man/man7/workspaces.7 +++ b/deps/npm/man/man7/workspaces.7 @@ -90,11 +90,65 @@ This demonstrates how the nature of \fBnode_modules\fP resolution allows for \fBworkspaces\fR to enable a portable workflow for requiring each \fBworkspace\fR in such a way that is also easy to npm help publish these nested workspaces to be consumed elsewhere\. +.SS Running commands in the context of workspaces +.P +You man use the \fBworkspace\fP configuration option to run commands in the context +of a configured workspace\. +.P +Following is a quick example on how to use the \fBnpm run\fP command in the context +of nested workspaces\. For a project containing multiple workspaces, e\.g: +.P +.RS 2 +.nf +\|\. ++\-\- package\.json +`\-\- packages + +\-\- a + | `\-\- package\.json + `\-\- b + `\-\- package\.json +.fi +.RE +.P +By running a command using the \fBworkspace\fP option, it's possible to run the +given command in the context of that specific workspace\. e\.g: +.P +.RS 2 +.nf +npm run test \-\-workspace=a +.fi +.RE +.P +This will run the \fBtest\fP script defined within the +\fB\|\./packages/a/package\.json\fP file\. +.P +Please note that you can also specify this argument multiple times in the +command\-line in order to target multiple workspaces, e\.g: +.P +.RS 2 +.nf +npm run test \-\-workspace=a \-\-workspace=b +.fi +.RE +.P +It's also possible to use the \fBworkspaces\fP (plural) configuration option to +enable the same behavior but running that command in the context of \fBall\fR +configured workspaces\. e\.g: +.P +.RS 2 +.nf +npm run test \-\-workspaces +.fi +.RE +.P +Will run the \fBtest\fP script in both \fB\|\./packages/a\fP and \fB\|\./packages/b\fP\|\. .SS See also .RS 0 .IP \(bu 2 npm help install .IP \(bu 2 npm help publish +.IP \(bu 2 +npm help run\-script .RE diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js b/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js index 9e4825c526451c..9a96fd1b3797cd 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/add-rm-pkg-deps.js @@ -71,7 +71,7 @@ const addSingle = ({pkg, spec, saveBundle, saveType}) => { pkg.devDependencies[name] = pkg.peerDependencies[name] } - if (saveBundle) { + if (saveBundle && saveType !== 'peer' && saveType !== 'peerOptional') { // keep it sorted, keep it unique const bd = new Set(pkg.bundleDependencies || []) bd.add(spec.name) diff --git a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js index 803fb9782f07c0..0008045528d685 100644 --- a/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js +++ b/deps/npm/node_modules/@npmcli/arborist/lib/arborist/reify.js @@ -890,7 +890,7 @@ module.exports = cls => class Reifier extends cls { const root = this.idealTree const pkg = root.package for (const { name } of this[_resolvedAdd]) { - const req = npa(root.edgesOut.get(name).spec, root.realpath) + const req = npa.resolve(name, root.edgesOut.get(name).spec, root.realpath) const {rawSpec, subSpec} = req const spec = subSpec ? subSpec.rawSpec : rawSpec diff --git a/deps/npm/node_modules/@npmcli/arborist/package.json b/deps/npm/node_modules/@npmcli/arborist/package.json index bff10db4b0dd86..e745be2c77f7c0 100644 --- a/deps/npm/node_modules/@npmcli/arborist/package.json +++ b/deps/npm/node_modules/@npmcli/arborist/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/arborist", - "version": "2.2.8", + "version": "2.2.9", "description": "Manage node_modules trees", "dependencies": { "@npmcli/installed-package-contents": "^1.0.7", @@ -26,7 +26,7 @@ "promise-call-limit": "^1.0.1", "read-package-json-fast": "^2.0.2", "readdir-scoped-modules": "^1.1.0", - "semver": "^7.3.4", + "semver": "^7.3.5", "tar": "^6.1.0", "treeverse": "^1.0.4", "walk-up-path": "^1.0.0" diff --git a/deps/npm/node_modules/@npmcli/config/lib/get-user-agent.js b/deps/npm/node_modules/@npmcli/config/lib/get-user-agent.js deleted file mode 100644 index c3d3c7a1bf3de2..00000000000000 --- a/deps/npm/node_modules/@npmcli/config/lib/get-user-agent.js +++ /dev/null @@ -1,13 +0,0 @@ -// Accepts a config object, returns a user-agent string -const getUserAgent = (config) => { - const ciName = config.get('ci-name') - return (config.get('user-agent') || '') - .replace(/\{node-version\}/gi, config.get('node-version')) - .replace(/\{npm-version\}/gi, config.get('npm-version')) - .replace(/\{platform\}/gi, process.platform) - .replace(/\{arch\}/gi, process.arch) - .replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '') - .trim() -} - -module.exports = getUserAgent diff --git a/deps/npm/node_modules/@npmcli/config/lib/index.js b/deps/npm/node_modules/@npmcli/config/lib/index.js index e7fac96c1c8c8f..21a37ded48e7c3 100644 --- a/deps/npm/node_modules/@npmcli/config/lib/index.js +++ b/deps/npm/node_modules/@npmcli/config/lib/index.js @@ -47,7 +47,6 @@ const envReplace = require('./env-replace.js') const parseField = require('./parse-field.js') const typeDescription = require('./type-description.js') const setEnvs = require('./set-envs.js') -const getUserAgent = require('./get-user-agent.js') // types that can be saved back to const confFileTypes = new Set([ @@ -69,6 +68,9 @@ const _get = Symbol('get') const _find = Symbol('find') const _loadObject = Symbol('loadObject') const _loadFile = Symbol('loadFile') +const _checkDeprecated = Symbol('checkDeprecated') +const _flatten = Symbol('flatten') +const _flatOptions = Symbol('flatOptions') class Config { static get typeDefs () { @@ -76,9 +78,9 @@ class Config { } constructor ({ - types, + definitions, shorthands, - defaults, + flatten, npmPath, // options just to override in tests, mostly @@ -89,10 +91,27 @@ class Config { execPath = process.execPath, cwd = process.cwd(), }) { - this.npmPath = npmPath + + // turn the definitions into nopt's weirdo syntax + this.definitions = definitions + const types = {} + const defaults = {} + this.deprecated = {} + for (const [key, def] of Object.entries(definitions)) { + defaults[key] = def.default + types[key] = def.type + if (def.deprecated) + this.deprecated[key] = def.deprecated.trim().replace(/\n +/, '\n') + } + + // populated the first time we flatten the object + this[_flatOptions] = null + this[_flatten] = flatten this.types = types this.shorthands = shorthands this.defaults = defaults + + this.npmPath = npmPath this.log = log this.argv = argv this.env = env @@ -183,10 +202,31 @@ class Config { if (!email) throw new Error('Cannot set _auth without first setting email') } - this.data.get(where).data[key] = val + this[_checkDeprecated](key) + const { data } = this.data.get(where) + data[key] = val // this is now dirty, the next call to this.valid will have to check it this.data.get(where)[_valid] = null + + // the flat options are invalidated, regenerate next time they're needed + this[_flatOptions] = null + } + + get flat () { + if (this[_flatOptions]) + return this[_flatOptions] + + // create the object for flat options passed to deps + process.emit('time', 'config:load:flatten') + this[_flatOptions] = {} + // walk from least priority to highest + for (const { data } of this.data.values()) { + this[_flatten](data, this[_flatOptions]) + } + process.emit('timeEnd', 'config:load:flatten') + + return this[_flatOptions] } delete (key, where = 'cli') { @@ -233,11 +273,6 @@ class Config { await this.loadGlobalConfig() process.emit('timeEnd', 'config:load:global') - // now the extras - process.emit('time', 'config:load:cafile') - await this.loadCAFile() - process.emit('timeEnd', 'config:load:cafile') - // warn if anything is not valid process.emit('time', 'config:load:validate') this.validate() @@ -250,10 +285,6 @@ class Config { // set proper globalPrefix now that everything is loaded this.globalPrefix = this.get('prefix') - process.emit('time', 'config:load:setUserAgent') - this.setUserAgent() - process.emit('timeEnd', 'config:load:setUserAgent') - process.emit('time', 'config:load:setEnvs') this.setEnvs() process.emit('timeEnd', 'config:load:setEnvs') @@ -376,13 +407,13 @@ class Config { this.data.get(where)[_valid] = false if (Array.isArray(type)) { - if (type.indexOf(typeDefs.url.type) !== -1) + if (type.includes(typeDefs.url.type)) type = typeDefs.url.type else { /* istanbul ignore if - no actual configs matching this, but * path types SHOULD be handled this way, like URLs, for the * same reason */ - if (type.indexOf(typeDefs.path.type) !== -1) + if (type.includes(typeDefs.path.type)) type = typeDefs.path.type } } @@ -428,11 +459,21 @@ class Config { for (const [key, value] of Object.entries(obj)) { const k = envReplace(key, this.env) const v = this.parseField(value, k) + if (where !== 'default') + this[_checkDeprecated](k, where, obj, [key, value]) conf.data[k] = v } } } + [_checkDeprecated] (key, where, obj, kv) { + // XXX a future npm version will make this a warning. + // An even more future npm version will make this an error. + if (this.deprecated[key]) { + this.log.verbose('config', key, this.deprecated[key]) + } + } + // Parse a field, coercing it to the best type available. parseField (f, key, listElement = false) { return parseField(f, key, this, listElement) @@ -675,48 +716,6 @@ class Config { return creds } - async loadCAFile () { - const where = this[_find]('cafile') - - /* istanbul ignore if - it'll always be set in the defaults */ - if (!where) - return - - const cafile = this[_get]('cafile', where) - const ca = this[_get]('ca', where) - - // if you have a ca, or cafile is set to null, then nothing to do here. - if (ca || !cafile) - return - - const raw = await readFile(cafile, 'utf8').catch(er => { - if (er.code !== 'ENOENT') - throw er - }) - if (!raw) - return - - const delim = '-----END CERTIFICATE-----' - const output = raw.replace(/\r\n/g, '\n').split(delim) - .filter(section => section.trim()) - .map(section => section.trimLeft() + delim) - - // make it non-enumerable so we don't save it back by accident - const { data } = this.data.get(where) - Object.defineProperty(data, 'ca', { - value: output, - enumerable: false, - configurable: true, - writable: true, - }) - } - - // the user-agent configuration is a template that gets populated - // with some variables, that takes place here - setUserAgent () { - this.set('user-agent', getUserAgent(this)) - } - // set up the environment object we have with npm_config_* environs // for all configs that are different from their default values, and // set EDITOR and HOME. diff --git a/deps/npm/node_modules/@npmcli/config/lib/set-envs.js b/deps/npm/node_modules/@npmcli/config/lib/set-envs.js index 36d37145466e0d..ffaf5ab383c75f 100644 --- a/deps/npm/node_modules/@npmcli/config/lib/set-envs.js +++ b/deps/npm/node_modules/@npmcli/config/lib/set-envs.js @@ -50,11 +50,13 @@ const setEnvs = (config) => { platform, env, defaults, + definitions, list: [cliConf, envConf], } = config env.INIT_CWD = process.cwd() + // if the key is deprecated, skip it always. // if the key is the default value, // if the environ is NOT the default value, // set the environ @@ -65,6 +67,10 @@ const setEnvs = (config) => { const cliSet = new Set(Object.keys(cliConf)) const envSet = new Set(Object.keys(envConf)) for (const key in cliConf) { + const { deprecated } = definitions[key] || {} + if (deprecated) + continue + if (sameConfigValue(defaults[key], cliConf[key])) { // config is the default, if the env thought different, then we // have to set it BACK to the default in the environment. diff --git a/deps/npm/node_modules/@npmcli/config/package.json b/deps/npm/node_modules/@npmcli/config/package.json index 644544a49d8698..b2b34f6af2712f 100644 --- a/deps/npm/node_modules/@npmcli/config/package.json +++ b/deps/npm/node_modules/@npmcli/config/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/config", - "version": "1.2.9", + "version": "2.0.0", "files": [ "lib" ], diff --git a/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js b/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js index 4c38b9401ddf06..8ee24c06daf7b1 100644 --- a/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js +++ b/deps/npm/node_modules/@npmcli/run-script/lib/make-spawn-args.js @@ -3,24 +3,6 @@ const isWindows = require('./is-windows.js') const setPATH = require('./set-path.js') const {resolve} = require('path') const npm_config_node_gyp = require.resolve('node-gyp/bin/node-gyp.js') -const { quoteForShell, ShellString, ShellStringText, ShellStringUnquoted } = require('puka') - -const escapeCmd = cmd => { - const result = [] - const parsed = ShellString.sh([cmd]) - for (const child of parsed.children) { - if (child instanceof ShellStringText) { - const children = child.contents.filter(segment => segment !== null).map(segment => quoteForShell(segment, false, isWindows && 'win32')) - result.push(...children) - } else if (child instanceof ShellStringUnquoted) { - result.push(child.value) - } else { - result.push(isWindows ? '&' : ';') - } - } - - return result.join('') -} const makeSpawnArgs = options => { const { @@ -34,7 +16,7 @@ const makeSpawnArgs = options => { } = options const isCmd = /(?:^|\\)cmd(?:\.exe)?$/i.test(scriptShell) - const args = isCmd ? ['/d', '/s', '/c', escapeCmd(cmd)] : ['-c', cmd] + const args = isCmd ? ['/d', '/s', '/c', cmd] : ['-c', cmd] const spawnOpts = { env: setPATH(path, { diff --git a/deps/npm/node_modules/@npmcli/run-script/package.json b/deps/npm/node_modules/@npmcli/run-script/package.json index 9df5b311787472..7e0e5255de4105 100644 --- a/deps/npm/node_modules/@npmcli/run-script/package.json +++ b/deps/npm/node_modules/@npmcli/run-script/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/run-script", - "version": "1.8.3", + "version": "1.8.4", "description": "Run a lifecycle script for a package (descendant of npm-lifecycle)", "author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)", "license": "ISC", @@ -32,7 +32,6 @@ "@npmcli/promise-spawn": "^1.3.2", "infer-owner": "^1.0.4", "node-gyp": "^7.1.0", - "puka": "^1.0.1", "read-package-json-fast": "^2.0.1" }, "files": [ diff --git a/deps/npm/node_modules/cacache/CHANGELOG.md b/deps/npm/node_modules/cacache/CHANGELOG.md index 14eee0b381c071..d10160846b1572 100644 --- a/deps/npm/node_modules/cacache/CHANGELOG.md +++ b/deps/npm/node_modules/cacache/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [15.0.6](https://github.com/npm/cacache/compare/v15.0.5...v15.0.6) (2021-03-22) + ### [15.0.5](https://github.com/npm/cacache/compare/v15.0.4...v15.0.5) (2020-07-11) ### [15.0.4](https://github.com/npm/cacache/compare/v15.0.3...v15.0.4) (2020-06-03) diff --git a/deps/npm/node_modules/cacache/package.json b/deps/npm/node_modules/cacache/package.json index 053c245b52b726..afe569c8bd6cb1 100644 --- a/deps/npm/node_modules/cacache/package.json +++ b/deps/npm/node_modules/cacache/package.json @@ -1,6 +1,6 @@ { "name": "cacache", - "version": "15.0.5", + "version": "15.0.6", "cache-version": { "content": "2", "index": "5" @@ -72,7 +72,7 @@ "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", - "ssri": "^8.0.0", + "ssri": "^8.0.1", "tar": "^6.0.2", "unique-filename": "^1.1.1" }, diff --git a/deps/npm/node_modules/hosted-git-info/README.md b/deps/npm/node_modules/hosted-git-info/README.md index 7b723f6b9e2134..87404060296269 100644 --- a/deps/npm/node_modules/hosted-git-info/README.md +++ b/deps/npm/node_modules/hosted-git-info/README.md @@ -22,7 +22,7 @@ var info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts) If the URL can't be matched with a git host, `null` will be returned. We can match git, ssh and https urls. Additionally, we can match ssh connect strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg, -`github:npm/hosted-git-info`). Github specifically, is detected in the case +`github:npm/hosted-git-info`). GitHub specifically, is detected in the case of a third, unprefixed, form: `npm/hosted-git-info`. If it does match, the returned object has properties of: @@ -129,5 +129,5 @@ SSH connect strings will be normalized into `git+ssh` URLs. ## Supported hosts -Currently this supports Github, Bitbucket and Gitlab. Pull requests for +Currently this supports GitHub, Bitbucket and GitLab. Pull requests for additional hosts welcome. diff --git a/deps/npm/node_modules/hosted-git-info/git-host-info.js b/deps/npm/node_modules/hosted-git-info/git-host-info.js index da3348fa7b817c..360d7b096be617 100644 --- a/deps/npm/node_modules/hosted-git-info/git-host-info.js +++ b/deps/npm/node_modules/hosted-git-info/git-host-info.js @@ -1,79 +1,154 @@ 'use strict' +const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : '' +const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : '' -var gitHosts = module.exports = { - github: { - // First two are insecure and generally shouldn't be used any more, but - // they are still supported. - 'protocols': [ 'git', 'http', 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'github.com', - 'treepath': 'tree', - 'filetemplate': 'https://{auth@}raw.githubusercontent.com/{user}/{project}/{committish}/{path}', - 'bugstemplate': 'https://{domain}/{user}/{project}/issues', - 'gittemplate': 'git://{auth@}{domain}/{user}/{project}.git{#committish}', - 'tarballtemplate': 'https://codeload.{domain}/{user}/{project}/tar.gz/{committish}' - }, - bitbucket: { - 'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'bitbucket.org', - 'treepath': 'src', - 'tarballtemplate': 'https://{domain}/{user}/{project}/get/{committish}.tar.gz' - }, - gitlab: { - 'protocols': [ 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'gitlab.com', - 'treepath': 'tree', - 'bugstemplate': 'https://{domain}/{user}/{project}/issues', - 'httpstemplate': 'git+https://{auth@}{domain}/{user}/{projectPath}.git{#committish}', - 'tarballtemplate': 'https://{domain}/{user}/{project}/repository/archive.tar.gz?ref={committish}', - 'pathmatch': /^\/([^/]+)\/((?!.*(\/-\/|\/repository(\/[^/]+)?\/archive\.tar\.gz)).*?)(?:\.git|\/)?$/ - }, - gist: { - 'protocols': [ 'git', 'git+ssh', 'git+https', 'ssh', 'https' ], - 'domain': 'gist.github.com', - 'pathmatch': /^[/](?:([^/]+)[/])?([a-z0-9]{7,})(?:[.]git)?$/, - 'filetemplate': 'https://gist.githubusercontent.com/{user}/{project}/raw{/committish}/{path}', - 'bugstemplate': 'https://{domain}/{project}', - 'gittemplate': 'git://{domain}/{project}.git{#committish}', - 'sshtemplate': 'git@{domain}:/{project}.git{#committish}', - 'sshurltemplate': 'git+ssh://git@{domain}/{project}.git{#committish}', - 'browsetemplate': 'https://{domain}/{project}{/committish}', - 'browsefiletemplate': 'https://{domain}/{project}{/committish}{#path}', - 'docstemplate': 'https://{domain}/{project}{/committish}', - 'httpstemplate': 'git+https://{domain}/{project}.git{#committish}', - 'shortcuttemplate': '{type}:{project}{#committish}', - 'pathtemplate': '{project}{#committish}', - 'tarballtemplate': 'https://codeload.github.com/gist/{project}/tar.gz/{committish}', - 'hashformat': function (fragment) { - return 'file-' + formatHashFragment(fragment) +const defaults = { + sshtemplate: ({ domain, user, project, committish }) => `git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`, + sshurltemplate: ({ domain, user, project, committish }) => `git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`, + browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`, + docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`, + httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/raw/${maybeEncode(committish) || 'master'}/${path}`, + shortcuttemplate: ({ type, user, project, committish }) => `${type}:${user}/${project}${maybeJoin('#', committish)}`, + pathtemplate: ({ user, project, committish }) => `${user}/${project}${maybeJoin('#', committish)}`, + bugstemplate: ({ domain, user, project }) => `https://${domain}/${user}/${project}/issues`, + hashformat: formatHashFragment +} + +const gitHosts = {} +gitHosts.github = Object.assign({}, defaults, { + // First two are insecure and generally shouldn't be used any more, but + // they are still supported. + protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'github.com', + treepath: 'tree', + filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${path}`, + gittemplate: ({ auth, domain, user, project, committish }) => `git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ domain, user, project, committish }) => `https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, + extract: (url) => { + let [, user, project, type, committish] = url.pathname.split('/', 5) + if (type && type !== 'tree') { + return + } + + if (!type) { + committish = url.hash.slice(1) + } + + if (project && project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (!user || !project) { + return } + + return { user, project, committish } } -} +}) -var gitHostDefaults = { - 'sshtemplate': 'git@{domain}:{user}/{project}.git{#committish}', - 'sshurltemplate': 'git+ssh://git@{domain}/{user}/{project}.git{#committish}', - 'browsetemplate': 'https://{domain}/{user}/{project}{/tree/committish}', - 'browsefiletemplate': 'https://{domain}/{user}/{project}/{treepath}/{committish}/{path}{#fragment}', - 'docstemplate': 'https://{domain}/{user}/{project}{/tree/committish}#readme', - 'httpstemplate': 'git+https://{auth@}{domain}/{user}/{project}.git{#committish}', - 'filetemplate': 'https://{domain}/{user}/{project}/raw/{committish}/{path}', - 'shortcuttemplate': '{type}:{user}/{project}{#committish}', - 'pathtemplate': '{user}/{project}{#committish}', - 'pathmatch': /^[/]([^/]+)[/]([^/]+?)(?:[.]git|[/])?$/, - 'hashformat': formatHashFragment -} +gitHosts.bitbucket = Object.assign({}, defaults, { + protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'bitbucket.org', + treepath: 'src', + tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.tar.gz`, + extract: (url) => { + let [, user, project, aux] = url.pathname.split('/', 4) + if (['get'].includes(aux)) { + return + } + + if (project && project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (!user || !project) { + return + } + + return { user, project, committish: url.hash.slice(1) } + } +}) + +gitHosts.gitlab = Object.assign({}, defaults, { + protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'gitlab.com', + treepath: 'tree', + httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`, + extract: (url) => { + const path = url.pathname.slice(1) + if (path.includes('/-/')) { + return + } + + const segments = path.split('/') + let project = segments.pop() + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + const user = segments.join('/') + if (!user || !project) { + return + } + + return { user, project, committish: url.hash.slice(1) } + } +}) + +gitHosts.gist = Object.assign({}, defaults, { + protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'gist.github.com', + sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`, + sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`, + browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, + browsefiletemplate: ({ domain, project, committish, path, hashformat }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`, + docstemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, + httpstemplate: ({ domain, project, committish }) => `git+https://${domain}/${project}.git${maybeJoin('#', committish)}`, + filetemplate: ({ user, project, committish, path }) => `https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`, + shortcuttemplate: ({ type, project, committish }) => `${type}:${project}${maybeJoin('#', committish)}`, + pathtemplate: ({ project, committish }) => `${project}${maybeJoin('#', committish)}`, + bugstemplate: ({ domain, project }) => `https://${domain}/${project}`, + gittemplate: ({ domain, project, committish }) => `git://${domain}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ project, committish }) => `https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, + extract: (url) => { + let [, user, project, aux] = url.pathname.split('/', 4) + if (aux === 'raw') { + return + } + + if (!project) { + if (!user) { + return + } + + project = user + user = null + } + + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } -Object.keys(gitHosts).forEach(function (name) { - Object.keys(gitHostDefaults).forEach(function (key) { - if (gitHosts[name][key]) return - gitHosts[name][key] = gitHostDefaults[key] - }) - gitHosts[name].protocols_re = RegExp('^(' + - gitHosts[name].protocols.map(function (protocol) { - return protocol.replace(/([\\+*{}()[\]$^|])/g, '\\$1') - }).join('|') + '):$') + return { user, project, committish: url.hash.slice(1) } + }, + hashformat: function (fragment) { + return fragment && 'file-' + formatHashFragment(fragment) + } }) +const names = Object.keys(gitHosts) +gitHosts.byShortcut = {} +gitHosts.byDomain = {} +for (const name of names) { + gitHosts.byShortcut[`${name}:`] = name + gitHosts.byDomain[gitHosts[name].domain] = name +} + function formatHashFragment (fragment) { return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') } + +module.exports = gitHosts diff --git a/deps/npm/node_modules/hosted-git-info/git-host.js b/deps/npm/node_modules/hosted-git-info/git-host.js index f9b1ec74563208..8a975e92e58bb7 100644 --- a/deps/npm/node_modules/hosted-git-info/git-host.js +++ b/deps/npm/node_modules/hosted-git-info/git-host.js @@ -1,156 +1,110 @@ 'use strict' -var gitHosts = require('./git-host-info.js') -/* eslint-disable node/no-deprecated-api */ - -// copy-pasta util._extend from node's source, to avoid pulling -// the whole util module into peoples' webpack bundles. -/* istanbul ignore next */ -var extend = Object.assign || function _extend (target, source) { - // Don't do anything if source isn't an object - if (source === null || typeof source !== 'object') return target - - const keys = Object.keys(source) - let i = keys.length - while (i--) { - target[keys[i]] = source[keys[i]] - } - return target -} +const gitHosts = require('./git-host-info.js') + +class GitHost { + constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) { + Object.assign(this, gitHosts[type]) + this.type = type + this.user = user + this.auth = auth + this.project = project + this.committish = committish + this.default = defaultRepresentation + this.opts = opts + } -module.exports = GitHost -function GitHost (type, user, auth, project, committish, defaultRepresentation, opts) { - var gitHostInfo = this - gitHostInfo.type = type - Object.keys(gitHosts[type]).forEach(function (key) { - gitHostInfo[key] = gitHosts[type][key] - }) - gitHostInfo.user = user - gitHostInfo.auth = auth - gitHostInfo.project = project - gitHostInfo.committish = committish - gitHostInfo.default = defaultRepresentation - gitHostInfo.opts = opts || {} -} + hash () { + return this.committish ? `#${this.committish}` : '' + } -GitHost.prototype.hash = function () { - return this.committish ? '#' + this.committish : '' -} + ssh (opts) { + return this._fill(this.sshtemplate, opts) + } + + _fill (template, opts) { + if (typeof template === 'function') { + const options = { ...this, ...this.opts, ...opts } + + // the path should always be set so we don't end up with 'undefined' in urls + if (!options.path) { + options.path = '' + } + + // template functions will insert the leading slash themselves + if (options.path.startsWith('/')) { + options.path = options.path.slice(1) + } -GitHost.prototype._fill = function (template, opts) { - if (!template) return - var vars = extend({}, opts) - vars.path = vars.path ? vars.path.replace(/^[/]+/g, '') : '' - opts = extend(extend({}, this.opts), opts) - var self = this - Object.keys(this).forEach(function (key) { - if (self[key] != null && vars[key] == null) vars[key] = self[key] - }) - var rawAuth = vars.auth - var rawcommittish = vars.committish - var rawFragment = vars.fragment - var rawPath = vars.path - var rawProject = vars.project - Object.keys(vars).forEach(function (key) { - var value = vars[key] - if ((key === 'path' || key === 'project') && typeof value === 'string') { - vars[key] = value.split('/').map(function (pathComponent) { - return encodeURIComponent(pathComponent) - }).join('/') - } else if (key !== 'domain') { - vars[key] = encodeURIComponent(value) + if (options.noCommittish) { + options.committish = null + } + + const result = template(options) + return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result } - }) - vars['auth@'] = rawAuth ? rawAuth + '@' : '' - vars['#fragment'] = rawFragment ? '#' + this.hashformat(rawFragment) : '' - vars.fragment = vars.fragment ? vars.fragment : '' - vars['#path'] = rawPath ? '#' + this.hashformat(rawPath) : '' - vars['/path'] = vars.path ? '/' + vars.path : '' - vars.projectPath = rawProject.split('/').map(encodeURIComponent).join('/') - if (opts.noCommittish) { - vars['#committish'] = '' - vars['/tree/committish'] = '' - vars['/committish'] = '' - vars.committish = '' - } else { - vars['#committish'] = rawcommittish ? '#' + rawcommittish : '' - vars['/tree/committish'] = vars.committish - ? '/' + vars.treepath + '/' + vars.committish - : '' - vars['/committish'] = vars.committish ? '/' + vars.committish : '' - vars.committish = vars.committish || 'master' - } - var res = template - Object.keys(vars).forEach(function (key) { - res = res.replace(new RegExp('[{]' + key + '[}]', 'g'), vars[key]) - }) - if (opts.noGitPlus) { - return res.replace(/^git[+]/, '') - } else { - return res + + return null } -} -GitHost.prototype.ssh = function (opts) { - return this._fill(this.sshtemplate, opts) -} + sshurl (opts) { + return this._fill(this.sshurltemplate, opts) + } -GitHost.prototype.sshurl = function (opts) { - return this._fill(this.sshurltemplate, opts) -} + browse (path, fragment, opts) { + // not a string, treat path as opts + if (typeof path !== 'string') { + return this._fill(this.browsetemplate, path) + } -GitHost.prototype.browse = function (P, F, opts) { - if (typeof P === 'string') { - if (typeof F !== 'string') { - opts = F - F = null + if (typeof fragment !== 'string') { + opts = fragment + fragment = null } - return this._fill(this.browsefiletemplate, extend({ - fragment: F, - path: P - }, opts)) - } else { - return this._fill(this.browsetemplate, P) + return this._fill(this.browsefiletemplate, { ...opts, fragment, path }) } -} -GitHost.prototype.docs = function (opts) { - return this._fill(this.docstemplate, opts) -} + docs (opts) { + return this._fill(this.docstemplate, opts) + } -GitHost.prototype.bugs = function (opts) { - return this._fill(this.bugstemplate, opts) -} + bugs (opts) { + return this._fill(this.bugstemplate, opts) + } -GitHost.prototype.https = function (opts) { - return this._fill(this.httpstemplate, opts) -} + https (opts) { + return this._fill(this.httpstemplate, opts) + } -GitHost.prototype.git = function (opts) { - return this._fill(this.gittemplate, opts) -} + git (opts) { + return this._fill(this.gittemplate, opts) + } -GitHost.prototype.shortcut = function (opts) { - return this._fill(this.shortcuttemplate, opts) -} + shortcut (opts) { + return this._fill(this.shortcuttemplate, opts) + } -GitHost.prototype.path = function (opts) { - return this._fill(this.pathtemplate, opts) -} + path (opts) { + return this._fill(this.pathtemplate, opts) + } -GitHost.prototype.tarball = function (opts_) { - var opts = extend({}, opts_, { noCommittish: false }) - return this._fill(this.tarballtemplate, opts) -} + tarball (opts) { + return this._fill(this.tarballtemplate, { ...opts, noCommittish: false }) + } -GitHost.prototype.file = function (P, opts) { - return this._fill(this.filetemplate, extend({ path: P }, opts)) -} + file (path, opts) { + return this._fill(this.filetemplate, { ...opts, path }) + } -GitHost.prototype.getDefaultRepresentation = function () { - return this.default -} + getDefaultRepresentation () { + return this.default + } + + toString (opts) { + if (this.default && typeof this[this.default] === 'function') { + return this[this.default](opts) + } -GitHost.prototype.toString = function (opts) { - if (this.default && typeof this[this.default] === 'function') return this[this.default](opts) - return this.sshurl(opts) + return this.sshurl(opts) + } } +module.exports = GitHost diff --git a/deps/npm/node_modules/hosted-git-info/index.js b/deps/npm/node_modules/hosted-git-info/index.js index 8b3eaba3da7fb9..f35c570c46b595 100644 --- a/deps/npm/node_modules/hosted-git-info/index.js +++ b/deps/npm/node_modules/hosted-git-info/index.js @@ -1,11 +1,11 @@ 'use strict' -var url = require('url') -var gitHosts = require('./git-host-info.js') -var GitHost = module.exports = require('./git-host.js') -var LRU = require('lru-cache') -var cache = new LRU({max: 1000}) +const url = require('url') +const gitHosts = require('./git-host-info.js') +const GitHost = module.exports = require('./git-host.js') +const LRU = require('lru-cache') +const cache = new LRU({ max: 1000 }) -var protocolToRepresentationMap = { +const protocolToRepresentationMap = { 'git+ssh:': 'sshurl', 'git+https:': 'https', 'ssh:': 'sshurl', @@ -16,7 +16,7 @@ function protocolToRepresentation (protocol) { return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) } -var authProtocols = { +const authProtocols = { 'git:': true, 'https:': true, 'git+https:': true, @@ -24,9 +24,14 @@ var authProtocols = { 'git+http:': true } +const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:']) + module.exports.fromUrl = function (giturl, opts) { - if (typeof giturl !== 'string') return - var key = giturl + JSON.stringify(opts || {}) + if (typeof giturl !== 'string') { + return + } + + const key = giturl + JSON.stringify(opts || {}) if (!cache.has(key)) { cache.set(key, fromUrl(giturl, opts)) @@ -36,111 +41,197 @@ module.exports.fromUrl = function (giturl, opts) { } function fromUrl (giturl, opts) { - if (giturl == null || giturl === '') return - var url = fixupUnqualifiedGist( - isGitHubShorthand(giturl) ? 'github:' + giturl : giturl - ) - var parsed = parseGitUrl(url) - var shortcutMatch = url.match(/^([^:]+):(?:[^@]+@)?(?:([^/]*)\/)?([^#]+)/) - var matches = Object.keys(gitHosts).map(function (gitHostName) { - try { - var gitHostInfo = gitHosts[gitHostName] - var auth = null - if (parsed.auth && authProtocols[parsed.protocol]) { - auth = parsed.auth + if (!giturl) { + return + } + + const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl) + const parsed = parseGitUrl(url) + if (!parsed) { + return parsed + } + + const gitHostShortcut = gitHosts.byShortcut[parsed.protocol] + const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.') ? parsed.hostname.slice(4) : parsed.hostname] + const gitHostName = gitHostShortcut || gitHostDomain + if (!gitHostName) { + return + } + + const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain] + let auth = null + if (authProtocols[parsed.protocol] && (parsed.username || parsed.password)) { + auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}` + } + + let committish = null + let user = null + let project = null + let defaultRepresentation = null + + try { + if (gitHostShortcut) { + let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname + const firstAt = pathname.indexOf('@') + // we ignore auth for shortcuts, so just trim it out + if (firstAt > -1) { + pathname = pathname.slice(firstAt + 1) } - var committish = parsed.hash ? decodeURIComponent(parsed.hash.substr(1)) : null - var user = null - var project = null - var defaultRepresentation = null - if (shortcutMatch && shortcutMatch[1] === gitHostName) { - user = shortcutMatch[2] && decodeURIComponent(shortcutMatch[2]) - project = decodeURIComponent(shortcutMatch[3].replace(/\.git$/, '')) - defaultRepresentation = 'shortcut' - } else { - if (parsed.host && parsed.host !== gitHostInfo.domain && parsed.host.replace(/^www[.]/, '') !== gitHostInfo.domain) return - if (!gitHostInfo.protocols_re.test(parsed.protocol)) return - if (!parsed.path) return - var pathmatch = gitHostInfo.pathmatch - var matched = parsed.path.match(pathmatch) - if (!matched) return - /* istanbul ignore else */ - if (matched[1] !== null && matched[1] !== undefined) { - user = decodeURIComponent(matched[1].replace(/^:/, '')) + + const lastSlash = pathname.lastIndexOf('/') + if (lastSlash > -1) { + user = decodeURIComponent(pathname.slice(0, lastSlash)) + // we want nulls only, never empty strings + if (!user) { + user = null } - project = decodeURIComponent(matched[2]) - defaultRepresentation = protocolToRepresentation(parsed.protocol) + project = decodeURIComponent(pathname.slice(lastSlash + 1)) + } else { + project = decodeURIComponent(pathname) + } + + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (parsed.hash) { + committish = decodeURIComponent(parsed.hash.slice(1)) + } + + defaultRepresentation = 'shortcut' + } else { + if (!gitHostInfo.protocols.includes(parsed.protocol)) { + return } - return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) - } catch (ex) { - /* istanbul ignore else */ - if (ex instanceof URIError) { - } else throw ex + + const segments = gitHostInfo.extract(parsed) + if (!segments) { + return + } + + user = segments.user && decodeURIComponent(segments.user) + project = decodeURIComponent(segments.project) + committish = decodeURIComponent(segments.committish) + defaultRepresentation = protocolToRepresentation(parsed.protocol) } - }).filter(function (gitHostInfo) { return gitHostInfo }) - if (matches.length !== 1) return - return matches[0] -} + } catch (err) { + /* istanbul ignore else */ + if (err instanceof URIError) { + return + } else { + throw err + } + } -function isGitHubShorthand (arg) { - // Note: This does not fully test the git ref format. - // See https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html - // - // The only way to do this properly would be to shell out to - // git-check-ref-format, and as this is a fast sync function, - // we don't want to do that. Just let git fail if it turns - // out that the commit-ish is invalid. - // GH usernames cannot start with . or - - return /^[^:@%/\s.-][^:@%/\s]*[/][^:@\s/%]+(?:#.*)?$/.test(arg) + return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) } -function fixupUnqualifiedGist (giturl) { - // necessary for round-tripping gists - var parsed = url.parse(giturl) - if (parsed.protocol === 'gist:' && parsed.host && !parsed.path) { - return parsed.protocol + '/' + parsed.host - } else { - return giturl +// accepts input like git:github.com:user/repo and inserts the // after the first : +const correctProtocol = (arg) => { + const firstColon = arg.indexOf(':') + const proto = arg.slice(0, firstColon + 1) + if (knownProtocols.includes(proto)) { + return arg } -} -function parseGitUrl (giturl) { - var matched = giturl.match(/^([^@]+)@([^:/]+):[/]?((?:[^/]+[/])?[^/]+?)(?:[.]git)?(#.*)?$/) - if (!matched) { - var legacy = url.parse(giturl) - if (legacy.auth) { - // git urls can be in the form of scp-style/ssh-connect strings, like - // git+ssh://user@host.com:some/path, which the legacy url parser - // supports, but WhatWG url.URL class does not. However, the legacy - // parser de-urlencodes the username and password, so something like - // https://user%3An%40me:p%40ss%3Aword@x.com/ becomes - // https://user:n@me:p@ss:word@x.com/ which is all kinds of wrong. - // Pull off just the auth and host, so we dont' get the confusing - // scp-style URL, then pass that to the WhatWG parser to get the - // auth properly escaped. - const authmatch = giturl.match(/[^@]+@[^:/]+/) - /* istanbul ignore else - this should be impossible */ - if (authmatch) { - var whatwg = new url.URL(authmatch[0]) - legacy.auth = whatwg.username || '' - if (whatwg.password) legacy.auth += ':' + whatwg.password - } + const firstAt = arg.indexOf('@') + if (firstAt > -1) { + if (firstAt > firstColon) { + return `git+ssh://${arg}` + } else { + return arg } - return legacy } - return { - protocol: 'git+ssh:', - slashes: true, - auth: matched[1], - host: matched[2], - port: null, - hostname: matched[2], - hash: matched[4], - search: null, - query: null, - pathname: '/' + matched[3], - path: '/' + matched[3], - href: 'git+ssh://' + matched[1] + '@' + matched[2] + - '/' + matched[3] + (matched[4] || '') + + const doubleSlash = arg.indexOf('//') + if (doubleSlash === firstColon + 1) { + return arg + } + + return arg.slice(0, firstColon + 1) + '//' + arg.slice(firstColon + 1) +} + +// look for github shorthand inputs, such as npm/cli +const isGitHubShorthand = (arg) => { + // it cannot contain whitespace before the first # + // it cannot start with a / because that's probably an absolute file path + // but it must include a slash since repos are username/repository + // it cannot start with a . because that's probably a relative file path + // it cannot start with an @ because that's a scoped package if it passes the other tests + // it cannot contain a : before a # because that tells us that there's a protocol + // a second / may not exist before a # + const firstHash = arg.indexOf('#') + const firstSlash = arg.indexOf('/') + const secondSlash = arg.indexOf('/', firstSlash + 1) + const firstColon = arg.indexOf(':') + const firstSpace = /\s/.exec(arg) + const firstAt = arg.indexOf('@') + + const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash) + const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash) + const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash) + const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash) + const hasSlash = firstSlash > 0 + // if a # is found, what we really want to know is that the character immediately before # is not a / + const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/') + const doesNotStartWithDot = !arg.startsWith('.') + + return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash && doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash && secondSlashOnlyAfterHash +} + +// attempt to correct an scp style url so that it will parse with `new URL()` +const correctUrl = (giturl) => { + const firstAt = giturl.indexOf('@') + const lastHash = giturl.lastIndexOf('#') + let firstColon = giturl.indexOf(':') + let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity) + + let corrected + if (lastColon > firstAt) { + // the last : comes after the first @ (or there is no @) + // like it would in: + // proto://hostname.com:user/repo + // username@hostname.com:user/repo + // :password@hostname.com:user/repo + // username:password@hostname.com:user/repo + // proto://username@hostname.com:user/repo + // proto://:password@hostname.com:user/repo + // proto://username:password@hostname.com:user/repo + // then we replace the last : with a / to create a valid path + corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1) + // // and we find our new : positions + firstColon = corrected.indexOf(':') + lastColon = corrected.lastIndexOf(':') + } + + if (firstColon === -1 && giturl.indexOf('//') === -1) { + // we have no : at all + // as it would be in: + // username@hostname.com/user/repo + // then we prepend a protocol + corrected = `git+ssh://${corrected}` + } + + return corrected +} + +// try to parse the url as its given to us, if that throws +// then we try to clean the url and parse that result instead +// THIS FUNCTION SHOULD NEVER THROW +const parseGitUrl = (giturl) => { + let result + try { + result = new url.URL(giturl) + } catch (err) {} + + if (result) { + return result } + + const correctedUrl = correctUrl(giturl) + try { + result = new url.URL(correctedUrl) + } catch (err) {} + + return result } diff --git a/deps/npm/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/hosted-git-info/package.json index 32712269f04278..930e3b693b9801 100644 --- a/deps/npm/node_modules/hosted-git-info/package.json +++ b/deps/npm/node_modules/hosted-git-info/package.json @@ -1,7 +1,7 @@ { "name": "hosted-git-info", - "version": "3.0.8", - "description": "Provides metadata and conversions from repository urls for Github, Bitbucket and Gitlab", + "version": "4.0.1", + "description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab", "main": "index.js", "repository": { "type": "git", @@ -20,20 +20,21 @@ }, "homepage": "https://github.com/npm/hosted-git-info", "scripts": { - "prerelease": "npm t", - "postrelease": "npm publish && git push --follow-tags", "posttest": "standard", - "release": "standard-version -s", - "test:coverage": "tap --coverage-report=html -J --100 --no-esm test/*.js", - "test": "tap -J --100 --no-esm test/*.js" + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preversion": "npm test", + "snap": "tap", + "test": "tap", + "test:coverage": "tap --coverage-report=html" }, "dependencies": { "lru-cache": "^6.0.0" }, "devDependencies": { - "standard": "^11.0.1", - "standard-version": "^4.4.0", - "tap": "^12.7.0" + "standard": "^16.0.3", + "standard-version": "^9.1.0", + "tap": "^14.11.0" }, "files": [ "index.js", @@ -42,5 +43,10 @@ ], "engines": { "node": ">=10" + }, + "tap": { + "color": 1, + "coverage": true, + "esm": false } } diff --git a/deps/npm/node_modules/libnpmversion/lib/index.js b/deps/npm/node_modules/libnpmversion/lib/index.js index 525d8264e737a8..c3f554834bf81d 100644 --- a/deps/npm/node_modules/libnpmversion/lib/index.js +++ b/deps/npm/node_modules/libnpmversion/lib/index.js @@ -1,4 +1,4 @@ -const readJson = require('read-package-json-fast') +const readJson = require('./read-json.js') const version = require('./version.js') const proclog = require('./proc-log.js') diff --git a/deps/npm/node_modules/libnpmversion/lib/read-json.js b/deps/npm/node_modules/libnpmversion/lib/read-json.js new file mode 100644 index 00000000000000..0a1f64f2f70e7e --- /dev/null +++ b/deps/npm/node_modules/libnpmversion/lib/read-json.js @@ -0,0 +1,7 @@ +// can't use read-package-json-fast, because we want to ensure +// that we make as few changes as possible, even for safety issues. +const {promisify} = require('util') +const readFile = promisify(require('fs').readFile) +const parse = require('json-parse-even-better-errors') + +module.exports = async path => parse(await readFile(path)) diff --git a/deps/npm/node_modules/libnpmversion/lib/version.js b/deps/npm/node_modules/libnpmversion/lib/version.js index 31e6023f017a5a..0fe1ea6213fc6f 100644 --- a/deps/npm/node_modules/libnpmversion/lib/version.js +++ b/deps/npm/node_modules/libnpmversion/lib/version.js @@ -4,7 +4,7 @@ const retrieveTag = require('./retrieve-tag.js') const semver = require('semver') const enforceClean = require('./enforce-clean.js') const writeJson = require('./write-json.js') -const readJson = require('read-package-json-fast') +const readJson = require('./read-json.js') const git = require('@npmcli/git') const commit = require('./commit.js') const tag = require('./tag.js') diff --git a/deps/npm/node_modules/libnpmversion/package.json b/deps/npm/node_modules/libnpmversion/package.json index 3d15bbc2f7f72c..0135c21e7232cb 100644 --- a/deps/npm/node_modules/libnpmversion/package.json +++ b/deps/npm/node_modules/libnpmversion/package.json @@ -1,6 +1,6 @@ { "name": "libnpmversion", - "version": "1.0.11", + "version": "1.0.12", "main": "lib/index.js", "files": [ "lib/*.js" @@ -30,7 +30,7 @@ "dependencies": { "@npmcli/git": "^2.0.6", "@npmcli/run-script": "^1.8.3", - "read-package-json-fast": "^2.0.1", + "json-parse-even-better-errors": "^2.3.1", "semver": "^7.3.4", "stringify-package": "^1.0.1" } diff --git a/deps/npm/node_modules/normalize-package-data/README.md b/deps/npm/node_modules/normalize-package-data/README.md index d2bd7bc7ff6062..84da5e8bcf2ddd 100644 --- a/deps/npm/node_modules/normalize-package-data/README.md +++ b/deps/npm/node_modules/normalize-package-data/README.md @@ -1,4 +1,6 @@ -# normalize-package-data [](https://travis-ci.org/npm/normalize-package-data) +# normalize-package-data + +[](https://travis-ci.org/npm/normalize-package-data) normalize-package-data exports a function that normalizes package metadata. This data is typically found in a package.json file, but in principle could come from any source - for example the npm registry. diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md new file mode 100644 index 00000000000000..3ffcacacc575c0 --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md @@ -0,0 +1,185 @@ +# Change Log + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +<a name="3.0.8"></a> +## [3.0.8](https://github.com/npm/hosted-git-info/compare/v3.0.7...v3.0.8) (2021-01-28) + + +### Bug Fixes + +* simplify the regular expression for shortcut matching ([bede0dc](https://github.com/npm/hosted-git-info/commit/bede0dc)), closes [#76](https://github.com/npm/hosted-git-info/issues/76) + + + +<a name="3.0.7"></a> +## [3.0.7](https://github.com/npm/hosted-git-info/compare/v3.0.6...v3.0.7) (2020-10-15) + + +### Bug Fixes + +* correctly filter out urls for tarballs in gitlab ([eb5bd5a](https://github.com/npm/hosted-git-info/commit/eb5bd5a)), closes [#69](https://github.com/npm/hosted-git-info/issues/69) + + + +<a name="3.0.6"></a> +## [3.0.6](https://github.com/npm/hosted-git-info/compare/v3.0.5...v3.0.6) (2020-10-12) + + +### Bug Fixes + +* support to github gist legacy hash length ([c067102](https://github.com/npm/hosted-git-info/commit/c067102)), closes [#68](https://github.com/npm/hosted-git-info/issues/68) + + + +<a name="3.0.5"></a> +## [3.0.5](https://github.com/npm/hosted-git-info/compare/v3.0.4...v3.0.5) (2020-07-11) + + + +<a name="3.0.4"></a> +## [3.0.4](https://github.com/npm/hosted-git-info/compare/v3.0.3...v3.0.4) (2020-02-26) + + +### Bug Fixes + +* Do not pass scp-style URLs to the WhatWG url.URL ([0835306](https://github.com/npm/hosted-git-info/commit/0835306)), closes [#60](https://github.com/npm/hosted-git-info/issues/60) [#63](https://github.com/npm/hosted-git-info/issues/63) + + + +<a name="3.0.3"></a> +## [3.0.3](https://github.com/npm/hosted-git-info/compare/v3.0.2...v3.0.3) (2020-02-25) + + + +<a name="3.0.2"></a> +## [3.0.2](https://github.com/npm/hosted-git-info/compare/v3.0.1...v3.0.2) (2019-10-08) + + +### Bug Fixes + +* do not encodeURIComponent the domain ([3e5fbec](https://github.com/npm/hosted-git-info/commit/3e5fbec)), closes [#53](https://github.com/npm/hosted-git-info/issues/53) + + + +<a name="3.0.1"></a> +## [3.0.1](https://github.com/npm/hosted-git-info/compare/v3.0.0...v3.0.1) (2019-10-07) + + +### Bug Fixes + +* update pathmatch for gitlab ([e3e3054](https://github.com/npm/hosted-git-info/commit/e3e3054)), closes [#52](https://github.com/npm/hosted-git-info/issues/52) +* updated pathmatch for gitlab ([fa87af7](https://github.com/npm/hosted-git-info/commit/fa87af7)) + + + +<a name="3.0.0"></a> +# [3.0.0](https://github.com/npm/hosted-git-info/compare/v2.8.3...v3.0.0) (2019-08-12) + + +### Bug Fixes + +* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([37c2891](https://github.com/npm/hosted-git-info/commit/37c2891)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) + + +### BREAKING CHANGES + +* **cache:** Drop support for node 0.x + + + +<a name="2.8.3"></a> +## [2.8.3](https://github.com/npm/hosted-git-info/compare/v2.8.2...v2.8.3) (2019-08-12) + + + +<a name="2.8.2"></a> +## [2.8.2](https://github.com/npm/hosted-git-info/compare/v2.8.1...v2.8.2) (2019-08-05) + + +### Bug Fixes + +* http protocol use sshurl by default ([3b1d629](https://github.com/npm/hosted-git-info/commit/3b1d629)), closes [#48](https://github.com/npm/hosted-git-info/issues/48) + + + +<a name="2.8.1"></a> +## [2.8.1](https://github.com/npm/hosted-git-info/compare/v2.8.0...v2.8.1) (2019-08-05) + + +### Bug Fixes + +* ignore noCommittish on tarball url generation ([5d4a8d7](https://github.com/npm/hosted-git-info/commit/5d4a8d7)) +* use gist tarball url that works for anonymous gists ([1692435](https://github.com/npm/hosted-git-info/commit/1692435)) + + + +<a name="2.8.0"></a> +# [2.8.0](https://github.com/npm/hosted-git-info/compare/v2.7.1...v2.8.0) (2019-08-05) + + +### Bug Fixes + +* Allow slashes in gitlab project section ([bbcf7b2](https://github.com/npm/hosted-git-info/commit/bbcf7b2)), closes [#46](https://github.com/npm/hosted-git-info/issues/46) [#43](https://github.com/npm/hosted-git-info/issues/43) +* **git-host:** disallow URI-encoded slash (%2F) in `path` ([3776fa5](https://github.com/npm/hosted-git-info/commit/3776fa5)), closes [#44](https://github.com/npm/hosted-git-info/issues/44) +* **gitlab:** Do not URL encode slashes in project name for GitLab https URL ([cbf04f9](https://github.com/npm/hosted-git-info/commit/cbf04f9)), closes [#47](https://github.com/npm/hosted-git-info/issues/47) +* do not allow invalid gist urls ([d5cf830](https://github.com/npm/hosted-git-info/commit/d5cf830)) +* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([e518222](https://github.com/npm/hosted-git-info/commit/e518222)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) + + +### Features + +* give these objects a name ([60abaea](https://github.com/npm/hosted-git-info/commit/60abaea)) + + + +<a name="2.7.1"></a> +## [2.7.1](https://github.com/npm/hosted-git-info/compare/v2.7.0...v2.7.1) (2018-07-07) + + +### Bug Fixes + +* **index:** Guard against non-string types ([5bc580d](https://github.com/npm/hosted-git-info/commit/5bc580d)) +* **parse:** Crash on strings that parse to having no host ([c931482](https://github.com/npm/hosted-git-info/commit/c931482)), closes [#35](https://github.com/npm/hosted-git-info/issues/35) + + + +<a name="2.7.0"></a> +# [2.7.0](https://github.com/npm/hosted-git-info/compare/v2.6.1...v2.7.0) (2018-07-06) + + +### Bug Fixes + +* **github tarball:** update github tarballtemplate ([6efd582](https://github.com/npm/hosted-git-info/commit/6efd582)), closes [#34](https://github.com/npm/hosted-git-info/issues/34) +* **gitlab docs:** switched to lowercase anchors for readmes ([701bcd1](https://github.com/npm/hosted-git-info/commit/701bcd1)) + + +### Features + +* **all:** Support www. prefixes on hostnames ([3349575](https://github.com/npm/hosted-git-info/commit/3349575)), closes [#32](https://github.com/npm/hosted-git-info/issues/32) + + + +<a name="2.6.1"></a> +## [2.6.1](https://github.com/npm/hosted-git-info/compare/v2.6.0...v2.6.1) (2018-06-25) + +### Bug Fixes + +* **Revert:** "compat: remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25))" ([cce5a62](https://github.com/npm/hosted-git-info/commit/cce5a62)) +* **Revert:** "git-host: fix forgotten extend()" ([a815ec9](https://github.com/npm/hosted-git-info/commit/a815ec9)) + + + +<a name="2.6.0"></a> +# [2.6.0](https://github.com/npm/hosted-git-info/compare/v2.5.0...v2.6.0) (2018-03-07) + + +### Bug Fixes + +* **compat:** remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25)) ([627ab55](https://github.com/npm/hosted-git-info/commit/627ab55)) +* **git-host:** fix forgotten extend() ([eba1f7b](https://github.com/npm/hosted-git-info/commit/eba1f7b)) + + +### Features + +* **browse:** fragment support for browse() ([#28](https://github.com/npm/hosted-git-info/issues/28)) ([cd5e5bb](https://github.com/npm/hosted-git-info/commit/cd5e5bb)) diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE new file mode 100644 index 00000000000000..45055763dc838d --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2015, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md new file mode 100644 index 00000000000000..87404060296269 --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md @@ -0,0 +1,133 @@ +# hosted-git-info + +This will let you identify and transform various git hosts URLs between +protocols. It also can tell you what the URL is for the raw path for +particular file for direct access without git. + +## Example + +```javascript +var hostedGitInfo = require("hosted-git-info") +var info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts) +/* info looks like: +{ + type: "github", + domain: "github.com", + user: "npm", + project: "hosted-git-info" +} +*/ +``` + +If the URL can't be matched with a git host, `null` will be returned. We +can match git, ssh and https urls. Additionally, we can match ssh connect +strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg, +`github:npm/hosted-git-info`). GitHub specifically, is detected in the case +of a third, unprefixed, form: `npm/hosted-git-info`. + +If it does match, the returned object has properties of: + +* info.type -- The short name of the service +* info.domain -- The domain for git protocol use +* info.user -- The name of the user/org on the git host +* info.project -- The name of the project on the git host + +## Version Contract + +The major version will be bumped any time… + +* The constructor stops accepting URLs that it previously accepted. +* A method is removed. +* A method can no longer accept the number and type of arguments it previously accepted. +* A method can return a different type than it currently returns. + +Implications: + +* I do not consider the specific format of the urls returned from, say + `.https()` to be a part of the contract. The contract is that it will + return a string that can be used to fetch the repo via HTTPS. But what + that string looks like, specifically, can change. +* Dropping support for a hosted git provider would constitute a breaking + change. + +## Usage + +### var info = hostedGitInfo.fromUrl(gitSpecifier[, options]) + +* *gitSpecifer* is a URL of a git repository or a SCP-style specifier of one. +* *options* is an optional object. It can have the following properties: + * *noCommittish* — If true then committishes won't be included in generated URLs. + * *noGitPlus* — If true then `git+` won't be prefixed on URLs. + +## Methods + +All of the methods take the same options as the `fromUrl` factory. Options +provided to a method override those provided to the constructor. + +* info.file(path, opts) + +Given the path of a file relative to the repository, returns a URL for +directly fetching it from the githost. If no committish was set then +`master` will be used as the default. + +For example `hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git#v1.0.0").file("package.json")` +would return `https://raw.githubusercontent.com/npm/hosted-git-info/v1.0.0/package.json` + +* info.shortcut(opts) + +eg, `github:npm/hosted-git-info` + +* info.browse(path, fragment, opts) + +eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0`, +`https://github.com/npm/hosted-git-info/tree/v1.2.0/package.json`, +`https://github.com/npm/hosted-git-info/tree/v1.2.0/REAMDE.md#supported-hosts` + +* info.bugs(opts) + +eg, `https://github.com/npm/hosted-git-info/issues` + +* info.docs(opts) + +eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0#readme` + +* info.https(opts) + +eg, `git+https://github.com/npm/hosted-git-info.git` + +* info.sshurl(opts) + +eg, `git+ssh://git@github.com/npm/hosted-git-info.git` + +* info.ssh(opts) + +eg, `git@github.com:npm/hosted-git-info.git` + +* info.path(opts) + +eg, `npm/hosted-git-info` + +* info.tarball(opts) + +eg, `https://github.com/npm/hosted-git-info/archive/v1.2.0.tar.gz` + +* info.getDefaultRepresentation() + +Returns the default output type. The default output type is based on the +string you passed in to be parsed + +* info.toString(opts) + +Uses the getDefaultRepresentation to call one of the other methods to get a URL for +this resource. As such `hostedGitInfo.fromUrl(url).toString()` will give +you a normalized version of the URL that still uses the same protocol. + +Shortcuts will still be returned as shortcuts, but the special case github +form of `org/project` will be normalized to `github:org/project`. + +SSH connect strings will be normalized into `git+ssh` URLs. + +## Supported hosts + +Currently this supports GitHub, Bitbucket and GitLab. Pull requests for +additional hosts welcome. diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js new file mode 100644 index 00000000000000..360d7b096be617 --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js @@ -0,0 +1,154 @@ +'use strict' +const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : '' +const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : '' + +const defaults = { + sshtemplate: ({ domain, user, project, committish }) => `git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`, + sshurltemplate: ({ domain, user, project, committish }) => `git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`, + browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`, + docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`, + httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/raw/${maybeEncode(committish) || 'master'}/${path}`, + shortcuttemplate: ({ type, user, project, committish }) => `${type}:${user}/${project}${maybeJoin('#', committish)}`, + pathtemplate: ({ user, project, committish }) => `${user}/${project}${maybeJoin('#', committish)}`, + bugstemplate: ({ domain, user, project }) => `https://${domain}/${user}/${project}/issues`, + hashformat: formatHashFragment +} + +const gitHosts = {} +gitHosts.github = Object.assign({}, defaults, { + // First two are insecure and generally shouldn't be used any more, but + // they are still supported. + protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'github.com', + treepath: 'tree', + filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${path}`, + gittemplate: ({ auth, domain, user, project, committish }) => `git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ domain, user, project, committish }) => `https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, + extract: (url) => { + let [, user, project, type, committish] = url.pathname.split('/', 5) + if (type && type !== 'tree') { + return + } + + if (!type) { + committish = url.hash.slice(1) + } + + if (project && project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (!user || !project) { + return + } + + return { user, project, committish } + } +}) + +gitHosts.bitbucket = Object.assign({}, defaults, { + protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'bitbucket.org', + treepath: 'src', + tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.tar.gz`, + extract: (url) => { + let [, user, project, aux] = url.pathname.split('/', 4) + if (['get'].includes(aux)) { + return + } + + if (project && project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (!user || !project) { + return + } + + return { user, project, committish: url.hash.slice(1) } + } +}) + +gitHosts.gitlab = Object.assign({}, defaults, { + protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'gitlab.com', + treepath: 'tree', + httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`, + extract: (url) => { + const path = url.pathname.slice(1) + if (path.includes('/-/')) { + return + } + + const segments = path.split('/') + let project = segments.pop() + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + const user = segments.join('/') + if (!user || !project) { + return + } + + return { user, project, committish: url.hash.slice(1) } + } +}) + +gitHosts.gist = Object.assign({}, defaults, { + protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'gist.github.com', + sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`, + sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`, + browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, + browsefiletemplate: ({ domain, project, committish, path, hashformat }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`, + docstemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, + httpstemplate: ({ domain, project, committish }) => `git+https://${domain}/${project}.git${maybeJoin('#', committish)}`, + filetemplate: ({ user, project, committish, path }) => `https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`, + shortcuttemplate: ({ type, project, committish }) => `${type}:${project}${maybeJoin('#', committish)}`, + pathtemplate: ({ project, committish }) => `${project}${maybeJoin('#', committish)}`, + bugstemplate: ({ domain, project }) => `https://${domain}/${project}`, + gittemplate: ({ domain, project, committish }) => `git://${domain}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ project, committish }) => `https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, + extract: (url) => { + let [, user, project, aux] = url.pathname.split('/', 4) + if (aux === 'raw') { + return + } + + if (!project) { + if (!user) { + return + } + + project = user + user = null + } + + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + return { user, project, committish: url.hash.slice(1) } + }, + hashformat: function (fragment) { + return fragment && 'file-' + formatHashFragment(fragment) + } +}) + +const names = Object.keys(gitHosts) +gitHosts.byShortcut = {} +gitHosts.byDomain = {} +for (const name of names) { + gitHosts.byShortcut[`${name}:`] = name + gitHosts.byDomain[gitHosts[name].domain] = name +} + +function formatHashFragment (fragment) { + return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') +} + +module.exports = gitHosts diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js new file mode 100644 index 00000000000000..8a975e92e58bb7 --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js @@ -0,0 +1,110 @@ +'use strict' +const gitHosts = require('./git-host-info.js') + +class GitHost { + constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) { + Object.assign(this, gitHosts[type]) + this.type = type + this.user = user + this.auth = auth + this.project = project + this.committish = committish + this.default = defaultRepresentation + this.opts = opts + } + + hash () { + return this.committish ? `#${this.committish}` : '' + } + + ssh (opts) { + return this._fill(this.sshtemplate, opts) + } + + _fill (template, opts) { + if (typeof template === 'function') { + const options = { ...this, ...this.opts, ...opts } + + // the path should always be set so we don't end up with 'undefined' in urls + if (!options.path) { + options.path = '' + } + + // template functions will insert the leading slash themselves + if (options.path.startsWith('/')) { + options.path = options.path.slice(1) + } + + if (options.noCommittish) { + options.committish = null + } + + const result = template(options) + return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result + } + + return null + } + + sshurl (opts) { + return this._fill(this.sshurltemplate, opts) + } + + browse (path, fragment, opts) { + // not a string, treat path as opts + if (typeof path !== 'string') { + return this._fill(this.browsetemplate, path) + } + + if (typeof fragment !== 'string') { + opts = fragment + fragment = null + } + return this._fill(this.browsefiletemplate, { ...opts, fragment, path }) + } + + docs (opts) { + return this._fill(this.docstemplate, opts) + } + + bugs (opts) { + return this._fill(this.bugstemplate, opts) + } + + https (opts) { + return this._fill(this.httpstemplate, opts) + } + + git (opts) { + return this._fill(this.gittemplate, opts) + } + + shortcut (opts) { + return this._fill(this.shortcuttemplate, opts) + } + + path (opts) { + return this._fill(this.pathtemplate, opts) + } + + tarball (opts) { + return this._fill(this.tarballtemplate, { ...opts, noCommittish: false }) + } + + file (path, opts) { + return this._fill(this.filetemplate, { ...opts, path }) + } + + getDefaultRepresentation () { + return this.default + } + + toString (opts) { + if (this.default && typeof this[this.default] === 'function') { + return this[this.default](opts) + } + + return this.sshurl(opts) + } +} +module.exports = GitHost diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js new file mode 100644 index 00000000000000..f35c570c46b595 --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js @@ -0,0 +1,237 @@ +'use strict' +const url = require('url') +const gitHosts = require('./git-host-info.js') +const GitHost = module.exports = require('./git-host.js') +const LRU = require('lru-cache') +const cache = new LRU({ max: 1000 }) + +const protocolToRepresentationMap = { + 'git+ssh:': 'sshurl', + 'git+https:': 'https', + 'ssh:': 'sshurl', + 'git:': 'git' +} + +function protocolToRepresentation (protocol) { + return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) +} + +const authProtocols = { + 'git:': true, + 'https:': true, + 'git+https:': true, + 'http:': true, + 'git+http:': true +} + +const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:']) + +module.exports.fromUrl = function (giturl, opts) { + if (typeof giturl !== 'string') { + return + } + + const key = giturl + JSON.stringify(opts || {}) + + if (!cache.has(key)) { + cache.set(key, fromUrl(giturl, opts)) + } + + return cache.get(key) +} + +function fromUrl (giturl, opts) { + if (!giturl) { + return + } + + const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl) + const parsed = parseGitUrl(url) + if (!parsed) { + return parsed + } + + const gitHostShortcut = gitHosts.byShortcut[parsed.protocol] + const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.') ? parsed.hostname.slice(4) : parsed.hostname] + const gitHostName = gitHostShortcut || gitHostDomain + if (!gitHostName) { + return + } + + const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain] + let auth = null + if (authProtocols[parsed.protocol] && (parsed.username || parsed.password)) { + auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}` + } + + let committish = null + let user = null + let project = null + let defaultRepresentation = null + + try { + if (gitHostShortcut) { + let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname + const firstAt = pathname.indexOf('@') + // we ignore auth for shortcuts, so just trim it out + if (firstAt > -1) { + pathname = pathname.slice(firstAt + 1) + } + + const lastSlash = pathname.lastIndexOf('/') + if (lastSlash > -1) { + user = decodeURIComponent(pathname.slice(0, lastSlash)) + // we want nulls only, never empty strings + if (!user) { + user = null + } + project = decodeURIComponent(pathname.slice(lastSlash + 1)) + } else { + project = decodeURIComponent(pathname) + } + + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (parsed.hash) { + committish = decodeURIComponent(parsed.hash.slice(1)) + } + + defaultRepresentation = 'shortcut' + } else { + if (!gitHostInfo.protocols.includes(parsed.protocol)) { + return + } + + const segments = gitHostInfo.extract(parsed) + if (!segments) { + return + } + + user = segments.user && decodeURIComponent(segments.user) + project = decodeURIComponent(segments.project) + committish = decodeURIComponent(segments.committish) + defaultRepresentation = protocolToRepresentation(parsed.protocol) + } + } catch (err) { + /* istanbul ignore else */ + if (err instanceof URIError) { + return + } else { + throw err + } + } + + return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) +} + +// accepts input like git:github.com:user/repo and inserts the // after the first : +const correctProtocol = (arg) => { + const firstColon = arg.indexOf(':') + const proto = arg.slice(0, firstColon + 1) + if (knownProtocols.includes(proto)) { + return arg + } + + const firstAt = arg.indexOf('@') + if (firstAt > -1) { + if (firstAt > firstColon) { + return `git+ssh://${arg}` + } else { + return arg + } + } + + const doubleSlash = arg.indexOf('//') + if (doubleSlash === firstColon + 1) { + return arg + } + + return arg.slice(0, firstColon + 1) + '//' + arg.slice(firstColon + 1) +} + +// look for github shorthand inputs, such as npm/cli +const isGitHubShorthand = (arg) => { + // it cannot contain whitespace before the first # + // it cannot start with a / because that's probably an absolute file path + // but it must include a slash since repos are username/repository + // it cannot start with a . because that's probably a relative file path + // it cannot start with an @ because that's a scoped package if it passes the other tests + // it cannot contain a : before a # because that tells us that there's a protocol + // a second / may not exist before a # + const firstHash = arg.indexOf('#') + const firstSlash = arg.indexOf('/') + const secondSlash = arg.indexOf('/', firstSlash + 1) + const firstColon = arg.indexOf(':') + const firstSpace = /\s/.exec(arg) + const firstAt = arg.indexOf('@') + + const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash) + const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash) + const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash) + const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash) + const hasSlash = firstSlash > 0 + // if a # is found, what we really want to know is that the character immediately before # is not a / + const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/') + const doesNotStartWithDot = !arg.startsWith('.') + + return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash && doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash && secondSlashOnlyAfterHash +} + +// attempt to correct an scp style url so that it will parse with `new URL()` +const correctUrl = (giturl) => { + const firstAt = giturl.indexOf('@') + const lastHash = giturl.lastIndexOf('#') + let firstColon = giturl.indexOf(':') + let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity) + + let corrected + if (lastColon > firstAt) { + // the last : comes after the first @ (or there is no @) + // like it would in: + // proto://hostname.com:user/repo + // username@hostname.com:user/repo + // :password@hostname.com:user/repo + // username:password@hostname.com:user/repo + // proto://username@hostname.com:user/repo + // proto://:password@hostname.com:user/repo + // proto://username:password@hostname.com:user/repo + // then we replace the last : with a / to create a valid path + corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1) + // // and we find our new : positions + firstColon = corrected.indexOf(':') + lastColon = corrected.lastIndexOf(':') + } + + if (firstColon === -1 && giturl.indexOf('//') === -1) { + // we have no : at all + // as it would be in: + // username@hostname.com/user/repo + // then we prepend a protocol + corrected = `git+ssh://${corrected}` + } + + return corrected +} + +// try to parse the url as its given to us, if that throws +// then we try to clean the url and parse that result instead +// THIS FUNCTION SHOULD NEVER THROW +const parseGitUrl = (giturl) => { + let result + try { + result = new url.URL(giturl) + } catch (err) {} + + if (result) { + return result + } + + const correctedUrl = correctUrl(giturl) + try { + result = new url.URL(correctedUrl) + } catch (err) {} + + return result +} diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json new file mode 100644 index 00000000000000..930e3b693b9801 --- /dev/null +++ b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json @@ -0,0 +1,52 @@ +{ + "name": "hosted-git-info", + "version": "4.0.1", + "description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab", + "main": "index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/hosted-git-info.git" + }, + "keywords": [ + "git", + "github", + "bitbucket", + "gitlab" + ], + "author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org)", + "license": "ISC", + "bugs": { + "url": "https://github.com/npm/hosted-git-info/issues" + }, + "homepage": "https://github.com/npm/hosted-git-info", + "scripts": { + "posttest": "standard", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preversion": "npm test", + "snap": "tap", + "test": "tap", + "test:coverage": "tap --coverage-report=html" + }, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "devDependencies": { + "standard": "^16.0.3", + "standard-version": "^9.1.0", + "tap": "^14.11.0" + }, + "files": [ + "index.js", + "git-host.js", + "git-host-info.js" + ], + "engines": { + "node": ">=10" + }, + "tap": { + "color": 1, + "coverage": true, + "esm": false + } +} diff --git a/deps/npm/node_modules/normalize-package-data/package.json b/deps/npm/node_modules/normalize-package-data/package.json index 8df2f8fcac21e7..09b184c19d563e 100644 --- a/deps/npm/node_modules/normalize-package-data/package.json +++ b/deps/npm/node_modules/normalize-package-data/package.json @@ -1,6 +1,6 @@ { "name": "normalize-package-data", - "version": "3.0.0", + "version": "3.0.2", "author": "Meryn Stol <merynstol@gmail.com>", "description": "Normalizes data that can be found in package.json files.", "license": "BSD-2-Clause", @@ -10,16 +10,19 @@ }, "main": "lib/normalize.js", "scripts": { + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preversion": "npm test", "test": "tap test/*.js" }, "dependencies": { - "hosted-git-info": "^3.0.6", - "resolve": "^1.17.0", - "semver": "^7.3.2", + "hosted-git-info": "^4.0.1", + "resolve": "^1.20.0", + "semver": "^7.3.4", "validate-npm-package-license": "^3.0.1" }, "devDependencies": { - "tap": "^14.10.8" + "tap": "^14.11.0" }, "files": [ "lib/*.js", diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md new file mode 100644 index 00000000000000..3ffcacacc575c0 --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md @@ -0,0 +1,185 @@ +# Change Log + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +<a name="3.0.8"></a> +## [3.0.8](https://github.com/npm/hosted-git-info/compare/v3.0.7...v3.0.8) (2021-01-28) + + +### Bug Fixes + +* simplify the regular expression for shortcut matching ([bede0dc](https://github.com/npm/hosted-git-info/commit/bede0dc)), closes [#76](https://github.com/npm/hosted-git-info/issues/76) + + + +<a name="3.0.7"></a> +## [3.0.7](https://github.com/npm/hosted-git-info/compare/v3.0.6...v3.0.7) (2020-10-15) + + +### Bug Fixes + +* correctly filter out urls for tarballs in gitlab ([eb5bd5a](https://github.com/npm/hosted-git-info/commit/eb5bd5a)), closes [#69](https://github.com/npm/hosted-git-info/issues/69) + + + +<a name="3.0.6"></a> +## [3.0.6](https://github.com/npm/hosted-git-info/compare/v3.0.5...v3.0.6) (2020-10-12) + + +### Bug Fixes + +* support to github gist legacy hash length ([c067102](https://github.com/npm/hosted-git-info/commit/c067102)), closes [#68](https://github.com/npm/hosted-git-info/issues/68) + + + +<a name="3.0.5"></a> +## [3.0.5](https://github.com/npm/hosted-git-info/compare/v3.0.4...v3.0.5) (2020-07-11) + + + +<a name="3.0.4"></a> +## [3.0.4](https://github.com/npm/hosted-git-info/compare/v3.0.3...v3.0.4) (2020-02-26) + + +### Bug Fixes + +* Do not pass scp-style URLs to the WhatWG url.URL ([0835306](https://github.com/npm/hosted-git-info/commit/0835306)), closes [#60](https://github.com/npm/hosted-git-info/issues/60) [#63](https://github.com/npm/hosted-git-info/issues/63) + + + +<a name="3.0.3"></a> +## [3.0.3](https://github.com/npm/hosted-git-info/compare/v3.0.2...v3.0.3) (2020-02-25) + + + +<a name="3.0.2"></a> +## [3.0.2](https://github.com/npm/hosted-git-info/compare/v3.0.1...v3.0.2) (2019-10-08) + + +### Bug Fixes + +* do not encodeURIComponent the domain ([3e5fbec](https://github.com/npm/hosted-git-info/commit/3e5fbec)), closes [#53](https://github.com/npm/hosted-git-info/issues/53) + + + +<a name="3.0.1"></a> +## [3.0.1](https://github.com/npm/hosted-git-info/compare/v3.0.0...v3.0.1) (2019-10-07) + + +### Bug Fixes + +* update pathmatch for gitlab ([e3e3054](https://github.com/npm/hosted-git-info/commit/e3e3054)), closes [#52](https://github.com/npm/hosted-git-info/issues/52) +* updated pathmatch for gitlab ([fa87af7](https://github.com/npm/hosted-git-info/commit/fa87af7)) + + + +<a name="3.0.0"></a> +# [3.0.0](https://github.com/npm/hosted-git-info/compare/v2.8.3...v3.0.0) (2019-08-12) + + +### Bug Fixes + +* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([37c2891](https://github.com/npm/hosted-git-info/commit/37c2891)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) + + +### BREAKING CHANGES + +* **cache:** Drop support for node 0.x + + + +<a name="2.8.3"></a> +## [2.8.3](https://github.com/npm/hosted-git-info/compare/v2.8.2...v2.8.3) (2019-08-12) + + + +<a name="2.8.2"></a> +## [2.8.2](https://github.com/npm/hosted-git-info/compare/v2.8.1...v2.8.2) (2019-08-05) + + +### Bug Fixes + +* http protocol use sshurl by default ([3b1d629](https://github.com/npm/hosted-git-info/commit/3b1d629)), closes [#48](https://github.com/npm/hosted-git-info/issues/48) + + + +<a name="2.8.1"></a> +## [2.8.1](https://github.com/npm/hosted-git-info/compare/v2.8.0...v2.8.1) (2019-08-05) + + +### Bug Fixes + +* ignore noCommittish on tarball url generation ([5d4a8d7](https://github.com/npm/hosted-git-info/commit/5d4a8d7)) +* use gist tarball url that works for anonymous gists ([1692435](https://github.com/npm/hosted-git-info/commit/1692435)) + + + +<a name="2.8.0"></a> +# [2.8.0](https://github.com/npm/hosted-git-info/compare/v2.7.1...v2.8.0) (2019-08-05) + + +### Bug Fixes + +* Allow slashes in gitlab project section ([bbcf7b2](https://github.com/npm/hosted-git-info/commit/bbcf7b2)), closes [#46](https://github.com/npm/hosted-git-info/issues/46) [#43](https://github.com/npm/hosted-git-info/issues/43) +* **git-host:** disallow URI-encoded slash (%2F) in `path` ([3776fa5](https://github.com/npm/hosted-git-info/commit/3776fa5)), closes [#44](https://github.com/npm/hosted-git-info/issues/44) +* **gitlab:** Do not URL encode slashes in project name for GitLab https URL ([cbf04f9](https://github.com/npm/hosted-git-info/commit/cbf04f9)), closes [#47](https://github.com/npm/hosted-git-info/issues/47) +* do not allow invalid gist urls ([d5cf830](https://github.com/npm/hosted-git-info/commit/d5cf830)) +* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([e518222](https://github.com/npm/hosted-git-info/commit/e518222)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) + + +### Features + +* give these objects a name ([60abaea](https://github.com/npm/hosted-git-info/commit/60abaea)) + + + +<a name="2.7.1"></a> +## [2.7.1](https://github.com/npm/hosted-git-info/compare/v2.7.0...v2.7.1) (2018-07-07) + + +### Bug Fixes + +* **index:** Guard against non-string types ([5bc580d](https://github.com/npm/hosted-git-info/commit/5bc580d)) +* **parse:** Crash on strings that parse to having no host ([c931482](https://github.com/npm/hosted-git-info/commit/c931482)), closes [#35](https://github.com/npm/hosted-git-info/issues/35) + + + +<a name="2.7.0"></a> +# [2.7.0](https://github.com/npm/hosted-git-info/compare/v2.6.1...v2.7.0) (2018-07-06) + + +### Bug Fixes + +* **github tarball:** update github tarballtemplate ([6efd582](https://github.com/npm/hosted-git-info/commit/6efd582)), closes [#34](https://github.com/npm/hosted-git-info/issues/34) +* **gitlab docs:** switched to lowercase anchors for readmes ([701bcd1](https://github.com/npm/hosted-git-info/commit/701bcd1)) + + +### Features + +* **all:** Support www. prefixes on hostnames ([3349575](https://github.com/npm/hosted-git-info/commit/3349575)), closes [#32](https://github.com/npm/hosted-git-info/issues/32) + + + +<a name="2.6.1"></a> +## [2.6.1](https://github.com/npm/hosted-git-info/compare/v2.6.0...v2.6.1) (2018-06-25) + +### Bug Fixes + +* **Revert:** "compat: remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25))" ([cce5a62](https://github.com/npm/hosted-git-info/commit/cce5a62)) +* **Revert:** "git-host: fix forgotten extend()" ([a815ec9](https://github.com/npm/hosted-git-info/commit/a815ec9)) + + + +<a name="2.6.0"></a> +# [2.6.0](https://github.com/npm/hosted-git-info/compare/v2.5.0...v2.6.0) (2018-03-07) + + +### Bug Fixes + +* **compat:** remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25)) ([627ab55](https://github.com/npm/hosted-git-info/commit/627ab55)) +* **git-host:** fix forgotten extend() ([eba1f7b](https://github.com/npm/hosted-git-info/commit/eba1f7b)) + + +### Features + +* **browse:** fragment support for browse() ([#28](https://github.com/npm/hosted-git-info/issues/28)) ([cd5e5bb](https://github.com/npm/hosted-git-info/commit/cd5e5bb)) diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE new file mode 100644 index 00000000000000..45055763dc838d --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2015, Rebecca Turner + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md new file mode 100644 index 00000000000000..87404060296269 --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md @@ -0,0 +1,133 @@ +# hosted-git-info + +This will let you identify and transform various git hosts URLs between +protocols. It also can tell you what the URL is for the raw path for +particular file for direct access without git. + +## Example + +```javascript +var hostedGitInfo = require("hosted-git-info") +var info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts) +/* info looks like: +{ + type: "github", + domain: "github.com", + user: "npm", + project: "hosted-git-info" +} +*/ +``` + +If the URL can't be matched with a git host, `null` will be returned. We +can match git, ssh and https urls. Additionally, we can match ssh connect +strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg, +`github:npm/hosted-git-info`). GitHub specifically, is detected in the case +of a third, unprefixed, form: `npm/hosted-git-info`. + +If it does match, the returned object has properties of: + +* info.type -- The short name of the service +* info.domain -- The domain for git protocol use +* info.user -- The name of the user/org on the git host +* info.project -- The name of the project on the git host + +## Version Contract + +The major version will be bumped any time… + +* The constructor stops accepting URLs that it previously accepted. +* A method is removed. +* A method can no longer accept the number and type of arguments it previously accepted. +* A method can return a different type than it currently returns. + +Implications: + +* I do not consider the specific format of the urls returned from, say + `.https()` to be a part of the contract. The contract is that it will + return a string that can be used to fetch the repo via HTTPS. But what + that string looks like, specifically, can change. +* Dropping support for a hosted git provider would constitute a breaking + change. + +## Usage + +### var info = hostedGitInfo.fromUrl(gitSpecifier[, options]) + +* *gitSpecifer* is a URL of a git repository or a SCP-style specifier of one. +* *options* is an optional object. It can have the following properties: + * *noCommittish* — If true then committishes won't be included in generated URLs. + * *noGitPlus* — If true then `git+` won't be prefixed on URLs. + +## Methods + +All of the methods take the same options as the `fromUrl` factory. Options +provided to a method override those provided to the constructor. + +* info.file(path, opts) + +Given the path of a file relative to the repository, returns a URL for +directly fetching it from the githost. If no committish was set then +`master` will be used as the default. + +For example `hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git#v1.0.0").file("package.json")` +would return `https://raw.githubusercontent.com/npm/hosted-git-info/v1.0.0/package.json` + +* info.shortcut(opts) + +eg, `github:npm/hosted-git-info` + +* info.browse(path, fragment, opts) + +eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0`, +`https://github.com/npm/hosted-git-info/tree/v1.2.0/package.json`, +`https://github.com/npm/hosted-git-info/tree/v1.2.0/REAMDE.md#supported-hosts` + +* info.bugs(opts) + +eg, `https://github.com/npm/hosted-git-info/issues` + +* info.docs(opts) + +eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0#readme` + +* info.https(opts) + +eg, `git+https://github.com/npm/hosted-git-info.git` + +* info.sshurl(opts) + +eg, `git+ssh://git@github.com/npm/hosted-git-info.git` + +* info.ssh(opts) + +eg, `git@github.com:npm/hosted-git-info.git` + +* info.path(opts) + +eg, `npm/hosted-git-info` + +* info.tarball(opts) + +eg, `https://github.com/npm/hosted-git-info/archive/v1.2.0.tar.gz` + +* info.getDefaultRepresentation() + +Returns the default output type. The default output type is based on the +string you passed in to be parsed + +* info.toString(opts) + +Uses the getDefaultRepresentation to call one of the other methods to get a URL for +this resource. As such `hostedGitInfo.fromUrl(url).toString()` will give +you a normalized version of the URL that still uses the same protocol. + +Shortcuts will still be returned as shortcuts, but the special case github +form of `org/project` will be normalized to `github:org/project`. + +SSH connect strings will be normalized into `git+ssh` URLs. + +## Supported hosts + +Currently this supports GitHub, Bitbucket and GitLab. Pull requests for +additional hosts welcome. diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js new file mode 100644 index 00000000000000..360d7b096be617 --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js @@ -0,0 +1,154 @@ +'use strict' +const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : '' +const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : '' + +const defaults = { + sshtemplate: ({ domain, user, project, committish }) => `git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`, + sshurltemplate: ({ domain, user, project, committish }) => `git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`, + browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`, + docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`, + httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/raw/${maybeEncode(committish) || 'master'}/${path}`, + shortcuttemplate: ({ type, user, project, committish }) => `${type}:${user}/${project}${maybeJoin('#', committish)}`, + pathtemplate: ({ user, project, committish }) => `${user}/${project}${maybeJoin('#', committish)}`, + bugstemplate: ({ domain, user, project }) => `https://${domain}/${user}/${project}/issues`, + hashformat: formatHashFragment +} + +const gitHosts = {} +gitHosts.github = Object.assign({}, defaults, { + // First two are insecure and generally shouldn't be used any more, but + // they are still supported. + protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'github.com', + treepath: 'tree', + filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${path}`, + gittemplate: ({ auth, domain, user, project, committish }) => `git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ domain, user, project, committish }) => `https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, + extract: (url) => { + let [, user, project, type, committish] = url.pathname.split('/', 5) + if (type && type !== 'tree') { + return + } + + if (!type) { + committish = url.hash.slice(1) + } + + if (project && project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (!user || !project) { + return + } + + return { user, project, committish } + } +}) + +gitHosts.bitbucket = Object.assign({}, defaults, { + protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'bitbucket.org', + treepath: 'src', + tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.tar.gz`, + extract: (url) => { + let [, user, project, aux] = url.pathname.split('/', 4) + if (['get'].includes(aux)) { + return + } + + if (project && project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (!user || !project) { + return + } + + return { user, project, committish: url.hash.slice(1) } + } +}) + +gitHosts.gitlab = Object.assign({}, defaults, { + protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'gitlab.com', + treepath: 'tree', + httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`, + extract: (url) => { + const path = url.pathname.slice(1) + if (path.includes('/-/')) { + return + } + + const segments = path.split('/') + let project = segments.pop() + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + const user = segments.join('/') + if (!user || !project) { + return + } + + return { user, project, committish: url.hash.slice(1) } + } +}) + +gitHosts.gist = Object.assign({}, defaults, { + protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], + domain: 'gist.github.com', + sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`, + sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`, + browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, + browsefiletemplate: ({ domain, project, committish, path, hashformat }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`, + docstemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, + httpstemplate: ({ domain, project, committish }) => `git+https://${domain}/${project}.git${maybeJoin('#', committish)}`, + filetemplate: ({ user, project, committish, path }) => `https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`, + shortcuttemplate: ({ type, project, committish }) => `${type}:${project}${maybeJoin('#', committish)}`, + pathtemplate: ({ project, committish }) => `${project}${maybeJoin('#', committish)}`, + bugstemplate: ({ domain, project }) => `https://${domain}/${project}`, + gittemplate: ({ domain, project, committish }) => `git://${domain}/${project}.git${maybeJoin('#', committish)}`, + tarballtemplate: ({ project, committish }) => `https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, + extract: (url) => { + let [, user, project, aux] = url.pathname.split('/', 4) + if (aux === 'raw') { + return + } + + if (!project) { + if (!user) { + return + } + + project = user + user = null + } + + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + return { user, project, committish: url.hash.slice(1) } + }, + hashformat: function (fragment) { + return fragment && 'file-' + formatHashFragment(fragment) + } +}) + +const names = Object.keys(gitHosts) +gitHosts.byShortcut = {} +gitHosts.byDomain = {} +for (const name of names) { + gitHosts.byShortcut[`${name}:`] = name + gitHosts.byDomain[gitHosts[name].domain] = name +} + +function formatHashFragment (fragment) { + return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') +} + +module.exports = gitHosts diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js new file mode 100644 index 00000000000000..8a975e92e58bb7 --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js @@ -0,0 +1,110 @@ +'use strict' +const gitHosts = require('./git-host-info.js') + +class GitHost { + constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) { + Object.assign(this, gitHosts[type]) + this.type = type + this.user = user + this.auth = auth + this.project = project + this.committish = committish + this.default = defaultRepresentation + this.opts = opts + } + + hash () { + return this.committish ? `#${this.committish}` : '' + } + + ssh (opts) { + return this._fill(this.sshtemplate, opts) + } + + _fill (template, opts) { + if (typeof template === 'function') { + const options = { ...this, ...this.opts, ...opts } + + // the path should always be set so we don't end up with 'undefined' in urls + if (!options.path) { + options.path = '' + } + + // template functions will insert the leading slash themselves + if (options.path.startsWith('/')) { + options.path = options.path.slice(1) + } + + if (options.noCommittish) { + options.committish = null + } + + const result = template(options) + return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result + } + + return null + } + + sshurl (opts) { + return this._fill(this.sshurltemplate, opts) + } + + browse (path, fragment, opts) { + // not a string, treat path as opts + if (typeof path !== 'string') { + return this._fill(this.browsetemplate, path) + } + + if (typeof fragment !== 'string') { + opts = fragment + fragment = null + } + return this._fill(this.browsefiletemplate, { ...opts, fragment, path }) + } + + docs (opts) { + return this._fill(this.docstemplate, opts) + } + + bugs (opts) { + return this._fill(this.bugstemplate, opts) + } + + https (opts) { + return this._fill(this.httpstemplate, opts) + } + + git (opts) { + return this._fill(this.gittemplate, opts) + } + + shortcut (opts) { + return this._fill(this.shortcuttemplate, opts) + } + + path (opts) { + return this._fill(this.pathtemplate, opts) + } + + tarball (opts) { + return this._fill(this.tarballtemplate, { ...opts, noCommittish: false }) + } + + file (path, opts) { + return this._fill(this.filetemplate, { ...opts, path }) + } + + getDefaultRepresentation () { + return this.default + } + + toString (opts) { + if (this.default && typeof this[this.default] === 'function') { + return this[this.default](opts) + } + + return this.sshurl(opts) + } +} +module.exports = GitHost diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js new file mode 100644 index 00000000000000..f35c570c46b595 --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js @@ -0,0 +1,237 @@ +'use strict' +const url = require('url') +const gitHosts = require('./git-host-info.js') +const GitHost = module.exports = require('./git-host.js') +const LRU = require('lru-cache') +const cache = new LRU({ max: 1000 }) + +const protocolToRepresentationMap = { + 'git+ssh:': 'sshurl', + 'git+https:': 'https', + 'ssh:': 'sshurl', + 'git:': 'git' +} + +function protocolToRepresentation (protocol) { + return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) +} + +const authProtocols = { + 'git:': true, + 'https:': true, + 'git+https:': true, + 'http:': true, + 'git+http:': true +} + +const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:']) + +module.exports.fromUrl = function (giturl, opts) { + if (typeof giturl !== 'string') { + return + } + + const key = giturl + JSON.stringify(opts || {}) + + if (!cache.has(key)) { + cache.set(key, fromUrl(giturl, opts)) + } + + return cache.get(key) +} + +function fromUrl (giturl, opts) { + if (!giturl) { + return + } + + const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl) + const parsed = parseGitUrl(url) + if (!parsed) { + return parsed + } + + const gitHostShortcut = gitHosts.byShortcut[parsed.protocol] + const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.') ? parsed.hostname.slice(4) : parsed.hostname] + const gitHostName = gitHostShortcut || gitHostDomain + if (!gitHostName) { + return + } + + const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain] + let auth = null + if (authProtocols[parsed.protocol] && (parsed.username || parsed.password)) { + auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}` + } + + let committish = null + let user = null + let project = null + let defaultRepresentation = null + + try { + if (gitHostShortcut) { + let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname + const firstAt = pathname.indexOf('@') + // we ignore auth for shortcuts, so just trim it out + if (firstAt > -1) { + pathname = pathname.slice(firstAt + 1) + } + + const lastSlash = pathname.lastIndexOf('/') + if (lastSlash > -1) { + user = decodeURIComponent(pathname.slice(0, lastSlash)) + // we want nulls only, never empty strings + if (!user) { + user = null + } + project = decodeURIComponent(pathname.slice(lastSlash + 1)) + } else { + project = decodeURIComponent(pathname) + } + + if (project.endsWith('.git')) { + project = project.slice(0, -4) + } + + if (parsed.hash) { + committish = decodeURIComponent(parsed.hash.slice(1)) + } + + defaultRepresentation = 'shortcut' + } else { + if (!gitHostInfo.protocols.includes(parsed.protocol)) { + return + } + + const segments = gitHostInfo.extract(parsed) + if (!segments) { + return + } + + user = segments.user && decodeURIComponent(segments.user) + project = decodeURIComponent(segments.project) + committish = decodeURIComponent(segments.committish) + defaultRepresentation = protocolToRepresentation(parsed.protocol) + } + } catch (err) { + /* istanbul ignore else */ + if (err instanceof URIError) { + return + } else { + throw err + } + } + + return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) +} + +// accepts input like git:github.com:user/repo and inserts the // after the first : +const correctProtocol = (arg) => { + const firstColon = arg.indexOf(':') + const proto = arg.slice(0, firstColon + 1) + if (knownProtocols.includes(proto)) { + return arg + } + + const firstAt = arg.indexOf('@') + if (firstAt > -1) { + if (firstAt > firstColon) { + return `git+ssh://${arg}` + } else { + return arg + } + } + + const doubleSlash = arg.indexOf('//') + if (doubleSlash === firstColon + 1) { + return arg + } + + return arg.slice(0, firstColon + 1) + '//' + arg.slice(firstColon + 1) +} + +// look for github shorthand inputs, such as npm/cli +const isGitHubShorthand = (arg) => { + // it cannot contain whitespace before the first # + // it cannot start with a / because that's probably an absolute file path + // but it must include a slash since repos are username/repository + // it cannot start with a . because that's probably a relative file path + // it cannot start with an @ because that's a scoped package if it passes the other tests + // it cannot contain a : before a # because that tells us that there's a protocol + // a second / may not exist before a # + const firstHash = arg.indexOf('#') + const firstSlash = arg.indexOf('/') + const secondSlash = arg.indexOf('/', firstSlash + 1) + const firstColon = arg.indexOf(':') + const firstSpace = /\s/.exec(arg) + const firstAt = arg.indexOf('@') + + const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash) + const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash) + const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash) + const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash) + const hasSlash = firstSlash > 0 + // if a # is found, what we really want to know is that the character immediately before # is not a / + const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/') + const doesNotStartWithDot = !arg.startsWith('.') + + return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash && doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash && secondSlashOnlyAfterHash +} + +// attempt to correct an scp style url so that it will parse with `new URL()` +const correctUrl = (giturl) => { + const firstAt = giturl.indexOf('@') + const lastHash = giturl.lastIndexOf('#') + let firstColon = giturl.indexOf(':') + let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity) + + let corrected + if (lastColon > firstAt) { + // the last : comes after the first @ (or there is no @) + // like it would in: + // proto://hostname.com:user/repo + // username@hostname.com:user/repo + // :password@hostname.com:user/repo + // username:password@hostname.com:user/repo + // proto://username@hostname.com:user/repo + // proto://:password@hostname.com:user/repo + // proto://username:password@hostname.com:user/repo + // then we replace the last : with a / to create a valid path + corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1) + // // and we find our new : positions + firstColon = corrected.indexOf(':') + lastColon = corrected.lastIndexOf(':') + } + + if (firstColon === -1 && giturl.indexOf('//') === -1) { + // we have no : at all + // as it would be in: + // username@hostname.com/user/repo + // then we prepend a protocol + corrected = `git+ssh://${corrected}` + } + + return corrected +} + +// try to parse the url as its given to us, if that throws +// then we try to clean the url and parse that result instead +// THIS FUNCTION SHOULD NEVER THROW +const parseGitUrl = (giturl) => { + let result + try { + result = new url.URL(giturl) + } catch (err) {} + + if (result) { + return result + } + + const correctedUrl = correctUrl(giturl) + try { + result = new url.URL(correctedUrl) + } catch (err) {} + + return result +} diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json new file mode 100644 index 00000000000000..930e3b693b9801 --- /dev/null +++ b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json @@ -0,0 +1,52 @@ +{ + "name": "hosted-git-info", + "version": "4.0.1", + "description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab", + "main": "index.js", + "repository": { + "type": "git", + "url": "git+https://github.com/npm/hosted-git-info.git" + }, + "keywords": [ + "git", + "github", + "bitbucket", + "gitlab" + ], + "author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org)", + "license": "ISC", + "bugs": { + "url": "https://github.com/npm/hosted-git-info/issues" + }, + "homepage": "https://github.com/npm/hosted-git-info", + "scripts": { + "posttest": "standard", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags", + "preversion": "npm test", + "snap": "tap", + "test": "tap", + "test:coverage": "tap --coverage-report=html" + }, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "devDependencies": { + "standard": "^16.0.3", + "standard-version": "^9.1.0", + "tap": "^14.11.0" + }, + "files": [ + "index.js", + "git-host.js", + "git-host-info.js" + ], + "engines": { + "node": ">=10" + }, + "tap": { + "color": 1, + "coverage": true, + "esm": false + } +} diff --git a/deps/npm/node_modules/npm-package-arg/package.json b/deps/npm/node_modules/npm-package-arg/package.json index c460be828efcb4..ed3b364442c2cc 100644 --- a/deps/npm/node_modules/npm-package-arg/package.json +++ b/deps/npm/node_modules/npm-package-arg/package.json @@ -1,6 +1,6 @@ { "name": "npm-package-arg", - "version": "8.1.1", + "version": "8.1.2", "description": "Parse the things that can be arguments to `npm install`", "main": "npa.js", "directories": { @@ -10,12 +10,12 @@ "npa.js" ], "dependencies": { - "hosted-git-info": "^3.0.6", - "semver": "^7.0.0", + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", "validate-npm-package-name": "^3.0.0" }, "devDependencies": { - "tap": "^14.10.2" + "tap": "^14.11.0" }, "scripts": { "preversion": "npm test", diff --git a/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md b/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md index a4ee13e92ab45c..5edb4a33c1a409 100644 --- a/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md +++ b/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +All notable changes to this project will be documented in this file. + +## [6.1.1](https://github.com/npm/npm-pick-manifest/compare/v6.0.0...v6.1.0) (2020-04-07) + +* normalize package bins in returned manifest ## [6.1.0](https://github.com/npm/npm-pick-manifest/compare/v6.0.0...v6.1.0) (2020-04-07) diff --git a/deps/npm/node_modules/npm-pick-manifest/index.js b/deps/npm/node_modules/npm-pick-manifest/index.js index 2b3ea6ffa4930e..695450524dc13d 100644 --- a/deps/npm/node_modules/npm-pick-manifest/index.js +++ b/deps/npm/node_modules/npm-pick-manifest/index.js @@ -3,6 +3,7 @@ const npa = require('npm-package-arg') const semver = require('semver') const { checkEngine } = require('npm-install-checks') +const normalizeBin = require('npm-normalize-package-bin') const engineOk = (manifest, npmVersion, nodeVersion) => { try { @@ -183,7 +184,8 @@ const pickManifest = (packument, wanted, opts) => { } module.exports = (packument, wanted, opts = {}) => { - const picked = pickManifest(packument, wanted, opts) + const mani = pickManifest(packument, wanted, opts) + const picked = mani && normalizeBin(mani) const policyRestrictions = packument.policyRestrictions const restricted = (policyRestrictions && policyRestrictions.versions) || {} diff --git a/deps/npm/node_modules/npm-pick-manifest/package.json b/deps/npm/node_modules/npm-pick-manifest/package.json index 805f5ac23a8463..4b4866cbf88326 100644 --- a/deps/npm/node_modules/npm-pick-manifest/package.json +++ b/deps/npm/node_modules/npm-pick-manifest/package.json @@ -1,6 +1,6 @@ { "name": "npm-pick-manifest", - "version": "6.1.0", + "version": "6.1.1", "description": "Resolves a matching manifest from a package metadata document according to standard npm semver resolution rules.", "main": "index.js", "files": [ @@ -9,12 +9,11 @@ "scripts": { "coverage": "tap", "lint": "standard", - "postrelease": "npm publish", + "test": "tap", "posttest": "npm run lint", - "prepublishOnly": "git push --follow-tags", - "prerelease": "npm t", - "release": "standard-version -s", - "test": "tap" + "preversion": "npm test", + "postversion": "npm publish", + "prepublishOnly": "git push origin --follow-tags" }, "repository": "https://github.com/npm/npm-pick-manifest", "keywords": [ @@ -30,13 +29,13 @@ "license": "ISC", "dependencies": { "npm-install-checks": "^4.0.0", - "npm-package-arg": "^8.0.0", - "semver": "^7.0.0" + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" }, "devDependencies": { "standard": "^14.3.1", - "standard-version": "^7.0.1", - "tap": "^14.10.2" + "tap": "^14.11.0" }, "tap": { "check-coverage": true diff --git a/deps/npm/node_modules/pacote/lib/util/tar-create-options.js b/deps/npm/node_modules/pacote/lib/util/tar-create-options.js index e8abbe175b2626..31ab34c9d949f8 100644 --- a/deps/npm/node_modules/pacote/lib/util/tar-create-options.js +++ b/deps/npm/node_modules/pacote/lib/util/tar-create-options.js @@ -4,7 +4,13 @@ const tarCreateOptions = manifest => ({ cwd: manifest._resolved, prefix: 'package/', portable: true, - gzip: true, + gzip: { + // forcing the level to 9 seems to avoid some + // platform specific optimizations that cause + // integrity mismatch errors due to differing + // end results after compression + level: 9 + }, // ensure that package bins are always executable // Note that npm-packlist is already filtering out diff --git a/deps/npm/node_modules/pacote/package.json b/deps/npm/node_modules/pacote/package.json index dca67f3e8876ac..dd6bf9400c6ea3 100644 --- a/deps/npm/node_modules/pacote/package.json +++ b/deps/npm/node_modules/pacote/package.json @@ -1,6 +1,6 @@ { "name": "pacote", - "version": "11.3.0", + "version": "11.3.1", "description": "JavaScript package downloader", "author": "Isaac Z. Schlueter <i@izs.me> (https://izs.me)", "bin": { diff --git a/deps/npm/node_modules/puka/CHANGELOG.md b/deps/npm/node_modules/puka/CHANGELOG.md deleted file mode 100644 index 781b81295a4a7f..00000000000000 --- a/deps/npm/node_modules/puka/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# Changelog -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## [1.0.1](https://gitlab.com/rhendric/puka/-/compare/v1.0.0...v1.0.1) - 2020-05-16 - -### Fixed - -- Add more carets to win32 command arguments ([45965ca](https://gitlab.com/rhendric/puka/-/commit/45965ca60fcc518082e0b085d8e81f3f3279ffb4)) - - As previously documented and implemented, Puka assumed that all programs - are batch files for the purpose of multi-escaping commands that appear - in pipelines. However, regardless of whether a command is in a pipeline, - one extra layer of escaping is needed if the command invokes a batch - file, which Puka was not producing. This only applies to the arguments - to the command, not to the batch file path, nor to paths used in - redirects. (The property-based spawn test which was supposed to catch - such oversights missed this one because it was invoking the Node.js - executable directly, not, as recommended in the documentation, a batch - file.) - - Going forward, the caveats described in the documentation continue to - apply: if you are running programs on Windows with Puka, make sure they - are batch files, or you may find arguments are being escaped with too - many carets. As the documentation says, if this causes problems for you, - please open an issue so we can work out the details of what a good - workaround looks like. - -## [1.0.0](https://gitlab.com/rhendric/puka/-/tags/v1.0.0) - 2017-09-29 diff --git a/deps/npm/node_modules/puka/LICENSE.txt b/deps/npm/node_modules/puka/LICENSE.txt deleted file mode 100644 index 0141196a593376..00000000000000 --- a/deps/npm/node_modules/puka/LICENSE.txt +++ /dev/null @@ -1,18 +0,0 @@ -Copyright 2017 Ryan Hendrickson <ryan.hendrickson@alum.mit.edu> - -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. diff --git a/deps/npm/node_modules/puka/README.md b/deps/npm/node_modules/puka/README.md deleted file mode 100644 index 2670f742b36773..00000000000000 --- a/deps/npm/node_modules/puka/README.md +++ /dev/null @@ -1,411 +0,0 @@ -# Puka - -[](https://gitlab.com/rhendric/puka/commits/master) [](https://ci.appveyor.com/project/rhendric/puka) [](https://codecov.io/gl/rhendric/puka) - -Puka is a cross-platform library for safely passing strings through shells. - -#### Contents - -- [Introduction](#introduction) - - [Why would I use Puka?](#why-would-i-use-puka) - - [How do I use Puka?](#how-do-i-use-puka) - - [What's the catch?](#whats-the-catch) -- [API Documentation](#api-documentation) - - [Basic API](#basic-api) - - [sh](#sh) - - [unquoted](#unquoted) - - [Advanced API](#advanced-api) - - [quoteForShell](#quoteforshell) - - [quoteForCmd](#quoteforcmd) - - [quoteForSh](#quoteforsh) - - [ShellString](#shellstring) - - [Secret API](#secret-api) -- [The sh DSL](#the-sh-dsl) - - [Syntax](#syntax) - - [Semantics](#semantics) - - [Types of placeholders](#types-of-placeholders) - -## Introduction - -### Why would I use Puka? - -When launching a child process from Node, you have a choice between launching -directly from the operating system (as with [child_process.spawn](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options), -if you don't use the `{ shell: true }` option), and running the command through -a shell (as with [child_process.exec](https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback)). -Using a shell gives you more power, such as the ability to chain multiple -commands together or use redirection, but you have to construct your command as -a single string instead of using an array of arguments. And doing that can be -buggy (if not dangerous) if you don't take care to quote any arguments -correctly for the shell you're targeting, _and_ the quoting has to be done -differently on Windows and non-Windows shells. - -Puka solves that problem by giving you a simple and platform-agnostic way to -build shell commands with arguments that pass through your shell unaltered and -with no unsafe side effects, **whether you are running on Windows or a -Unix-based OS**. - -### How do I use Puka? - -Puka gives you an `sh` function intended for tagging -[template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), -which quotes (if necessary) any values interpolated into the template. A simple -example: - -```javascript -const { sh } = require('puka'); -const { execSync } = require('child_process'); - -const arg = 'file with spaces.txt'; -execSync(sh`some-command ${arg}`); -``` - -But Puka supports more than this! See [the `sh` DSL documentation](#the-sh-dsl) -for a detailed description of all the features currently supported. - -### What's the catch? - -Here are the ones I know about: - -Puka does _not_ ensure that the actual commands you're running are -cross-platform. If you're running npm programs, you generally won't have a -problem with that, but if you want to run ``sh`cat file` `` on Windows, you'll -need to depend on something like -[cash-cat](https://www.npmjs.com/package/cash-cat). - -I searched for days for a way to quote or escape line breaks in arguments to -`cmd.exe`, but couldn't find one (regular `^`-prepending and quotation marks -don't seem to cut it). If you know of a way that works, please [open an -issue](https://gitlab.com/rhendric/puka/issues/new) to tell me about it! Until -then, any line break characters (`\r` or `\n`) in values being interpolated by -`sh` will cause an error to be thrown on Windows only. - -Also on Windows, you may notice quoting mistakes if you run commands that -involve invoking a native executable (not a batch file ending in `.cmd` or -`.bat`). Unfortunately, batch files require some extra escaping on Windows, and -Puka assumes all programs are batch files because npm creates batch file shims -for programs it installs (and, if you care about cross-platform, you'll be -using npm programs in your commands). If this causes problems for you, please -[open an issue](https://gitlab.com/rhendric/puka/issues/new); if your situation -is specific enough, there may be workarounds or improvements to Puka to be -found. - -## API Documentation - -### Basic API - - - - -#### sh - -A string template tag for safely constructing cross-platform shell commands. - -An `sh` template is not actually treated as a literal string to be -interpolated; instead, it is a tiny DSL designed to make working with shell -strings safe, simple, and straightforward. To get started quickly, see the -examples below. [More detailed documentation][1] is available -further down. - -##### Examples - -```javascript -const title = '"this" & "that"'; -sh`script --title=${title}`; // => "script '--title=\"this\" & \"that\"'" -// Note: these examples show results for non-Windows platforms. -// On Windows, the above would instead be -// 'script ^^^"--title=\\^^^"this\\^^^" ^^^& \\^^^"that\\^^^"^^^"'. - -const names = ['file1', 'file 2']; -sh`rimraf ${names}.txt`; // => "rimraf file1.txt 'file 2.txt'" - -const cmd1 = ['cat', 'file 1.txt', 'file 2.txt']; -const cmd2 = ['use-input', '-abc']; -sh`${cmd1}|${cmd2}`; // => "cat 'file 1.txt' 'file 2.txt'|use-input -abc" -``` - -Returns **[String][2]** a string formatted for the platform Node is currently -running on. - -#### unquoted - -This function permits raw strings to be interpolated into a `sh` template. - -**IMPORTANT**: If you're using Puka due to security concerns, make sure you -don't pass any untrusted content to `unquoted`. This may be obvious, but -stray punctuation in an `unquoted` section can compromise the safety of the -entire shell command. - -##### Parameters - -- `value` any value (it will be treated as a string) - -##### Examples - -```javascript -const both = true; -sh`foo ${unquoted(both ? '&&' : '||')} bar`; // => 'foo && bar' -``` - -### Advanced API - -If these functions make life easier for you, go ahead and use them; they -are just as well supported as the above. But if you aren't certain you -need them, you probably don't. - - -#### quoteForShell - -Quotes a string for injecting into a shell command. - -This function is exposed for some hypothetical case when the `sh` DSL simply -won't do; `sh` is expected to be the more convenient option almost always. -Compare: - -```javascript -console.log('cmd' + args.map(a => ' ' + quoteForShell(a)).join('')); -console.log(sh`cmd ${args}`); // same as above - -console.log('cmd' + args.map(a => ' ' + quoteForShell(a, true)).join('')); -console.log(sh`cmd "${args}"`); // same as above -``` - -Additionally, on Windows, `sh` checks the entire command string for pipes, -which subtly change how arguments need to be quoted. If your commands may -involve pipes, you are strongly encouraged to use `sh` and not try to roll -your own with `quoteForShell`. - -##### Parameters - -- `text` **[String][2]** to be quoted -- `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string - is already safe. Defaults to `false`. -- `platform` **[String][2]?** a value that `process.platform` might take: - `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - When omitted, effectively the same as `process.platform`. - -Returns **[String][2]** a string that is safe for the current (or specified) -platform. - -#### quoteForCmd - -A Windows-specific version of [quoteForShell][4]. - -##### Parameters - -- `text` **[String][2]** to be quoted -- `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string - is already safe. Defaults to `false`. - -#### quoteForSh - -A Unix-specific version of [quoteForShell][4]. - -##### Parameters - -- `text` **[String][2]** to be quoted -- `forceQuote` **[Boolean][3]?** whether to always add quotes even if the string - is already safe. Defaults to `false`. - -#### ShellString - -A ShellString represents a shell command after it has been interpolated, but -before it has been formatted for a particular platform. ShellStrings are -useful if you want to prepare a command for a different platform than the -current one, for instance. - -To create a ShellString, use `ShellString.sh` the same way you would use -top-level `sh`. - -##### toString - -A method to format a ShellString into a regular String formatted for a -particular platform. - -###### Parameters - -- `platform` **[String][2]?** a value that `process.platform` might take: - `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - When omitted, effectively the same as `process.platform`. - -Returns **[String][2]** - -##### sh - -`ShellString.sh` is a template tag just like `sh`; the only difference is -that this function returns a ShellString which has not yet been formatted -into a String. - -Returns **[ShellString][5]** - -### Secret API - -Some internals of string formatting have been exposed for the ambitious and -brave souls who want to try to extend Puka to handle more shells or custom -interpolated values. This ‘secret’ API is partially documented in the code -but not here, and the semantic versioning guarantees on this API are bumped -down by one level: in other words, minor version releases of Puka can change -the secret API in backward-incompatible ways, and patch releases can add or -deprecate functionality. - -If it's not even documented in the code, use at your own risk—no semver -guarantees apply. - - -[1]: #the-sh-dsl - -[2]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String - -[3]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean - -[4]: #quoteforshell - -[5]: #shellstring - -## The sh DSL - -### Syntax - -An `sh` template comprises words, separated by whitespace. Words can contain: - -- text, which is composed of any characters that are not whitespace, single or - double quotes, or any of the special characters - ``# $ & ( ) ; < > \ ` |``; -- quotations, which are matching single or double quotes surrounding any - characters other than the delimiting quote; and -- placeholders, using the standard JavaScript template syntax (`${}`). - (Placeholders may also appear inside quotations.) - -The special characters ``# $ & ( ) ; < > \ ` |``, if unquoted, form their own -words. - -Redirect operators (`<`, `>`, `>>`, `2>`, etc.) receive their own special -handling, as do semicolons. Other than these two exceptions, no attempt is made -to understand any more sophisticated features of shell syntax. - -Standard JavaScript escape sequences, such as `\t`, are honored in the template -literal, and are treated equivalently to the characters they represent. There -is no further mechanism for escaping within the `sh` DSL itself; in particular, -if you want to put quotes inside quotes, you have to use interpolation, like -this: - -```javascript -sh`echo "${'single = \', double = "'}"` // => "echo 'single = '\\'', double = \"'" -``` - -### Semantics - -Words that do not contain placeholders are emitted mostly verbatim to the -output string. Quotations are formatted in the expected style for the target -platform (single quotes for Unix, double quotes for Windows) regardless of the -quotes used in the template literal—as with JavaScript, single and double quotes -are interchangeable, except for the requirement to pair like with like. Unquoted -semicolons are translated to ampersands on Windows; all other special characters -(as enumerated above), when unquoted, are passed as-is to the output for the -shell to interpret. - -Puka may still quote words not containing the above special characters, if they -contain characters that need quoting on the target platform. For example, on -Windows, the character `%` is used for variable interpolation in `cmd.exe`, and -Puka quotes it on on that platform even if it appears unquoted in the template -literal. Consequently, there is no need to be paranoid about quoting anything -that doesn't look alphanumeric inside a `sh` template literal, for fear of being -burned on a different operating system; anything that matches the definition of -‘text’ above will never need manual quoting. - -#### Types of placeholders - -##### Strings - -If a word contains a string placeholder, then the value of the placeholder is -interpolated into the word and the entire word, if necessary, is quoted. If -the placeholder occurs within quotes, no further quoting is performed: - -```javascript -sh`script --file="${'herp derp'}.txt"`; // => "script --file='herp derp.txt'" -``` - -This behavior can be exploited to force consistent quoting, if desired; but -both of the examples below are safe on all platforms: - -```javascript -const words = ['oneword', 'two words']; -sh`minimal ${words[0]}`; // => "minimal oneword" -sh`minimal ${words[1]}`; // => "minimal 'two words'" -sh`consistent '${words[0]}'`; // => "consistent 'oneword'" -sh`consistent '${words[1]}'`; // => "consistent 'two words'" -``` - -##### Arrays and iterables - -If a word contains a placeholder for an array (or other iterable object), then -the entire word is repeated once for each value in the array, separated by -spaces. If the array is empty, then the word is not emitted at all, and neither -is any leading whitespace. - -```javascript -const files = ['foo', 'bar']; -sh`script ${files}`; // => "script foo bar" -sh`script --file=${files}`; // => "script --file=foo --file=bar" -sh`script --file=${[]}`; // => "script" -``` - -Note that, since special characters are their own words, the pipe operator here -is not repeated: - -```javascript -const cmd = ['script', 'foo', 'bar']; -sh`${cmd}|another-script`; // => "script foo bar|another-script" -``` - -Multiple arrays in the same word generate a Cartesian product: - -```javascript -const names = ['foo', 'bar'], exts = ['log', 'txt']; -// Same word -sh`... ${names}.${exts}`; // => "... foo.log foo.txt bar.log bar.txt" -sh`... "${names} ${exts}"`; // => "... 'foo log' 'foo txt' 'bar log' 'bar txt'" - -// Not the same word (extra space just for emphasis): -sh`... ${names} ${exts}`; // => "... foo bar log txt" -sh`... ${names};${exts}`; // => "... foo bar;log txt" -``` - -Finally, if a placeholder appears in the object of a redirect operator, the -entire redirect is repeated as necessary: - -```javascript -sh`script > ${['foo', 'bar']}.txt`; // => "script > foo.txt > bar.txt" -sh`script > ${[]}.txt`; // => "script" -``` - -##### unquoted - -The `unquoted` function returns a value that will skip being quoted when used -in a placeholder, alone or in an array. - -```javascript -const cmd = 'script < input.txt'; -const fields = ['foo', 'bar']; -sh`${unquoted(cmd)} | json ${fields}`; // => "script < input.txt | json foo bar" -``` - -##### ShellString - -If `ShellString.sh` is used to construct an unformatted ShellString, that value -can be used in a placeholder to insert the contents of the ShellString into the -outer template literal. This is safer than using `unquoted` as in the previous -example, but `unquoted` can be used when all you have is a string from another -(trusted!) source. - -```javascript -const url = 'http://example.com/data.json?x=1&y=2'; -const curl = ShellString.sh`curl -L ${url}`; -const fields = ['foo', 'bar']; -sh`${curl} | json ${fields}`; // => "curl -L 'http://example.com/data.json?x=1&y=2' | json foo bar" -``` - -##### Anything else - -... is treated like a string—namely, a value `x` is equivalent to `'' + x`, if -not in one of the above categories. diff --git a/deps/npm/node_modules/puka/index.js b/deps/npm/node_modules/puka/index.js deleted file mode 100644 index b69e47d7639db9..00000000000000 --- a/deps/npm/node_modules/puka/index.js +++ /dev/null @@ -1,804 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, '__esModule', { value: true }); - -/** - * Key a method on your object with this symbol and you can get special - * formatting for that value! See ShellStringText, ShellStringUnquoted, or - * shellStringSemicolon for examples. - * @ignore - */ -const formatSymbol = Symbol('format'); -/** - * This symbol is for implementing advanced behaviors like the need for extra - * carets in Windows shell strings that use pipes. If present, it's called in - * an earlier phase than formatSymbol, and is passed a mutable context that can - * be read during the format phase to influence formatting. - * @ignore - */ -const preformatSymbol = Symbol('preformat'); - -// When minimum Node version becomes 6, replace calls to sticky with /.../y and -// inline execFrom. -let stickySupported = true; -try { - new RegExp('', 'y'); -} catch (e) { - stickySupported = false; -} -const sticky = stickySupported ? source => new RegExp(source, 'y') : source => new RegExp(`^(?:${source})`); -const execFrom = stickySupported ? (re, haystack, index) => (re.lastIndex = index, re.exec(haystack)) : (re, haystack, index) => re.exec(haystack.substr(index)); - -function quoteForCmd(text, forceQuote) { - let caretDepth = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - // See the below blog post for an explanation of this function and - // quoteForWin32: - // https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/ - if (!text.length) { - return '""'; - } - if (/[\n\r]/.test(text)) { - throw new Error("Line breaks can't be quoted on Windows"); - } - const caretEscape = /["%]/.test(text); - text = quoteForWin32(text, forceQuote || !caretEscape && /[&()<>^|]/.test(text)); - if (caretEscape) { - // See Win32Context for explanation of what caretDepth is for. - do { - text = text.replace(/[\t "%&()<>^|]/g, '^$&'); - } while (caretDepth--); - } - return text; -} -const quoteForWin32 = (text, forceQuote) => forceQuote || /[\t "]/.test(text) ? `"${text.replace(/\\+(?=$|")/g, '$&$&').replace(/"/g, '\\"')}"` : text; -const cmdMetaChars = /[\t\n\r "%&()<>^|]/; -class Win32Context { - constructor() { - this.currentScope = newScope(null); - this.scopesByObject = new Map(); - this.argDetectState = 0; - this.argSet = new Set(); - } - read(text) { - // When cmd.exe executes a batch file, or pipes to or from one, it spawns a - // second copy of itself to run the inner command. This necessitates - // doubling up on carets so that escaped characters survive both cmd.exe - // invocations. See: - // https://stackoverflow.com/questions/8192318/why-does-delayed-expansion-fail-when-inside-a-piped-block-of-code#8194279 - // https://ss64.com/nt/syntax-redirection.html - // - // Parentheses can create an additional subshell, requiring additional - // escaping... it's a mess. - // - // So here's what we do about it: we read all unquoted text in a shell - // string and put it through this tiny parser that looks for pipes, - // sequence operators (&, &&, ||), redirects, and parentheses. This can't - // be part of the main Puka parsing, because it can be affected by - // `unquoted(...)` values provided at evaluation time. - // - // Then, after associating each thing that needs to be quoted with a scope - // (via `mark()`), and identifying whether or not it's an argument to a - // command, we can determine the depth of caret escaping required in each - // scope and pass it (via `Formatter::quote()`) to `quoteForCmd()`. - // - // See also `ShellStringText`, which holds the logic for the previous - // paragraph. - const length = text.length; - for (let pos = 0, match; pos < length;) { - while (match = execFrom(reUnimportant, text, pos)) { - if (match[2] == null) { - // (not whitespace) - if (match[1] != null) { - // (>&) - this.argDetectState = this.argDetectState === 0 ? ADS_FLAG_INITIAL_REDIRECT : 0; - } else if (this.argDetectState !== ADS_FLAG_ARGS) { - this.argDetectState |= ADS_FLAG_WORD; - } - } else { - // (whitespace) - if ((this.argDetectState & ADS_FLAG_WORD) !== 0) { - this.argDetectState = ADS_FLAG_ARGS & ~this.argDetectState >> 1; - } - } - pos += match[0].length; - } - if (pos >= length) break; - if (match = execFrom(reSeqOp, text, pos)) { - this.seq(); - pos += match[0].length; - } else { - const char = text.charCodeAt(pos); - if (char === CARET) { - pos += 2; - } else if (char === QUOTE) { - // If you were foolish enough to leave a dangling quotation mark in - // an unquoted span... you're likely to have bigger problems than - // incorrect escaping. So we just do the simplest thing of looking for - // the end quote only in this piece of text. - pos += execFrom(reNotQuote, text, pos + 1)[0].length + 2; - } else { - if (char === OPEN_PAREN) { - this.enterScope(); - } else if (char === CLOSE_PAREN) { - this.exitScope(); - } else if (char === PIPE) { - this.pipe(); - } else { - // (char === '<' or '>') - this.argDetectState = this.argDetectState === 0 ? ADS_FLAG_INITIAL_REDIRECT : 0; - } - pos++; - } - } - } - } - enterScope() { - this.currentScope = newScope(this.currentScope); - this.argDetectState = 0; - } - exitScope() { - this.currentScope = this.currentScope.parent || (this.currentScope.parent = newScope(null)); - this.argDetectState = ADS_FLAG_ARGS; - } - seq() { - // | binds tighter than sequence operators, so the latter create new sibling - // scopes for future |s to mutate. - this.currentScope = newScope(this.currentScope.parent); - this.argDetectState = 0; - } - pipe() { - this.currentScope.depthDelta = 1; - this.argDetectState = 0; - } - mark(obj) { - this.scopesByObject.set(obj, this.currentScope); - if (this.argDetectState === ADS_FLAG_ARGS) { - this.argSet.add(obj); - } else { - this.argDetectState |= ADS_FLAG_WORD; - } - } - at(obj) { - const scope = this.scopesByObject.get(obj); - return { - depth: getDepth(scope), - isArgument: this.argSet.has(obj), - isNative: scope.isNative - }; - } -} -// These flags span the Win32Context's argument detection state machine. WORD -// is set when the context is inside a word that is not an argument (meaning it -// is either the first word in the command, or it is the object of a redirect). -// ARGS is set when the context has reached the arguments of a command. -// INITIAL_REDIRECT tracks the edge case when a redirect occurs before the -// first word of the command (if this flag is set, reaching the end of a word -// should take the state machine back to 0 instead of setting ADS_FLAG_ARGS). -const ADS_FLAG_WORD = 0x1; -const ADS_FLAG_ARGS = 0x2; -const ADS_FLAG_INITIAL_REDIRECT = 0x4; -const getDepth = scope => scope === null ? 0 : scope.depth !== -1 ? scope.depth : scope.depth = getDepth(scope.parent) + scope.depthDelta; -const newScope = parent => ({ - parent, - depthDelta: 0, - depth: -1, - isNative: false -}); -const CARET = '^'.charCodeAt(); -const QUOTE = '"'.charCodeAt(); -const OPEN_PAREN = '('.charCodeAt(); -const CLOSE_PAREN = ')'.charCodeAt(); -const PIPE = '|'.charCodeAt(); -const reNotQuote = sticky('[^"]*'); -const reSeqOp = sticky('&&?|\\|\\|'); -const reUnimportant = sticky('(\\d*>&)|[^\\s"$&()<>^|]+|(\\s+)'); - -const quoteForSh = (text, forceQuote) => text.length ? forceQuote || shMetaChars.test(text) ? `'${text.replace(/'/g, "'\\''")}'`.replace(/^(?:'')+(?!$)/, '').replace(/\\'''/g, "\\'") : text : "''"; -const shMetaChars = /[\t\n\r "#$&'()*;<>?\\`|~]/; - -/** - * To get a Formatter, call `Formatter.for`. - * - * To create a new Formatter, pass an object to `Formatter.declare`. - * - * To set the global default Formatter, assign to `Formatter.default`. - * - * @class - * @property {Formatter} default - The Formatter to be used when no platform - * is provided—for example, when creating strings with `sh`. - * @ignore - */ -function Formatter() {} -Object.assign(Formatter, -/** @lends Formatter */ -{ - /** - * Gets a Formatter that has been declared for the provided platform, or - * the base `'sh'` formatter if there is no Formatter specific to this - * platform, or the Formatter for the current platform if no specific platform - * is provided. - */ - for(platform) { - return platform == null ? Formatter.default || (Formatter.default = Formatter.for(process.platform)) : Formatter._registry.get(platform) || Formatter._registry.get('sh'); - }, - /** - * Creates a new Formatter or mutates the properties on an existing - * Formatter. The `platform` key on the provided properties object determines - * when the Formatter is retrieved. - */ - declare(props) { - const platform = props && props.platform || 'sh'; - const existingFormatter = Formatter._registry.get(platform); - const formatter = Object.assign(existingFormatter || new Formatter(), props); - formatter.emptyString === void 0 && (formatter.emptyString = formatter.quote('', true)); - existingFormatter || Formatter._registry.set(formatter.platform, formatter); - }, - _registry: new Map(), - prototype: { - platform: 'sh', - quote: quoteForSh, - metaChars: shMetaChars, - hasExtraMetaChars: false, - statementSeparator: ';', - createContext() { - return defaultContext; - } - } -}); -const defaultContext = { - at() {} -}; -Formatter.declare(); -Formatter.declare({ - platform: 'win32', - quote(text, forceQuote, opts) { - const caretDepth = opts ? (opts.depth || 0) + (opts.isArgument && !opts.isNative ? 1 : 0) : 0; - return quoteForCmd(text, forceQuote, caretDepth); - }, - metaChars: cmdMetaChars, - hasExtraMetaChars: true, - statementSeparator: '&', - createContext(root) { - const context = new this.Context(); - root[preformatSymbol](context); - return context; - }, - Context: Win32Context -}); - -const isObject = any => any === Object(any); -function memoize(f) { - const cache = new WeakMap(); - return arg => { - let result = cache.get(arg); - if (result === void 0) { - result = f(arg); - cache.set(arg, result); - } - return result; - }; -} - -/** - * Represents a contiguous span of text that may or must be quoted. The contents - * may already contain quoted segments, which will always be quoted. If unquoted - * segments also require quoting, the entire span will be quoted together. - * @ignore - */ -class ShellStringText { - constructor(contents, untested) { - this.contents = contents; - this.untested = untested; - } - [formatSymbol](formatter, context) { - const unformattedContents = this.contents; - const length = unformattedContents.length; - const contents = new Array(length); - for (let i = 0; i < length; i++) { - const c = unformattedContents[i]; - contents[i] = isObject(c) && formatSymbol in c ? c[formatSymbol](formatter) : c; - } - for (let unquoted = true, i = 0; i < length; i++) { - const content = contents[i]; - if (content === null) { - unquoted = !unquoted; - } else { - if (unquoted && (formatter.hasExtraMetaChars || this.untested && this.untested.has(i)) && formatter.metaChars.test(content)) { - return formatter.quote(contents.join(''), false, context.at(this)); - } - } - } - const parts = []; - for (let quoted = null, i = 0; i < length; i++) { - const content = contents[i]; - if (content === null) { - quoted = quoted ? (parts.push(formatter.quote(quoted.join(''), true, context.at(this))), null) : []; - } else { - (quoted || parts).push(content); - } - } - const result = parts.join(''); - return result.length ? result : formatter.emptyString; - } - [preformatSymbol](context) { - context.mark(this); - } -} - -/** - * Represents a contiguous span of text that will not be quoted. - * @ignore - */ -class ShellStringUnquoted { - constructor(value) { - this.value = value; - } - [formatSymbol]() { - return this.value; - } - [preformatSymbol](context) { - context.read(this.value); - } -} - -/** - * Represents a semicolon... or an ampersand, on Windows. - * @ignore - */ -const shellStringSemicolon = { - [formatSymbol](formatter) { - return formatter.statementSeparator; - }, - [preformatSymbol](context) { - context.seq(); - } -}; - -const PLACEHOLDER = {}; -const parse = memoize(templateSpans => { - // These are the token types our DSL can recognize. Their values won't escape - // this function. - const TOKEN_TEXT = 0; - const TOKEN_QUOTE = 1; - const TOKEN_SEMI = 2; - const TOKEN_UNQUOTED = 3; - const TOKEN_SPACE = 4; - const TOKEN_REDIRECT = 5; - const result = []; - let placeholderCount = 0; - let prefix = null; - let onlyPrefixOnce = false; - let contents = []; - let quote = 0; - const lastSpan = templateSpans.length - 1; - for (let spanIndex = 0; spanIndex <= lastSpan; spanIndex++) { - const templateSpan = templateSpans[spanIndex]; - const posEnd = templateSpan.length; - let tokenStart = 0; - if (spanIndex) { - placeholderCount++; - contents.push(PLACEHOLDER); - } - // For each span, we first do a recognizing pass in which we use regular - // expressions to identify the positions of tokens in the text, and then - // a second pass that actually splits the text into the minimum number of - // substrings necessary. - const recognized = []; // [type1, index1, type2, index2...] - let firstWordBreak = -1; - let lastWordBreak = -1; - { - let pos = 0, - match; - while (pos < posEnd) { - if (quote) { - if (match = execFrom(quote === CHAR_SQUO ? reQuotation1 : reQuotation2, templateSpan, pos)) { - recognized.push(TOKEN_TEXT, pos); - pos += match[0].length; - } - if (pos < posEnd) { - recognized.push(TOKEN_QUOTE, pos++); - quote = 0; - } - } else { - if (match = execFrom(reRedirectOrSpace, templateSpan, pos)) { - firstWordBreak < 0 && (firstWordBreak = pos); - lastWordBreak = pos; - recognized.push(match[1] ? TOKEN_REDIRECT : TOKEN_SPACE, pos); - pos += match[0].length; - } - if (match = execFrom(reText, templateSpan, pos)) { - const setBreaks = match[1] != null; - setBreaks && firstWordBreak < 0 && (firstWordBreak = pos); - recognized.push(setBreaks ? TOKEN_UNQUOTED : TOKEN_TEXT, pos); - pos += match[0].length; - setBreaks && (lastWordBreak = pos); - } - const char = templateSpan.charCodeAt(pos); - if (char === CHAR_SEMI) { - firstWordBreak < 0 && (firstWordBreak = pos); - recognized.push(TOKEN_SEMI, pos++); - lastWordBreak = pos; - } else if (char === CHAR_SQUO || char === CHAR_DQUO) { - recognized.push(TOKEN_QUOTE, pos++); - quote = char; - } - } - } - } - // Word breaks are only important if they separate words with placeholders, - // so we can ignore the first/last break if this is the first/last span. - spanIndex === 0 && (firstWordBreak = -1); - spanIndex === lastSpan && (lastWordBreak = posEnd); - // Here begins the second pass mentioned above. This loop runs one more - // iteration than there are tokens in recognized, because it handles tokens - // on a one-iteration delay; hence the i <= iEnd instead of i < iEnd. - const iEnd = recognized.length; - for (let i = 0, type = -1; i <= iEnd; i += 2) { - let typeNext = -1, - pos; - if (i === iEnd) { - pos = posEnd; - } else { - typeNext = recognized[i]; - pos = recognized[i + 1]; - // If the next token is space or redirect, but there's another word - // break in this span, then we can handle that token the same way we - // would handle unquoted text because it isn't being attached to a - // placeholder. - typeNext >= TOKEN_SPACE && pos !== lastWordBreak && (typeNext = TOKEN_UNQUOTED); - } - const breakHere = pos === firstWordBreak || pos === lastWordBreak; - if (pos && (breakHere || typeNext !== type)) { - let value = type === TOKEN_QUOTE ? null : type === TOKEN_SEMI ? shellStringSemicolon : templateSpan.substring(tokenStart, pos); - if (type >= TOKEN_SEMI) { - // This branch handles semicolons, unquoted text, spaces, and - // redirects. shellStringSemicolon is already a formatSymbol object; - // the rest need to be wrapped. - type === TOKEN_SEMI || (value = new ShellStringUnquoted(value)); - // We don't need to check placeholderCount here like we do below; - // that's only relevant during the first word break of the span, and - // because this iteration of the loop is processing the token that - // was checked for breaks in the previous iteration, it will have - // already been handled. For the same reason, prefix is guaranteed to - // be null. - if (contents.length) { - result.push(new ShellStringText(contents, null)); - contents = []; - } - // Only spaces and redirects become prefixes, but not if they've been - // rewritten to unquoted above. - if (type >= TOKEN_SPACE) { - prefix = value; - onlyPrefixOnce = type === TOKEN_SPACE; - } else { - result.push(value); - } - } else { - contents.push(value); - } - tokenStart = pos; - } - if (breakHere) { - if (placeholderCount) { - result.push({ - contents, - placeholderCount, - prefix, - onlyPrefixOnce - }); - } else { - // There's no prefix to handle in this branch; a prefix prior to this - // span would mean placeholderCount > 0, and a prefix in this span - // can't be created because spaces and redirects get rewritten to - // unquoted before the last word break. - contents.length && result.push(new ShellStringText(contents, null)); - } - placeholderCount = 0; - prefix = null; - onlyPrefixOnce = false; - contents = []; - } - type = typeNext; - } - } - if (quote) { - throw new SyntaxError(`String is missing a ${String.fromCharCode(quote)} character`); - } - return result; -}); -const CHAR_SEMI = ';'.charCodeAt(); -const CHAR_SQUO = "'".charCodeAt(); -const CHAR_DQUO = '"'.charCodeAt(); -const reQuotation1 = sticky("[^']+"); -const reQuotation2 = sticky('[^"]+'); -const reText = sticky('[^\\s"#$&\'();<>\\\\`|]+|([#$&()\\\\`|]+)'); -const reRedirectOrSpace = sticky('(\\s*\\d*[<>]+\\s*)|\\s+'); - -class BitSet { - constructor() { - this.vector = new Int32Array(1); - } - has(n) { - return (this.vector[n >>> 5] & 1 << n) !== 0; - } - add(n) { - const i = n >>> 5, - requiredLength = i + 1; - let vector = this.vector, - _vector = vector, - length = _vector.length; - if (requiredLength > length) { - while (requiredLength > (length *= 2)); - const oldValues = vector; - vector = new Int32Array(length); - vector.set(oldValues); - this.vector = vector; - } - vector[i] |= 1 << n; - } -} - -function evaluate(template, values) { - values = values.map(toStringishArray); - const children = []; - let valuesStart = 0; - for (let i = 0, iMax = template.length; i < iMax; i++) { - const word = template[i]; - if (formatSymbol in word) { - children.push(word); - continue; - } - const contents = word.contents, - placeholderCount = word.placeholderCount, - prefix = word.prefix, - onlyPrefixOnce = word.onlyPrefixOnce; - const kMax = contents.length; - const valuesEnd = valuesStart + placeholderCount; - const tuples = cartesianProduct(values, valuesStart, valuesEnd); - valuesStart = valuesEnd; - for (let j = 0, jMax = tuples.length; j < jMax; j++) { - const needSpace = j > 0; - const tuple = tuples[j]; - (needSpace || prefix) && children.push(needSpace && (onlyPrefixOnce || !prefix) ? unquotedSpace : prefix); - let interpolatedContents = []; - let untested = null; - let quoting = false; - let tupleIndex = 0; - for (let k = 0; k < kMax; k++) { - const content = contents[k]; - if (content === PLACEHOLDER) { - const value = tuple[tupleIndex++]; - if (quoting) { - interpolatedContents.push(value); - } else { - if (isObject(value) && formatSymbol in value) { - if (interpolatedContents.length) { - children.push(new ShellStringText(interpolatedContents, untested)); - interpolatedContents = []; - untested = null; - } - children.push(value); - } else { - (untested || (untested = new BitSet())).add(interpolatedContents.length); - interpolatedContents.push(value); - } - } - } else { - interpolatedContents.push(content); - content === null && (quoting = !quoting); - } - } - if (interpolatedContents.length) { - children.push(new ShellStringText(interpolatedContents, untested)); - } - } - } - return children; -} -const primToStringish = value => value == null ? '' + value : value; -function toStringishArray(value) { - let array; - switch (true) { - default: - if (isObject(value)) { - if (Array.isArray(value)) { - array = value; - break; - } - if (Symbol.iterator in value) { - array = Array.from(value); - break; - } - } - array = [value]; - } - return array.map(primToStringish); -} -function cartesianProduct(arrs, start, end) { - const size = end - start; - let resultLength = 1; - for (let i = start; i < end; i++) { - resultLength *= arrs[i].length; - } - if (resultLength > 1e6) { - throw new RangeError("Far too many elements to interpolate"); - } - const result = new Array(resultLength); - const indices = new Array(size).fill(0); - for (let i = 0; i < resultLength; i++) { - const value = result[i] = new Array(size); - for (let j = 0; j < size; j++) { - value[j] = arrs[j + start][indices[j]]; - } - for (let j = size - 1; j >= 0; j--) { - if (++indices[j] < arrs[j + start].length) break; - indices[j] = 0; - } - } - return result; -} -const unquotedSpace = new ShellStringUnquoted(' '); - -/** - * A ShellString represents a shell command after it has been interpolated, but - * before it has been formatted for a particular platform. ShellStrings are - * useful if you want to prepare a command for a different platform than the - * current one, for instance. - * - * To create a ShellString, use `ShellString.sh` the same way you would use - * top-level `sh`. - */ -class ShellString { - /** @hideconstructor */ - constructor(children) { - this.children = children; - } - /** - * `ShellString.sh` is a template tag just like `sh`; the only difference is - * that this function returns a ShellString which has not yet been formatted - * into a String. - * @returns {ShellString} - * @function sh - * @static - * @memberof ShellString - */ - static sh(templateSpans) { - for (var _len = arguments.length, values = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - values[_key - 1] = arguments[_key]; - } - return new ShellString(evaluate(parse(templateSpans), values)); - } - /** - * A method to format a ShellString into a regular String formatted for a - * particular platform. - * - * @param {String} [platform] a value that `process.platform` might take: - * `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - * When omitted, effectively the same as `process.platform`. - * @returns {String} - */ - toString(platform) { - return this[formatSymbol](Formatter.for(platform)); - } - [formatSymbol](formatter) { - let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : formatter.createContext(this); - return this.children.map(child => child[formatSymbol](formatter, context)).join(''); - } - [preformatSymbol](context) { - const children = this.children; - for (let i = 0, iMax = children.length; i < iMax; i++) { - const child = children[i]; - if (preformatSymbol in child) { - child[preformatSymbol](context); - } - } - } -} - -/** - * A Windows-specific version of {@link quoteForShell}. - * @param {String} text to be quoted - * @param {Boolean} [forceQuote] whether to always add quotes even if the string - * is already safe. Defaults to `false`. - */ - -/** - * A Unix-specific version of {@link quoteForShell}. - * @param {String} text to be quoted - * @param {Boolean} [forceQuote] whether to always add quotes even if the string - * is already safe. Defaults to `false`. - */ - -/** - * Quotes a string for injecting into a shell command. - * - * This function is exposed for some hypothetical case when the `sh` DSL simply - * won't do; `sh` is expected to be the more convenient option almost always. - * Compare: - * - * ```javascript - * console.log('cmd' + args.map(a => ' ' + quoteForShell(a)).join('')); - * console.log(sh`cmd ${args}`); // same as above - * - * console.log('cmd' + args.map(a => ' ' + quoteForShell(a, true)).join('')); - * console.log(sh`cmd "${args}"`); // same as above - * ``` - * - * Additionally, on Windows, `sh` checks the entire command string for pipes, - * which subtly change how arguments need to be quoted. If your commands may - * involve pipes, you are strongly encouraged to use `sh` and not try to roll - * your own with `quoteForShell`. - * - * @param {String} text to be quoted - * @param {Boolean} [forceQuote] whether to always add quotes even if the string - * is already safe. Defaults to `false`. - * @param {String} [platform] a value that `process.platform` might take: - * `'win32'`, `'linux'`, etc.; determines how the string is to be formatted. - * When omitted, effectively the same as `process.platform`. - * - * @returns {String} a string that is safe for the current (or specified) - * platform. - */ -function quoteForShell(text, forceQuote, platform) { - return Formatter.for(platform).quote(text, forceQuote); -} - -/** - * A string template tag for safely constructing cross-platform shell commands. - * - * An `sh` template is not actually treated as a literal string to be - * interpolated; instead, it is a tiny DSL designed to make working with shell - * strings safe, simple, and straightforward. To get started quickly, see the - * examples below. {@link #the-sh-dsl More detailed documentation} is available - * further down. - * - * @name sh - * @example - * const title = '"this" & "that"'; - * sh`script --title=${title}`; // => "script '--title=\"this\" & \"that\"'" - * // Note: these examples show results for non-Windows platforms. - * // On Windows, the above would instead be - * // 'script ^^^"--title=\\^^^"this\\^^^" ^^^& \\^^^"that\\^^^"^^^"'. - * - * const names = ['file1', 'file 2']; - * sh`rimraf ${names}.txt`; // => "rimraf file1.txt 'file 2.txt'" - * - * const cmd1 = ['cat', 'file 1.txt', 'file 2.txt']; - * const cmd2 = ['use-input', '-abc']; - * sh`${cmd1}|${cmd2}`; // => "cat 'file 1.txt' 'file 2.txt'|use-input -abc" - * - * @returns {String} - a string formatted for the platform Node is currently - * running on. - */ -const sh = function () { - return ShellString.sh.apply(ShellString, arguments).toString(); -}; - -/** - * This function permits raw strings to be interpolated into a `sh` template. - * - * **IMPORTANT**: If you're using Puka due to security concerns, make sure you - * don't pass any untrusted content to `unquoted`. This may be obvious, but - * stray punctuation in an `unquoted` section can compromise the safety of the - * entire shell command. - * - * @param value - any value (it will be treated as a string) - * - * @example - * const both = true; - * sh`foo ${unquoted(both ? '&&' : '||')} bar`; // => 'foo && bar' - */ -const unquoted = value => new ShellStringUnquoted(value); - -exports.Formatter = Formatter; -exports.ShellString = ShellString; -exports.ShellStringText = ShellStringText; -exports.ShellStringUnquoted = ShellStringUnquoted; -exports.quoteForCmd = quoteForCmd; -exports.quoteForSh = quoteForSh; -exports.quoteForShell = quoteForShell; -exports.sh = sh; -exports.shellStringSemicolon = shellStringSemicolon; -exports.formatSymbol = formatSymbol; -exports.preformatSymbol = preformatSymbol; -exports.unquoted = unquoted; diff --git a/deps/npm/node_modules/puka/package.json b/deps/npm/node_modules/puka/package.json deleted file mode 100644 index 41798dc2493b85..00000000000000 --- a/deps/npm/node_modules/puka/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "puka", - "version": "1.0.1", - "description": "A cross-platform library for safely passing strings through shells", - "keywords": [ - "args", - "arguments", - "cmd", - "command", - "command-line", - "cross-platform", - "escape", - "escaping", - "exec", - "linux", - "mac", - "macos", - "osx", - "quote", - "quoting", - "sh", - "shell", - "spawn", - "unix", - "win", - "win32", - "windows" - ], - "homepage": "https://gitlab.com/rhendric/puka", - "bugs": "https://gitlab.com/rhendric/puka/issues", - "license": "MIT", - "author": "Ryan Hendrickson <ryan.hendrickson@alum.mit.edu>", - "repository": "gitlab:rhendric/puka", - "dependencies": {}, - "engines": { - "node": ">=4" - } -} \ No newline at end of file diff --git a/deps/npm/node_modules/semver/package.json b/deps/npm/node_modules/semver/package.json index d4043d38a13529..4e1154195a5f17 100644 --- a/deps/npm/node_modules/semver/package.json +++ b/deps/npm/node_modules/semver/package.json @@ -1,6 +1,6 @@ { "name": "semver", - "version": "7.3.4", + "version": "7.3.5", "description": "The semantic version parser used by npm.", "main": "index.js", "scripts": { diff --git a/deps/npm/node_modules/semver/ranges/subset.js b/deps/npm/node_modules/semver/ranges/subset.js index bb7d15fe2696bb..532fd1364ce754 100644 --- a/deps/npm/node_modules/semver/ranges/subset.js +++ b/deps/npm/node_modules/semver/ranges/subset.js @@ -1,20 +1,28 @@ const Range = require('../classes/range.js') -const { ANY } = require('../classes/comparator.js') +const Comparator = require('../classes/comparator.js') +const { ANY } = Comparator const satisfies = require('../functions/satisfies.js') const compare = require('../functions/compare.js') // Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff: -// - Every simple range `r1, r2, ...` is a subset of some `R1, R2, ...` +// - Every simple range `r1, r2, ...` is a null set, OR +// - Every simple range `r1, r2, ...` which is not a null set is a subset of +// some `R1, R2, ...` // // Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff: // - If c is only the ANY comparator // - If C is only the ANY comparator, return true -// - Else return false +// - Else if in prerelease mode, return false +// - else replace c with `[>=0.0.0]` +// - If C is only the ANY comparator +// - if in prerelease mode, return true +// - else replace C with `[>=0.0.0]` // - Let EQ be the set of = comparators in c // - If EQ is more than one, return true (null set) // - Let GT be the highest > or >= comparator in c // - Let LT be the lowest < or <= comparator in c // - If GT and LT, and GT.semver > LT.semver, return true (null set) +// - If any C is a = range, and GT or LT are set, return false // - If EQ // - If GT, and EQ does not satisfy GT, return true (null set) // - If LT, and EQ does not satisfy LT, return true (null set) @@ -23,13 +31,16 @@ const compare = require('../functions/compare.js') // - If GT // - If GT.semver is lower than any > or >= comp in C, return false // - If GT is >=, and GT.semver does not satisfy every C, return false +// - If GT.semver has a prerelease, and not in prerelease mode +// - If no C has a prerelease and the GT.semver tuple, return false // - If LT // - If LT.semver is greater than any < or <= comp in C, return false // - If LT is <=, and LT.semver does not satisfy every C, return false -// - If any C is a = range, and GT or LT are set, return false +// - If GT.semver has a prerelease, and not in prerelease mode +// - If no C has a prerelease and the LT.semver tuple, return false // - Else return true -const subset = (sub, dom, options) => { +const subset = (sub, dom, options = {}) => { if (sub === dom) return true @@ -58,8 +69,21 @@ const simpleSubset = (sub, dom, options) => { if (sub === dom) return true - if (sub.length === 1 && sub[0].semver === ANY) - return dom.length === 1 && dom[0].semver === ANY + if (sub.length === 1 && sub[0].semver === ANY) { + if (dom.length === 1 && dom[0].semver === ANY) + return true + else if (options.includePrerelease) + sub = [ new Comparator('>=0.0.0-0') ] + else + sub = [ new Comparator('>=0.0.0') ] + } + + if (dom.length === 1 && dom[0].semver === ANY) { + if (options.includePrerelease) + return true + else + dom = [ new Comparator('>=0.0.0') ] + } const eqSet = new Set() let gt, lt @@ -102,10 +126,32 @@ const simpleSubset = (sub, dom, options) => { let higher, lower let hasDomLT, hasDomGT + // if the subset has a prerelease, we need a comparator in the superset + // with the same tuple and a prerelease, or it's not a subset + let needDomLTPre = lt && + !options.includePrerelease && + lt.semver.prerelease.length ? lt.semver : false + let needDomGTPre = gt && + !options.includePrerelease && + gt.semver.prerelease.length ? gt.semver : false + // exception: <1.2.3-0 is the same as <1.2.3 + if (needDomLTPre && needDomLTPre.prerelease.length === 1 && + lt.operator === '<' && needDomLTPre.prerelease[0] === 0) { + needDomLTPre = false + } + for (const c of dom) { hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>=' hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<=' if (gt) { + if (needDomGTPre) { + if (c.semver.prerelease && c.semver.prerelease.length && + c.semver.major === needDomGTPre.major && + c.semver.minor === needDomGTPre.minor && + c.semver.patch === needDomGTPre.patch) { + needDomGTPre = false + } + } if (c.operator === '>' || c.operator === '>=') { higher = higherGT(gt, c, options) if (higher === c && higher !== gt) @@ -114,6 +160,14 @@ const simpleSubset = (sub, dom, options) => { return false } if (lt) { + if (needDomLTPre) { + if (c.semver.prerelease && c.semver.prerelease.length && + c.semver.major === needDomLTPre.major && + c.semver.minor === needDomLTPre.minor && + c.semver.patch === needDomLTPre.patch) { + needDomLTPre = false + } + } if (c.operator === '<' || c.operator === '<=') { lower = lowerLT(lt, c, options) if (lower === c && lower !== lt) @@ -134,6 +188,12 @@ const simpleSubset = (sub, dom, options) => { if (lt && hasDomGT && !gt && gtltComp !== 0) return false + // we needed a prerelease range in a specific tuple, but didn't get one + // then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0, + // because it includes prereleases in the 1.2.3 tuple + if (needDomGTPre || needDomLTPre) + return false + return true } diff --git a/deps/npm/package.json b/deps/npm/package.json index afd3b36cb08f7b..c4b10a831b610b 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "7.6.3", + "version": "7.7.0", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -42,16 +42,16 @@ "./package.json": "./package.json" }, "dependencies": { - "@npmcli/arborist": "^2.2.8", + "@npmcli/arborist": "^2.2.9", "@npmcli/ci-detect": "^1.2.0", - "@npmcli/config": "^1.2.9", - "@npmcli/run-script": "^1.8.3", + "@npmcli/config": "^2.0.0", + "@npmcli/run-script": "^1.8.4", "abbrev": "~1.1.1", "ansicolors": "~0.3.2", "ansistyles": "~0.1.3", "archy": "~1.0.0", "byte-size": "^7.0.1", - "cacache": "^15.0.5", + "cacache": "^15.0.6", "chalk": "^4.1.0", "chownr": "^2.0.0", "cli-columns": "^3.1.2", @@ -59,7 +59,7 @@ "columnify": "~1.5.4", "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "hosted-git-info": "^3.0.8", + "hosted-git-info": "^4.0.1", "ini": "^2.0.0", "init-package-json": "^2.0.2", "is-cidr": "^4.0.2", @@ -74,7 +74,7 @@ "libnpmpublish": "^4.0.0", "libnpmsearch": "^3.1.0", "libnpmteam": "^2.0.2", - "libnpmversion": "^1.0.11", + "libnpmversion": "^1.0.12", "make-fetch-happen": "^8.0.14", "minipass": "^3.1.3", "minipass-pipeline": "^1.2.4", @@ -84,14 +84,14 @@ "node-gyp": "^7.1.2", "nopt": "^5.0.0", "npm-audit-report": "^2.1.4", - "npm-package-arg": "^8.1.1", - "npm-pick-manifest": "^6.1.0", + "npm-package-arg": "^8.1.2", + "npm-pick-manifest": "^6.1.1", "npm-profile": "^5.0.2", "npm-registry-fetch": "^9.0.0", "npm-user-validate": "^1.0.1", "npmlog": "~4.1.2", "opener": "^1.5.2", - "pacote": "^11.3.0", + "pacote": "^11.3.1", "parse-conflict-json": "^1.1.1", "qrcode-terminal": "^0.12.0", "read": "~1.0.7", @@ -99,7 +99,7 @@ "read-package-json-fast": "^2.0.2", "readdir-scoped-modules": "^1.1.0", "rimraf": "^3.0.2", - "semver": "^7.3.4", + "semver": "^7.3.5", "ssri": "^8.0.1", "tar": "^6.1.0", "text-table": "~0.2.0", @@ -180,17 +180,17 @@ "devDependencies": { "@mdx-js/mdx": "^1.6.22", "cmark-gfm": "^0.8.5", - "eslint": "^7.21.0", + "eslint": "^7.22.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^5.0.0", - "jsdom": "^16.4.0", + "jsdom": "^16.5.1", "licensee": "^8.1.0", "marked-man": "^0.7.0", "require-inject": "^1.4.4", "tap": "^14.11.0", - "yaml": "^1.10.0" + "yaml": "^1.10.2" }, "scripts": { "dumpconf": "env | grep npm | sort | uniq", diff --git a/deps/npm/tap-snapshots/test-lib-dist-tag.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-dist-tag.js-TAP.test.js index 89a87ae64e137c..06936795bcf035 100644 --- a/deps/npm/tap-snapshots/test-lib-dist-tag.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-dist-tag.js-TAP.test.js @@ -8,6 +8,8 @@ exports[`test/lib/dist-tag.js TAP add missing args > should exit usage error message 1`] = ` npm dist-tag +Modify package distribution tags + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> @@ -21,6 +23,8 @@ Run "npm help dist-tag" for more info exports[`test/lib/dist-tag.js TAP add missing pkg name > should exit usage error message 1`] = ` npm dist-tag +Modify package distribution tags + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> @@ -43,6 +47,8 @@ dist-tag add 1.0.0 to @scoped/another@7.7.7 exports[`test/lib/dist-tag.js TAP borked cmd usage > should show usage error 1`] = ` npm dist-tag +Modify package distribution tags + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> @@ -62,6 +68,8 @@ latest: 1.0.0 exports[`test/lib/dist-tag.js TAP ls on missing name in current package > should throw usage error message 1`] = ` npm dist-tag +Modify package distribution tags + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> @@ -111,6 +119,8 @@ exports[`test/lib/dist-tag.js TAP remove existing tag > should return success ms exports[`test/lib/dist-tag.js TAP remove missing pkg name > should exit usage error message 1`] = ` npm dist-tag +Modify package distribution tags + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> diff --git a/deps/npm/tap-snapshots/test-lib-publish.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-publish.js-TAP.test.js index 97ce7a7733384e..172ed5b29f478e 100644 --- a/deps/npm/tap-snapshots/test-lib-publish.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-publish.js-TAP.test.js @@ -8,8 +8,13 @@ exports[`test/lib/publish.js TAP shows usage with wrong set of arguments > should print usage 1`] = ` npm publish +Publish a package + Usage: -npm publish [<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run] +npm publish [<folder>] + +Options: +[--tag <tag>] [--access <restricted|public>] [--dry-run] Run "npm help publish" for more info ` diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js new file mode 100644 index 00000000000000..ad506ae8e3585c --- /dev/null +++ b/deps/npm/tap-snapshots/test-lib-utils-config-definition.js-TAP.test.js @@ -0,0 +1,264 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/utils/config/definition.js TAP basic definition > description of deprecated thing 1`] = ` +#### \`deprecated\` + +* Default: A number bigger than 1 +* Type: An expression of a numeric quantity using numerals +* DEPRECATED: do not use this + +it should not be used ever + +not even once. +` + +exports[`test/lib/utils/config/definition.js TAP basic definition > human-readable description 1`] = ` +#### \`key\` + +* Default: "some default value" +* Type: Number or String + +just a test thingie +` + +exports[`test/lib/utils/config/definition.js TAP long description > cols=-1 1`] = ` +#### \`walden\` + +* Default: true +* Type: Boolean + +WHEN I WROTE the +following pages, +or rather the +bulk of them, I +lived alone, in +the woods, a +mile from any +neighbor, in a +house which I +had built +myself, on the +shore of Walden +Pond, in +Concord, +Massachusetts, +and earned my +living by the +labor of my +hands only. I +lived there two +years and two +months. At +present I am a +sojourner in +civilized life +again. + +I should not +obtrude my +affairs so much +on the notice of +my readers if +very particular +inquiries had +not been made by +my townsmen +concerning my +mode of life, +which some would +call +impertinent, +though they do +not appear to me +at all +impertinent, +but, considering +the +circumstances, +very natural and +pertinent. + +\`\`\` +this.is('a', { + code: 'sample', +}) + +with (multiple) { + blocks() +} +\`\`\` + +` + +exports[`test/lib/utils/config/definition.js TAP long description > cols=0 1`] = ` +#### \`walden\` + +* Default: true +* Type: Boolean + +WHEN I WROTE the +following pages, +or rather the +bulk of them, I +lived alone, in +the woods, a +mile from any +neighbor, in a +house which I +had built +myself, on the +shore of Walden +Pond, in +Concord, +Massachusetts, +and earned my +living by the +labor of my +hands only. I +lived there two +years and two +months. At +present I am a +sojourner in +civilized life +again. + +I should not +obtrude my +affairs so much +on the notice of +my readers if +very particular +inquiries had +not been made by +my townsmen +concerning my +mode of life, +which some would +call +impertinent, +though they do +not appear to me +at all +impertinent, +but, considering +the +circumstances, +very natural and +pertinent. + +\`\`\` +this.is('a', { + code: 'sample', +}) + +with (multiple) { + blocks() +} +\`\`\` + +` + +exports[`test/lib/utils/config/definition.js TAP long description > cols=40 1`] = ` +#### \`walden\` + +* Default: true +* Type: Boolean + +WHEN I WROTE the following pages, or +rather the bulk of them, I lived +alone, in the woods, a mile from any +neighbor, in a house which I had +built myself, on the shore of Walden +Pond, in Concord, Massachusetts, and +earned my living by the labor of my +hands only. I lived there two years +and two months. At present I am a +sojourner in civilized life again. + +I should not obtrude my affairs so +much on the notice of my readers if +very particular inquiries had not +been made by my townsmen concerning +my mode of life, which some would +call impertinent, though they do not +appear to me at all impertinent, +but, considering the circumstances, +very natural and pertinent. + +\`\`\` +this.is('a', { + code: 'sample', +}) + +with (multiple) { + blocks() +} +\`\`\` + +` + +exports[`test/lib/utils/config/definition.js TAP long description > cols=9000 1`] = ` +#### \`walden\` + +* Default: true +* Type: Boolean + +WHEN I WROTE the following pages, or rather the bulk of them, I lived alone, +in the woods, a mile from any neighbor, in a house which I had built myself, +on the shore of Walden Pond, in Concord, Massachusetts, and earned my living +by the labor of my hands only. I lived there two years and two months. At +present I am a sojourner in civilized life again. + +I should not obtrude my affairs so much on the notice of my readers if very +particular inquiries had not been made by my townsmen concerning my mode of +life, which some would call impertinent, though they do not appear to me at +all impertinent, but, considering the circumstances, very natural and +pertinent. + +\`\`\` +this.is('a', { + code: 'sample', +}) + +with (multiple) { + blocks() +} +\`\`\` + +` + +exports[`test/lib/utils/config/definition.js TAP long description > cols=NaN 1`] = ` +#### \`walden\` + +* Default: true +* Type: Boolean + +WHEN I WROTE the following pages, or rather the bulk of them, I lived alone, +in the woods, a mile from any neighbor, in a house which I had built myself, +on the shore of Walden Pond, in Concord, Massachusetts, and earned my living +by the labor of my hands only. I lived there two years and two months. At +present I am a sojourner in civilized life again. + +I should not obtrude my affairs so much on the notice of my readers if very +particular inquiries had not been made by my townsmen concerning my mode of +life, which some would call impertinent, though they do not appear to me at +all impertinent, but, considering the circumstances, very natural and +pertinent. + +\`\`\` +this.is('a', { + code: 'sample', +}) + +with (multiple) { + blocks() +} +\`\`\` + +` diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js new file mode 100644 index 00000000000000..2ed810da8a2840 --- /dev/null +++ b/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js @@ -0,0 +1,156 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/utils/config/definitions.js TAP > all config keys 1`] = ` +Array [ + "_auth", + "access", + "all", + "allow-same-version", + "also", + "always-auth", + "audit", + "audit-level", + "auth-type", + "before", + "bin-links", + "browser", + "ca", + "cache", + "cache-max", + "cache-min", + "cafile", + "call", + "cert", + "ci-name", + "cidr", + "color", + "commit-hooks", + "depth", + "description", + "diff", + "diff-ignore-all-space", + "diff-name-only", + "diff-no-prefix", + "diff-dst-prefix", + "diff-src-prefix", + "diff-text", + "diff-unified", + "dry-run", + "editor", + "engine-strict", + "fetch-retries", + "fetch-retry-factor", + "fetch-retry-maxtimeout", + "fetch-retry-mintimeout", + "fetch-timeout", + "force", + "foreground-scripts", + "format-package-lock", + "fund", + "git", + "git-tag-version", + "global", + "global-style", + "globalconfig", + "heading", + "https-proxy", + "if-present", + "ignore-scripts", + "include", + "include-staged", + "init-author-email", + "init-author-name", + "init-author-url", + "init-license", + "init-module", + "init-version", + "init.author.email", + "init.author.name", + "init.author.url", + "init.license", + "init.module", + "init.version", + "json", + "key", + "legacy-bundling", + "legacy-peer-deps", + "link", + "local-address", + "loglevel", + "logs-max", + "long", + "maxsockets", + "message", + "node-options", + "node-version", + "noproxy", + "npm-version", + "offline", + "omit", + "only", + "optional", + "otp", + "package", + "package-lock", + "package-lock-only", + "parseable", + "prefer-offline", + "prefer-online", + "prefix", + "preid", + "production", + "progress", + "proxy", + "read-only", + "rebuild-bundle", + "registry", + "save", + "save-bundle", + "save-dev", + "save-exact", + "save-optional", + "save-peer", + "save-prefix", + "save-prod", + "scope", + "script-shell", + "searchexclude", + "searchlimit", + "searchopts", + "searchstaleness", + "shell", + "shrinkwrap", + "sign-git-commit", + "sign-git-tag", + "sso-poll-frequency", + "sso-type", + "strict-peer-deps", + "strict-ssl", + "tag", + "tag-version-prefix", + "timing", + "tmp", + "umask", + "unicode", + "update-notifier", + "usage", + "user-agent", + "userconfig", + "version", + "versions", + "viewer", + "which", + "workspace", + "workspaces", + "yes", +] +` + +exports[`test/lib/utils/config/definitions.js TAP > all config keys that are shared to flatOptions 1`] = ` +Array [] +` diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js new file mode 100644 index 00000000000000..8323e793e075f8 --- /dev/null +++ b/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js @@ -0,0 +1,1377 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/utils/config/describe-all.js TAP > must match snapshot 1`] = ` +#### \`_auth\` + +* Default: null +* Type: null or String + +A basic-auth string to use when authenticating against the npm registry. + +Warning: This should generally not be set via a command-line option. It is +safer to use a registry-provided authentication bearer token stored in the +~/.npmrc file by running \`npm login\`. + +#### \`access\` + +* Default: 'restricted' for scoped packages, 'public' for unscoped packages +* Type: null, "restricted", or "public" + +When publishing scoped packages, the access level defaults to \`restricted\`. +If you want your scoped package to be publicly viewable (and installable) +set \`--access=public\`. The only valid values for \`access\` are \`public\` and +\`restricted\`. Unscoped packages _always_ have an access level of \`public\`. + +#### \`all\` + +* Default: false +* Type: Boolean + +When running \`npm outdated\` and \`npm ls\`, setting \`--all\` will show all +outdated or installed packages, rather than only those directly depended +upon by the current project. + +#### \`allow-same-version\` + +* Default: false +* Type: Boolean + +Prevents throwing an error when \`npm version\` is used to set the new version +to the same value as the current version. + +#### \`always-auth\` + +* Default: false +* Type: Boolean + +Force npm to always require authentication when accessing the registry, even +for \`GET\` requests. + +#### \`audit\` + +* Default: true +* Type: Boolean + +When "true" submit audit reports alongside \`npm install\` runs to the default +registry and all registries configured for scopes. See the documentation for +[\`npm audit\`](/commands/npm-audit) for details on what is submitted. + +#### \`audit-level\` + +* Default: null +* Type: "low", "moderate", "high", "critical", "none", or null + +The minimum level of vulnerability for \`npm audit\` to exit with a non-zero +exit code. + +#### \`before\` + +* Default: null +* Type: null or Date + +If passed to \`npm install\`, will rebuild the npm tree such that only +versions that were available **on or before** the \`--before\` time get +installed. If there's no versions available for the current set of direct +dependencies, the command will error. + +If the requested version is a \`dist-tag\` and the given tag does not pass the +\`--before\` filter, the most recent version less than or equal to that tag +will be used. For example, \`foo@latest\` might install \`foo@1.2\` even though +\`latest\` is \`2.0\`. + +#### \`bin-links\` + +* Default: true +* Type: Boolean + +Tells npm to create symlinks (or \`.cmd\` shims on Windows) for package +executables. + +Set to false to have it not do this. This can be used to work around the +fact that some file systems don't support symlinks, even on ostensibly Unix +systems. + +#### \`browser\` + +* Default: OS X: \`"open"\`, Windows: \`"start"\`, Others: \`"xdg-open"\` +* Type: null, Boolean, or String + +The browser that is called by npm commands to open websites. + +Set to \`false\` to suppress browser behavior and instead print urls to +terminal. + +Set to \`true\` to use default system URL opener. + +#### \`ca\` + +* Default: null +* Type: null or String (can be set multiple times) + +The Certificate Authority signing certificate that is trusted for SSL +connections to the registry. Values should be in PEM format (Windows calls +it "Base-64 encoded X.509 (.CER)") with newlines replaced by the string +"\\n". For example: + +\`\`\`ini +ca="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----" +\`\`\` + +Set to \`null\` to only allow "known" registrars, or to a specific CA cert to +trust only that specific signing authority. + +Multiple CAs can be trusted by specifying an array of certificates: + +\`\`\`ini +ca[]="..." +ca[]="..." +\`\`\` + +See also the \`strict-ssl\` config. + +#### \`cache\` + +* Default: Windows: \`%LocalAppData%\\npm-cache\`, Posix: \`~/.npm\` +* Type: Path + +The location of npm's cache directory. See [\`npm +cache\`](/commands/npm-cache) + +#### \`cafile\` + +* Default: null +* Type: Path + +A path to a file containing one or multiple Certificate Authority signing +certificates. Similar to the \`ca\` setting, but allows for multiple CA's, as +well as for the CA information to be stored in a file on disk. + +#### \`call\` + +* Default: "" +* Type: String + +Optional companion option for \`npm exec\`, \`npx\` that allows for specifying a +custom command to be run along with the installed packages. + +\`\`\`bash +npm exec --package yo --package generator-node --call "yo node" +\`\`\` + + +#### \`cert\` + +* Default: null +* Type: null or String + +A client certificate to pass when accessing the registry. Values should be +in PEM format (Windows calls it "Base-64 encoded X.509 (.CER)") with +newlines replaced by the string "\\n". For example: + +\`\`\`ini +cert="-----BEGIN CERTIFICATE-----\\nXXXX\\nXXXX\\n-----END CERTIFICATE-----" +\`\`\` + +It is _not_ the path to a certificate file (and there is no "certfile" +option). + +#### \`ci-name\` + +* Default: The name of the current CI system, or \`null\` when not on a known CI + platform. +* Type: null or String + +The name of a continuous integration system. If not set explicitly, npm will +detect the current CI environment using the +[\`@npmcli/ci-detect\`](http://npm.im/@npmcli/ci-detect) module. + +#### \`cidr\` + +* Default: null +* Type: null or String (can be set multiple times) + +This is a list of CIDR address to be used when configuring limited access +tokens with the \`npm token create\` command. + +#### \`color\` + +* Default: true unless the NO_COLOR environ is set to something other than '0' +* Type: "always" or Boolean + +If false, never shows colors. If \`"always"\` then always shows colors. If +true, then only prints color codes for tty file descriptors. + +#### \`commit-hooks\` + +* Default: true +* Type: Boolean + +Run git commit hooks when using the \`npm version\` command. + +#### \`depth\` + +* Default: \`Infinity\` if \`--all\` is set, otherwise \`1\` +* Type: null or Number + +The depth to go when recursing packages for \`npm ls\`. + +If not set, \`npm ls\` will show only the immediate dependencies of the root +project. If \`--all\` is set, then npm will show all dependencies by default. + +#### \`description\` + +* Default: true +* Type: Boolean + +Show the description in \`npm search\` + +#### \`diff\` + +* Default: +* Type: String (can be set multiple times) + +Define arguments to compare in \`npm diff\`. + +#### \`diff-dst-prefix\` + +* Default: "b/" +* Type: String + +Destination prefix to be used in \`npm diff\` output. + +#### \`diff-ignore-all-space\` + +* Default: false +* Type: Boolean + +Ignore whitespace when comparing lines in \`npm diff\`. + +#### \`diff-name-only\` + +* Default: false +* Type: Boolean + +Prints only filenames when using \`npm diff\`. + +#### \`diff-no-prefix\` + +* Default: false +* Type: Boolean + +Do not show any source or destination prefix in \`npm diff\` output. + +Note: this causes \`npm diff\` to ignore the \`--diff-src-prefix\` and +\`--diff-dst-prefix\` configs. + +#### \`diff-src-prefix\` + +* Default: "a/" +* Type: String + +Source prefix to be used in \`npm diff\` output. + +#### \`diff-text\` + +* Default: false +* Type: Boolean + +Treat all files as text in \`npm diff\`. + +#### \`diff-unified\` + +* Default: 3 +* Type: Number + +The number of lines of context to print in \`npm diff\`. + +#### \`dry-run\` + +* Default: false +* Type: Boolean + +Indicates that you don't want npm to make any changes and that it should +only report what it would have done. This can be passed into any of the +commands that modify your local installation, eg, \`install\`, \`update\`, +\`dedupe\`, \`uninstall\`, as well as \`pack\` and \`publish\`. + +Note: This is NOT honored by other network related commands, eg \`dist-tags\`, +\`owner\`, etc. + +#### \`editor\` + +* Default: The EDITOR or VISUAL environment variables, or 'notepad.exe' on + Windows, or 'vim' on Unix systems +* Type: String + +The command to run for \`npm edit\` and \`npm config edit\`. + +#### \`engine-strict\` + +* Default: false +* Type: Boolean + +If set to true, then npm will stubbornly refuse to install (or even consider +installing) any package that claims to not be compatible with the current +Node.js version. + +This can be overridden by setting the \`--force\` flag. + +#### \`fetch-retries\` + +* Default: 2 +* Type: Number + +The "retries" config for the \`retry\` module to use when fetching packages +from the registry. + +npm will retry idempotent read requests to the registry in the case of +network failures or 5xx HTTP errors. + +#### \`fetch-retry-factor\` + +* Default: 10 +* Type: Number + +The "factor" config for the \`retry\` module to use when fetching packages. + +#### \`fetch-retry-maxtimeout\` + +* Default: 60000 (1 minute) +* Type: Number + +The "maxTimeout" config for the \`retry\` module to use when fetching +packages. + +#### \`fetch-retry-mintimeout\` + +* Default: 10000 (10 seconds) +* Type: Number + +The "minTimeout" config for the \`retry\` module to use when fetching +packages. + +#### \`fetch-timeout\` + +* Default: 300000 (5 minutes) +* Type: Number + +The maximum amount of time to wait for HTTP requests to complete. + +#### \`force\` + +* Default: false +* Type: Boolean + +Removes various protections against unfortunate side effects, common +mistakes, unnecessary performance degradation, and malicious input. + +* Allow clobbering non-npm files in global installs. +* Allow the \`npm version\` command to work on an unclean git repository. +* Allow deleting the cache folder with \`npm cache clean\`. +* Allow installing packages that have an \`engines\` declaration requiring a + different version of npm. +* Allow installing packages that have an \`engines\` declaration requiring a + different version of \`node\`, even if \`--engine-strict\` is enabled. +* Allow \`npm audit fix\` to install modules outside your stated dependency + range (including SemVer-major changes). +* Allow unpublishing all versions of a published package. +* Allow conflicting peerDependencies to be installed in the root project. + +If you don't have a clear idea of what you want to do, it is strongly +recommended that you do not use this option! + +#### \`foreground-scripts\` + +* Default: false +* Type: Boolean + +Run all build scripts (ie, \`preinstall\`, \`install\`, and \`postinstall\`) +scripts for installed packages in the foreground process, sharing standard +input, output, and error with the main npm process. + +Note that this will generally make installs run slower, and be much noisier, +but can be useful for debugging. + +#### \`format-package-lock\` + +* Default: true +* Type: Boolean + +Format \`package-lock.json\` or \`npm-shrinkwrap.json\` as a human readable +file. + +#### \`fund\` + +* Default: true +* Type: Boolean + +When "true" displays the message at the end of each \`npm install\` +acknowledging the number of dependencies looking for funding. See [\`npm +fund\`](/commands/npm-fund) for details. + +#### \`git\` + +* Default: "git" +* Type: String + +The command to use for git commands. If git is installed on the computer, +but is not in the \`PATH\`, then set this to the full path to the git binary. + +#### \`git-tag-version\` + +* Default: true +* Type: Boolean + +Tag the commit when using the \`npm version\` command. + +#### \`global\` + +* Default: false +* Type: Boolean + +Operates in "global" mode, so that packages are installed into the \`prefix\` +folder instead of the current working directory. See +[folders](/configuring-npm/folders) for more on the differences in behavior. + +* packages are installed into the \`{prefix}/lib/node_modules\` folder, instead + of the current working directory. +* bin files are linked to \`{prefix}/bin\` +* man pages are linked to \`{prefix}/share/man\` + +#### \`global-style\` + +* Default: false +* Type: Boolean + +Causes npm to install the package into your local \`node_modules\` folder with +the same layout it uses with the global \`node_modules\` folder. Only your +direct dependencies will show in \`node_modules\` and everything they depend +on will be flattened in their \`node_modules\` folders. This obviously will +eliminate some deduping. If used with \`legacy-bundling\`, \`legacy-bundling\` +will be preferred. + +#### \`globalconfig\` + +* Default: The global --prefix setting plus 'etc/npmrc'. For example, + '/usr/local/etc/npmrc' +* Type: Path + +The config file to read for global config options. + +#### \`heading\` + +* Default: "npm" +* Type: String + +The string that starts all the debugging log output. + +#### \`https-proxy\` + +* Default: null +* Type: null or URL + +A proxy to use for outgoing https requests. If the \`HTTPS_PROXY\` or +\`https_proxy\` or \`HTTP_PROXY\` or \`http_proxy\` environment variables are set, +proxy settings will be honored by the underlying \`make-fetch-happen\` +library. + +#### \`if-present\` + +* Default: false +* Type: Boolean + +If true, npm will not exit with an error code when \`run-script\` is invoked +for a script that isn't defined in the \`scripts\` section of \`package.json\`. +This option can be used when it's desirable to optionally run a script when +it's present and fail if the script fails. This is useful, for example, when +running scripts that may only apply for some builds in an otherwise generic +CI setup. + +#### \`ignore-scripts\` + +* Default: false +* Type: Boolean + +If true, npm does not run scripts specified in package.json files. + +#### \`include\` + +* Default: +* Type: "prod", "dev", "optional", or "peer" (can be set multiple times) + +Option that allows for defining which types of dependencies to install. + +This is the inverse of \`--omit=<type>\`. + +Dependency types specified in \`--include\` will not be omitted, regardless of +the order in which omit/include are specified on the command-line. + +#### \`include-staged\` + +* Default: false +* Type: Boolean + +Allow installing "staged" published packages, as defined by [npm RFC PR +#92](https://github.com/npm/rfcs/pull/92). + +This is experimental, and not implemented by the npm public registry. + +#### \`init-author-email\` + +* Default: "" +* Type: String + +The value \`npm init\` should use by default for the package author's email. + +#### \`init-author-name\` + +* Default: "" +* Type: String + +The value \`npm init\` should use by default for the package author's name. + +#### \`init-author-url\` + +* Default: "" +* Type: "" or URL + +The value \`npm init\` should use by default for the package author's +homepage. + +#### \`init-license\` + +* Default: "ISC" +* Type: String + +The value \`npm init\` should use by default for the package license. + +#### \`init-module\` + +* Default: "~/.npm-init.js" +* Type: Path + +A module that will be loaded by the \`npm init\` command. See the +documentation for the +[init-package-json](https://github.com/npm/init-package-json) module for +more information, or [npm init](/commands/npm-init). + +#### \`init-version\` + +* Default: "1.0.0" +* Type: SemVer string + +The value that \`npm init\` should use by default for the package version +number, if not already set in package.json. + +#### \`json\` + +* Default: false +* Type: Boolean + +Whether or not to output JSON data, rather than the normal output. + +This feature is currently experimental, and the output data structures for +many commands is either not implemented in JSON yet, or subject to change. +Only the output from \`npm ls --json\` and \`npm search --json\` are currently +valid. + +#### \`key\` + +* Default: null +* Type: null or String + +A client key to pass when accessing the registry. Values should be in PEM +format with newlines replaced by the string "\\n". For example: + +\`\`\`ini +key="-----BEGIN PRIVATE KEY-----\\nXXXX\\nXXXX\\n-----END PRIVATE KEY-----" +\`\`\` + +It is _not_ the path to a key file (and there is no "keyfile" option). + +#### \`legacy-bundling\` + +* Default: false +* Type: Boolean + +Causes npm to install the package such that versions of npm prior to 1.4, +such as the one included with node 0.8, can install the package. This +eliminates all automatic deduping. If used with \`global-style\` this option +will be preferred. + +#### \`legacy-peer-deps\` + +* Default: false +* Type: Boolean + +Causes npm to completely ignore \`peerDependencies\` when building a package +tree, as in npm versions 3 through 6. + +If a package cannot be installed because of overly strict \`peerDependencies\` +that collide, it provides a way to move forward resolving the situation. + +This differs from \`--omit=peer\`, in that \`--omit=peer\` will avoid unpacking +\`peerDependencies\` on disk, but will still design a tree such that +\`peerDependencies\` _could_ be unpacked in a correct place. + +Use of \`legacy-peer-deps\` is not recommended, as it will not enforce the +\`peerDependencies\` contract that meta-dependencies may rely on. + +#### \`link\` + +* Default: false +* Type: Boolean + +If true, then local installs will link if there is a suitable globally +installed package. + +Note that this means that local installs can cause things to be installed +into the global space at the same time. The link is only done if one of the +two conditions are met: + +* The package is not already installed globally, or +* the globally installed version is identical to the version that is being + installed locally. + +#### \`local-address\` + +* Default: null +* Type: IP Address + +The IP address of the local interface to use when making connections to the +npm registry. Must be IPv4 in versions of Node prior to 0.12. + +#### \`loglevel\` + +* Default: "notice" +* Type: "silent", "error", "warn", "notice", "http", "timing", "info", + "verbose", or "silly" + +What level of logs to report. On failure, *all* logs are written to +\`npm-debug.log\` in the current working directory. + +Any logs of a higher level than the setting are shown. The default is +"notice". + +#### \`logs-max\` + +* Default: 10 +* Type: Number + +The maximum number of log files to store. + +#### \`long\` + +* Default: false +* Type: Boolean + +Show extended information in \`npm ls\` and \`npm search\`. + +#### \`maxsockets\` + +* Default: Infinity +* Type: Number + +The maximum number of connections to use per origin (protocol/host/port +combination). + +#### \`message\` + +* Default: "%s" +* Type: String + +Commit message which is used by \`npm version\` when creating version commit. + +Any "%s" in the message will be replaced with the version number. + +#### \`node-options\` + +* Default: null +* Type: null or String + +Options to pass through to Node.js via the \`NODE_OPTIONS\` environment +variable. This does not impact how npm itself is executed but it does impact +how lifecycle scripts are called. + +#### \`node-version\` + +* Default: Node.js \`process.version\` value +* Type: SemVer string + +The node version to use when checking a package's \`engines\` setting. + +#### \`noproxy\` + +* Default: The value of the NO_PROXY environment variable +* Type: String (can be set multiple times) + +Domain extensions that should bypass any proxies. + +Also accepts a comma-delimited string. + +#### \`npm-version\` + +* Default: Output of \`npm --version\` +* Type: SemVer string + +The npm version to use when checking a package's \`engines\` setting. + +#### \`offline\` + +* Default: false +* Type: Boolean + +Force offline mode: no network requests will be done during install. To +allow the CLI to fill in missing cache data, see \`--prefer-offline\`. + +#### \`omit\` + +* Default: 'dev' if the NODE_ENV environment variable is set to 'production', + otherwise empty. +* Type: "dev", "optional", or "peer" (can be set multiple times) + +Dependency types to omit from the installation tree on disk. + +Note that these dependencies _are_ still resolved and added to the +\`package-lock.json\` or \`npm-shrinkwrap.json\` file. They are just not +physically installed on disk. + +If a package type appears in both the \`--include\` and \`--omit\` lists, then +it will be included. + +If the resulting omit list includes \`'dev'\`, then the \`NODE_ENV\` environment +variable will be set to \`'production'\` for all lifecycle scripts. + +#### \`otp\` + +* Default: null +* Type: null or String + +This is a one-time password from a two-factor authenticator. It's needed +when publishing or changing package permissions with \`npm access\`. + +If not set, and a registry response fails with a challenge for a one-time +password, npm will prompt on the command line for one. + +#### \`package\` + +* Default: +* Type: String (can be set multiple times) + +The package to install for [\`npm exec\`](/commands/npm-exec) + +#### \`package-lock\` + +* Default: true +* Type: Boolean + +If set to false, then ignore \`package-lock.json\` files when installing. This +will also prevent _writing_ \`package-lock.json\` if \`save\` is true. + +When package package-locks are disabled, automatic pruning of extraneous +modules will also be disabled. To remove extraneous modules with +package-locks disabled use \`npm prune\`. + +#### \`package-lock-only\` + +* Default: false +* Type: Boolean + +If set to true, it will update only the \`package-lock.json\`, instead of +checking \`node_modules\` and downloading dependencies. + +#### \`parseable\` + +* Default: false +* Type: Boolean + +Output parseable results from commands that write to standard output. For +\`npm search\`, this will be tab-separated table format. + +#### \`prefer-offline\` + +* Default: false +* Type: Boolean + +If true, staleness checks for cached data will be bypassed, but missing data +will be requested from the server. To force full offline mode, use +\`--offline\`. + +#### \`prefer-online\` + +* Default: false +* Type: Boolean + +If true, staleness checks for cached data will be forced, making the CLI +look for updates immediately even for fresh package data. + +#### \`prefix\` + +* Default: In global mode, the folder where the node executable is installed. + In local mode, the nearest parent folder containing either a package.json + file or a node_modules folder. +* Type: Path + +The location to install global items. If set on the command line, then it +forces non-global commands to run in the specified folder. + +#### \`preid\` + +* Default: "" +* Type: String + +The "prerelease identifier" to use as a prefix for the "prerelease" part of +a semver. Like the \`rc\` in \`1.2.0-rc.8\`. + +#### \`progress\` + +* Default: \`true\` unless running in a known CI system +* Type: Boolean + +When set to \`true\`, npm will display a progress bar during time intensive +operations, if \`process.stderr\` is a TTY. + +Set to \`false\` to suppress the progress bar. + +#### \`proxy\` + +* Default: null +* Type: null, false, or URL + +A proxy to use for outgoing http requests. If the \`HTTP_PROXY\` or +\`http_proxy\` environment variables are set, proxy settings will be honored +by the underlying \`request\` library. + +#### \`read-only\` + +* Default: false +* Type: Boolean + +This is used to mark a token as unable to publish when configuring limited +access tokens with the \`npm token create\` command. + +#### \`rebuild-bundle\` + +* Default: true +* Type: Boolean + +Rebuild bundled dependencies after installation. + +#### \`registry\` + +* Default: "https://registry.npmjs.org/" +* Type: URL + +The base URL of the npm registry. + +#### \`save\` + +* Default: true +* Type: Boolean + +Save installed packages to a package.json file as dependencies. + +When used with the \`npm rm\` command, removes the dependency from +package.json. + +#### \`save-bundle\` + +* Default: false +* Type: Boolean + +If a package would be saved at install time by the use of \`--save\`, +\`--save-dev\`, or \`--save-optional\`, then also put it in the +\`bundleDependencies\` list. + +Ignore if \`--save-peer\` is set, since peerDependencies cannot be bundled. + +#### \`save-dev\` + +* Default: false +* Type: Boolean + +Save installed packages to a package.json file as \`devDependencies\`. + +#### \`save-exact\` + +* Default: false +* Type: Boolean + +Dependencies saved to package.json will be configured with an exact version +rather than using npm's default semver range operator. + +#### \`save-optional\` + +* Default: false +* Type: Boolean + +Save installed packages to a package.json file as \`optionalDependencies\`. + +#### \`save-peer\` + +* Default: false +* Type: Boolean + +Save installed packages. to a package.json file as \`peerDependencies\` + +#### \`save-prefix\` + +* Default: "^" +* Type: String + +Configure how versions of packages installed to a package.json file via +\`--save\` or \`--save-dev\` get prefixed. + +For example if a package has version \`1.2.3\`, by default its version is set +to \`^1.2.3\` which allows minor upgrades for that package, but after \`npm +config set save-prefix='~'\` it would be set to \`~1.2.3\` which only allows +patch upgrades. + +#### \`save-prod\` + +* Default: false +* Type: Boolean + +Save installed packages into \`dependencies\` specifically. This is useful if +a package already exists in \`devDependencies\` or \`optionalDependencies\`, but +you want to move it to be a non-optional production dependency. + +This is the default behavior if \`--save\` is true, and neither \`--save-dev\` +or \`--save-optional\` are true. + +#### \`scope\` + +* Default: the scope of the current project, if any, or "" +* Type: String + +Associate an operation with a scope for a scoped registry. + +Useful when logging in to a private registry for the first time: + +\`\`\`bash +npm login --scope=@mycorp --registry=https://registry.mycorp.com +\`\`\` + +This will cause \`@mycorp\` to be mapped to the registry for future +installation of packages specified according to the pattern +\`@mycorp/package\`. + +#### \`script-shell\` + +* Default: '/bin/sh' on POSIX systems, 'cmd.exe' on Windows +* Type: null or String + +The shell to use for scripts run with the \`npm run\` command. + +#### \`searchexclude\` + +* Default: "" +* Type: String + +Space-separated options that limit the results from search. + +#### \`searchlimit\` + +* Default: 20 +* Type: Number + +Number of items to limit search results to. Will not apply at all to legacy +searches. + +#### \`searchopts\` + +* Default: "" +* Type: String + +Space-separated options that are always passed to search. + +#### \`searchstaleness\` + +* Default: 900 +* Type: Number + +The age of the cache, in seconds, before another registry request is made if +using legacy search endpoint. + +#### \`shell\` + +* Default: SHELL environment variable, or "bash" on Posix, or "cmd.exe" on + Windows +* Type: String + +The shell to run for the \`npm explore\` command. + +#### \`sign-git-commit\` + +* Default: false +* Type: Boolean + +If set to true, then the \`npm version\` command will commit the new package +version using \`-S\` to add a signature. + +Note that git requires you to have set up GPG keys in your git configs for +this to work properly. + +#### \`sign-git-tag\` + +* Default: false +* Type: Boolean + +If set to true, then the \`npm version\` command will tag the version using +\`-s\` to add a signature. + +Note that git requires you to have set up GPG keys in your git configs for +this to work properly. + +#### \`strict-peer-deps\` + +* Default: false +* Type: Boolean + +If set to \`true\`, and \`--legacy-peer-deps\` is not set, then _any_ +conflicting \`peerDependencies\` will be treated as an install failure, even +if npm could reasonably guess the appropriate resolution based on non-peer +dependency relationships. + +By default, conflicting \`peerDependencies\` deep in the dependency graph will +be resolved using the nearest non-peer dependency specification, even if +doing so will result in some packages receiving a peer dependency outside +the range set in their package's \`peerDependencies\` object. + +When such and override is performed, a warning is printed, explaining the +conflict and the packages involved. If \`--strict-peer-deps\` is set, then +this warning is treated as a failure. + +#### \`strict-ssl\` + +* Default: true +* Type: Boolean + +Whether or not to do SSL key validation when making requests to the registry +via https. + +See also the \`ca\` config. + +#### \`tag\` + +* Default: "latest" +* Type: String + +If you ask npm to install a package and don't tell it a specific version, +then it will install the specified tag. + +Also the tag that is added to the package@version specified by the \`npm tag\` +command, if no explicit tag is given. + +#### \`tag-version-prefix\` + +* Default: "v" +* Type: String + +If set, alters the prefix used when tagging a new version when performing a +version increment using \`npm-version\`. To remove the prefix altogether, set +it to the empty string: \`""\`. + +Because other tools may rely on the convention that npm version tags look +like \`v1.0.0\`, _only use this property if it is absolutely necessary_. In +particular, use care when overriding this setting for public packages. + +#### \`timing\` + +* Default: false +* Type: Boolean + +If true, writes an \`npm-debug\` log to \`_logs\` and timing information to +\`_timing.json\`, both in your cache, even if the command completes +successfully. \`_timing.json\` is a newline delimited list of JSON objects. + +You can quickly view it with this [json](https://npm.im/json) command line: +\`npm exec -- json -g < ~/.npm/_timing.json\`. + +#### \`umask\` + +* Default: 0 +* Type: Octal numeric string in range 0000..0777 (0..511) + +The "umask" value to use when setting the file creation mode on files and +folders. + +Folders and executables are given a mode which is \`0o777\` masked against +this value. Other files are given a mode which is \`0o666\` masked against +this value. + +Note that the underlying system will _also_ apply its own umask value to +files and folders that are created, and npm does not circumvent this, but +rather adds the \`--umask\` config to it. + +Thus, the effective default umask value on most POSIX systems is 0o22, +meaning that folders and executables are created with a mode of 0o755 and +other files are created with a mode of 0o644. + +#### \`unicode\` + +* Default: false on windows, true on mac/unix systems with a unicode locale, + as defined by the LC_ALL, LC_CTYPE, or LANG environment variables. +* Type: Boolean + +When set to true, npm uses unicode characters in the tree output. When +false, it uses ascii characters instead of unicode glyphs. + +#### \`update-notifier\` + +* Default: true +* Type: Boolean + +Set to false to suppress the update notification when using an older version +of npm than the latest. + +#### \`usage\` + +* Default: false +* Type: Boolean + +Show short usage output about the command specified. + +#### \`user-agent\` + +* Default: "npm/{npm-version} node/{node-version} {platform} {arch} {ci}" +* Type: String + +Sets the User-Agent request header. The following fields are replaced with +their actual counterparts: + +* \`{npm-version}\` - The npm version in use +* \`{node-version}\` - The Node.js version in use +* \`{platform}\` - The value of \`process.platform\` +* \`{arch}\` - The value of \`process.arch\` +* \`{ci}\` - The value of the \`ci-name\` config, if set, prefixed with \`ci/\`, or + an empty string if \`ci-name\` is empty. + +#### \`userconfig\` + +* Default: "~/.npmrc" +* Type: Path + +The location of user-level configuration settings. + +This may be overridden by the \`npm_config_userconfig\` environment variable +or the \`--userconfig\` command line option, but may _not_ be overridden by +settings in the \`globalconfig\` file. + +#### \`version\` + +* Default: false +* Type: Boolean + +If true, output the npm version and exit successfully. + +Only relevant when specified explicitly on the command line. + +#### \`versions\` + +* Default: false +* Type: Boolean + +If true, output the npm version as well as node's \`process.versions\` map and +the version in the current working directory's \`package.json\` file if one +exists, and exit successfully. + +Only relevant when specified explicitly on the command line. + +#### \`viewer\` + +* Default: "man" on Posix, "browser" on Windows +* Type: String + +The program to use to view help content. + +Set to \`"browser"\` to view html help content in the default web browser. + +#### \`which\` + +* Default: null +* Type: null or Number + +If there are multiple funding sources, which 1-indexed source URL to open. + +#### \`workspace\` + +* Default: +* Type: String (can be set multiple times) + +Enable running a command in the context of the configured workspaces of the +current project while filtering by running only the workspaces defined by +this configuration option. + +Valid values for the \`workspace\` config are either: - Workspace names - Path +to a workspace directory - Path to a parent workspace directory (will result +to selecting all of the nested workspaces) + +#### \`workspaces\` + +* Default: false +* Type: Boolean + +Enable running a command in the context of **all** the configured +workspaces. + +#### \`yes\` + +* Default: null +* Type: null or Boolean + +Automatically answer "yes" to any prompts that npm might print on the +command line. + +#### \`also\` + +* Default: null +* Type: null, "dev", or "development" +* DEPRECATED: Please use --include=dev instead. + +When set to \`dev\` or \`development\`, this is an alias for \`--include=dev\`. + +#### \`auth-type\` + +* Default: "legacy" +* Type: "legacy", "sso", "saml", or "oauth" +* DEPRECATED: This method of SSO/SAML/OAuth is deprecated and will be removed + in a future version of npm in favor of web-based login. + +What authentication strategy to use with \`adduser\`/\`login\`. + +#### \`cache-max\` + +* Default: Infinity +* Type: Number +* DEPRECATED: This option has been deprecated in favor of \`--prefer-online\` + +\`--cache-max=0\` is an alias for \`--prefer-online\` + +#### \`cache-min\` + +* Default: 0 +* Type: Number +* DEPRECATED: This option has been deprecated in favor of \`--prefer-offline\`. + +\`--cache-min=9999 (or bigger)\` is an alias for \`--prefer-offline\`. + +#### \`init.author.email\` + +* Default: "" +* Type: String +* DEPRECATED: Use \`--init-author-email\` instead. + +Alias for \`--init-author-email\` + +#### \`init.author.name\` + +* Default: "" +* Type: String +* DEPRECATED: Use \`--init-author-name\` instead. + +Alias for \`--init-author-name\` + +#### \`init.author.url\` + +* Default: "" +* Type: "" or URL +* DEPRECATED: Use \`--init-author-url\` instead. + +Alias for \`--init-author-url\` + +#### \`init.license\` + +* Default: "ISC" +* Type: String +* DEPRECATED: Use \`--init-license\` instead. + +Alias for \`--init-license\` + +#### \`init.module\` + +* Default: "~/.npm-init.js" +* Type: Path +* DEPRECATED: Use \`--init-module\` instead. + +Alias for \`--init-module\` + +#### \`init.version\` + +* Default: "1.0.0" +* Type: SemVer string +* DEPRECATED: Use \`--init-version\` instead. + +Alias for \`--init-version\` + +#### \`only\` + +* Default: null +* Type: null, "prod", or "production" +* DEPRECATED: Use \`--omit=dev\` to omit dev dependencies from the install. + +When set to \`prod\` or \`production\`, this is an alias for \`--omit=dev\`. + +#### \`optional\` + +* Default: null +* Type: null or Boolean +* DEPRECATED: Use \`--omit=optional\` to exclude optional dependencies, or + \`--include=optional\` to include them. + +Default value does install optional deps unless otherwise omitted. + +Alias for --include=optional or --omit=optional + +#### \`production\` + +* Default: false +* Type: Boolean +* DEPRECATED: Use \`--omit=dev\` instead. + +Alias for \`--omit=dev\` + +#### \`shrinkwrap\` + +* Default: true +* Type: Boolean +* DEPRECATED: Use the --package-lock setting instead. + +Alias for --package-lock + +#### \`sso-poll-frequency\` + +* Default: 500 +* Type: Number +* DEPRECATED: The --auth-type method of SSO/SAML/OAuth will be removed in a + future version of npm in favor of web-based login. + +When used with SSO-enabled \`auth-type\`s, configures how regularly the +registry should be polled while the user is completing authentication. + +#### \`sso-type\` + +* Default: "oauth" +* Type: null, "oauth", or "saml" +* DEPRECATED: The --auth-type method of SSO/SAML/OAuth will be removed in a + future version of npm in favor of web-based login. + +If \`--auth-type=sso\`, the type of SSO type to use. + +#### \`tmp\` + +* Default: The value returned by the Node.js \`os.tmpdir()\` method + <https://nodejs.org/api/os.html#os_os_tmpdir> +* Type: Path +* DEPRECATED: This setting is no longer used. npm stores temporary files in a + special location in the cache, and they are managed by + [\`cacache\`](http://npm.im/cacache). + +Historically, the location where temporary files were stored. No longer +relevant. +` diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js new file mode 100644 index 00000000000000..1e5ca232452e02 --- /dev/null +++ b/deps/npm/tap-snapshots/test-lib-utils-config-index.js-TAP.test.js @@ -0,0 +1,133 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`test/lib/utils/config/index.js TAP > shorthands 1`] = ` +Object { + "?": Array [ + "--usage", + ], + "a": Array [ + "--all", + ], + "B": Array [ + "--save-bundle", + ], + "c": Array [ + "--call", + ], + "C": Array [ + "--prefix", + ], + "d": Array [ + "--loglevel", + "info", + ], + "D": Array [ + "--save-dev", + ], + "dd": Array [ + "--loglevel", + "verbose", + ], + "ddd": Array [ + "--loglevel", + "silly", + ], + "desc": Array [ + "--description", + ], + "E": Array [ + "--save-exact", + ], + "enjoy-by": Array [ + "--before", + ], + "f": Array [ + "--force", + ], + "g": Array [ + "--global", + ], + "h": Array [ + "--usage", + ], + "H": Array [ + "--usage", + ], + "help": Array [ + "--usage", + ], + "l": Array [ + "--long", + ], + "local": Array [ + "--no-global", + ], + "m": Array [ + "--message", + ], + "n": Array [ + "--no-yes", + ], + "no": Array [ + "--no-yes", + ], + "O": Array [ + "--save-optional", + ], + "p": Array [ + "--parseable", + ], + "P": Array [ + "--save-prod", + ], + "porcelain": Array [ + "--parseable", + ], + "q": Array [ + "--loglevel", + "warn", + ], + "quiet": Array [ + "--loglevel", + "warn", + ], + "readonly": Array [ + "--read-only", + ], + "reg": Array [ + "--registry", + ], + "s": Array [ + "--loglevel", + "silent", + ], + "S": Array [ + "--save", + ], + "silent": Array [ + "--loglevel", + "silent", + ], + "v": Array [ + "--version", + ], + "verbose": Array [ + "--loglevel", + "verbose", + ], + "w": Array [ + "--workspace", + ], + "ws": Array [ + "--workspaces", + ], + "y": Array [ + "--yes", + ], +} +` diff --git a/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js deleted file mode 100644 index 39927e600e1232..00000000000000 --- a/deps/npm/tap-snapshots/test-lib-utils-config.js-TAP.test.js +++ /dev/null @@ -1,1110 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/lib/utils/config.js TAP no working network interfaces, on windows > must match snapshot 1`] = ` -Object { - "defaults": Object { - "_auth": null, - "access": null, - "all": false, - "allow-same-version": false, - "also": null, - "always-auth": false, - "audit": true, - "audit-level": null, - "auth-type": "legacy", - "before": null, - "bin-links": true, - "browser": null, - "ca": null, - "cache": "{CACHE DIR} npm-cache", - "cache-lock-retries": 10, - "cache-lock-stale": 60000, - "cache-lock-wait": 10000, - "cache-max": null, - "cache-min": 10, - "cafile": null, - "call": "", - "cert": null, - "ci-name": null, - "cidr": null, - "color": true, - "commit-hooks": true, - "depth": null, - "description": true, - "dev": false, - "diff": Array [], - "diff-dst-prefix": "", - "diff-ignore-all-space": false, - "diff-name-only": false, - "diff-no-prefix": false, - "diff-src-prefix": "", - "diff-text": false, - "diff-unified": null, - "dry-run": false, - "editor": "vim", - "engine-strict": false, - "fetch-retries": 2, - "fetch-retry-factor": 10, - "fetch-retry-maxtimeout": 60000, - "fetch-retry-mintimeout": 10000, - "fetch-timeout": 300000, - "force": false, - "foreground-script": false, - "format-package-lock": true, - "fund": true, - "git": "git", - "git-tag-version": true, - "global": false, - "global-style": false, - "heading": "npm", - "https-proxy": null, - "if-present": false, - "ignore-prepublish": false, - "ignore-scripts": false, - "include": Array [], - "include-staged": false, - "init-author-email": "", - "init-author-name": "", - "init-author-url": "", - "init-license": "ISC", - "init-module": "~/.npm-init.js", - "init-version": "1.0.0", - "init.author.email": "", - "init.author.name": "", - "init.author.url": "", - "init.license": "ISC", - "init.module": "~/.npm-init.js", - "init.version": "1.0.0", - "json": false, - "key": null, - "legacy-bundling": false, - "legacy-peer-deps": false, - "link": false, - "local-address": undefined, - "loglevel": "notice", - "logs-max": 10, - "long": false, - "maxsockets": 50, - "message": "%s", - "node-options": null, - "node-version": "v14.8.0", - "noproxy": null, - "npm-version": "7.0.0", - "offline": false, - "omit": Array [], - "only": null, - "optional": true, - "otp": null, - "package": Array [], - "package-lock": true, - "package-lock-only": false, - "parseable": false, - "prefer-offline": false, - "prefer-online": false, - "preid": "", - "production": false, - "progress": true, - "proxy": null, - "read-only": false, - "rebuild-bundle": true, - "registry": "https://registry.npmjs.org/", - "rollback": true, - "save": true, - "save-bundle": false, - "save-dev": false, - "save-exact": false, - "save-optional": false, - "save-prefix": "^", - "save-prod": false, - "scope": "", - "script-shell": null, - "scripts-prepend-node-path": "warn-only", - "searchexclude": null, - "searchlimit": 20, - "searchopts": "", - "searchstaleness": 900, - "shell": "cmd.exe", - "shrinkwrap": true, - "sign-git-commit": false, - "sign-git-tag": false, - "sso-poll-frequency": 500, - "sso-type": "oauth", - "strict-peer-deps": false, - "strict-ssl": true, - "tag": "latest", - "tag-version-prefix": "v", - "timing": false, - "tmp": "/tmp", - "umask": 0, - "unicode": true, - "update-notifier": true, - "usage": false, - "user-agent": "npm/{npm-version} node/{node-version} {platform} {arch} {ci}", - "userconfig": "~/.npmrc", - "version": false, - "versions": false, - "viewer": "browser", - }, - "shorthands": Object { - "?": Array [ - "--usage", - ], - "a": Array [ - "--all", - ], - "B": Array [ - "--save-bundle", - ], - "c": Array [ - "--call", - ], - "C": Array [ - "--prefix", - ], - "d": Array [ - "--loglevel", - "info", - ], - "D": Array [ - "--save-dev", - ], - "dd": Array [ - "--loglevel", - "verbose", - ], - "ddd": Array [ - "--loglevel", - "silly", - ], - "desc": Array [ - "--description", - ], - "E": Array [ - "--save-exact", - ], - "enjoy-by": Array [ - "--before", - ], - "f": Array [ - "--force", - ], - "g": Array [ - "--global", - ], - "h": Array [ - "--usage", - ], - "H": Array [ - "--usage", - ], - "help": Array [ - "--usage", - ], - "l": Array [ - "--long", - ], - "local": Array [ - "--no-global", - ], - "m": Array [ - "--message", - ], - "n": Array [ - "--no-yes", - ], - "no-desc": Array [ - "--no-description", - ], - "no-reg": Array [ - "--no-registry", - ], - "noreg": Array [ - "--no-registry", - ], - "O": Array [ - "--save-optional", - ], - "p": Array [ - "--parseable", - ], - "P": Array [ - "--save-prod", - ], - "porcelain": Array [ - "--parseable", - ], - "q": Array [ - "--loglevel", - "warn", - ], - "quiet": Array [ - "--loglevel", - "warn", - ], - "readonly": Array [ - "--read-only", - ], - "reg": Array [ - "--registry", - ], - "s": Array [ - "--loglevel", - "silent", - ], - "S": Array [ - "--save", - ], - "silent": Array [ - "--loglevel", - "silent", - ], - "v": Array [ - "--version", - ], - "verbose": Array [ - "--loglevel", - "verbose", - ], - "y": Array [ - "--yes", - ], - }, - "types": Object { - "_auth": Array [ - null, - "{String TYPE}", - ], - "access": Array [ - null, - "restricted", - "public", - ], - "all": "{Boolean TYPE}", - "allow-same-version": "{Boolean TYPE}", - "also": Array [ - null, - "dev", - "development", - ], - "always-auth": "{Boolean TYPE}", - "audit": "{Boolean TYPE}", - "audit-level": Array [ - "low", - "moderate", - "high", - "critical", - "none", - null, - ], - "auth-type": Array [ - "legacy", - "sso", - "saml", - "oauth", - ], - "before": Array [ - null, - "{Date TYPE}", - ], - "bin-links": "{Boolean TYPE}", - "browser": Array [ - null, - "{Boolean TYPE}", - "{String TYPE}", - ], - "ca": Array [ - null, - "{String TYPE}", - "{Array TYPE}", - ], - "cache": "{PATH MODULE}", - "cache-lock-retries": "{Number TYPE}", - "cache-lock-stale": "{Number TYPE}", - "cache-lock-wait": "{Number TYPE}", - "cache-max": "{Number TYPE}", - "cache-min": "{Number TYPE}", - "cafile": "{PATH MODULE}", - "call": "{String TYPE}", - "cert": Array [ - null, - "{String TYPE}", - ], - "ci-name": Array [ - null, - "{String TYPE}", - ], - "cidr": Array [ - null, - "{String TYPE}", - "{Array TYPE}", - ], - "color": Array [ - "always", - "{Boolean TYPE}", - ], - "commit-hooks": "{Boolean TYPE}", - "depth": Array [ - null, - "{Number TYPE}", - ], - "description": "{Boolean TYPE}", - "dev": "{Boolean TYPE}", - "diff": Array [ - "{String TYPE}", - "{Array TYPE}", - ], - "diff-dst-prefix": "{String TYPE}", - "diff-ignore-all-space": "{Boolean TYPE}", - "diff-name-only": "{Boolean TYPE}", - "diff-no-prefix": "{Boolean TYPE}", - "diff-src-prefix": "{String TYPE}", - "diff-text": "{Boolean TYPE}", - "diff-unified": Array [ - null, - "{Number TYPE}", - ], - "dry-run": "{Boolean TYPE}", - "editor": "{String TYPE}", - "engine-strict": "{Boolean TYPE}", - "fetch-retries": "{Number TYPE}", - "fetch-retry-factor": "{Number TYPE}", - "fetch-retry-maxtimeout": "{Number TYPE}", - "fetch-retry-mintimeout": "{Number TYPE}", - "fetch-timeout": "{Number TYPE}", - "force": "{Boolean TYPE}", - "foreground-script": "{Boolean TYPE}", - "format-package-lock": "{Boolean TYPE}", - "fund": "{Boolean TYPE}", - "git": "{String TYPE}", - "git-tag-version": "{Boolean TYPE}", - "global": "{Boolean TYPE}", - "global-style": "{Boolean TYPE}", - "globalconfig": "{PATH MODULE}", - "heading": "{String TYPE}", - "https-proxy": Array [ - null, - "{URL MODULE}", - ], - "if-present": "{Boolean TYPE}", - "ignore-prepublish": "{Boolean TYPE}", - "ignore-scripts": "{Boolean TYPE}", - "include": Array [ - "{Array TYPE}", - "prod", - "dev", - "optional", - "peer", - ], - "include-staged": "{Boolean TYPE}", - "init-author-email": "{String TYPE}", - "init-author-name": "{String TYPE}", - "init-author-url": Array [ - "", - "{URL MODULE}", - ], - "init-license": "{String TYPE}", - "init-module": "{PATH MODULE}", - "init-version": "{SEMVER MODULE}", - "init.author.email": "{String TYPE}", - "init.author.name": "{String TYPE}", - "init.author.url": Array [ - "", - "{URL MODULE}", - ], - "init.license": "{String TYPE}", - "init.module": "{PATH MODULE}", - "init.version": "{SEMVER MODULE}", - "json": "{Boolean TYPE}", - "key": Array [ - null, - "{String TYPE}", - ], - "legacy-bundling": "{Boolean TYPE}", - "legacy-peer-deps": "{Boolean TYPE}", - "link": "{Boolean TYPE}", - "local-address": Array [ - undefined, - ], - "loglevel": Array [ - "silent", - "error", - "warn", - "notice", - "http", - "timing", - "info", - "verbose", - "silly", - ], - "logs-max": "{Number TYPE}", - "long": "{Boolean TYPE}", - "maxsockets": "{Number TYPE}", - "message": "{String TYPE}", - "node-options": Array [ - null, - "{String TYPE}", - ], - "node-version": Array [ - null, - "{SEMVER MODULE}", - ], - "noproxy": Array [ - null, - "{String TYPE}", - "{Array TYPE}", - ], - "npm-version": "{SEMVER MODULE}", - "offline": "{Boolean TYPE}", - "omit": Array [ - "{Array TYPE}", - "dev", - "optional", - "peer", - ], - "only": Array [ - null, - "dev", - "development", - "prod", - "production", - ], - "optional": "{Boolean TYPE}", - "otp": Array [ - null, - "{String TYPE}", - ], - "package": Array [ - "{String TYPE}", - "{Array TYPE}", - ], - "package-lock": "{Boolean TYPE}", - "package-lock-only": "{Boolean TYPE}", - "parseable": "{Boolean TYPE}", - "prefer-offline": "{Boolean TYPE}", - "prefer-online": "{Boolean TYPE}", - "prefix": "{PATH MODULE}", - "preid": "{String TYPE}", - "production": "{Boolean TYPE}", - "progress": "{Boolean TYPE}", - "proxy": Array [ - null, - false, - "{URL MODULE}", - ], - "read-only": "{Boolean TYPE}", - "rebuild-bundle": "{Boolean TYPE}", - "registry": Array [ - null, - "{URL MODULE}", - ], - "rollback": "{Boolean TYPE}", - "save": "{Boolean TYPE}", - "save-bundle": "{Boolean TYPE}", - "save-dev": "{Boolean TYPE}", - "save-exact": "{Boolean TYPE}", - "save-optional": "{Boolean TYPE}", - "save-prefix": "{String TYPE}", - "save-prod": "{Boolean TYPE}", - "scope": "{String TYPE}", - "script-shell": Array [ - null, - "{String TYPE}", - ], - "scripts-prepend-node-path": Array [ - "{Boolean TYPE}", - "auto", - "warn-only", - ], - "searchexclude": Array [ - null, - "{String TYPE}", - ], - "searchlimit": "{Number TYPE}", - "searchopts": "{String TYPE}", - "searchstaleness": "{Number TYPE}", - "shell": "{String TYPE}", - "shrinkwrap": "{Boolean TYPE}", - "sign-git-commit": "{Boolean TYPE}", - "sign-git-tag": "{Boolean TYPE}", - "sso-poll-frequency": "{Number TYPE}", - "sso-type": Array [ - null, - "oauth", - "saml", - ], - "strict-peer-deps": "{Boolean TYPE}", - "strict-ssl": "{Boolean TYPE}", - "tag": "{String TYPE}", - "tag-version-prefix": "{String TYPE}", - "timing": "{Boolean TYPE}", - "tmp": "{PATH MODULE}", - "umask": "{Umask TYPE}", - "unicode": "{Boolean TYPE}", - "update-notifier": "{Boolean TYPE}", - "usage": "{Boolean TYPE}", - "user-agent": "{String TYPE}", - "userconfig": "{PATH MODULE}", - "version": "{Boolean TYPE}", - "versions": "{Boolean TYPE}", - "viewer": "{String TYPE}", - }, -} -` - -exports[`test/lib/utils/config.js TAP working network interfaces, not windows > must match snapshot 1`] = ` -Object { - "defaults": Object { - "_auth": null, - "access": null, - "all": false, - "allow-same-version": false, - "also": null, - "always-auth": false, - "audit": true, - "audit-level": null, - "auth-type": "legacy", - "before": null, - "bin-links": true, - "browser": null, - "ca": null, - "cache": "{CACHE DIR} .npm", - "cache-lock-retries": 10, - "cache-lock-stale": 60000, - "cache-lock-wait": 10000, - "cache-max": null, - "cache-min": 10, - "cafile": null, - "call": "", - "cert": null, - "ci-name": null, - "cidr": null, - "color": true, - "commit-hooks": true, - "depth": null, - "description": true, - "dev": false, - "diff": Array [], - "diff-dst-prefix": "", - "diff-ignore-all-space": false, - "diff-name-only": false, - "diff-no-prefix": false, - "diff-src-prefix": "", - "diff-text": false, - "diff-unified": null, - "dry-run": false, - "editor": "vim", - "engine-strict": false, - "fetch-retries": 2, - "fetch-retry-factor": 10, - "fetch-retry-maxtimeout": 60000, - "fetch-retry-mintimeout": 10000, - "fetch-timeout": 300000, - "force": false, - "foreground-script": false, - "format-package-lock": true, - "fund": true, - "git": "git", - "git-tag-version": true, - "global": false, - "global-style": false, - "heading": "npm", - "https-proxy": null, - "if-present": false, - "ignore-prepublish": false, - "ignore-scripts": false, - "include": Array [], - "include-staged": false, - "init-author-email": "", - "init-author-name": "", - "init-author-url": "", - "init-license": "ISC", - "init-module": "~/.npm-init.js", - "init-version": "1.0.0", - "init.author.email": "", - "init.author.name": "", - "init.author.url": "", - "init.license": "ISC", - "init.module": "~/.npm-init.js", - "init.version": "1.0.0", - "json": false, - "key": null, - "legacy-bundling": false, - "legacy-peer-deps": false, - "link": false, - "local-address": undefined, - "loglevel": "notice", - "logs-max": 10, - "long": false, - "maxsockets": 50, - "message": "%s", - "node-options": null, - "node-version": "v14.8.0", - "noproxy": null, - "npm-version": "7.0.0", - "offline": false, - "omit": Array [], - "only": null, - "optional": true, - "otp": null, - "package": Array [], - "package-lock": true, - "package-lock-only": false, - "parseable": false, - "prefer-offline": false, - "prefer-online": false, - "preid": "", - "production": false, - "progress": true, - "proxy": null, - "read-only": false, - "rebuild-bundle": true, - "registry": "https://registry.npmjs.org/", - "rollback": true, - "save": true, - "save-bundle": false, - "save-dev": false, - "save-exact": false, - "save-optional": false, - "save-prefix": "^", - "save-prod": false, - "scope": "", - "script-shell": null, - "scripts-prepend-node-path": "warn-only", - "searchexclude": null, - "searchlimit": 20, - "searchopts": "", - "searchstaleness": 900, - "shell": "/usr/local/bin/bash", - "shrinkwrap": true, - "sign-git-commit": false, - "sign-git-tag": false, - "sso-poll-frequency": 500, - "sso-type": "oauth", - "strict-peer-deps": false, - "strict-ssl": true, - "tag": "latest", - "tag-version-prefix": "v", - "timing": false, - "tmp": "/tmp", - "umask": 0, - "unicode": true, - "update-notifier": true, - "usage": false, - "user-agent": "npm/{npm-version} node/{node-version} {platform} {arch} {ci}", - "userconfig": "~/.npmrc", - "version": false, - "versions": false, - "viewer": "man", - }, - "shorthands": Object { - "?": Array [ - "--usage", - ], - "a": Array [ - "--all", - ], - "B": Array [ - "--save-bundle", - ], - "c": Array [ - "--call", - ], - "C": Array [ - "--prefix", - ], - "d": Array [ - "--loglevel", - "info", - ], - "D": Array [ - "--save-dev", - ], - "dd": Array [ - "--loglevel", - "verbose", - ], - "ddd": Array [ - "--loglevel", - "silly", - ], - "desc": Array [ - "--description", - ], - "E": Array [ - "--save-exact", - ], - "enjoy-by": Array [ - "--before", - ], - "f": Array [ - "--force", - ], - "g": Array [ - "--global", - ], - "h": Array [ - "--usage", - ], - "H": Array [ - "--usage", - ], - "help": Array [ - "--usage", - ], - "l": Array [ - "--long", - ], - "local": Array [ - "--no-global", - ], - "m": Array [ - "--message", - ], - "n": Array [ - "--no-yes", - ], - "no-desc": Array [ - "--no-description", - ], - "no-reg": Array [ - "--no-registry", - ], - "noreg": Array [ - "--no-registry", - ], - "O": Array [ - "--save-optional", - ], - "p": Array [ - "--parseable", - ], - "P": Array [ - "--save-prod", - ], - "porcelain": Array [ - "--parseable", - ], - "q": Array [ - "--loglevel", - "warn", - ], - "quiet": Array [ - "--loglevel", - "warn", - ], - "readonly": Array [ - "--read-only", - ], - "reg": Array [ - "--registry", - ], - "s": Array [ - "--loglevel", - "silent", - ], - "S": Array [ - "--save", - ], - "silent": Array [ - "--loglevel", - "silent", - ], - "v": Array [ - "--version", - ], - "verbose": Array [ - "--loglevel", - "verbose", - ], - "y": Array [ - "--yes", - ], - }, - "types": Object { - "_auth": Array [ - null, - "{String TYPE}", - ], - "access": Array [ - null, - "restricted", - "public", - ], - "all": "{Boolean TYPE}", - "allow-same-version": "{Boolean TYPE}", - "also": Array [ - null, - "dev", - "development", - ], - "always-auth": "{Boolean TYPE}", - "audit": "{Boolean TYPE}", - "audit-level": Array [ - "low", - "moderate", - "high", - "critical", - "none", - null, - ], - "auth-type": Array [ - "legacy", - "sso", - "saml", - "oauth", - ], - "before": Array [ - null, - "{Date TYPE}", - ], - "bin-links": "{Boolean TYPE}", - "browser": Array [ - null, - "{Boolean TYPE}", - "{String TYPE}", - ], - "ca": Array [ - null, - "{String TYPE}", - "{Array TYPE}", - ], - "cache": "{PATH MODULE}", - "cache-lock-retries": "{Number TYPE}", - "cache-lock-stale": "{Number TYPE}", - "cache-lock-wait": "{Number TYPE}", - "cache-max": "{Number TYPE}", - "cache-min": "{Number TYPE}", - "cafile": "{PATH MODULE}", - "call": "{String TYPE}", - "cert": Array [ - null, - "{String TYPE}", - ], - "ci-name": Array [ - null, - "{String TYPE}", - ], - "cidr": Array [ - null, - "{String TYPE}", - "{Array TYPE}", - ], - "color": Array [ - "always", - "{Boolean TYPE}", - ], - "commit-hooks": "{Boolean TYPE}", - "depth": Array [ - null, - "{Number TYPE}", - ], - "description": "{Boolean TYPE}", - "dev": "{Boolean TYPE}", - "diff": Array [ - "{String TYPE}", - "{Array TYPE}", - ], - "diff-dst-prefix": "{String TYPE}", - "diff-ignore-all-space": "{Boolean TYPE}", - "diff-name-only": "{Boolean TYPE}", - "diff-no-prefix": "{Boolean TYPE}", - "diff-src-prefix": "{String TYPE}", - "diff-text": "{Boolean TYPE}", - "diff-unified": Array [ - null, - "{Number TYPE}", - ], - "dry-run": "{Boolean TYPE}", - "editor": "{String TYPE}", - "engine-strict": "{Boolean TYPE}", - "fetch-retries": "{Number TYPE}", - "fetch-retry-factor": "{Number TYPE}", - "fetch-retry-maxtimeout": "{Number TYPE}", - "fetch-retry-mintimeout": "{Number TYPE}", - "fetch-timeout": "{Number TYPE}", - "force": "{Boolean TYPE}", - "foreground-script": "{Boolean TYPE}", - "format-package-lock": "{Boolean TYPE}", - "fund": "{Boolean TYPE}", - "git": "{String TYPE}", - "git-tag-version": "{Boolean TYPE}", - "global": "{Boolean TYPE}", - "global-style": "{Boolean TYPE}", - "globalconfig": "{PATH MODULE}", - "heading": "{String TYPE}", - "https-proxy": Array [ - null, - "{URL MODULE}", - ], - "if-present": "{Boolean TYPE}", - "ignore-prepublish": "{Boolean TYPE}", - "ignore-scripts": "{Boolean TYPE}", - "include": Array [ - "{Array TYPE}", - "prod", - "dev", - "optional", - "peer", - ], - "include-staged": "{Boolean TYPE}", - "init-author-email": "{String TYPE}", - "init-author-name": "{String TYPE}", - "init-author-url": Array [ - "", - "{URL MODULE}", - ], - "init-license": "{String TYPE}", - "init-module": "{PATH MODULE}", - "init-version": "{SEMVER MODULE}", - "init.author.email": "{String TYPE}", - "init.author.name": "{String TYPE}", - "init.author.url": Array [ - "", - "{URL MODULE}", - ], - "init.license": "{String TYPE}", - "init.module": "{PATH MODULE}", - "init.version": "{SEMVER MODULE}", - "json": "{Boolean TYPE}", - "key": Array [ - null, - "{String TYPE}", - ], - "legacy-bundling": "{Boolean TYPE}", - "legacy-peer-deps": "{Boolean TYPE}", - "link": "{Boolean TYPE}", - "local-address": Array [ - undefined, - "127.0.0.1", - "no place like home", - ], - "loglevel": Array [ - "silent", - "error", - "warn", - "notice", - "http", - "timing", - "info", - "verbose", - "silly", - ], - "logs-max": "{Number TYPE}", - "long": "{Boolean TYPE}", - "maxsockets": "{Number TYPE}", - "message": "{String TYPE}", - "node-options": Array [ - null, - "{String TYPE}", - ], - "node-version": Array [ - null, - "{SEMVER MODULE}", - ], - "noproxy": Array [ - null, - "{String TYPE}", - "{Array TYPE}", - ], - "npm-version": "{SEMVER MODULE}", - "offline": "{Boolean TYPE}", - "omit": Array [ - "{Array TYPE}", - "dev", - "optional", - "peer", - ], - "only": Array [ - null, - "dev", - "development", - "prod", - "production", - ], - "optional": "{Boolean TYPE}", - "otp": Array [ - null, - "{String TYPE}", - ], - "package": Array [ - "{String TYPE}", - "{Array TYPE}", - ], - "package-lock": "{Boolean TYPE}", - "package-lock-only": "{Boolean TYPE}", - "parseable": "{Boolean TYPE}", - "prefer-offline": "{Boolean TYPE}", - "prefer-online": "{Boolean TYPE}", - "prefix": "{PATH MODULE}", - "preid": "{String TYPE}", - "production": "{Boolean TYPE}", - "progress": "{Boolean TYPE}", - "proxy": Array [ - null, - false, - "{URL MODULE}", - ], - "read-only": "{Boolean TYPE}", - "rebuild-bundle": "{Boolean TYPE}", - "registry": Array [ - null, - "{URL MODULE}", - ], - "rollback": "{Boolean TYPE}", - "save": "{Boolean TYPE}", - "save-bundle": "{Boolean TYPE}", - "save-dev": "{Boolean TYPE}", - "save-exact": "{Boolean TYPE}", - "save-optional": "{Boolean TYPE}", - "save-prefix": "{String TYPE}", - "save-prod": "{Boolean TYPE}", - "scope": "{String TYPE}", - "script-shell": Array [ - null, - "{String TYPE}", - ], - "scripts-prepend-node-path": Array [ - "{Boolean TYPE}", - "auto", - "warn-only", - ], - "searchexclude": Array [ - null, - "{String TYPE}", - ], - "searchlimit": "{Number TYPE}", - "searchopts": "{String TYPE}", - "searchstaleness": "{Number TYPE}", - "shell": "{String TYPE}", - "shrinkwrap": "{Boolean TYPE}", - "sign-git-commit": "{Boolean TYPE}", - "sign-git-tag": "{Boolean TYPE}", - "sso-poll-frequency": "{Number TYPE}", - "sso-type": Array [ - null, - "oauth", - "saml", - ], - "strict-peer-deps": "{Boolean TYPE}", - "strict-ssl": "{Boolean TYPE}", - "tag": "{String TYPE}", - "tag-version-prefix": "{String TYPE}", - "timing": "{Boolean TYPE}", - "tmp": "{PATH MODULE}", - "umask": "{Umask TYPE}", - "unicode": "{Boolean TYPE}", - "update-notifier": "{Boolean TYPE}", - "usage": "{Boolean TYPE}", - "user-agent": "{String TYPE}", - "userconfig": "{PATH MODULE}", - "version": "{Boolean TYPE}", - "versions": "{Boolean TYPE}", - "viewer": "{String TYPE}", - }, -} -` diff --git a/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js deleted file mode 100644 index 47de89e9761484..00000000000000 --- a/deps/npm/tap-snapshots/test-lib-utils-flat-options.js-TAP.test.js +++ /dev/null @@ -1,129 +0,0 @@ -/* IMPORTANT - * This snapshot file is auto-generated, but designed for humans. - * It should be checked into source control and tracked carefully. - * Re-generate by setting TAP_SNAPSHOT=1 and running tests. - * Make sure to inspect the output below. Do not ignore changes! - */ -'use strict' -exports[`test/lib/utils/flat-options.js TAP basic > flat options 1`] = ` -Object { - "_auth": undefined, - "@scope:registry": "@scope:registry", - "//nerf.dart:_authToken": "//nerf.dart:_authToken", - "access": "access", - "all": undefined, - "allowSameVersion": "allow-same-version", - "alwaysAuth": "always-auth", - "audit": "audit", - "auditLevel": "audit-level", - "authType": "auth-type", - "before": "before", - "binLinks": "bin-links", - "browser": "browser", - "ca": "ca", - "cache": "cache/_cacache", - "cafile": "cafile", - "call": "call", - "cert": "cert", - "cidr": "cidr", - "color": true, - "commitHooks": "commit-hooks", - "defaultTag": "tag", - "depth": "depth", - "diff": undefined, - "diffDstPrefix": undefined, - "diffIgnoreAllSpace": undefined, - "diffNameOnly": undefined, - "diffNoPrefix": undefined, - "diffSrcPrefix": undefined, - "diffText": undefined, - "diffUnified": undefined, - "dmode": 511, - "dryRun": "dry-run", - "editor": "editor", - "engineStrict": "engine-strict", - "fmode": 438, - "force": "force", - "foregroundScripts": false, - "formatPackageLock": "format-package-lock", - "fund": "fund", - "git": "git", - "gitTagVersion": "git-tag-version", - "global": "global", - "globalPrefix": "/usr/local", - "globalStyle": "global-style", - "hashAlgorithm": "sha1", - "ignoreScripts": undefined, - "includeStaged": undefined, - "json": undefined, - "key": "key", - "legacyBundling": "legacy-bundling", - "legacyPeerDeps": undefined, - "localPrefix": "/path/to/npm/cli", - "log": Object {}, - "long": undefined, - "message": "message", - "nodeBin": "/path/to/some/node", - "nodeVersion": "1.2.3", - "noProxy": "noproxy", - "npmBin": "/path/to/npm/bin.js", - "npmCommand": null, - "npmSession": "12345", - "npmVersion": "7.6.5", - "offline": "offline", - "omit": Array [], - "otp": "otp", - "package": "package", - "packageLock": true, - "packageLockOnly": "package-lock-only", - "parseable": undefined, - "preferDedupe": undefined, - "preferOffline": "prefer-offline", - "preferOnline": "prefer-online", - "prefix": "/path/to/npm/cli", - "preid": "preid", - "projectScope": "@npmcli", - "proxy": "proxy", - "readOnly": "read-only", - "rebuildBundle": "rebuild-bundle", - "registry": "registry", - "retry": Object { - "factor": "fetch-retry-factor", - "maxTimeout": "fetch-retry-maxtimeout", - "minTimeout": "fetch-retry-mintimeout", - "retries": "fetch-retries", - }, - "save": "save", - "saveBundle": false, - "savePrefix": "", - "saveType": "peerOptional", - "scope": "", - "scriptShell": "script-shell", - "search": Object { - "description": "description", - "exclude": "searchexclude", - "limit": "searchlimit", - "opts": Null Object { - "from": "1", - }, - "staleness": "searchstaleness", - }, - "shell": undefined, - "signGitCommit": "sign-git-commit", - "signGitTag": "sign-git-tag", - "ssoPollFrequency": undefined, - "ssoType": undefined, - "strictPeerDeps": undefined, - "strictSSL": "strict-ssl", - "tag": "tag", - "tagVersionPrefix": "tag-version-prefix", - "timeout": "fetch-timeout", - "tmp": "/tmp", - "umask": 18, - "unicode": undefined, - "userAgent": "user-agent", - "viewer": "viewer", - "which": undefined, - "yes": undefined, -} -` diff --git a/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js index 318f3ac1738624..cf085f1ad5b1aa 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js @@ -6,8 +6,9 @@ */ 'use strict' exports[`test/lib/utils/npm-usage.js TAP usage basic usage > must match snapshot 1`] = ` +npm <command> -Usage: npm <command> +Usage: npm install install all the dependencies in your project npm install <foo> add the <foo> dependency to your project @@ -38,55 +39,12 @@ More configuration info: npm help config Configuration fields: npm help 7 config npm@{VERSION} {BASEDIR} - -` - -exports[`test/lib/utils/npm-usage.js TAP usage did you mean? > must match snapshot 1`] = ` - -Usage: npm <command> - -npm install install all the dependencies in your project -npm install <foo> add the <foo> dependency to your project -npm test run this project's tests -npm run <foo> run the script named <foo> -npm <command> -h quick help on <command> -npm -l display usage info for all commands -npm help <term> search for help on <term> -npm help npm more involved overview - -All commands: - - access, adduser, audit, bin, bugs, cache, ci, completion, - config, dedupe, deprecate, diff, dist-tag, docs, doctor, - edit, exec, explain, explore, find-dupes, fund, get, help, - hook, init, install, install-ci-test, install-test, link, - ll, login, logout, ls, org, outdated, owner, pack, ping, - prefix, profile, prune, publish, rebuild, repo, restart, - root, run-script, search, set, set-script, shrinkwrap, star, - stars, start, stop, team, test, token, uninstall, unpublish, - unstar, update, version, view, whoami - -Specify configs in the ini-formatted file: - /some/config/file/.npmrc -or on the command line via: npm <command> --key=value - -More configuration info: npm help config -Configuration fields: npm help 7 config - -npm@{VERSION} {BASEDIR} - -` - -exports[`test/lib/utils/npm-usage.js TAP usage did you mean? > must match snapshot 2`] = ` - -Did you mean one of these? - install - uninstall ` exports[`test/lib/utils/npm-usage.js TAP usage set process.stdout.columns columns=0 > must match snapshot 1`] = ` +npm <command> -Usage: npm <command> +Usage: npm install install all the dependencies in your project npm install <foo> add the <foo> dependency to your project @@ -117,12 +75,12 @@ More configuration info: npm help config Configuration fields: npm help 7 config npm@{VERSION} {BASEDIR} - ` exports[`test/lib/utils/npm-usage.js TAP usage set process.stdout.columns columns=90 > must match snapshot 1`] = ` +npm <command> -Usage: npm <command> +Usage: npm install install all the dependencies in your project npm install <foo> add the <foo> dependency to your project @@ -153,12 +111,12 @@ More configuration info: npm help config Configuration fields: npm help 7 config npm@{VERSION} {BASEDIR} - ` exports[`test/lib/utils/npm-usage.js TAP usage with browser > must match snapshot 1`] = ` +npm <command> -Usage: npm <command> +Usage: npm install install all the dependencies in your project npm install <foo> add the <foo> dependency to your project @@ -189,12 +147,12 @@ More configuration info: npm help config Configuration fields: npm help 7 config npm@{VERSION} {BASEDIR} - ` exports[`test/lib/utils/npm-usage.js TAP usage with long > must match snapshot 1`] = ` +npm <command> -Usage: npm <command> +Usage: npm install install all the dependencies in your project npm install <foo> add the <foo> dependency to your project @@ -209,6 +167,8 @@ All commands: access npm access + Set access level on published packages + Usage: npm access public [<package>] npm access restricted [<package>] @@ -224,8 +184,13 @@ All commands: adduser npm adduser + Add a registry user account + Usage: - npm adduser [--registry=url] [--scope=@orgname] [--always-auth] + npm adduser + + Options: + [--registry <registry>] [--scope <@scope>] [--always-auth] aliases: login, add-user @@ -233,21 +198,32 @@ All commands: audit npm audit + Run a security audit + Usage: - npm audit [--json] [--production] - npm audit fix [--force|--package-lock-only|--dry-run|--production|--only=(dev|prod)] + npm audit [fix] + + Options: + [--dry-run] [-f|--force] [--json] [--package-lock-only] [--production] Run "npm help audit" for more info bin npm bin + Display npm bin folder + Usage: - npm bin [-g] + npm bin + + Options: + [-g|--global] Run "npm help bin" for more info bugs npm bugs + Report bugs for a package in a web browser + Usage: npm bugs [<pkgname>] @@ -257,6 +233,8 @@ All commands: cache npm cache + Manipulates packages cache + Usage: npm cache add <tarball file> npm cache add <folder> @@ -270,6 +248,8 @@ All commands: ci npm ci + Install a project with a clean slate + Usage: npm ci @@ -279,7 +259,7 @@ All commands: completion npm completion - npm command completion script. save to ~/.bashrc or ~/.zshrc + Tab Completion for npm Usage: npm completion @@ -288,6 +268,8 @@ All commands: config npm config + Manage the npm configuration files + Usage: npm config set <key>=<value> [<key>=<value> ...] npm config get [<key> [<key> ...]] @@ -301,6 +283,8 @@ All commands: dedupe npm dedupe + Reduce duplication in the package tree + Usage: npm dedupe @@ -310,6 +294,8 @@ All commands: deprecate npm deprecate + Deprecate a version of a package + Usage: npm deprecate <pkg>[@<version>] <message> @@ -317,6 +303,8 @@ All commands: diff npm diff + The registry diff command + Usage: npm diff [...<paths>] npm diff --diff=<pkg-name> [...<paths>] @@ -328,6 +316,8 @@ All commands: dist-tag npm dist-tag + Modify package distribution tags + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> @@ -337,12 +327,21 @@ All commands: Run "npm help dist-tag" for more info - docs npm docs [<pkgname> [<pkgname> ...]] + docs npm docs + + Open documentation for a package in a web browser + + Usage: + npm docs [<pkgname> [<pkgname> ...]] alias: home + Run "npm help docs" for more info + doctor npm doctor + Check your npm environment + Usage: npm doctor @@ -350,6 +349,8 @@ All commands: edit npm edit + Edit an installed package + Usage: npm edit <pkg>[/<subpkg>...] @@ -357,7 +358,7 @@ All commands: exec npm exec - Run a command from a local or remote npm package. + Run a command from a local or remote npm package Usage: npm exec -- <pkg>[@<version>] [args...] @@ -371,6 +372,8 @@ All commands: explain npm explain + Explain installed packages + Usage: npm explain <folder | specifier> @@ -380,6 +383,8 @@ All commands: explore npm explore + Browse an installed package + Usage: npm explore <pkg> [ -- <command>] @@ -387,6 +392,8 @@ All commands: find-dupes npm find-dupes + Find duplication in the package tree + Usage: npm find-dupes @@ -394,13 +401,20 @@ All commands: fund npm fund + Retrieve funding information + Usage: - npm fund [--json] [--browser] [--unicode] [[<@scope>/]<pkg> [--which=<fundingSourceNumber>] + npm fund [[<@scope>/]<pkg>] + + Options: + [--json] [--browser|--browser <browser>] [--unicode] [--which <fundingSourceNumber>] Run "npm help fund" for more info get npm get + Get a value from the npm configuration + Usage: npm get [<key> ...] (See \`npm config\`) @@ -408,6 +422,8 @@ All commands: help npm help + Get help on npm + Usage: npm help <term> [<terms..>] @@ -417,6 +433,8 @@ All commands: hook npm hook + Manage registry hooks + Usage: npm hook add <pkg> <url> <secret> [--type=<type>] npm hook ls [pkg] @@ -427,6 +445,8 @@ All commands: init npm init + Create a package.json file + Usage: npm init [--force|-f|--yes|-y|--scope] npm init <@scope> (same as \`npx <@scope>/create\`) @@ -438,6 +458,8 @@ All commands: install npm install + Install a package + Usage: npm install [<@scope>/]<pkg> npm install [<@scope>/]<pkg>@<tag> @@ -448,7 +470,10 @@ All commands: npm install <tarball file> npm install <tarball url> npm install <git:// url> - npm install <github username>/<github project> [--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save] + npm install <github username>/<github project> + + Options: + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [-E|--save-exact] aliases: i, in, ins, inst, insta, instal, isnt, isnta, isntal, add @@ -456,6 +481,8 @@ All commands: install-ci-test npm install-ci-test + Install a project with a clean slate and run tests + Usage: npm install-ci-test @@ -465,6 +492,8 @@ All commands: install-test npm install-test + Install package(s) and run tests + Usage: npm install-test [<@scope>/]<pkg> npm install-test [<@scope>/]<pkg>@<tag> @@ -475,7 +504,10 @@ All commands: npm install-test <tarball file> npm install-test <tarball url> npm install-test <git:// url> - npm install-test <github username>/<github project> [--save-prod|--save-dev|--save-optional|--save-peer] [--save-exact] [--no-save] + npm install-test <github username>/<github project> + + Options: + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [-E|--save-exact] alias: it @@ -483,6 +515,8 @@ All commands: link npm link + Symlink a package folder + Usage: npm link (in package dir) npm link [<@scope>/]<pkg>[@<version>] @@ -493,6 +527,8 @@ All commands: ll npm ll + List installed packages + Usage: npm ll [[<@scope>/]<pkg> ...] @@ -502,8 +538,13 @@ All commands: login npm adduser + Add a registry user account + Usage: - npm adduser [--registry=url] [--scope=@orgname] [--always-auth] + npm adduser + + Options: + [--registry <registry>] [--scope <@scope>] [--always-auth] aliases: login, add-user @@ -511,13 +552,20 @@ All commands: logout npm logout + Log out of the registry + Usage: - npm logout [--registry=<url>] [--scope=<@scope>] + npm logout + + Options: + [--registry <registry>] [--scope <@scope>] Run "npm help logout" for more info ls npm ls + List installed packages + Usage: npm ls npm ls [[<@scope>/]<pkg> ...] @@ -527,6 +575,8 @@ All commands: org npm org + Manage orgs + Usage: npm org set orgname username [developer | admin | owner] npm org rm orgname username @@ -538,6 +588,8 @@ All commands: outdated npm outdated + Check for outdated packages + Usage: npm outdated [[<@scope>/]<pkg> ...] @@ -545,6 +597,8 @@ All commands: owner npm owner + Manage package owners + Usage: npm owner add <user> [<@scope>/]<pkg> npm owner rm <user> [<@scope>/]<pkg> @@ -556,22 +610,32 @@ All commands: pack npm pack + Create a tarball from a package + Usage: - npm pack [[<@scope>/]<pkg>...] [--dry-run] + npm pack [[<@scope>/]<pkg>...] + + Options: + [--dry-run] Run "npm help pack" for more info ping npm ping - ping registry + Ping npm registry Usage: npm ping + Options: + [--registry <registry>] + Run "npm help ping" for more info prefix npm prefix + Display prefix + Usage: npm prefix [-g] @@ -579,6 +643,8 @@ All commands: profile npm profile + Change settings on your registry profile + Usage: npm profile enable-2fa [auth-only|auth-and-writes] npm profile disable-2fa @@ -589,20 +655,32 @@ All commands: prune npm prune + Remove extraneous packages + Usage: - npm prune [[<@scope>/]<pkg>...] [--production] + npm prune [[<@scope>/]<pkg>...] + + Options: + [--production] Run "npm help prune" for more info publish npm publish + Publish a package + Usage: - npm publish [<folder>] [--tag <tag>] [--access <public|restricted>] [--dry-run] + npm publish [<folder>] + + Options: + [--tag <tag>] [--access <restricted|public>] [--dry-run] Run "npm help publish" for more info rebuild npm rebuild + Rebuild a package + Usage: npm rebuild [[<@scope>/]<name>[@<version>] ...] @@ -612,6 +690,8 @@ All commands: repo npm repo + Open package repository page in the browser + Usage: npm repo [<pkgname> [<pkgname> ...]] @@ -619,6 +699,8 @@ All commands: restart npm restart + Restart a package + Usage: npm restart [-- <args>] @@ -626,13 +708,20 @@ All commands: root npm root + Display npm root + Usage: - npm root [-g] + npm root + + Options: + [-g|--global] Run "npm help root" for more info run-script npm run-script + Run arbitrary package scripts + Usage: npm run-script <command> [-- <args>] @@ -642,8 +731,13 @@ All commands: search npm search + Search for pacakges + Usage: - npm search [-l|--long] [--json] [--parseable] [--no-description] [search terms ...] + npm search [search terms ...] + + Options: + [-l|--long] [--json] [-p|--parseable] [--no-description] aliases: s, se, find @@ -651,6 +745,8 @@ All commands: set npm set + Set a value in the npm configuration + Usage: npm set <key>=<value> [<key>=<value> ...] (See \`npm config\`) @@ -658,6 +754,8 @@ All commands: set-script npm set-script + Set tasks in the scripts section of package.json + Usage: npm set-script [<script>] [<command>] @@ -665,6 +763,8 @@ All commands: shrinkwrap npm shrinkwrap + Lock down dependency versions for publication + Usage: npm shrinkwrap @@ -672,6 +772,8 @@ All commands: star npm star + Mark your favorite packages + Usage: npm star [<pkg>...] @@ -679,6 +781,8 @@ All commands: stars npm stars + View packages marked as favorites + Usage: npm stars [<user>] @@ -686,6 +790,8 @@ All commands: start npm start + Start a package + Usage: npm start [-- <args>] @@ -693,6 +799,8 @@ All commands: stop npm stop + Stop a package + Usage: npm stop [-- <args>] @@ -700,6 +808,8 @@ All commands: team npm team + Manage organization teams and team memberships + Usage: npm team create <scope:team> [--otp <otpcode>] npm team destroy <scope:team> [--otp <otpcode>] @@ -711,6 +821,8 @@ All commands: test npm test + Test a package + Usage: npm test [-- <args>] @@ -720,6 +832,8 @@ All commands: token npm token + Manage your authentication tokens + Usage: npm token list npm token revoke <id|token> @@ -729,8 +843,13 @@ All commands: uninstall npm uninstall + Remove a package + Usage: - npm uninstall [<@scope>/]<pkg>[@<version>]... [-S|--save|--no-save] + npm uninstall [<@scope>/]<pkg>... + + Options: + [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] aliases: un, unlink, remove, rm, r @@ -738,6 +857,8 @@ All commands: unpublish npm unpublish + Remove a package from the registry + Usage: npm unpublish [<@scope>/]<pkg>[@<version>] @@ -745,6 +866,8 @@ All commands: unstar npm unstar + Remove an item from your favorite packages + Usage: npm unstar [<pkg>...] @@ -752,8 +875,13 @@ All commands: update npm update + Update packages + Usage: - npm update [-g] [<pkg>...] + npm update [<pkg>...] + + Options: + [-g|--global] aliases: up, upgrade, udpate @@ -761,6 +889,8 @@ All commands: version npm version + Bump a package version + Usage: npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git] @@ -770,6 +900,8 @@ All commands: view npm view + View registry info + Usage: npm view [<@scope>/]<pkg>[@<version>] [<field>[.subfield]...] @@ -779,10 +911,13 @@ All commands: whoami npm whoami - prints username according to given registry + Display npm username Usage: - npm whoami [--registry <registry>] + npm whoami + + Options: + [--registry <registry>] Run "npm help whoami" for more info @@ -794,5 +929,4 @@ More configuration info: npm help config Configuration fields: npm help 7 config npm@{VERSION} {BASEDIR} - ` diff --git a/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js index 402a0e735afc40..b0b38de341f1b2 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js @@ -20,10 +20,10 @@ bundle-dep name: my-cool-pkg version: 1.0.0 filename: my-cool-pkg-1.0.0.tgz -package size: 222 B +package size: 216 B unpacked size: 101 B -shasum: fe3a2f6064ade3bc21640874530586343f2d832f -integrity: sha512-ehndP8xBQL4yo[...]kWinZ4k1SCqUA== +shasum: a604258e06adecec0b18f48e901c5802f19f7dab +integrity: sha512-fnN6NmI8DerTt[...]6rH17jx7OIFig== bundled deps: 1 bundled files: 0 own files: 2 diff --git a/deps/npm/test/fixtures/mock-npm.js b/deps/npm/test/fixtures/mock-npm.js new file mode 100644 index 00000000000000..c47758111fd40a --- /dev/null +++ b/deps/npm/test/fixtures/mock-npm.js @@ -0,0 +1,22 @@ +// Basic npm fixture that you can give a config object that acts like +// npm.config You still need a separate flatOptions but this is the first step +// to eventually just using npm itself + +const mockNpm = (base = {}) => { + const config = base.config || {} + const flatOptions = base.flatOptions || {} + return { + ...base, + flatOptions, + config: { + // for now just set `find` to what config.find should return + // this works cause `find` is not an existing config entry + find: (k) => config[k], + get: (k) => config[k], + set: (k, v) => config[k] = v, + list: [config] + }, + } +} + +module.exports = mockNpm diff --git a/deps/npm/test/lib/audit.js b/deps/npm/test/lib/audit.js index d291ef87948c94..a25e6b0e277404 100644 --- a/deps/npm/test/lib/audit.js +++ b/deps/npm/test/lib/audit.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') t.test('should audit using Arborist', t => { let ARB_ARGS = null @@ -9,15 +10,15 @@ t.test('should audit using Arborist', t => { let OUTPUT_CALLED = false let ARB_OBJ = null - const npm = { + const npm = mockNpm({ prefix: 'foo', - flatOptions: { + config: { json: false, }, output: () => { OUTPUT_CALLED = true }, - } + }) const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => { AUDIT_REPORT_CALLED = true @@ -65,13 +66,13 @@ t.test('should audit using Arborist', t => { }) t.test('should audit - json', t => { - const npm = { + const npm = mockNpm({ prefix: 'foo', - flatOptions: { + config: { json: true, }, output: () => {}, - } + }) const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => ({ @@ -98,9 +99,12 @@ t.test('report endpoint error', t => { t.test(`json=${json}`, t => { const OUTPUT = [] const LOGS = [] - const npm = { + const npm = mockNpm({ prefix: 'foo', command: 'audit', + config: { + json, + }, flatOptions: { json, }, @@ -110,7 +114,7 @@ t.test('report endpoint error', t => { output: (...msg) => { OUTPUT.push(msg) }, - } + }) const Audit = requireInject('../../lib/audit.js', { 'npm-audit-report': () => { throw new Error('should not call audit report when there are errors') diff --git a/deps/npm/test/lib/bin.js b/deps/npm/test/lib/bin.js index 428b2e3bad4ab8..1d9341169be154 100644 --- a/deps/npm/test/lib/bin.js +++ b/deps/npm/test/lib/bin.js @@ -1,5 +1,6 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('bin', (t) => { t.plan(4) @@ -7,13 +8,13 @@ test('bin', (t) => { const Bin = require('../../lib/bin.js') - const npm = { + const npm = mockNpm({ bin: dir, - flatOptions: { global: false }, + config: { global: false }, output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - } + }) const bin = new Bin(npm) t.match(bin.usage, 'bin', 'usage has command name in it') @@ -39,13 +40,13 @@ test('bin -g', (t) => { '../../lib/utils/path.js': [dir], }) - const npm = { + const npm = mockNpm({ bin: dir, - flatOptions: { global: true }, + config: { global: true }, output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - } + }) const bin = new Bin(npm) bin.exec([], (err) => { @@ -69,13 +70,13 @@ test('bin -g (not in path)', (t) => { const Bin = requireInject('../../lib/bin.js', { '../../lib/utils/path.js': ['/not/my/dir'], }) - const npm = { + const npm = mockNpm({ bin: dir, - flatOptions: { global: true }, + config: { global: true }, output: (output) => { t.equal(output, dir, 'prints the correct directory') }, - } + }) const bin = new Bin(npm) bin.exec([], (err) => { diff --git a/deps/npm/test/lib/birthday.js b/deps/npm/test/lib/birthday.js index c818223fb51e5c..0589be7a8eedbf 100644 --- a/deps/npm/test/lib/birthday.js +++ b/deps/npm/test/lib/birthday.js @@ -1,20 +1,23 @@ const t = require('tap') -const npm = { - flatOptions: { - yes: false, - package: [], - }, +const mockNpm = require('../fixtures/mock-npm') + +const config = { + yes: false, + package: [], +} +const npm = mockNpm({ + config, commands: { exec: (args, cb) => { - t.equal(npm.flatOptions.yes, true, 'should say yes') - t.strictSame(npm.flatOptions.package, ['@npmcli/npm-birthday'], + t.equal(npm.config.get('yes'), true, 'should say yes') + t.strictSame(npm.config.get('package'), ['@npmcli/npm-birthday'], 'uses correct package') t.strictSame(args, ['npm-birthday'], 'called with correct args') t.match(cb, Function, 'callback is a function') cb() }, }, -} +}) const Birthday = require('../../lib/birthday.js') const birthday = new Birthday(npm) diff --git a/deps/npm/test/lib/cache.js b/deps/npm/test/lib/cache.js index 773adc6a8a3049..0fdf7685684799 100644 --- a/deps/npm/test/lib/cache.js +++ b/deps/npm/test/lib/cache.js @@ -1,23 +1,12 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const path = require('path') const usageUtil = () => 'usage instructions' -const flatOptions = { - force: false, -} - let outputOutput = [] -const npm = { - flatOptions, - cache: '/fake/path', - output: (msg) => { - outputOutput.push(msg) - }, -} - let rimrafPath = '' const rimraf = (path, cb) => { rimrafPath = path @@ -66,6 +55,14 @@ const Cache = requireInject('../../lib/cache.js', { '../../lib/utils/usage.js': usageUtil, }) +const npm = mockNpm({ + cache: '/fake/path', + flatOptions: { force: false }, + config: { force: false }, + output: (msg) => { + outputOutput.push(msg) + }, +}) const cache = new Cache(npm) t.test('cache no args', t => { @@ -83,10 +80,12 @@ t.test('cache clean', t => { }) t.test('cache clean (force)', t => { - flatOptions.force = true + npm.config.set('force', true) + npm.flatOptions.force = true t.teardown(() => { rimrafPath = '' - flatOptions.force = false + npm.config.force = false + npm.flatOptions.force = false }) cache.exec(['clear'], err => { @@ -131,7 +130,7 @@ t.test('cache add pkg only', t => { ['silly', 'cache add', 'spec', 'mypkg'], ], 'logs correctly') t.equal(tarballStreamSpec, 'mypkg', 'passes the correct spec to pacote') - t.same(tarballStreamOpts, flatOptions, 'passes the correct options to pacote') + t.same(tarballStreamOpts, npm.flatOptions, 'passes the correct options to pacote') t.end() }) }) @@ -150,7 +149,7 @@ t.test('cache add pkg w/ spec modifier', t => { ['silly', 'cache add', 'spec', 'mypkg@latest'], ], 'logs correctly') t.equal(tarballStreamSpec, 'mypkg@latest', 'passes the correct spec to pacote') - t.same(tarballStreamOpts, flatOptions, 'passes the correct options to pacote') + t.same(tarballStreamOpts, npm.flatOptions, 'passes the correct options to pacote') t.end() }) }) diff --git a/deps/npm/test/lib/ci.js b/deps/npm/test/lib/ci.js index 3419218ef9d8b8..7f06a6cebcbc12 100644 --- a/deps/npm/test/lib/ci.js +++ b/deps/npm/test/lib/ci.js @@ -5,6 +5,7 @@ const readdir = util.promisify(fs.readdir) const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('should ignore scripts with --ignore-scripts', (t) => { const SCRIPTS = [] @@ -22,17 +23,15 @@ test('should ignore scripts with --ignore-scripts', (t) => { }, }) - const ci = new CI({ + const npm = mockNpm({ globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: false, - ignoreScripts: true, - }, config: { - get: () => false, + global: false, + 'ignore-scripts': true, }, }) + const ci = new CI(npm) ci.exec([], er => { if (er) @@ -115,12 +114,13 @@ test('should use Arborist and run-script', (t) => { }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: path, - flatOptions: { + config: { global: false, }, }) + const ci = new CI(npm) ci.exec(null, er => { if (er) @@ -146,12 +146,13 @@ test('should pass flatOptions to Arborist.reify', (t) => { } }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: 'foo', flatOptions: { production: true, }, }) + const ci = new CI(npm) ci.exec(null, er => { if (er) throw er @@ -173,14 +174,15 @@ test('should throw if package-lock.json or npm-shrinkwrap missing', (t) => { }, }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { + config: { global: false, }, }) + const ci = new CI(npm) ci.exec(null, (err, res) => { - t.ok(err, 'throws error when there is no package-lock') + t.match(err, /package-lock.json/, 'throws error when there is no package-lock') t.notOk(res) t.end() }) @@ -191,12 +193,13 @@ test('should throw ECIGLOBAL', (t) => { '@npmcli/run-script': opts => {}, '../../lib/utils/reify-finish.js': async () => {}, }) - const ci = new CI({ + const npm = mockNpm({ prefix: 'foo', - flatOptions: { + config: { global: true, }, }) + const ci = new CI(npm) ci.exec(null, (err, res) => { t.equals(err.code, 'ECIGLOBAL', 'throws error with global packages') t.notOk(res) @@ -227,12 +230,13 @@ test('should remove existing node_modules before installing', (t) => { }, }) - const ci = new CI({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { + config: { global: false, }, }) + const ci = new CI(npm) ci.exec(null, er => { if (er) diff --git a/deps/npm/test/lib/cli.js b/deps/npm/test/lib/cli.js index b5441be1e44d87..40da77bf44e3d8 100644 --- a/deps/npm/test/lib/cli.js +++ b/deps/npm/test/lib/cli.js @@ -1,7 +1,11 @@ const t = require('tap') let LOAD_ERROR = null +const npmOutputs = [] const npmock = { + log: { level: 'silent' }, + output: (...msg) => npmOutputs.push(msg), + usage: 'npm usage test example', version: '99.99.99', load: cb => cb(LOAD_ERROR), argv: [], @@ -21,8 +25,11 @@ const unsupportedMock = { } let errorHandlerCalled = null +let errorHandlerCb const errorHandlerMock = (...args) => { errorHandlerCalled = args + if (errorHandlerCb) + errorHandlerCb() } let errorHandlerExitCalled = null errorHandlerMock.exit = code => { @@ -39,15 +46,23 @@ const npmlogMock = { const requireInject = require('require-inject') const cli = requireInject.installGlobally('../../lib/cli.js', { '../../lib/npm.js': npmock, + '../../lib/utils/did-you-mean.js': () => '\ntest did you mean', '../../lib/utils/unsupported.js': unsupportedMock, '../../lib/utils/error-handler.js': errorHandlerMock, npmlog: npmlogMock, }) t.test('print the version, and treat npm_g to npm -g', t => { - const { log } = console - const consoleLogs = [] - console.log = (...msg) => consoleLogs.push(msg) + t.teardown(() => { + delete npmock.config.settings.version + process.argv = argv + npmock.argv.length = 0 + proc.argv.length = 0 + logs.length = 0 + npmOutputs.length = 0 + errorHandlerExitCalled = null + }) + const { argv } = process const proc = { argv: ['node', 'npm_g', '-v'], @@ -67,25 +82,13 @@ t.test('print the version, and treat npm_g to npm -g', t => { ['info', 'using', 'npm@%s', '99.99.99'], ['info', 'using', 'node@%s', '420.69.lol'], ]) - t.strictSame(consoleLogs, [['99.99.99']]) + t.strictSame(npmOutputs, [['99.99.99']]) t.strictSame(errorHandlerExitCalled, 0) - delete npmock.config.settings.version - process.argv = argv - console.log = log - npmock.argv.length = 0 - proc.argv.length = 0 - logs.length = 0 - consoleLogs.length = 0 - errorHandlerExitCalled = null - t.end() }) t.test('calling with --versions calls npm version with no args', t => { - const { log } = console - const consoleLogs = [] - console.log = (...msg) => consoleLogs.push(msg) const processArgv = process.argv const proc = { argv: ['node', 'npm', 'install', 'or', 'whatever', '--versions'], @@ -97,11 +100,10 @@ t.test('calling with --versions calls npm version with no args', t => { t.teardown(() => { delete npmock.config.settings.versions process.argv = processArgv - console.log = log npmock.argv.length = 0 proc.argv.length = 0 logs.length = 0 - consoleLogs.length = 0 + npmOutputs.length = 0 errorHandlerExitCalled = null delete npmock.commands.version }) @@ -117,7 +119,7 @@ t.test('calling with --versions calls npm version with no args', t => { ['info', 'using', 'node@%s', undefined], ]) - t.strictSame(consoleLogs, []) + t.strictSame(npmOutputs, []) t.strictSame(errorHandlerExitCalled, null) t.strictSame(args, []) @@ -127,55 +129,80 @@ t.test('calling with --versions calls npm version with no args', t => { cli(proc) }) -t.test('print usage if -h provided', t => { - const { log } = console - const consoleLogs = [] - console.log = (...msg) => consoleLogs.push(msg) +t.test('print usage if no params provided', t => { + const { output } = npmock + t.teardown(() => { + npmock.output = output + }) + const proc = { + argv: ['node', 'npm'], + on: () => {}, + } + npmock.argv = [] + npmock.output = (msg) => { + if (msg) { + t.match(msg, 'npm usage test example', 'outputs npm usage') + t.end() + } + } + cli(proc) +}) + +t.test('print usage if non-command param provided', t => { + const { output } = npmock + t.teardown(() => { + npmock.output = output + }) const proc = { argv: ['node', 'npm', 'asdf'], on: () => {}, } npmock.argv = ['asdf'] + npmock.output = (msg) => { + if (msg) { + t.match(msg, 'Unknown command: "asdf"\ntest did you mean', 'outputs did you mean') + t.end() + } + } + cli(proc) +}) +t.test('gracefully handles error printing usage', t => { + const { output } = npmock t.teardown(() => { - console.log = log - npmock.argv.length = 0 - proc.argv.length = 0 - logs.length = 0 - consoleLogs.length = 0 - errorHandlerExitCalled = null - delete npmock.commands.help + npmock.output = output + errorHandlerCb = null }) - - npmock.commands.help = (args, cb) => { - delete npmock.commands.help - t.equal(proc.title, 'npm') - t.strictSame(args, ['asdf']) - t.strictSame(npmock.argv, ['asdf']) - t.strictSame(proc.argv, ['node', 'npm', 'asdf']) - t.strictSame(logs, [ - 'pause', - ['verbose', 'cli', ['node', 'npm', 'asdf']], - ['info', 'using', 'npm@%s', '99.99.99'], - ['info', 'using', 'node@%s', undefined], - ]) - t.strictSame(consoleLogs, []) - t.strictSame(errorHandlerExitCalled, null) + const proc = { + argv: ['node', 'npm', 'asdf'], + on: () => {}, + } + npmock.argv = [] + npmock.output = (msg) => { + throw new Error('test exception') + } + errorHandlerCb = () => { + t.match(errorHandlerCalled, /test exception/) t.end() } - cli(proc) }) t.test('load error calls error handler', t => { - const er = new Error('poop') + t.teardown(() => { + errorHandlerCb = null + LOAD_ERROR = null + }) + + const er = new Error('test load error') LOAD_ERROR = er const proc = { argv: ['node', 'npm', 'asdf'], on: () => {}, } + errorHandlerCb = () => { + t.strictSame(errorHandlerCalled, [er]) + t.end() + } cli(proc) - t.strictSame(errorHandlerCalled, [er]) - LOAD_ERROR = null - t.end() }) diff --git a/deps/npm/test/lib/completion.js b/deps/npm/test/lib/completion.js index 708f138251d19c..c6ef901a7ed7d3 100644 --- a/deps/npm/test/lib/completion.js +++ b/deps/npm/test/lib/completion.js @@ -63,11 +63,14 @@ const cmdList = { plumbing: [], } +// only include a subset so that the snapshots aren't huge and +// don't change when we add/remove config definitions. +const definitions = require('../../lib/utils/config/definitions.js') const config = { - types: { - global: Boolean, - browser: [null, Boolean, String], - registry: [null, String], + definitions: { + global: definitions.global, + browser: definitions.browser, + registry: definitions.registry, }, shorthands: { reg: ['--registry'], @@ -80,7 +83,7 @@ const deref = (cmd) => { const Completion = requireInject('../../lib/completion.js', { '../../lib/utils/cmd-list.js': cmdList, - '../../lib/utils/config.js': config, + '../../lib/utils/config/index.js': config, '../../lib/utils/deref-command.js': deref, '../../lib/utils/is-windows-shell.js': false, }) diff --git a/deps/npm/test/lib/config.js b/deps/npm/test/lib/config.js index 3aeb29f8d3426e..14cd816171da57 100644 --- a/deps/npm/test/lib/config.js +++ b/deps/npm/test/lib/config.js @@ -1,4 +1,5 @@ const t = require('tap') + const requireInject = require('require-inject') const { EventEmitter } = require('events') @@ -22,12 +23,21 @@ const redactCwd = (path) => { t.cleanSnapshot = (str) => redactCwd(str) let result = '' -const types = { - 'init-author-name': String, - 'init-version': String, - 'init.author.name': String, - 'init.version': String, -} + +const configDefs = require('../../lib/utils/config') +const definitions = Object.entries(configDefs.definitions) + .filter(([key, def]) => { + return [ + 'init-author-name', + 'init.author.name', + 'init-version', + 'init.version', + ].includes(key) + }).reduce((defs, [key, def]) => { + defs[key] = def + return defs + }, {}) + const defaults = { 'init-author-name': '', 'init-version': '1.0.0', @@ -35,7 +45,7 @@ const defaults = { 'init.version': '1.0.0', } -const flatOptions = { +const cliConfig = { editor: 'vi', json: false, long: false, @@ -43,7 +53,6 @@ const flatOptions = { } const npm = { - flatOptions, log: { info: () => null, enableProgress: () => null, @@ -53,10 +62,10 @@ const npm = { data: new Map(Object.entries({ default: { data: defaults, source: 'default values' }, global: { data: {}, source: '/etc/npmrc' }, - cli: { data: flatOptions, source: 'command line options' }, + cli: { data: cliConfig, source: 'command line options' }, })), get (key) { - return flatOptions[key] + return cliConfig[key] }, validate () { return true @@ -70,7 +79,7 @@ const npm = { const usageUtil = () => 'usage instructions' const mocks = { - '../../lib/utils/config.js': { defaults, types }, + '../../lib/utils/config/index.js': { defaults, definitions }, '../../lib/utils/usage.js': usageUtil, } @@ -110,13 +119,13 @@ t.test('config list overrides', t => { }, source: '~/.npmrc', }) - flatOptions['init.author.name'] = 'Bar' + cliConfig['init.author.name'] = 'Bar' npm.config.find = () => 'cli' result = '' t.teardown(() => { result = '' npm.config.data.delete('user') - delete flatOptions['init.author.name'] + delete cliConfig['init.author.name'] delete npm.config.find }) @@ -129,12 +138,12 @@ t.test('config list overrides', t => { t.test('config list --long', t => { t.plan(2) - npm.config.find = key => key in flatOptions ? 'cli' : 'default' - flatOptions.long = true + npm.config.find = key => key in cliConfig ? 'cli' : 'default' + cliConfig.long = true result = '' t.teardown(() => { delete npm.config.find - flatOptions.long = false + cliConfig.long = false result = '' }) @@ -147,7 +156,7 @@ t.test('config list --long', t => { t.test('config list --json', t => { t.plan(2) - flatOptions.json = true + cliConfig.json = true result = '' npm.config.list = [{ '//private-reg.npmjs.org/:_authThoken': 'f00ba1', @@ -158,7 +167,7 @@ t.test('config list --json', t => { t.teardown(() => { delete npm.config.list - flatOptions.json = false + cliConfig.json = false npm.config.get = npmConfigGet result = '' }) @@ -246,13 +255,13 @@ t.test('config delete key --global', t => { t.equal(where, 'global', 'should save global config post-delete') } - flatOptions.global = true + cliConfig.global = true config.exec(['delete', 'foo'], (err) => { t.ifError(err, 'npm config delete key --global') }) t.teardown(() => { - flatOptions.global = false + cliConfig.global = false delete npm.config.delete delete npm.config.save }) @@ -401,13 +410,13 @@ t.test('config set key --global', t => { t.equal(where, 'global', 'should save global config') } - flatOptions.global = true + cliConfig.global = true config.exec(['set', 'foo', 'bar'], (err) => { t.ifError(err, 'npm config set key --global') }) t.teardown(() => { - flatOptions.global = false + cliConfig.global = false delete npm.config.set delete npm.config.save }) @@ -555,7 +564,7 @@ sign-git-commit=true` t.test('config edit --global', t => { t.plan(6) - flatOptions.global = true + cliConfig.global = true const npmrc = 'init.author.name=Foo' npm.config.data.set('global', { source: '/etc/npmrc', @@ -595,7 +604,7 @@ t.test('config edit --global', t => { }) t.teardown(() => { - flatOptions.global = false + cliConfig.global = false npm.config.data.delete('user') delete npm.config.save }) @@ -612,7 +621,7 @@ t.test('completion', t => { testComp(['npm', 'config'], ['get', 'set', 'delete', 'ls', 'rm', 'edit', 'list']) testComp(['npm', 'config', 'set', 'foo'], []) - const possibleConfigKeys = [...Object.keys(types)] + const possibleConfigKeys = [...Object.keys(definitions)] testComp(['npm', 'config', 'get'], possibleConfigKeys) testComp(['npm', 'config', 'set'], possibleConfigKeys) testComp(['npm', 'config', 'delete'], possibleConfigKeys) diff --git a/deps/npm/test/lib/dedupe.js b/deps/npm/test/lib/dedupe.js index 3e8b2f4c01347f..851163f935e271 100644 --- a/deps/npm/test/lib/dedupe.js +++ b/deps/npm/test/lib/dedupe.js @@ -1,20 +1,13 @@ const { test } = require('tap') const requireInject = require('require-inject') - -const npm = (base) => { - const config = base.config - return { - ...base, - flatOptions: { dryRun: false }, - config: { - get: (k) => config[k], - }, - } -} +const mockNpm = require('../fixtures/mock-npm') test('should throw in global mode', (t) => { const Dedupe = requireInject('../../lib/dedupe.js') - const dedupe = new Dedupe(npm({ config: { global: true }})) + const npm = mockNpm({ + config: { 'dry-run': false, global: true }, + }) + const dedupe = new Dedupe(npm) dedupe.exec([], er => { t.match(er, { code: 'EDEDUPEGLOBAL' }, 'throws EDEDUPEGLOBAL') @@ -36,12 +29,13 @@ test('should remove dupes using Arborist', (t) => { t.ok(arb, 'gets arborist tree') }, }) - const dedupe = new Dedupe(npm({ + const npm = mockNpm({ prefix: 'foo', config: { 'dry-run': 'true', }, - })) + }) + const dedupe = new Dedupe(npm) dedupe.exec([], er => { if (er) throw er @@ -53,17 +47,18 @@ test('should remove dupes using Arborist', (t) => { test('should remove dupes using Arborist - no arguments', (t) => { const Dedupe = requireInject('../../lib/dedupe.js', { '@npmcli/arborist': function (args) { - t.ok(args.dryRun, 'gets dryRun from flatOptions') + t.ok(args.dryRun, 'gets dryRun from config') this.dedupe = () => {} }, '../../lib/utils/reify-output.js': () => {}, }) - const dedupe = new Dedupe(npm({ + const npm = mockNpm({ prefix: 'foo', config: { - 'dry-run': true, + 'dry-run': 'true', }, - })) + }) + const dedupe = new Dedupe(npm) dedupe.exec(null, () => { t.end() }) diff --git a/deps/npm/test/lib/diff.js b/deps/npm/test/lib/diff.js index 9f58505dca6ad2..08761c64c86b4e 100644 --- a/deps/npm/test/lib/diff.js +++ b/deps/npm/test/lib/diff.js @@ -1,30 +1,35 @@ const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const noop = () => null let libnpmdiff = noop let rlp = () => 'foo' -const defaultFlatOptions = { - defaultTag: 'latest', + +const config = { + global: false, + tag: 'latest', diff: [], +} +const flatOptions = { + global: false, diffUnified: null, diffIgnoreAllSpace: false, diffNoPrefix: false, diffSrcPrefix: '', diffDstPrefix: '', diffText: false, - prefix: '.', savePrefix: '^', } -const npm = { +const npm = mockNpm({ globalDir: __dirname, - flatOptions: { ...defaultFlatOptions }, - get prefix () { - return this.flatOptions.prefix - }, + prefix: '.', + config, + flatOptions, output: noop, -} +}) + const mocks = { npmlog: { info: noop, verbose: noop }, libnpmdiff: (...args) => libnpmdiff(...args), @@ -34,10 +39,21 @@ const mocks = { } t.afterEach(cb => { - npm.flatOptions = { ...defaultFlatOptions } + config.global = false + config.tag = 'latest' + config.diff = [] + flatOptions.global = false + flatOptions.diffUnified = null + flatOptions.diffIgnoreAllSpace = false + flatOptions.diffNoPrefix = false + flatOptions.diffSrcPrefix = '' + flatOptions.diffDstPrefix = '' + flatOptions.diffText = false + flatOptions.savePrefix = '^' + npm.globalDir = __dirname + npm.prefix = '..' libnpmdiff = noop rlp = () => 'foo' - npm.globalDir = __dirname cb() }) @@ -55,7 +71,7 @@ t.test('no args', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.prefix = path + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -102,10 +118,11 @@ t.test('single arg', t => { t.equal(a, 'foo@1.0.0', 'should forward single spec') t.equal(b, `file:${path}`, 'should compare to cwd') t.match(opts, npm.flatOptions, 'should forward flat options') + t.end() } - npm.flatOptions.diff = ['foo@1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['foo@1.0.0'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -118,8 +135,8 @@ t.test('single arg', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['foo@1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['foo@1.0.0'] + npm.prefix = path diff.exec([], err => { t.match( err, @@ -140,8 +157,8 @@ t.test('single arg', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['foo@~1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['foo@~1.0.0'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -158,8 +175,8 @@ t.test('single arg', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['2.1.4'] - npm.flatOptions.prefix = path + config.diff = ['2.1.4'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -171,7 +188,7 @@ t.test('single arg', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['2.1.4'] + config.diff = ['2.1.4'] diff.exec([], err => { t.match( err, @@ -198,8 +215,8 @@ t.test('single arg', t => { }, 'should forward flatOptions and diffFiles') } - npm.flatOptions.diff = ['2.1.4'] - npm.flatOptions.prefix = path + config.diff = ['2.1.4'] + npm.prefix = path diff.exec(['./foo.js', './bar.js'], err => { if (err) throw err @@ -221,8 +238,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['bar@1.0.0'] - npm.flatOptions.prefix = path + config.diff = ['bar@1.0.0'] + npm.prefix = path diff.exec([], err => { if (err) @@ -248,8 +265,8 @@ t.test('single arg', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['simple-output'] - npm.flatOptions.prefix = path + config.diff = ['simple-output'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -262,8 +279,8 @@ t.test('single arg', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['bar'] - npm.flatOptions.prefix = path + config.diff = ['bar'] + npm.prefix = path diff.exec([], err => { t.match( err, @@ -294,8 +311,8 @@ t.test('single arg', t => { }), }) - npm.flatOptions.diff = ['bar'] - npm.flatOptions.prefix = path + config.diff = ['bar'] + npm.prefix = path const Diff = requireInject('../../lib/diff.js', { ...mocks, @@ -355,9 +372,10 @@ t.test('single arg', t => { }, }) - npm.flatOptions.global = true - npm.flatOptions.diff = ['lorem'] - npm.flatOptions.prefix = resolve(path, 'project') + config.global = true + flatOptions.global = true + config.diff = ['lorem'] + npm.prefix = resolve(path, 'project') npm.globalDir = resolve(path, 'globalDir/lib/node_modules') const Diff = requireInject('../../lib/diff.js', { @@ -409,8 +427,8 @@ t.test('single arg', t => { t.equal(b, 'bar@2.0.0', 'should have expected comparison spec') } - npm.flatOptions.diff = ['bar@2.0.0'] - npm.flatOptions.prefix = path + config.diff = ['bar@2.0.0'] + npm.prefix = path diff.exec([], err => { if (err) @@ -466,8 +484,8 @@ t.test('single arg', t => { }) const diff = new Diff(npm) - npm.flatOptions.diff = ['lorem'] - npm.flatOptions.prefix = path + config.diff = ['lorem'] + npm.prefix = path diff.exec([], err => { if (err) @@ -499,8 +517,8 @@ t.test('single arg', t => { }) const diff = new Diff(npm) - npm.flatOptions.diff = ['lorem'] - npm.flatOptions.prefix = path + config.diff = ['lorem'] + npm.prefix = path diff.exec([], err => { if (err) @@ -518,8 +536,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['bar'] - npm.flatOptions.prefix = path + config.diff = ['bar'] + npm.prefix = path diff.exec([], err => { if (err) @@ -537,8 +555,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['my-project'] - npm.flatOptions.prefix = path + config.diff = ['my-project'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -555,8 +573,8 @@ t.test('single arg', t => { t.equal(b, `file:${path}`, 'should compare to cwd') } - npm.flatOptions.diff = ['/path/to/other-dir'] - npm.flatOptions.prefix = path + config.diff = ['/path/to/other-dir'] + npm.prefix = path diff.exec([], err => { if (err) throw err @@ -566,7 +584,7 @@ t.test('single arg', t => { t.test('unsupported spec type', t => { rlp = async () => 'my-project' - npm.flatOptions.diff = ['git+https://github.com/user/foo'] + config.diff = ['git+https://github.com/user/foo'] diff.exec([], err => { t.match( @@ -591,7 +609,7 @@ t.test('first arg is a qualified spec', t => { t.match(opts, npm.flatOptions, 'should forward flat options') } - npm.flatOptions.diff = ['bar@1.0.0', 'bar@^2.0.0'] + config.diff = ['bar@1.0.0', 'bar@^2.0.0'] diff.exec([], err => { if (err) throw err @@ -624,8 +642,8 @@ t.test('first arg is a qualified spec', t => { t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar@2.0.0', 'bar'] + npm.prefix = path + config.diff = ['bar@2.0.0', 'bar'] diff.exec([], err => { if (err) throw err @@ -635,7 +653,7 @@ t.test('first arg is a qualified spec', t => { t.test('second arg is a valid semver version', t => { t.plan(2) - npm.flatOptions.diff = ['bar@1.0.0', '2.0.0'] + config.diff = ['bar@1.0.0', '2.0.0'] libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@1.0.0', 'should set expected first spec') @@ -656,7 +674,7 @@ t.test('first arg is a qualified spec', t => { t.equal(b, 'bar-fork@latest', 'should target latest tag if not a dep') } - npm.flatOptions.diff = ['bar@1.0.0', 'bar-fork'] + config.diff = ['bar@1.0.0', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -693,8 +711,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, 'bar@2.0.0', 'should set expected second spec') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', 'bar@2.0.0'] + npm.prefix = path + config.diff = ['bar', 'bar@2.0.0'] diff.exec([], err => { if (err) throw err @@ -733,8 +751,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, `bar-fork@file:${resolve(path, 'node_modules/bar-fork')}`, 'should target fork local node_modules pkg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', 'bar-fork'] + npm.prefix = path + config.diff = ['bar', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -767,8 +785,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, 'bar@2.0.0', 'should use package name from first arg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', '2.0.0'] + npm.prefix = path + config.diff = ['bar', '2.0.0'] diff.exec([], err => { if (err) throw err @@ -801,8 +819,8 @@ t.test('first arg is a known dependency name', t => { t.equal(b, 'bar-fork@latest', 'should set expected second spec') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar', 'bar-fork'] + npm.prefix = path + config.diff = ['bar', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -816,7 +834,7 @@ t.test('first arg is a valid semver range', t => { t.test('second arg is a qualified spec', t => { t.plan(2) - npm.flatOptions.diff = ['1.0.0', 'bar@2.0.0'] + config.diff = ['1.0.0', 'bar@2.0.0'] libnpmdiff = async ([a, b], opts) => { t.equal(a, 'bar@1.0.0', 'should use name from second arg') @@ -855,8 +873,8 @@ t.test('first arg is a valid semver range', t => { t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should set expected second spec from nm') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['1.0.0', 'bar'] + npm.prefix = path + config.diff = ['1.0.0', 'bar'] diff.exec([], err => { if (err) throw err @@ -872,7 +890,7 @@ t.test('first arg is a valid semver range', t => { t.equal(b, 'my-project@2.0.0', 'should use name from project dir') } - npm.flatOptions.diff = ['1.0.0', '2.0.0'] + config.diff = ['1.0.0', '2.0.0'] diff.exec([], err => { if (err) throw err @@ -885,8 +903,8 @@ t.test('first arg is a valid semver range', t => { throw new Error('ERR') } - npm.flatOptions.diff = ['1.0.0', '2.0.0'] - npm.flatOptions.prefix = path + config.diff = ['1.0.0', '2.0.0'] + npm.prefix = path diff.exec([], err => { t.match( err, @@ -906,7 +924,7 @@ t.test('first arg is a valid semver range', t => { t.equal(b, 'bar@latest', 'should compare against latest tag') } - npm.flatOptions.diff = ['1.0.0', 'bar'] + config.diff = ['1.0.0', 'bar'] diff.exec([], err => { if (err) throw err @@ -937,8 +955,8 @@ t.test('first arg is a valid semver range', t => { }) const diff = new Diff(npm) - npm.flatOptions.diff = ['1.0.0', 'lorem@2.0.0'] - npm.flatOptions.prefix = path + config.diff = ['1.0.0', 'lorem@2.0.0'] + npm.prefix = path diff.exec([], err => { if (err) @@ -960,7 +978,7 @@ t.test('first arg is an unknown dependency name', t => { t.match(opts, { where: '.' }, 'should forward pacote options') } - npm.flatOptions.diff = ['bar', 'bar@2.0.0'] + config.diff = ['bar', 'bar@2.0.0'] diff.exec([], err => { if (err) throw err @@ -993,8 +1011,8 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, `bar@file:${resolve(path, 'node_modules/bar')}`, 'should target local node_modules pkg') } - npm.flatOptions.prefix = path - npm.flatOptions.diff = ['bar-fork', 'bar'] + npm.prefix = path + config.diff = ['bar-fork', 'bar'] diff.exec([], err => { if (err) throw err @@ -1009,7 +1027,7 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, 'bar@^1.0.0', 'should use name from first arg') } - npm.flatOptions.diff = ['bar', '^1.0.0'] + config.diff = ['bar', '^1.0.0'] diff.exec([], err => { if (err) throw err @@ -1024,7 +1042,7 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, 'bar-fork@latest', 'should use latest tag') } - npm.flatOptions.diff = ['bar', 'bar-fork'] + config.diff = ['bar', 'bar-fork'] diff.exec([], err => { if (err) throw err @@ -1043,8 +1061,8 @@ t.test('first arg is an unknown dependency name', t => { t.equal(b, 'bar-fork@latest', 'should use latest tag') } - npm.flatOptions.diff = ['bar', 'bar-fork'] - npm.flatOptions.prefix = path + config.diff = ['bar', 'bar-fork'] + npm.prefix = path diff.exec([], err => { if (err) @@ -1059,7 +1077,7 @@ t.test('various options', t => { t.test('using --name-only option', t => { t.plan(1) - npm.flatOptions.diffNameOnly = true + flatOptions.diffNameOnly = true libnpmdiff = async ([a, b], opts) => { t.match(opts, { @@ -1077,7 +1095,7 @@ t.test('various options', t => { t.test('set files after both versions', t => { t.plan(3) - npm.flatOptions.diff = ['2.1.4', '3.0.0'] + config.diff = ['2.1.4', '3.0.0'] libnpmdiff = async ([a, b], opts) => { t.equal(a, 'foo@2.1.4', 'should use expected spec') @@ -1114,7 +1132,7 @@ t.test('various options', t => { }, 'should forward all remaining items as filenames') } - npm.flatOptions.prefix = path + npm.prefix = path diff.exec(['./foo.js', './bar.js'], err => { if (err) throw err @@ -1124,12 +1142,12 @@ t.test('various options', t => { t.test('using diff option', t => { t.plan(1) - npm.flatOptions.diffContext = 5 - npm.flatOptions.diffIgnoreWhitespace = true - npm.flatOptions.diffNoPrefix = false - npm.flatOptions.diffSrcPrefix = 'foo/' - npm.flatOptions.diffDstPrefix = 'bar/' - npm.flatOptions.diffText = true + flatOptions.diffContext = 5 + flatOptions.diffIgnoreWhitespace = true + flatOptions.diffNoPrefix = false + flatOptions.diffSrcPrefix = 'foo/' + flatOptions.diffDstPrefix = 'bar/' + flatOptions.diffText = true libnpmdiff = async ([a, b], opts) => { t.match(opts, { @@ -1153,7 +1171,7 @@ t.test('various options', t => { }) t.test('too many args', t => { - npm.flatOptions.diff = ['a', 'b', 'c'] + config.diff = ['a', 'b', 'c'] diff.exec([], err => { t.match( err, diff --git a/deps/npm/test/lib/dist-tag.js b/deps/npm/test/lib/dist-tag.js index a3c05bb2b3a15a..9415dacbe47564 100644 --- a/deps/npm/test/lib/dist-tag.js +++ b/deps/npm/test/lib/dist-tag.js @@ -1,18 +1,10 @@ const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const { test } = require('tap') -let prefix let result = '' let log = '' -// these declared opts are used in ./utils/read-local-package.js -const _flatOptions = { - global: false, - get prefix () { - return prefix - }, -} - const routeMap = { '/-/package/@scoped%2fpkg/dist-tags': { latest: '1.0.0', @@ -60,20 +52,18 @@ const DistTag = requireInject('../../lib/dist-tag.js', { }, }) -const distTag = new DistTag({ - flatOptions: _flatOptions, +const npm = mockNpm({ config: { - get (key) { - return _flatOptions[key] - }, + global: false, }, output: msg => { result = msg }, }) +const distTag = new DistTag(npm) test('ls in current package', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: '@scoped/pkg', }), @@ -91,7 +81,7 @@ test('ls in current package', (t) => { }) test('no args in current package', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: '@scoped/pkg', }), @@ -109,7 +99,7 @@ test('no args in current package', (t) => { }) test('borked cmd usage', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['borked', '@scoped/pkg'], (err) => { t.matchSnapshot(err, 'should show usage error') result = '' @@ -119,7 +109,7 @@ test('borked cmd usage', (t) => { }) test('ls on named package', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['ls', '@scoped/another'], (err) => { t.ifError(err, 'npm dist-tags ls') t.matchSnapshot( @@ -133,7 +123,7 @@ test('ls on named package', (t) => { }) test('ls on missing package', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['ls', 'foo'], (err) => { t.matchSnapshot( log, @@ -150,7 +140,7 @@ test('ls on missing package', (t) => { }) test('ls on missing name in current package', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ version: '1.0.0', }), @@ -167,7 +157,7 @@ test('ls on missing name in current package', (t) => { }) test('only named package arg', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['@scoped/another'], (err) => { t.ifError(err, 'npm dist-tags ls') t.matchSnapshot( @@ -186,7 +176,7 @@ test('add new tag', (t) => { t.equal(opts.method, 'PUT', 'should trigger request to add new tag') t.equal(opts.body, '7.7.7', 'should point to expected version') } - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', '@scoped/another@7.7.7', 'c'], (err) => { t.ifError(err, 'npm dist-tags add') t.matchSnapshot( @@ -201,7 +191,7 @@ test('add new tag', (t) => { }) test('add using valid semver range as name', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', '@scoped/another@7.7.7', '1.0.0'], (err) => { t.match( err, @@ -219,7 +209,7 @@ test('add using valid semver range as name', (t) => { }) test('add missing args', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', '@scoped/another@7.7.7'], (err) => { t.matchSnapshot(err, 'should exit usage error message') result = '' @@ -229,7 +219,7 @@ test('add missing args', (t) => { }) test('add missing pkg name', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['add', null], (err) => { t.matchSnapshot(err, 'should exit usage error message') result = '' @@ -239,7 +229,7 @@ test('add missing pkg name', (t) => { }) test('set existing version', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['set', '@scoped/another@0.6.0', 'b'], (err) => { t.ifError(err, 'npm dist-tags set') t.matchSnapshot( @@ -256,7 +246,7 @@ test('remove existing tag', (t) => { npmRegistryFetchMock = async (url, opts) => { t.equal(opts.method, 'DELETE', 'should trigger request to remove tag') } - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['rm', '@scoped/another', 'c'], (err) => { t.ifError(err, 'npm dist-tags rm') t.matchSnapshot(log, 'should log remove info') @@ -269,7 +259,7 @@ test('remove existing tag', (t) => { }) test('remove non-existing tag', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['rm', '@scoped/another', 'nonexistent'], (err) => { t.match( err, @@ -284,7 +274,7 @@ test('remove non-existing tag', (t) => { }) test('remove missing pkg name', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) distTag.exec(['rm', null], (err) => { t.matchSnapshot(err, 'should exit usage error message') result = '' diff --git a/deps/npm/test/lib/exec.js b/deps/npm/test/lib/exec.js index eb9fef6a61da2b..bcfe75577ce27d 100644 --- a/deps/npm/test/lib/exec.js +++ b/deps/npm/test/lib/exec.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const { resolve, delimiter } = require('path') const OUTPUT = [] const output = (...msg) => OUTPUT.push(msg) @@ -25,25 +26,23 @@ class Arborist { let PROGRESS_ENABLED = true const LOG_WARN = [] let PROGRESS_IGNORED = false -const npm = { - flatOptions: { - yes: true, - call: '', - package: [], - legacyPeerDeps: false, - shell: 'shell-cmd', - }, +const flatOptions = { + legacyPeerDeps: false, + package: [], +} +const config = { + cache: 'cache-dir', + yes: true, + call: '', + package: [], + shell: 'shell-cmd', +} +const npm = mockNpm({ + flatOptions, + config, localPrefix: 'local-prefix', localBin: 'local-bin', globalBin: 'global-bin', - config: { - get: k => { - if (k !== 'cache') - throw new Error('unexpected config get') - - return 'cache-dir' - }, - }, log: { disableProgress: () => { PROGRESS_ENABLED = false @@ -56,7 +55,7 @@ const npm = { }, }, output, -} +}) const RUN_SCRIPTS = [] const runScript = async opt => { @@ -108,9 +107,12 @@ t.afterEach(cb => { READ_ERROR = null LOG_WARN.length = 0 PROGRESS_IGNORED = false - npm.flatOptions.legacyPeerDeps = false - npm.flatOptions.package = [] - npm.flatOptions.call = '' + flatOptions.legacyPeerDeps = false + config.color = false + config.package = [] + flatOptions.package = [] + config.call = '' + config.yes = true npm.localBin = 'local-bin' npm.globalBin = 'global-bin' cb() @@ -186,7 +188,7 @@ t.test('npm exec foo, already present locally', t => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -240,14 +242,27 @@ t.test('npm exec <noargs>, run interactive shell', t => { cb() }) } - t.test('print message when tty and not in CI', t => { CI_NAME = null process.stdin.isTTY = true run(t, true, () => { t.strictSame(LOG_WARN, []) t.strictSame(OUTPUT, [ - ['\nEntering npm script environment\nType \'exit\' or ^D when finished\n'], + [`\nEntering npm script environment at location:\n${process.cwd()}\nType 'exit' or ^D when finished\n`], + ], 'printed message about interactive shell') + t.end() + }) + }) + + t.test('print message with color when tty and not in CI', t => { + CI_NAME = null + process.stdin.isTTY = true + config.color = true + + run(t, true, () => { + t.strictSame(LOG_WARN, []) + t.strictSame(OUTPUT, [ + [`\u001b[0m\u001b[0m\n\u001b[0mEntering npm script environment\u001b[0m\u001b[0m at location:\u001b[0m\n\u001b[0m\u001b[2m${process.cwd()}\u001b[22m\u001b[0m\u001b[1m\u001b[22m\n\u001b[1mType 'exit' or ^D when finished\u001b[22m\n\u001b[1m\u001b[22m`], ], 'printed message about interactive shell') t.end() }) @@ -300,7 +315,7 @@ t.test('npm exec foo, not present locally or in central loc', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add: ['foo@'], legacyPeerDeps: false}], 'need to install foo@') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -340,7 +355,7 @@ t.test('npm exec foo, not present locally but in central loc', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [], 'no need to install again, already there') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -380,7 +395,7 @@ t.test('npm exec foo, present locally but wrong version', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{ add: ['foo@2.x'], legacyPeerDeps: false }], 'need to add foo@2.x') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -412,12 +427,13 @@ t.test('npm exec --package=foo bar', t => { }, _from: 'foo@', } - npm.flatOptions.package = ['foo'] + config.package = ['foo'] + flatOptions.package = ['foo'] exec.exec(['bar', 'one arg', 'two arg'], er => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -459,7 +475,7 @@ t.test('npm exec @foo/bar -- --some=arg, locally installed', t => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -502,7 +518,7 @@ t.test('npm exec @foo/bar, with same bin alias and no unscoped named bin, locall if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['@foo/bar'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -552,7 +568,7 @@ t.test('run command with 2 packages, need install, verify sort', t => { t.plan(cases.length) for (const packages of cases) { t.test(packages.join(', '), t => { - npm.flatOptions.package = packages + config.package = packages const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -583,7 +599,7 @@ t.test('run command with 2 packages, need install, verify sort', t => { if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -652,8 +668,8 @@ t.test('npm exec foo, many bins in package, none named foo', t => { t.test('npm exec -p foo -c "ls -laF"', t => { const path = t.testdir() npm.localPrefix = path - npm.flatOptions.package = ['foo'] - npm.flatOptions.call = 'ls -laF' + config.package = ['foo'] + config.call = 'ls -laF' ARB_ACTUAL_TREE[path] = { children: new Map([['foo', { name: 'foo', version: '1.2.3' }]]), } @@ -666,7 +682,7 @@ t.test('npm exec -p foo -c "ls -laF"', t => { if (er) throw er t.strictSame(MKDIRPS, [], 'no need to make any dirs') - t.match(ARB_CTOR, [{ package: ['foo'], path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no need to reify anything') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.match(RUN_SCRIPTS, [{ @@ -683,7 +699,7 @@ t.test('npm exec -p foo -c "ls -laF"', t => { }) t.test('positional args and --call together is an error', t => { - npm.flatOptions.call = 'true' + config.call = 'true' exec.exec(['foo'], er => { t.equal(er, exec.usage) t.end() @@ -705,8 +721,8 @@ t.test('prompt when installs are needed if not already present and shell is a TT const packages = ['foo', 'bar'] READ_RESULT = 'yolo' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() @@ -738,7 +754,7 @@ t.test('prompt when installs are needed if not already present and shell is a TT if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -774,8 +790,8 @@ t.test('skip prompt when installs are needed if not already present and shell is const packages = ['foo', 'bar'] READ_RESULT = 'yolo' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() @@ -807,7 +823,7 @@ t.test('skip prompt when installs are needed if not already present and shell is if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install both packages') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -841,8 +857,8 @@ t.test('skip prompt when installs are needed if not already present and shell is const packages = ['foo'] READ_RESULT = 'yolo' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const add = packages.map(p => `${p}@`).sort((a, b) => a.localeCompare(b)) const path = t.testdir() @@ -866,7 +882,7 @@ t.test('skip prompt when installs are needed if not already present and shell is if (er) throw er t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.match(ARB_REIFY, [{add, legacyPeerDeps: false}], 'need to install the package') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') const PATH = `${resolve(installDir, 'node_modules', '.bin')}${delimiter}${process.env.PATH}` @@ -900,8 +916,8 @@ t.test('abort if prompt rejected', t => { const packages = ['foo', 'bar'] READ_RESULT = 'no, why would I want such a thing??' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -929,9 +945,9 @@ t.test('abort if prompt rejected', t => { _from: 'bar@', } exec.exec(['foobar'], er => { - t.equal(er, 'canceled', 'should be canceled') + t.match(er, /canceled/, 'should be canceled') t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no install performed') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.strictSame(RUN_SCRIPTS, []) @@ -958,8 +974,8 @@ t.test('abort if prompt false', t => { const packages = ['foo', 'bar'] READ_ERROR = 'canceled' - npm.flatOptions.package = packages - npm.flatOptions.yes = undefined + config.package = packages + config.yes = undefined const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -989,7 +1005,7 @@ t.test('abort if prompt false', t => { exec.exec(['foobar'], er => { t.equal(er, 'canceled', 'should be canceled') t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no install performed') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.strictSame(RUN_SCRIPTS, []) @@ -1015,8 +1031,8 @@ t.test('abort if -n provided', t => { const packages = ['foo', 'bar'] - npm.flatOptions.package = packages - npm.flatOptions.yes = false + config.package = packages + config.yes = false const path = t.testdir() const installDir = resolve('cache-dir/_npx/07de77790e5f40f2') @@ -1044,9 +1060,9 @@ t.test('abort if -n provided', t => { _from: 'bar@', } exec.exec(['foobar'], er => { - t.equal(er, 'canceled', 'should be canceled') + t.match(er, /canceled/, 'should be canceled') t.strictSame(MKDIRPS, [installDir], 'need to make install dir') - t.match(ARB_CTOR, [{ package: packages, path }]) + t.match(ARB_CTOR, [{ path }]) t.strictSame(ARB_REIFY, [], 'no install performed') t.equal(PROGRESS_ENABLED, true, 'progress re-enabled') t.strictSame(RUN_SCRIPTS, []) @@ -1073,8 +1089,8 @@ t.test('forward legacyPeerDeps opt', t => { }, _from: 'foo@', } - npm.flatOptions.yes = true - npm.flatOptions.legacyPeerDeps = true + config.yes = true + flatOptions.legacyPeerDeps = true exec.exec(['foo'], er => { if (er) throw er @@ -1082,3 +1098,93 @@ t.test('forward legacyPeerDeps opt', t => { t.done() }) }) + +t.test('workspaces', t => { + npm.localPrefix = t.testdir({ + node_modules: { + '.bin': { + foo: '', + }, + }, + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + bin: 'cli.js', + }), + 'cli.js': '', + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1.0.0', + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'root', + version: '1.0.0', + workspaces: ['packages/*'], + }), + }) + + PROGRESS_IGNORED = true + npm.localBin = resolve(npm.localPrefix, 'node_modules/.bin') + + t.test('with args, run scripts in the context of a workspace', t => { + exec.execWorkspaces(['foo', 'one arg', 'two arg'], ['a', 'b'], er => { + if (er) + throw er + + t.match(RUN_SCRIPTS, [{ + pkg: { scripts: { npx: 'foo' }}, + args: ['one arg', 'two arg'], + banner: false, + path: process.cwd(), + stdioString: true, + event: 'npx', + env: { + PATH: [npm.localBin, ...PATH].join(delimiter), + }, + stdio: 'inherit', + }]) + t.end() + }) + }) + + t.test('no args, spawn interactive shell', async t => { + CI_NAME = null + process.stdin.isTTY = true + + await new Promise((res, rej) => { + exec.execWorkspaces([], ['a'], er => { + if (er) + return rej(er) + + t.strictSame(LOG_WARN, []) + t.strictSame(OUTPUT, [ + [`\nEntering npm script environment in workspace a@1.0.0 at location:\n${resolve(npm.localPrefix, 'packages/a')}\nType 'exit' or ^D when finished\n`], + ], 'printed message about interactive shell') + res() + }) + }) + + config.color = true + OUTPUT.length = 0 + await new Promise((res, rej) => { + exec.execWorkspaces([], ['a'], er => { + if (er) + return rej(er) + + t.strictSame(LOG_WARN, []) + t.strictSame(OUTPUT, [ + [`\u001b[0m\u001b[0m\n\u001b[0mEntering npm script environment\u001b[0m\u001b[0m in workspace \u001b[32ma@1.0.0\u001b[39m at location:\u001b[0m\n\u001b[0m\u001b[2m${resolve(npm.localPrefix, 'packages/a')}\u001b[22m\u001b[0m\u001b[1m\u001b[22m\n\u001b[1mType 'exit' or ^D when finished\u001b[22m\n\u001b[1m\u001b[22m`], + ], 'printed message about interactive shell') + res() + }) + }) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/fund.js b/deps/npm/test/lib/fund.js index 2ae604a6536329..8c10007844f0ac 100644 --- a/deps/npm/test/lib/fund.js +++ b/deps/npm/test/lib/fund.js @@ -1,5 +1,6 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const version = '1.0.0' const funding = { @@ -180,19 +181,18 @@ const conflictingFundingPackages = { let result = '' let printUrl = '' -const _flatOptions = { +const config = { color: false, json: false, global: false, - prefix: undefined, unicode: false, - which: undefined, + which: null, } const openUrl = async (npm, url, msg) => { if (url === 'http://npmjs.org') throw new Error('ERROR') - if (_flatOptions.json) { + if (config.json) { printUrl = JSON.stringify({ title: msg, url: url, @@ -210,18 +210,16 @@ const Fund = requireInject('../../lib/fund.js', { : Promise.reject(new Error('ERROR')), }, }) -const fund = new Fund({ - flatOptions: _flatOptions, - get prefix () { - return _flatOptions.prefix - }, +const npm = mockNpm({ + config, output: msg => { result += msg + '\n' }, }) +const fund = new Fund(npm) test('fund with no package containing funding', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'no-funding-package', version: '0.0.0', @@ -237,7 +235,7 @@ test('fund with no package containing funding', t => { }) test('fund in which same maintainer owns all its deps', t => { - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) + npm.prefix = t.testdir(maintainerOwnsAllDeps) fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -248,8 +246,8 @@ test('fund in which same maintainer owns all its deps', t => { }) test('fund in which same maintainer owns all its deps, using --json option', t => { - _flatOptions.json = true - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) + config.json = true + npm.prefix = t.testdir(maintainerOwnsAllDeps) fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -281,13 +279,13 @@ test('fund in which same maintainer owns all its deps, using --json option', t = ) result = '' - _flatOptions.json = false + config.json = false t.end() }) }) test('fund containing multi-level nested deps with no funding', t => { - _flatOptions.prefix = t.testdir(nestedNoFundingPackages) + npm.prefix = t.testdir(nestedNoFundingPackages) fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -302,8 +300,8 @@ test('fund containing multi-level nested deps with no funding', t => { }) test('fund containing multi-level nested deps with no funding, using --json option', t => { - _flatOptions.prefix = t.testdir(nestedNoFundingPackages) - _flatOptions.json = true + npm.prefix = t.testdir(nestedNoFundingPackages) + config.json = true fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -328,14 +326,14 @@ test('fund containing multi-level nested deps with no funding, using --json opti ) result = '' - _flatOptions.json = false + config.json = false t.end() }) }) test('fund containing multi-level nested deps with no funding, using --json option', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) - _flatOptions.json = true + npm.prefix = t.testdir(nestedMultipleFundingPackages) + config.json = true fund.exec([], (err) => { t.ifError(err, 'should not error out') @@ -385,26 +383,26 @@ test('fund containing multi-level nested deps with no funding, using --json opti ) result = '' - _flatOptions.json = false + config.json = false t.end() }) }) test('fund does not support global', t => { - _flatOptions.prefix = t.testdir({}) - _flatOptions.global = true + npm.prefix = t.testdir({}) + config.global = true fund.exec([], (err) => { t.match(err.code, 'EFUNDGLOBAL', 'should throw EFUNDGLOBAL error') result = '' - _flatOptions.global = false + config.global = false t.end() }) }) test('fund using package argument', t => { - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) + npm.prefix = t.testdir(maintainerOwnsAllDeps) fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') @@ -416,9 +414,9 @@ test('fund using package argument', t => { }) test('fund does not support global, using --json option', t => { - _flatOptions.prefix = t.testdir({}) - _flatOptions.global = true - _flatOptions.json = true + npm.prefix = t.testdir({}) + config.global = true + config.json = true fund.exec([], (err) => { t.equal(err.code, 'EFUNDGLOBAL', 'should use EFUNDGLOBAL error code') @@ -428,14 +426,14 @@ test('fund does not support global, using --json option', t => { 'should use expected error msg' ) - _flatOptions.global = false - _flatOptions.json = false + config.global = false + config.json = false t.end() }) }) test('fund using string shorthand', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'funding-string-shorthand', version: '0.0.0', @@ -453,7 +451,7 @@ test('fund using string shorthand', t => { }) test('fund using nested packages with multiple sources', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) + npm.prefix = t.testdir(nestedMultipleFundingPackages) fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') @@ -465,7 +463,7 @@ test('fund using nested packages with multiple sources', t => { }) test('fund using symlink ref', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'using-symlink-ref', version: '1.0.0', @@ -511,7 +509,7 @@ test('fund using symlink ref', t => { }) test('fund using data from actual tree', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'using-actual-tree', version: '1.0.0', @@ -558,22 +556,22 @@ test('fund using data from actual tree', t => { }) test('fund using nested packages with multiple sources, with a source number', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) - _flatOptions.which = '1' + npm.prefix = t.testdir(nestedMultipleFundingPackages) + config.which = '1' fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') t.matchSnapshot(printUrl, 'should open the numbered URL') - _flatOptions.which = undefined + config.which = null printUrl = '' t.end() }) }) test('fund using pkg name while having conflicting versions', t => { - _flatOptions.prefix = t.testdir(conflictingFundingPackages) - _flatOptions.which = '1' + npm.prefix = t.testdir(conflictingFundingPackages) + config.which = '1' fund.exec(['foo'], (err) => { t.ifError(err, 'should not error out') @@ -585,8 +583,8 @@ test('fund using pkg name while having conflicting versions', t => { }) test('fund using package argument with no browser, using --json option', t => { - _flatOptions.prefix = t.testdir(maintainerOwnsAllDeps) - _flatOptions.json = true + npm.prefix = t.testdir(maintainerOwnsAllDeps) + config.json = true fund.exec(['.'], (err) => { t.ifError(err, 'should not error out') @@ -599,14 +597,14 @@ test('fund using package argument with no browser, using --json option', t => { 'should open funding url using json output' ) - _flatOptions.json = false + config.json = false printUrl = '' t.end() }) }) test('fund using package info fetch from registry', t => { - _flatOptions.prefix = t.testdir({}) + npm.prefix = t.testdir({}) fund.exec(['ntl'], (err) => { t.ifError(err, 'should not error out') @@ -622,7 +620,7 @@ test('fund using package info fetch from registry', t => { }) test('fund tries to use package info fetch from registry but registry has nothing', t => { - _flatOptions.prefix = t.testdir({}) + npm.prefix = t.testdir({}) fund.exec(['foo'], (err) => { t.equal(err.code, 'ENOFUND', 'should have ENOFUND error code') @@ -638,7 +636,7 @@ test('fund tries to use package info fetch from registry but registry has nothin }) test('fund but target module has no funding info', t => { - _flatOptions.prefix = t.testdir(nestedNoFundingPackages) + npm.prefix = t.testdir(nestedNoFundingPackages) fund.exec(['foo'], (err) => { t.equal(err.code, 'ENOFUND', 'should have ENOFUND error code') @@ -654,8 +652,8 @@ test('fund but target module has no funding info', t => { }) test('fund using bad which value', t => { - _flatOptions.prefix = t.testdir(nestedMultipleFundingPackages) - _flatOptions.which = 3 + npm.prefix = t.testdir(nestedMultipleFundingPackages) + config.which = 3 fund.exec(['bar'], (err) => { t.equal(err.code, 'EFUNDNUMBER', 'should have EFUNDNUMBER error code') @@ -665,14 +663,14 @@ test('fund using bad which value', t => { 'should have bad which option error message' ) - _flatOptions.which = undefined + config.which = null result = '' t.end() }) }) test('fund pkg missing version number', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'foo', funding: 'http://example.com/foo', @@ -688,7 +686,7 @@ test('fund pkg missing version number', t => { }) test('fund a package throws on openUrl', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'foo', version: '1.0.0', @@ -704,7 +702,7 @@ test('fund a package throws on openUrl', t => { }) test('fund a package with type and multiple sources', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'foo', funding: [ @@ -730,7 +728,7 @@ test('fund a package with type and multiple sources', t => { }) test('fund colors', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-fund-colors', version: '1.0.0', @@ -782,20 +780,20 @@ test('fund colors', t => { }, }, }) - _flatOptions.color = true + npm.color = true fund.exec([], (err) => { t.ifError(err, 'should not error out') t.matchSnapshot(result, 'should print output with color info') result = '' - _flatOptions.color = false + npm.color = false t.end() }) }) test('sub dep with fund info and a parent with no funding info', t => { - _flatOptions.prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-multiple-funding-sources', version: '1.0.0', diff --git a/deps/npm/test/lib/help-search.js b/deps/npm/test/lib/help-search.js index 6228f5ca97b3bf..567097a2eb183f 100644 --- a/deps/npm/test/lib/help-search.js +++ b/deps/npm/test/lib/help-search.js @@ -1,6 +1,7 @@ const { test } = require('tap') const { join } = require('path') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const ansicolors = require('ansicolors') const OUTPUT = [] @@ -8,26 +9,24 @@ const output = (msg) => { OUTPUT.push(msg) } -let npmHelpArgs = null -let npmHelpErr = null -const npm = { +const config = { + long: false, +} +const npmHelpErr = null +const npm = mockNpm({ color: false, + config, flatOptions: { long: false, }, + usage: 'npm test usage', commands: { help: (args, cb) => { - npmHelpArgs = args return cb(npmHelpErr) }, }, output, -} - -let npmUsageArg = null -const npmUsage = (npm, arg) => { - npmUsageArg = arg -} +}) let globRoot = null const globDir = { @@ -45,7 +44,6 @@ const glob = (p, cb) => cb(null, Object.keys(globDir).map((file) => join(globRoot, file))) const HelpSearch = requireInject('../../lib/help-search.js', { - '../../lib/utils/npm-usage.js': npmUsage, glob, }) const helpSearch = new HelpSearch(npm) @@ -61,8 +59,7 @@ test('npm help-search', t => { if (err) throw err - t.match(OUTPUT, /Top hits for/, 'outputs results') - t.match(OUTPUT, /Did you mean this\?\n\s+exec/, 'matched command, so suggest it') + t.match(OUTPUT, /Top hits for "exec"/, 'outputs results') t.end() }) }) @@ -84,46 +81,12 @@ test('npm help-search multiple terms', t => { }) }) -test('npm help-search single result prints full section', t => { - globRoot = t.testdir(globDir) - t.teardown(() => { - OUTPUT.length = 0 - npmHelpArgs = null - globRoot = null - }) - - return helpSearch.exec(['does not exist in'], (err) => { - if (err) - throw err - - t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it') - t.end() - }) -}) - -test('npm help-search single result propagates error', t => { - globRoot = t.testdir(globDir) - npmHelpErr = new Error('help broke') - t.teardown(() => { - OUTPUT.length = 0 - npmHelpArgs = null - npmHelpErr = null - globRoot = null - }) - - return helpSearch.exec(['does not exist in'], (err) => { - t.strictSame(npmHelpArgs, ['npm-install'], 'identified the correct man page and called help with it') - t.match(err, /help broke/, 'propagated the error from help') - t.end() - }) -}) - test('npm help-search long output', t => { globRoot = t.testdir(globDir) - npm.flatOptions.long = true + config.long = true t.teardown(() => { OUTPUT.length = 0 - npm.flatOptions.long = false + config.long = false globRoot = null }) @@ -138,11 +101,11 @@ test('npm help-search long output', t => { test('npm help-search long output with color', t => { globRoot = t.testdir(globDir) - npm.flatOptions.long = true + config.long = true npm.color = true t.teardown(() => { OUTPUT.length = 0 - npm.flatOptions.long = false + config.long = false npm.color = false globRoot = null }) @@ -159,7 +122,8 @@ test('npm help-search long output with color', t => { test('npm help-search no args', t => { return helpSearch.exec([], (err) => { - t.match(err, /npm help-search/, 'throws usage') + t.notOk(err) + t.match(OUTPUT, /npm help-search/, 'outputs usage') t.end() }) }) @@ -168,7 +132,6 @@ test('npm help-search no matches', t => { globRoot = t.testdir(globDir) t.teardown(() => { OUTPUT.length = 0 - npmUsageArg = null globRoot = null }) @@ -176,7 +139,7 @@ test('npm help-search no matches', t => { if (err) throw err - t.equal(npmUsageArg, false, 'called npmUsage for no matches') + t.match(OUTPUT, /No matches/) t.end() }) }) diff --git a/deps/npm/test/lib/help.js b/deps/npm/test/lib/help.js index ae2f7e99dafa89..ccf13a7e4660c2 100644 --- a/deps/npm/test/lib/help.js +++ b/deps/npm/test/lib/help.js @@ -2,11 +2,6 @@ const { test } = require('tap') const requireInject = require('require-inject') const { EventEmitter } = require('events') -let npmUsageArg = null -const npmUsage = (npm, arg) => { - npmUsageArg = arg -} - const npmConfig = { usage: false, viewer: undefined, @@ -16,6 +11,7 @@ const npmConfig = { let helpSearchArgs = null const OUTPUT = [] const npm = { + usage: 'test npm usage', config: { get: (key) => npmConfig[key], set: (key, value) => { @@ -48,7 +44,9 @@ const globDefaults = [ let globErr = null let globResult = globDefaults +let globParam const glob = (p, cb) => { + globParam = p return cb(globErr, globResult) } @@ -71,7 +69,6 @@ const openUrl = async (npm, url, msg) => { } const Help = requireInject('../../lib/help.js', { - '../../lib/utils/npm-usage.js': npmUsage, '../../lib/utils/open-url.js': openUrl, child_process: { spawn, @@ -81,15 +78,11 @@ const Help = requireInject('../../lib/help.js', { const help = new Help(npm) test('npm help', t => { - t.teardown(() => { - npmUsageArg = null - }) - return help.exec([], (err) => { if (err) throw err - t.equal(npmUsageArg, false, 'called npmUsage') + t.match(OUTPUT, ['test npm usage'], 'showed npm usage') t.end() }) }) @@ -107,22 +100,6 @@ test('npm help completion', async t => { t.rejects(help.completion({ conf: { argv: { remain: [] } } }), /glob failed/, 'glob errors propagate') }) -test('npm help -h', t => { - npmConfig.usage = true - t.teardown(() => { - npmConfig.usage = false - OUTPUT.length = 0 - }) - - return help.exec(['help'], (err) => { - if (err) - throw err - - t.strictSame(OUTPUT, ['npm help <term>'], 'outputs usage information for command') - t.end() - }) -}) - test('npm help multiple args calls search', t => { t.teardown(() => { helpSearchArgs = null @@ -180,7 +157,7 @@ test('npm help whoami', t => { throw err t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['1', 'npm-whoami'], 'passes the correct arguments') + t.strictSame(spawnArgs, [globResult[0]], 'passes the correct arguments') t.end() }) }) @@ -212,12 +189,12 @@ test('npm help 5 install', t => { npmConfig.viewer = 'browser' globResult = [ '/root/man/man5/install.5', - '/root/man/man1/npm-install.1', ] t.teardown(() => { npmConfig.viewer = undefined globResult = globDefaults + globParam = null spawnBin = null spawnArgs = null }) @@ -226,6 +203,7 @@ test('npm help 5 install', t => { if (err) throw err + t.match(globParam, /man5/, 'searches only in man5 folder') t.match(openUrlArg, /configuring-npm(\/|\\)install.html$/, 'attempts to open the correct url') t.end() }) @@ -234,11 +212,11 @@ test('npm help 5 install', t => { test('npm help 7 config', t => { npmConfig.viewer = 'browser' globResult = [ - '/root/man/man1/npm-config.1', '/root/man/man7/config.7', ] t.teardown(() => { npmConfig.viewer = undefined + globParam = null globResult = globDefaults spawnBin = null spawnArgs = null @@ -248,49 +226,12 @@ test('npm help 7 config', t => { if (err) throw err + t.match(globParam, /man7/, 'searches only in man5 folder') t.match(openUrlArg, /using-npm(\/|\\)config.html$/, 'attempts to open the correct url') t.end() }) }) -test('npm help with browser viewer and invalid section throws', t => { - npmConfig.viewer = 'browser' - globResult = [ - '/root/man/man1/npm-config.1', - '/root/man/man7/config.7', - '/root/man/man9/config.9', - ] - t.teardown(() => { - npmConfig.viewer = undefined - globResult = globDefaults - spawnBin = null - spawnArgs = null - }) - - return help.exec(['9', 'config'], (err) => { - t.match(err, /invalid man section: 9/, 'throws appropriate error') - t.end() - }) -}) - -test('npm help global redirects to folders', t => { - globResult = ['/root/man/man5/folders.5'] - t.teardown(() => { - globResult = globDefaults - spawnBin = null - spawnArgs = null - }) - - return help.exec(['global'], (err) => { - if (err) - throw err - - t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['5', 'folders'], 'passes the correct arguments') - t.end() - }) -}) - test('npm help package.json redirects to package-json', t => { globResult = ['/root/man/man5/package-json.5'] t.teardown(() => { @@ -304,7 +245,8 @@ test('npm help package.json redirects to package-json', t => { throw err t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['5', 'package-json'], 'passes the correct arguments') + t.match(globParam, /package-json/, 'glob was asked to find package-json') + t.strictSame(spawnArgs, [globResult[0]], 'passes the correct arguments') t.end() }) }) @@ -327,7 +269,7 @@ test('npm help ?(un)star', t => { throw err t.equal(spawnBin, 'emacsclient', 'maps woman to emacs correctly') - t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-unstar.1')`], 'passes the correct arguments') + t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-star.1')`], 'passes the correct arguments') t.end() }) }) @@ -350,7 +292,7 @@ test('npm help - woman viewer propagates errors', t => { return help.exec(['?(un)star'], (err) => { t.match(err, /help process exited with code: 1/, 'received the correct error') t.equal(spawnBin, 'emacsclient', 'maps woman to emacs correctly') - t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-unstar.1')`], 'passes the correct arguments') + t.strictSame(spawnArgs, ['-e', `(woman-find-file '/root/man/man1/npm-star.1')`], 'passes the correct arguments') t.end() }) }) @@ -372,7 +314,7 @@ test('npm help un*', t => { throw err t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['1', 'npm-unstar'], 'passes the correct arguments') + t.strictSame(spawnArgs, ['/root/man/man1/npm-uninstall.1'], 'passes the correct arguments') t.end() }) }) @@ -394,7 +336,7 @@ test('npm help - man viewer propagates errors', t => { return help.exec(['un*'], (err) => { t.match(err, /help process exited with code: 1/, 'received correct error') t.equal(spawnBin, 'man', 'calls man by default') - t.strictSame(spawnArgs, ['1', 'npm-unstar'], 'passes the correct arguments') + t.strictSame(spawnArgs, ['/root/man/man1/npm-uninstall.1'], 'passes the correct arguments') t.end() }) }) diff --git a/deps/npm/test/lib/init.js b/deps/npm/test/lib/init.js index 8b9f32e156e3d6..2b212f4a159e8a 100644 --- a/deps/npm/test/lib/init.js +++ b/deps/npm/test/lib/init.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = '' const npmLog = { @@ -10,14 +11,17 @@ const npmLog = { resume: () => null, silly: () => null, } -const npm = { - config: { set () {} }, - flatOptions: {}, +const config = { + 'init-module': '~/.npm-init.js', +} +const npm = mockNpm({ + config, log: npmLog, + commands: {}, output: (...msg) => { result += msg.join('\n') }, -} +}) const mocks = { 'init-package-json': (dir, initFile, config, cb) => cb(null, 'data'), '../../lib/utils/usage.js': () => 'usage instructions', @@ -27,19 +31,13 @@ const init = new Init(npm) t.afterEach(cb => { result = '' - npm.config = { get: () => '', set () {} } + config.package = undefined npm.commands = {} - Object.defineProperty(npm, 'flatOptions', { value: {} }) npm.log = npmLog cb() }) t.test('classic npm init no args', t => { - npm.config = { - get () { - return '~/.npm-init.js' - }, - } init.exec([], err => { t.ifError(err, 'npm init no args') t.matchSnapshot(result, 'should print helper info') @@ -49,9 +47,7 @@ t.test('classic npm init no args', t => { t.test('classic npm init -y', t => { t.plan(7) - npm.config = { - get: () => '~/.npm-init.js', - } + config.yes = true Object.defineProperty(npm, 'flatOptions', { value: { yes: true} }) npm.log = { ...npm.log } npm.log.silly = (title, msg) => { @@ -72,14 +68,9 @@ t.test('classic npm init -y', t => { }) t.test('npm init <arg>', t => { - t.plan(4) - npm.config = { - set (key, val) { - t.equal(key, 'package', 'should set package key') - t.deepEqual(val, [], 'should set empty array value') - }, - } + t.plan(3) npm.commands.exec = (arr, cb) => { + t.deepEqual(config.package, [], 'should set empty array value') t.deepEqual( arr, ['create-react-app'], @@ -178,20 +169,9 @@ t.test('npm init exec error', t => { }) t.test('should not rewrite flatOptions', t => { - t.plan(4) - Object.defineProperty(npm, 'flatOptions', { - get: () => ({}), - set () { - throw new Error('Should not set flatOptions') - }, - }) - npm.config = { - set (key, val) { - t.equal(key, 'package', 'should set package key') - t.deepEqual(val, [], 'should set empty array value') - }, - } + t.plan(3) npm.commands.exec = (arr, cb) => { + t.deepEqual(config.package, [], 'should set empty array value') t.deepEqual( arr, ['create-react-app', 'my-app'], diff --git a/deps/npm/test/lib/install.js b/deps/npm/test/lib/install.js index 8b7a968511136b..b44452a69cc6f3 100644 --- a/deps/npm/test/lib/install.js +++ b/deps/npm/test/lib/install.js @@ -2,6 +2,7 @@ const { test } = require('tap') const Install = require('../../lib/install.js') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('should install using Arborist', (t) => { const SCRIPTS = [] @@ -28,16 +29,14 @@ test('should install using Arborist', (t) => { throw new Error('got wrong object passed to reify-finish') }, }) - const install = new Install({ + + const npm = mockNpm({ + config: { dev: true }, + flatOptions: { global: false }, globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: false, - }, - config: { - get: () => true, - }, }) + const install = new Install(npm) t.test('with args', t => { install.exec(['fizzbuzz'], er => { @@ -86,17 +85,16 @@ test('should ignore scripts with --ignore-scripts', (t) => { } }, }) - const install = new Install({ + const npm = mockNpm({ globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: false, - ignoreScripts: true, - }, + flatOptions: { global: false }, config: { - get: () => false, + global: false, + 'ignore-scripts': true, }, }) + const install = new Install(npm) install.exec([], er => { if (er) throw er @@ -113,16 +111,13 @@ test('should install globally using Arborist', (t) => { this.reify = () => {} }, }) - const install = new Install({ + const npm = mockNpm({ globalDir: 'path/to/node_modules/', prefix: 'foo', - flatOptions: { - global: true, - }, - config: { - get: () => false, - }, + config: { global: true }, + flatOptions: { global: true }, }) + const install = new Install(npm) install.exec([], er => { if (er) throw er diff --git a/deps/npm/test/lib/link.js b/deps/npm/test/lib/link.js index be7af3f5240190..0d96ba0bcd6847 100644 --- a/deps/npm/test/lib/link.js +++ b/deps/npm/test/lib/link.js @@ -3,6 +3,7 @@ const { resolve } = require('path') const Arborist = require('@npmcli/arborist') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const redactCwd = (path) => { const normalizePath = p => p @@ -15,17 +16,13 @@ const redactCwd = (path) => { t.cleanSnapshot = (str) => redactCwd(str) let reifyOutput -const npm = { +const config = {} +const npm = mockNpm({ globalDir: null, prefix: null, - flatOptions: {}, - config: { - get () { - return false - }, - find () {}, - }, -} + config, +}) + const printLinks = async (opts) => { let res = '' const arb = new Arborist(opts) diff --git a/deps/npm/test/lib/load-all-commands.js b/deps/npm/test/lib/load-all-commands.js index e31a2b99369e1a..d7eb2eae0a8ad2 100644 --- a/deps/npm/test/lib/load-all-commands.js +++ b/deps/npm/test/lib/load-all-commands.js @@ -1,27 +1,37 @@ -// Thanks to nyc not working properly with proxies this -// doesn't affect coverage. but it does ensure that every command has a usage -// that contains its name, and if it has completion it is a function -const npm = require('../../lib/npm.js') +// Thanks to nyc not working properly with proxies this doesn't affect +// coverage. but it does ensure that every command has a usage that renders, +// contains its name, a description, and if it has completion it is a function. +// That it renders also ensures that any params we've defined in our commands +// work. +const requireInject = require('require-inject') +const npm = requireInject('../../lib/npm.js') const t = require('tap') const { cmdList } = require('../../lib/utils/cmd-list.js') -t.test('load npm', t => npm.load(er => { - if (er) - throw er -})) - +let npmOutput = [] +npm.output = (msg) => { + npmOutput = msg +} t.test('load each command', t => { - t.plan(cmdList.length) - for (const cmd of cmdList.sort((a, b) => a.localeCompare(b))) { - t.test(cmd, t => { - const impl = npm.commands[cmd] - if (impl.completion) { - t.plan(3) - t.isa(impl.completion, 'function', 'completion, if present, is a function') - } else - t.plan(2) - t.isa(impl, 'function', 'implementation is a function') - t.match(impl.usage, cmd, 'usage contains the command') - }) - } + t.plan(cmdList.length + 1) + npm.load((er) => { + t.notOk(er) + npm.config.set('usage', true) + for (const cmd of cmdList.sort((a, b) => a.localeCompare(b))) { + t.test(cmd, t => { + const impl = npm.commands[cmd] + if (impl.completion) + t.isa(impl.completion, 'function', 'completion, if present, is a function') + t.isa(impl, 'function', 'implementation is a function') + t.ok(impl.description, 'implementation has a description') + t.ok(impl.name, 'implementation has a name') + t.match(impl.usage, cmd, 'usage contains the command') + impl([], (err) => { + t.notOk(err) + t.match(npmOutput, impl.usage, 'usage is output') + t.end() + }) + }) + } + }) }) diff --git a/deps/npm/test/lib/logout.js b/deps/npm/test/lib/logout.js index b00fa641d8c16b..bae797f9693215 100644 --- a/deps/npm/test/lib/logout.js +++ b/deps/npm/test/lib/logout.js @@ -1,12 +1,17 @@ const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const { test } = require('tap') -const _flatOptions = { +const config = { registry: 'https://registry.npmjs.org/', scope: '', } +const flatOptions = { + registry: 'https://registry.npmjs.org/', + scope: '', +} +const npm = mockNpm({ config, flatOptions }) -const config = {} const npmlog = {} let result = null @@ -20,15 +25,12 @@ const mocks = { } const Logout = requireInject('../../lib/logout.js', mocks) -const logout = new Logout({ - flatOptions: _flatOptions, - config, -}) +const logout = new Logout(npm) test('token logout', async (t) => { t.plan(6) - _flatOptions.token = '@foo/' + flatOptions.token = '@foo/' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -39,7 +41,7 @@ test('token logout', async (t) => { ) } - config.clearCredentialsByURI = (registry) => { + npm.config.clearCredentialsByURI = (registry) => { t.equal( registry, 'https://registry.npmjs.org/', @@ -47,7 +49,7 @@ test('token logout', async (t) => { ) } - config.save = (type) => { + npm.config.save = (type) => { t.equal(type, 'user', 'should save to user config') } @@ -70,7 +72,7 @@ test('token logout', async (t) => { 'should call npm-registry-fetch with expected values' ) - delete _flatOptions.token + delete flatOptions.token result = null mocks['npm-registry-fetch'] = null config.clearCredentialsByURI = null @@ -86,9 +88,11 @@ test('token logout', async (t) => { test('token scoped logout', async (t) => { t.plan(8) - _flatOptions.token = '@foo/' - _flatOptions.scope = '@myscope' - _flatOptions['@myscope:registry'] = 'https://diff-registry.npmjs.com/' + flatOptions.token = '@foo/' + config.scope = '@myscope' + config['@myscope:registry'] = 'https://diff-registry.npmjs.com/' + flatOptions.scope = '@myscope' + flatOptions['@myscope:registry'] = 'https://diff-registry.npmjs.com/' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -99,7 +103,7 @@ test('token scoped logout', async (t) => { ) } - config.clearCredentialsByURI = (registry) => { + npm.config.clearCredentialsByURI = (registry) => { t.equal( registry, 'https://diff-registry.npmjs.com/', @@ -107,7 +111,7 @@ test('token scoped logout', async (t) => { ) } - config.delete = (ref, type) => { + npm.config.delete = (ref, type) => { t.equal( ref, '@myscope:registry', @@ -116,7 +120,7 @@ test('token scoped logout', async (t) => { t.equal(type, 'user', 'should delete from user config') } - config.save = (type) => { + npm.config.save = (type) => { t.equal(type, 'user', 'should save to user config') } @@ -140,9 +144,9 @@ test('token scoped logout', async (t) => { 'should call npm-registry-fetch with expected values' ) - _flatOptions.scope = '' - delete _flatOptions['@myscope:registry'] - delete _flatOptions.token + config.scope = '' + delete config['@myscope:registry'] + delete flatOptions.token result = null mocks['npm-registry-fetch'] = null config.clearCredentialsByURI = null @@ -158,8 +162,8 @@ test('token scoped logout', async (t) => { test('user/pass logout', async (t) => { t.plan(3) - _flatOptions.username = 'foo' - _flatOptions.password = 'bar' + flatOptions.username = 'foo' + flatOptions.password = 'bar' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -170,17 +174,17 @@ test('user/pass logout', async (t) => { ) } - config.clearCredentialsByURI = () => null - config.save = () => null + npm.config.clearCredentialsByURI = () => null + npm.config.save = () => null await new Promise((res, rej) => { logout.exec([], (err) => { t.ifError(err, 'should not error out') - delete _flatOptions.username - delete _flatOptions.password - config.clearCredentialsByURI = null - config.save = null + delete flatOptions.username + delete flatOptions.password + npm.config.clearCredentialsByURI = null + npm.config.save = null npmlog.verbose = null res() @@ -203,9 +207,9 @@ test('missing credentials', (t) => { test('ignore invalid scoped registry config', async (t) => { t.plan(5) - _flatOptions.token = '@foo/' - _flatOptions.scope = '@myscope' - _flatOptions['@myscope:registry'] = '' + flatOptions.token = '@foo/' + config.scope = '@myscope' + flatOptions['@myscope:registry'] = '' npmlog.verbose = (title, msg) => { t.equal(title, 'logout', 'should have correcct log prefix') @@ -216,7 +220,7 @@ test('ignore invalid scoped registry config', async (t) => { ) } - config.clearCredentialsByURI = (registry) => { + npm.config.clearCredentialsByURI = (registry) => { t.equal( registry, 'https://registry.npmjs.org/', @@ -224,8 +228,8 @@ test('ignore invalid scoped registry config', async (t) => { ) } - config.delete = () => null - config.save = () => null + npm.config.delete = () => null + npm.config.save = () => null await new Promise((res, rej) => { logout.exec([], (err) => { @@ -247,7 +251,7 @@ test('ignore invalid scoped registry config', async (t) => { 'should call npm-registry-fetch with expected values' ) - delete _flatOptions.token + delete flatOptions.token result = null mocks['npm-registry-fetch'] = null config.clearCredentialsByURI = null diff --git a/deps/npm/test/lib/ls.js b/deps/npm/test/lib/ls.js index bcbd3413563ddb..5367dec688658d 100644 --- a/deps/npm/test/lib/ls.js +++ b/deps/npm/test/lib/ls.js @@ -1,4 +1,5 @@ const t = require('tap') +const mockNpm = require('../fixtures/mock-npm') const { resolve } = require('path') const { utimesSync } = require('fs') @@ -84,12 +85,9 @@ const diffDepTypesNmFixture = { }, } -let prefix -let globalDir = 'MISSING_GLOBAL_DIR' let result = '' -// note this _flatOptions representations is for tests-only and does not -// represent exactly the properties found in the actual flatOptions obj -const _flatOptions = { +const LS = require('../../lib/ls.js') +const config = { all: true, color: false, dev: false, @@ -99,32 +97,18 @@ const _flatOptions = { link: false, only: null, parseable: false, - get prefix () { - return prefix - }, production: false, } -const LS = require('../../lib/ls.js') -const ls = new LS({ - flatOptions: _flatOptions, - limit: { - fetch: 3, - }, - get prefix () { - return _flatOptions.prefix - }, - get globalDir () { - return globalDir - }, - config: { - get (key) { - return _flatOptions[key] - }, - }, +const flatOptions = { +} +const npm = mockNpm({ + config, + flatOptions, output: msg => { result = msg }, }) +const ls = new LS(npm) const redactCwd = res => res && res.replace(/\\+/g, '/').replace(new RegExp(__dirname.replace(/\\+/g, '/'), 'gi'), '{CWD}') @@ -138,10 +122,10 @@ const cleanUpResult = (done, t) => { t.test('ls', (t) => { t.beforeEach(cleanUpResult) - _flatOptions.json = false - _flatOptions.unicode = false + config.json = false + config.unicode = false t.test('no args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -160,7 +144,7 @@ t.test('ls', (t) => { }) t.test('missing package.json', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ ...simpleNmFixture, }) ls.exec([], (err) => { @@ -175,7 +159,7 @@ t.test('ls', (t) => { }) t.test('extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -198,8 +182,8 @@ t.test('ls', (t) => { }) t.test('with filter arg', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -213,15 +197,15 @@ t.test('ls', (t) => { ls.exec(['lorem'], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('with dot filter arg', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -235,14 +219,14 @@ t.test('ls', (t) => { ls.exec(['.'], (err) => { t.ifError(err, 'should not throw on missing dep above current level') t.matchSnapshot(redactCwd(result), 'should output tree contaning only occurrences of filtered by package and colored output') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('with filter arg nested dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -261,7 +245,7 @@ t.test('ls', (t) => { }) t.test('with multiple filter args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -289,7 +273,7 @@ t.test('ls', (t) => { }) t.test('with missing filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -314,9 +298,9 @@ t.test('ls', (t) => { }) t.test('default --depth value should be 0', (t) => { - _flatOptions.all = false - _flatOptions.depth = undefined - prefix = t.testdir({ + config.all = false + config.depth = undefined + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -330,16 +314,16 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -353,16 +337,16 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=1', (t) => { - _flatOptions.all = false - _flatOptions.depth = 1 - prefix = t.testdir({ + config.all = false + config.depth = 1 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -414,14 +398,14 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps and their deps only') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('missing/invalid/extraneous', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -447,8 +431,8 @@ t.test('ls', (t) => { }) t.test('colored output', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -462,14 +446,14 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.equal(err.code, 'ELSPROBLEMS', 'should have error code') t.matchSnapshot(redactCwd(result), 'should output tree containing color info') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('--dev', (t) => { - _flatOptions.dev = true - prefix = t.testdir({ + config.dev = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -491,14 +475,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing dev deps') - _flatOptions.dev = false + config.dev = false t.end() }) }) t.test('--only=development', (t) => { - _flatOptions.only = 'development' - prefix = t.testdir({ + config.only = 'development' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -520,14 +504,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only development deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--link', (t) => { - _flatOptions.link = true - prefix = t.testdir({ + config.link = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -559,13 +543,13 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps') - _flatOptions.link = false + config.link = false t.end() }) }) t.test('print deduped symlinks', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'print-deduped-symlinks', version: '1.0.0', @@ -595,14 +579,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps') - _flatOptions.link = false + config.link = false t.end() }) }) t.test('--production', (t) => { - _flatOptions.production = true - prefix = t.testdir({ + config.production = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -624,14 +608,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing production deps') - _flatOptions.production = false + config.production = false t.end() }) }) t.test('--only=prod', (t) => { - _flatOptions.only = 'prod' - prefix = t.testdir({ + config.only = 'prod' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -653,14 +637,14 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only prod deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--long', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -682,16 +666,16 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree info with descriptions') - _flatOptions.long = true + config.long = true t.end() }) }) t.test('--long --depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - _flatOptions.long = true - prefix = t.testdir({ + config.all = false + config.depth = 0 + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -713,15 +697,15 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps with descriptions') - _flatOptions.all = true - _flatOptions.depth = Infinity - _flatOptions.long = false + config.all = true + config.depth = Infinity + config.long = false t.end() }) }) t.test('json read problems', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': '{broken json', }) ls.exec([], (err) => { @@ -732,7 +716,7 @@ t.test('ls', (t) => { }) t.test('empty location', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) ls.exec([], (err) => { t.ifError(err, 'should not error out on empty locations') t.matchSnapshot(redactCwd(result), 'should print empty result') @@ -741,7 +725,7 @@ t.test('ls', (t) => { }) t.test('invalid peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -768,8 +752,8 @@ t.test('ls', (t) => { }) t.test('invalid deduped dep', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'invalid-deduped-dep', version: '1.0.0', @@ -798,13 +782,13 @@ t.test('ls', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree signaling mismatching peer dep in problems') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('deduped missing dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -834,7 +818,7 @@ t.test('ls', (t) => { }) t.test('unmet peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -852,8 +836,8 @@ t.test('ls', (t) => { }) t.test('unmet optional dep', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -878,13 +862,13 @@ t.test('ls', (t) => { t.match(err.code, 'ELSPROBLEMS', 'should have ELSPROBLEMS error code') t.match(err.message, /invalid: optional-dep@1.0.0/, 'should have invalid dep error msg') t.matchSnapshot(redactCwd(result), 'should output tree with empty entry for missing optional deps') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('cycle deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -921,8 +905,8 @@ t.test('ls', (t) => { }) t.test('cycle deps with filter args', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -954,13 +938,13 @@ t.test('ls', (t) => { ls.exec(['a'], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('with no args dedupe entries', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1007,9 +991,9 @@ t.test('ls', (t) => { }) t.test('with no args dedupe entries and not displaying all', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1051,15 +1035,15 @@ t.test('ls', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('with args and dedupe entries', (t) => { - _flatOptions.color = true - prefix = t.testdir({ + npm.color = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1101,13 +1085,13 @@ t.test('ls', (t) => { ls.exec(['@npmcli/b'], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should print tree output containing deduped ref') - _flatOptions.color = false + npm.color = false t.end() }) }) t.test('with args and different order of items', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'dedupe-entries', version: '1.0.0', @@ -1154,7 +1138,7 @@ t.test('ls', (t) => { }) t.test('using aliases', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1189,7 +1173,7 @@ t.test('ls', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing aliases') t.end() @@ -1197,7 +1181,7 @@ t.test('ls', (t) => { }) t.test('resolved points to git ref', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1235,7 +1219,7 @@ t.test('ls', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing git refs') @@ -1244,7 +1228,7 @@ t.test('ls', (t) => { }) t.test('broken resolved field', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ node_modules: { a: { 'package.json': JSON.stringify({ @@ -1288,7 +1272,7 @@ t.test('ls', (t) => { }) t.test('from and resolved properties', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1334,7 +1318,7 @@ t.test('ls', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should not be printed in tree output') t.end() @@ -1342,7 +1326,7 @@ t.test('ls', (t) => { }) t.test('global', (t) => { - _flatOptions.global = true + config.global = true const fixtures = t.testdir({ node_modules: { a: { @@ -1369,18 +1353,18 @@ t.test('ls', (t) => { }) // mimics lib/npm.js globalDir getter but pointing to fixtures - globalDir = resolve(fixtures, 'node_modules') + npm.globalDir = resolve(fixtures, 'node_modules') ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should print tree and not mark top-level items extraneous') - globalDir = 'MISSING_GLOBAL_DIR' - _flatOptions.global = false + npm.globalDir = 'MISSING_GLOBAL_DIR' + config.global = false t.end() }) }) t.test('filtering by child of missing dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'filter-by-child-of-missing-dep', version: '1.0.0', @@ -1432,7 +1416,7 @@ t.test('ls', (t) => { }) t.test('loading a tree containing workspaces', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'filter-by-child-of-missing-dep', version: '1.0.0', @@ -1483,8 +1467,8 @@ t.test('ls', (t) => { }) t.test('filter pkg arg using depth option', (t) => { - _flatOptions.depth = 0 - prefix = t.testdir({ + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-pkg-arg-filter-with-depth-opt', version: '1.0.0', @@ -1540,7 +1524,7 @@ t.test('ls', (t) => { t.matchSnapshot(redactCwd(result), 'should print empty results msg') // if no --depth config is defined, should print path to dep - _flatOptions.depth = null // default config value + config.depth = null // default config value ls.exec(['d'], (err) => { t.ifError(err, 'should NOT have ELSPROBLEMS error code when filter') t.matchSnapshot(redactCwd(result), 'should print expected result') @@ -1550,7 +1534,7 @@ t.test('ls', (t) => { }) t.teardown(() => { - _flatOptions.depth = Infinity + config.depth = Infinity }) t.end() @@ -1558,11 +1542,11 @@ t.test('ls', (t) => { t.test('ls --parseable', (t) => { t.beforeEach(cleanUpResult) - _flatOptions.json = false - _flatOptions.unicode = false - _flatOptions.parseable = true + config.json = false + config.unicode = false + config.parseable = true t.test('no args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1581,7 +1565,7 @@ t.test('ls --parseable', (t) => { }) t.test('missing package.json', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ ...simpleNmFixture, }) ls.exec([], (err) => { @@ -1596,7 +1580,7 @@ t.test('ls --parseable', (t) => { }) t.test('extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1614,7 +1598,7 @@ t.test('ls --parseable', (t) => { }) t.test('with filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1633,7 +1617,7 @@ t.test('ls --parseable', (t) => { }) t.test('with filter arg nested dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1652,7 +1636,7 @@ t.test('ls --parseable', (t) => { }) t.test('with multiple filter args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1680,7 +1664,7 @@ t.test('ls --parseable', (t) => { }) t.test('with missing filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1705,9 +1689,9 @@ t.test('ls --parseable', (t) => { }) t.test('default --depth value should be 0', (t) => { - _flatOptions.all = false - _flatOptions.depth = undefined - prefix = t.testdir({ + config.all = false + config.depth = undefined + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1721,16 +1705,16 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output parseable output containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1744,16 +1728,16 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output tree containing only top-level dependencies') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=1', (t) => { - _flatOptions.all = false - _flatOptions.depth = 1 - prefix = t.testdir({ + config.all = false + config.depth = 1 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1767,14 +1751,14 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output parseable containing top-level deps and their deps only') - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('missing/invalid/extraneous', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1793,8 +1777,8 @@ t.test('ls --parseable', (t) => { }) t.test('--dev', (t) => { - _flatOptions.dev = true - prefix = t.testdir({ + config.dev = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1816,14 +1800,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing dev deps') - _flatOptions.dev = false + config.dev = false t.end() }) }) t.test('--only=development', (t) => { - _flatOptions.only = 'development' - prefix = t.testdir({ + config.only = 'development' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1845,14 +1829,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only development deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--link', (t) => { - _flatOptions.link = true - prefix = t.testdir({ + config.link = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1884,14 +1868,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing linked deps') - _flatOptions.link = false + config.link = false t.end() }) }) t.test('--production', (t) => { - _flatOptions.production = true - prefix = t.testdir({ + config.production = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1913,14 +1897,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing production deps') - _flatOptions.production = false + config.production = false t.end() }) }) t.test('--only=prod', (t) => { - _flatOptions.only = 'prod' - prefix = t.testdir({ + config.only = 'prod' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1942,14 +1926,14 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing only prod deps') - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--long', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1971,13 +1955,13 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree info with descriptions') - _flatOptions.long = true + config.long = true t.end() }) }) t.test('--long with extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -1996,8 +1980,8 @@ t.test('ls --parseable', (t) => { }) t.test('--long missing/invalid/extraneous', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2011,14 +1995,14 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.match(err, { code: 'ELSPROBLEMS' }, 'should list dep problems') t.matchSnapshot(redactCwd(result), 'should output parseable result containing EXTRANEOUS/INVALID labels') - _flatOptions.long = false + config.long = false t.end() }) }) t.test('--long print symlink target location', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2051,16 +2035,16 @@ t.test('ls --parseable', (t) => { ls.exec([], (err) => { t.ifError(err, 'npm ls') t.matchSnapshot(redactCwd(result), 'should output parseable results with symlink targets') - _flatOptions.long = false + config.long = false t.end() }) }) t.test('--long --depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - _flatOptions.long = true - prefix = t.testdir({ + config.all = false + config.depth = 0 + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2082,15 +2066,15 @@ t.test('ls --parseable', (t) => { }) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing top-level deps with descriptions') - _flatOptions.all = true - _flatOptions.depth = Infinity - _flatOptions.long = false + config.all = true + config.depth = Infinity + config.long = false t.end() }) }) t.test('json read problems', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': '{broken json', }) ls.exec([], (err) => { @@ -2101,7 +2085,7 @@ t.test('ls --parseable', (t) => { }) t.test('empty location', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) ls.exec([], (err) => { t.ifError(err, 'should not error out on empty locations') t.matchSnapshot(redactCwd(result), 'should print empty result') @@ -2110,7 +2094,7 @@ t.test('ls --parseable', (t) => { }) t.test('unmet peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2137,7 +2121,7 @@ t.test('ls --parseable', (t) => { }) t.test('unmet optional dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2167,7 +2151,7 @@ t.test('ls --parseable', (t) => { }) t.test('cycle deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2203,7 +2187,7 @@ t.test('ls --parseable', (t) => { }) t.test('using aliases', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2234,7 +2218,7 @@ t.test('ls --parseable', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing aliases') t.end() @@ -2242,7 +2226,7 @@ t.test('ls --parseable', (t) => { }) t.test('resolved points to git ref', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2279,7 +2263,7 @@ t.test('ls --parseable', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should output tree containing git refs') t.end() @@ -2287,7 +2271,7 @@ t.test('ls --parseable', (t) => { }) t.test('from and resolved properties', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2333,7 +2317,7 @@ t.test('ls --parseable', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should not be printed in tree output') t.end() @@ -2341,7 +2325,7 @@ t.test('ls --parseable', (t) => { }) t.test('global', (t) => { - _flatOptions.global = true + config.global = true const fixtures = t.testdir({ node_modules: { a: { @@ -2368,12 +2352,12 @@ t.test('ls --parseable', (t) => { }) // mimics lib/npm.js globalDir getter but pointing to fixtures - globalDir = resolve(fixtures, 'node_modules') + npm.globalDir = resolve(fixtures, 'node_modules') ls.exec([], () => { t.matchSnapshot(redactCwd(result), 'should print parseable output for global deps') - globalDir = 'MISSING_GLOBAL_DIR' - _flatOptions.global = false + npm.globalDir = 'MISSING_GLOBAL_DIR' + config.global = false t.end() }) }) @@ -2383,10 +2367,10 @@ t.test('ls --parseable', (t) => { t.test('ls --json', (t) => { t.beforeEach(cleanUpResult) - _flatOptions.json = true - _flatOptions.parseable = false + config.json = true + config.parseable = false t.test('no args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2425,7 +2409,7 @@ t.test('ls --json', (t) => { }) t.test('missing package.json', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ ...simpleNmFixture, }) ls.exec([], (err) => { @@ -2474,7 +2458,7 @@ t.test('ls --json', (t) => { }) t.test('extraneous deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2528,7 +2512,7 @@ t.test('ls --json', (t) => { }) t.test('with filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2564,7 +2548,7 @@ t.test('ls --json', (t) => { }) t.test('with filter arg nested dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2600,7 +2584,7 @@ t.test('ls --json', (t) => { }) t.test('with multiple filter args', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2648,7 +2632,7 @@ t.test('ls --json', (t) => { }) t.test('with missing filter arg', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2680,9 +2664,9 @@ t.test('ls --json', (t) => { }) t.test('default --depth value should now be 0', (t) => { - _flatOptions.all = false - _flatOptions.depth = undefined - prefix = t.testdir({ + config.all = false + config.depth = undefined + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2711,16 +2695,16 @@ t.test('ls --json', (t) => { }, 'should output json containing only top-level dependencies' ) - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - prefix = t.testdir({ + config.all = false + config.depth = 0 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2749,16 +2733,16 @@ t.test('ls --json', (t) => { }, 'should output json containing only top-level dependencies' ) - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('--depth=1', (t) => { - _flatOptions.all = false - _flatOptions.depth = 1 - prefix = t.testdir({ + config.all = false + config.depth = 1 + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2792,14 +2776,14 @@ t.test('ls --json', (t) => { }, 'should output json containing top-level deps and their deps only' ) - _flatOptions.all = true - _flatOptions.depth = Infinity + config.all = true + config.depth = Infinity t.end() }) }) t.test('missing/invalid/extraneous', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2858,8 +2842,8 @@ t.test('ls --json', (t) => { }) t.test('--dev', (t) => { - _flatOptions.dev = true - prefix = t.testdir({ + config.dev = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2899,14 +2883,14 @@ t.test('ls --json', (t) => { }, 'should output json containing dev deps' ) - _flatOptions.dev = false + config.dev = false t.end() }) }) t.test('--only=development', (t) => { - _flatOptions.only = 'development' - prefix = t.testdir({ + config.only = 'development' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2946,14 +2930,14 @@ t.test('ls --json', (t) => { }, 'should output json containing only development deps' ) - _flatOptions.only = null + config.only = null t.end() }) }) t.test('--link', (t) => { - _flatOptions.link = true - prefix = t.testdir({ + config.link = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -2998,14 +2982,14 @@ t.test('ls --json', (t) => { }, 'should output json containing linked deps' ) - _flatOptions.link = false + config.link = false t.end() }) }) t.test('--production', (t) => { - _flatOptions.production = true - prefix = t.testdir({ + config.production = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3039,14 +3023,14 @@ t.test('ls --json', (t) => { }, 'should output json containing production deps' ) - _flatOptions.production = false + config.production = false t.end() }) }) t.test('--only=prod', (t) => { - _flatOptions.only = 'prod' - prefix = t.testdir({ + config.only = 'prod' + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3080,13 +3064,13 @@ t.test('ls --json', (t) => { }, 'should output json containing only prod deps' ) - _flatOptions.only = null + config.only = null t.end() }) }) t.test('from lockfile', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ node_modules: { '@isaacs': { 'dedupe-tests-a': { @@ -3215,8 +3199,8 @@ t.test('ls --json', (t) => { }) t.test('--long', (t) => { - _flatOptions.long = true - prefix = t.testdir({ + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3345,16 +3329,16 @@ t.test('ls --json', (t) => { }, 'should output long json info' ) - _flatOptions.long = true + config.long = true t.end() }) }) t.test('--long --depth=0', (t) => { - _flatOptions.all = false - _flatOptions.depth = 0 - _flatOptions.long = true - prefix = t.testdir({ + config.all = false + config.depth = 0 + config.long = true + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3446,15 +3430,15 @@ t.test('ls --json', (t) => { }, 'should output json containing top-level deps in long format' ) - _flatOptions.all = true - _flatOptions.depth = Infinity - _flatOptions.long = false + config.all = true + config.depth = Infinity + config.long = false t.end() }) }) t.test('json read problems', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': '{broken json', }) ls.exec([], (err) => { @@ -3475,7 +3459,7 @@ t.test('ls --json', (t) => { }) t.test('empty location', (t) => { - prefix = t.testdir({}) + npm.prefix = t.testdir({}) ls.exec([], (err) => { t.ifError(err, 'should not error out on empty locations') t.deepEqual( @@ -3488,7 +3472,7 @@ t.test('ls --json', (t) => { }) t.test('unmet peer dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3547,7 +3531,7 @@ t.test('ls --json', (t) => { }) t.test('unmet optional dep', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3611,7 +3595,7 @@ t.test('ls --json', (t) => { }) t.test('cycle deps', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3667,7 +3651,7 @@ t.test('ls --json', (t) => { }) t.test('using aliases', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3697,7 +3681,7 @@ t.test('ls --json', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.deepEqual( jsonParse(result), @@ -3718,7 +3702,7 @@ t.test('ls --json', (t) => { }) t.test('resolved points to git ref', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3757,7 +3741,7 @@ t.test('ls --json', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.deepEqual( jsonParse(result), @@ -3778,7 +3762,7 @@ t.test('ls --json', (t) => { }) t.test('from and resolved properties', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'test-npm-ls', version: '1.0.0', @@ -3841,7 +3825,7 @@ t.test('ls --json', (t) => { }, }, }) - touchHiddenPackageLock(prefix) + touchHiddenPackageLock(npm.prefix) ls.exec([], () => { t.deepEqual( jsonParse(result), @@ -3862,7 +3846,7 @@ t.test('ls --json', (t) => { }) t.test('node.name fallback if missing root package name', (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ version: '1.0.0', }), @@ -3881,7 +3865,7 @@ t.test('ls --json', (t) => { }) t.test('global', (t) => { - _flatOptions.global = true + config.global = true const fixtures = t.testdir({ node_modules: { a: { @@ -3908,7 +3892,7 @@ t.test('ls --json', (t) => { }) // mimics lib/npm.js globalDir getter but pointing to fixtures - globalDir = resolve(fixtures, 'node_modules') + npm.globalDir = resolve(fixtures, 'node_modules') ls.exec([], () => { t.deepEqual( @@ -3931,8 +3915,8 @@ t.test('ls --json', (t) => { }, 'should print json output for global deps' ) - globalDir = 'MISSING_GLOBAL_DIR' - _flatOptions.global = false + npm.globalDir = 'MISSING_GLOBAL_DIR' + config.global = false t.end() }) }) diff --git a/deps/npm/test/lib/npm.js b/deps/npm/test/lib/npm.js index 87cbea8f2c6178..57391939800b5b 100644 --- a/deps/npm/test/lib/npm.js +++ b/deps/npm/test/lib/npm.js @@ -42,7 +42,7 @@ const npmlog = require('npmlog') const npmPath = resolve(__dirname, '..', '..') const Config = require('@npmcli/config') -const { types, defaults, shorthands } = require('../../lib/utils/config.js') +const { definitions, shorthands, flatten } = require('../../lib/utils/config') const freshConfig = (opts = {}) => { for (const env of Object.keys(process.env).filter(e => /^npm_/.test(e))) delete process.env[env] @@ -50,12 +50,12 @@ const freshConfig = (opts = {}) => { process.env.npm_config_cache = CACHE npm.config = new Config({ - types, - defaults, + definitions, shorthands, npmPath, log: npmlog, ...opts, + flatten, }) } @@ -145,6 +145,7 @@ t.test('npm.load', t => { t.equal(npm.loading, false, 'not loading yet') const p = npm.load(first).then(() => { + t.ok(npm.usage, 'has usage') npm.config.set('prefix', dir) t.match(npm, { loaded: true, @@ -160,7 +161,7 @@ t.test('npm.load', t => { npm.load(third) t.equal(thirdCalled, true, 'third callbback got called') t.match(logs, [ - ['timing', 'npm:load', /Completed in [0-9]+ms/], + ['timing', 'npm:load', /Completed in [0-9.]+ms/], ]) logs.length = 0 @@ -288,6 +289,11 @@ t.test('npm.load', t => { t.equal(npm.config.get('scope'), '@foo', 'added the @ sign to scope') t.match(logs.filter(l => l[0] !== 'timing' || !/^config:/.test(l[1])), [ + [ + 'timing', + 'npm:load:whichnode', + /Completed in [0-9.]+ms/, + ], [ 'verbose', 'node symlink', @@ -296,7 +302,7 @@ t.test('npm.load', t => { [ 'timing', 'npm:load', - /Completed in [0-9]+ms/, + /Completed in [0-9.]+ms/, ], ]) logs.length = 0 @@ -307,6 +313,9 @@ t.test('npm.load', t => { if (er) throw er + t.equal(npm.command, 'll', 'command set to first npm command') + t.equal(npm.flatOptions.npmCommand, 'll', 'npmCommand flatOption set') + t.same(consoleLogs, [[npm.commands.ll.usage]], 'print usage') consoleLogs.length = 0 npm.config.set('usage', false) @@ -318,6 +327,9 @@ t.test('npm.load', t => { if (er) throw er + t.strictSame([npm.command, npm.flatOptions.npmCommand], ['ll', 'll'], + 'does not change npm.command when another command is called') + t.match(logs, [ [ 'error', @@ -328,12 +340,12 @@ t.test('npm.load', t => { [ 'timing', 'command:config', - /Completed in [0-9]+ms/, + /Completed in [0-9.]+ms/, ], [ 'timing', 'command:get', - /Completed in [0-9]+ms/, + /Completed in [0-9.]+ms/, ], ]) t.same(consoleLogs, [['scope=@foo\n\u2010not-a-dash=undefined']]) @@ -343,6 +355,84 @@ t.test('npm.load', t => { await new Promise((res) => setTimeout(res)) }) + t.test('workpaces-aware configs and commands', async t => { + const dir = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { test: 'echo test a' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '1.0.0', + scripts: { test: 'echo test b' }, + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'root', + version: '1.0.0', + workspaces: ['./packages/*'], + }), + '.npmrc': '', + }) + + const { log } = console + const consoleLogs = [] + console.log = (...msg) => consoleLogs.push(msg) + + const { execPath } = process + t.teardown(() => { + console.log = log + }) + + freshConfig({ + argv: [ + execPath, + process.argv[1], + '--userconfig', + resolve(dir, '.npmrc'), + '--color', + 'false', + '--workspaces', + 'true', + ], + }) + + await npm.load(er => { + if (er) + throw er + }) + + npm.localPrefix = dir + + await new Promise((res, rej) => { + npm.commands['run-script']([], er => { + if (er) + rej(er) + + t.match( + consoleLogs, + [ + ['Lifecycle scripts included in a@1.0.0:'], + [' test\n echo test a'], + [''], + ['Lifecycle scripts included in b@1.0.0:'], + [' test\n echo test b'], + [''], + ], + 'should exec workspaces version of commands' + ) + + res() + }) + }) + }) + t.end() }) diff --git a/deps/npm/test/lib/outdated.js b/deps/npm/test/lib/outdated.js index 02952971b69f99..5aff7c37ac11ad 100644 --- a/deps/npm/test/lib/outdated.js +++ b/deps/npm/test/lib/outdated.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const packument = spec => { const mocks = { @@ -89,12 +90,13 @@ const outdated = (dir, opts) => { packument, }, }) - return new Outdated({ + const npm = mockNpm({ + ...opts, prefix: dir, globalDir: `${globalDir}/node_modules`, - flatOptions: opts, output, }) + return new Outdated(npm) } t.beforeEach((done) => { @@ -173,7 +175,7 @@ t.test('should display outdated deps', t => { t.test('outdated global', t => { outdated(null, { - global: true, + config: { global: true }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -182,7 +184,9 @@ t.test('should display outdated deps', t => { t.test('outdated', t => { outdated(testDir, { - global: false, + config: { + global: false, + }, color: true, }).exec([], () => { t.matchSnapshot(logs) @@ -192,9 +196,11 @@ t.test('should display outdated deps', t => { t.test('outdated --omit=dev', t => { outdated(testDir, { - global: false, + config: { + global: false, + omit: ['dev'], + }, color: true, - omit: ['dev'], }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -203,9 +209,11 @@ t.test('should display outdated deps', t => { t.test('outdated --omit=dev --omit=peer', t => { outdated(testDir, { - global: false, + config: { + global: false, + omit: ['dev', 'peer'], + }, color: true, - omit: ['dev', 'peer'], }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -214,9 +222,11 @@ t.test('should display outdated deps', t => { t.test('outdated --omit=prod', t => { outdated(testDir, { - global: false, + config: { + global: false, + omit: ['prod'], + }, color: true, - omit: ['prod'], }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -225,8 +235,10 @@ t.test('should display outdated deps', t => { t.test('outdated --long', t => { outdated(testDir, { - global: false, - long: true, + config: { + global: false, + long: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -235,8 +247,10 @@ t.test('should display outdated deps', t => { t.test('outdated --json', t => { outdated(testDir, { - global: false, - json: true, + config: { + global: false, + json: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -245,9 +259,11 @@ t.test('should display outdated deps', t => { t.test('outdated --json --long', t => { outdated(testDir, { - global: false, - json: true, - long: true, + config: { + global: false, + json: true, + long: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -256,8 +272,10 @@ t.test('should display outdated deps', t => { t.test('outdated --parseable', t => { outdated(testDir, { - global: false, - parseable: true, + config: { + global: false, + parseable: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -266,9 +284,11 @@ t.test('should display outdated deps', t => { t.test('outdated --parseable --long', t => { outdated(testDir, { - global: false, - parseable: true, - long: true, + config: { + global: false, + parseable: true, + long: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -277,7 +297,9 @@ t.test('should display outdated deps', t => { t.test('outdated --all', t => { outdated(testDir, { - all: true, + config: { + all: true, + }, }).exec([], () => { t.matchSnapshot(logs) t.end() @@ -286,7 +308,9 @@ t.test('should display outdated deps', t => { t.test('outdated specific dep', t => { outdated(testDir, { - global: false, + config: { + global: false, + }, }).exec(['alpha'], () => { t.matchSnapshot(logs) t.end() diff --git a/deps/npm/test/lib/pack.js b/deps/npm/test/lib/pack.js index ce319be76ba3f4..a5163acfdc49b1 100644 --- a/deps/npm/test/lib/pack.js +++ b/deps/npm/test/lib/pack.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const OUTPUT = [] const output = (...msg) => OUTPUT.push(msg) @@ -25,14 +26,15 @@ t.test('should pack current directory with no arguments', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: false, json: false, - dryRun: false, + 'dry-run': false, }, output, }) + const pack = new Pack(npm) pack.exec([], er => { if (er) @@ -60,14 +62,15 @@ t.test('should pack given directory', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: true, json: true, - dryRun: true, + 'dry-run': true, }, output, }) + const pack = new Pack(npm) pack.exec([testDir], er => { if (er) @@ -95,14 +98,15 @@ t.test('should pack given directory for scoped package', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: true, json: true, - dryRun: true, + 'dry-run': true, }, output, }) + const pack = new Pack(npm) return pack.exec([testDir], er => { if (er) @@ -129,14 +133,15 @@ t.test('should log pack contents', (t) => { clearProgress: () => {}, }, }) - const pack = new Pack({ - flatOptions: { + const npm = mockNpm({ + config: { unicode: false, json: false, - dryRun: false, + 'dry-run': false, }, output, }) + const pack = new Pack(npm) pack.exec([], er => { if (er) diff --git a/deps/npm/test/lib/ping.js b/deps/npm/test/lib/ping.js index f3563036f71fd6..95361035acb53d 100644 --- a/deps/npm/test/lib/ping.js +++ b/deps/npm/test/lib/ping.js @@ -1,14 +1,15 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('pings', (t) => { t.plan(8) - const flatOptions = { registry: 'https://registry.npmjs.org' } + const registry = 'https://registry.npmjs.org' let noticeCalls = 0 const Ping = requireInject('../../lib/ping.js', { '../../lib/utils/ping.js': function (spec) { - t.equal(spec, flatOptions, 'passes flatOptions') + t.equal(spec.registry, registry, 'passes flatOptions') return {} }, npmlog: { @@ -16,7 +17,7 @@ test('pings', (t) => { ++noticeCalls if (noticeCalls === 1) { t.equal(type, 'PING', 'should log a PING') - t.equal(spec, flatOptions.registry, 'should log the registry url') + t.equal(spec, registry, 'should log the registry url') } else { t.equal(type, 'PONG', 'should log a PONG') t.match(spec, /\d+ms/, 'should log the elapsed milliseconds') @@ -24,7 +25,11 @@ test('pings', (t) => { }, }, }) - const ping = new Ping({ flatOptions }) + const npm = mockNpm({ + config: { registry }, + flatOptions: { registry }, + }) + const ping = new Ping(npm) ping.exec([], (err) => { t.equal(noticeCalls, 2, 'should have logged 2 lines') @@ -36,12 +41,12 @@ test('pings', (t) => { test('pings and logs details', (t) => { t.plan(10) - const flatOptions = { registry: 'https://registry.npmjs.org' } + const registry = 'https://registry.npmjs.org' const details = { extra: 'data' } let noticeCalls = 0 const Ping = requireInject('../../lib/ping.js', { '../../lib/utils/ping.js': function (spec) { - t.equal(spec, flatOptions, 'passes flatOptions') + t.equal(spec.registry, registry, 'passes flatOptions') return details }, npmlog: { @@ -49,7 +54,7 @@ test('pings and logs details', (t) => { ++noticeCalls if (noticeCalls === 1) { t.equal(type, 'PING', 'should log a PING') - t.equal(spec, flatOptions.registry, 'should log the registry url') + t.equal(spec, registry, 'should log the registry url') } else if (noticeCalls === 2) { t.equal(type, 'PONG', 'should log a PONG') t.match(spec, /\d+ms/, 'should log the elapsed milliseconds') @@ -61,7 +66,11 @@ test('pings and logs details', (t) => { }, }, }) - const ping = new Ping({ flatOptions }) + const npm = mockNpm({ + config: { registry }, + flatOptions: { registry }, + }) + const ping = new Ping(npm) ping.exec([], (err) => { t.equal(noticeCalls, 3, 'should have logged 3 lines') @@ -73,12 +82,12 @@ test('pings and logs details', (t) => { test('pings and returns json', (t) => { t.plan(11) - const flatOptions = { registry: 'https://registry.npmjs.org', json: true } + const registry = 'https://registry.npmjs.org' const details = { extra: 'data' } let noticeCalls = 0 const Ping = requireInject('../../lib/ping.js', { '../../lib/utils/ping.js': function (spec) { - t.equal(spec, flatOptions, 'passes flatOptions') + t.equal(spec.registry, registry, 'passes flatOptions') return details }, npmlog: { @@ -86,7 +95,7 @@ test('pings and returns json', (t) => { ++noticeCalls if (noticeCalls === 1) { t.equal(type, 'PING', 'should log a PING') - t.equal(spec, flatOptions.registry, 'should log the registry url') + t.equal(spec, registry, 'should log the registry url') } else { t.equal(type, 'PONG', 'should log a PONG') t.match(spec, /\d+ms/, 'should log the elapsed milliseconds') @@ -94,15 +103,17 @@ test('pings and returns json', (t) => { }, }, }) - const ping = new Ping({ - flatOptions, + const npm = mockNpm({ + config: { registry, json: true }, + flatOptions: { registry }, output: function (spec) { const parsed = JSON.parse(spec) - t.equal(parsed.registry, flatOptions.registry, 'returns the correct registry url') + t.equal(parsed.registry, registry, 'returns the correct registry url') t.match(parsed.details, details, 'prints returned details') t.type(parsed.time, 'number', 'returns time as a number') }, }) + const ping = new Ping(npm) ping.exec([], (err) => { t.equal(noticeCalls, 2, 'should have logged 2 lines') diff --git a/deps/npm/test/lib/profile.js b/deps/npm/test/lib/profile.js index d1be93b0cbb62e..5b1210615aa2f4 100644 --- a/deps/npm/test/lib/profile.js +++ b/deps/npm/test/lib/profile.js @@ -1,20 +1,24 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = '' -const flatOptions = { +const config = { otp: '', json: false, parseable: false, registry: 'https://registry.npmjs.org/', } -const npm = { - config: {}, - flatOptions: { ...flatOptions }, +const flatOptions = { + registry: 'https://registry.npmjs.org/', +} +const npm = mockNpm({ + config, + flatOptions, output: (...msg) => { result = result ? `${result}\n${msg.join('\n')}` : msg.join('\n') }, -} +}) const mocks = { ansistyles: { bright: a => a }, npmlog: { @@ -65,8 +69,10 @@ const userProfile = { t.afterEach(cb => { result = '' - npm.config = {} - npm.flatOptions = { ...flatOptions } + flatOptions.otp = '' + config.json = false + config.parseable = false + config.registry = 'https://registry.npmjs.org/' cb() }) @@ -111,7 +117,7 @@ t.test('profile get no args', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['get'], err => { if (err) @@ -127,7 +133,7 @@ t.test('profile get no args', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['get'], err => { if (err) @@ -256,7 +262,7 @@ t.test('profile get <key>', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['get', 'name'], err => { if (err) @@ -272,7 +278,7 @@ t.test('profile get <key>', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['get', 'name'], err => { if (err) @@ -316,7 +322,7 @@ t.test('profile get multiple args', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['get', 'name', 'email', 'github'], err => { if (err) @@ -332,7 +338,7 @@ t.test('profile get multiple args', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['get', 'name', 'email', 'github'], err => { if (err) @@ -451,7 +457,7 @@ t.test('profile set <key> <value>', t => { t.test('--json', t => { t.plan(2) - npm.flatOptions.json = true + config.json = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -476,7 +482,7 @@ t.test('profile set <key> <value>', t => { t.test('--parseable', t => { t.plan(2) - npm.flatOptions.parseable = true + config.parseable = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -705,7 +711,7 @@ t.test('enable-2fa', t => { }) t.test('no support for --json output', t => { - npm.flatOptions.json = true + config.json = true profile.exec(['enable-2fa', 'auth-only'], err => { t.match( @@ -719,7 +725,7 @@ t.test('enable-2fa', t => { }) t.test('no support for --parseable output', t => { - npm.flatOptions.parseable = true + config.parseable = true profile.exec(['enable-2fa', 'auth-only'], err => { t.match( @@ -817,18 +823,16 @@ t.test('enable-2fa', t => { t.plan(10) // mock legacy basic auth style - npm.config = { - getCredentialsByURI (reg) { - t.equal(reg, flatOptions.registry, 'should use expected registry') - return { auth: Buffer.from('foo:bar').toString('base64') } - }, - setCredentialsByURI (registry, { token }) { - t.equal(registry, flatOptions.registry, 'should set expected registry') - t.equal(token, 'token', 'should set expected token') - }, - save (type) { - t.equal(type, 'user', 'should save to user config') - }, + npm.config.getCredentialsByURI = (reg) => { + t.equal(reg, flatOptions.registry, 'should use expected registry') + return { auth: Buffer.from('foo:bar').toString('base64') } + } + npm.config.setCredentialsByURI = (registry, { token }) => { + t.equal(registry, flatOptions.registry, 'should set expected registry') + t.equal(token, 'token', 'should set expected token') + } + npm.config.save = (type) => { + t.equal(type, 'user', 'should save to user config') } const npmProfile = { @@ -901,12 +905,10 @@ t.test('enable-2fa', t => { t.test('from token and set otp, retries on pending and verifies with qrcode', t => { t.plan(4) - npm.flatOptions.otp = '1234' + flatOptions.otp = '1234' - npm.config = { - getCredentialsByURI () { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = () => { + return { token: 'token' } } let setCount = 0 @@ -1003,12 +1005,10 @@ t.test('enable-2fa', t => { }) t.test('from token and set otp, retrieves invalid otp', t => { - npm.flatOptions.otp = '1234' + flatOptions.otp = '1234' - npm.config = { - getCredentialsByURI () { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = () => { + return { token: 'token' } } const npmProfile = { @@ -1055,12 +1055,11 @@ t.test('enable-2fa', t => { }) t.test('from token auth provides --otp config arg', t => { - npm.flatOptions.otp = '123456' + flatOptions.otp = '123456' + flatOptions.otp = '123456' - npm.config = { - getCredentialsByURI (reg) { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = (reg) => { + return { token: 'token' } } const npmProfile = { @@ -1105,10 +1104,8 @@ t.test('enable-2fa', t => { }) t.test('missing tfa from user profile', t => { - npm.config = { - getCredentialsByURI (reg) { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = (reg) => { + return { token: 'token' } } const npmProfile = { @@ -1156,10 +1153,8 @@ t.test('enable-2fa', t => { }) t.test('defaults to auth-and-writes permission if no mode specified', t => { - npm.config = { - getCredentialsByURI (reg) { - return { token: 'token' } - }, + npm.config.getCredentialsByURI = (reg) => { + return { token: 'token' } } const npmProfile = { @@ -1303,7 +1298,7 @@ t.test('disable-2fa', t => { }) t.test('--json', t => { - npm.flatOptions.json = true + config.json = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -1326,7 +1321,7 @@ t.test('disable-2fa', t => { }) t.test('--parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true const Profile = requireInject('../../lib/profile.js', { ...mocks, @@ -1354,7 +1349,7 @@ t.test('disable-2fa', t => { t.test('--otp config already set', t => { t.plan(3) - npm.flatOptions.otp = '123456' + flatOptions.otp = '123456' const npmProfile = { async get () { diff --git a/deps/npm/test/lib/publish.js b/deps/npm/test/lib/publish.js index 6337a1fcf0b2a2..f61377b54f9971 100644 --- a/deps/npm/test/lib/publish.js +++ b/deps/npm/test/lib/publish.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const fs = require('fs') // The way we set loglevel is kind of convoluted, and there is no way to affect @@ -10,11 +11,13 @@ const log = require('npmlog') log.level = 'silent' // mock config -const {defaults} = require('../../lib/utils/config.js') +const {definitions} = require('../../lib/utils/config') +const defaults = Object.entries(definitions).reduce((defaults, [key, def]) => { + defaults[key] = def.default + return defaults +}, {}) -const config = { - list: [defaults], -} +const config = defaults t.afterEach(cb => { log.level = 'silent' @@ -54,18 +57,17 @@ t.test('should publish with libnpmpublish, passing through flatOptions and respe }, }, }) - const publish = new Publish({ + const npm = mockNpm({ + config, flatOptions: { customValue: true, }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -104,15 +106,12 @@ t.test('re-loads publishConfig.registry if added during script process', (t) => }, }, }) - const publish = new Publish({ - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, - }) + const npm = mockNpm({ config }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -148,21 +147,17 @@ t.test('if loglevel=info and json, should not output package contents', (t) => { }, }, }) - const publish = new Publish({ - flatOptions: { - json: true, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, defaults.registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, + const npm = mockNpm({ + config: { ...config, json: true }, output: () => { t.pass('output is called') }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, defaults.registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -198,20 +193,17 @@ t.test('if loglevel=silent and dry-run, should not output package contents or pu }, }, }) - const publish = new Publish({ - flatOptions: { - dryRun: true, - }, - config: { - ...config, - getCredentialsByURI: () => { - throw new Error('should not call getCredentialsByURI in dry run') - }, - }, + const npm = mockNpm({ + config: { ...config, 'dry-run': true }, output: () => { throw new Error('should not output in dry run mode') }, }) + npm.config.getCredentialsByURI = () => { + throw new Error('should not call getCredentialsByURI in dry run') + } + + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -247,20 +239,16 @@ t.test('if loglevel=info and dry-run, should not publish, should log package con }, }, }) - const publish = new Publish({ - flatOptions: { - dryRun: true, - }, - config: { - ...config, - getCredentialsByURI: () => { - throw new Error('should not call getCredentialsByURI in dry run') - }, - }, + const npm = mockNpm({ + config: { ...config, 'dry-run': true }, output: () => { t.pass('output fn is called') }, }) + npm.config.getCredentialsByURI = () => { + throw new Error('should not call getCredentialsByURI in dry run') + } + const publish = new Publish(npm) publish.exec([testDir], (er) => { if (er) @@ -285,12 +273,10 @@ t.test('throws when invalid tag', (t) => { t.plan(1) const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - flatOptions: { - defaultTag: '0.0.13', - }, - config, + const npm = mockNpm({ + config: { ...config, tag: '0.0.13' }, }) + const publish = new Publish(npm) publish.exec([], (err) => { t.match(err, { @@ -331,15 +317,12 @@ t.test('can publish a tarball', t => { }, }, }) - const publish = new Publish({ - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, defaults.registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, - }) + const npm = mockNpm({ config }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, defaults.registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) publish.exec([`${testDir}/tarball/package.tgz`], (er) => { if (er) @@ -352,15 +335,12 @@ t.test('can publish a tarball', t => { t.test('should check auth for default registry', t => { t.plan(2) const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, defaults.registry, 'gets credentials for expected registry') - return {} - }, - }, - }) + const npm = mockNpm({ config }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, defaults.registry, 'gets credentials for expected registry') + return {} + } + const publish = new Publish(npm) publish.exec([], (err) => { t.match(err, { @@ -375,18 +355,15 @@ t.test('should check auth for configured registry', t => { t.plan(2) const registry = 'https://some.registry' const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - flatOptions: { - registry, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return {} - }, - }, + const npm = mockNpm({ + config, + flatOptions: { registry }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return {} + } + const publish = new Publish(npm) publish.exec([], (err) => { t.match(err, { @@ -408,18 +385,15 @@ t.test('should check auth for scope specific registry', t => { }) const Publish = requireInject('../../lib/publish.js') - const publish = new Publish({ - flatOptions: { - '@npm:registry': registry, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return {} - }, - }, + const npm = mockNpm({ + config, + flatOptions: { '@npm:registry': registry }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return {} + } + const publish = new Publish(npm) publish.exec([testDir], (err) => { t.match(err, { @@ -448,18 +422,16 @@ t.test('should use auth for scope specific registry', t => { }, }, }) - const publish = new Publish({ - flatOptions: { - '@npm:registry': registry, - }, - config: { - ...config, - getCredentialsByURI: (uri) => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, + const npm = mockNpm({ + config, + flatOptions: { '@npm:registry': registry }, }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) + publish.exec([testDir], (er) => { if (er) throw er @@ -489,9 +461,60 @@ t.test('read registry only from publishConfig', t => { }, }, }) + const npm = mockNpm({ + config, + }) + npm.config.getCredentialsByURI = (uri) => { + t.same(uri, registry, 'gets credentials for expected registry') + return { token: 'some.registry.token' } + } + const publish = new Publish(npm) + + publish.exec([testDir], (er) => { + if (er) + throw er + t.pass('got to callback') + t.end() + }) +}) + +t.test('able to publish after if encountered multiple configs', t => { + t.plan(3) + + const registry = 'https://some.registry' + const tag = 'better-tag' + const publishConfig = { registry } + const testDir = t.testdir({ + 'package.json': JSON.stringify({ + name: 'my-cool-pkg', + version: '1.0.0', + publishConfig, + }, null, 2), + }) + + const configList = [defaults] + configList.unshift(Object.assign(Object.create(configList[0]), { + registry: `https://other.registry`, + tag: 'some-tag', + })) + configList.unshift(Object.assign(Object.create(configList[0]), { tag })) + + const Publish = requireInject('../../lib/publish.js', { + libnpmpublish: { + publish: (manifest, tarData, opts) => { + t.same(opts.defaultTag, tag, 'gets option for expected tag') + }, + }, + }) const publish = new Publish({ + // what would be flattened by the configList created above + flatOptions: { + defaultTag: 'better-tag', + registry: 'https://other.registry', + }, config: { - ...config, + get: key => configList[0][key], + list: configList, getCredentialsByURI: (uri) => { t.same(uri, registry, 'gets credentials for expected registry') return { token: 'some.registry.token' } diff --git a/deps/npm/test/lib/rebuild.js b/deps/npm/test/lib/rebuild.js index 1eb45e0d1d7bd2..6e144b7e11cc70 100644 --- a/deps/npm/test/lib/rebuild.js +++ b/deps/npm/test/lib/rebuild.js @@ -1,25 +1,27 @@ const fs = require('fs') const { resolve } = require('path') +const mockNpm = require('../fixtures/mock-npm') const t = require('tap') let result = '' -const npm = { +const config = { + global: false, +} +const npm = mockNpm({ globalDir: '', - flatOptions: { - global: false, - }, + config, prefix: '', output: (...msg) => { result += msg.join('\n') }, -} +}) const Rebuild = require('../../lib/rebuild.js') const rebuild = new Rebuild(npm) t.afterEach(cb => { npm.prefix = '' - npm.flatOptions.global = false + config.global = false npm.globalDir = '' result = '' cb() @@ -34,7 +36,7 @@ t.test('no args', t => { version: '1.0.0', bin: 'cwd', scripts: { - preinstall: `node -e 'require("fs").writeFileSync("cwd", "")'`, + preinstall: "node -e \"require('fs').writeFileSync('cwd', '')\"", }, }), }, @@ -44,7 +46,7 @@ t.test('no args', t => { version: '1.0.0', bin: 'cwd', scripts: { - preinstall: `node -e 'require("fs").writeFileSync("cwd", "")'`, + preinstall: "node -e \"require('fs').writeFileSync('cwd', '')\"", }, }), }, @@ -237,7 +239,7 @@ t.test('global prefix', t => { }, }) - npm.flatOptions.global = true + config.global = true npm.globalDir = resolve(globalPath, 'lib', 'node_modules') rebuild.exec([], err => { diff --git a/deps/npm/test/lib/run-script.js b/deps/npm/test/lib/run-script.js index 0566daf2341f43..f7eb46fedf5eb5 100644 --- a/deps/npm/test/lib/run-script.js +++ b/deps/npm/test/lib/run-script.js @@ -1,37 +1,60 @@ +const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') + +const normalizePath = p => p + .replace(/\\+/g, '/') + .replace(/\r\n/g, '\n') + +const cleanOutput = (str) => normalizePath(str) + .replace(normalizePath(process.cwd()), '{CWD}') const RUN_SCRIPTS = [] -const npm = { +const flatOptions = { + scriptShell: undefined, +} +const config = { + json: false, + parseable: false, + 'if-present': false, +} + +const npm = mockNpm({ localPrefix: __dirname, - flatOptions: { - scriptShell: undefined, - json: false, - parseable: false, - }, - config: { - settings: { - 'if-present': false, + flatOptions, + config, + commands: { + help: { + description: 'test help description', }, - get: k => npm.config.settings[k], - set: (k, v) => { - npm.config.settings[k] = v + test: { + description: 'test test description', }, }, output: (...msg) => output.push(msg), -} +}) const output = [] +const npmlog = { + disableProgress: () => null, + level: 'warn', + error: () => null, +} + t.afterEach(cb => { + npm.color = false + npmlog.level = 'warn' + npmlog.error = () => null output.length = 0 RUN_SCRIPTS.length = 0 - npm.flatOptions.json = false - npm.flatOptions.parseable = false + config['if-present'] = false + config.json = false + config.parseable = false cb() }) -const npmlog = { level: 'warn' } const getRS = windows => { const RunScript = requireInject('../../lib/run-script.js', { '@npmcli/run-script': Object.assign(async opts => { @@ -261,26 +284,41 @@ t.test('try to run missing script', t => { npm.localPrefix = t.testdir({ 'package.json': JSON.stringify({ scripts: { hello: 'world' }, + bin: { goodnight: 'moon' }, }), }) t.test('no suggestions', t => { runScript.exec(['notevenclose'], er => { t.match(er, { - message: 'missing script: notevenclose', + message: 'Missing script: "notevenclose"', }) t.end() }) }) - t.test('suggestions', t => { + t.test('script suggestions', t => { runScript.exec(['helo'], er => { t.match(er, { - message: 'missing script: helo\n\nDid you mean this?\n hello', + message: 'Missing script: "helo"', + }) + t.match(er, { + message: 'npm run hello', + }) + t.end() + }) + }) + t.test('bin suggestions', t => { + runScript.exec(['goodneght'], er => { + t.match(er, { + message: 'Missing script: "goodneght"', + }) + t.match(er, { + message: 'npm exec goodnight', }) t.end() }) }) t.test('with --if-present', t => { - npm.config.set('if-present', true) + config['if-present'] = true runScript.exec(['goodbye'], er => { if (er) throw er @@ -331,7 +369,7 @@ t.test('run pre/post hooks', t => { }) t.test('skip pre/post hooks when using ignoreScripts', t => { - npm.flatOptions.ignoreScripts = true + config['ignore-scripts'] = true npm.localPrefix = t.testdir({ 'package.json': JSON.stringify({ @@ -368,7 +406,7 @@ t.test('skip pre/post hooks when using ignoreScripts', t => { }, ]) t.end() - delete npm.flatOptions.ignoreScripts + delete config['ignore-scripts'] }) }) @@ -443,13 +481,14 @@ t.test('list scripts', t => { if (er) throw er t.strictSame(output, [ - ['Lifecycle scripts included in x:'], + ['Lifecycle scripts included in x@1.2.3:'], [' test\n exit 2'], [' start\n node server.js'], [' stop\n node kill-server.js'], ['\navailable via `npm run-script`:'], [' preenv\n echo before the env'], [' postenv\n echo after the env'], + [''], ], 'basic report') t.end() }) @@ -466,7 +505,7 @@ t.test('list scripts', t => { }) t.test('warn json', t => { npmlog.level = 'warn' - npm.flatOptions.json = true + config.json = true runScript.exec([], er => { if (er) throw er @@ -476,7 +515,7 @@ t.test('list scripts', t => { }) t.test('parseable', t => { - npm.flatOptions.parseable = true + config.parseable = true runScript.exec([], er => { if (er) throw er @@ -522,8 +561,9 @@ t.test('list scripts, only commands', t => { if (er) throw er t.strictSame(output, [ - ['Lifecycle scripts included in x:'], + ['Lifecycle scripts included in x@1.2.3:'], [' preversion\n echo doing the version dance'], + [''], ]) t.end() }) @@ -542,9 +582,443 @@ t.test('list scripts, only non-commands', t => { if (er) throw er t.strictSame(output, [ - ['Scripts available in x via `npm run-script`:'], + ['Scripts available in x@1.2.3 via `npm run-script`:'], [' glorp\n echo doing the glerp glop'], + [''], ]) t.end() }) }) + +t.test('workspaces', t => { + npm.localPrefix = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { glorp: 'echo a doing the glerp glop' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '2.0.0', + scripts: { glorp: 'echo b doing the glerp glop' }, + }), + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + lorem: 'echo c lorem', + }, + }), + }, + d: { + 'package.json': JSON.stringify({ + name: 'd', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + }, + }), + }, + e: { + 'package.json': JSON.stringify({ + name: 'e', + scripts: { test: 'exit 0', start: 'echo start something' }, + }), + }, + noscripts: { + 'package.json': JSON.stringify({ + name: 'noscripts', + version: '1.0.0', + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + workspaces: ['packages/*'], + }), + }) + + t.test('list all scripts', t => { + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ['Scripts available in b@2.0.0 via `npm run-script`:'], + [' glorp\n echo b doing the glerp glop'], + [''], + ['Lifecycle scripts included in c@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + ['\navailable via `npm run-script`:'], + [' lorem\n echo c lorem'], + [''], + ['Lifecycle scripts included in d@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + [''], + ['Lifecycle scripts included in e:'], + [' test\n exit 0'], + [' start\n echo start something'], + [''], + ]) + t.end() + }) + }) + + t.test('list regular scripts, filtered by name', t => { + runScript.execWorkspaces([], ['a', 'b'], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ['Scripts available in b@2.0.0 via `npm run-script`:'], + [' glorp\n echo b doing the glerp glop'], + [''], + ]) + t.end() + }) + }) + + t.test('list regular scripts, filtered by path', t => { + runScript.execWorkspaces([], ['./packages/a'], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ]) + t.end() + }) + }) + + t.test('list regular scripts, filtered by parent folder', t => { + runScript.execWorkspaces([], ['./packages'], er => { + if (er) + throw er + t.strictSame(output, [ + ['Scripts available in a@1.0.0 via `npm run-script`:'], + [' glorp\n echo a doing the glerp glop'], + [''], + ['Scripts available in b@2.0.0 via `npm run-script`:'], + [' glorp\n echo b doing the glerp glop'], + [''], + ['Lifecycle scripts included in c@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + ['\navailable via `npm run-script`:'], + [' lorem\n echo c lorem'], + [''], + ['Lifecycle scripts included in d@1.0.0:'], + [' test\n exit 0'], + [' posttest\n echo posttest'], + [''], + ['Lifecycle scripts included in e:'], + [' test\n exit 0'], + [' start\n echo start something'], + [''], + ]) + t.end() + }) + }) + + t.test('list all scripts with colors', t => { + npm.color = true + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + [ + '\u001b[1mScripts\u001b[22m available in \x1B[32ma@1.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:', + ], + [' glorp\n \x1B[2mecho a doing the glerp glop\x1B[22m'], + [''], + [ + '\u001b[1mScripts\u001b[22m available in \x1B[32mb@2.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:', + ], + [' glorp\n \x1B[2mecho b doing the glerp glop\x1B[22m'], + [''], + [ + '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32mc@1.0.0\x1B[39m:', + ], + [' test\n \x1B[2mexit 0\x1B[22m'], + [' posttest\n \x1B[2mecho posttest\x1B[22m'], + ['\navailable via `\x1B[34mnpm run-script\x1B[39m`:'], + [' lorem\n \x1B[2mecho c lorem\x1B[22m'], + [''], + [ + '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32md@1.0.0\x1B[39m:', + ], + [' test\n \x1B[2mexit 0\x1B[22m'], + [' posttest\n \x1B[2mecho posttest\x1B[22m'], + [''], + [ + '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32me\x1B[39m:', + ], + [' test\n \x1B[2mexit 0\x1B[22m'], + [' start\n \x1B[2mecho start something\x1B[22m'], + [''], + ]) + t.end() + }) + }) + + t.test('list all scripts --json', t => { + config.json = true + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + [ + '{\n' + + ' "a": {\n' + + ' "glorp": "echo a doing the glerp glop"\n' + + ' },\n' + + ' "b": {\n' + + ' "glorp": "echo b doing the glerp glop"\n' + + ' },\n' + + ' "c": {\n' + + ' "test": "exit 0",\n' + + ' "posttest": "echo posttest",\n' + + ' "lorem": "echo c lorem"\n' + + ' },\n' + + ' "d": {\n' + + ' "test": "exit 0",\n' + + ' "posttest": "echo posttest"\n' + + ' },\n' + + ' "e": {\n' + + ' "test": "exit 0",\n' + + ' "start": "echo start something"\n' + + ' },\n' + + ' "noscripts": {}\n' + + '}', + ], + ]) + t.end() + }) + }) + + t.test('list all scripts --parseable', t => { + config.parseable = true + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, [ + ['a:glorp:echo a doing the glerp glop'], + ['b:glorp:echo b doing the glerp glop'], + ['c:test:exit 0'], + ['c:posttest:echo posttest'], + ['c:lorem:echo c lorem'], + ['d:test:exit 0'], + ['d:posttest:echo posttest'], + ['e:test:exit 0'], + ['e:start:echo start something'], + ]) + t.end() + }) + }) + + t.test('list no scripts --loglevel=silent', t => { + npmlog.level = 'silent' + runScript.execWorkspaces([], [], er => { + if (er) + throw er + t.strictSame(output, []) + t.end() + }) + }) + + t.test('run scripts across all workspaces', t => { + runScript.execWorkspaces(['test'], [], er => { + if (er) + throw er + + t.match(RUN_SCRIPTS, [ + { + path: resolve(npm.localPrefix, 'packages/c'), + pkg: { name: 'c', version: '1.0.0' }, + event: 'test', + }, + { + path: resolve(npm.localPrefix, 'packages/c'), + pkg: { name: 'c', version: '1.0.0' }, + event: 'posttest', + }, + { + path: resolve(npm.localPrefix, 'packages/d'), + pkg: { name: 'd', version: '1.0.0' }, + event: 'test', + }, + { + path: resolve(npm.localPrefix, 'packages/d'), + pkg: { name: 'd', version: '1.0.0' }, + event: 'posttest', + }, + { + path: resolve(npm.localPrefix, 'packages/e'), + pkg: { name: 'e' }, + event: 'test', + }, + ]) + t.end() + }) + }) + + t.test('missing scripts in all workspaces', t => { + const LOG = [] + npmlog.error = (err) => { + LOG.push(String(err)) + } + runScript.execWorkspaces(['missing-script'], [], er => { + t.match( + er, + /Missing script: missing-script/, + 'should throw missing script error' + ) + + process.exitCode = 0 // clean exit code + + t.match(RUN_SCRIPTS, []) + t.strictSame(LOG.map(cleanOutput), [ + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: a@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/a', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: b@2.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/b', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: c@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/c', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: d@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/d', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: e', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/e', + 'Lifecycle script `missing-script` failed with error:', + 'Error: Missing script: "missing-script"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: noscripts@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/noscripts', + ], 'should log error msgs for each workspace script') + + t.end() + }) + }) + + t.test('missing scripts in some workspaces', t => { + const LOG = [] + npmlog.error = (err) => { + LOG.push(String(err)) + } + runScript.execWorkspaces(['test'], ['a', 'b', 'c', 'd'], er => { + if (er) + throw er + + t.match(RUN_SCRIPTS, []) + t.strictSame(LOG.map(cleanOutput), [ + 'Lifecycle script `test` failed with error:', + 'Error: Missing script: "test"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: a@1.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/a', + 'Lifecycle script `test` failed with error:', + 'Error: Missing script: "test"\n\nTo see a list of scripts, run:\n npm run', + ' in workspace: b@2.0.0', + ' at location: {CWD}/test/lib/run-script-workspaces/packages/b', + ], 'should log error msgs for each workspace script') + t.end() + }) + }) + + t.test('no workspaces when filtering by user args', t => { + runScript.execWorkspaces([], ['foo', 'bar'], er => { + t.equal( + er.message, + 'No workspaces found:\n --workspace=foo --workspace=bar', + 'should throw error msg' + ) + t.end() + }) + }) + + t.test('no workspaces', t => { + const _prevPrefix = npm.localPrefix + npm.localPrefix = t.testdir({ + 'package.json': JSON.stringify({ + name: 'foo', + version: '1.0.0', + }), + }) + + runScript.execWorkspaces([], [], er => { + t.match(er, /No workspaces found!/, 'should throw error msg') + npm.localPrefix = _prevPrefix + t.end() + }) + }) + + t.test('single failed workspace run', t => { + const RunScript = requireInject('../../lib/run-script.js', { + '@npmcli/run-script': () => { + throw new Error('err') + }, + npmlog, + '../../lib/utils/is-windows-shell.js': false, + }) + const runScript = new RunScript(npm) + + runScript.execWorkspaces(['test'], ['c'], er => { + t.ok('should complete running all targets') + process.exitCode = 0 // clean up exit code + t.end() + }) + }) + + t.test('failed workspace run with succeeded runs', t => { + const RunScript = requireInject('../../lib/run-script.js', { + '@npmcli/run-script': async opts => { + if (opts.pkg.name === 'a') + throw new Error('ERR') + + RUN_SCRIPTS.push(opts) + }, + npmlog, + '../../lib/utils/is-windows-shell.js': false, + }) + const runScript = new RunScript(npm) + + runScript.execWorkspaces(['glorp'], ['a', 'b'], er => { + t.match(RUN_SCRIPTS, [ + { + path: resolve(npm.localPrefix, 'packages/b'), + pkg: { name: 'b', version: '2.0.0' }, + event: 'glorp', + }, + ]) + + process.exitCode = 0 // clean up exit code + t.end() + }) + }) + + t.end() +}) diff --git a/deps/npm/test/lib/search.js b/deps/npm/test/lib/search.js index b1ba7775c73c8d..b7b40844219fbc 100644 --- a/deps/npm/test/lib/search.js +++ b/deps/npm/test/lib/search.js @@ -1,6 +1,7 @@ const Minipass = require('minipass') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const libnpmsearchResultFixture = require('../fixtures/libnpmsearch-stream-result.js') @@ -12,12 +13,17 @@ const flatOptions = { opts: '', }, } -const npm = { +const config = { + json: false, + parseable: false, +} +const npm = mockNpm({ + config, flatOptions: { ...flatOptions }, output: (...msg) => { result += msg.join('\n') }, -} +}) const npmlog = { silly () {}, clearProgress () {}, @@ -29,12 +35,13 @@ const mocks = { npmlog, libnpmsearch, '../../lib/utils/usage.js': () => 'usage instructions', - // '../../lib/search/format-package-stream.js': a => a, } t.afterEach(cb => { result = '' - npm.flatOptions = flatOptions + config.json = false + config.parseable = false + npm.flatOptions = { ...flatOptions } cb() }) @@ -86,7 +93,8 @@ t.test('search <name> --json', (t) => { const src = new Minipass() src.objectMode = true - flatOptions.json = true + npm.flatOptions.json = true + config.json = true const libnpmsearch = { stream () { return src @@ -114,7 +122,7 @@ t.test('search <name> --json', (t) => { 'should have expected search results as json' ) - flatOptions.json = false + config.json = false t.end() }) diff --git a/deps/npm/test/lib/set-script.js b/deps/npm/test/lib/set-script.js index 7a057c503652fa..b6b6e2519f5bad 100644 --- a/deps/npm/test/lib/set-script.js +++ b/deps/npm/test/lib/set-script.js @@ -31,6 +31,7 @@ test.test('fails when package.json not found', (t) => { }) test.test('fails on invalid JSON', (t) => { const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFile: () => {}, // read-package-json-fast explodes w/o this readFileSync: (name, charcode) => { @@ -45,6 +46,7 @@ test.test('fails on invalid JSON', (t) => { test.test('creates scripts object', (t) => { var mockFile = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return '{}' @@ -70,6 +72,7 @@ test.test('creates scripts object', (t) => { test.test('warns before overwriting', (t) => { var warningListened = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return JSON.stringify({ @@ -102,6 +105,7 @@ test.test('warns before overwriting', (t) => { test.test('provided indentation and eol is used', (t) => { var mockFile = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return '{}' @@ -128,6 +132,7 @@ test.test('provided indentation and eol is used', (t) => { test.test('goes to default when undefined indent and eol provided', (t) => { var mockFile = '' const SetScript = requireInject('../../lib/set-script.js', { + '../../lib/utils/config/definitions.js': {}, fs: { readFileSync: (name, charcode) => { return '{}' diff --git a/deps/npm/test/lib/shrinkwrap.js b/deps/npm/test/lib/shrinkwrap.js index dc4bc3b220ca2a..faf452ea702f20 100644 --- a/deps/npm/test/lib/shrinkwrap.js +++ b/deps/npm/test/lib/shrinkwrap.js @@ -1,16 +1,21 @@ const t = require('tap') const fs = require('fs') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') -const npm = { +const config = { + global: false, +} +const flatOptions = { + depth: 0, +} +const npm = mockNpm({ + config, + flatOptions, lockfileVersion: 2, globalDir: '', - flatOptions: { - depth: 0, - global: false, - }, prefix: '', -} +}) const tree = { meta: { hiddenLockfile: null, @@ -32,11 +37,12 @@ const mocks = { } }, '../../lib/utils/usage.js': () => 'usage instructions', + '../../lib/utils/config/definitions.js': {}, } t.afterEach(cb => { npm.prefix = '' - npm.flatOptions.global = false + config.global = false npm.globalDir = '' cb() }) @@ -50,7 +56,7 @@ t.test('no args', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -101,7 +107,7 @@ t.test('no virtual tree', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -156,7 +162,7 @@ t.test('existing package-json file', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -218,7 +224,7 @@ t.test('update shrinkwrap file version', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -272,7 +278,7 @@ t.test('update to date shrinkwrap file', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...flatOptions, path: npm.prefix }, 'should call arborist constructor with expected args' ) } @@ -320,7 +326,7 @@ t.test('update to date shrinkwrap file', t => { t.test('shrinkwrap --global', t => { const Shrinkwrap = requireInject('../../lib/shrinkwrap.js', mocks) - npm.flatOptions.global = true + config.global = true const shrinkwrap = new Shrinkwrap(npm) shrinkwrap.exec([], err => { diff --git a/deps/npm/test/lib/star.js b/deps/npm/test/lib/star.js index 774fabe3924c4e..fa75d210577acd 100644 --- a/deps/npm/test/lib/star.js +++ b/deps/npm/test/lib/star.js @@ -1,16 +1,20 @@ const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') const t = require('tap') let result = '' const noop = () => null -const npm = { - config: { get () {} }, - flatOptions: { unicode: false }, +const config = { + unicode: false, + 'star.unstar': false, +} +const npm = mockNpm({ + config, output: (...msg) => { result += msg.join('\n') }, -} +}) const npmFetch = { json: noop } const npmlog = { error: noop, info: noop, verbose: noop } const mocks = { @@ -24,8 +28,8 @@ const Star = requireInject('../../lib/star.js', mocks) const star = new Star(npm) t.afterEach(cb => { - npm.config = { get () {} } - npm.flatOptions.unicode = false + config.unicode = false + config['star.unstar'] = false npmlog.info = noop result = '' cb() @@ -73,7 +77,7 @@ t.test('star a package', t => { t.test('unstar a package', t => { t.plan(4) const pkgName = '@npmcli/arborist' - npm.config.get = key => key === 'star.unstar' + config['star.unstar'] = true npmFetch.json = async (uri, opts) => ({ _id: pkgName, _rev: 'hash', @@ -100,7 +104,7 @@ t.test('unstar a package', t => { t.test('unicode', async t => { t.test('star a package', t => { - npm.flatOptions.unicode = true + config.unicode = true npmFetch.json = async (uri, opts) => ({}) star.exec(['pkg'], err => { if (err) @@ -115,8 +119,8 @@ t.test('unicode', async t => { }) t.test('unstar a package', t => { - npm.flatOptions.unicode = true - npm.config.get = key => key === 'star.unstar' + config.unicode = true + config['star.unstar'] = true npmFetch.json = async (uri, opts) => ({}) star.exec(['pkg'], err => { if (err) diff --git a/deps/npm/test/lib/uninstall.js b/deps/npm/test/lib/uninstall.js index c62b59950b894a..5cb8a243ec19be 100644 --- a/deps/npm/test/lib/uninstall.js +++ b/deps/npm/test/lib/uninstall.js @@ -2,18 +2,18 @@ const fs = require('fs') const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') -const npm = { +const npm = mockNpm({ globalDir: '', - flatOptions: { + config: { global: false, prefix: '', }, localPrefix: '', -} +}) const mocks = { '../../lib/utils/reify-finish.js': () => Promise.resolve(), - '../../lib/utils/usage.js': () => 'usage instructions', } const Uninstall = requireInject('../../lib/uninstall.js', mocks) @@ -85,13 +85,13 @@ t.test('remove single installed lib', t => { const b = resolve(path, 'node_modules/b') t.ok(() => fs.statSync(b)) - npm.flatOptions.prefix = path + npm.config.set('prefix', path) uninstall.exec(['b'], err => { if (err) throw err - t.throws(() => fs.statSync(b), 'should have removed package from nm') + t.throws(() => fs.statSync(b), 'should have removed package from npm') t.end() }) }) @@ -148,7 +148,7 @@ t.test('remove multiple installed libs', t => { t.ok(() => fs.statSync(a)) t.ok(() => fs.statSync(b)) - npm.flatOptions.prefix = path + npm.config.set('prefix', path) uninstall.exec(['b'], err => { if (err) @@ -195,8 +195,8 @@ t.test('no args global', t => { npm.localPrefix = resolve(path, 'projects', 'a') npm.globalDir = resolve(path, 'lib', 'node_modules') - npm.flatOptions.global = true - npm.flatOptions.prefix = path + npm.config.set('global', true) + npm.config.set('prefix', path) const a = resolve(path, 'lib/node_modules/a') t.ok(() => fs.statSync(a)) @@ -221,8 +221,7 @@ t.test('no args global but no package.json', t => { uninstall.exec([], err => { t.match( err, - 'usage instructions', - 'should throw usage instructions' + 'npm uninstall' ) t.end() diff --git a/deps/npm/test/lib/unpublish.js b/deps/npm/test/lib/unpublish.js index b1255b94a8fe4d..ba99b533030733 100644 --- a/deps/npm/test/lib/unpublish.js +++ b/deps/npm/test/lib/unpublish.js @@ -1,19 +1,21 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = '' const noop = () => null -const npm = { +const config = { + force: false, + silent: false, + loglevel: 'silly', +} +const npm = mockNpm({ localPrefix: '', - flatOptions: { - force: false, - silent: false, - loglevel: 'silly', - }, + config, output: (...msg) => { result += msg.join('\n') }, -} +}) const mocks = { npmlog: { silly () {}, verbose () {} }, libnpmaccess: { lsPackages: noop }, @@ -28,16 +30,16 @@ const mocks = { t.afterEach(cb => { result = '' - npm.flatOptions.force = false - npm.flatOptions.loglevel = 'silly' - npm.flatOptions.silent = false + config.force = false + config.loglevel = 'silly' + config.silent = false cb() }) t.test('no args --force', t => { t.plan(9) - npm.flatOptions.force = true + config.force = true const npmlog = { silly (title) { @@ -67,9 +69,6 @@ t.test('no args --force', t => { t.deepEqual( opts, { - force: true, - silent: false, - loglevel: 'silly', publishConfig: undefined, }, 'should unpublish with expected opts' @@ -102,7 +101,7 @@ t.test('no args --force', t => { }) t.test('no args --force missing package.json', t => { - npm.flatOptions.force = true + config.force = true const Unpublish = requireInject('../../lib/unpublish.js', { ...mocks, @@ -124,7 +123,7 @@ t.test('no args --force missing package.json', t => { }) t.test('no args --force unknown error reading package.json', t => { - npm.flatOptions.force = true + config.force = true const Unpublish = requireInject('../../lib/unpublish.js', { ...mocks, @@ -200,11 +199,7 @@ t.test('unpublish <pkg>@version', t => { t.equal(spec, pa, 'should unpublish expected parsed spec') t.deepEqual( opts, - { - force: false, - silent: false, - loglevel: 'silly', - }, + {}, 'should unpublish with expected opts' ) }, @@ -231,7 +226,7 @@ t.test('unpublish <pkg>@version', t => { }) t.test('no version found in package.json', t => { - npm.flatOptions.force = true + config.force = true const npa = () => ({ name: 'pkg', @@ -263,7 +258,7 @@ t.test('no version found in package.json', t => { }) t.test('unpublish <pkg> --force no version set', t => { - npm.flatOptions.force = true + config.force = true const Unpublish = requireInject('../../lib/unpublish.js', { ...mocks, @@ -289,7 +284,7 @@ t.test('unpublish <pkg> --force no version set', t => { }) t.test('silent', t => { - npm.flatOptions.loglevel = 'silent' + config.loglevel = 'silent' const npa = () => ({ name: 'pkg', diff --git a/deps/npm/test/lib/update.js b/deps/npm/test/lib/update.js index 15195573f5a24d..695218a7f69cd9 100644 --- a/deps/npm/test/lib/update.js +++ b/deps/npm/test/lib/update.js @@ -1,16 +1,18 @@ const { resolve } = require('path') const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') +const config = { + depth: 0, + global: false, +} const noop = () => null -const npm = { +const npm = mockNpm({ globalDir: '', - flatOptions: { - depth: 0, - global: false, - }, + config, prefix: '', -} +}) const mocks = { npmlog: { warn () {} }, '@npmcli/arborist': class { @@ -22,7 +24,7 @@ const mocks = { t.afterEach(cb => { npm.prefix = '' - npm.flatOptions.global = false + config.global = false npm.globalDir = '' cb() }) @@ -99,7 +101,7 @@ t.test('update --depth=<number>', t => { t.plan(2) npm.prefix = '/project/a' - npm.flatOptions.depth = 1 + config.depth = 1 const Update = requireInject('../../lib/update.js', { ...mocks, @@ -131,7 +133,7 @@ t.test('update --global', t => { npm.prefix = '/project/a' npm.globalDir = resolve(process.cwd(), 'global/lib/node_modules') - npm.flatOptions.global = true + config.global = true class Arborist { constructor (args) { diff --git a/deps/npm/test/lib/utils/config.js b/deps/npm/test/lib/utils/config.js deleted file mode 100644 index 4d4b1a1d1a70d3..00000000000000 --- a/deps/npm/test/lib/utils/config.js +++ /dev/null @@ -1,143 +0,0 @@ -const t = require('tap') -const requireInject = require('require-inject') - -// have to fake the node version, or else it'll only pass on this one -Object.defineProperty(process, 'version', { - value: 'v14.8.0', -}) - -t.formatSnapshot = obj => { - if (typeof obj !== 'object' || !obj || !obj.types) - return obj - - return { - ...obj, - defaults: { - ...obj.defaults, - cache: '{CACHE DIR} ' + path.basename(obj.defaults.cache), - }, - types: formatTypes(obj.types), - } -} - -const path = require('path') -const url = require('url') -const semver = require('semver') - -const formatTypes = (types) => Object.entries(types).map(([key, value]) => { - return [key, formatTypeValue(value)] -}).reduce((set, [key, value]) => { - set[key] = value - return set -}, {}) - -const formatTypeValue = (value) => { - if (Array.isArray(value)) - return value.map(formatTypeValue) - else if (value === url) - return '{URL MODULE}' - else if (value === path) - return '{PATH MODULE}' - else if (value === semver) - return '{SEMVER MODULE}' - else if (typeof value === 'function') - return `{${value.name} TYPE}` - else - return value -} - -process.env.ComSpec = 'cmd.exe' -process.env.SHELL = '/usr/local/bin/bash' -process.env.LANG = 'UTF-8' -delete process.env.LC_ALL -delete process.env.LC_CTYPE -process.env.EDITOR = 'vim' -process.env.VISUAL = 'mate' - -const networkInterfacesThrow = () => { - throw new Error('no network interfaces for some reason') -} -const networkInterfaces = () => ({ - eth420: [{ address: '127.0.0.1' }], - eth69: [{ address: 'no place like home' }], -}) -const tmpdir = () => '/tmp' -const os = { networkInterfaces, tmpdir } -const pkg = { version: '7.0.0' } - -t.test('working network interfaces, not windows', t => { - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - '../../../package.json': pkg, - }) - t.matchSnapshot(config) - t.end() -}) - -t.test('no working network interfaces, on windows', t => { - const config = requireInject('../../../lib/utils/config.js', { - os: { tmpdir, networkInterfaces: networkInterfacesThrow }, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': true, - '../../../package.json': pkg, - }) - t.matchSnapshot(config) - t.end() -}) - -t.test('no comspec on windows', t => { - delete process.env.ComSpec - const config = requireInject('../../../lib/utils/config.js', { - os: { tmpdir, networkInterfaces: networkInterfacesThrow }, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': true, - }) - t.equal(config.defaults.shell, 'cmd') - t.end() -}) - -t.test('no shell on posix', t => { - delete process.env.SHELL - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - }) - t.equal(config.defaults.shell, 'sh') - t.end() -}) - -t.test('no EDITOR env, use VISUAL', t => { - delete process.env.EDITOR - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - }) - t.equal(config.defaults.editor, 'mate') - t.end() -}) - -t.test('no VISUAL, use system default, not windows', t => { - delete process.env.VISUAL - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': false, - }) - t.equal(config.defaults.editor, 'vi') - t.end() -}) - -t.test('no VISUAL, use system default, not windows', t => { - delete process.env.VISUAL - const config = requireInject('../../../lib/utils/config.js', { - os, - '@npmcli/ci-detect': () => false, - '../../../lib/utils/is-windows.js': true, - }) - t.equal(config.defaults.editor, 'notepad.exe') - t.end() -}) diff --git a/deps/npm/test/lib/utils/config/definition.js b/deps/npm/test/lib/utils/config/definition.js new file mode 100644 index 00000000000000..56e10da0cbd7d4 --- /dev/null +++ b/deps/npm/test/lib/utils/config/definition.js @@ -0,0 +1,185 @@ +const t = require('tap') +const Definition = require('../../../../lib/utils/config/definition.js') +const { + typeDefs: { + semver: { type: semver }, + Umask: { type: Umask }, + url: { type: url }, + path: { type: path }, + }, +} = require('@npmcli/config') + +t.test('basic definition', async t => { + const def = new Definition('key', { + default: 'some default value', + type: [Number, String], + description: 'just a test thingie', + }) + t.same(def, { + constructor: Definition, + key: 'key', + default: 'some default value', + defaultDescription: '"some default value"', + type: [Number, String], + hint: '<key>', + usage: '--key <key>|--key <key>', + typeDescription: 'Number or String', + description: 'just a test thingie', + }) + t.matchSnapshot(def.describe(), 'human-readable description') + + const deprecated = new Definition('deprecated', { + deprecated: 'do not use this', + default: 1234, + description: ' it should not be used\n ever\n\n not even once.\n\n', + type: Number, + defaultDescription: 'A number bigger than 1', + typeDescription: 'An expression of a numeric quantity using numerals', + }) + t.matchSnapshot(deprecated.describe(), 'description of deprecated thing') + + const nullOrUmask = new Definition('key', { + default: null, + type: [null, Umask], + description: 'asdf', + }) + t.equal(nullOrUmask.typeDescription, 'null or Octal numeric string in range 0000..0777 (0..511)') + const nullDateOrBool = new Definition('key', { + default: 7, + type: [null, Date, Boolean], + description: 'asdf', + }) + t.equal(nullDateOrBool.typeDescription, 'null, Date, or Boolean') + const manyPaths = new Definition('key', { + default: ['asdf'], + type: [path, Array], + description: 'asdf', + }) + t.equal(manyPaths.typeDescription, 'Path (can be set multiple times)') + const pathOrUrl = new Definition('key', { + default: ['https://example.com'], + type: [path, url], + description: 'asdf', + }) + t.equal(pathOrUrl.typeDescription, 'Path or URL') + const multi12 = new Definition('key', { + default: [], + type: [1, 2, Array], + description: 'asdf', + }) + t.equal(multi12.typeDescription, '1 or 2 (can be set multiple times)') + const multi123 = new Definition('key', { + default: [], + type: [1, 2, 3, Array], + description: 'asdf', + }) + t.equal(multi123.typeDescription, '1, 2, or 3 (can be set multiple times)') + const multi123Semver = new Definition('key', { + default: [], + type: [1, 2, 3, Array, semver], + description: 'asdf', + }) + t.equal(multi123Semver.typeDescription, '1, 2, 3, or SemVer string (can be set multiple times)') + const hasUsage = new Definition('key', { + default: 'test default', + type: String, + description: 'test description', + usage: 'test usage', + }) + t.equal(hasUsage.usage, 'test usage') + const hasShort = new Definition('key', { + default: 'test default', + short: 't', + type: String, + description: 'test description', + }) + t.equal(hasShort.usage, '-t|--key <key>') + const hardCodedTypes = new Definition('key', { + default: 'test default', + type: ['string1', 'string2'], + description: 'test description', + }) + t.equal(hardCodedTypes.usage, '--key <string1|string2>') + const hardCodedOptionalTypes = new Definition('key', { + default: 'test default', + type: [null, 'string1', 'string2'], + description: 'test description', + }) + t.equal(hardCodedOptionalTypes.usage, '--key <string1|string2>') + const hasHint = new Definition('key', { + default: 'test default', + type: String, + description: 'test description', + hint: '<testparam>', + }) + t.equal(hasHint.usage, '--key <testparam>') +}) + +t.test('missing fields', async t => { + t.throws(() => new Definition('lacks-default', { + description: 'no default', + type: String, + }), { message: 'config lacks default: lacks-default' }) + t.throws(() => new Definition('lacks-type', { + description: 'no type', + default: 1234, + }), { message: 'config lacks type: lacks-type' }) + t.throws(() => new Definition(null, { + description: 'falsey key', + default: 1234, + type: Number, + }), { message: 'config lacks key: null' }) + t.throws(() => new Definition('extra-field', { + type: String, + default: 'extra', + extra: 'more than is wanted', + description: 'this is not ok', + }), { message: 'config defines unknown field extra: extra-field' }) +}) + +t.test('long description', async t => { + const { stdout: { columns } } = process + t.teardown(() => process.stdout.columns = columns) + + const long = new Definition('walden', { + description: ` + WHEN I WROTE the following pages, or rather the bulk of them, I lived + alone, in the woods, a mile from any neighbor, in a house which I had + built myself, on the shore of Walden Pond, in Concord, Massachusetts, and + earned my living by the labor of my hands only. I lived there two years + and two months. At present I am a sojourner in civilized life again. + + I should not obtrude my affairs so much on the notice of my readers if + very particular inquiries had not been made by my townsmen concerning my + mode of life, which some would call impertinent, though they do not + appear to me at all impertinent, but, considering the circumstances, very + natural and pertinent. + + \`\`\` + this.is('a', { + code: 'sample', + }) + + with (multiple) { + blocks() + } + \`\`\` + `, + default: true, + type: Boolean, + }) + process.stdout.columns = 40 + t.matchSnapshot(long.describe(), 'cols=40') + + process.stdout.columns = 9000 + t.matchSnapshot(long.describe(), 'cols=9000') + + process.stdout.columns = 0 + t.matchSnapshot(long.describe(), 'cols=0') + + process.stdout.columns = -1 + t.matchSnapshot(long.describe(), 'cols=-1') + + process.stdout.columns = NaN + t.matchSnapshot(long.describe(), 'cols=NaN') +}) diff --git a/deps/npm/test/lib/utils/config/definitions.js b/deps/npm/test/lib/utils/config/definitions.js new file mode 100644 index 00000000000000..3169feefb8f914 --- /dev/null +++ b/deps/npm/test/lib/utils/config/definitions.js @@ -0,0 +1,697 @@ +const t = require('tap') + +const requireInject = require('require-inject') +const { resolve } = require('path') + +// have to fake the node version, or else it'll only pass on this one +Object.defineProperty(process, 'version', { + value: 'v14.8.0', +}) + +// also fake the npm version, so that it doesn't get reset every time +const pkg = require('../../../../package.json') + +// this is a pain to keep typing +const defpath = '../../../../lib/utils/config/definitions.js' + +// set this in the test when we need it +delete process.env.NODE_ENV +const definitions = require(defpath) + +const isWin = '../../../../lib/utils/is-windows.js' + +// snapshot these just so we note when they change +t.matchSnapshot(Object.keys(definitions), 'all config keys') +t.matchSnapshot(Object.keys(definitions).filter(d => d.flatten), + 'all config keys that are shared to flatOptions') + +t.equal(definitions['npm-version'].default, pkg.version, 'npm-version default') +t.equal(definitions['node-version'].default, process.version, 'node-version default') + +t.test('basic flattening function camelCases from css-case', t => { + const flat = {} + const obj = { 'always-auth': true } + definitions['always-auth'].flatten('always-auth', obj, flat) + t.strictSame(flat, { alwaysAuth: true }) + t.end() +}) + +t.test('editor', t => { + t.test('has EDITOR and VISUAL, use EDITOR', t => { + process.env.EDITOR = 'vim' + process.env.VISUAL = 'mate' + const defs = requireInject(defpath) + t.equal(defs.editor.default, 'vim') + t.end() + }) + t.test('has VISUAL but no EDITOR, use VISUAL', t => { + delete process.env.EDITOR + process.env.VISUAL = 'mate' + const defs = requireInject(defpath) + t.equal(defs.editor.default, 'mate') + t.end() + }) + t.test('has neither EDITOR nor VISUAL, system specific', t => { + delete process.env.EDITOR + delete process.env.VISUAL + const defsWin = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsWin.editor.default, 'notepad.exe') + const defsNix = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsNix.editor.default, 'vi') + t.end() + }) + t.end() +}) + +t.test('shell', t => { + t.test('windows, env.ComSpec then cmd.exe', t => { + process.env.ComSpec = 'command.com' + const defsComSpec = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsComSpec.shell.default, 'command.com') + delete process.env.ComSpec + const defsNoComSpec = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsNoComSpec.shell.default, 'cmd') + t.end() + }) + + t.test('nix, SHELL then sh', t => { + process.env.SHELL = '/usr/local/bin/bash' + const defsShell = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsShell.shell.default, '/usr/local/bin/bash') + delete process.env.SHELL + const defsNoShell = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsNoShell.shell.default, 'sh') + t.end() + }) + + t.end() +}) + +t.test('local-address allowed types', t => { + t.test('get list from os.networkInterfaces', t => { + const os = { + tmpdir: () => '/tmp', + networkInterfaces: () => ({ + eth420: [{ address: '127.0.0.1' }], + eth69: [{ address: 'no place like home' }], + }), + } + const defs = requireInject(defpath, { os }) + t.same(defs['local-address'].type, [ + null, + '127.0.0.1', + 'no place like home', + ]) + t.end() + }) + t.test('handle os.networkInterfaces throwing', t => { + const os = { + tmpdir: () => '/tmp', + networkInterfaces: () => { + throw new Error('no network interfaces for some reason') + }, + } + const defs = requireInject(defpath, { os }) + t.same(defs['local-address'].type, [null]) + t.end() + }) + t.end() +}) + +t.test('unicode allowed?', t => { + const { LC_ALL, LC_CTYPE, LANG } = process.env + t.teardown(() => Object.assign(process.env, { LC_ALL, LC_CTYPE, LANG })) + + process.env.LC_ALL = 'utf8' + process.env.LC_CTYPE = 'UTF-8' + process.env.LANG = 'Unicode utf-8' + + const lcAll = requireInject(defpath) + t.equal(lcAll.unicode.default, true) + process.env.LC_ALL = 'no unicode for youUUUU!' + const noLcAll = requireInject(defpath) + t.equal(noLcAll.unicode.default, false) + + delete process.env.LC_ALL + const lcCtype = requireInject(defpath) + t.equal(lcCtype.unicode.default, true) + process.env.LC_CTYPE = 'something other than unicode version 8' + const noLcCtype = requireInject(defpath) + t.equal(noLcCtype.unicode.default, false) + + delete process.env.LC_CTYPE + const lang = requireInject(defpath) + t.equal(lang.unicode.default, true) + process.env.LANG = 'ISO-8859-1' + const noLang = requireInject(defpath) + t.equal(noLang.unicode.default, false) + t.end() +}) + +t.test('cache', t => { + process.env.LOCALAPPDATA = 'app/data/local' + const defsWinLocalAppData = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsWinLocalAppData.cache.default, 'app/data/local/npm-cache') + + delete process.env.LOCALAPPDATA + const defsWinNoLocalAppData = requireInject(defpath, { + [isWin]: true, + }) + t.equal(defsWinNoLocalAppData.cache.default, '~/npm-cache') + + const defsNix = requireInject(defpath, { + [isWin]: false, + }) + t.equal(defsNix.cache.default, '~/.npm') + + const flat = {} + defsNix.cache.flatten('cache', { cache: '/some/cache/value' }, flat) + const {join} = require('path') + t.equal(flat.cache, join('/some/cache/value', '_cacache')) + + t.end() +}) + +t.test('flatteners that populate flat.omit array', t => { + t.test('also', t => { + const flat = {} + const obj = {} + + // ignored if setting is not dev or development + obj.also = 'ignored' + definitions.also.flatten('also', obj, flat) + t.strictSame(obj, {also: 'ignored'}, 'nothing done') + t.strictSame(flat, {}, 'nothing done') + + obj.also = 'development' + definitions.also.flatten('also', obj, flat) + t.strictSame(obj, { also: 'development', include: ['dev'] }, 'marked dev as included') + t.strictSame(flat, { omit: [] }, 'nothing omitted, so nothing changed') + + obj.omit = ['dev', 'optional'] + obj.include = [] + definitions.also.flatten('also', obj, flat) + t.strictSame(obj, { also: 'development', omit: ['dev', 'optional'], include: ['dev'] }, 'marked dev as included') + t.strictSame(flat, { omit: ['optional'] }, 'removed dev from omit') + t.end() + }) + + t.test('include', t => { + const flat = {} + const obj = { include: ['dev'] } + definitions.include.flatten('include', obj, flat) + t.strictSame(flat, {omit: []}, 'not omitting anything') + obj.omit = ['optional', 'dev'] + definitions.include.flatten('include', obj, flat) + t.strictSame(flat, {omit: ['optional']}, 'only omitting optional') + t.end() + }) + + t.test('omit', t => { + const flat = {} + const obj = { include: ['dev'], omit: ['dev', 'optional'] } + definitions.omit.flatten('omit', obj, flat) + t.strictSame(flat, { omit: ['optional'] }, 'do not omit what is included') + + process.env.NODE_ENV = 'production' + const defProdEnv = requireInject(defpath) + t.strictSame(defProdEnv.omit.default, ['dev'], 'omit dev in production') + t.end() + }) + + t.test('only', t => { + const flat = {} + const obj = { only: 'asdf' } + definitions.only.flatten('only', obj, flat) + t.strictSame(flat, {}, 'ignored if value is not production') + + obj.only = 'prod' + definitions.only.flatten('only', obj, flat) + t.strictSame(flat, {omit: ['dev']}, 'omit dev when --only=prod') + + obj.include = ['dev'] + flat.omit = [] + definitions.only.flatten('only', obj, flat) + t.strictSame(flat, {omit: []}, 'do not omit when included') + + t.end() + }) + + t.test('optional', t => { + const flat = {} + const obj = { optional: null } + + definitions.optional.flatten('optional', obj, flat) + t.strictSame(obj, { optional: null }, 'do nothing by default') + t.strictSame(flat, {}, 'do nothing by default') + + obj.optional = true + definitions.optional.flatten('optional', obj, flat) + t.strictSame(obj, {include: ['optional'], optional: true}, 'include optional when set') + t.strictSame(flat, {omit: []}, 'nothing to omit in flatOptions') + + delete obj.include + obj.optional = false + definitions.optional.flatten('optional', obj, flat) + t.strictSame(obj, {omit: ['optional'], optional: false}, 'omit optional when set false') + t.strictSame(flat, {omit: ['optional']}, 'omit optional when set false') + + t.end() + }) + + t.test('production', t => { + const flat = {} + const obj = {production: true} + definitions.production.flatten('production', obj, flat) + t.strictSame(obj, {production: true, omit: ['dev']}, '--production sets --omit=dev') + t.strictSame(flat, {omit: ['dev']}, '--production sets --omit=dev') + + delete obj.omit + obj.production = false + delete flat.omit + definitions.production.flatten('production', obj, flat) + t.strictSame(obj, {production: false}, '--no-production has no effect') + t.strictSame(flat, {}, '--no-production has no effect') + + obj.production = true + obj.include = ['dev'] + definitions.production.flatten('production', obj, flat) + t.strictSame(obj, {production: true, include: ['dev'], omit: ['dev']}, 'omit and include dev') + t.strictSame(flat, {omit: []}, 'do not omit dev when included') + + t.end() + }) + + t.end() +}) + +t.test('cache-max', t => { + const flat = {} + const obj = { 'cache-max': 10342 } + definitions['cache-max'].flatten('cache-max', obj, flat) + t.strictSame(flat, {}, 'no effect if not <= 0') + obj['cache-max'] = 0 + definitions['cache-max'].flatten('cache-max', obj, flat) + t.strictSame(flat, {preferOnline: true}, 'preferOnline if <= 0') + t.end() +}) + +t.test('cache-min', t => { + const flat = {} + const obj = { 'cache-min': 123 } + definitions['cache-min'].flatten('cache-min', obj, flat) + t.strictSame(flat, {}, 'no effect if not >= 9999') + obj['cache-min'] = 9999 + definitions['cache-min'].flatten('cache-min', obj, flat) + t.strictSame(flat, {preferOffline: true}, 'preferOffline if >=9999') + t.end() +}) + +t.test('color', t => { + const { isTTY } = process.stdout + t.teardown(() => process.stdout.isTTY = isTTY) + + const flat = {} + const obj = { color: 'always' } + + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: true}, 'true when --color=always') + + obj.color = false + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: false}, 'true when --no-color') + + process.stdout.isTTY = false + obj.color = true + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: false}, 'no color when stdout not tty') + process.stdout.isTTY = true + definitions.color.flatten('color', obj, flat) + t.strictSame(flat, {color: true}, '--color turns on color when stdout is tty') + + delete process.env.NO_COLOR + const defsAllowColor = requireInject(defpath) + t.equal(defsAllowColor.color.default, true, 'default true when no NO_COLOR env') + + process.env.NO_COLOR = '0' + const defsNoColor0 = requireInject(defpath) + t.equal(defsNoColor0.color.default, true, 'default true when no NO_COLOR=0') + + process.env.NO_COLOR = '1' + const defsNoColor1 = requireInject(defpath) + t.equal(defsNoColor1.color.default, false, 'default false when no NO_COLOR=1') + + t.end() +}) + +t.test('retry options', t => { + const obj = {} + // <config>: flat.retry[<option>] + const mapping = { + 'fetch-retries': 'retries', + 'fetch-retry-factor': 'factor', + 'fetch-retry-maxtimeout': 'maxTimeout', + 'fetch-retry-mintimeout': 'minTimeout', + } + for (const [config, option] of Object.entries(mapping)) { + const msg = `${config} -> retry.${option}` + const flat = {} + obj[config] = 99 + definitions[config].flatten(config, obj, flat) + t.strictSame(flat, {retry: {[option]: 99}}, msg) + delete obj[config] + } + t.end() +}) + +t.test('search options', t => { + const obj = {} + // <config>: flat.search[<option>] + const mapping = { + description: 'description', + searchexclude: 'exclude', + searchlimit: 'limit', + searchstaleness: 'staleness', + } + + for (const [config, option] of Object.entries(mapping)) { + const msg = `${config} -> search.${option}` + const flat = {} + obj[config] = 99 + definitions[config].flatten(config, obj, flat) + t.strictSame(flat, { search: { limit: 20, [option]: 99 }}, msg) + delete obj[config] + } + + const flat = {} + obj.searchopts = 'a=b&b=c' + definitions.searchopts.flatten('searchopts', obj, flat) + t.strictSame(flat, { + search: { + limit: 20, + opts: Object.assign(Object.create(null), { + a: 'b', + b: 'c', + }), + }, + }, 'searchopts -> querystring.parse() -> search.opts') + delete obj.searchopts + + t.end() +}) + +t.test('noProxy', t => { + const obj = { noproxy: ['1.2.3.4,2.3.4.5', '3.4.5.6'] } + const flat = {} + definitions.noproxy.flatten('noproxy', obj, flat) + t.strictSame(flat, { noProxy: '1.2.3.4,2.3.4.5,3.4.5.6' }) + t.end() +}) + +t.test('maxSockets', t => { + const obj = { maxsockets: 123 } + const flat = {} + definitions.maxsockets.flatten('maxsockets', obj, flat) + t.strictSame(flat, { maxSockets: 123 }) + t.end() +}) + +t.test('projectScope', t => { + const obj = { scope: 'asdf' } + const flat = {} + definitions.scope.flatten('scope', obj, flat) + t.strictSame(flat, { projectScope: '@asdf' }, 'prepend @ if needed') + + obj.scope = '@asdf' + definitions.scope.flatten('scope', obj, flat) + t.strictSame(flat, { projectScope: '@asdf' }, 'leave untouched if has @') + + t.end() +}) + +t.test('strictSSL', t => { + const obj = { 'strict-ssl': false } + const flat = {} + definitions['strict-ssl'].flatten('strict-ssl', obj, flat) + t.strictSame(flat, { strictSSL: false }) + obj['strict-ssl'] = true + definitions['strict-ssl'].flatten('strict-ssl', obj, flat) + t.strictSame(flat, { strictSSL: true }) + t.end() +}) + +t.test('shrinkwrap/package-lock', t => { + const obj = { shrinkwrap: false } + const flat = {} + definitions.shrinkwrap.flatten('shrinkwrap', obj, flat) + t.strictSame(flat, {packageLock: false}) + obj.shrinkwrap = true + definitions.shrinkwrap.flatten('shrinkwrap', obj, flat) + t.strictSame(flat, {packageLock: true}) + + delete obj.shrinkwrap + obj['package-lock'] = false + definitions['package-lock'].flatten('package-lock', obj, flat) + t.strictSame(flat, {packageLock: false}) + obj['package-lock'] = true + definitions['package-lock'].flatten('package-lock', obj, flat) + t.strictSame(flat, {packageLock: true}) + + t.end() +}) + +t.test('scriptShell', t => { + const obj = { 'script-shell': null } + const flat = {} + definitions['script-shell'].flatten('script-shell', obj, flat) + t.ok(Object.prototype.hasOwnProperty.call(flat, 'scriptShell'), + 'should set it to undefined explicitly') + t.strictSame(flat, { scriptShell: undefined }, 'no other fields') + + obj['script-shell'] = 'asdf' + definitions['script-shell'].flatten('script-shell', obj, flat) + t.strictSame(flat, { scriptShell: 'asdf' }, 'sets if not falsey') + + t.end() +}) + +t.test('defaultTag', t => { + const obj = { tag: 'next' } + const flat = {} + definitions.tag.flatten('tag', obj, flat) + t.strictSame(flat, {defaultTag: 'next'}) + t.end() +}) + +t.test('timeout', t => { + const obj = { 'fetch-timeout': 123 } + const flat = {} + definitions['fetch-timeout'].flatten('fetch-timeout', obj, flat) + t.strictSame(flat, {timeout: 123}) + t.end() +}) + +t.test('saveType', t => { + t.test('save-prod', t => { + const obj = { 'save-prod': false } + const flat = {} + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {}, 'no effect if false and missing') + flat.saveType = 'prod' + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {}, 'remove if false and set to prod') + flat.saveType = 'dev' + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {saveType: 'dev'}, 'ignore if false and not already prod') + obj['save-prod'] = true + definitions['save-prod'].flatten('save-prod', obj, flat) + t.strictSame(flat, {saveType: 'prod'}, 'set to prod if true') + t.end() + }) + + t.test('save-dev', t => { + const obj = { 'save-dev': false } + const flat = {} + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {}, 'no effect if false and missing') + flat.saveType = 'dev' + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {}, 'remove if false and set to dev') + flat.saveType = 'prod' + obj['save-dev'] = false + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {saveType: 'prod'}, 'ignore if false and not already dev') + obj['save-dev'] = true + definitions['save-dev'].flatten('save-dev', obj, flat) + t.strictSame(flat, {saveType: 'dev'}, 'set to dev if true') + t.end() + }) + + t.test('save-bundle', t => { + const obj = { 'save-bundle': true } + const flat = {} + definitions['save-bundle'].flatten('save-bundle', obj, flat) + t.strictSame(flat, {saveBundle: true}, 'set the saveBundle flag') + + obj['save-bundle'] = false + definitions['save-bundle'].flatten('save-bundle', obj, flat) + t.strictSame(flat, {saveBundle: false}, 'unset the saveBundle flag') + + obj['save-bundle'] = true + obj['save-peer'] = true + definitions['save-bundle'].flatten('save-bundle', obj, flat) + t.strictSame(flat, {saveBundle: false}, 'false if save-peer is set') + + t.end() + }) + + t.test('save-peer', t => { + const obj = { 'save-peer': false} + const flat = {} + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {}, 'no effect if false and not yet set') + + obj['save-peer'] = true + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'peer'}, 'set saveType to peer if unset') + + flat.saveType = 'optional' + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'set to peerOptional if optional already') + + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'no effect if already peerOptional') + + obj['save-peer'] = false + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {saveType: 'optional'}, 'switch peerOptional to optional if false') + + obj['save-peer'] = false + flat.saveType = 'peer' + definitions['save-peer'].flatten('save-peer', obj, flat) + t.strictSame(flat, {}, 'remove saveType if peer and setting false') + + t.end() + }) + + t.test('save-optional', t => { + const obj = { 'save-optional': false} + const flat = {} + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {}, 'no effect if false and not yet set') + + obj['save-optional'] = true + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'optional'}, 'set saveType to optional if unset') + + flat.saveType = 'peer' + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'set to peerOptional if peer already') + + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'peerOptional'}, 'no effect if already peerOptional') + + obj['save-optional'] = false + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {saveType: 'peer'}, 'switch peerOptional to peer if false') + + flat.saveType = 'optional' + definitions['save-optional'].flatten('save-optional', obj, flat) + t.strictSame(flat, {}, 'remove saveType if optional and setting false') + + t.end() + }) + + t.end() +}) + +t.test('cafile -> flat.ca', t => { + const path = t.testdir({ + cafile: ` +-----BEGIN CERTIFICATE----- +XXXX +XXXX +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +YYYY\r +YYYY\r +-----END CERTIFICATE----- +`, + }) + const cafile = resolve(path, 'cafile') + + const obj = {} + const flat = {} + definitions.cafile.flatten('cafile', obj, flat) + t.strictSame(flat, {}, 'no effect if no cafile set') + obj.cafile = resolve(path, 'no/cafile/here') + definitions.cafile.flatten('cafile', obj, flat) + t.strictSame(flat, {}, 'no effect if cafile not found') + obj.cafile = cafile + definitions.cafile.flatten('cafile', obj, flat) + t.strictSame(flat, { + ca: [ + '-----BEGIN CERTIFICATE-----\nXXXX\nXXXX\n-----END CERTIFICATE-----', + '-----BEGIN CERTIFICATE-----\nYYYY\nYYYY\n-----END CERTIFICATE-----', + ], + }) + t.test('error other than ENOENT gets thrown', t => { + const poo = new Error('poo') + const defnReadFileThrows = requireInject(defpath, { + fs: { + ...require('fs'), + readFileSync: () => { + throw poo + }, + }, + }) + t.throws(() => defnReadFileThrows.cafile.flatten('cafile', obj, {}), poo) + t.end() + }) + + t.end() +}) + +t.test('detect CI', t => { + const defnNoCI = requireInject(defpath, { + '@npmcli/ci-detect': () => false, + }) + const defnCIFoo = requireInject(defpath, { + '@npmcli/ci-detect': () => 'foo', + }) + t.equal(defnNoCI['ci-name'].default, null, 'null when not in CI') + t.equal(defnCIFoo['ci-name'].default, 'foo', 'name of CI when in CI') + t.end() +}) + +t.test('user-agent', t => { + const obj = { + 'user-agent': definitions['user-agent'].default, + 'npm-version': '1.2.3', + 'node-version': '9.8.7', + } + const flat = {} + const expectNoCI = `npm/1.2.3 node/9.8.7 ` + + `${process.platform} ${process.arch}` + definitions['user-agent'].flatten('user-agent', obj, flat) + t.equal(flat.userAgent, expectNoCI) + obj['ci-name'] = 'foo' + const expectCI = `${expectNoCI} ci/foo` + definitions['user-agent'].flatten('user-agent', obj, flat) + t.equal(flat.userAgent, expectCI) + t.end() +}) diff --git a/deps/npm/test/lib/utils/config/describe-all.js b/deps/npm/test/lib/utils/config/describe-all.js new file mode 100644 index 00000000000000..814d92ac959707 --- /dev/null +++ b/deps/npm/test/lib/utils/config/describe-all.js @@ -0,0 +1,6 @@ +const t = require('tap') +const describeAll = require('../../../../lib/utils/config/describe-all.js') +// this basically ends up being a duplicate of the helpdoc dumped into +// a snapshot, but it verifies that we get the same help output on every +// platform where we run CI. +t.matchSnapshot(describeAll()) diff --git a/deps/npm/test/lib/utils/config/flatten.js b/deps/npm/test/lib/utils/config/flatten.js new file mode 100644 index 00000000000000..9fac0820cb0ea9 --- /dev/null +++ b/deps/npm/test/lib/utils/config/flatten.js @@ -0,0 +1,34 @@ +const t = require('tap') +const flatten = require('../../../../lib/utils/config/flatten.js') + +require.main.filename = '/path/to/npm' +delete process.env.NODE +process.execPath = '/path/to/node' + +const obj = { + 'save-dev': true, + '@foobar:registry': 'https://foo.bar.com/', + '//foo.bar.com:_authToken': 'foobarbazquuxasdf', + userconfig: '/path/to/.npmrc', +} + +const flat = flatten(obj) +t.strictSame(flat, { + saveType: 'dev', + '@foobar:registry': 'https://foo.bar.com/', + '//foo.bar.com:_authToken': 'foobarbazquuxasdf', + npmBin: '/path/to/npm', + nodeBin: '/path/to/node', + hashAlgorithm: 'sha1', +}) + +// now flatten something else on top of it. +process.env.NODE = '/usr/local/bin/node.exe' +flatten({ 'save-dev': false }, flat) +t.strictSame(flat, { + '@foobar:registry': 'https://foo.bar.com/', + '//foo.bar.com:_authToken': 'foobarbazquuxasdf', + npmBin: '/path/to/npm', + nodeBin: '/usr/local/bin/node.exe', + hashAlgorithm: 'sha1', +}) diff --git a/deps/npm/test/lib/utils/config/index.js b/deps/npm/test/lib/utils/config/index.js new file mode 100644 index 00000000000000..75d72e784fd89b --- /dev/null +++ b/deps/npm/test/lib/utils/config/index.js @@ -0,0 +1,24 @@ +const t = require('tap') +const config = require('../../../../lib/utils/config/index.js') +const flatten = require('../../../../lib/utils/config/flatten.js') +const definitions = require('../../../../lib/utils/config/definitions.js') +const describeAll = require('../../../../lib/utils/config/describe-all.js') +t.matchSnapshot(config.shorthands, 'shorthands') + +// just spot check a few of these to show that we got defaults assembled +t.match(config.defaults, { + registry: definitions.registry.default, + 'init-module': definitions['init-module'].default, +}) + +// is a getter, so changes are reflected +definitions.registry.default = 'https://example.com' +t.strictSame(config.defaults.registry, 'https://example.com') + +t.strictSame(config, { + defaults: config.defaults, + shorthands: config.shorthands, + flatten, + definitions, + describeAll, +}) diff --git a/deps/npm/test/lib/utils/did-you-mean.js b/deps/npm/test/lib/utils/did-you-mean.js index 0c9c95c7f9e60f..68893a800fda45 100644 --- a/deps/npm/test/lib/utils/did-you-mean.js +++ b/deps/npm/test/lib/utils/did-you-mean.js @@ -1,7 +1,39 @@ const t = require('tap') +const requireInject = require('require-inject') +const npm = requireInject('../../../lib/npm.js') + const dym = require('../../../lib/utils/did-you-mean.js') -t.equal(dym('asdfa', ['asdf', 'asfd', 'adfs', 'safd', 'foobarbaz', 'foobar']), - '\nDid you mean this?\n asdf') -t.equal(dym('asdfa', ['asdf', 'sdfa', 'foo', 'bar', 'fdsa']), - '\nDid you mean one of these?\n asdf\n sdfa') -t.equal(dym('asdfa', ['install', 'list', 'test']), '') +t.test('did-you-mean', t => { + npm.load(err => { + t.notOk(err) + t.test('nistall', async t => { + const result = await dym(npm, npm.localPrefix, 'nistall') + t.match(result, 'npm install') + }) + t.test('sttest', async t => { + const result = await dym(npm, npm.localPrefix, 'sttest') + t.match(result, 'npm test') + t.match(result, 'npm run posttest') + }) + t.test('npz', async t => { + const result = await dym(npm, npm.localPrefix, 'npxx') + t.match(result, 'npm exec npx') + }) + t.test('qwuijbo', async t => { + const result = await dym(npm, npm.localPrefix, 'qwuijbo') + t.match(result, '') + }) + t.end() + }) +}) + +t.test('missing bin and script properties', async t => { + const path = t.testdir({ + 'package.json': JSON.stringify({ + name: 'missing-bin', + }), + }) + + const result = await dym(npm, path, 'nistall') + t.match(result, 'npm install') +}) diff --git a/deps/npm/test/lib/utils/flat-options.js b/deps/npm/test/lib/utils/flat-options.js deleted file mode 100644 index 6f580fabc45114..00000000000000 --- a/deps/npm/test/lib/utils/flat-options.js +++ /dev/null @@ -1,359 +0,0 @@ -const t = require('tap') - -process.env.NODE = '/path/to/some/node' -process.env.NODE_ENV = 'development' - -const logs = [] -const log = require('npmlog') -log.warn = (...args) => logs.push(['warn', ...args]) -log.verbose = (...args) => logs.push(['verbose', ...args]) - -class Mocknpm { - constructor (opts = {}) { - this.modes = { - exec: 0o777, - file: 0o666, - umask: 0o22, - } - this.color = true - this.projectScope = '@npmcli' - this.tmp = '/tmp' - this.command = null - this.globalPrefix = '/usr/local' - this.localPrefix = '/path/to/npm/cli' - this.prefix = this.localPrefix - this.version = '7.6.5' - this.config = new MockConfig(opts) - this.flatOptions = null - } -} - -class MockConfig { - constructor (opts = {}) { - this.list = [{ - cache: 'cache', - 'node-version': '1.2.3', - global: 'global', - registry: 'registry', - access: 'access', - 'always-auth': 'always-auth', - audit: 'audit', - 'audit-level': 'audit-level', - 'auth-type': 'auth-type', - before: 'before', - browser: 'browser', - ca: 'ca', - cafile: 'cafile', - call: 'call', - cert: 'cert', - key: 'key', - 'cache-lock-retries': 'cache-lock-retries', - 'cache-lock-stale': 'cache-lock-stale', - 'cache-lock-wait': 'cache-lock-wait', - cidr: 'cidr', - 'read-only': 'read-only', - preid: 'preid', - 'tag-version-prefix': 'tag-version-prefix', - 'allow-same-version': 'allow-same-version', - message: 'message', - 'commit-hooks': 'commit-hooks', - 'git-tag-version': 'git-tag-version', - 'sign-git-commit': 'sign-git-commit', - 'sign-git-tag': 'sign-git-tag', - depth: 'depth', - description: 'description', - searchexclude: 'searchexclude', - searchlimit: 'searchlimit', - searchopts: 'from=1', - searchstaleness: 'searchstaleness', - 'dry-run': 'dry-run', - 'engine-strict': 'engine-strict', - 'fetch-retries': 'fetch-retries', - 'fetch-retry-factor': 'fetch-retry-factor', - 'fetch-retry-mintimeout': 'fetch-retry-mintimeout', - 'fetch-retry-maxtimeout': 'fetch-retry-maxtimeout', - 'fetch-timeout': 'fetch-timeout', - force: 'force', - 'format-package-lock': 'format-package-lock', - fund: 'fund', - git: 'git', - viewer: 'viewer', - editor: 'editor', - 'bin-links': 'bin-links', - 'rebuild-bundle': 'rebuild-bundle', - package: 'package', - 'package-lock': 'package-lock', - 'package-lock-only': 'package-lock-only', - 'global-style': 'global-style', - 'legacy-bundling': 'legacy-bundling', - 'script-shell': 'script-shell', - omit: [], - include: [], - save: 'save', - 'save-bundle': 'save-bundle', - 'save-dev': 'save-dev', - 'save-optional': 'save-optional', - 'save-peer': 'save-peer', - 'save-prod': 'save-prod', - 'save-exact': 'save-exact', - 'save-prefix': 'save-prefix', - otp: 'otp', - offline: 'offline', - 'prefer-online': 'prefer-online', - 'prefer-offline': 'prefer-offline', - 'cache-max': 'cache-max', - 'cache-min': 'cache-min', - 'strict-ssl': 'strict-ssl', - scope: '', - tag: 'tag', - 'user-agent': 'user-agent', - '@scope:registry': '@scope:registry', - '//nerf.dart:_authToken': '//nerf.dart:_authToken', - proxy: 'proxy', - noproxy: 'noproxy', - ...opts, - }] - } - - get (key) { - return this.list[0][key] - } - - set (key, val) { - this.list[0][key] = val - } -} - -const flatOptions = require('../../../lib/utils/flat-options.js') -t.match(logs, [[ - 'verbose', - 'npm-session', - /^[0-9a-f]{16}$/, -]], 'logged npm session verbosely') -logs.length = 0 - -t.test('basic', t => { - const npm = new Mocknpm() - const generatedFlat = flatOptions(npm) - const clean = { - ...generatedFlat, - npmBin: '/path/to/npm/bin.js', - log: {}, - npmSession: '12345', - cache: generatedFlat.cache.replace(/\\/g, '/'), - } - t.matchSnapshot(clean, 'flat options') - t.equal(generatedFlat.npmCommand, null, 'command not set yet') - npm.command = 'view' - t.equal(generatedFlat.npmCommand, 'view', 'command updated via getter') - t.equal(generatedFlat.npmBin, require.main.filename) - // test the object is frozen - generatedFlat.newField = 'asdf' - t.equal(generatedFlat.newField, undefined, 'object is frozen') - const preExistingOpts = { flat: 'options' } - npm.flatOptions = preExistingOpts - t.equal(flatOptions(npm), preExistingOpts, 'use pre-existing npm.flatOptions') - t.end() -}) - -t.test('get preferOffline from cache-min', t => { - const npm = new Mocknpm({ - 'cache-min': 9999999, - 'prefer-offline': undefined, - }) - const opts = flatOptions(npm) - t.equal(opts.preferOffline, true, 'got preferOffline from cache min') - logs.length = 0 - t.equal(opts.cacheMin, undefined, 'opts.cacheMin is not set') - t.match(logs, []) - logs.length = 0 - t.end() -}) - -t.test('get preferOnline from cache-max', t => { - const npm = new Mocknpm({ - 'cache-max': -1, - 'prefer-online': undefined, - }) - const opts = flatOptions(npm) - t.equal(opts.preferOnline, true, 'got preferOnline from cache min') - logs.length = 0 - t.equal(opts.cacheMax, undefined, 'opts.cacheMax is not set') - t.match(logs, []) - logs.length = 0 - t.end() -}) - -t.test('tag emits warning', t => { - const npm = new Mocknpm({ tag: 'foobar' }) - t.equal(flatOptions(npm).tag, 'foobar', 'tag is foobar') - t.match(logs, []) - logs.length = 0 - t.end() -}) - -t.test('omit/include options', t => { - t.test('omit explicitly', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - }) - t.strictSame(flatOptions(npm).omit, ['dev', 'optional', 'peer']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('omit and include some', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - include: ['peer'], - }) - t.strictSame(flatOptions(npm).omit, ['dev', 'optional']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('dev flag', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - include: [], - dev: true, - }) - t.strictSame(flatOptions(npm).omit, ['optional', 'peer']) - t.equal(process.env.NODE_ENV, NODE_ENV) - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('production flag', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: [], - include: [], - production: true, - }) - t.strictSame(flatOptions(npm).omit, ['dev']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('only', t => { - const { NODE_ENV } = process.env - const cases = ['prod', 'production'] - t.plan(cases.length) - cases.forEach(c => t.test(c, t => { - const npm = new Mocknpm({ - omit: [], - include: [], - only: c, - }) - t.strictSame(flatOptions(npm).omit, ['dev']) - t.equal(process.env.NODE_ENV, 'production') - process.env.NODE_ENV = NODE_ENV - t.end() - })) - }) - - t.test('also dev', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - omit: ['dev', 'optional', 'peer'], - also: 'dev', - }) - t.strictSame(flatOptions(npm).omit, ['optional', 'peer']) - t.equal(process.env.NODE_ENV, NODE_ENV) - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.test('no-optional', t => { - const { NODE_ENV } = process.env - const npm = new Mocknpm({ - optional: false, - omit: null, - include: null, - }) - t.strictSame(flatOptions(npm).omit, ['optional']) - t.equal(process.env.NODE_ENV, NODE_ENV) - process.env.NODE_ENV = NODE_ENV - t.end() - }) - - t.end() -}) - -t.test('get the node without the environ', t => { - delete process.env.NODE - t.equal(flatOptions(new Mocknpm()).nodeBin, process.execPath) - t.end() -}) - -t.test('various default values and falsey fallbacks', t => { - const npm = new Mocknpm({ - 'script-shell': false, - registry: 'http://example.com', - searchlimit: 0, - 'save-exact': false, - 'save-prefix': '>=', - }) - const opts = flatOptions(npm) - t.equal(opts.scriptShell, undefined, 'scriptShell is undefined if falsey') - t.equal(opts.search.limit, 20, 'searchLimit defaults to 20') - t.equal(opts.savePrefix, '>=', 'save-prefix respected if no save-exact') - t.equal(opts.scope, '', 'scope defaults to empty string') - logs.length = 0 - t.end() -}) - -t.test('legacy _auth token', t => { - const npm = new Mocknpm({ - _auth: 'asdfasdf', - }) - t.strictSame( - flatOptions(npm)._auth, - 'asdfasdf', - 'should set legacy _auth token' - ) - t.end() -}) - -t.test('save-type', t => { - const base = { - 'save-optional': false, - 'save-peer': false, - 'save-dev': false, - 'save-prod': false, - } - const cases = [ - ['peerOptional', { - 'save-optional': true, - 'save-peer': true, - }], - ['optional', { - 'save-optional': true, - }], - ['dev', { - 'save-dev': true, - }], - ['peer', { - 'save-peer': true, - }], - ['prod', { - 'save-prod': true, - }], - [null, {}], - ] - for (const [expect, options] of cases) { - const opts = flatOptions(new Mocknpm({ - ...base, - ...options, - })) - t.equal(opts.saveType, expect, JSON.stringify(options)) - } - t.end() -}) diff --git a/deps/npm/test/lib/utils/lifecycle-cmd.js b/deps/npm/test/lib/utils/lifecycle-cmd.js index 3e3a7da43443e8..862c87a8e032cf 100644 --- a/deps/npm/test/lib/utils/lifecycle-cmd.js +++ b/deps/npm/test/lib/utils/lifecycle-cmd.js @@ -10,6 +10,7 @@ const npm = { }, } t.test('create a lifecycle command', t => { + t.plan(5) class TestStage extends LifecycleCmd { static get name () { return 'test-stage' @@ -20,6 +21,9 @@ t.test('create a lifecycle command', t => { cmd.exec(['some', 'args'], (er, result) => { t.same(runArgs, ['test-stage', 'some', 'args']) t.strictSame(result, 'called npm.commands.run') - t.end() + }) + cmd.execWorkspaces(['some', 'args'], [], (er, result) => { + t.same(runArgs, ['test-stage', 'some', 'args']) + t.strictSame(result, 'called npm.commands.run') }) }) diff --git a/deps/npm/test/lib/utils/npm-usage.js b/deps/npm/test/lib/utils/npm-usage.js index fbc453811ec2f8..ebf637ae1cfb71 100644 --- a/deps/npm/test/lib/utils/npm-usage.js +++ b/deps/npm/test/lib/utils/npm-usage.js @@ -1,12 +1,5 @@ const t = require('tap') - -const OUTPUT = [] -const output = (...msg) => OUTPUT.push(msg) -const requireInject = require('require-inject') -const usage = require('../../../lib/utils/npm-usage.js') - -const npm = requireInject('../../../lib/npm.js') -npm.output = output +const npm = require('../../../lib/npm.js') t.test('usage', t => { t.afterEach((cb) => { @@ -29,61 +22,19 @@ t.test('usage', t => { npm.config.set('userconfig', '/some/config/file/.npmrc') t.test('basic usage', t => { - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 + t.matchSnapshot(npm.usage) t.end() }) t.test('with browser', t => { npm.config.set('viewer', 'browser') - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 - npm.config.set('viewer', null) + t.matchSnapshot(npm.usage) t.end() }) t.test('with long', t => { npm.config.set('long', true) - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 - npm.config.set('long', false) - t.end() - }) - - t.test('did you mean?', t => { - npm.argv.push('unistnall') - usage(npm) - t.equal(OUTPUT.length, 2) - t.equal(OUTPUT[0].length, 1) - t.equal(OUTPUT[1].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - t.matchSnapshot(OUTPUT[1][0]) - OUTPUT.length = 0 - npm.argv.length = 0 - t.end() - }) - - t.test('did you mean?', t => { - npm.argv.push('unistnall') - const { exitCode } = process - t.teardown(() => { - if (t.passing()) - process.exitCode = exitCode - }) - // make sure it fails when invalid - usage(npm, false) - t.equal(process.exitCode, 1) - OUTPUT.length = 0 - npm.argv.length = 0 + t.matchSnapshot(npm.usage) t.end() }) @@ -106,11 +57,7 @@ t.test('usage', t => { configurable: true, writable: true, }) - usage(npm) - t.equal(OUTPUT.length, 1) - t.equal(OUTPUT[0].length, 1) - t.matchSnapshot(OUTPUT[0][0]) - OUTPUT.length = 0 + t.matchSnapshot(npm.usage) t.end() }) } diff --git a/deps/npm/test/lib/utils/read-local-package.js b/deps/npm/test/lib/utils/read-local-package.js index 9ae21f7d62b4c4..4b693afb48b8ea 100644 --- a/deps/npm/test/lib/utils/read-local-package.js +++ b/deps/npm/test/lib/utils/read-local-package.js @@ -1,22 +1,17 @@ const requireInject = require('require-inject') const { test } = require('tap') +const mockNpm = require('../../fixtures/mock-npm') -let prefix -const _flatOptions = { +const config = { json: false, global: false, - get prefix () { - return prefix - }, } +const npm = mockNpm({ config }) const readLocalPackageName = requireInject('../../../lib/utils/read-local-package.js') -const npm = { - flatOptions: _flatOptions, -} test('read local package.json', async (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: 'my-local-package', version: '1.0.0', @@ -31,7 +26,7 @@ test('read local package.json', async (t) => { }) test('read local scoped-package.json', async (t) => { - prefix = t.testdir({ + npm.prefix = t.testdir({ 'package.json': JSON.stringify({ name: '@my-scope/my-local-package', version: '1.0.0', @@ -46,13 +41,13 @@ test('read local scoped-package.json', async (t) => { }) test('read using --global', async (t) => { - prefix = t.testdir({}) - _flatOptions.global = true + npm.prefix = t.testdir({}) + config.global = true const packageName = await readLocalPackageName(npm) t.equal( packageName, undefined, 'should not retrieve a package name' ) - _flatOptions.global = false + config.global = false }) diff --git a/deps/npm/test/lib/utils/tar.js b/deps/npm/test/lib/utils/tar.js index b780a73e5ec1c7..d9b8c5584a61b8 100644 --- a/deps/npm/test/lib/utils/tar.js +++ b/deps/npm/test/lib/utils/tar.js @@ -101,9 +101,9 @@ test('should getContents of a tarball', async (t) => { id: 'my-cool-pkg@1.0.0', name: 'my-cool-pkg', version: '1.0.0', - size: 149, + size: 146, unpackedSize: 49, - shasum: 'c0bfd67a5142104e429afda09119eedd6a30d2fc', + shasum: 'b8379c5e69693cdda73aec3d81dae1d11c1e75bd', integrity: ssri.parse(integrity.sha512[0]), filename: 'my-cool-pkg-1.0.0.tgz', files: [{ path: 'package.json', size: 49, mode: 420 }], diff --git a/deps/npm/test/lib/version.js b/deps/npm/test/lib/version.js index a8fcd831fb5c32..35d3d92cd2beeb 100644 --- a/deps/npm/test/lib/version.js +++ b/deps/npm/test/lib/version.js @@ -1,21 +1,23 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let result = [] const noop = () => null -const npm = { - flatOptions: { - tagVersionPrefix: 'v', - json: false, - }, +const config = { + 'tag-version-prefix': 'v', + json: false, +} +const npm = mockNpm({ + config, prefix: '', version: '1.0.0', output: (...msg) => { for (const m of msg) result.push(m) }, -} +}) const mocks = { libnpmversion: noop, } @@ -25,7 +27,7 @@ const version = new Version(npm) const _processVersions = process.versions t.afterEach(cb => { - npm.flatOptions.json = false + config.json = false npm.prefix = '' process.versions = _processVersions result = [] @@ -116,7 +118,7 @@ t.test('failure reading package.json', t => { t.test('--json option', t => { const prefix = t.testdir({}) - npm.flatOptions.json = true + config.json = true npm.prefix = prefix Object.defineProperty(process, 'versions', { value: {} }) @@ -140,8 +142,6 @@ t.test('with one arg', t => { t.deepEqual( opts, { - tagVersionPrefix: 'v', - json: false, path: '', }, 'should forward expected options' diff --git a/deps/npm/test/lib/view.js b/deps/npm/test/lib/view.js index 1363a5b9f9ac84..d136a1f418d10f 100644 --- a/deps/npm/test/lib/view.js +++ b/deps/npm/test/lib/view.js @@ -1,5 +1,6 @@ const t = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') let logs const cleanLogs = (done) => { @@ -243,34 +244,33 @@ t.test('should log package info', t => { packument, }, }) - const view = new View({ - flatOptions: { - global: false, - }, + const npm = mockNpm({ + config: { global: false }, }) + const view = new View(npm) const ViewJson = requireInject('../../lib/view.js', { pacote: { packument, }, }) - const viewJson = new ViewJson({ - flatOptions: { - json: true, - }, + const jsonNpm = mockNpm({ + config: { json: true }, }) + const viewJson = new ViewJson(jsonNpm) const ViewUnicode = requireInject('../../lib/view.js', { pacote: { packument, }, }) - const viewUnicode = new ViewUnicode({ - flatOptions: { + const unicodeNpm = mockNpm({ + config: { global: false, unicode: true, }, }) + const viewUnicode = new ViewUnicode(unicodeNpm) t.test('package with license, bugs, repository and other fields', t => { view.exec(['green@1.0.0'], () => { @@ -351,13 +351,14 @@ t.test('should log info of package in current working dir', t => { packument, }, }) - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - defaultTag: '1.0.0', + config: { + tag: '1.0.0', global: false, }, }) + const view = new View(npm) t.test('specific version', t => { view.exec(['.@1.0.0'], () => { @@ -382,23 +383,24 @@ t.test('should log info by field name', t => { packument, }, }) - const viewJson = new ViewJson({ - flatOptions: { + const jsonNpm = mockNpm({ + config: { json: true, global: false, }, }) + const viewJson = new ViewJson(jsonNpm) + const View = requireInject('../../lib/view.js', { pacote: { packument, }, }) - const view = new View({ - flatOptions: { - global: false, - }, + const npm = mockNpm({ + config: { global: false }, }) + const view = new View(npm) t.test('readme', t => { view.exec(['yellow@1.0.0', 'readme'], () => { @@ -468,11 +470,10 @@ t.test('should log info by field name', t => { t.test('throw error if global mode', (t) => { const View = requireInject('../../lib/view.js') - const view = new View({ - flatOptions: { - global: true, - }, + const npm = mockNpm({ + config: { global: true }, }) + const view = new View(npm) view.exec([], (err) => { t.equals(err.message, 'Cannot use view command in global mode.') t.end() @@ -483,12 +484,11 @@ t.test('throw ENOENT error if package.json misisng', (t) => { const testDir = t.testdir({}) const View = requireInject('../../lib/view.js') - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - global: false, - }, + config: { global: false }, }) + const view = new View(npm) view.exec([], (err) => { t.match(err, { code: 'ENOENT' }) t.end() @@ -501,12 +501,11 @@ t.test('throw EJSONPARSE error if package.json not json', (t) => { }) const View = requireInject('../../lib/view.js') - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - global: false, - }, + config: { global: false }, }) + const view = new View(npm) view.exec([], (err) => { t.match(err, { code: 'EJSONPARSE' }) t.end() @@ -519,12 +518,11 @@ t.test('throw error if package.json has no name', (t) => { }) const View = requireInject('../../lib/view.js') - const view = new View({ + const npm = mockNpm({ prefix: testDir, - flatOptions: { - global: false, - }, + config: { global: false }, }) + const view = new View(npm) view.exec([], (err) => { t.equals(err.message, 'Invalid package.json, no "name" field') t.end() @@ -537,12 +535,13 @@ t.test('throws when unpublished', (t) => { packument, }, }) - const view = new View({ - flatOptions: { - defaultTag: '1.0.1', + const npm = mockNpm({ + config: { + tag: '1.0.1', global: false, }, }) + const view = new View(npm) view.exec(['red'], (err) => { t.equals(err.code, 'E404') t.end() @@ -555,12 +554,13 @@ t.test('completion', async t => { packument, }, }) - const view = new View({ - flatOptions: { - defaultTag: '1.0.1', + const npm = mockNpm({ + config: { + tag: '1.0.1', global: false, }, }) + const view = new View(npm) const res = await view.completion({ conf: { argv: { remain: ['npm', 'view', 'green@1.0.0'] } }, }) @@ -570,11 +570,13 @@ t.test('completion', async t => { t.test('no registry completion', async t => { const View = requireInject('../../lib/view.js') - const view = new View({ - flatOptions: { - defaultTag: '1.0.1', + const npm = mockNpm({ + config: { + tag: '1.0.1', + global: false, }, }) + const view = new View(npm) const res = await view.completion({conf: { argv: { remain: ['npm', 'view'] } } }) t.notOk(res, 'there is no package completion') t.end() diff --git a/deps/npm/test/lib/whoami.js b/deps/npm/test/lib/whoami.js index 1a1ecd25742e1f..b242ea89414787 100644 --- a/deps/npm/test/lib/whoami.js +++ b/deps/npm/test/lib/whoami.js @@ -1,18 +1,21 @@ const { test } = require('tap') const requireInject = require('require-inject') +const mockNpm = require('../fixtures/mock-npm') test('whoami', (t) => { t.plan(3) const Whoami = requireInject('../../lib/whoami.js', { '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), }) - const whoami = new Whoami({ - flatOptions: {}, + const npm = mockNpm({ + config: { json: false }, output: (output) => { t.equal(output, 'foo', 'should output the username') }, }) + const whoami = new Whoami(npm) + whoami.exec([], (err) => { t.ifError(err, 'npm whoami') t.ok('should successfully print username') @@ -24,12 +27,13 @@ test('whoami json', (t) => { const Whoami = requireInject('../../lib/whoami.js', { '../../lib/utils/get-identity.js': () => Promise.resolve('foo'), }) - const whoami = new Whoami({ - flatOptions: { json: true }, + const npm = mockNpm({ + config: { json: true }, output: (output) => { - t.equal(output, '"foo"', 'should output the username as json') + t.equal(output, '"foo"', 'should output the username') }, }) + const whoami = new Whoami(npm) whoami.exec([], (err) => { t.ifError(err, 'npm whoami') diff --git a/deps/npm/test/lib/workspaces/get-workspaces.js b/deps/npm/test/lib/workspaces/get-workspaces.js new file mode 100644 index 00000000000000..ebed9dd35c519e --- /dev/null +++ b/deps/npm/test/lib/workspaces/get-workspaces.js @@ -0,0 +1,199 @@ +const { resolve } = require('path') +const t = require('tap') +const getWorkspaces = require('../../../lib/workspaces/get-workspaces.js') + +const normalizePath = p => p + .replace(/\\+/g, '/') + .replace(/\r\n/g, '\n') + +const cleanOutput = (str, path) => normalizePath(str) + .replace(normalizePath(path), '{PATH}') + +const clean = (res, path) => { + const cleaned = new Map() + for (const [key, value] of res.entries()) + cleaned.set(key, cleanOutput(value, path)) + return cleaned +} + +t.test('get-workspaces', async t => { + const path = t.testdir({ + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + version: '1.0.0', + scripts: { glorp: 'echo a doing the glerp glop' }, + }), + }, + b: { + 'package.json': JSON.stringify({ + name: 'b', + version: '2.0.0', + scripts: { glorp: 'echo b doing the glerp glop' }, + }), + }, + c: { + 'package.json': JSON.stringify({ + name: 'c', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + lorem: 'echo c lorem', + }, + }), + }, + d: { + 'package.json': JSON.stringify({ + name: 'd', + version: '1.0.0', + scripts: { + test: 'exit 0', + posttest: 'echo posttest', + }, + }), + }, + e: { + 'package.json': JSON.stringify({ + name: 'e', + scripts: { test: 'exit 0', start: 'echo start something' }, + }), + }, + noscripts: { + 'package.json': JSON.stringify({ + name: 'noscripts', + version: '1.0.0', + }), + }, + }, + 'package.json': JSON.stringify({ + name: 'x', + version: '1.2.3', + workspaces: ['packages/*'], + }), + }) + + let workspaces + + workspaces = await getWorkspaces(['a', 'b'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + })), + 'should filter by package name' + ) + + workspaces = await getWorkspaces(['./packages/c'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + c: '{PATH}/packages/c', + })), + 'should filter by package directory' + ) + + workspaces = await getWorkspaces(['packages/c'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + c: '{PATH}/packages/c', + })), + 'should filter by rel package directory' + ) + + workspaces = await getWorkspaces([resolve(path, 'packages/c')], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + c: '{PATH}/packages/c', + })), + 'should filter by absolute package directory' + ) + + workspaces = await getWorkspaces(['packages'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should filter by parent directory name' + ) + + workspaces = await getWorkspaces(['./packages/'], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should filter by parent directory path' + ) + + workspaces = await getWorkspaces([resolve(path, './packages')], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should filter by absolute parent directory path' + ) + + workspaces = await getWorkspaces([], { path }) + t.deepEqual( + clean(workspaces, path), + new Map(Object.entries({ + a: '{PATH}/packages/a', + b: '{PATH}/packages/b', + c: '{PATH}/packages/c', + d: '{PATH}/packages/d', + e: '{PATH}/packages/e', + noscripts: '{PATH}/packages/noscripts', + })), + 'should return all workspaces if no filter set' + ) + + try { + await getWorkspaces(['missing'], { path }) + throw new Error('missed throw') + } catch (err) { + t.match( + err, + /No workspaces found/, + 'should throw no workspaces found error' + ) + } + + const unconfiguredWorkspaces = t.testdir({ + 'package.json': JSON.stringify({ + name: 'no-configured-workspaces', + version: '1.0.0', + }), + }) + try { + await getWorkspaces([], { path: unconfiguredWorkspaces }) + throw new Error('missed throw') + } catch (err) { + t.match( + err, + /No workspaces found/, + 'should throw no workspaces found error' + ) + } +}) From 33c35a38dc116914847b76428ff696c403dfb9e6 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius <daniel.bevenius@gmail.com> Date: Mon, 22 Mar 2021 11:28:48 +0100 Subject: [PATCH 54/85] test: add OpenSSL 3.0 checks to test-crypto-keygen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently test-crypto-keygen.js fails when dynamically linking against OpenSSL 3.0 which the following error: === debug test-crypto-keygen === Path: parallel/test-crypto-keygen node:assert:901 throw newErr; ^ AssertionError [ERR_ASSERTION]: ifError got unwanted exception: error:05000072:dsa routines::bad ffc parameters at DsaKeyPairGenJob.<anonymous> (/nodejs/openssl/test/common/index.js:342:12) at DsaKeyPairGenJob.<anonymous> (/openssl/test/common/index.js:379:15) at DsaKeyPairGenJob.job.ondone (node:internal/crypto/keygen:77:23) { generatedMessage: false, code: 'ERR_ASSERTION', actual: [Error: error:05000072:dsa routines::bad ffc parameters], expected: null, operator: 'ifError' } Command: node /nodejs/openssl/test/parallel/test-crypto-keygen.js This commit adds a check and adjusts the modulus length when linking against OpenSSL 3.0. PR-URL: https://github.com/nodejs/node/pull/37860 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- test/parallel/test-crypto-keygen.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-crypto-keygen.js b/test/parallel/test-crypto-keygen.js index af73d5a6b45fd6..e5c8a107ba58a1 100644 --- a/test/parallel/test-crypto-keygen.js +++ b/test/parallel/test-crypto-keygen.js @@ -389,20 +389,20 @@ const sec1EncExp = (cipher) => getRegExpForPEM('EC PRIVATE KEY', cipher); { // Test async DSA key object generation. generateKeyPair('dsa', { - modulusLength: 512, + modulusLength: common.hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }, common.mustSucceed((publicKey, privateKey) => { assert.strictEqual(publicKey.type, 'public'); assert.strictEqual(publicKey.asymmetricKeyType, 'dsa'); assert.deepStrictEqual(publicKey.asymmetricKeyDetails, { - modulusLength: 512, + modulusLength: common.hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }); assert.strictEqual(privateKey.type, 'private'); assert.strictEqual(privateKey.asymmetricKeyType, 'dsa'); assert.deepStrictEqual(privateKey.asymmetricKeyDetails, { - modulusLength: 512, + modulusLength: common.hasOpenSSL3 ? 2048 : 512, divisorLength: 256 }); })); From 5cdeb7670834f3fb1316289f23916200ef3ceae8 Mon Sep 17 00:00:00 2001 From: Daniel Bevenius <daniel.bevenius@gmail.com> Date: Mon, 22 Mar 2021 12:02:55 +0100 Subject: [PATCH 55/85] test: add OpenSSL 3.0 checks to tls-passphrase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37860 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> --- test/parallel/test-tls-passphrase.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-tls-passphrase.js b/test/parallel/test-tls-passphrase.js index 610bbefe46c9c0..c0d0051ff8bbc2 100644 --- a/test/parallel/test-tls-passphrase.js +++ b/test/parallel/test-tls-passphrase.js @@ -224,7 +224,7 @@ server.listen(0, common.mustCall(function() { })).unref(); const errMessagePassword = common.hasOpenSSL3 ? - /processing error/ : /bad decrypt/; + /Error: PEM_read_bio_PrivateKey/ : /bad decrypt/; // Missing passphrase assert.throws(function() { @@ -254,7 +254,8 @@ assert.throws(function() { }); }, errMessagePassword); -const errMessageDecrypt = /bad decrypt/; +const errMessageDecrypt = common.hasOpenSSL3 ? + /Error: PEM_read_bio_PrivateKey/ : /bad decrypt/; // Invalid passphrase assert.throws(function() { From 3ab223dd3221d1c548cbad4859512f716b7223f6 Mon Sep 17 00:00:00 2001 From: Michael Dawson <mdawson@devrus.com> Date: Tue, 23 Mar 2021 11:26:18 -0400 Subject: [PATCH 56/85] node-api: fix crash in finalization Refs: https://github.com/nodejs/node-addon-api/issues/906 Refs: https://github.com/nodejs/node/pull/37616 Fix crash introduced by https://github.com/nodejs/node/pull/37616 Signed-off-by: Michael Dawson <mdawson@devrus.com> PR-URL: https://github.com/nodejs/node/pull/37876 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- src/js_native_api_v8.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/js_native_api_v8.cc b/src/js_native_api_v8.cc index 602ded21569cd7..664aa2d41bbc98 100644 --- a/src/js_native_api_v8.cc +++ b/src/js_native_api_v8.cc @@ -374,8 +374,11 @@ class Reference : public RefBase { inline void Finalize(bool is_env_teardown = false) override { // During env teardown, `~napi_env()` alone is responsible for finalizing. // Thus, we don't want any stray gc passes to trigger a second call to - // `Finalize()`, so let's reset the persistent here. - if (is_env_teardown) _persistent.ClearWeak(); + // `Finalize()`, so let's reset the persistent here if nothing is + // keeping it alive. + if (is_env_teardown && _persistent.IsWeak()) { + _persistent.ClearWeak(); + } // Chain up to perform the rest of the finalization. RefBase::Finalize(is_env_teardown); From 649e04c4a5eb2d62c76003f4e3e5e045419a0d59 Mon Sep 17 00:00:00 2001 From: Ruy Adorno <ruyadorno@hotmail.com> Date: Wed, 24 Mar 2021 17:25:32 -0400 Subject: [PATCH 57/85] deps: upgrade npm to 7.7.4 PR-URL: https://github.com/nodejs/node/pull/37897 Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com> --- deps/npm/AUTHORS | 2 + deps/npm/CHANGELOG.md | 77 +++ .../docs/content/commands/npm-find-dupes.md | 1 + deps/npm/docs/content/using-npm/config.md | 14 +- deps/npm/docs/output/commands/npm-ls.html | 2 +- deps/npm/docs/output/commands/npm.html | 2 +- deps/npm/docs/output/using-npm/config.html | 15 +- deps/npm/lib/audit.js | 1 + deps/npm/lib/ci.js | 11 +- deps/npm/lib/config.js | 5 + deps/npm/lib/dedupe.js | 1 + deps/npm/lib/exec.js | 12 +- deps/npm/lib/install.js | 13 +- deps/npm/lib/link.js | 9 +- deps/npm/lib/prune.js | 8 +- deps/npm/lib/set-script.js | 11 + deps/npm/lib/uninstall.js | 16 +- deps/npm/lib/update.js | 1 + deps/npm/lib/utils/config/definitions.js | 86 ++-- deps/npm/man/man1/npm-ls.1 | 2 +- deps/npm/man/man1/npm.1 | 2 +- deps/npm/man/man7/config.7 | 18 +- .../@npmcli/disparity-colors/CHANGELOG.md | 1 + .../@npmcli/disparity-colors/README.md | 3 +- .../node_modules/string-width/index.js | 8 +- .../node_modules/string-width/package.json | 2 +- .../node_modules/string-width/readme.md | 2 +- deps/npm/node_modules/diff/README.md | 2 +- deps/npm/node_modules/env-paths/index.d.ts | 32 ++ deps/npm/node_modules/env-paths/package.json | 2 +- deps/npm/node_modules/env-paths/readme.md | 49 +- .../hosted-git-info/git-host-info.js | 2 +- .../node_modules/hosted-git-info/package.json | 2 +- deps/npm/node_modules/libnpmdiff/CHANGELOG.md | 1 + deps/npm/node_modules/libnpmdiff/README.md | 5 +- deps/npm/node_modules/mime-db/HISTORY.md | 8 + deps/npm/node_modules/mime-db/README.md | 6 +- deps/npm/node_modules/mime-db/db.json | 77 ++- deps/npm/node_modules/mime-db/package.json | 18 +- deps/npm/node_modules/mime-types/HISTORY.md | 9 + deps/npm/node_modules/mime-types/README.md | 10 - deps/npm/node_modules/mime-types/package.json | 14 +- .../node_modules/hosted-git-info/CHANGELOG.md | 185 ------- .../node_modules/hosted-git-info/LICENSE | 13 - .../node_modules/hosted-git-info/README.md | 133 ----- .../hosted-git-info/git-host-info.js | 154 ------ .../node_modules/hosted-git-info/git-host.js | 110 ---- .../node_modules/hosted-git-info/index.js | 237 --------- .../node_modules/hosted-git-info/package.json | 52 -- .../node_modules/hosted-git-info/CHANGELOG.md | 185 ------- .../node_modules/hosted-git-info/LICENSE | 13 - .../node_modules/hosted-git-info/README.md | 133 ----- .../hosted-git-info/git-host-info.js | 154 ------ .../node_modules/hosted-git-info/git-host.js | 110 ---- .../node_modules/hosted-git-info/index.js | 237 --------- .../node_modules/hosted-git-info/package.json | 52 -- .../npm-pick-manifest/CHANGELOG.md | 2 +- .../node_modules/resolve/test/mock_sync.js | 1 + deps/npm/node_modules/retry/Readme.md | 2 +- deps/npm/node_modules/socks/README.md | 26 +- .../socks/build/client/socksclient.js | 90 +++- .../socks/build/client/socksclient.js.map | 2 +- .../socks/build/common/constants.js | 12 +- .../socks/build/common/constants.js.map | 2 +- .../socks/build/common/helpers.js | 27 + .../socks/build/common/helpers.js.map | 2 +- deps/npm/node_modules/socks/package.json | 16 +- .../socks/typings/client/socksclient.d.ts | 5 + .../socks/typings/common/constants.d.ts | 11 +- deps/npm/node_modules/uuid/README.md | 8 +- deps/npm/package.json | 4 +- ...ib-utils-config-definitions.js-TAP.test.js | 1 + ...b-utils-config-describe-all.js-TAP.test.js | 14 +- .../test-lib-utils-npm-usage.js-TAP.test.js | 482 +++++++++--------- .../test-lib-utils-tar.js-TAP.test.js | 2 +- deps/npm/test/lib/config.js | 19 +- deps/npm/test/lib/install.js | 6 +- deps/npm/test/lib/set-script.js | 37 ++ deps/npm/test/lib/uninstall.js | 6 +- deps/npm/test/lib/update.js | 7 +- deps/npm/test/lib/utils/config/definitions.js | 85 ++- deps/npm/test/lib/utils/config/flatten.js | 6 + 82 files changed, 1001 insertions(+), 2204 deletions(-) delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js delete mode 100644 deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js delete mode 100644 deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json diff --git a/deps/npm/AUTHORS b/deps/npm/AUTHORS index 9c67cf88ef2505..769d18f83d5d98 100644 --- a/deps/npm/AUTHORS +++ b/deps/npm/AUTHORS @@ -765,3 +765,5 @@ Jan Sepke <625043+jansepke@users.noreply.github.com> Augusto Moura <augusto.borgesm@gmail.com> Eric Chow <eric.zjp.chow@gmail.com> kbayrhammer <klaus.bayrhammer@redbull.com> +James Chen-Smith <jameschensmith@gmail.com> +Yash Singh <saiansh2525@gmail.com> diff --git a/deps/npm/CHANGELOG.md b/deps/npm/CHANGELOG.md index c11a44734ae8eb..8bec00e87d7b57 100644 --- a/deps/npm/CHANGELOG.md +++ b/deps/npm/CHANGELOG.md @@ -1,3 +1,80 @@ +## v7.7.4 (2021-03-24) + +### BUG FIXES + +* [`200bee74b`](https://github.com/npm/cli/commit/200bee74b31a738687446b7b535cac67b1c582fd) + [#2951](https://github.com/npm/cli/issues/2951) + fix(config): accept explicit `production=false` + ([@wraithgar](https://github.com/wraithgar)) +* [`7b45e9df6`](https://github.com/npm/cli/commit/7b45e9df6102c7bd6e403d1fdc9939581c38f546) + [#2950](https://github.com/npm/cli/issues/2950) + warn if using workspaces config options in `npm config` + ([@ruyadorno](https://github.com/ruyadorno)) + +## v7.7.3 (2021-03-24) + +### BUG FIXES + +* [`c76f04ac2`](https://github.com/npm/cli/commit/c76f04ac28ddf2ae4df4b3ce0aec684a118de1b5) + [#2925](https://github.com/npm/cli/issues/2925) + fix(set-script): add completion + ([@Yash-Singh1](https://github.com/Yash-Singh1)) +* [`0379eab69`](https://github.com/npm/cli/commit/0379eab698b78ae4aa89bbe2043607f420e52f11) + [#2929](https://github.com/npm/cli/issues/2929) + fix(install): ignore auditLevel + `npm install` should not be affected by the `auditLevel` config, as the + results of audit do not change its exit status. + ([@wraithgar](https://github.com/wraithgar)) +* [`98efadeb4`](https://github.com/npm/cli/commit/98efadeb4b2ae9289f14ed6f42a169230faf7239) + [#2923](https://github.com/npm/cli/issues/2923) + fix(audit-level): add `info` audit level + This is a valid level but wasn't configured to be allowed. + Also added this param to the usage output for `npm audit` + ([@wraithgar](https://github.com/wraithgar)) +* [`e8d2adcf4`](https://github.com/npm/cli/commit/e8d2adcf40ad63030f844c9aa44c6d16e2146797) + [#2945](https://github.com/npm/cli/issues/2945) + config should not error when workspaces are configured + ([@nlf](https://github.com/nlf)) +* [`aba2bc623`](https://github.com/npm/cli/commit/aba2bc623ea99e563b1b15b81dbb4ba94f86fe4c) + [#2944](https://github.com/npm/cli/issues/2944) + fix(progress): re-add progress bar to reify + The logger was no longer in flatOptions, we pass it in explicitly now + ([@wraithgar](https://github.com/wraithgar)) +* [`877b4ed29`](https://github.com/npm/cli/commit/877b4ed2925c97b5249a4d33575420dda64f7339) + [#2946](https://github.com/npm/cli/issues/2946) + fix(flatOptions): re-add `_auth` + This was not being added to flatOptions, and things like + `npm-registry-fetch` are looking for it. + ([@wraithgar](https://github.com/wraithgar)) + +## v7.7.2 (2021-03-24) + +### BUG FIXES +* [`a4df2b98d`](https://github.com/npm/cli/commit/a4df2b98d89429b19cd29b5fc895cdbfc0a6bd78) + [#2942](https://github.com/npm/cli/issues/2942) + Restore --dev flag, unify --omit flatteners + ([@isaacs](https://github.com/isaacs)) + +### DEPENDENCIES +* [`2cbfaac0e`](https://github.com/npm/cli/commit/2cbfaac0ecd5810316f6d76168ed9618bd11bf3a) + `hosted-git-info@4.0.2` + * [#83](https://github.com/npm/hosted-git-info/pull/83) Do not parse + urls for gitlab + ([@nlf](https://github.com/nlf)) + +## v7.7.1 (2021-03-24) + +### BUG FIXES + +* [`543b0e39b`](https://github.com/npm/cli/commit/543b0e39bcb94fc408804b01ca9c0d7b960b2681) + [#2930](https://github.com/npm/cli/issues/2930) + fix(uninstall): use correct local prefix + ([@jameschensmith](https://github.com/jameschensmith)) +* [`dce4960ef`](https://github.com/npm/cli/commit/dce4960ef6d52af128affe7755b2ca72de913b6c) + [#2932](https://github.com/npm/cli/issues/2932) + fix(config): flatten savePrefix properly + ([@wraithgar](https://github.com/wraithgar)) + ## v7.7.0 (2021-03-23) ### FEATURES diff --git a/deps/npm/docs/content/commands/npm-find-dupes.md b/deps/npm/docs/content/commands/npm-find-dupes.md index e098cf47f49915..6f55d47bfd7f32 100644 --- a/deps/npm/docs/content/commands/npm-find-dupes.md +++ b/deps/npm/docs/content/commands/npm-find-dupes.md @@ -21,3 +21,4 @@ duplications, without actually changing the package tree. * [npm ls](/cli-commands/ls) * [npm update](/cli-commands/update) * [npm install](/cli-commands/install) + diff --git a/deps/npm/docs/content/using-npm/config.md b/deps/npm/docs/content/using-npm/config.md index da69ad46325305..c701a361afbb94 100644 --- a/deps/npm/docs/content/using-npm/config.md +++ b/deps/npm/docs/content/using-npm/config.md @@ -185,7 +185,7 @@ registry and all registries configured for scopes. See the documentation for #### `audit-level` * Default: null -* Type: "low", "moderate", "high", "critical", "none", or null +* Type: "info", "low", "moderate", "high", "critical", "none", or null The minimum level of vulnerability for `npm audit` to exit with a non-zero exit code. @@ -1382,6 +1382,14 @@ What authentication strategy to use with `adduser`/`login`. `--cache-min=9999 (or bigger)` is an alias for `--prefer-offline`. +#### `dev` + +* Default: false +* Type: Boolean +* DEPRECATED: Please use --include=dev instead. + +Alias for `--include=dev`. + #### `init.author.email` * Default: "" @@ -1451,8 +1459,8 @@ Alias for --include=optional or --omit=optional #### `production` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean * DEPRECATED: Use `--omit=dev` instead. Alias for `--omit=dev` diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index 454c5567442657..287149aba986dd 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -159,7 +159,7 @@ <h3 id="description">Description</h3> the results to only the paths to the packages named. Note that nested packages will <em>also</em> show the paths to the specified packages. For example, running <code>npm ls promzard</code> in npm’s source tree will show:</p> -<pre lang="bash"><code>npm@7.7.0 /path/to/npm +<pre lang="bash"><code>npm@7.7.4 /path/to/npm └─┬ init-package-json@0.0.4 └── promzard@0.1.5 </code></pre> diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index f55659d9ee82a1..90e4019f1bc5d6 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -148,7 +148,7 @@ <h2 id="table-of-contents">Table of contents</h2> <pre lang="bash"><code>npm <command> [args] </code></pre> <h3 id="version">Version</h3> -<p>7.7.0</p> +<p>7.7.4</p> <h3 id="description">Description</h3> <p>npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html index dc50eb72b71ca4..6fe8f828157683 100644 --- a/deps/npm/docs/output/using-npm/config.html +++ b/deps/npm/docs/output/using-npm/config.html @@ -141,7 +141,7 @@ <h1 id="config">config</h1> <section id="table_of_contents"> <h2 id="table-of-contents">Table of contents</h2> -<div id="_table_of_contents"><ul><li><a href="#description">Description</a></li><ul><li><a href="#command-line-flags">Command Line Flags</a></li><li><a href="#environment-variables">Environment Variables</a></li><li><a href="#npmrc-files">npmrc Files</a></li><li><a href="#default-configs">Default Configs</a></li></ul><li><a href="#shorthands-and-other-cli-niceties">Shorthands and Other CLI Niceties</a></li><li><a href="#config-settings">Config Settings</a></li><ul><li><a href="#auth"><code>_auth</code></a></li><li><a href="#access"><code>access</code></a></li><li><a href="#all"><code>all</code></a></li><li><a href="#allow-same-version"><code>allow-same-version</code></a></li><li><a href="#always-auth"><code>always-auth</code></a></li><li><a href="#audit"><code>audit</code></a></li><li><a href="#audit-level"><code>audit-level</code></a></li><li><a href="#before"><code>before</code></a></li><li><a href="#bin-links"><code>bin-links</code></a></li><li><a href="#browser"><code>browser</code></a></li><li><a href="#ca"><code>ca</code></a></li><li><a href="#cache"><code>cache</code></a></li><li><a href="#cafile"><code>cafile</code></a></li><li><a href="#call"><code>call</code></a></li><li><a href="#cert"><code>cert</code></a></li><li><a href="#ci-name"><code>ci-name</code></a></li><li><a href="#cidr"><code>cidr</code></a></li><li><a href="#color"><code>color</code></a></li><li><a href="#commit-hooks"><code>commit-hooks</code></a></li><li><a href="#depth"><code>depth</code></a></li><li><a href="#description2"><code>description</code></a></li><li><a href="#diff"><code>diff</code></a></li><li><a href="#diff-dst-prefix"><code>diff-dst-prefix</code></a></li><li><a href="#diff-ignore-all-space"><code>diff-ignore-all-space</code></a></li><li><a href="#diff-name-only"><code>diff-name-only</code></a></li><li><a href="#diff-no-prefix"><code>diff-no-prefix</code></a></li><li><a href="#diff-src-prefix"><code>diff-src-prefix</code></a></li><li><a href="#diff-text"><code>diff-text</code></a></li><li><a href="#diff-unified"><code>diff-unified</code></a></li><li><a href="#dry-run"><code>dry-run</code></a></li><li><a href="#editor"><code>editor</code></a></li><li><a href="#engine-strict"><code>engine-strict</code></a></li><li><a href="#fetch-retries"><code>fetch-retries</code></a></li><li><a href="#fetch-retry-factor"><code>fetch-retry-factor</code></a></li><li><a href="#fetch-retry-maxtimeout"><code>fetch-retry-maxtimeout</code></a></li><li><a href="#fetch-retry-mintimeout"><code>fetch-retry-mintimeout</code></a></li><li><a href="#fetch-timeout"><code>fetch-timeout</code></a></li><li><a href="#force"><code>force</code></a></li><li><a href="#foreground-scripts"><code>foreground-scripts</code></a></li><li><a href="#format-package-lock"><code>format-package-lock</code></a></li><li><a href="#fund"><code>fund</code></a></li><li><a href="#git"><code>git</code></a></li><li><a href="#git-tag-version"><code>git-tag-version</code></a></li><li><a href="#global"><code>global</code></a></li><li><a href="#global-style"><code>global-style</code></a></li><li><a href="#globalconfig"><code>globalconfig</code></a></li><li><a href="#heading"><code>heading</code></a></li><li><a href="#https-proxy"><code>https-proxy</code></a></li><li><a href="#if-present"><code>if-present</code></a></li><li><a href="#ignore-scripts"><code>ignore-scripts</code></a></li><li><a href="#include"><code>include</code></a></li><li><a href="#include-staged"><code>include-staged</code></a></li><li><a href="#init-author-email"><code>init-author-email</code></a></li><li><a href="#init-author-name"><code>init-author-name</code></a></li><li><a href="#init-author-url"><code>init-author-url</code></a></li><li><a href="#init-license"><code>init-license</code></a></li><li><a href="#init-module"><code>init-module</code></a></li><li><a href="#init-version"><code>init-version</code></a></li><li><a href="#json"><code>json</code></a></li><li><a href="#key"><code>key</code></a></li><li><a href="#legacy-bundling"><code>legacy-bundling</code></a></li><li><a href="#legacy-peer-deps"><code>legacy-peer-deps</code></a></li><li><a href="#link"><code>link</code></a></li><li><a href="#local-address"><code>local-address</code></a></li><li><a href="#loglevel"><code>loglevel</code></a></li><li><a href="#logs-max"><code>logs-max</code></a></li><li><a href="#long"><code>long</code></a></li><li><a href="#maxsockets"><code>maxsockets</code></a></li><li><a href="#message"><code>message</code></a></li><li><a href="#node-options"><code>node-options</code></a></li><li><a href="#node-version"><code>node-version</code></a></li><li><a href="#noproxy"><code>noproxy</code></a></li><li><a href="#npm-version"><code>npm-version</code></a></li><li><a href="#offline"><code>offline</code></a></li><li><a href="#omit"><code>omit</code></a></li><li><a href="#otp"><code>otp</code></a></li><li><a href="#package"><code>package</code></a></li><li><a href="#package-lock"><code>package-lock</code></a></li><li><a href="#package-lock-only"><code>package-lock-only</code></a></li><li><a href="#parseable"><code>parseable</code></a></li><li><a href="#prefer-offline"><code>prefer-offline</code></a></li><li><a href="#prefer-online"><code>prefer-online</code></a></li><li><a href="#prefix"><code>prefix</code></a></li><li><a href="#preid"><code>preid</code></a></li><li><a href="#progress"><code>progress</code></a></li><li><a href="#proxy"><code>proxy</code></a></li><li><a href="#read-only"><code>read-only</code></a></li><li><a href="#rebuild-bundle"><code>rebuild-bundle</code></a></li><li><a href="#registry"><code>registry</code></a></li><li><a href="#save"><code>save</code></a></li><li><a href="#save-bundle"><code>save-bundle</code></a></li><li><a href="#save-dev"><code>save-dev</code></a></li><li><a href="#save-exact"><code>save-exact</code></a></li><li><a href="#save-optional"><code>save-optional</code></a></li><li><a href="#save-peer"><code>save-peer</code></a></li><li><a href="#save-prefix"><code>save-prefix</code></a></li><li><a href="#save-prod"><code>save-prod</code></a></li><li><a href="#scope"><code>scope</code></a></li><li><a href="#script-shell"><code>script-shell</code></a></li><li><a href="#searchexclude"><code>searchexclude</code></a></li><li><a href="#searchlimit"><code>searchlimit</code></a></li><li><a href="#searchopts"><code>searchopts</code></a></li><li><a href="#searchstaleness"><code>searchstaleness</code></a></li><li><a href="#shell"><code>shell</code></a></li><li><a href="#sign-git-commit"><code>sign-git-commit</code></a></li><li><a href="#sign-git-tag"><code>sign-git-tag</code></a></li><li><a href="#strict-peer-deps"><code>strict-peer-deps</code></a></li><li><a href="#strict-ssl"><code>strict-ssl</code></a></li><li><a href="#tag"><code>tag</code></a></li><li><a href="#tag-version-prefix"><code>tag-version-prefix</code></a></li><li><a href="#timing"><code>timing</code></a></li><li><a href="#umask"><code>umask</code></a></li><li><a href="#unicode"><code>unicode</code></a></li><li><a href="#update-notifier"><code>update-notifier</code></a></li><li><a href="#usage"><code>usage</code></a></li><li><a href="#user-agent"><code>user-agent</code></a></li><li><a href="#userconfig"><code>userconfig</code></a></li><li><a href="#version"><code>version</code></a></li><li><a href="#versions"><code>versions</code></a></li><li><a href="#viewer"><code>viewer</code></a></li><li><a href="#which"><code>which</code></a></li><li><a href="#workspace"><code>workspace</code></a></li><li><a href="#workspaces"><code>workspaces</code></a></li><li><a href="#yes"><code>yes</code></a></li><li><a href="#also"><code>also</code></a></li><li><a href="#auth-type"><code>auth-type</code></a></li><li><a href="#cache-max"><code>cache-max</code></a></li><li><a href="#cache-min"><code>cache-min</code></a></li><li><a href="#initauthoremail"><code>init.author.email</code></a></li><li><a href="#initauthorname"><code>init.author.name</code></a></li><li><a href="#initauthorurl"><code>init.author.url</code></a></li><li><a href="#initlicense"><code>init.license</code></a></li><li><a href="#initmodule"><code>init.module</code></a></li><li><a href="#initversion"><code>init.version</code></a></li><li><a href="#only"><code>only</code></a></li><li><a href="#optional"><code>optional</code></a></li><li><a href="#production"><code>production</code></a></li><li><a href="#shrinkwrap"><code>shrinkwrap</code></a></li><li><a href="#sso-poll-frequency"><code>sso-poll-frequency</code></a></li><li><a href="#sso-type"><code>sso-type</code></a></li><li><a href="#tmp"><code>tmp</code></a></li></ul><li><a href="#see-also">See also</a></li></ul></div> +<div id="_table_of_contents"><ul><li><a href="#description">Description</a></li><ul><li><a href="#command-line-flags">Command Line Flags</a></li><li><a href="#environment-variables">Environment Variables</a></li><li><a href="#npmrc-files">npmrc Files</a></li><li><a href="#default-configs">Default Configs</a></li></ul><li><a href="#shorthands-and-other-cli-niceties">Shorthands and Other CLI Niceties</a></li><li><a href="#config-settings">Config Settings</a></li><ul><li><a href="#auth"><code>_auth</code></a></li><li><a href="#access"><code>access</code></a></li><li><a href="#all"><code>all</code></a></li><li><a href="#allow-same-version"><code>allow-same-version</code></a></li><li><a href="#always-auth"><code>always-auth</code></a></li><li><a href="#audit"><code>audit</code></a></li><li><a href="#audit-level"><code>audit-level</code></a></li><li><a href="#before"><code>before</code></a></li><li><a href="#bin-links"><code>bin-links</code></a></li><li><a href="#browser"><code>browser</code></a></li><li><a href="#ca"><code>ca</code></a></li><li><a href="#cache"><code>cache</code></a></li><li><a href="#cafile"><code>cafile</code></a></li><li><a href="#call"><code>call</code></a></li><li><a href="#cert"><code>cert</code></a></li><li><a href="#ci-name"><code>ci-name</code></a></li><li><a href="#cidr"><code>cidr</code></a></li><li><a href="#color"><code>color</code></a></li><li><a href="#commit-hooks"><code>commit-hooks</code></a></li><li><a href="#depth"><code>depth</code></a></li><li><a href="#description2"><code>description</code></a></li><li><a href="#diff"><code>diff</code></a></li><li><a href="#diff-dst-prefix"><code>diff-dst-prefix</code></a></li><li><a href="#diff-ignore-all-space"><code>diff-ignore-all-space</code></a></li><li><a href="#diff-name-only"><code>diff-name-only</code></a></li><li><a href="#diff-no-prefix"><code>diff-no-prefix</code></a></li><li><a href="#diff-src-prefix"><code>diff-src-prefix</code></a></li><li><a href="#diff-text"><code>diff-text</code></a></li><li><a href="#diff-unified"><code>diff-unified</code></a></li><li><a href="#dry-run"><code>dry-run</code></a></li><li><a href="#editor"><code>editor</code></a></li><li><a href="#engine-strict"><code>engine-strict</code></a></li><li><a href="#fetch-retries"><code>fetch-retries</code></a></li><li><a href="#fetch-retry-factor"><code>fetch-retry-factor</code></a></li><li><a href="#fetch-retry-maxtimeout"><code>fetch-retry-maxtimeout</code></a></li><li><a href="#fetch-retry-mintimeout"><code>fetch-retry-mintimeout</code></a></li><li><a href="#fetch-timeout"><code>fetch-timeout</code></a></li><li><a href="#force"><code>force</code></a></li><li><a href="#foreground-scripts"><code>foreground-scripts</code></a></li><li><a href="#format-package-lock"><code>format-package-lock</code></a></li><li><a href="#fund"><code>fund</code></a></li><li><a href="#git"><code>git</code></a></li><li><a href="#git-tag-version"><code>git-tag-version</code></a></li><li><a href="#global"><code>global</code></a></li><li><a href="#global-style"><code>global-style</code></a></li><li><a href="#globalconfig"><code>globalconfig</code></a></li><li><a href="#heading"><code>heading</code></a></li><li><a href="#https-proxy"><code>https-proxy</code></a></li><li><a href="#if-present"><code>if-present</code></a></li><li><a href="#ignore-scripts"><code>ignore-scripts</code></a></li><li><a href="#include"><code>include</code></a></li><li><a href="#include-staged"><code>include-staged</code></a></li><li><a href="#init-author-email"><code>init-author-email</code></a></li><li><a href="#init-author-name"><code>init-author-name</code></a></li><li><a href="#init-author-url"><code>init-author-url</code></a></li><li><a href="#init-license"><code>init-license</code></a></li><li><a href="#init-module"><code>init-module</code></a></li><li><a href="#init-version"><code>init-version</code></a></li><li><a href="#json"><code>json</code></a></li><li><a href="#key"><code>key</code></a></li><li><a href="#legacy-bundling"><code>legacy-bundling</code></a></li><li><a href="#legacy-peer-deps"><code>legacy-peer-deps</code></a></li><li><a href="#link"><code>link</code></a></li><li><a href="#local-address"><code>local-address</code></a></li><li><a href="#loglevel"><code>loglevel</code></a></li><li><a href="#logs-max"><code>logs-max</code></a></li><li><a href="#long"><code>long</code></a></li><li><a href="#maxsockets"><code>maxsockets</code></a></li><li><a href="#message"><code>message</code></a></li><li><a href="#node-options"><code>node-options</code></a></li><li><a href="#node-version"><code>node-version</code></a></li><li><a href="#noproxy"><code>noproxy</code></a></li><li><a href="#npm-version"><code>npm-version</code></a></li><li><a href="#offline"><code>offline</code></a></li><li><a href="#omit"><code>omit</code></a></li><li><a href="#otp"><code>otp</code></a></li><li><a href="#package"><code>package</code></a></li><li><a href="#package-lock"><code>package-lock</code></a></li><li><a href="#package-lock-only"><code>package-lock-only</code></a></li><li><a href="#parseable"><code>parseable</code></a></li><li><a href="#prefer-offline"><code>prefer-offline</code></a></li><li><a href="#prefer-online"><code>prefer-online</code></a></li><li><a href="#prefix"><code>prefix</code></a></li><li><a href="#preid"><code>preid</code></a></li><li><a href="#progress"><code>progress</code></a></li><li><a href="#proxy"><code>proxy</code></a></li><li><a href="#read-only"><code>read-only</code></a></li><li><a href="#rebuild-bundle"><code>rebuild-bundle</code></a></li><li><a href="#registry"><code>registry</code></a></li><li><a href="#save"><code>save</code></a></li><li><a href="#save-bundle"><code>save-bundle</code></a></li><li><a href="#save-dev"><code>save-dev</code></a></li><li><a href="#save-exact"><code>save-exact</code></a></li><li><a href="#save-optional"><code>save-optional</code></a></li><li><a href="#save-peer"><code>save-peer</code></a></li><li><a href="#save-prefix"><code>save-prefix</code></a></li><li><a href="#save-prod"><code>save-prod</code></a></li><li><a href="#scope"><code>scope</code></a></li><li><a href="#script-shell"><code>script-shell</code></a></li><li><a href="#searchexclude"><code>searchexclude</code></a></li><li><a href="#searchlimit"><code>searchlimit</code></a></li><li><a href="#searchopts"><code>searchopts</code></a></li><li><a href="#searchstaleness"><code>searchstaleness</code></a></li><li><a href="#shell"><code>shell</code></a></li><li><a href="#sign-git-commit"><code>sign-git-commit</code></a></li><li><a href="#sign-git-tag"><code>sign-git-tag</code></a></li><li><a href="#strict-peer-deps"><code>strict-peer-deps</code></a></li><li><a href="#strict-ssl"><code>strict-ssl</code></a></li><li><a href="#tag"><code>tag</code></a></li><li><a href="#tag-version-prefix"><code>tag-version-prefix</code></a></li><li><a href="#timing"><code>timing</code></a></li><li><a href="#umask"><code>umask</code></a></li><li><a href="#unicode"><code>unicode</code></a></li><li><a href="#update-notifier"><code>update-notifier</code></a></li><li><a href="#usage"><code>usage</code></a></li><li><a href="#user-agent"><code>user-agent</code></a></li><li><a href="#userconfig"><code>userconfig</code></a></li><li><a href="#version"><code>version</code></a></li><li><a href="#versions"><code>versions</code></a></li><li><a href="#viewer"><code>viewer</code></a></li><li><a href="#which"><code>which</code></a></li><li><a href="#workspace"><code>workspace</code></a></li><li><a href="#workspaces"><code>workspaces</code></a></li><li><a href="#yes"><code>yes</code></a></li><li><a href="#also"><code>also</code></a></li><li><a href="#auth-type"><code>auth-type</code></a></li><li><a href="#cache-max"><code>cache-max</code></a></li><li><a href="#cache-min"><code>cache-min</code></a></li><li><a href="#dev"><code>dev</code></a></li><li><a href="#initauthoremail"><code>init.author.email</code></a></li><li><a href="#initauthorname"><code>init.author.name</code></a></li><li><a href="#initauthorurl"><code>init.author.url</code></a></li><li><a href="#initlicense"><code>init.license</code></a></li><li><a href="#initmodule"><code>init.module</code></a></li><li><a href="#initversion"><code>init.version</code></a></li><li><a href="#only"><code>only</code></a></li><li><a href="#optional"><code>optional</code></a></li><li><a href="#production"><code>production</code></a></li><li><a href="#shrinkwrap"><code>shrinkwrap</code></a></li><li><a href="#sso-poll-frequency"><code>sso-poll-frequency</code></a></li><li><a href="#sso-type"><code>sso-type</code></a></li><li><a href="#tmp"><code>tmp</code></a></li></ul><li><a href="#see-also">See also</a></li></ul></div> </section> <div id="_content"><h3 id="description">Description</h3> @@ -297,7 +297,7 @@ <h4 id="audit"><code>audit</code></h4> <h4 id="audit-level"><code>audit-level</code></h4> <ul> <li>Default: null</li> -<li>Type: “low”, “moderate”, “high”, “critical”, “none”, or null</li> +<li>Type: “info”, “low”, “moderate”, “high”, “critical”, “none”, or null</li> </ul> <p>The minimum level of vulnerability for <code>npm audit</code> to exit with a non-zero exit code.</p> @@ -1305,6 +1305,13 @@ <h4 id="cache-min"><code>cache-min</code></h4> <li>DEPRECATED: This option has been deprecated in favor of <code>--prefer-offline</code>.</li> </ul> <p><code>--cache-min=9999 (or bigger)</code> is an alias for <code>--prefer-offline</code>.</p> +<h4 id="dev"><code>dev</code></h4> +<ul> +<li>Default: false</li> +<li>Type: Boolean</li> +<li>DEPRECATED: Please use –include=dev instead.</li> +</ul> +<p>Alias for <code>--include=dev</code>.</p> <h4 id="initauthoremail"><code>init.author.email</code></h4> <ul> <li>Default: “”</li> @@ -1365,8 +1372,8 @@ <h4 id="optional"><code>optional</code></h4> <p>Alias for –include=optional or –omit=optional</p> <h4 id="production"><code>production</code></h4> <ul> -<li>Default: false</li> -<li>Type: Boolean</li> +<li>Default: null</li> +<li>Type: null or Boolean</li> <li>DEPRECATED: Use <code>--omit=dev</code> instead.</li> </ul> <p>Alias for <code>--omit=dev</code></p> diff --git a/deps/npm/lib/audit.js b/deps/npm/lib/audit.js index f990e1fa5efaa9..9df26985892783 100644 --- a/deps/npm/lib/audit.js +++ b/deps/npm/lib/audit.js @@ -18,6 +18,7 @@ class Audit extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ static get params () { return [ + 'audit-level', 'dry-run', 'force', 'json', diff --git a/deps/npm/lib/ci.js b/deps/npm/lib/ci.js index b73b3a85911148..9ae31950ef1021 100644 --- a/deps/npm/lib/ci.js +++ b/deps/npm/lib/ci.js @@ -42,8 +42,14 @@ class CI extends BaseCommand { } const where = this.npm.prefix - const arb = new Arborist({ ...this.npm.flatOptions, path: where }) + const opts = { + ...this.npm.flatOptions, + path: where, + log: this.npm.log, + save: false, // npm ci should never modify the lockfile or package.json + } + const arb = new Arborist(opts) await Promise.all([ arb.loadVirtual().catch(er => { log.verbose('loadVirtual', er.stack) @@ -55,8 +61,7 @@ class CI extends BaseCommand { }), removeNodeModules(where), ]) - // npm ci should never modify the lockfile or package.json - await arb.reify({ ...this.npm.flatOptions, save: false }) + await arb.reify(opts) const ignoreScripts = this.npm.config.get('ignore-scripts') // run the same set of scripts that `npm install` runs. diff --git a/deps/npm/lib/config.js b/deps/npm/lib/config.js index d5ef6ec50a5e60..f53d7e5ae271bc 100644 --- a/deps/npm/lib/config.js +++ b/deps/npm/lib/config.js @@ -88,6 +88,11 @@ class Config extends BaseCommand { this.config(args).then(() => cb()).catch(cb) } + execWorkspaces (args, filters, cb) { + this.npm.log.warn('config', 'This command does not support workspaces.') + this.exec(args, cb) + } + async config ([action, ...args]) { this.npm.log.disableProgress() try { diff --git a/deps/npm/lib/dedupe.js b/deps/npm/lib/dedupe.js index b80a777fcc2f75..9649025739c601 100644 --- a/deps/npm/lib/dedupe.js +++ b/deps/npm/lib/dedupe.js @@ -30,6 +30,7 @@ class Dedupe extends BaseCommand { const where = this.npm.prefix const opts = { ...this.npm.flatOptions, + log: this.npm.log, path: where, dryRun, } diff --git a/deps/npm/lib/exec.js b/deps/npm/lib/exec.js index 0a61de7c1200cd..5967ee42345920 100644 --- a/deps/npm/lib/exec.js +++ b/deps/npm/lib/exec.js @@ -175,7 +175,11 @@ class Exec extends BaseCommand { if (needInstall) { const installDir = this.cacheInstallDir(packages) await mkdirp(installDir) - const arb = new Arborist({ ...this.npm.flatOptions, path: installDir }) + const arb = new Arborist({ + ...this.npm.flatOptions, + log: this.npm.log, + path: installDir, + }) const tree = await arb.loadActual() // at this point, we have to ensure that we get the exact same @@ -212,7 +216,11 @@ class Exec extends BaseCommand { throw new Error('canceled') } } - await arb.reify({ ...this.npm.flatOptions, add }) + await arb.reify({ + ...this.npm.flatOptions, + log: this.npm.log, + add, + }) } pathArr.unshift(resolve(installDir, 'node_modules/.bin')) } diff --git a/deps/npm/lib/install.js b/deps/npm/lib/install.js index 54ea6d82510512..a023015ed823a3 100644 --- a/deps/npm/lib/install.js +++ b/deps/npm/lib/install.js @@ -126,15 +126,16 @@ class Install extends BaseCommand { if (this.npm.config.get('dev')) log.warn('install', 'Usage of the `--dev` option is deprecated. Use `--include=dev` instead.') - const arb = new Arborist({ + const opts = { ...this.npm.flatOptions, + log: this.npm.log, + auditLevel: null, path: where, - }) - - await arb.reify({ - ...this.npm.flatOptions, add: args, - }) + } + const arb = new Arborist(opts) + await arb.reify(opts) + if (!args.length && !isGlobalInstall && !ignoreScripts) { const scriptShell = this.npm.config.get('script-shell') || undefined const scripts = [ diff --git a/deps/npm/lib/link.js b/deps/npm/lib/link.js index fe9cfd3a6b254a..3e9ec1807fca1a 100644 --- a/deps/npm/lib/link.js +++ b/deps/npm/lib/link.js @@ -66,6 +66,7 @@ class Link extends BaseCommand { const globalOpts = { ...this.npm.flatOptions, path: globalTop, + log: this.npm.log, global: true, prune: false, } @@ -113,12 +114,14 @@ class Link extends BaseCommand { // reify all the pending names as symlinks there const localArb = new Arborist({ ...this.npm.flatOptions, + log: this.npm.log, path: this.npm.prefix, save, }) await localArb.reify({ ...this.npm.flatOptions, path: this.npm.prefix, + log: this.npm.log, add: names.map(l => `file:${resolve(globalTop, 'node_modules', l)}`), save, }) @@ -131,9 +134,13 @@ class Link extends BaseCommand { const arb = new Arborist({ ...this.npm.flatOptions, path: globalTop, + log: this.npm.log, global: true, }) - await arb.reify({ add: [`file:${this.npm.prefix}`] }) + await arb.reify({ + add: [`file:${this.npm.prefix}`], + log: this.npm.log, + }) await reifyFinish(this.npm, arb) } diff --git a/deps/npm/lib/prune.js b/deps/npm/lib/prune.js index 1da86a3e821878..5c4a549d4d7ada 100644 --- a/deps/npm/lib/prune.js +++ b/deps/npm/lib/prune.js @@ -30,11 +30,13 @@ class Prune extends BaseCommand { async prune () { const where = this.npm.prefix - const arb = new Arborist({ + const opts = { ...this.npm.flatOptions, path: where, - }) - await arb.prune(this.npm.flatOptions) + log: this.npm.log, + } + const arb = new Arborist(opts) + await arb.prune(opts) await reifyFinish(this.npm, arb) } } diff --git a/deps/npm/lib/set-script.js b/deps/npm/lib/set-script.js index df101a0acb7090..9d4aadad558fbf 100644 --- a/deps/npm/lib/set-script.js +++ b/deps/npm/lib/set-script.js @@ -2,6 +2,7 @@ const log = require('npmlog') const fs = require('fs') const parseJSON = require('json-parse-even-better-errors') const rpj = require('read-package-json-fast') +const { resolve } = require('path') const BaseCommand = require('./base-command.js') class SetScript extends BaseCommand { @@ -20,6 +21,16 @@ class SetScript extends BaseCommand { return ['[<script>] [<command>]'] } + async completion (opts) { + const argv = opts.conf.argv.remain + if (argv.length === 2) { + // find the script name + const json = resolve(this.npm.localPrefix, 'package.json') + const { scripts = {} } = await rpj(json).catch(er => ({})) + return Object.keys(scripts) + } + } + exec (args, cb) { this.set(args).then(() => cb()).catch(cb) } diff --git a/deps/npm/lib/uninstall.js b/deps/npm/lib/uninstall.js index 11e65533a8e98d..79a4420d89f39a 100644 --- a/deps/npm/lib/uninstall.js +++ b/deps/npm/lib/uninstall.js @@ -38,8 +38,9 @@ class Uninstall extends BaseCommand { async uninstall (args) { // the /path/to/node_modules/.. const global = this.npm.config.get('global') - const prefix = this.npm.config.get('prefix') - const path = global ? resolve(this.npm.globalDir, '..') : prefix + const path = global + ? resolve(this.npm.globalDir, '..') + : this.npm.localPrefix if (!args.length) { if (!global) @@ -60,12 +61,15 @@ class Uninstall extends BaseCommand { } } - const arb = new Arborist({ ...this.npm.flatOptions, path }) - - await arb.reify({ + const opts = { ...this.npm.flatOptions, + path, + log: this.npm.log, rm: args, - }) + + } + const arb = new Arborist(opts) + await arb.reify(opts) await reifyFinish(this.npm, arb) } } diff --git a/deps/npm/lib/update.js b/deps/npm/lib/update.js index 6a87dd9ecddcfb..f8cb12d267d8ac 100644 --- a/deps/npm/lib/update.js +++ b/deps/npm/lib/update.js @@ -51,6 +51,7 @@ class Update extends BaseCommand { const arb = new Arborist({ ...this.npm.flatOptions, + log: this.npm.log, path: where, }) diff --git a/deps/npm/lib/utils/config/definitions.js b/deps/npm/lib/utils/config/definitions.js index a6ecbcd0c40a59..67a830448e311d 100644 --- a/deps/npm/lib/utils/config/definitions.js +++ b/deps/npm/lib/utils/config/definitions.js @@ -22,6 +22,36 @@ const maybeReadFile = file => { } } +const buildOmitList = obj => { + const include = obj.include || [] + const omit = obj.omit || [] + + const only = obj.only + if (/^prod(uction)?$/.test(only) || obj.production) + omit.push('dev') + else if (obj.production === false) + include.push('dev') + + if (/^dev/.test(obj.also)) + include.push('dev') + + if (obj.dev) + include.push('dev') + + if (obj.optional === false) + omit.push('optional') + else if (obj.optional === true) + include.push('optional') + + obj.omit = [...new Set(omit)].filter(type => !include.includes(type)) + obj.include = [...new Set(include)] + + if (obj.omit.includes('dev')) + process.env.NODE_ENV = 'production' + + return obj.omit +} + const editor = process.env.EDITOR || process.env.VISUAL || (isWindows ? 'notepad.exe' : 'vi') @@ -115,6 +145,7 @@ define('_auth', { is safer to use a registry-provided authentication bearer token stored in the ~/.npmrc file by running \`npm login\`. `, + flatten, }) define('access', { @@ -164,12 +195,6 @@ define('also', { `, deprecated: 'Please use --include=dev instead.', flatten (key, obj, flatOptions) { - if (!/^dev(elopment)?$/.test(obj.also)) - return - - // add to include, and call the omit flattener - obj.include = obj.include || [] - obj.include.push('dev') definitions.omit.flatten('omit', obj, flatOptions) }, }) @@ -198,7 +223,7 @@ define('audit', { define('audit-level', { default: null, - type: ['low', 'moderate', 'high', 'critical', 'none', null], + type: ['info', 'low', 'moderate', 'high', 'critical', 'none', null], description: ` The minimum level of vulnerability for \`npm audit\` to exit with a non-zero exit code. @@ -477,6 +502,18 @@ define('description', { }, }) +define('dev', { + default: false, + type: Boolean, + description: ` + Alias for \`--include=dev\`. + `, + deprecated: 'Please use --include=dev instead.', + flatten (key, obj, flatOptions) { + definitions.omit.flatten('omit', obj, flatOptions) + }, +}) + define('diff', { default: [], type: [String, Array], @@ -1218,10 +1255,7 @@ define('omit', { scripts. `, flatten (key, obj, flatOptions) { - const include = obj.include || [] - const omit = flatOptions.omit || [] - flatOptions.omit = omit.concat(obj[key]) - .filter(type => type && !include.includes(type)) + flatOptions.omit = buildOmitList(obj) }, }) @@ -1236,12 +1270,6 @@ define('only', { \`--omit=dev\`. `, flatten (key, obj, flatOptions) { - const value = obj[key] - if (!/^prod(uction)?$/.test(value)) - return - - obj.omit = obj.omit || [] - obj.omit.push('dev') definitions.omit.flatten('omit', obj, flatOptions) }, }) @@ -1259,16 +1287,6 @@ define('optional', { Alias for --include=optional or --omit=optional `, flatten (key, obj, flatOptions) { - const value = obj[key] - if (value === null) - return - else if (value === true) { - obj.include = obj.include || [] - obj.include.push('optional') - } else { - obj.omit = obj.omit || [] - obj.omit.push('optional') - } definitions.omit.flatten('omit', obj, flatOptions) }, }) @@ -1380,17 +1398,11 @@ define('preid', { }) define('production', { - default: false, - type: Boolean, + default: null, + type: [null, Boolean], deprecated: 'Use `--omit=dev` instead.', description: 'Alias for `--omit=dev`', flatten (key, obj, flatOptions) { - const value = obj[key] - if (!value) - return - - obj.omit = obj.omit || [] - obj.omit.push('dev') definitions.omit.flatten('omit', obj, flatOptions) }, }) @@ -1581,7 +1593,9 @@ define('save-prefix', { \`npm config set save-prefix='~'\` it would be set to \`~1.2.3\` which only allows patch upgrades. `, - flatten, + flatten (key, obj, flatOptions) { + flatOptions.savePrefix = obj['save-exact'] ? '' : obj['save-prefix'] + }, }) define('save-prod', { diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index 84bd11f2d86ac8..182f113638960d 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@7\.7\.0 /path/to/npm +npm@7\.7\.4 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index 9b3b58ce87676d..f756c4c7324082 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -10,7 +10,7 @@ npm <command> [args] .RE .SS Version .P -7\.7\.0 +7\.7\.4 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index a5f3095bcbd9e2..bd581c88809487 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -245,7 +245,7 @@ npm help \fBaudit\fP for details on what is submitted\. .IP \(bu 2 Default: null .IP \(bu 2 -Type: "low", "moderate", "high", "critical", "none", or null +Type: "info", "low", "moderate", "high", "critical", "none", or null .RE .P @@ -1847,6 +1847,18 @@ DEPRECATED: This option has been deprecated in favor of \fB\-\-prefer\-offline\f .RE .P \fB\-\-cache\-min=9999 (or bigger)\fP is an alias for \fB\-\-prefer\-offline\fP\|\. +.SS \fBdev\fP +.RS 0 +.IP \(bu 2 +Default: false +.IP \(bu 2 +Type: Boolean +.IP \(bu 2 +DEPRECATED: Please use \-\-include=dev instead\. + +.RE +.P +Alias for \fB\-\-include=dev\fP\|\. .SS \fBinit\.author\.email\fP .RS 0 .IP \(bu 2 @@ -1949,9 +1961,9 @@ Alias for \-\-include=optional or \-\-omit=optional .SS \fBproduction\fP .RS 0 .IP \(bu 2 -Default: false +Default: null .IP \(bu 2 -Type: Boolean +Type: null or Boolean .IP \(bu 2 DEPRECATED: Use \fB\-\-omit=dev\fP instead\. diff --git a/deps/npm/node_modules/@npmcli/disparity-colors/CHANGELOG.md b/deps/npm/node_modules/@npmcli/disparity-colors/CHANGELOG.md index c6dc19711a96dc..216d1db905db8c 100644 --- a/deps/npm/node_modules/@npmcli/disparity-colors/CHANGELOG.md +++ b/deps/npm/node_modules/@npmcli/disparity-colors/CHANGELOG.md @@ -3,3 +3,4 @@ ## 1.0.0 - Initial release + diff --git a/deps/npm/node_modules/@npmcli/disparity-colors/README.md b/deps/npm/node_modules/@npmcli/disparity-colors/README.md index 31d2b9c10d3ec6..a89be36d87f2eb 100644 --- a/deps/npm/node_modules/@npmcli/disparity-colors/README.md +++ b/deps/npm/node_modules/@npmcli/disparity-colors/README.md @@ -27,7 +27,7 @@ mapWorkspaces(`--- a/src/index.js // [35m@@ -1,4 +1,5 @@[39m // "use strict"; // [32m+"use foo";[39m -// +// // const os = require("os"); ``` @@ -46,3 +46,4 @@ A **String** including the appropriate [ANSI escape codes](https://en.wikipedia. ## LICENSE [ISC](./LICENSE) + diff --git a/deps/npm/node_modules/cli-table3/node_modules/string-width/index.js b/deps/npm/node_modules/cli-table3/node_modules/string-width/index.js index a348067f032dfc..f4d261a96a099e 100644 --- a/deps/npm/node_modules/cli-table3/node_modules/string-width/index.js +++ b/deps/npm/node_modules/cli-table3/node_modules/string-width/index.js @@ -4,14 +4,18 @@ const isFullwidthCodePoint = require('is-fullwidth-code-point'); const emojiRegex = require('emoji-regex'); const stringWidth = string => { - string = string.replace(emojiRegex(), ' '); - if (typeof string !== 'string' || string.length === 0) { return 0; } string = stripAnsi(string); + if (string.length === 0) { + return 0; + } + + string = string.replace(emojiRegex(), ' '); + let width = 0; for (let i = 0; i < string.length; i++) { diff --git a/deps/npm/node_modules/cli-table3/node_modules/string-width/package.json b/deps/npm/node_modules/cli-table3/node_modules/string-width/package.json index 5751de5a6464ea..b9b20caaf6f1cd 100644 --- a/deps/npm/node_modules/cli-table3/node_modules/string-width/package.json +++ b/deps/npm/node_modules/cli-table3/node_modules/string-width/package.json @@ -1,6 +1,6 @@ { "name": "string-width", - "version": "4.2.0", + "version": "4.2.2", "description": "Get the visual width of a string - the number of columns required to display it", "license": "MIT", "repository": "sindresorhus/string-width", diff --git a/deps/npm/node_modules/cli-table3/node_modules/string-width/readme.md b/deps/npm/node_modules/cli-table3/node_modules/string-width/readme.md index 705f206001b777..bdd314129ca747 100644 --- a/deps/npm/node_modules/cli-table3/node_modules/string-width/readme.md +++ b/deps/npm/node_modules/cli-table3/node_modules/string-width/readme.md @@ -1,4 +1,4 @@ -# string-width [](https://travis-ci.org/sindresorhus/string-width) +# string-width > Get the visual width of a string - the number of columns required to display it diff --git a/deps/npm/node_modules/diff/README.md b/deps/npm/node_modules/diff/README.md index d7fb897311d74f..be7b4ec8a5b241 100644 --- a/deps/npm/node_modules/diff/README.md +++ b/deps/npm/node_modules/diff/README.md @@ -168,7 +168,7 @@ Basic example in a web page const one = 'beep boop', other = 'beep boob blah', color = ''; - + let span = null; const diff = Diff.diffChars(one, other), diff --git a/deps/npm/node_modules/env-paths/index.d.ts b/deps/npm/node_modules/env-paths/index.d.ts index e57fa8f661f1ff..277ddc0a183c97 100644 --- a/deps/npm/node_modules/env-paths/index.d.ts +++ b/deps/npm/node_modules/env-paths/index.d.ts @@ -13,26 +13,56 @@ declare namespace envPaths { export interface Paths { /** Directory for data files. + + Example locations (with the default `nodejs` suffix): + + - macOS: `~/Library/Application Support/MyApp-nodejs` + - Windows: `%LOCALAPPDATA%\MyApp-nodejs\Data` (for example, `C:\Users\USERNAME\AppData\Local\MyApp-nodejs\Data`) + - Linux: `~/.local/share/MyApp-nodejs` (or `$XDG_DATA_HOME/MyApp-nodejs`) */ readonly data: string; /** Directory for data files. + + Example locations (with the default `nodejs` suffix): + + - macOS: `~/Library/Preferences/MyApp-nodejs` + - Windows: `%APPDATA%\MyApp-nodejs\Config` (for example, `C:\Users\USERNAME\AppData\Roaming\MyApp-nodejs\Config`) + - Linux: `~/.config/MyApp-nodejs` (or `$XDG_CONFIG_HOME/MyApp-nodejs`) */ readonly config: string; /** Directory for non-essential data files. + + Example locations (with the default `nodejs` suffix): + + - macOS: `~/Library/Caches/MyApp-nodejs` + - Windows: `%LOCALAPPDATA%\MyApp-nodejs\Cache` (for example, `C:\Users\USERNAME\AppData\Local\MyApp-nodejs\Cache`) + - Linux: `~/.cache/MyApp-nodejs` (or `$XDG_CACHE_HOME/MyApp-nodejs`) */ readonly cache: string; /** Directory for log files. + + Example locations (with the default `nodejs` suffix): + + - macOS: `~/Library/Logs/MyApp-nodejs` + - Windows: `%LOCALAPPDATA%\MyApp-nodejs\Log` (for example, `C:\Users\USERNAME\AppData\Local\MyApp-nodejs\Log`) + - Linux: `~/.local/state/MyApp-nodejs` (or `$XDG_STATE_HOME/MyApp-nodejs`) */ readonly log: string; /** Directory for temporary files. + + Example locations (with the default `nodejs` suffix): + + - macOS: `/var/folders/jf/f2twvvvs5jl_m49tf034ffpw0000gn/T/MyApp-nodejs` + - Windows: `%LOCALAPPDATA%\Temp\MyApp-nodejs` (for example, `C:\Users\USERNAME\AppData\Local\Temp\MyApp-nodejs`) + - Linux: `/tmp/USERNAME/MyApp-nodejs` */ readonly temp: string; } @@ -42,6 +72,8 @@ declare const envPaths: { /** Get paths for storing things like data, config, cache, etc. + Note: It only generates the path strings. It doesn't create the directories for you. You could use [`make-dir`](https://github.com/sindresorhus/make-dir) to create the directories. + @param name - Name of your project. Used to generate the paths. @returns The paths to use for your project on current OS. diff --git a/deps/npm/node_modules/env-paths/package.json b/deps/npm/node_modules/env-paths/package.json index ea4e1d53edeb0a..fae4ebcf20c675 100644 --- a/deps/npm/node_modules/env-paths/package.json +++ b/deps/npm/node_modules/env-paths/package.json @@ -1,6 +1,6 @@ { "name": "env-paths", - "version": "2.2.0", + "version": "2.2.1", "description": "Get paths for storing things like data, config, cache, etc", "license": "MIT", "repository": "sindresorhus/env-paths", diff --git a/deps/npm/node_modules/env-paths/readme.md b/deps/npm/node_modules/env-paths/readme.md index ec3439316f9a1b..b66d571af48df8 100644 --- a/deps/npm/node_modules/env-paths/readme.md +++ b/deps/npm/node_modules/env-paths/readme.md @@ -1,4 +1,4 @@ -# env-paths [](https://travis-ci.org/sindresorhus/env-paths) +# env-paths > Get paths for storing things like data, config, cache, etc @@ -29,7 +29,9 @@ paths.config ## API -### paths = envPaths(name, [options]) +### paths = envPaths(name, options?) + +Note: It only generates the path strings. It doesn't create the directories for you. You could use [`make-dir`](https://github.com/sindresorhus/make-dir) to create the directories. #### name @@ -39,7 +41,7 @@ Name of your project. Used to generate the paths. #### options -Type: `Object` +Type: `object` ##### suffix @@ -54,23 +56,60 @@ apps. Pass an empty string to disable it. Directory for data files. +Example locations (with the default `nodejs` [suffix](#suffix)): + +- macOS: `~/Library/Application Support/MyApp-nodejs` +- Windows: `%LOCALAPPDATA%\MyApp-nodejs\Data` (for example, `C:\Users\USERNAME\AppData\Local\MyApp-nodejs\Data`) +- Linux: `~/.local/share/MyApp-nodejs` (or `$XDG_DATA_HOME/MyApp-nodejs`) + ### paths.config Directory for config files. +Example locations (with the default `nodejs` [suffix](#suffix)): + +- macOS: `~/Library/Preferences/MyApp-nodejs` +- Windows: `%APPDATA%\MyApp-nodejs\Config` (for example, `C:\Users\USERNAME\AppData\Roaming\MyApp-nodejs\Config`) +- Linux: `~/.config/MyApp-nodejs` (or `$XDG_CONFIG_HOME/MyApp-nodejs`) + ### paths.cache Directory for non-essential data files. +Example locations (with the default `nodejs` [suffix](#suffix)): + +- macOS: `~/Library/Caches/MyApp-nodejs` +- Windows: `%LOCALAPPDATA%\MyApp-nodejs\Cache` (for example, `C:\Users\USERNAME\AppData\Local\MyApp-nodejs\Cache`) +- Linux: `~/.cache/MyApp-nodejs` (or `$XDG_CACHE_HOME/MyApp-nodejs`) + ### paths.log Directory for log files. +Example locations (with the default `nodejs` [suffix](#suffix)): + +- macOS: `~/Library/Logs/MyApp-nodejs` +- Windows: `%LOCALAPPDATA%\MyApp-nodejs\Log` (for example, `C:\Users\USERNAME\AppData\Local\MyApp-nodejs\Log`) +- Linux: `~/.local/state/MyApp-nodejs` (or `$XDG_STATE_HOME/MyApp-nodejs`) + ### paths.temp Directory for temporary files. +Example locations (with the default `nodejs` [suffix](#suffix)): + +- macOS: `/var/folders/jf/f2twvvvs5jl_m49tf034ffpw0000gn/T/MyApp-nodejs` +- Windows: `%LOCALAPPDATA%\Temp\MyApp-nodejs` (for example, `C:\Users\USERNAME\AppData\Local\Temp\MyApp-nodejs`) +- Linux: `/tmp/USERNAME/MyApp-nodejs` -## License +--- -MIT © [Sindre Sorhus](https://sindresorhus.com) +<div align="center"> + <b> + <a href="https://tidelift.com/subscription/pkg/npm-env-paths?utm_source=npm-env-paths&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a> + </b> + <br> + <sub> + Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies. + </sub> +</div> diff --git a/deps/npm/node_modules/hosted-git-info/git-host-info.js b/deps/npm/node_modules/hosted-git-info/git-host-info.js index 360d7b096be617..d4919344c77bf1 100644 --- a/deps/npm/node_modules/hosted-git-info/git-host-info.js +++ b/deps/npm/node_modules/hosted-git-info/git-host-info.js @@ -79,7 +79,7 @@ gitHosts.gitlab = Object.assign({}, defaults, { tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`, extract: (url) => { const path = url.pathname.slice(1) - if (path.includes('/-/')) { + if (path.includes('/-/') || path.includes('/archive.tar.gz')) { return } diff --git a/deps/npm/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/hosted-git-info/package.json index 930e3b693b9801..b7e2ee28e5b117 100644 --- a/deps/npm/node_modules/hosted-git-info/package.json +++ b/deps/npm/node_modules/hosted-git-info/package.json @@ -1,6 +1,6 @@ { "name": "hosted-git-info", - "version": "4.0.1", + "version": "4.0.2", "description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab", "main": "index.js", "repository": { diff --git a/deps/npm/node_modules/libnpmdiff/CHANGELOG.md b/deps/npm/node_modules/libnpmdiff/CHANGELOG.md index a4c3396e117d8a..b93b15b7b1113b 100644 --- a/deps/npm/node_modules/libnpmdiff/CHANGELOG.md +++ b/deps/npm/node_modules/libnpmdiff/CHANGELOG.md @@ -27,3 +27,4 @@ ## 1.0.0 - Initial release + diff --git a/deps/npm/node_modules/libnpmdiff/README.md b/deps/npm/node_modules/libnpmdiff/README.md index da6bf538421967..bc260ad15ce121 100644 --- a/deps/npm/node_modules/libnpmdiff/README.md +++ b/deps/npm/node_modules/libnpmdiff/README.md @@ -34,8 +34,8 @@ Returns: ```patch diff --git a/package.json b/package.json index v1.1.0..v1.1.1 100644 ---- a/package.json -+++ b/package.json +--- a/package.json ++++ b/package.json @@ -1,6 +1,6 @@ { "name": "abbrev", @@ -95,3 +95,4 @@ Throws an error if either `a` or `b` are missing or if trying to diff more than ## LICENSE [ISC](./LICENSE) + diff --git a/deps/npm/node_modules/mime-db/HISTORY.md b/deps/npm/node_modules/mime-db/HISTORY.md index 56d792f73d2341..aff74740dc55e5 100644 --- a/deps/npm/node_modules/mime-db/HISTORY.md +++ b/deps/npm/node_modules/mime-db/HISTORY.md @@ -1,3 +1,11 @@ +1.46.0 / 2021-02-13 +=================== + + * Add extension `.amr` to `audio/amr` + * Add extension `.m4s` to `video/iso.segment` + * Add extension `.opus` to `audio/ogg` + * Add new upstream MIME types + 1.45.0 / 2020-09-22 =================== diff --git a/deps/npm/node_modules/mime-db/README.md b/deps/npm/node_modules/mime-db/README.md index f1e639139a84a5..8f1d8c4e53ffd7 100644 --- a/deps/npm/node_modules/mime-db/README.md +++ b/deps/npm/node_modules/mime-db/README.md @@ -3,7 +3,7 @@ [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Node.js Version][node-image]][node-url] -[![Build Status][travis-image]][travis-url] +[![Build Status][ci-image]][ci-url] [![Coverage Status][coveralls-image]][coveralls-url] This is a database of all mime types. @@ -91,6 +91,8 @@ definitively lists the media type. If an extension is going to be listed as associateed with this media type, the source must definitively link the media type and extension as well. +[ci-image]: https://badgen.net/github/checks/jshttp/mime-db/master?label=ci +[ci-url]: https://github.com/jshttp/mime-db/actions?query=workflow%3Aci [coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/mime-db/master [coveralls-url]: https://coveralls.io/r/jshttp/mime-db?branch=master [node-image]: https://badgen.net/npm/node/mime-db @@ -98,5 +100,3 @@ media type and extension as well. [npm-downloads-image]: https://badgen.net/npm/dm/mime-db [npm-url]: https://npmjs.org/package/mime-db [npm-version-image]: https://badgen.net/npm/v/mime-db -[travis-image]: https://badgen.net/travis/jshttp/mime-db/master -[travis-url]: https://travis-ci.org/jshttp/mime-db diff --git a/deps/npm/node_modules/mime-db/db.json b/deps/npm/node_modules/mime-db/db.json index 05cfa68735f0ac..4871607a58a980 100644 --- a/deps/npm/node_modules/mime-db/db.json +++ b/deps/npm/node_modules/mime-db/db.json @@ -236,6 +236,9 @@ "application/cfw": { "source": "iana" }, + "application/clr": { + "source": "iana" + }, "application/clue+xml": { "source": "iana", "compressible": true @@ -402,6 +405,15 @@ "application/efi": { "source": "iana" }, + "application/elm+json": { + "source": "iana", + "charset": "UTF-8", + "compressible": true + }, + "application/elm+xml": { + "source": "iana", + "compressible": true + }, "application/emergencycalldata.cap+xml": { "source": "iana", "charset": "UTF-8", @@ -663,6 +675,10 @@ "source": "iana", "compressible": true }, + "application/jscalendar+json": { + "source": "iana", + "compressible": true + }, "application/json": { "source": "iana", "charset": "UTF-8", @@ -1171,6 +1187,10 @@ "source": "iana", "extensions": ["cww"] }, + "application/prs.cyn": { + "source": "iana", + "charset": "7-BIT" + }, "application/prs.hpub+zip": { "source": "iana", "compressible": false @@ -1690,6 +1710,9 @@ "source": "iana", "compressible": true }, + "application/vnd.3gpp.interworking-data": { + "source": "iana" + }, "application/vnd.3gpp.mc-signalling-ear": { "source": "iana" }, @@ -1905,6 +1928,9 @@ "application/vnd.afpc.afplinedata-pagedef": { "source": "iana" }, + "application/vnd.afpc.cmoca-cmresource": { + "source": "iana" + }, "application/vnd.afpc.foca-charset": { "source": "iana" }, @@ -1917,6 +1943,9 @@ "application/vnd.afpc.modca": { "source": "iana" }, + "application/vnd.afpc.modca-cmtable": { + "source": "iana" + }, "application/vnd.afpc.modca-formdef": { "source": "iana" }, @@ -2334,6 +2363,14 @@ "application/vnd.cybank": { "source": "iana" }, + "application/vnd.cyclonedx+json": { + "source": "iana", + "compressible": true + }, + "application/vnd.cyclonedx+xml": { + "source": "iana", + "compressible": true + }, "application/vnd.d2l.coursepackage1p0+zip": { "source": "iana", "compressible": false @@ -2857,6 +2894,9 @@ "source": "iana", "extensions": ["ggb"] }, + "application/vnd.geogebra.slides": { + "source": "iana" + }, "application/vnd.geogebra.tool": { "source": "iana", "extensions": ["ggt"] @@ -4812,6 +4852,10 @@ "source": "iana", "extensions": ["see"] }, + "application/vnd.seis+json": { + "source": "iana", + "compressible": true + }, "application/vnd.sema": { "source": "iana", "extensions": ["sema"] @@ -5232,6 +5276,9 @@ "source": "iana", "extensions": ["wtb"] }, + "application/vnd.wfa.dpp": { + "source": "iana" + }, "application/vnd.wfa.p2p": { "source": "iana" }, @@ -6166,7 +6213,8 @@ "extensions": ["adp"] }, "audio/amr": { - "source": "iana" + "source": "iana", + "extensions": ["amr"] }, "audio/amr-wb": { "source": "iana" @@ -6415,7 +6463,7 @@ "audio/ogg": { "source": "iana", "compressible": false, - "extensions": ["oga","ogg","spx"] + "extensions": ["oga","ogg","spx","opus"] }, "audio/opus": { "source": "iana" @@ -6463,6 +6511,9 @@ "source": "apache", "extensions": ["s3m"] }, + "audio/scip": { + "source": "iana" + }, "audio/silk": { "source": "apache", "extensions": ["sil"] @@ -6806,6 +6857,7 @@ "source": "iana" }, "image/avif": { + "source": "iana", "compressible": false, "extensions": ["avif"] }, @@ -7463,6 +7515,15 @@ "text/coffeescript": { "extensions": ["coffee","litcoffee"] }, + "text/cql": { + "source": "iana" + }, + "text/cql-expression": { + "source": "iana" + }, + "text/cql-identifier": { + "source": "iana" + }, "text/css": { "source": "iana", "charset": "UTF-8", @@ -7492,6 +7553,9 @@ "text/enriched": { "source": "iana" }, + "text/fhirpath": { + "source": "iana" + }, "text/flexfec": { "source": "iana" }, @@ -7898,6 +7962,9 @@ "source": "iana", "extensions": ["3g2"] }, + "video/av1": { + "source": "iana" + }, "video/bmpeg": { "source": "iana" }, @@ -7944,7 +8011,8 @@ "source": "iana" }, "video/iso.segment": { - "source": "iana" + "source": "iana", + "extensions": ["m4s"] }, "video/jpeg": { "source": "iana", @@ -8024,6 +8092,9 @@ "video/rtx": { "source": "iana" }, + "video/scip": { + "source": "iana" + }, "video/smpte291": { "source": "iana" }, diff --git a/deps/npm/node_modules/mime-db/package.json b/deps/npm/node_modules/mime-db/package.json index 243b45f7978cc0..ec0328404d9490 100644 --- a/deps/npm/node_modules/mime-db/package.json +++ b/deps/npm/node_modules/mime-db/package.json @@ -1,7 +1,7 @@ { "name": "mime-db", "description": "Media Type Database", - "version": "1.45.0", + "version": "1.46.0", "contributors": [ "Douglas Christopher Wilson <doug@somethingdoug.com>", "Jonathan Ong <me@jongleberry.com> (http://jongleberry.com)", @@ -22,16 +22,16 @@ "bluebird": "3.7.2", "co": "4.6.0", "cogent": "1.0.1", - "csv-parse": "4.12.0", - "eslint": "7.9.0", - "eslint-config-standard": "14.1.1", - "eslint-plugin-import": "2.22.0", + "csv-parse": "4.15.1", + "eslint": "7.20.0", + "eslint-config-standard": "15.0.1", + "eslint-plugin-import": "2.22.1", "eslint-plugin-markdown": "1.0.2", "eslint-plugin-node": "11.1.0", - "eslint-plugin-promise": "4.2.1", - "eslint-plugin-standard": "4.0.1", + "eslint-plugin-promise": "4.3.1", + "eslint-plugin-standard": "4.1.0", "gnode": "0.1.2", - "mocha": "8.1.3", + "mocha": "8.3.0", "nyc": "15.1.0", "raw-body": "2.4.1", "stream-to-array": "2.3.0" @@ -51,8 +51,8 @@ "fetch": "node scripts/fetch-apache && gnode scripts/fetch-iana && node scripts/fetch-nginx", "lint": "eslint --plugin markdown --ext js,md .", "test": "mocha --reporter spec --bail --check-leaks test/", + "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test", - "test-travis": "nyc --reporter=text npm test", "update": "npm run fetch && npm run build", "version": "node scripts/version-history.js && git add HISTORY.md" } diff --git a/deps/npm/node_modules/mime-types/HISTORY.md b/deps/npm/node_modules/mime-types/HISTORY.md index af65fdb103c6d2..2e50fc84a4d883 100644 --- a/deps/npm/node_modules/mime-types/HISTORY.md +++ b/deps/npm/node_modules/mime-types/HISTORY.md @@ -1,3 +1,12 @@ +2.1.29 / 2021-02-17 +=================== + + * deps: mime-db@1.46.0 + - Add extension `.amr` to `audio/amr` + - Add extension `.m4s` to `video/iso.segment` + - Add extension `.opus` to `audio/ogg` + - Add new upstream MIME types + 2.1.28 / 2021-01-01 =================== diff --git a/deps/npm/node_modules/mime-types/README.md b/deps/npm/node_modules/mime-types/README.md index e8bf8ebcef4b22..c978ac27a8b9fb 100644 --- a/deps/npm/node_modules/mime-types/README.md +++ b/deps/npm/node_modules/mime-types/README.md @@ -36,8 +36,6 @@ so open a PR there if you'd like to add mime types. ## API -<!-- eslint-disable no-unused-vars --> - ```js var mime = require('mime-types') ``` @@ -48,8 +46,6 @@ All functions return `false` if input is invalid or not found. Lookup the content-type associated with a file. -<!-- eslint-disable no-undef --> - ```js mime.lookup('json') // 'application/json' mime.lookup('.md') // 'text/markdown' @@ -68,8 +64,6 @@ content-type, otherwise the given content-type is used. Then if the content-type does not already have a `charset` parameter, `mime.charset` is used to get the default charset and add to the returned content-type. -<!-- eslint-disable no-undef --> - ```js mime.contentType('markdown') // 'text/x-markdown; charset=utf-8' mime.contentType('file.json') // 'application/json; charset=utf-8' @@ -84,8 +78,6 @@ mime.contentType(path.extname('/path/to/file.json')) // 'application/json; chars Get the default extension for a content-type. -<!-- eslint-disable no-undef --> - ```js mime.extension('application/octet-stream') // 'bin' ``` @@ -94,8 +86,6 @@ mime.extension('application/octet-stream') // 'bin' Lookup the implied default charset of a content-type. -<!-- eslint-disable no-undef --> - ```js mime.charset('text/markdown') // 'UTF-8' ``` diff --git a/deps/npm/node_modules/mime-types/package.json b/deps/npm/node_modules/mime-types/package.json index 26fa6f694ac5bc..9ed59e843e7261 100644 --- a/deps/npm/node_modules/mime-types/package.json +++ b/deps/npm/node_modules/mime-types/package.json @@ -1,7 +1,7 @@ { "name": "mime-types", "description": "The ultimate javascript content-type utility.", - "version": "2.1.28", + "version": "2.1.29", "contributors": [ "Douglas Christopher Wilson <doug@somethingdoug.com>", "Jeremiah Senkpiel <fishrock123@rocketmail.com> (https://searchbeam.jit.su)", @@ -14,17 +14,17 @@ ], "repository": "jshttp/mime-types", "dependencies": { - "mime-db": "1.45.0" + "mime-db": "1.46.0" }, "devDependencies": { - "eslint": "7.17.0", + "eslint": "7.20.0", "eslint-config-standard": "14.1.1", "eslint-plugin-import": "2.22.1", - "eslint-plugin-markdown": "1.0.2", + "eslint-plugin-markdown": "2.0.0", "eslint-plugin-node": "11.1.0", - "eslint-plugin-promise": "4.2.1", + "eslint-plugin-promise": "4.3.1", "eslint-plugin-standard": "4.1.0", - "mocha": "8.2.1", + "mocha": "8.3.0", "nyc": "15.1.0" }, "files": [ @@ -36,7 +36,7 @@ "node": ">= 0.6" }, "scripts": { - "lint": "eslint --plugin markdown --ext js,md .", + "lint": "eslint .", "test": "mocha --reporter spec test/test.js", "test-ci": "nyc --reporter=lcov --reporter=text npm test", "test-cov": "nyc --reporter=html --reporter=text npm test" diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md deleted file mode 100644 index 3ffcacacc575c0..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/CHANGELOG.md +++ /dev/null @@ -1,185 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. - -<a name="3.0.8"></a> -## [3.0.8](https://github.com/npm/hosted-git-info/compare/v3.0.7...v3.0.8) (2021-01-28) - - -### Bug Fixes - -* simplify the regular expression for shortcut matching ([bede0dc](https://github.com/npm/hosted-git-info/commit/bede0dc)), closes [#76](https://github.com/npm/hosted-git-info/issues/76) - - - -<a name="3.0.7"></a> -## [3.0.7](https://github.com/npm/hosted-git-info/compare/v3.0.6...v3.0.7) (2020-10-15) - - -### Bug Fixes - -* correctly filter out urls for tarballs in gitlab ([eb5bd5a](https://github.com/npm/hosted-git-info/commit/eb5bd5a)), closes [#69](https://github.com/npm/hosted-git-info/issues/69) - - - -<a name="3.0.6"></a> -## [3.0.6](https://github.com/npm/hosted-git-info/compare/v3.0.5...v3.0.6) (2020-10-12) - - -### Bug Fixes - -* support to github gist legacy hash length ([c067102](https://github.com/npm/hosted-git-info/commit/c067102)), closes [#68](https://github.com/npm/hosted-git-info/issues/68) - - - -<a name="3.0.5"></a> -## [3.0.5](https://github.com/npm/hosted-git-info/compare/v3.0.4...v3.0.5) (2020-07-11) - - - -<a name="3.0.4"></a> -## [3.0.4](https://github.com/npm/hosted-git-info/compare/v3.0.3...v3.0.4) (2020-02-26) - - -### Bug Fixes - -* Do not pass scp-style URLs to the WhatWG url.URL ([0835306](https://github.com/npm/hosted-git-info/commit/0835306)), closes [#60](https://github.com/npm/hosted-git-info/issues/60) [#63](https://github.com/npm/hosted-git-info/issues/63) - - - -<a name="3.0.3"></a> -## [3.0.3](https://github.com/npm/hosted-git-info/compare/v3.0.2...v3.0.3) (2020-02-25) - - - -<a name="3.0.2"></a> -## [3.0.2](https://github.com/npm/hosted-git-info/compare/v3.0.1...v3.0.2) (2019-10-08) - - -### Bug Fixes - -* do not encodeURIComponent the domain ([3e5fbec](https://github.com/npm/hosted-git-info/commit/3e5fbec)), closes [#53](https://github.com/npm/hosted-git-info/issues/53) - - - -<a name="3.0.1"></a> -## [3.0.1](https://github.com/npm/hosted-git-info/compare/v3.0.0...v3.0.1) (2019-10-07) - - -### Bug Fixes - -* update pathmatch for gitlab ([e3e3054](https://github.com/npm/hosted-git-info/commit/e3e3054)), closes [#52](https://github.com/npm/hosted-git-info/issues/52) -* updated pathmatch for gitlab ([fa87af7](https://github.com/npm/hosted-git-info/commit/fa87af7)) - - - -<a name="3.0.0"></a> -# [3.0.0](https://github.com/npm/hosted-git-info/compare/v2.8.3...v3.0.0) (2019-08-12) - - -### Bug Fixes - -* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([37c2891](https://github.com/npm/hosted-git-info/commit/37c2891)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) - - -### BREAKING CHANGES - -* **cache:** Drop support for node 0.x - - - -<a name="2.8.3"></a> -## [2.8.3](https://github.com/npm/hosted-git-info/compare/v2.8.2...v2.8.3) (2019-08-12) - - - -<a name="2.8.2"></a> -## [2.8.2](https://github.com/npm/hosted-git-info/compare/v2.8.1...v2.8.2) (2019-08-05) - - -### Bug Fixes - -* http protocol use sshurl by default ([3b1d629](https://github.com/npm/hosted-git-info/commit/3b1d629)), closes [#48](https://github.com/npm/hosted-git-info/issues/48) - - - -<a name="2.8.1"></a> -## [2.8.1](https://github.com/npm/hosted-git-info/compare/v2.8.0...v2.8.1) (2019-08-05) - - -### Bug Fixes - -* ignore noCommittish on tarball url generation ([5d4a8d7](https://github.com/npm/hosted-git-info/commit/5d4a8d7)) -* use gist tarball url that works for anonymous gists ([1692435](https://github.com/npm/hosted-git-info/commit/1692435)) - - - -<a name="2.8.0"></a> -# [2.8.0](https://github.com/npm/hosted-git-info/compare/v2.7.1...v2.8.0) (2019-08-05) - - -### Bug Fixes - -* Allow slashes in gitlab project section ([bbcf7b2](https://github.com/npm/hosted-git-info/commit/bbcf7b2)), closes [#46](https://github.com/npm/hosted-git-info/issues/46) [#43](https://github.com/npm/hosted-git-info/issues/43) -* **git-host:** disallow URI-encoded slash (%2F) in `path` ([3776fa5](https://github.com/npm/hosted-git-info/commit/3776fa5)), closes [#44](https://github.com/npm/hosted-git-info/issues/44) -* **gitlab:** Do not URL encode slashes in project name for GitLab https URL ([cbf04f9](https://github.com/npm/hosted-git-info/commit/cbf04f9)), closes [#47](https://github.com/npm/hosted-git-info/issues/47) -* do not allow invalid gist urls ([d5cf830](https://github.com/npm/hosted-git-info/commit/d5cf830)) -* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([e518222](https://github.com/npm/hosted-git-info/commit/e518222)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) - - -### Features - -* give these objects a name ([60abaea](https://github.com/npm/hosted-git-info/commit/60abaea)) - - - -<a name="2.7.1"></a> -## [2.7.1](https://github.com/npm/hosted-git-info/compare/v2.7.0...v2.7.1) (2018-07-07) - - -### Bug Fixes - -* **index:** Guard against non-string types ([5bc580d](https://github.com/npm/hosted-git-info/commit/5bc580d)) -* **parse:** Crash on strings that parse to having no host ([c931482](https://github.com/npm/hosted-git-info/commit/c931482)), closes [#35](https://github.com/npm/hosted-git-info/issues/35) - - - -<a name="2.7.0"></a> -# [2.7.0](https://github.com/npm/hosted-git-info/compare/v2.6.1...v2.7.0) (2018-07-06) - - -### Bug Fixes - -* **github tarball:** update github tarballtemplate ([6efd582](https://github.com/npm/hosted-git-info/commit/6efd582)), closes [#34](https://github.com/npm/hosted-git-info/issues/34) -* **gitlab docs:** switched to lowercase anchors for readmes ([701bcd1](https://github.com/npm/hosted-git-info/commit/701bcd1)) - - -### Features - -* **all:** Support www. prefixes on hostnames ([3349575](https://github.com/npm/hosted-git-info/commit/3349575)), closes [#32](https://github.com/npm/hosted-git-info/issues/32) - - - -<a name="2.6.1"></a> -## [2.6.1](https://github.com/npm/hosted-git-info/compare/v2.6.0...v2.6.1) (2018-06-25) - -### Bug Fixes - -* **Revert:** "compat: remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25))" ([cce5a62](https://github.com/npm/hosted-git-info/commit/cce5a62)) -* **Revert:** "git-host: fix forgotten extend()" ([a815ec9](https://github.com/npm/hosted-git-info/commit/a815ec9)) - - - -<a name="2.6.0"></a> -# [2.6.0](https://github.com/npm/hosted-git-info/compare/v2.5.0...v2.6.0) (2018-03-07) - - -### Bug Fixes - -* **compat:** remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25)) ([627ab55](https://github.com/npm/hosted-git-info/commit/627ab55)) -* **git-host:** fix forgotten extend() ([eba1f7b](https://github.com/npm/hosted-git-info/commit/eba1f7b)) - - -### Features - -* **browse:** fragment support for browse() ([#28](https://github.com/npm/hosted-git-info/issues/28)) ([cd5e5bb](https://github.com/npm/hosted-git-info/commit/cd5e5bb)) diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE deleted file mode 100644 index 45055763dc838d..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2015, Rebecca Turner - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md deleted file mode 100644 index 87404060296269..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# hosted-git-info - -This will let you identify and transform various git hosts URLs between -protocols. It also can tell you what the URL is for the raw path for -particular file for direct access without git. - -## Example - -```javascript -var hostedGitInfo = require("hosted-git-info") -var info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts) -/* info looks like: -{ - type: "github", - domain: "github.com", - user: "npm", - project: "hosted-git-info" -} -*/ -``` - -If the URL can't be matched with a git host, `null` will be returned. We -can match git, ssh and https urls. Additionally, we can match ssh connect -strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg, -`github:npm/hosted-git-info`). GitHub specifically, is detected in the case -of a third, unprefixed, form: `npm/hosted-git-info`. - -If it does match, the returned object has properties of: - -* info.type -- The short name of the service -* info.domain -- The domain for git protocol use -* info.user -- The name of the user/org on the git host -* info.project -- The name of the project on the git host - -## Version Contract - -The major version will be bumped any time… - -* The constructor stops accepting URLs that it previously accepted. -* A method is removed. -* A method can no longer accept the number and type of arguments it previously accepted. -* A method can return a different type than it currently returns. - -Implications: - -* I do not consider the specific format of the urls returned from, say - `.https()` to be a part of the contract. The contract is that it will - return a string that can be used to fetch the repo via HTTPS. But what - that string looks like, specifically, can change. -* Dropping support for a hosted git provider would constitute a breaking - change. - -## Usage - -### var info = hostedGitInfo.fromUrl(gitSpecifier[, options]) - -* *gitSpecifer* is a URL of a git repository or a SCP-style specifier of one. -* *options* is an optional object. It can have the following properties: - * *noCommittish* — If true then committishes won't be included in generated URLs. - * *noGitPlus* — If true then `git+` won't be prefixed on URLs. - -## Methods - -All of the methods take the same options as the `fromUrl` factory. Options -provided to a method override those provided to the constructor. - -* info.file(path, opts) - -Given the path of a file relative to the repository, returns a URL for -directly fetching it from the githost. If no committish was set then -`master` will be used as the default. - -For example `hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git#v1.0.0").file("package.json")` -would return `https://raw.githubusercontent.com/npm/hosted-git-info/v1.0.0/package.json` - -* info.shortcut(opts) - -eg, `github:npm/hosted-git-info` - -* info.browse(path, fragment, opts) - -eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0`, -`https://github.com/npm/hosted-git-info/tree/v1.2.0/package.json`, -`https://github.com/npm/hosted-git-info/tree/v1.2.0/REAMDE.md#supported-hosts` - -* info.bugs(opts) - -eg, `https://github.com/npm/hosted-git-info/issues` - -* info.docs(opts) - -eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0#readme` - -* info.https(opts) - -eg, `git+https://github.com/npm/hosted-git-info.git` - -* info.sshurl(opts) - -eg, `git+ssh://git@github.com/npm/hosted-git-info.git` - -* info.ssh(opts) - -eg, `git@github.com:npm/hosted-git-info.git` - -* info.path(opts) - -eg, `npm/hosted-git-info` - -* info.tarball(opts) - -eg, `https://github.com/npm/hosted-git-info/archive/v1.2.0.tar.gz` - -* info.getDefaultRepresentation() - -Returns the default output type. The default output type is based on the -string you passed in to be parsed - -* info.toString(opts) - -Uses the getDefaultRepresentation to call one of the other methods to get a URL for -this resource. As such `hostedGitInfo.fromUrl(url).toString()` will give -you a normalized version of the URL that still uses the same protocol. - -Shortcuts will still be returned as shortcuts, but the special case github -form of `org/project` will be normalized to `github:org/project`. - -SSH connect strings will be normalized into `git+ssh` URLs. - -## Supported hosts - -Currently this supports GitHub, Bitbucket and GitLab. Pull requests for -additional hosts welcome. diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js deleted file mode 100644 index 360d7b096be617..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host-info.js +++ /dev/null @@ -1,154 +0,0 @@ -'use strict' -const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : '' -const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : '' - -const defaults = { - sshtemplate: ({ domain, user, project, committish }) => `git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`, - sshurltemplate: ({ domain, user, project, committish }) => `git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`, - browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`, - docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`, - httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/raw/${maybeEncode(committish) || 'master'}/${path}`, - shortcuttemplate: ({ type, user, project, committish }) => `${type}:${user}/${project}${maybeJoin('#', committish)}`, - pathtemplate: ({ user, project, committish }) => `${user}/${project}${maybeJoin('#', committish)}`, - bugstemplate: ({ domain, user, project }) => `https://${domain}/${user}/${project}/issues`, - hashformat: formatHashFragment -} - -const gitHosts = {} -gitHosts.github = Object.assign({}, defaults, { - // First two are insecure and generally shouldn't be used any more, but - // they are still supported. - protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'github.com', - treepath: 'tree', - filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${path}`, - gittemplate: ({ auth, domain, user, project, committish }) => `git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - tarballtemplate: ({ domain, user, project, committish }) => `https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, - extract: (url) => { - let [, user, project, type, committish] = url.pathname.split('/', 5) - if (type && type !== 'tree') { - return - } - - if (!type) { - committish = url.hash.slice(1) - } - - if (project && project.endsWith('.git')) { - project = project.slice(0, -4) - } - - if (!user || !project) { - return - } - - return { user, project, committish } - } -}) - -gitHosts.bitbucket = Object.assign({}, defaults, { - protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'bitbucket.org', - treepath: 'src', - tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.tar.gz`, - extract: (url) => { - let [, user, project, aux] = url.pathname.split('/', 4) - if (['get'].includes(aux)) { - return - } - - if (project && project.endsWith('.git')) { - project = project.slice(0, -4) - } - - if (!user || !project) { - return - } - - return { user, project, committish: url.hash.slice(1) } - } -}) - -gitHosts.gitlab = Object.assign({}, defaults, { - protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'gitlab.com', - treepath: 'tree', - httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`, - extract: (url) => { - const path = url.pathname.slice(1) - if (path.includes('/-/')) { - return - } - - const segments = path.split('/') - let project = segments.pop() - if (project.endsWith('.git')) { - project = project.slice(0, -4) - } - - const user = segments.join('/') - if (!user || !project) { - return - } - - return { user, project, committish: url.hash.slice(1) } - } -}) - -gitHosts.gist = Object.assign({}, defaults, { - protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'gist.github.com', - sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`, - sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`, - browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, - browsefiletemplate: ({ domain, project, committish, path, hashformat }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`, - docstemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, - httpstemplate: ({ domain, project, committish }) => `git+https://${domain}/${project}.git${maybeJoin('#', committish)}`, - filetemplate: ({ user, project, committish, path }) => `https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`, - shortcuttemplate: ({ type, project, committish }) => `${type}:${project}${maybeJoin('#', committish)}`, - pathtemplate: ({ project, committish }) => `${project}${maybeJoin('#', committish)}`, - bugstemplate: ({ domain, project }) => `https://${domain}/${project}`, - gittemplate: ({ domain, project, committish }) => `git://${domain}/${project}.git${maybeJoin('#', committish)}`, - tarballtemplate: ({ project, committish }) => `https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, - extract: (url) => { - let [, user, project, aux] = url.pathname.split('/', 4) - if (aux === 'raw') { - return - } - - if (!project) { - if (!user) { - return - } - - project = user - user = null - } - - if (project.endsWith('.git')) { - project = project.slice(0, -4) - } - - return { user, project, committish: url.hash.slice(1) } - }, - hashformat: function (fragment) { - return fragment && 'file-' + formatHashFragment(fragment) - } -}) - -const names = Object.keys(gitHosts) -gitHosts.byShortcut = {} -gitHosts.byDomain = {} -for (const name of names) { - gitHosts.byShortcut[`${name}:`] = name - gitHosts.byDomain[gitHosts[name].domain] = name -} - -function formatHashFragment (fragment) { - return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') -} - -module.exports = gitHosts diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js deleted file mode 100644 index 8a975e92e58bb7..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/git-host.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict' -const gitHosts = require('./git-host-info.js') - -class GitHost { - constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) { - Object.assign(this, gitHosts[type]) - this.type = type - this.user = user - this.auth = auth - this.project = project - this.committish = committish - this.default = defaultRepresentation - this.opts = opts - } - - hash () { - return this.committish ? `#${this.committish}` : '' - } - - ssh (opts) { - return this._fill(this.sshtemplate, opts) - } - - _fill (template, opts) { - if (typeof template === 'function') { - const options = { ...this, ...this.opts, ...opts } - - // the path should always be set so we don't end up with 'undefined' in urls - if (!options.path) { - options.path = '' - } - - // template functions will insert the leading slash themselves - if (options.path.startsWith('/')) { - options.path = options.path.slice(1) - } - - if (options.noCommittish) { - options.committish = null - } - - const result = template(options) - return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result - } - - return null - } - - sshurl (opts) { - return this._fill(this.sshurltemplate, opts) - } - - browse (path, fragment, opts) { - // not a string, treat path as opts - if (typeof path !== 'string') { - return this._fill(this.browsetemplate, path) - } - - if (typeof fragment !== 'string') { - opts = fragment - fragment = null - } - return this._fill(this.browsefiletemplate, { ...opts, fragment, path }) - } - - docs (opts) { - return this._fill(this.docstemplate, opts) - } - - bugs (opts) { - return this._fill(this.bugstemplate, opts) - } - - https (opts) { - return this._fill(this.httpstemplate, opts) - } - - git (opts) { - return this._fill(this.gittemplate, opts) - } - - shortcut (opts) { - return this._fill(this.shortcuttemplate, opts) - } - - path (opts) { - return this._fill(this.pathtemplate, opts) - } - - tarball (opts) { - return this._fill(this.tarballtemplate, { ...opts, noCommittish: false }) - } - - file (path, opts) { - return this._fill(this.filetemplate, { ...opts, path }) - } - - getDefaultRepresentation () { - return this.default - } - - toString (opts) { - if (this.default && typeof this[this.default] === 'function') { - return this[this.default](opts) - } - - return this.sshurl(opts) - } -} -module.exports = GitHost diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js deleted file mode 100644 index f35c570c46b595..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/index.js +++ /dev/null @@ -1,237 +0,0 @@ -'use strict' -const url = require('url') -const gitHosts = require('./git-host-info.js') -const GitHost = module.exports = require('./git-host.js') -const LRU = require('lru-cache') -const cache = new LRU({ max: 1000 }) - -const protocolToRepresentationMap = { - 'git+ssh:': 'sshurl', - 'git+https:': 'https', - 'ssh:': 'sshurl', - 'git:': 'git' -} - -function protocolToRepresentation (protocol) { - return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) -} - -const authProtocols = { - 'git:': true, - 'https:': true, - 'git+https:': true, - 'http:': true, - 'git+http:': true -} - -const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:']) - -module.exports.fromUrl = function (giturl, opts) { - if (typeof giturl !== 'string') { - return - } - - const key = giturl + JSON.stringify(opts || {}) - - if (!cache.has(key)) { - cache.set(key, fromUrl(giturl, opts)) - } - - return cache.get(key) -} - -function fromUrl (giturl, opts) { - if (!giturl) { - return - } - - const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl) - const parsed = parseGitUrl(url) - if (!parsed) { - return parsed - } - - const gitHostShortcut = gitHosts.byShortcut[parsed.protocol] - const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.') ? parsed.hostname.slice(4) : parsed.hostname] - const gitHostName = gitHostShortcut || gitHostDomain - if (!gitHostName) { - return - } - - const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain] - let auth = null - if (authProtocols[parsed.protocol] && (parsed.username || parsed.password)) { - auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}` - } - - let committish = null - let user = null - let project = null - let defaultRepresentation = null - - try { - if (gitHostShortcut) { - let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname - const firstAt = pathname.indexOf('@') - // we ignore auth for shortcuts, so just trim it out - if (firstAt > -1) { - pathname = pathname.slice(firstAt + 1) - } - - const lastSlash = pathname.lastIndexOf('/') - if (lastSlash > -1) { - user = decodeURIComponent(pathname.slice(0, lastSlash)) - // we want nulls only, never empty strings - if (!user) { - user = null - } - project = decodeURIComponent(pathname.slice(lastSlash + 1)) - } else { - project = decodeURIComponent(pathname) - } - - if (project.endsWith('.git')) { - project = project.slice(0, -4) - } - - if (parsed.hash) { - committish = decodeURIComponent(parsed.hash.slice(1)) - } - - defaultRepresentation = 'shortcut' - } else { - if (!gitHostInfo.protocols.includes(parsed.protocol)) { - return - } - - const segments = gitHostInfo.extract(parsed) - if (!segments) { - return - } - - user = segments.user && decodeURIComponent(segments.user) - project = decodeURIComponent(segments.project) - committish = decodeURIComponent(segments.committish) - defaultRepresentation = protocolToRepresentation(parsed.protocol) - } - } catch (err) { - /* istanbul ignore else */ - if (err instanceof URIError) { - return - } else { - throw err - } - } - - return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) -} - -// accepts input like git:github.com:user/repo and inserts the // after the first : -const correctProtocol = (arg) => { - const firstColon = arg.indexOf(':') - const proto = arg.slice(0, firstColon + 1) - if (knownProtocols.includes(proto)) { - return arg - } - - const firstAt = arg.indexOf('@') - if (firstAt > -1) { - if (firstAt > firstColon) { - return `git+ssh://${arg}` - } else { - return arg - } - } - - const doubleSlash = arg.indexOf('//') - if (doubleSlash === firstColon + 1) { - return arg - } - - return arg.slice(0, firstColon + 1) + '//' + arg.slice(firstColon + 1) -} - -// look for github shorthand inputs, such as npm/cli -const isGitHubShorthand = (arg) => { - // it cannot contain whitespace before the first # - // it cannot start with a / because that's probably an absolute file path - // but it must include a slash since repos are username/repository - // it cannot start with a . because that's probably a relative file path - // it cannot start with an @ because that's a scoped package if it passes the other tests - // it cannot contain a : before a # because that tells us that there's a protocol - // a second / may not exist before a # - const firstHash = arg.indexOf('#') - const firstSlash = arg.indexOf('/') - const secondSlash = arg.indexOf('/', firstSlash + 1) - const firstColon = arg.indexOf(':') - const firstSpace = /\s/.exec(arg) - const firstAt = arg.indexOf('@') - - const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash) - const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash) - const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash) - const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash) - const hasSlash = firstSlash > 0 - // if a # is found, what we really want to know is that the character immediately before # is not a / - const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/') - const doesNotStartWithDot = !arg.startsWith('.') - - return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash && doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash && secondSlashOnlyAfterHash -} - -// attempt to correct an scp style url so that it will parse with `new URL()` -const correctUrl = (giturl) => { - const firstAt = giturl.indexOf('@') - const lastHash = giturl.lastIndexOf('#') - let firstColon = giturl.indexOf(':') - let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity) - - let corrected - if (lastColon > firstAt) { - // the last : comes after the first @ (or there is no @) - // like it would in: - // proto://hostname.com:user/repo - // username@hostname.com:user/repo - // :password@hostname.com:user/repo - // username:password@hostname.com:user/repo - // proto://username@hostname.com:user/repo - // proto://:password@hostname.com:user/repo - // proto://username:password@hostname.com:user/repo - // then we replace the last : with a / to create a valid path - corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1) - // // and we find our new : positions - firstColon = corrected.indexOf(':') - lastColon = corrected.lastIndexOf(':') - } - - if (firstColon === -1 && giturl.indexOf('//') === -1) { - // we have no : at all - // as it would be in: - // username@hostname.com/user/repo - // then we prepend a protocol - corrected = `git+ssh://${corrected}` - } - - return corrected -} - -// try to parse the url as its given to us, if that throws -// then we try to clean the url and parse that result instead -// THIS FUNCTION SHOULD NEVER THROW -const parseGitUrl = (giturl) => { - let result - try { - result = new url.URL(giturl) - } catch (err) {} - - if (result) { - return result - } - - const correctedUrl = correctUrl(giturl) - try { - result = new url.URL(correctedUrl) - } catch (err) {} - - return result -} diff --git a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json deleted file mode 100644 index 930e3b693b9801..00000000000000 --- a/deps/npm/node_modules/normalize-package-data/node_modules/hosted-git-info/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "hosted-git-info", - "version": "4.0.1", - "description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab", - "main": "index.js", - "repository": { - "type": "git", - "url": "git+https://github.com/npm/hosted-git-info.git" - }, - "keywords": [ - "git", - "github", - "bitbucket", - "gitlab" - ], - "author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org)", - "license": "ISC", - "bugs": { - "url": "https://github.com/npm/hosted-git-info/issues" - }, - "homepage": "https://github.com/npm/hosted-git-info", - "scripts": { - "posttest": "standard", - "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags", - "preversion": "npm test", - "snap": "tap", - "test": "tap", - "test:coverage": "tap --coverage-report=html" - }, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "devDependencies": { - "standard": "^16.0.3", - "standard-version": "^9.1.0", - "tap": "^14.11.0" - }, - "files": [ - "index.js", - "git-host.js", - "git-host-info.js" - ], - "engines": { - "node": ">=10" - }, - "tap": { - "color": 1, - "coverage": true, - "esm": false - } -} diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md deleted file mode 100644 index 3ffcacacc575c0..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/CHANGELOG.md +++ /dev/null @@ -1,185 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. - -<a name="3.0.8"></a> -## [3.0.8](https://github.com/npm/hosted-git-info/compare/v3.0.7...v3.0.8) (2021-01-28) - - -### Bug Fixes - -* simplify the regular expression for shortcut matching ([bede0dc](https://github.com/npm/hosted-git-info/commit/bede0dc)), closes [#76](https://github.com/npm/hosted-git-info/issues/76) - - - -<a name="3.0.7"></a> -## [3.0.7](https://github.com/npm/hosted-git-info/compare/v3.0.6...v3.0.7) (2020-10-15) - - -### Bug Fixes - -* correctly filter out urls for tarballs in gitlab ([eb5bd5a](https://github.com/npm/hosted-git-info/commit/eb5bd5a)), closes [#69](https://github.com/npm/hosted-git-info/issues/69) - - - -<a name="3.0.6"></a> -## [3.0.6](https://github.com/npm/hosted-git-info/compare/v3.0.5...v3.0.6) (2020-10-12) - - -### Bug Fixes - -* support to github gist legacy hash length ([c067102](https://github.com/npm/hosted-git-info/commit/c067102)), closes [#68](https://github.com/npm/hosted-git-info/issues/68) - - - -<a name="3.0.5"></a> -## [3.0.5](https://github.com/npm/hosted-git-info/compare/v3.0.4...v3.0.5) (2020-07-11) - - - -<a name="3.0.4"></a> -## [3.0.4](https://github.com/npm/hosted-git-info/compare/v3.0.3...v3.0.4) (2020-02-26) - - -### Bug Fixes - -* Do not pass scp-style URLs to the WhatWG url.URL ([0835306](https://github.com/npm/hosted-git-info/commit/0835306)), closes [#60](https://github.com/npm/hosted-git-info/issues/60) [#63](https://github.com/npm/hosted-git-info/issues/63) - - - -<a name="3.0.3"></a> -## [3.0.3](https://github.com/npm/hosted-git-info/compare/v3.0.2...v3.0.3) (2020-02-25) - - - -<a name="3.0.2"></a> -## [3.0.2](https://github.com/npm/hosted-git-info/compare/v3.0.1...v3.0.2) (2019-10-08) - - -### Bug Fixes - -* do not encodeURIComponent the domain ([3e5fbec](https://github.com/npm/hosted-git-info/commit/3e5fbec)), closes [#53](https://github.com/npm/hosted-git-info/issues/53) - - - -<a name="3.0.1"></a> -## [3.0.1](https://github.com/npm/hosted-git-info/compare/v3.0.0...v3.0.1) (2019-10-07) - - -### Bug Fixes - -* update pathmatch for gitlab ([e3e3054](https://github.com/npm/hosted-git-info/commit/e3e3054)), closes [#52](https://github.com/npm/hosted-git-info/issues/52) -* updated pathmatch for gitlab ([fa87af7](https://github.com/npm/hosted-git-info/commit/fa87af7)) - - - -<a name="3.0.0"></a> -# [3.0.0](https://github.com/npm/hosted-git-info/compare/v2.8.3...v3.0.0) (2019-08-12) - - -### Bug Fixes - -* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([37c2891](https://github.com/npm/hosted-git-info/commit/37c2891)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) - - -### BREAKING CHANGES - -* **cache:** Drop support for node 0.x - - - -<a name="2.8.3"></a> -## [2.8.3](https://github.com/npm/hosted-git-info/compare/v2.8.2...v2.8.3) (2019-08-12) - - - -<a name="2.8.2"></a> -## [2.8.2](https://github.com/npm/hosted-git-info/compare/v2.8.1...v2.8.2) (2019-08-05) - - -### Bug Fixes - -* http protocol use sshurl by default ([3b1d629](https://github.com/npm/hosted-git-info/commit/3b1d629)), closes [#48](https://github.com/npm/hosted-git-info/issues/48) - - - -<a name="2.8.1"></a> -## [2.8.1](https://github.com/npm/hosted-git-info/compare/v2.8.0...v2.8.1) (2019-08-05) - - -### Bug Fixes - -* ignore noCommittish on tarball url generation ([5d4a8d7](https://github.com/npm/hosted-git-info/commit/5d4a8d7)) -* use gist tarball url that works for anonymous gists ([1692435](https://github.com/npm/hosted-git-info/commit/1692435)) - - - -<a name="2.8.0"></a> -# [2.8.0](https://github.com/npm/hosted-git-info/compare/v2.7.1...v2.8.0) (2019-08-05) - - -### Bug Fixes - -* Allow slashes in gitlab project section ([bbcf7b2](https://github.com/npm/hosted-git-info/commit/bbcf7b2)), closes [#46](https://github.com/npm/hosted-git-info/issues/46) [#43](https://github.com/npm/hosted-git-info/issues/43) -* **git-host:** disallow URI-encoded slash (%2F) in `path` ([3776fa5](https://github.com/npm/hosted-git-info/commit/3776fa5)), closes [#44](https://github.com/npm/hosted-git-info/issues/44) -* **gitlab:** Do not URL encode slashes in project name for GitLab https URL ([cbf04f9](https://github.com/npm/hosted-git-info/commit/cbf04f9)), closes [#47](https://github.com/npm/hosted-git-info/issues/47) -* do not allow invalid gist urls ([d5cf830](https://github.com/npm/hosted-git-info/commit/d5cf830)) -* **cache:** Switch to lru-cache to save ourselves from unlimited memory consumption ([e518222](https://github.com/npm/hosted-git-info/commit/e518222)), closes [#38](https://github.com/npm/hosted-git-info/issues/38) - - -### Features - -* give these objects a name ([60abaea](https://github.com/npm/hosted-git-info/commit/60abaea)) - - - -<a name="2.7.1"></a> -## [2.7.1](https://github.com/npm/hosted-git-info/compare/v2.7.0...v2.7.1) (2018-07-07) - - -### Bug Fixes - -* **index:** Guard against non-string types ([5bc580d](https://github.com/npm/hosted-git-info/commit/5bc580d)) -* **parse:** Crash on strings that parse to having no host ([c931482](https://github.com/npm/hosted-git-info/commit/c931482)), closes [#35](https://github.com/npm/hosted-git-info/issues/35) - - - -<a name="2.7.0"></a> -# [2.7.0](https://github.com/npm/hosted-git-info/compare/v2.6.1...v2.7.0) (2018-07-06) - - -### Bug Fixes - -* **github tarball:** update github tarballtemplate ([6efd582](https://github.com/npm/hosted-git-info/commit/6efd582)), closes [#34](https://github.com/npm/hosted-git-info/issues/34) -* **gitlab docs:** switched to lowercase anchors for readmes ([701bcd1](https://github.com/npm/hosted-git-info/commit/701bcd1)) - - -### Features - -* **all:** Support www. prefixes on hostnames ([3349575](https://github.com/npm/hosted-git-info/commit/3349575)), closes [#32](https://github.com/npm/hosted-git-info/issues/32) - - - -<a name="2.6.1"></a> -## [2.6.1](https://github.com/npm/hosted-git-info/compare/v2.6.0...v2.6.1) (2018-06-25) - -### Bug Fixes - -* **Revert:** "compat: remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25))" ([cce5a62](https://github.com/npm/hosted-git-info/commit/cce5a62)) -* **Revert:** "git-host: fix forgotten extend()" ([a815ec9](https://github.com/npm/hosted-git-info/commit/a815ec9)) - - - -<a name="2.6.0"></a> -# [2.6.0](https://github.com/npm/hosted-git-info/compare/v2.5.0...v2.6.0) (2018-03-07) - - -### Bug Fixes - -* **compat:** remove Object.assign fallback ([#25](https://github.com/npm/hosted-git-info/issues/25)) ([627ab55](https://github.com/npm/hosted-git-info/commit/627ab55)) -* **git-host:** fix forgotten extend() ([eba1f7b](https://github.com/npm/hosted-git-info/commit/eba1f7b)) - - -### Features - -* **browse:** fragment support for browse() ([#28](https://github.com/npm/hosted-git-info/issues/28)) ([cd5e5bb](https://github.com/npm/hosted-git-info/commit/cd5e5bb)) diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE deleted file mode 100644 index 45055763dc838d..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2015, Rebecca Turner - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md deleted file mode 100644 index 87404060296269..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# hosted-git-info - -This will let you identify and transform various git hosts URLs between -protocols. It also can tell you what the URL is for the raw path for -particular file for direct access without git. - -## Example - -```javascript -var hostedGitInfo = require("hosted-git-info") -var info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts) -/* info looks like: -{ - type: "github", - domain: "github.com", - user: "npm", - project: "hosted-git-info" -} -*/ -``` - -If the URL can't be matched with a git host, `null` will be returned. We -can match git, ssh and https urls. Additionally, we can match ssh connect -strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg, -`github:npm/hosted-git-info`). GitHub specifically, is detected in the case -of a third, unprefixed, form: `npm/hosted-git-info`. - -If it does match, the returned object has properties of: - -* info.type -- The short name of the service -* info.domain -- The domain for git protocol use -* info.user -- The name of the user/org on the git host -* info.project -- The name of the project on the git host - -## Version Contract - -The major version will be bumped any time… - -* The constructor stops accepting URLs that it previously accepted. -* A method is removed. -* A method can no longer accept the number and type of arguments it previously accepted. -* A method can return a different type than it currently returns. - -Implications: - -* I do not consider the specific format of the urls returned from, say - `.https()` to be a part of the contract. The contract is that it will - return a string that can be used to fetch the repo via HTTPS. But what - that string looks like, specifically, can change. -* Dropping support for a hosted git provider would constitute a breaking - change. - -## Usage - -### var info = hostedGitInfo.fromUrl(gitSpecifier[, options]) - -* *gitSpecifer* is a URL of a git repository or a SCP-style specifier of one. -* *options* is an optional object. It can have the following properties: - * *noCommittish* — If true then committishes won't be included in generated URLs. - * *noGitPlus* — If true then `git+` won't be prefixed on URLs. - -## Methods - -All of the methods take the same options as the `fromUrl` factory. Options -provided to a method override those provided to the constructor. - -* info.file(path, opts) - -Given the path of a file relative to the repository, returns a URL for -directly fetching it from the githost. If no committish was set then -`master` will be used as the default. - -For example `hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git#v1.0.0").file("package.json")` -would return `https://raw.githubusercontent.com/npm/hosted-git-info/v1.0.0/package.json` - -* info.shortcut(opts) - -eg, `github:npm/hosted-git-info` - -* info.browse(path, fragment, opts) - -eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0`, -`https://github.com/npm/hosted-git-info/tree/v1.2.0/package.json`, -`https://github.com/npm/hosted-git-info/tree/v1.2.0/REAMDE.md#supported-hosts` - -* info.bugs(opts) - -eg, `https://github.com/npm/hosted-git-info/issues` - -* info.docs(opts) - -eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0#readme` - -* info.https(opts) - -eg, `git+https://github.com/npm/hosted-git-info.git` - -* info.sshurl(opts) - -eg, `git+ssh://git@github.com/npm/hosted-git-info.git` - -* info.ssh(opts) - -eg, `git@github.com:npm/hosted-git-info.git` - -* info.path(opts) - -eg, `npm/hosted-git-info` - -* info.tarball(opts) - -eg, `https://github.com/npm/hosted-git-info/archive/v1.2.0.tar.gz` - -* info.getDefaultRepresentation() - -Returns the default output type. The default output type is based on the -string you passed in to be parsed - -* info.toString(opts) - -Uses the getDefaultRepresentation to call one of the other methods to get a URL for -this resource. As such `hostedGitInfo.fromUrl(url).toString()` will give -you a normalized version of the URL that still uses the same protocol. - -Shortcuts will still be returned as shortcuts, but the special case github -form of `org/project` will be normalized to `github:org/project`. - -SSH connect strings will be normalized into `git+ssh` URLs. - -## Supported hosts - -Currently this supports GitHub, Bitbucket and GitLab. Pull requests for -additional hosts welcome. diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js deleted file mode 100644 index 360d7b096be617..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host-info.js +++ /dev/null @@ -1,154 +0,0 @@ -'use strict' -const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : '' -const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : '' - -const defaults = { - sshtemplate: ({ domain, user, project, committish }) => `git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`, - sshurltemplate: ({ domain, user, project, committish }) => `git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - browsetemplate: ({ domain, user, project, committish, treepath }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`, - browsefiletemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) => `https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'master')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`, - docstemplate: ({ domain, user, project, treepath, committish }) => `https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`, - httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - filetemplate: ({ domain, user, project, committish, path }) => `https://${domain}/${user}/${project}/raw/${maybeEncode(committish) || 'master'}/${path}`, - shortcuttemplate: ({ type, user, project, committish }) => `${type}:${user}/${project}${maybeJoin('#', committish)}`, - pathtemplate: ({ user, project, committish }) => `${user}/${project}${maybeJoin('#', committish)}`, - bugstemplate: ({ domain, user, project }) => `https://${domain}/${user}/${project}/issues`, - hashformat: formatHashFragment -} - -const gitHosts = {} -gitHosts.github = Object.assign({}, defaults, { - // First two are insecure and generally shouldn't be used any more, but - // they are still supported. - protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'github.com', - treepath: 'tree', - filetemplate: ({ auth, user, project, committish, path }) => `https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish) || 'master'}/${path}`, - gittemplate: ({ auth, domain, user, project, committish }) => `git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - tarballtemplate: ({ domain, user, project, committish }) => `https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, - extract: (url) => { - let [, user, project, type, committish] = url.pathname.split('/', 5) - if (type && type !== 'tree') { - return - } - - if (!type) { - committish = url.hash.slice(1) - } - - if (project && project.endsWith('.git')) { - project = project.slice(0, -4) - } - - if (!user || !project) { - return - } - - return { user, project, committish } - } -}) - -gitHosts.bitbucket = Object.assign({}, defaults, { - protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'bitbucket.org', - treepath: 'src', - tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/get/${maybeEncode(committish) || 'master'}.tar.gz`, - extract: (url) => { - let [, user, project, aux] = url.pathname.split('/', 4) - if (['get'].includes(aux)) { - return - } - - if (project && project.endsWith('.git')) { - project = project.slice(0, -4) - } - - if (!user || !project) { - return - } - - return { user, project, committish: url.hash.slice(1) } - } -}) - -gitHosts.gitlab = Object.assign({}, defaults, { - protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'gitlab.com', - treepath: 'tree', - httpstemplate: ({ auth, domain, user, project, committish }) => `git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`, - tarballtemplate: ({ domain, user, project, committish }) => `https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish) || 'master'}`, - extract: (url) => { - const path = url.pathname.slice(1) - if (path.includes('/-/')) { - return - } - - const segments = path.split('/') - let project = segments.pop() - if (project.endsWith('.git')) { - project = project.slice(0, -4) - } - - const user = segments.join('/') - if (!user || !project) { - return - } - - return { user, project, committish: url.hash.slice(1) } - } -}) - -gitHosts.gist = Object.assign({}, defaults, { - protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'], - domain: 'gist.github.com', - sshtemplate: ({ domain, project, committish }) => `git@${domain}:${project}.git${maybeJoin('#', committish)}`, - sshurltemplate: ({ domain, project, committish }) => `git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`, - browsetemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, - browsefiletemplate: ({ domain, project, committish, path, hashformat }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`, - docstemplate: ({ domain, project, committish }) => `https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`, - httpstemplate: ({ domain, project, committish }) => `git+https://${domain}/${project}.git${maybeJoin('#', committish)}`, - filetemplate: ({ user, project, committish, path }) => `https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`, - shortcuttemplate: ({ type, project, committish }) => `${type}:${project}${maybeJoin('#', committish)}`, - pathtemplate: ({ project, committish }) => `${project}${maybeJoin('#', committish)}`, - bugstemplate: ({ domain, project }) => `https://${domain}/${project}`, - gittemplate: ({ domain, project, committish }) => `git://${domain}/${project}.git${maybeJoin('#', committish)}`, - tarballtemplate: ({ project, committish }) => `https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish) || 'master'}`, - extract: (url) => { - let [, user, project, aux] = url.pathname.split('/', 4) - if (aux === 'raw') { - return - } - - if (!project) { - if (!user) { - return - } - - project = user - user = null - } - - if (project.endsWith('.git')) { - project = project.slice(0, -4) - } - - return { user, project, committish: url.hash.slice(1) } - }, - hashformat: function (fragment) { - return fragment && 'file-' + formatHashFragment(fragment) - } -}) - -const names = Object.keys(gitHosts) -gitHosts.byShortcut = {} -gitHosts.byDomain = {} -for (const name of names) { - gitHosts.byShortcut[`${name}:`] = name - gitHosts.byDomain[gitHosts[name].domain] = name -} - -function formatHashFragment (fragment) { - return fragment.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-') -} - -module.exports = gitHosts diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js deleted file mode 100644 index 8a975e92e58bb7..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/git-host.js +++ /dev/null @@ -1,110 +0,0 @@ -'use strict' -const gitHosts = require('./git-host-info.js') - -class GitHost { - constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) { - Object.assign(this, gitHosts[type]) - this.type = type - this.user = user - this.auth = auth - this.project = project - this.committish = committish - this.default = defaultRepresentation - this.opts = opts - } - - hash () { - return this.committish ? `#${this.committish}` : '' - } - - ssh (opts) { - return this._fill(this.sshtemplate, opts) - } - - _fill (template, opts) { - if (typeof template === 'function') { - const options = { ...this, ...this.opts, ...opts } - - // the path should always be set so we don't end up with 'undefined' in urls - if (!options.path) { - options.path = '' - } - - // template functions will insert the leading slash themselves - if (options.path.startsWith('/')) { - options.path = options.path.slice(1) - } - - if (options.noCommittish) { - options.committish = null - } - - const result = template(options) - return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result - } - - return null - } - - sshurl (opts) { - return this._fill(this.sshurltemplate, opts) - } - - browse (path, fragment, opts) { - // not a string, treat path as opts - if (typeof path !== 'string') { - return this._fill(this.browsetemplate, path) - } - - if (typeof fragment !== 'string') { - opts = fragment - fragment = null - } - return this._fill(this.browsefiletemplate, { ...opts, fragment, path }) - } - - docs (opts) { - return this._fill(this.docstemplate, opts) - } - - bugs (opts) { - return this._fill(this.bugstemplate, opts) - } - - https (opts) { - return this._fill(this.httpstemplate, opts) - } - - git (opts) { - return this._fill(this.gittemplate, opts) - } - - shortcut (opts) { - return this._fill(this.shortcuttemplate, opts) - } - - path (opts) { - return this._fill(this.pathtemplate, opts) - } - - tarball (opts) { - return this._fill(this.tarballtemplate, { ...opts, noCommittish: false }) - } - - file (path, opts) { - return this._fill(this.filetemplate, { ...opts, path }) - } - - getDefaultRepresentation () { - return this.default - } - - toString (opts) { - if (this.default && typeof this[this.default] === 'function') { - return this[this.default](opts) - } - - return this.sshurl(opts) - } -} -module.exports = GitHost diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js deleted file mode 100644 index f35c570c46b595..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/index.js +++ /dev/null @@ -1,237 +0,0 @@ -'use strict' -const url = require('url') -const gitHosts = require('./git-host-info.js') -const GitHost = module.exports = require('./git-host.js') -const LRU = require('lru-cache') -const cache = new LRU({ max: 1000 }) - -const protocolToRepresentationMap = { - 'git+ssh:': 'sshurl', - 'git+https:': 'https', - 'ssh:': 'sshurl', - 'git:': 'git' -} - -function protocolToRepresentation (protocol) { - return protocolToRepresentationMap[protocol] || protocol.slice(0, -1) -} - -const authProtocols = { - 'git:': true, - 'https:': true, - 'git+https:': true, - 'http:': true, - 'git+http:': true -} - -const knownProtocols = Object.keys(gitHosts.byShortcut).concat(['http:', 'https:', 'git:', 'git+ssh:', 'git+https:', 'ssh:']) - -module.exports.fromUrl = function (giturl, opts) { - if (typeof giturl !== 'string') { - return - } - - const key = giturl + JSON.stringify(opts || {}) - - if (!cache.has(key)) { - cache.set(key, fromUrl(giturl, opts)) - } - - return cache.get(key) -} - -function fromUrl (giturl, opts) { - if (!giturl) { - return - } - - const url = isGitHubShorthand(giturl) ? 'github:' + giturl : correctProtocol(giturl) - const parsed = parseGitUrl(url) - if (!parsed) { - return parsed - } - - const gitHostShortcut = gitHosts.byShortcut[parsed.protocol] - const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.') ? parsed.hostname.slice(4) : parsed.hostname] - const gitHostName = gitHostShortcut || gitHostDomain - if (!gitHostName) { - return - } - - const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain] - let auth = null - if (authProtocols[parsed.protocol] && (parsed.username || parsed.password)) { - auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}` - } - - let committish = null - let user = null - let project = null - let defaultRepresentation = null - - try { - if (gitHostShortcut) { - let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname - const firstAt = pathname.indexOf('@') - // we ignore auth for shortcuts, so just trim it out - if (firstAt > -1) { - pathname = pathname.slice(firstAt + 1) - } - - const lastSlash = pathname.lastIndexOf('/') - if (lastSlash > -1) { - user = decodeURIComponent(pathname.slice(0, lastSlash)) - // we want nulls only, never empty strings - if (!user) { - user = null - } - project = decodeURIComponent(pathname.slice(lastSlash + 1)) - } else { - project = decodeURIComponent(pathname) - } - - if (project.endsWith('.git')) { - project = project.slice(0, -4) - } - - if (parsed.hash) { - committish = decodeURIComponent(parsed.hash.slice(1)) - } - - defaultRepresentation = 'shortcut' - } else { - if (!gitHostInfo.protocols.includes(parsed.protocol)) { - return - } - - const segments = gitHostInfo.extract(parsed) - if (!segments) { - return - } - - user = segments.user && decodeURIComponent(segments.user) - project = decodeURIComponent(segments.project) - committish = decodeURIComponent(segments.committish) - defaultRepresentation = protocolToRepresentation(parsed.protocol) - } - } catch (err) { - /* istanbul ignore else */ - if (err instanceof URIError) { - return - } else { - throw err - } - } - - return new GitHost(gitHostName, user, auth, project, committish, defaultRepresentation, opts) -} - -// accepts input like git:github.com:user/repo and inserts the // after the first : -const correctProtocol = (arg) => { - const firstColon = arg.indexOf(':') - const proto = arg.slice(0, firstColon + 1) - if (knownProtocols.includes(proto)) { - return arg - } - - const firstAt = arg.indexOf('@') - if (firstAt > -1) { - if (firstAt > firstColon) { - return `git+ssh://${arg}` - } else { - return arg - } - } - - const doubleSlash = arg.indexOf('//') - if (doubleSlash === firstColon + 1) { - return arg - } - - return arg.slice(0, firstColon + 1) + '//' + arg.slice(firstColon + 1) -} - -// look for github shorthand inputs, such as npm/cli -const isGitHubShorthand = (arg) => { - // it cannot contain whitespace before the first # - // it cannot start with a / because that's probably an absolute file path - // but it must include a slash since repos are username/repository - // it cannot start with a . because that's probably a relative file path - // it cannot start with an @ because that's a scoped package if it passes the other tests - // it cannot contain a : before a # because that tells us that there's a protocol - // a second / may not exist before a # - const firstHash = arg.indexOf('#') - const firstSlash = arg.indexOf('/') - const secondSlash = arg.indexOf('/', firstSlash + 1) - const firstColon = arg.indexOf(':') - const firstSpace = /\s/.exec(arg) - const firstAt = arg.indexOf('@') - - const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash) - const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash) - const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash) - const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash) - const hasSlash = firstSlash > 0 - // if a # is found, what we really want to know is that the character immediately before # is not a / - const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/') - const doesNotStartWithDot = !arg.startsWith('.') - - return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash && doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash && secondSlashOnlyAfterHash -} - -// attempt to correct an scp style url so that it will parse with `new URL()` -const correctUrl = (giturl) => { - const firstAt = giturl.indexOf('@') - const lastHash = giturl.lastIndexOf('#') - let firstColon = giturl.indexOf(':') - let lastColon = giturl.lastIndexOf(':', lastHash > -1 ? lastHash : Infinity) - - let corrected - if (lastColon > firstAt) { - // the last : comes after the first @ (or there is no @) - // like it would in: - // proto://hostname.com:user/repo - // username@hostname.com:user/repo - // :password@hostname.com:user/repo - // username:password@hostname.com:user/repo - // proto://username@hostname.com:user/repo - // proto://:password@hostname.com:user/repo - // proto://username:password@hostname.com:user/repo - // then we replace the last : with a / to create a valid path - corrected = giturl.slice(0, lastColon) + '/' + giturl.slice(lastColon + 1) - // // and we find our new : positions - firstColon = corrected.indexOf(':') - lastColon = corrected.lastIndexOf(':') - } - - if (firstColon === -1 && giturl.indexOf('//') === -1) { - // we have no : at all - // as it would be in: - // username@hostname.com/user/repo - // then we prepend a protocol - corrected = `git+ssh://${corrected}` - } - - return corrected -} - -// try to parse the url as its given to us, if that throws -// then we try to clean the url and parse that result instead -// THIS FUNCTION SHOULD NEVER THROW -const parseGitUrl = (giturl) => { - let result - try { - result = new url.URL(giturl) - } catch (err) {} - - if (result) { - return result - } - - const correctedUrl = correctUrl(giturl) - try { - result = new url.URL(correctedUrl) - } catch (err) {} - - return result -} diff --git a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json b/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json deleted file mode 100644 index 930e3b693b9801..00000000000000 --- a/deps/npm/node_modules/npm-package-arg/node_modules/hosted-git-info/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "hosted-git-info", - "version": "4.0.1", - "description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab", - "main": "index.js", - "repository": { - "type": "git", - "url": "git+https://github.com/npm/hosted-git-info.git" - }, - "keywords": [ - "git", - "github", - "bitbucket", - "gitlab" - ], - "author": "Rebecca Turner <me@re-becca.org> (http://re-becca.org)", - "license": "ISC", - "bugs": { - "url": "https://github.com/npm/hosted-git-info/issues" - }, - "homepage": "https://github.com/npm/hosted-git-info", - "scripts": { - "posttest": "standard", - "postversion": "npm publish", - "prepublishOnly": "git push origin --follow-tags", - "preversion": "npm test", - "snap": "tap", - "test": "tap", - "test:coverage": "tap --coverage-report=html" - }, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "devDependencies": { - "standard": "^16.0.3", - "standard-version": "^9.1.0", - "tap": "^14.11.0" - }, - "files": [ - "index.js", - "git-host.js", - "git-host-info.js" - ], - "engines": { - "node": ">=10" - }, - "tap": { - "color": 1, - "coverage": true, - "esm": false - } -} diff --git a/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md b/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md index 5edb4a33c1a409..c7170c5651d164 100644 --- a/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md +++ b/deps/npm/node_modules/npm-pick-manifest/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to this project will be documented in this file. +All notable changes to this project will be documented in this file. ## [6.1.1](https://github.com/npm/npm-pick-manifest/compare/v6.0.0...v6.1.0) (2020-04-07) diff --git a/deps/npm/node_modules/resolve/test/mock_sync.js b/deps/npm/node_modules/resolve/test/mock_sync.js index 3e7116f9dc7342..0e3a60d4414b66 100644 --- a/deps/npm/node_modules/resolve/test/mock_sync.js +++ b/deps/npm/node_modules/resolve/test/mock_sync.js @@ -213,3 +213,4 @@ test('readPackageSync', function (t) { ); }); }); + diff --git a/deps/npm/node_modules/retry/Readme.md b/deps/npm/node_modules/retry/Readme.md index 1c888deee9c9d4..16e28ec267d6da 100644 --- a/deps/npm/node_modules/retry/Readme.md +++ b/deps/npm/node_modules/retry/Readme.md @@ -66,7 +66,7 @@ Creates a new `RetryOperation` object. `options` is the same as `retry.timeouts( * `forever`: Whether to retry forever, defaults to `false`. * `unref`: Whether to [unref](https://nodejs.org/api/timers.html#timers_unref) the setTimeout's, defaults to `false`. -* `maxRetryTime`: The maximum time (in milliseconds) that the retried operation is allowed to run. Default is `Infinity`. +* `maxRetryTime`: The maximum time (in milliseconds) that the retried operation is allowed to run. Default is `Infinity`. ### retry.timeouts([options]) diff --git a/deps/npm/node_modules/socks/README.md b/deps/npm/node_modules/socks/README.md index f7fba45748711d..f05ac0acf0de7d 100644 --- a/deps/npm/node_modules/socks/README.md +++ b/deps/npm/node_modules/socks/README.md @@ -4,11 +4,12 @@ Fully featured SOCKS proxy client supporting SOCKSv4, SOCKSv4a, and SOCKSv5. Inc ### Features -* Supports SOCKS v4, v4a, and v5 protocols. +* Supports SOCKS v4, v4a, v5, and v5h protocols. * Supports the CONNECT, BIND, and ASSOCIATE commands. * Supports callbacks, promises, and events for proxy connection creation async flow control. * Supports proxy chaining (CONNECT only). -* Supports user/pass authentication. +* Supports user/password authentication. +* Supports custom authentication. * Built in UDP frame creation & parse functions. * Created with TypeScript, type definitions are provided. @@ -396,17 +397,19 @@ Looking for a guide to migrate from v1? Look [here](docs/migratingFromV1.md) ### SocksClient -SocksClient establishes SOCKS proxy connections to remote destination hosts. These proxy connections are fully transparent to the server and once established act as full duplex streams. SOCKS v4, v4a, and v5 are supported, as well as the connect, bind, and associate commands. +SocksClient establishes SOCKS proxy connections to remote destination hosts. These proxy connections are fully transparent to the server and once established act as full duplex streams. SOCKS v4, v4a, v5, and v5h are supported, as well as the connect, bind, and associate commands. SocksClient supports creating connections using callbacks, promises, and async/await flow control using two static factory functions createConnection and createConnectionChain. It also internally extends EventEmitter which results in allowing event handling based async flow control. **SOCKS Compatibility Table** +Note: When using 4a please specify type: 4, and when using 5h please specify type 5. + | Socks Version | TCP | UDP | IPv4 | IPv6 | Hostname | | --- | :---: | :---: | :---: | :---: | :---: | | SOCKS v4 | ✅ | ❌ | ✅ | ❌ | ❌ | | SOCKS v4a | ✅ | ❌ | ✅ | ❌ | ✅ | -| SOCKS v5 | ✅ | ✅ | ✅ | ✅ | ✅ | +| SOCKS v5 (includes 5hh) | ✅ | ✅ | ✅ | ✅ | ✅ | ### new SocksClient(options) @@ -419,11 +422,22 @@ SocksClient supports creating connections using callbacks, promises, and async/a proxy: { host: '159.203.75.200', // ipv4, ipv6, or hostname port: 1080, - type: 5 // Proxy version (4 or 5). For v4a, just use 4. + type: 5 // Proxy version (4 or 5). For v4a use 4, for v5h use 5. // Optional fields userId: 'some username', // Used for SOCKS4 userId auth, and SOCKS5 user/pass auth in conjunction with password. - password: 'some password' // Used in conjunction with userId for user/pass auth for SOCKS5 proxies. + password: 'some password', // Used in conjunction with userId for user/pass auth for SOCKS5 proxies. + custom_auth_method: 0x80, // If using a custom auth method, specify the type here. If this is set, ALL other custom_auth_*** options must be set as well. + custom_auth_request_handler: async () =>. { + // This will be called when it's time to send the custom auth handshake. You must return a Buffer containing the data to send as your authentication. + return Buffer.from([0x01,0x02,0x03]); + }, + // This is the expected size (bytes) of the custom auth response from the proxy server. + custom_auth_response_size: 2, + // This is called when the auth response is received. The received packet is passed in as a Buffer, and you must return a boolean indicating the response from the server said your custom auth was successful or failed. + custom_auth_response_handler: async (data) => { + return data[1] === 0x00; + } }, command: 'connect', // connect, bind, associate diff --git a/deps/npm/node_modules/socks/build/client/socksclient.js b/deps/npm/node_modules/socks/build/client/socksclient.js index 14ac6714973548..fba45c6cc34319 100644 --- a/deps/npm/node_modules/socks/build/client/socksclient.js +++ b/deps/npm/node_modules/socks/build/client/socksclient.js @@ -45,7 +45,7 @@ class SocksClient extends events_1.EventEmitter { catch (err) { if (typeof callback === 'function') { callback(err); - return resolve(); // Resolves pending promise (prevents memory leaks). + return resolve(err); // Resolves pending promise (prevents memory leaks). } else { return reject(err); @@ -57,7 +57,7 @@ class SocksClient extends events_1.EventEmitter { client.removeAllListeners(); if (typeof callback === 'function') { callback(null, info); - resolve(); // Resolves pending promise (prevents memory leaks). + resolve(info); // Resolves pending promise (prevents memory leaks). } else { resolve(info); @@ -68,7 +68,7 @@ class SocksClient extends events_1.EventEmitter { client.removeAllListeners(); if (typeof callback === 'function') { callback(err); - resolve(); // Resolves pending promise (prevents memory leaks). + resolve(err); // Resolves pending promise (prevents memory leaks). } else { reject(err); @@ -94,7 +94,7 @@ class SocksClient extends events_1.EventEmitter { catch (err) { if (typeof callback === 'function') { callback(err); - return resolve(); // Resolves pending promise (prevents memory leaks). + return resolve(err); // Resolves pending promise (prevents memory leaks). } else { return reject(err); @@ -121,6 +121,7 @@ class SocksClient extends events_1.EventEmitter { command: 'connect', proxy: nextProxy, destination: nextDestination, + // Initial connection ignores this as sock is undefined. Subsequent connections re-use the first proxy socket to form a chain. }); // If sock is undefined, assign it here. if (!sock) { @@ -129,7 +130,7 @@ class SocksClient extends events_1.EventEmitter { } if (typeof callback === 'function') { callback(null, { socket: sock }); - resolve(); // Resolves pending promise (prevents memory leaks). + resolve({ socket: sock }); // Resolves pending promise (prevents memory leaks). } else { resolve({ socket: sock }); @@ -138,7 +139,7 @@ class SocksClient extends events_1.EventEmitter { catch (err) { if (typeof callback === 'function') { callback(err); - resolve(); // Resolves pending promise (prevents memory leaks). + resolve(err); // Resolves pending promise (prevents memory leaks). } else { reject(err); @@ -472,17 +473,22 @@ class SocksClient extends events_1.EventEmitter { */ sendSocks5InitialHandshake() { const buff = new smart_buffer_1.SmartBuffer(); - buff.writeUInt8(0x05); + // By default we always support no auth. + const supportedAuthMethods = [constants_1.Socks5Auth.NoAuth]; // We should only tell the proxy we support user/pass auth if auth info is actually provided. // Note: As of Tor v0.3.5.7+, if user/pass auth is an option from the client, by default it will always take priority. if (this.options.proxy.userId || this.options.proxy.password) { - buff.writeUInt8(2); - buff.writeUInt8(constants_1.Socks5Auth.NoAuth); - buff.writeUInt8(constants_1.Socks5Auth.UserPass); + supportedAuthMethods.push(constants_1.Socks5Auth.UserPass); } - else { - buff.writeUInt8(1); - buff.writeUInt8(constants_1.Socks5Auth.NoAuth); + // Custom auth method? + if (this.options.proxy.custom_auth_method !== undefined) { + supportedAuthMethods.push(this.options.proxy.custom_auth_method); + } + // Build handshake packet + buff.writeUInt8(0x05); + buff.writeUInt8(supportedAuthMethods.length); + for (const authMethod of supportedAuthMethods) { + buff.writeUInt8(authMethod); } this.nextRequiredPacketBufferSize = constants_1.SOCKS_INCOMING_PACKET_SIZES.Socks5InitialHandshakeResponse; @@ -498,17 +504,24 @@ class SocksClient extends events_1.EventEmitter { if (data[0] !== 0x05) { this.closeSocket(constants_1.ERRORS.InvalidSocks5IntiailHandshakeSocksVersion); } - else if (data[1] === 0xff) { + else if (data[1] === constants_1.SOCKS5_NO_ACCEPTABLE_AUTH) { this.closeSocket(constants_1.ERRORS.InvalidSocks5InitialHandshakeNoAcceptedAuthType); } else { // If selected Socks v5 auth method is no auth, send final handshake request. if (data[1] === constants_1.Socks5Auth.NoAuth) { + this.socks5ChosenAuthType = constants_1.Socks5Auth.NoAuth; this.sendSocks5CommandRequest(); // If selected Socks v5 auth method is user/password, send auth handshake. } else if (data[1] === constants_1.Socks5Auth.UserPass) { + this.socks5ChosenAuthType = constants_1.Socks5Auth.UserPass; this.sendSocks5UserPassAuthentication(); + // If selected Socks v5 auth method is the custom_auth_method, send custom handshake. + } + else if (data[1] === this.options.proxy.custom_auth_method) { + this.socks5ChosenAuthType = this.options.proxy.custom_auth_method; + this.sendSocks5CustomAuthentication(); } else { this.closeSocket(constants_1.ERRORS.InvalidSocks5InitialHandshakeUnknownAuthType); @@ -534,19 +547,52 @@ class SocksClient extends events_1.EventEmitter { this.socket.write(buff.toBuffer()); this.setState(constants_1.SocksClientState.SentAuthentication); } + sendSocks5CustomAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + this.nextRequiredPacketBufferSize = this.options.proxy.custom_auth_response_size; + this.socket.write(yield this.options.proxy.custom_auth_request_handler()); + this.setState(constants_1.SocksClientState.SentAuthentication); + }); + } + handleSocks5CustomAuthHandshakeResponse(data) { + return __awaiter(this, void 0, void 0, function* () { + return yield this.options.proxy.custom_auth_response_handler(data); + }); + } + handleSocks5AuthenticationNoAuthHandshakeResponse(data) { + return __awaiter(this, void 0, void 0, function* () { + return data[1] === 0x00; + }); + } + handleSocks5AuthenticationUserPassHandshakeResponse(data) { + return __awaiter(this, void 0, void 0, function* () { + return data[1] === 0x00; + }); + } /** * Handles Socks v5 auth handshake response. * @param data */ handleInitialSocks5AuthenticationHandshakeResponse() { - this.setState(constants_1.SocksClientState.ReceivedAuthenticationResponse); - const data = this.receiveBuffer.get(2); - if (data[1] !== 0x00) { - this.closeSocket(constants_1.ERRORS.Socks5AuthenticationFailed); - } - else { - this.sendSocks5CommandRequest(); - } + return __awaiter(this, void 0, void 0, function* () { + this.setState(constants_1.SocksClientState.ReceivedAuthenticationResponse); + let authResult = false; + if (this.socks5ChosenAuthType === constants_1.Socks5Auth.NoAuth) { + authResult = yield this.handleSocks5AuthenticationNoAuthHandshakeResponse(this.receiveBuffer.get(2)); + } + else if (this.socks5ChosenAuthType === constants_1.Socks5Auth.UserPass) { + authResult = yield this.handleSocks5AuthenticationUserPassHandshakeResponse(this.receiveBuffer.get(2)); + } + else if (this.socks5ChosenAuthType === this.options.proxy.custom_auth_method) { + authResult = yield this.handleSocks5CustomAuthHandshakeResponse(this.receiveBuffer.get(this.options.proxy.custom_auth_response_size)); + } + if (!authResult) { + this.closeSocket(constants_1.ERRORS.Socks5AuthenticationFailed); + } + else { + this.sendSocks5CommandRequest(); + } + }); } /** * Sends Socks v5 final handshake request. diff --git a/deps/npm/node_modules/socks/build/client/socksclient.js.map b/deps/npm/node_modules/socks/build/client/socksclient.js.map index 2145dacf5f00eb..b44af183897de7 100644 --- a/deps/npm/node_modules/socks/build/client/socksclient.js.map +++ b/deps/npm/node_modules/socks/build/client/socksclient.js.map @@ -1 +1 @@ -{"version":3,"file":"socksclient.js","sourceRoot":"","sources":["../../src/client/socksclient.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mCAAoC;AACpC,2BAA2B;AAC3B,yBAAyB;AACzB,+CAAyC;AACzC,mDAiB6B;AAC7B,+CAG2B;AAC3B,2DAAsD;AACtD,yCAA8D;AAw3B5D,iGAx3BM,uBAAgB,OAw3BN;AA91BlB,MAAM,WAAY,SAAQ,qBAAY;IAepC,YAAY,OAA2B;QACrC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,qBACP,OAAO,CACX,CAAC;QAEF,8BAA8B;QAC9B,oCAA0B,CAAC,OAAO,CAAC,CAAC;QAEpC,gBAAgB;QAChB,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,gBAAgB,CACrB,OAA2B,EAC3B,QAAmB;QAEnB,OAAO,IAAI,OAAO,CAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAClE,8BAA8B;YAC9B,IAAI;gBACF,oCAA0B,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;aAClD;YAAC,OAAO,GAAG,EAAE;gBACZ,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,OAAO,EAAE,CAAC,CAAC,oDAAoD;iBACvE;qBAAM;oBACL,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;iBACpB;aACF;YAED,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAiC,EAAE,EAAE;gBAC/D,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBACrB,OAAO,EAAE,CAAC,CAAC,oDAAoD;iBAChE;qBAAM;oBACL,OAAO,CAAC,IAAI,CAAC,CAAC;iBACf;YACH,CAAC,CAAC,CAAC;YAEH,kDAAkD;YAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBAClC,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,EAAE,CAAC,CAAC,oDAAoD;iBAChE;qBAAM;oBACL,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,qBAAqB,CAC1B,OAAgC,EAChC,QAAmB;QAEnB,OAAO,IAAI,OAAO,CAA8B,CAAO,OAAO,EAAE,MAAM,EAAE,EAAE;YACxE,mCAAmC;YACnC,IAAI;gBACF,yCAA+B,CAAC,OAAO,CAAC,CAAC;aAC1C;YAAC,OAAO,GAAG,EAAE;gBACZ,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,OAAO,EAAE,CAAC,CAAC,oDAAoD;iBACvE;qBAAM;oBACL,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;iBACpB;aACF;YAED,IAAI,IAAgB,CAAC;YAErB,kBAAkB;YAClB,IAAI,OAAO,CAAC,cAAc,EAAE;gBAC1B,mBAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;aAC/B;YAED,IAAI;gBACF,kDAAkD;gBAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;oBAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAErC,0HAA0H;oBAC1H,MAAM,eAAe,GACnB,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;wBAC9B,CAAC,CAAC,OAAO,CAAC,WAAW;wBACrB,CAAC,CAAC;4BACE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;4BACtC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;yBAClC,CAAC;oBAER,4CAA4C;oBAC5C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,gBAAgB,CAAC;wBAChD,OAAO,EAAE,SAAS;wBAClB,KAAK,EAAE,SAAS;wBAChB,WAAW,EAAE,eAAe;qBAE7B,CAAC,CAAC;oBAEH,wCAAwC;oBACxC,IAAI,CAAC,IAAI,EAAE;wBACT,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;qBACtB;iBACF;gBAED,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,IAAI,EAAE,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC;oBAC/B,OAAO,EAAE,CAAC,CAAC,oDAAoD;iBAChE;qBAAM;oBACL,OAAO,CAAC,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC;iBACzB;aACF;YAAC,OAAO,GAAG,EAAE;gBACZ,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,EAAE,CAAC,CAAC,oDAAoD;iBAChE;qBAAM;oBACL,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;aACF;QACH,CAAC,CAAA,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,OAA6B;QACjD,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;QAE1C,qBAAqB;QACrB,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YACvC,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;SACxD;aAAM,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YAC9C,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;SACxD;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SAC3C;QAED,OAAO;QACP,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAE5C,OAAO;QACP,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,IAAY;QAC/B,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QAEpB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAmB,IAAI,CAAC,SAAS,EAAE,CAAC;QAClD,IAAI,UAAU,CAAC;QAEf,IAAI,QAAQ,KAAK,0BAAc,CAAC,IAAI,EAAE;YACpC,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;SAC/C;aAAM,IAAI,QAAQ,KAAK,0BAAc,CAAC,IAAI,EAAE;YAC3C,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;SAC/C;aAAM;YACL,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;SAChD;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAEvC,OAAO;YACL,WAAW;YACX,UAAU,EAAE;gBACV,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,UAAU;aACjB;YACD,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;SACxB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,QAAQ,CAAC,QAA0B;QACzC,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,KAAK,EAAE;YACzC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;SACvB;IACH,CAAC;IAED;;;OAGG;IACI,OAAO,CAAC,cAAuB;QACpC,IAAI,CAAC,cAAc,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE/C,+CAA+C;QAC/C,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,EACjC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,2BAAe,CACxC,CAAC;QAEF,8EAA8E;QAC9E,IAAI,KAAK,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU,EAAE;YACpD,KAAK,CAAC,KAAK,EAAE,CAAC;SACf;QAED,yGAAyG;QACzG,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;SAC9B;aAAM;YACL,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;SAChC;QAED,gCAAgC;QAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAE5C,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,6BAAa,EAAE,CAAC;QAEzC,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SAC7B;aAAM;YACJ,IAAI,CAAC,MAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAE7D,IACE,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS;gBAC1C,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,IAAI,EACrC;gBACC,IAAI,CAAC,MAAqB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;aACxE;SACF;QAED,6FAA6F;QAC7F,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE;YAC/C,YAAY,CAAC,GAAG,EAAE;gBAChB,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE;oBACjC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;oBAErE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;iBACtC;gBACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACvB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,+EAA+E;IACvE,gBAAgB;QACtB,uCACK,IAAI,CAAC,OAAO,CAAC,cAAc,KAC9B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAC7D,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAC7B;IACJ,CAAC;IAED;;;OAGG;IACK,oBAAoB;QAC1B,IACE,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,WAAW;YAC3C,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,yBAAyB,EACzD;YACA,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,uBAAuB,CAAC,CAAC;SAClD;IACH,CAAC;IAED;;OAEG;IACK,gBAAgB;QACtB,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,SAAS,CAAC,CAAC;QAE1C,0BAA0B;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;YACjC,IAAI,CAAC,0BAA0B,EAAE,CAAC;SACnC;aAAM;YACL,IAAI,CAAC,0BAA0B,EAAE,CAAC;SACnC;QAED,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,IAAY;QACxC;;;UAGE;QACF,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEhC,6BAA6B;QAC7B,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,WAAW;QACjB,mFAAmF;QACnF,OACE,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,WAAW;YAC3C,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,KAAK;YACrC,IAAI,CAAC,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,4BAA4B,EAC9D;YACA,gDAAgD;YAChD,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,oBAAoB,EAAE;gBACxD,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;oBACjC,4CAA4C;oBAC5C,IAAI,CAAC,kCAAkC,EAAE,CAAC;iBAC3C;qBAAM;oBACL,wDAAwD;oBACxD,IAAI,CAAC,oCAAoC,EAAE,CAAC;iBAC7C;gBACD,wDAAwD;aACzD;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,kBAAkB,EAAE;gBAC7D,IAAI,CAAC,kDAAkD,EAAE,CAAC;gBAC1D,6DAA6D;aAC9D;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,kBAAkB,EAAE;gBAC7D,IAAI,CAAC,kCAAkC,EAAE,CAAC;gBAC1C,mEAAmE;aACpE;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,yBAAyB,EAAE;gBACpE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;oBACjC,IAAI,CAAC,sCAAsC,EAAE,CAAC;iBAC/C;qBAAM;oBACL,IAAI,CAAC,sCAAsC,EAAE,CAAC;iBAC/C;aACF;iBAAM;gBACL,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,aAAa,CAAC,CAAC;gBACvC,MAAM;aACP;SACF;IACH,CAAC;IAED;;;OAGG;IACK,cAAc;QACpB,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC;IAED;;;OAGG;IACK,cAAc,CAAC,GAAU;QAC/B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACK,4BAA4B;QAClC,6FAA6F;QAC7F,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED;;;OAGG;IACK,WAAW,CAAC,GAAW;QAC7B,2FAA2F;QAC3F,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,KAAK,EAAE;YACzC,+BAA+B;YAC/B,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,KAAK,CAAC,CAAC;YAEtC,iBAAiB;YACjB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAEtB,4BAA4B;YAC5B,IAAI,CAAC,4BAA4B,EAAE,CAAC;YAEpC,sBAAsB;YACtB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,uBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;SAC7D;IACH,CAAC;IAED;;OAEG;IACK,0BAA0B;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;QAE/C,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAElD,iBAAiB;QACjB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;YAC7C,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC3B,sBAAsB;SACvB;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC3B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;SACnD;QAED,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrC,CAAC;IAED;;;OAGG;IACK,kCAAkC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YACtC,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,6BAA6B,OACrC,0BAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,GAAG,CACJ,CAAC;SACH;aAAM;YACL,gBAAgB;YAChB,IAAI,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,IAAI,EAAE;gBAC5D,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;gBAEpB,MAAM,UAAU,GAAoB;oBAClC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;oBACzB,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;iBACvC,CAAC;gBAEF,yCAAyC;gBACzC,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE;oBACjC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;iBAChD;gBACD,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,yBAAyB,CAAC,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;gBAEtD,mBAAmB;aACpB;iBAAM;gBACL,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;gBAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;aACjD;SACF;IACH,CAAC;IAED;;;OAGG;IACK,sCAAsC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YACtC,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,0CAA0C,OAClD,0BAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,GAAG,CACJ,CAAC;SACH;aAAM;YACL,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YAEpB,MAAM,UAAU,GAAoB;gBAClC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;gBACzB,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;aACvC,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;SAC7D;IACH,CAAC;IAED;;OAEG;IACK,0BAA0B;QAChC,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,6FAA6F;QAC7F,sHAAsH;QACtH,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE;YAC5D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACnB,IAAI,CAAC,UAAU,CAAC,sBAAU,CAAC,MAAM,CAAC,CAAC;YACnC,IAAI,CAAC,UAAU,CAAC,sBAAU,CAAC,QAAQ,CAAC,CAAC;SACtC;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YACnB,IAAI,CAAC,UAAU,CAAC,sBAAU,CAAC,MAAM,CAAC,CAAC;SACpC;QAED,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,8BAA8B,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACK,oCAAoC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;YACpB,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,yCAAyC,CAAC,CAAC;SACpE;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;YAC3B,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,+CAA+C,CAAC,CAAC;SAC1E;aAAM;YACL,6EAA6E;YAC7E,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAU,CAAC,MAAM,EAAE;gBACjC,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAChC,0EAA0E;aAC3E;iBAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAU,CAAC,QAAQ,EAAE;gBAC1C,IAAI,CAAC,gCAAgC,EAAE,CAAC;aACzC;iBAAM;gBACL,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,4CAA4C,CAAC,CAAC;aACvE;SACF;IACH,CAAC;IAED;;;;OAIG;IACK,gCAAgC;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;QAEnD,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAE3B,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,oCAAoC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,kBAAkB,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACK,kDAAkD;QACxD,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,8BAA8B,CAAC,CAAC;QAE/D,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;YACpB,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,0BAA0B,CAAC,CAAC;SACrD;aAAM;YACL,IAAI,CAAC,wBAAwB,EAAE,CAAC;SACjC;IACH,CAAC;IAED;;OAEG;IACK,wBAAwB;QAC9B,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAE/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,sBAAsB;QACtB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;YAC7C,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;SAC9D;aAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;YACpD,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;SAC9D;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;SACjD;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAElD,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,oBAAoB,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,kBAAkB,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACK,kCAAkC;QACxC,+EAA+E;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YAC9D,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,mCAAmC,MAC3C,0BAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAC1B,EAAE,CACH,CAAC;SACH;aAAM;YACL,oBAAoB;YACpB,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YAE9B,IAAI,UAA2B,CAAC;YAChC,IAAI,IAAiB,CAAC;YAEtB,OAAO;YACP,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBACvC,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBAEF,4DAA4D;gBAC5D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE;oBACjC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;iBAChD;gBAED,WAAW;aACZ;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,QAAQ,EAAE;gBAClD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,MAAM,UAAU,GAAG,uCAA2B,CAAC,sBAAsB,CACnE,UAAU,CACX,CAAC,CAAC,qCAAqC;gBAExC,8BAA8B;gBAC9B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;oBACjC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBACF,OAAO;aACR;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBAC9C,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;aACH;YAED,6BAA6B;YAC7B,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,qBAAqB,CAAC,CAAC;YAEtD,gEAAgE;YAChE,IAAI,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,OAAO,EAAE;gBAC/D,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;gBAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;aACjD;iBAAM,IAAI,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,IAAI,EAAE;gBACnE;mHACmG;gBACnG,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,yBAAyB,CAAC,CAAC;gBAC1D,IAAI,CAAC,4BAA4B;oBAC/B,uCAA2B,CAAC,oBAAoB,CAAC;gBACnD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;gBACtD;;;kBAGE;aACH;iBAAM,IACL,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,SAAS,EAC7D;gBACA,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;gBAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;oBACvB,UAAU;oBACV,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC,CAAC;aACJ;SACF;IACH,CAAC;IAED;;OAEG;IACK,sCAAsC;QAC5C,+EAA+E;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YAC9D,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,0CAA0C,MAClD,0BAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAC1B,EAAE,CACH,CAAC;SACH;aAAM;YACL,oBAAoB;YACpB,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YAE9B,IAAI,UAA2B,CAAC;YAChC,IAAI,IAAiB,CAAC;YAEtB,OAAO;YACP,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBACvC,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBAEF,4DAA4D;gBAC5D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE;oBACjC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;iBAChD;gBAED,WAAW;aACZ;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,QAAQ,EAAE;gBAClD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,MAAM,UAAU,GAAG,uCAA2B,CAAC,sBAAsB,CACnE,UAAU,CACX,CAAC,CAAC,8BAA8B;gBAEjC,8BAA8B;gBAC9B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;oBACjC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBACF,OAAO;aACR;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBAC9C,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;aACH;YAED,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;SAC7D;IACH,CAAC;IAED,IAAI,kBAAkB;QACpB,yBACK,IAAI,CAAC,OAAO,EACf;IACJ,CAAC;CACF;AAGC,kCAAW"} \ No newline at end of file +{"version":3,"file":"socksclient.js","sourceRoot":"","sources":["../../src/client/socksclient.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,mCAAoC;AACpC,2BAA2B;AAC3B,yBAAyB;AACzB,+CAAyC;AACzC,mDAkB6B;AAC7B,+CAG2B;AAC3B,2DAAsD;AACtD,yCAA8D;AA86B5D,iGA96BM,uBAAgB,OA86BN;AAp5BlB,MAAM,WAAY,SAAQ,qBAAY;IAgBpC,YAAY,OAA2B;QACrC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,qBACP,OAAO,CACX,CAAC;QAEF,8BAA8B;QAC9B,oCAA0B,CAAC,OAAO,CAAC,CAAC;QAEpC,gBAAgB;QAChB,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,gBAAgB,CACrB,OAA2B,EAC3B,QAAmB;QAEnB,OAAO,IAAI,OAAO,CAA8B,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAClE,8BAA8B;YAC9B,IAAI;gBACF,oCAA0B,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;aAClD;YAAC,OAAO,GAAG,EAAE;gBACZ,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,OAAO,CAAC,GAAU,CAAC,CAAC,CAAC,oDAAoD;iBACjF;qBAAM;oBACL,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;iBACpB;aACF;YAED,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;YACxC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC,IAAiC,EAAE,EAAE;gBAC/D,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,oDAAoD;iBACpE;qBAAM;oBACL,OAAO,CAAC,IAAI,CAAC,CAAC;iBACf;YACH,CAAC,CAAC,CAAC;YAEH,kDAAkD;YAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;gBAClC,MAAM,CAAC,kBAAkB,EAAE,CAAC;gBAC5B,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,CAAC,GAAU,CAAC,CAAC,CAAC,oDAAoD;iBAC1E;qBAAM;oBACL,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;OAQG;IACH,MAAM,CAAC,qBAAqB,CAC1B,OAAgC,EAChC,QAAmB;QAEnB,OAAO,IAAI,OAAO,CAA8B,CAAO,OAAO,EAAE,MAAM,EAAE,EAAE;YACxE,mCAAmC;YACnC,IAAI;gBACF,yCAA+B,CAAC,OAAO,CAAC,CAAC;aAC1C;YAAC,OAAO,GAAG,EAAE;gBACZ,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,OAAO,CAAC,GAAU,CAAC,CAAC,CAAC,oDAAoD;iBACjF;qBAAM;oBACL,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;iBACpB;aACF;YAED,IAAI,IAAgB,CAAC;YAErB,kBAAkB;YAClB,IAAI,OAAO,CAAC,cAAc,EAAE;gBAC1B,mBAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;aAC/B;YAED,IAAI;gBACF,kDAAkD;gBAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;oBAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;oBAErC,0HAA0H;oBAC1H,MAAM,eAAe,GACnB,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;wBAC9B,CAAC,CAAC,OAAO,CAAC,WAAW;wBACrB,CAAC,CAAC;4BACE,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;4BACtC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;yBAClC,CAAC;oBAER,4CAA4C;oBAC5C,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,gBAAgB,CAAC;wBAChD,OAAO,EAAE,SAAS;wBAClB,KAAK,EAAE,SAAS;wBAChB,WAAW,EAAE,eAAe;wBAC5B,8HAA8H;qBAC/H,CAAC,CAAC;oBAEH,wCAAwC;oBACxC,IAAI,CAAC,IAAI,EAAE;wBACT,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;qBACtB;iBACF;gBAED,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,IAAI,EAAE,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC;oBAC/B,OAAO,CAAC,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC,oDAAoD;iBAC9E;qBAAM;oBACL,OAAO,CAAC,EAAC,MAAM,EAAE,IAAI,EAAC,CAAC,CAAC;iBACzB;aACF;YAAC,OAAO,GAAG,EAAE;gBACZ,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE;oBAClC,QAAQ,CAAC,GAAG,CAAC,CAAC;oBACd,OAAO,CAAC,GAAU,CAAC,CAAC,CAAC,oDAAoD;iBAC1E;qBAAM;oBACL,MAAM,CAAC,GAAG,CAAC,CAAC;iBACb;aACF;QACH,CAAC,CAAA,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,cAAc,CAAC,OAA6B;QACjD,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;QAE1C,qBAAqB;QACrB,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YACvC,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;SACxD;aAAM,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE;YAC9C,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;SACxD;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;SAC3C;QAED,OAAO;QACP,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAE5C,OAAO;QACP,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,IAAY;QAC/B,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QAEpB,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAmB,IAAI,CAAC,SAAS,EAAE,CAAC;QAClD,IAAI,UAAU,CAAC;QAEf,IAAI,QAAQ,KAAK,0BAAc,CAAC,IAAI,EAAE;YACpC,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;SAC/C;aAAM,IAAI,QAAQ,KAAK,0BAAc,CAAC,IAAI,EAAE;YAC3C,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;SAC/C;aAAM;YACL,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;SAChD;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QAEvC,OAAO;YACL,WAAW;YACX,UAAU,EAAE;gBACV,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,UAAU;aACjB;YACD,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE;SACxB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,QAAQ,CAAC,QAA0B;QACzC,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,KAAK,EAAE;YACzC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;SACvB;IACH,CAAC;IAED;;;OAGG;IACI,OAAO,CAAC,cAAuB;QACpC,IAAI,CAAC,cAAc,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3C,IAAI,CAAC,OAAO,GAAG,CAAC,GAAU,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACxD,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE/C,+CAA+C;QAC/C,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,EACjC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,2BAAe,CACxC,CAAC;QAEF,8EAA8E;QAC9E,IAAI,KAAK,CAAC,KAAK,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU,EAAE;YACpD,KAAK,CAAC,KAAK,EAAE,CAAC;SACf;QAED,yGAAyG;QACzG,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC;SAC9B;aAAM;YACL,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;SAChC;QAED,gCAAgC;QAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAE5C,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,CAAC,aAAa,GAAG,IAAI,6BAAa,EAAE,CAAC;QAEzC,IAAI,cAAc,EAAE;YAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;SAC7B;aAAM;YACJ,IAAI,CAAC,MAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAE7D,IACE,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS;gBAC1C,IAAI,CAAC,OAAO,CAAC,eAAe,KAAK,IAAI,EACrC;gBACC,IAAI,CAAC,MAAqB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;aACxE;SACF;QAED,6FAA6F;QAC7F,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,EAAE;YAC/C,YAAY,CAAC,GAAG,EAAE;gBAChB,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE;oBACjC,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;oBAErE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;iBACtC;gBACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACvB,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,+EAA+E;IACvE,gBAAgB;QACtB,uCACK,IAAI,CAAC,OAAO,CAAC,cAAc,KAC9B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,EAC7D,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAC7B;IACJ,CAAC;IAED;;;OAGG;IACK,oBAAoB;QAC1B,IACE,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,WAAW;YAC3C,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,yBAAyB,EACzD;YACA,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,uBAAuB,CAAC,CAAC;SAClD;IACH,CAAC;IAED;;OAEG;IACK,gBAAgB;QACtB,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,SAAS,CAAC,CAAC;QAE1C,0BAA0B;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;YACjC,IAAI,CAAC,0BAA0B,EAAE,CAAC;SACnC;aAAM;YACL,IAAI,CAAC,0BAA0B,EAAE,CAAC;SACnC;QAED,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,IAAY;QACxC;;;UAGE;QACF,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAEhC,6BAA6B;QAC7B,IAAI,CAAC,WAAW,EAAE,CAAC;IACrB,CAAC;IAED;;OAEG;IACK,WAAW;QACjB,mFAAmF;QACnF,OACE,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,WAAW;YAC3C,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,KAAK;YACrC,IAAI,CAAC,aAAa,CAAC,MAAM,IAAI,IAAI,CAAC,4BAA4B,EAC9D;YACA,gDAAgD;YAChD,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,oBAAoB,EAAE;gBACxD,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;oBACjC,4CAA4C;oBAC5C,IAAI,CAAC,kCAAkC,EAAE,CAAC;iBAC3C;qBAAM;oBACL,wDAAwD;oBACxD,IAAI,CAAC,oCAAoC,EAAE,CAAC;iBAC7C;gBACD,wDAAwD;aACzD;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,kBAAkB,EAAE;gBAC7D,IAAI,CAAC,kDAAkD,EAAE,CAAC;gBAC1D,6DAA6D;aAC9D;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,kBAAkB,EAAE;gBAC7D,IAAI,CAAC,kCAAkC,EAAE,CAAC;gBAC1C,mEAAmE;aACpE;iBAAM,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,yBAAyB,EAAE;gBACpE,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;oBACjC,IAAI,CAAC,sCAAsC,EAAE,CAAC;iBAC/C;qBAAM;oBACL,IAAI,CAAC,sCAAsC,EAAE,CAAC;iBAC/C;aACF;iBAAM;gBACL,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,aAAa,CAAC,CAAC;gBACvC,MAAM;aACP;SACF;IACH,CAAC;IAED;;;OAGG;IACK,cAAc;QACpB,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,YAAY,CAAC,CAAC;IACxC,CAAC;IAED;;;OAGG;IACK,cAAc,CAAC,GAAU;QAC/B,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED;;OAEG;IACK,4BAA4B;QAClC,6FAA6F;QAC7F,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IACxD,CAAC;IAED;;;OAGG;IACK,WAAW,CAAC,GAAW;QAC7B,2FAA2F;QAC3F,IAAI,IAAI,CAAC,KAAK,KAAK,4BAAgB,CAAC,KAAK,EAAE;YACzC,+BAA+B;YAC/B,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,KAAK,CAAC,CAAC;YAEtC,iBAAiB;YACjB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YAEtB,4BAA4B;YAC5B,IAAI,CAAC,4BAA4B,EAAE,CAAC;YAEpC,sBAAsB;YACtB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,uBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;SAC7D;IACH,CAAC;IAED;;OAEG;IACK,0BAA0B;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;QAE/C,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAElD,iBAAiB;QACjB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;YAC7C,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7D,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC3B,sBAAsB;SACvB;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC3B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;SACnD;QAED,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrC,CAAC;IAED;;;OAGG;IACK,kCAAkC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YACtC,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,6BAA6B,OACrC,0BAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,GAAG,CACJ,CAAC;SACH;aAAM;YACL,gBAAgB;YAChB,IAAI,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,IAAI,EAAE;gBAC5D,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;gBAEpB,MAAM,UAAU,GAAoB;oBAClC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;oBACzB,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;iBACvC,CAAC;gBAEF,yCAAyC;gBACzC,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE;oBACjC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;iBAChD;gBACD,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,yBAAyB,CAAC,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;gBAEtD,mBAAmB;aACpB;iBAAM;gBACL,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;gBAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;aACjD;SACF;IACH,CAAC;IAED;;;OAGG;IACK,sCAAsC;QAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YACtC,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,0CAA0C,OAClD,0BAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,GAAG,CACJ,CAAC;SACH;aAAM;YACL,MAAM,IAAI,GAAG,0BAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;YAEpB,MAAM,UAAU,GAAoB;gBAClC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;gBACzB,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;aACvC,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;SAC7D;IACH,CAAC;IAED;;OAEG;IACK,0BAA0B;QAChC,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAE/B,wCAAwC;QACxC,MAAM,oBAAoB,GAAG,CAAC,sBAAU,CAAC,MAAM,CAAC,CAAC;QAEjD,6FAA6F;QAC7F,sHAAsH;QACtH,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE;YAC5D,oBAAoB,CAAC,IAAI,CAAC,sBAAU,CAAC,QAAQ,CAAC,CAAC;SAChD;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE;YACvD,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;SAClE;QAED,yBAAyB;QACzB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC7C,KAAK,MAAM,UAAU,IAAI,oBAAoB,EAAE;YAC7C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;SAC7B;QAED,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,8BAA8B,CAAC;QAC7D,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,oBAAoB,CAAC,CAAC;IACvD,CAAC;IAED;;;OAGG;IACK,oCAAoC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE;YACpB,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,yCAAyC,CAAC,CAAC;SACpE;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,qCAAyB,EAAE;YAChD,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,+CAA+C,CAAC,CAAC;SAC1E;aAAM;YACL,6EAA6E;YAC7E,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAU,CAAC,MAAM,EAAE;gBACjC,IAAI,CAAC,oBAAoB,GAAG,sBAAU,CAAC,MAAM,CAAC;gBAC9C,IAAI,CAAC,wBAAwB,EAAE,CAAC;gBAChC,0EAA0E;aAC3E;iBAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,sBAAU,CAAC,QAAQ,EAAE;gBAC1C,IAAI,CAAC,oBAAoB,GAAG,sBAAU,CAAC,QAAQ,CAAC;gBAChD,IAAI,CAAC,gCAAgC,EAAE,CAAC;gBACxC,qFAAqF;aACtF;iBAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,EAAE;gBAC5D,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC;gBAClE,IAAI,CAAC,8BAA8B,EAAE,CAAC;aACvC;iBAAM;gBACL,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,4CAA4C,CAAC,CAAC;aACvE;SACF;IACH,CAAC;IAED;;;;OAIG;IACK,gCAAgC;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC;QAEnD,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAE3B,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,oCAAoC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,kBAAkB,CAAC,CAAC;IACrD,CAAC;IAEa,8BAA8B;;YAC1C,IAAI,CAAC,4BAA4B,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC;YACjF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,CAAC,CAAC;YAC1E,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,kBAAkB,CAAC,CAAC;QACrD,CAAC;KAAA;IAEa,uCAAuC,CAAC,IAAY;;YAChE,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,IAAI,CAAC,CAAC;QACrE,CAAC;KAAA;IAEa,iDAAiD,CAC7D,IAAY;;YAEZ,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;QAC1B,CAAC;KAAA;IAEa,mDAAmD,CAC/D,IAAY;;YAEZ,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;QAC1B,CAAC;KAAA;IAED;;;OAGG;IACW,kDAAkD;;YAC9D,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,8BAA8B,CAAC,CAAC;YAE/D,IAAI,UAAU,GAAY,KAAK,CAAC;YAEhC,IAAI,IAAI,CAAC,oBAAoB,KAAK,sBAAU,CAAC,MAAM,EAAE;gBACnD,UAAU,GAAG,MAAM,IAAI,CAAC,iDAAiD,CACvE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAC1B,CAAC;aACH;iBAAM,IAAI,IAAI,CAAC,oBAAoB,KAAK,sBAAU,CAAC,QAAQ,EAAE;gBAC5D,UAAU,GAAG,MAAM,IAAI,CAAC,mDAAmD,CACzE,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAC1B,CAAC;aACH;iBAAM,IACL,IAAI,CAAC,oBAAoB,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,kBAAkB,EACnE;gBACA,UAAU,GAAG,MAAM,IAAI,CAAC,uCAAuC,CAC7D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,yBAAyB,CAAC,CACrE,CAAC;aACH;YAED,IAAI,CAAC,UAAU,EAAE;gBACf,IAAI,CAAC,WAAW,CAAC,kBAAM,CAAC,0BAA0B,CAAC,CAAC;aACrD;iBAAM;gBACL,IAAI,CAAC,wBAAwB,EAAE,CAAC;aACjC;QACH,CAAC;KAAA;IAED;;OAEG;IACK,wBAAwB;QAC9B,MAAM,IAAI,GAAG,IAAI,0BAAW,EAAE,CAAC;QAE/B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,UAAU,CAAC,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAEtB,sBAAsB;QACtB,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;YAC7C,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;SAC9D;aAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;YACpD,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;SAC9D;aAAM;YACL,IAAI,CAAC,UAAU,CAAC,0BAAc,CAAC,QAAQ,CAAC,CAAC;YACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACtD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;SACjD;QACD,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QAElD,IAAI,CAAC,4BAA4B;YAC/B,uCAA2B,CAAC,oBAAoB,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,kBAAkB,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACK,kCAAkC;QACxC,+EAA+E;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YAC9D,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,mCAAmC,MAC3C,0BAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAC1B,EAAE,CACH,CAAC;SACH;aAAM;YACL,oBAAoB;YACpB,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YAE9B,IAAI,UAA2B,CAAC;YAChC,IAAI,IAAiB,CAAC;YAEtB,OAAO;YACP,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBACvC,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBAEF,4DAA4D;gBAC5D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE;oBACjC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;iBAChD;gBAED,WAAW;aACZ;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,QAAQ,EAAE;gBAClD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,MAAM,UAAU,GAAG,uCAA2B,CAAC,sBAAsB,CACnE,UAAU,CACX,CAAC,CAAC,qCAAqC;gBAExC,8BAA8B;gBAC9B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;oBACjC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBACF,OAAO;aACR;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBAC9C,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;aACH;YAED,6BAA6B;YAC7B,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,qBAAqB,CAAC,CAAC;YAEtD,gEAAgE;YAChE,IAAI,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,OAAO,EAAE;gBAC/D,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;gBAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;aACjD;iBAAM,IAAI,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,IAAI,EAAE;gBACnE;mHACmG;gBACnG,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,yBAAyB,CAAC,CAAC;gBAC1D,IAAI,CAAC,4BAA4B;oBAC/B,uCAA2B,CAAC,oBAAoB,CAAC;gBACnD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;gBACtD;;;kBAGE;aACH;iBAAM,IACL,wBAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,wBAAY,CAAC,SAAS,EAC7D;gBACA,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;gBAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;gBACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE;oBACvB,UAAU;oBACV,MAAM,EAAE,IAAI,CAAC,MAAM;iBACpB,CAAC,CAAC;aACJ;SACF;IACH,CAAC;IAED;;OAEG;IACK,sCAAsC;QAC5C,+EAA+E;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE1C,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,0BAAc,CAAC,OAAO,EAAE;YAC9D,IAAI,CAAC,WAAW,CACd,GAAG,kBAAM,CAAC,0CAA0C,MAClD,0BAAc,CAAC,MAAM,CAAC,CAAC,CAAC,CAC1B,EAAE,CACH,CAAC;SACH;aAAM;YACL,oBAAoB;YACpB,MAAM,WAAW,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YAE9B,IAAI,UAA2B,CAAC;YAChC,IAAI,IAAiB,CAAC;YAEtB,OAAO;YACP,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBACvC,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBAEF,4DAA4D;gBAC5D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE;oBACjC,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;iBAChD;gBAED,WAAW;aACZ;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,QAAQ,EAAE;gBAClD,MAAM,UAAU,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,MAAM,UAAU,GAAG,uCAA2B,CAAC,sBAAsB,CACnE,UAAU,CACX,CAAC,CAAC,8BAA8B;gBAEjC,8BAA8B;gBAC9B,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;oBACjC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;gBACF,OAAO;aACR;iBAAM,IAAI,WAAW,KAAK,0BAAc,CAAC,IAAI,EAAE;gBAC9C,8BAA8B;gBAC9B,MAAM,UAAU,GAAG,uCAA2B,CAAC,kBAAkB,CAAC;gBAClE,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,GAAG,UAAU,EAAE;oBAC1C,IAAI,CAAC,4BAA4B,GAAG,UAAU,CAAC;oBAC/C,OAAO;iBACR;gBAED,IAAI,GAAG,0BAAW,CAAC,UAAU,CAC3B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5C,CAAC;gBAEF,UAAU,GAAG;oBACX,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;oBACtC,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE;iBAC1B,CAAC;aACH;YAED,IAAI,CAAC,QAAQ,CAAC,4BAAgB,CAAC,WAAW,CAAC,CAAC;YAC5C,IAAI,CAAC,4BAA4B,EAAE,CAAC;YACpC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,EAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAC,CAAC,CAAC;SAC7D;IACH,CAAC;IAED,IAAI,kBAAkB;QACpB,yBACK,IAAI,CAAC,OAAO,EACf;IACJ,CAAC;CACF;AAGC,kCAAW"} \ No newline at end of file diff --git a/deps/npm/node_modules/socks/build/common/constants.js b/deps/npm/node_modules/socks/build/common/constants.js index 8f8f5436ca142f..3c9ff90ac9feb4 100644 --- a/deps/npm/node_modules/socks/build/common/constants.js +++ b/deps/npm/node_modules/socks/build/common/constants.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.SOCKS_INCOMING_PACKET_SIZES = exports.SocksClientState = exports.Socks5Response = exports.Socks5HostType = exports.Socks5Auth = exports.Socks4Response = exports.SocksCommand = exports.ERRORS = exports.DEFAULT_TIMEOUT = void 0; +exports.SOCKS5_NO_ACCEPTABLE_AUTH = exports.SOCKS5_CUSTOM_AUTH_END = exports.SOCKS5_CUSTOM_AUTH_START = exports.SOCKS_INCOMING_PACKET_SIZES = exports.SocksClientState = exports.Socks5Response = exports.Socks5HostType = exports.Socks5Auth = exports.Socks4Response = exports.SocksCommand = exports.ERRORS = exports.DEFAULT_TIMEOUT = void 0; const DEFAULT_TIMEOUT = 30000; exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT; // prettier-ignore @@ -13,6 +13,8 @@ const ERRORS = { InvalidSocksClientOptionsProxy: 'Invalid SOCKS proxy details were provided.', InvalidSocksClientOptionsTimeout: 'An invalid timeout value was provided. Please enter a value above 0 (in ms).', InvalidSocksClientOptionsProxiesLength: 'At least two socks proxies must be provided for chaining.', + InvalidSocksClientOptionsCustomAuthRange: 'Custom auth must be a value between 0x80 and 0xFE.', + InvalidSocksClientOptionsCustomAuthOptions: 'When a custom_auth_method is provided, custom_auth_request_handler, custom_auth_response_size, and custom_auth_response_handler must also be provided and valid.', NegotiationError: 'Negotiation error', SocketClosed: 'Socket closed', ProxyConnectionTimedOut: 'Proxy connection timed out', @@ -41,7 +43,7 @@ const SOCKS_INCOMING_PACKET_SIZES = { Socks5ResponseIPv6: 22, Socks5ResponseHostname: (hostNameLength) => hostNameLength + 7, // Command response + incoming connection (bind) - Socks4Response: 8, + Socks4Response: 8, // 2 header + 2 port + 4 ip }; exports.SOCKS_INCOMING_PACKET_SIZES = SOCKS_INCOMING_PACKET_SIZES; var SocksCommand; @@ -66,6 +68,12 @@ var Socks5Auth; Socks5Auth[Socks5Auth["UserPass"] = 2] = "UserPass"; })(Socks5Auth || (Socks5Auth = {})); exports.Socks5Auth = Socks5Auth; +const SOCKS5_CUSTOM_AUTH_START = 0x80; +exports.SOCKS5_CUSTOM_AUTH_START = SOCKS5_CUSTOM_AUTH_START; +const SOCKS5_CUSTOM_AUTH_END = 0xfe; +exports.SOCKS5_CUSTOM_AUTH_END = SOCKS5_CUSTOM_AUTH_END; +const SOCKS5_NO_ACCEPTABLE_AUTH = 0xff; +exports.SOCKS5_NO_ACCEPTABLE_AUTH = SOCKS5_NO_ACCEPTABLE_AUTH; var Socks5Response; (function (Socks5Response) { Socks5Response[Socks5Response["Granted"] = 0] = "Granted"; diff --git a/deps/npm/node_modules/socks/build/common/constants.js.map b/deps/npm/node_modules/socks/build/common/constants.js.map index 70d31db294c55a..c1e070dea4ac3c 100644 --- a/deps/npm/node_modules/socks/build/common/constants.js.map +++ b/deps/npm/node_modules/socks/build/common/constants.js.map @@ -1 +1 @@ -{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":";;;AAIA,MAAM,eAAe,GAAG,KAAK,CAAC;AA6L5B,0CAAe;AAzLjB,kBAAkB;AAClB,MAAM,MAAM,GAAG;IACb,mBAAmB,EAAE,wFAAwF;IAC7G,+BAA+B,EAAE,oGAAoG;IACrI,wBAAwB,EAAE,8FAA8F;IACxH,oCAAoC,EAAE,2CAA2C;IACjF,uCAAuC,EAAE,uFAAuF;IAChI,8BAA8B,EAAE,4CAA4C;IAC5E,gCAAgC,EAAE,8EAA8E;IAChH,sCAAsC,EAAE,2DAA2D;IACnG,gBAAgB,EAAE,mBAAmB;IACrC,YAAY,EAAE,eAAe;IAC7B,uBAAuB,EAAE,4BAA4B;IACrD,aAAa,EAAE,qDAAqD;IACpE,8BAA8B,EAAE,4CAA4C;IAC5E,6BAA6B,EAAE,kCAAkC;IACjE,uCAAuC,EAAE,6CAA6C;IACtF,0CAA0C,EAAE,iDAAiD;IAC7F,qCAAqC,EAAE,oDAAoD;IAC3F,yCAAyC,EAAE,mEAAmE;IAC9G,+CAA+C,EAAE,6EAA6E;IAC9H,4CAA4C,EAAE,yEAAyE;IACvH,0BAA0B,EAAE,8BAA8B;IAC1D,2BAA2B,EAAE,kDAAkD;IAC/E,mCAAmC,EAAE,kCAAkC;IACvE,uCAAuC,EAAE,sDAAsD;IAC/F,0CAA0C,EAAE,iDAAiD;CAC9F,CAAC;AA+JA,wBAAM;AA7JR,MAAM,2BAA2B,GAAG;IAClC,8BAA8B,EAAE,CAAC;IACjC,oCAAoC,EAAE,CAAC;IACvC,gDAAgD;IAChD,oBAAoB,EAAE,CAAC;IACvB,kBAAkB,EAAE,EAAE;IACtB,kBAAkB,EAAE,EAAE;IACtB,sBAAsB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC,cAAc,GAAG,CAAC;IACtE,gDAAgD;IAChD,cAAc,EAAE,CAAC;CAClB,CAAC;AAmKA,kEAA2B;AA/J7B,IAAK,YAIJ;AAJD,WAAK,YAAY;IACf,qDAAc,CAAA;IACd,+CAAW,CAAA;IACX,yDAAgB,CAAA;AAClB,CAAC,EAJI,YAAY,KAAZ,YAAY,QAIhB;AA6IC,oCAAY;AA3Id,IAAK,cAKJ;AALD,WAAK,cAAc;IACjB,0DAAc,CAAA;IACd,wDAAa,CAAA;IACb,4DAAe,CAAA;IACf,sEAAoB,CAAA;AACtB,CAAC,EALI,cAAc,KAAd,cAAc,QAKlB;AAuIC,wCAAc;AArIhB,IAAK,UAIJ;AAJD,WAAK,UAAU;IACb,+CAAa,CAAA;IACb,+CAAa,CAAA;IACb,mDAAe,CAAA;AACjB,CAAC,EAJI,UAAU,KAAV,UAAU,QAId;AAkIC,gCAAU;AAhIZ,IAAK,cAUJ;AAVD,WAAK,cAAc;IACjB,yDAAc,CAAA;IACd,yDAAc,CAAA;IACd,+DAAiB,CAAA;IACjB,+EAAyB,CAAA;IACzB,yEAAsB,CAAA;IACtB,6EAAwB,CAAA;IACxB,+DAAiB,CAAA;IACjB,iFAA0B,CAAA;IAC1B,iFAA0B,CAAA;AAC5B,CAAC,EAVI,cAAc,KAAd,cAAc,QAUlB;AAwHC,wCAAc;AAtHhB,IAAK,cAIJ;AAJD,WAAK,cAAc;IACjB,mDAAW,CAAA;IACX,2DAAe,CAAA;IACf,mDAAW,CAAA;AACb,CAAC,EAJI,cAAc,KAAd,cAAc,QAIlB;AAiHC,wCAAc;AA/GhB,IAAK,gBAcJ;AAdD,WAAK,gBAAgB;IACnB,6DAAW,CAAA;IACX,mEAAc,CAAA;IACd,iEAAa,CAAA;IACb,uFAAwB,CAAA;IACxB,+GAAoC,CAAA;IACpC,mFAAsB,CAAA;IACtB,2GAAkC,CAAA;IAClC,mFAAsB,CAAA;IACtB,yFAAyB,CAAA;IACzB,iGAA6B,CAAA;IAC7B,sEAAgB,CAAA;IAChB,wEAAiB,CAAA;IACjB,0DAAU,CAAA;AACZ,CAAC,EAdI,gBAAgB,KAAhB,gBAAgB,QAcpB;AAmGC,4CAAgB"} \ No newline at end of file +{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/common/constants.ts"],"names":[],"mappings":";;;AAIA,MAAM,eAAe,GAAG,KAAK,CAAC;AA4M5B,0CAAe;AAxMjB,kBAAkB;AAClB,MAAM,MAAM,GAAG;IACb,mBAAmB,EAAE,wFAAwF;IAC7G,+BAA+B,EAAE,oGAAoG;IACrI,wBAAwB,EAAE,8FAA8F;IACxH,oCAAoC,EAAE,2CAA2C;IACjF,uCAAuC,EAAE,uFAAuF;IAChI,8BAA8B,EAAE,4CAA4C;IAC5E,gCAAgC,EAAE,8EAA8E;IAChH,sCAAsC,EAAE,2DAA2D;IACnG,wCAAwC,EAAE,oDAAoD;IAC9F,0CAA0C,EAAE,kKAAkK;IAC9M,gBAAgB,EAAE,mBAAmB;IACrC,YAAY,EAAE,eAAe;IAC7B,uBAAuB,EAAE,4BAA4B;IACrD,aAAa,EAAE,qDAAqD;IACpE,8BAA8B,EAAE,4CAA4C;IAC5E,6BAA6B,EAAE,kCAAkC;IACjE,uCAAuC,EAAE,6CAA6C;IACtF,0CAA0C,EAAE,iDAAiD;IAC7F,qCAAqC,EAAE,oDAAoD;IAC3F,yCAAyC,EAAE,mEAAmE;IAC9G,+CAA+C,EAAE,6EAA6E;IAC9H,4CAA4C,EAAE,yEAAyE;IACvH,0BAA0B,EAAE,8BAA8B;IAC1D,2BAA2B,EAAE,kDAAkD;IAC/E,mCAAmC,EAAE,kCAAkC;IACvE,uCAAuC,EAAE,sDAAsD;IAC/F,0CAA0C,EAAE,iDAAiD;CAC9F,CAAC;AA4KA,wBAAM;AA1KR,MAAM,2BAA2B,GAAG;IAClC,8BAA8B,EAAE,CAAC;IACjC,oCAAoC,EAAE,CAAC;IACvC,gDAAgD;IAChD,oBAAoB,EAAE,CAAC;IACvB,kBAAkB,EAAE,EAAE;IACtB,kBAAkB,EAAE,EAAE;IACtB,sBAAsB,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC,cAAc,GAAG,CAAC;IACtE,gDAAgD;IAChD,cAAc,EAAE,CAAC,EAAE,2BAA2B;CAC/C,CAAC;AAgLA,kEAA2B;AA5K7B,IAAK,YAIJ;AAJD,WAAK,YAAY;IACf,qDAAc,CAAA;IACd,+CAAW,CAAA;IACX,yDAAgB,CAAA;AAClB,CAAC,EAJI,YAAY,KAAZ,YAAY,QAIhB;AA0JC,oCAAY;AAxJd,IAAK,cAKJ;AALD,WAAK,cAAc;IACjB,0DAAc,CAAA;IACd,wDAAa,CAAA;IACb,4DAAe,CAAA;IACf,sEAAoB,CAAA;AACtB,CAAC,EALI,cAAc,KAAd,cAAc,QAKlB;AAoJC,wCAAc;AAlJhB,IAAK,UAIJ;AAJD,WAAK,UAAU;IACb,+CAAa,CAAA;IACb,+CAAa,CAAA;IACb,mDAAe,CAAA;AACjB,CAAC,EAJI,UAAU,KAAV,UAAU,QAId;AA+IC,gCAAU;AA7IZ,MAAM,wBAAwB,GAAG,IAAI,CAAC;AA0JpC,4DAAwB;AAzJ1B,MAAM,sBAAsB,GAAG,IAAI,CAAC;AA0JlC,wDAAsB;AAxJxB,MAAM,yBAAyB,GAAG,IAAI,CAAC;AAyJrC,8DAAyB;AAvJ3B,IAAK,cAUJ;AAVD,WAAK,cAAc;IACjB,yDAAc,CAAA;IACd,yDAAc,CAAA;IACd,+DAAiB,CAAA;IACjB,+EAAyB,CAAA;IACzB,yEAAsB,CAAA;IACtB,6EAAwB,CAAA;IACxB,+DAAiB,CAAA;IACjB,iFAA0B,CAAA;IAC1B,iFAA0B,CAAA;AAC5B,CAAC,EAVI,cAAc,KAAd,cAAc,QAUlB;AAgIC,wCAAc;AA9HhB,IAAK,cAIJ;AAJD,WAAK,cAAc;IACjB,mDAAW,CAAA;IACX,2DAAe,CAAA;IACf,mDAAW,CAAA;AACb,CAAC,EAJI,cAAc,KAAd,cAAc,QAIlB;AAyHC,wCAAc;AAvHhB,IAAK,gBAcJ;AAdD,WAAK,gBAAgB;IACnB,6DAAW,CAAA;IACX,mEAAc,CAAA;IACd,iEAAa,CAAA;IACb,uFAAwB,CAAA;IACxB,+GAAoC,CAAA;IACpC,mFAAsB,CAAA;IACtB,2GAAkC,CAAA;IAClC,mFAAsB,CAAA;IACtB,yFAAyB,CAAA;IACzB,iGAA6B,CAAA;IAC7B,sEAAgB,CAAA;IAChB,wEAAiB,CAAA;IACjB,0DAAU,CAAA;AACZ,CAAC,EAdI,gBAAgB,KAAhB,gBAAgB,QAcpB;AA2GC,4CAAgB"} \ No newline at end of file diff --git a/deps/npm/node_modules/socks/build/common/helpers.js b/deps/npm/node_modules/socks/build/common/helpers.js index 5bf4cc47744c5e..f84db8f6729d6c 100644 --- a/deps/npm/node_modules/socks/build/common/helpers.js +++ b/deps/npm/node_modules/socks/build/common/helpers.js @@ -26,6 +26,8 @@ function validateSocksClientOptions(options, acceptedCommands = ['connect', 'bin if (!isValidSocksProxy(options.proxy)) { throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsProxy, options); } + // Validate custom auth (if set) + validateCustomProxyAuth(options.proxy, options); // Check timeout if (options.timeout && !isValidTimeoutValue(options.timeout)) { throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsTimeout, options); @@ -61,6 +63,8 @@ function validateSocksClientChainOptions(options) { if (!isValidSocksProxy(proxy)) { throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsProxy, options); } + // Validate custom auth (if set) + validateCustomProxyAuth(proxy, options); }); // Check timeout if (options.timeout && !isValidTimeoutValue(options.timeout)) { @@ -68,6 +72,29 @@ function validateSocksClientChainOptions(options) { } } exports.validateSocksClientChainOptions = validateSocksClientChainOptions; +function validateCustomProxyAuth(proxy, options) { + if (proxy.custom_auth_method !== undefined) { + // Invalid auth method range + if (proxy.custom_auth_method < constants_1.SOCKS5_CUSTOM_AUTH_START || + proxy.custom_auth_method > constants_1.SOCKS5_CUSTOM_AUTH_END) { + throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsCustomAuthRange, options); + } + // Missing custom_auth_request_handler + if (proxy.custom_auth_request_handler === undefined || + typeof proxy.custom_auth_request_handler !== 'function') { + throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsCustomAuthOptions, options); + } + // Missing custom_auth_response_size + if (proxy.custom_auth_response_size === undefined) { + throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsCustomAuthOptions, options); + } + // Missing/invalid custom_auth_response_handler + if (proxy.custom_auth_response_handler === undefined || + typeof proxy.custom_auth_response_handler !== 'function') { + throw new util_1.SocksClientError(constants_1.ERRORS.InvalidSocksClientOptionsCustomAuthOptions, options); + } + } +} /** * Validates a SocksRemoteHost * @param remoteHost { SocksRemoteHost } diff --git a/deps/npm/node_modules/socks/build/common/helpers.js.map b/deps/npm/node_modules/socks/build/common/helpers.js.map index 3313a3c0323af1..dae124861aa90f 100644 --- a/deps/npm/node_modules/socks/build/common/helpers.js.map +++ b/deps/npm/node_modules/socks/build/common/helpers.js.map @@ -1 +1 @@ -{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/common/helpers.ts"],"names":[],"mappings":";;;AAKA,iCAAwC;AACxC,2CAA6D;AAC7D,iCAAiC;AAEjC;;;;GAIG;AACH,SAAS,0BAA0B,CACjC,OAA2B,EAC3B,gBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC;IAEnD,8BAA8B;IAC9B,IAAI,CAAC,wBAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAClC,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;KACjE;IAED,6CAA6C;IAC7C,IAAI,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE;QACpD,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAC;KAC7E;IAED,oBAAoB;IACpB,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QAChD,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,oCAAoC,EAC3C,OAAO,CACR,CAAC;KACH;IAED,2BAA2B;IAC3B,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QACrC,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,8BAA8B,EAAE,OAAO,CAAC,CAAC;KAC5E;IAED,gBAAgB;IAChB,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAC5D,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,gCAAgC,EACvC,OAAO,CACR,CAAC;KACH;IAED,sCAAsC;IACtC,IACE,OAAO,CAAC,eAAe;QACvB,CAAC,CAAC,OAAO,CAAC,eAAe,YAAY,MAAM,CAAC,MAAM,CAAC,EACnD;QACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,uCAAuC,EAC9C,OAAO,CACR,CAAC;KACH;AACH,CAAC;AA0FO,gEAA0B;AAxFlC;;;GAGG;AACH,SAAS,+BAA+B,CAAC,OAAgC;IACvE,2CAA2C;IAC3C,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE;QACjC,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;KACtE;IAED,oBAAoB;IACpB,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QAChD,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,oCAAoC,EAC3C,OAAO,CACR,CAAC;KACH;IAED,4BAA4B;IAC5B,IACE,CAAC,CACC,OAAO,CAAC,OAAO;QACf,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9B,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAC5B,EACD;QACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,sCAAsC,EAC7C,OAAO,CACR,CAAC;KACH;IAED,mBAAmB;IACnB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAiB,EAAE,EAAE;QAC5C,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE;YAC7B,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,8BAA8B,EACrC,OAAO,CACR,CAAC;SACH;IACH,CAAC,CAAC,CAAC;IAEH,gBAAgB;IAChB,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAC5D,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,gCAAgC,EACvC,OAAO,CACR,CAAC;KACH;AACH,CAAC;AAuCmC,0EAA+B;AArCnE;;;GAGG;AACH,SAAS,sBAAsB,CAAC,UAA2B;IACzD,OAAO,CACL,UAAU;QACV,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ;QACnC,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ;QACnC,UAAU,CAAC,IAAI,IAAI,CAAC;QACpB,UAAU,CAAC,IAAI,IAAI,KAAK,CACzB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,KAAiB;IAC1C,OAAO,CACL,KAAK;QACL,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC;QACvE,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,IAAI,IAAI,CAAC;QACf,KAAK,CAAC,IAAI,IAAI,KAAK;QACnB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CACvC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,KAAa;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC,CAAC;AAChD,CAAC"} \ No newline at end of file +{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/common/helpers.ts"],"names":[],"mappings":";;;AAKA,iCAAwC;AACxC,2CAMqB;AACrB,iCAAiC;AAEjC;;;;GAIG;AACH,SAAS,0BAA0B,CACjC,OAA2B,EAC3B,gBAAgB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC;IAEnD,8BAA8B;IAC9B,IAAI,CAAC,wBAAY,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAClC,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;KACjE;IAED,6CAA6C;IAC7C,IAAI,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE;QACpD,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,+BAA+B,EAAE,OAAO,CAAC,CAAC;KAC7E;IAED,oBAAoB;IACpB,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QAChD,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,oCAAoC,EAC3C,OAAO,CACR,CAAC;KACH;IAED,2BAA2B;IAC3B,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;QACrC,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,8BAA8B,EAAE,OAAO,CAAC,CAAC;KAC5E;IAED,gCAAgC;IAChC,uBAAuB,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAEhD,gBAAgB;IAChB,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAC5D,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,gCAAgC,EACvC,OAAO,CACR,CAAC;KACH;IAED,sCAAsC;IACtC,IACE,OAAO,CAAC,eAAe;QACvB,CAAC,CAAC,OAAO,CAAC,eAAe,YAAY,MAAM,CAAC,MAAM,CAAC,EACnD;QACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,uCAAuC,EAC9C,OAAO,CACR,CAAC;KACH;AACH,CAAC;AA6IO,gEAA0B;AA3IlC;;;GAGG;AACH,SAAS,+BAA+B,CAAC,OAAgC;IACvE,2CAA2C;IAC3C,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE;QACjC,MAAM,IAAI,uBAAgB,CAAC,kBAAM,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;KACtE;IAED,oBAAoB;IACpB,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE;QAChD,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,oCAAoC,EAC3C,OAAO,CACR,CAAC;KACH;IAED,4BAA4B;IAC5B,IACE,CAAC,CACC,OAAO,CAAC,OAAO;QACf,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9B,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAC5B,EACD;QACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,sCAAsC,EAC7C,OAAO,CACR,CAAC;KACH;IAED,mBAAmB;IACnB,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAiB,EAAE,EAAE;QAC5C,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE;YAC7B,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,8BAA8B,EACrC,OAAO,CACR,CAAC;SACH;QAED,gCAAgC;QAChC,uBAAuB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,gBAAgB;IAChB,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;QAC5D,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,gCAAgC,EACvC,OAAO,CACR,CAAC;KACH;AACH,CAAC;AAuFmC,0EAA+B;AArFnE,SAAS,uBAAuB,CAC9B,KAAiB,EACjB,OAAqD;IAErD,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE;QAC1C,4BAA4B;QAC5B,IACE,KAAK,CAAC,kBAAkB,GAAG,oCAAwB;YACnD,KAAK,CAAC,kBAAkB,GAAG,kCAAsB,EACjD;YACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,wCAAwC,EAC/C,OAAO,CACR,CAAC;SACH;QAED,sCAAsC;QACtC,IACE,KAAK,CAAC,2BAA2B,KAAK,SAAS;YAC/C,OAAO,KAAK,CAAC,2BAA2B,KAAK,UAAU,EACvD;YACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,0CAA0C,EACjD,OAAO,CACR,CAAC;SACH;QAED,oCAAoC;QACpC,IAAI,KAAK,CAAC,yBAAyB,KAAK,SAAS,EAAE;YACjD,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,0CAA0C,EACjD,OAAO,CACR,CAAC;SACH;QAED,+CAA+C;QAC/C,IACE,KAAK,CAAC,4BAA4B,KAAK,SAAS;YAChD,OAAO,KAAK,CAAC,4BAA4B,KAAK,UAAU,EACxD;YACA,MAAM,IAAI,uBAAgB,CACxB,kBAAM,CAAC,0CAA0C,EACjD,OAAO,CACR,CAAC;SACH;KACF;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,sBAAsB,CAAC,UAA2B;IACzD,OAAO,CACL,UAAU;QACV,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ;QACnC,OAAO,UAAU,CAAC,IAAI,KAAK,QAAQ;QACnC,UAAU,CAAC,IAAI,IAAI,CAAC;QACpB,UAAU,CAAC,IAAI,IAAI,KAAK,CACzB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,KAAiB;IAC1C,OAAO,CACL,KAAK;QACL,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC;QACvE,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,IAAI,IAAI,CAAC;QACf,KAAK,CAAC,IAAI,IAAI,KAAK;QACnB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CACvC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAAC,KAAa;IACxC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,GAAG,CAAC,CAAC;AAChD,CAAC"} \ No newline at end of file diff --git a/deps/npm/node_modules/socks/package.json b/deps/npm/node_modules/socks/package.json index 8900ebbbb581b0..d6fb6d64c042b7 100644 --- a/deps/npm/node_modules/socks/package.json +++ b/deps/npm/node_modules/socks/package.json @@ -1,7 +1,7 @@ { "name": "socks", "private": false, - "version": "2.5.1", + "version": "2.6.0", "description": "Fully featured SOCKS proxy client supporting SOCKSv4, SOCKSv4a, and SOCKSv5. Includes Bind and Associate functionality.", "main": "build/index.js", "typings": "typings/index.d.ts", @@ -34,17 +34,17 @@ "readmeFilename": "README.md", "devDependencies": { "@types/ip": "1.1.0", - "@types/mocha": "^8.0.3", - "@types/node": "^14.14.3", + "@types/mocha": "^8.2.1", + "@types/node": "^14.14.35", "coveralls": "3.1.0", - "mocha": "^8.2.0", + "mocha": "^8.3.2", "nyc": "15.1.0", - "prettier": "^2.1.2", + "prettier": "^2.2.1", "socks5-server": "^0.1.1", - "ts-node": "^9.0.0", + "ts-node": "^9.1.1", "tslint": "^6.1.3", "tslint-config-airbnb": "^5.11.2", - "typescript": "^4.0.3" + "typescript": "^4.2.3" }, "dependencies": { "ip": "^1.1.5", @@ -57,7 +57,7 @@ "coverage": "NODE_ENV=test nyc npm test", "coveralls": "NODE_ENV=test nyc npm test && nyc report --reporter=text-lcov | coveralls", "lint": "tslint --project tsconfig.json 'src/**/*.ts'", - "build": "rm -rf build typings && tslint --project tsconfig.json && prettier --write ./src/**/*.ts --config .prettierrc.yaml && tsc -p ." + "build": "rm -rf build typings && prettier --write ./src/**/*.ts --config .prettierrc.yaml && tsc -p ." }, "nyc": { "extension": [ diff --git a/deps/npm/node_modules/socks/typings/client/socksclient.d.ts b/deps/npm/node_modules/socks/typings/client/socksclient.d.ts index ff762a8a663f3a..d8ce1b965f0e0c 100644 --- a/deps/npm/node_modules/socks/typings/client/socksclient.d.ts +++ b/deps/npm/node_modules/socks/typings/client/socksclient.d.ts @@ -22,6 +22,7 @@ declare class SocksClient extends EventEmitter implements SocksClient { private state; private receiveBuffer; private nextRequiredPacketBufferSize; + private socks5ChosenAuthType; private onDataReceived; private onClose; private onError; @@ -132,6 +133,10 @@ declare class SocksClient extends EventEmitter implements SocksClient { * Note: No auth and user/pass are currently supported. */ private sendSocks5UserPassAuthentication; + private sendSocks5CustomAuthentication; + private handleSocks5CustomAuthHandshakeResponse; + private handleSocks5AuthenticationNoAuthHandshakeResponse; + private handleSocks5AuthenticationUserPassHandshakeResponse; /** * Handles Socks v5 auth handshake response. * @param data diff --git a/deps/npm/node_modules/socks/typings/common/constants.d.ts b/deps/npm/node_modules/socks/typings/common/constants.d.ts index b801c1e0607e9e..664795cb180fb9 100644 --- a/deps/npm/node_modules/socks/typings/common/constants.d.ts +++ b/deps/npm/node_modules/socks/typings/common/constants.d.ts @@ -13,6 +13,8 @@ declare const ERRORS: { InvalidSocksClientOptionsProxy: string; InvalidSocksClientOptionsTimeout: string; InvalidSocksClientOptionsProxiesLength: string; + InvalidSocksClientOptionsCustomAuthRange: string; + InvalidSocksClientOptionsCustomAuthOptions: string; NegotiationError: string; SocketClosed: string; ProxyConnectionTimedOut: string; @@ -57,6 +59,9 @@ declare enum Socks5Auth { GSSApi = 1, UserPass = 2 } +declare const SOCKS5_CUSTOM_AUTH_START = 128; +declare const SOCKS5_CUSTOM_AUTH_END = 254; +declare const SOCKS5_NO_ACCEPTABLE_AUTH = 255; declare enum Socks5Response { Granted = 0, Failure = 1, @@ -98,6 +103,10 @@ declare type SocksProxy = RequireOnlyOne<{ type: SocksProxyType; userId?: string; password?: string; + custom_auth_method?: number; + custom_auth_request_handler?: () => Promise<Buffer>; + custom_auth_response_size?: number; + custom_auth_response_handler?: (data: Buffer) => Promise<boolean>; }, 'host' | 'ipaddress'>; /** * Represents a remote host @@ -138,4 +147,4 @@ interface SocksUDPFrameDetails { remoteHost: SocksRemoteHost; data: Buffer; } -export { DEFAULT_TIMEOUT, ERRORS, SocksProxyType, SocksCommand, Socks4Response, Socks5Auth, Socks5HostType, Socks5Response, SocksClientState, SocksProxy, SocksRemoteHost, SocksCommandOption, SocksClientOptions, SocksClientChainOptions, SocksClientEstablishedEvent, SocksClientBoundEvent, SocksUDPFrameDetails, SOCKS_INCOMING_PACKET_SIZES, }; +export { DEFAULT_TIMEOUT, ERRORS, SocksProxyType, SocksCommand, Socks4Response, Socks5Auth, Socks5HostType, Socks5Response, SocksClientState, SocksProxy, SocksRemoteHost, SocksCommandOption, SocksClientOptions, SocksClientChainOptions, SocksClientEstablishedEvent, SocksClientBoundEvent, SocksUDPFrameDetails, SOCKS_INCOMING_PACKET_SIZES, SOCKS5_CUSTOM_AUTH_START, SOCKS5_CUSTOM_AUTH_END, SOCKS5_NO_ACCEPTABLE_AUTH, }; diff --git a/deps/npm/node_modules/uuid/README.md b/deps/npm/node_modules/uuid/README.md index 6bccc345ee08e4..1752e4751fc920 100644 --- a/deps/npm/node_modules/uuid/README.md +++ b/deps/npm/node_modules/uuid/README.md @@ -127,13 +127,13 @@ Example: In-place generation of two binary IDs ```javascript // Generate two ids in an array const arr = new Array(); -uuidv1(null, arr, 0); // ⇨ +uuidv1(null, arr, 0); // ⇨ // [ // 44, 94, 164, 192, 64, 103, // 17, 233, 146, 52, 155, 29, // 235, 77, 59, 125 // ] -uuidv1(null, arr, 16); // ⇨ +uuidv1(null, arr, 16); // ⇨ // [ // 44, 94, 164, 192, 64, 103, 17, 233, // 146, 52, 155, 29, 235, 77, 59, 125, @@ -208,14 +208,14 @@ Example: Generate two IDs in a single buffer ```javascript const buffer = new Array(); -uuidv4(null, buffer, 0); // ⇨ +uuidv4(null, buffer, 0); // ⇨ // [ // 155, 29, 235, 77, 59, // 125, 75, 173, 155, 221, // 43, 13, 123, 61, 203, // 109 // ] -uuidv4(null, buffer, 16); // ⇨ +uuidv4(null, buffer, 16); // ⇨ // [ // 155, 29, 235, 77, 59, 125, 75, 173, // 155, 221, 43, 13, 123, 61, 203, 109, diff --git a/deps/npm/package.json b/deps/npm/package.json index c4b10a831b610b..4d531d7de90590 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "7.7.0", + "version": "7.7.4", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -59,7 +59,7 @@ "columnify": "~1.5.4", "glob": "^7.1.4", "graceful-fs": "^4.2.6", - "hosted-git-info": "^4.0.1", + "hosted-git-info": "^4.0.2", "ini": "^2.0.0", "init-package-json": "^2.0.2", "is-cidr": "^4.0.2", diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js index 2ed810da8a2840..b3d920a0ca2845 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-config-definitions.js-TAP.test.js @@ -32,6 +32,7 @@ Array [ "commit-hooks", "depth", "description", + "dev", "diff", "diff-ignore-all-space", "diff-name-only", diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js index 8323e793e075f8..a85f90ac84181a 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js @@ -64,7 +64,7 @@ registry and all registries configured for scopes. See the documentation for #### \`audit-level\` * Default: null -* Type: "low", "moderate", "high", "critical", "none", or null +* Type: "info", "low", "moderate", "high", "critical", "none", or null The minimum level of vulnerability for \`npm audit\` to exit with a non-zero exit code. @@ -1261,6 +1261,14 @@ What authentication strategy to use with \`adduser\`/\`login\`. \`--cache-min=9999 (or bigger)\` is an alias for \`--prefer-offline\`. +#### \`dev\` + +* Default: false +* Type: Boolean +* DEPRECATED: Please use --include=dev instead. + +Alias for \`--include=dev\`. + #### \`init.author.email\` * Default: "" @@ -1330,8 +1338,8 @@ Alias for --include=optional or --omit=optional #### \`production\` -* Default: false -* Type: Boolean +* Default: null +* Type: null or Boolean * DEPRECATED: Use \`--omit=dev\` instead. Alias for \`--omit=dev\` diff --git a/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js index cf085f1ad5b1aa..5a860bd2ee554b 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-npm-usage.js-TAP.test.js @@ -166,9 +166,9 @@ npm help npm more involved overview All commands: access npm access - + Set access level on published packages - + Usage: npm access public [<package>] npm access restricted [<package>] @@ -179,62 +179,62 @@ All commands: npm access ls-packages [<user>|<scope>|<scope:team>] npm access ls-collaborators [<package> [<user>]] npm access edit [<package>] - + Run "npm help access" for more info adduser npm adduser - + Add a registry user account - + Usage: npm adduser - + Options: [--registry <registry>] [--scope <@scope>] [--always-auth] - + aliases: login, add-user - + Run "npm help adduser" for more info audit npm audit - + Run a security audit - + Usage: npm audit [fix] - + Options: - [--dry-run] [-f|--force] [--json] [--package-lock-only] [--production] - + [--audit-level <info|low|moderate|high|critical|none>] [--dry-run] [-f|--force] [--json] [--package-lock-only] [--production] + Run "npm help audit" for more info bin npm bin - + Display npm bin folder - + Usage: npm bin - + Options: [-g|--global] - + Run "npm help bin" for more info bugs npm bugs - + Report bugs for a package in a web browser - + Usage: npm bugs [<pkgname>] - + alias: issues - + Run "npm help bugs" for more info cache npm cache - + Manipulates packages cache - + Usage: npm cache add <tarball file> npm cache add <folder> @@ -243,223 +243,223 @@ All commands: npm cache add <name>@<version> npm cache clean npm cache verify - + Run "npm help cache" for more info ci npm ci - + Install a project with a clean slate - + Usage: npm ci - + aliases: clean-install, ic, install-clean, isntall-clean - + Run "npm help ci" for more info completion npm completion - + Tab Completion for npm - + Usage: npm completion - + Run "npm help completion" for more info config npm config - + Manage the npm configuration files - + Usage: npm config set <key>=<value> [<key>=<value> ...] npm config get [<key> [<key> ...]] npm config delete <key> [<key> ...] npm config list [--json] npm config edit - + alias: c - + Run "npm help config" for more info dedupe npm dedupe - + Reduce duplication in the package tree - + Usage: npm dedupe - + alias: ddp - + Run "npm help dedupe" for more info deprecate npm deprecate - + Deprecate a version of a package - + Usage: npm deprecate <pkg>[@<version>] <message> - + Run "npm help deprecate" for more info diff npm diff - + The registry diff command - + Usage: npm diff [...<paths>] npm diff --diff=<pkg-name> [...<paths>] npm diff --diff=<version-a> [--diff=<version-b>] [...<paths>] npm diff --diff=<spec-a> [--diff=<spec-b>] [...<paths>] npm diff [--diff-ignore-all-space] [--diff-name-only] [...<paths>] [...<paths>] - + Run "npm help diff" for more info dist-tag npm dist-tag - + Modify package distribution tags - + Usage: npm dist-tag add <pkg>@<version> [<tag>] npm dist-tag rm <pkg> <tag> npm dist-tag ls [<pkg>] - + alias: dist-tags - + Run "npm help dist-tag" for more info docs npm docs - + Open documentation for a package in a web browser - + Usage: npm docs [<pkgname> [<pkgname> ...]] - + alias: home - + Run "npm help docs" for more info doctor npm doctor - + Check your npm environment - + Usage: npm doctor - + Run "npm help doctor" for more info edit npm edit - + Edit an installed package - + Usage: npm edit <pkg>[/<subpkg>...] - + Run "npm help edit" for more info exec npm exec - + Run a command from a local or remote npm package - + Usage: npm exec -- <pkg>[@<version>] [args...] npm exec --package=<pkg>[@<version>] -- <cmd> [args...] npm exec -c '<cmd> [args...]' npm exec --package=foo -c '<cmd> [args...]' - + alias: x - + Run "npm help exec" for more info explain npm explain - + Explain installed packages - + Usage: npm explain <folder | specifier> - + alias: why - + Run "npm help explain" for more info explore npm explore - + Browse an installed package - + Usage: npm explore <pkg> [ -- <command>] - + Run "npm help explore" for more info find-dupes npm find-dupes - + Find duplication in the package tree - + Usage: npm find-dupes - + Run "npm help find-dupes" for more info fund npm fund - + Retrieve funding information - + Usage: npm fund [[<@scope>/]<pkg>] - + Options: [--json] [--browser|--browser <browser>] [--unicode] [--which <fundingSourceNumber>] - + Run "npm help fund" for more info get npm get - + Get a value from the npm configuration - + Usage: npm get [<key> ...] (See \`npm config\`) - + Run "npm help get" for more info help npm help - + Get help on npm - + Usage: npm help <term> [<terms..>] - + alias: hlep - + Run "npm help help" for more info hook npm hook - + Manage registry hooks - + Usage: npm hook add <pkg> <url> <secret> [--type=<type>] npm hook ls [pkg] npm hook rm <id> npm hook update <id> <url> <secret> - + Run "npm help hook" for more info init npm init - + Create a package.json file - + Usage: npm init [--force|-f|--yes|-y|--scope] npm init <@scope> (same as \`npx <@scope>/create\`) npm init [<@scope>/]<name> (same as \`npx [<@scope>/]create-<name>\`) - + aliases: create, innit - + Run "npm help init" for more info install npm install - + Install a package - + Usage: npm install [<@scope>/]<pkg> npm install [<@scope>/]<pkg>@<tag> @@ -471,29 +471,29 @@ All commands: npm install <tarball url> npm install <git:// url> npm install <github username>/<github project> - + Options: [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [-E|--save-exact] - + aliases: i, in, ins, inst, insta, instal, isnt, isnta, isntal, add - + Run "npm help install" for more info install-ci-test npm install-ci-test - + Install a project with a clean slate and run tests - + Usage: npm install-ci-test - + alias: cit - + Run "npm help install-ci-test" for more info install-test npm install-test - + Install package(s) and run tests - + Usage: npm install-test [<@scope>/]<pkg> npm install-test [<@scope>/]<pkg>@<tag> @@ -505,420 +505,420 @@ All commands: npm install-test <tarball url> npm install-test <git:// url> npm install-test <github username>/<github project> - + Options: [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] [-E|--save-exact] - + alias: it - + Run "npm help install-test" for more info link npm link - + Symlink a package folder - + Usage: npm link (in package dir) npm link [<@scope>/]<pkg>[@<version>] - + alias: ln - + Run "npm help link" for more info ll npm ll - + List installed packages - + Usage: npm ll [[<@scope>/]<pkg> ...] - + alias: la - + Run "npm help ll" for more info login npm adduser - + Add a registry user account - + Usage: npm adduser - + Options: [--registry <registry>] [--scope <@scope>] [--always-auth] - + aliases: login, add-user - + Run "npm help adduser" for more info logout npm logout - + Log out of the registry - + Usage: npm logout - + Options: [--registry <registry>] [--scope <@scope>] - + Run "npm help logout" for more info ls npm ls - + List installed packages - + Usage: npm ls npm ls [[<@scope>/]<pkg> ...] - + alias: list - + Run "npm help ls" for more info org npm org - + Manage orgs - + Usage: npm org set orgname username [developer | admin | owner] npm org rm orgname username npm org ls orgname [<username>] - + alias: ogr - + Run "npm help org" for more info outdated npm outdated - + Check for outdated packages - + Usage: npm outdated [[<@scope>/]<pkg> ...] - + Run "npm help outdated" for more info owner npm owner - + Manage package owners - + Usage: npm owner add <user> [<@scope>/]<pkg> npm owner rm <user> [<@scope>/]<pkg> npm owner ls [<@scope>/]<pkg> - + alias: author - + Run "npm help owner" for more info pack npm pack - + Create a tarball from a package - + Usage: npm pack [[<@scope>/]<pkg>...] - + Options: [--dry-run] - + Run "npm help pack" for more info ping npm ping - + Ping npm registry - + Usage: npm ping - + Options: [--registry <registry>] - + Run "npm help ping" for more info prefix npm prefix - + Display prefix - + Usage: npm prefix [-g] - + Run "npm help prefix" for more info profile npm profile - + Change settings on your registry profile - + Usage: npm profile enable-2fa [auth-only|auth-and-writes] npm profile disable-2fa npm profile get [<key>] npm profile set <key> <value> - + Run "npm help profile" for more info prune npm prune - + Remove extraneous packages - + Usage: npm prune [[<@scope>/]<pkg>...] - + Options: [--production] - + Run "npm help prune" for more info publish npm publish - + Publish a package - + Usage: npm publish [<folder>] - + Options: [--tag <tag>] [--access <restricted|public>] [--dry-run] - + Run "npm help publish" for more info rebuild npm rebuild - + Rebuild a package - + Usage: npm rebuild [[<@scope>/]<name>[@<version>] ...] - + alias: rb - + Run "npm help rebuild" for more info repo npm repo - + Open package repository page in the browser - + Usage: npm repo [<pkgname> [<pkgname> ...]] - + Run "npm help repo" for more info restart npm restart - + Restart a package - + Usage: npm restart [-- <args>] - + Run "npm help restart" for more info root npm root - + Display npm root - + Usage: npm root - + Options: [-g|--global] - + Run "npm help root" for more info run-script npm run-script - + Run arbitrary package scripts - + Usage: npm run-script <command> [-- <args>] - + aliases: run, rum, urn - + Run "npm help run-script" for more info search npm search - + Search for pacakges - + Usage: npm search [search terms ...] - + Options: [-l|--long] [--json] [-p|--parseable] [--no-description] - + aliases: s, se, find - + Run "npm help search" for more info set npm set - + Set a value in the npm configuration - + Usage: npm set <key>=<value> [<key>=<value> ...] (See \`npm config\`) - + Run "npm help set" for more info set-script npm set-script - + Set tasks in the scripts section of package.json - + Usage: npm set-script [<script>] [<command>] - + Run "npm help set-script" for more info shrinkwrap npm shrinkwrap - + Lock down dependency versions for publication - + Usage: npm shrinkwrap - + Run "npm help shrinkwrap" for more info star npm star - + Mark your favorite packages - + Usage: npm star [<pkg>...] - + Run "npm help star" for more info stars npm stars - + View packages marked as favorites - + Usage: npm stars [<user>] - + Run "npm help stars" for more info start npm start - + Start a package - + Usage: npm start [-- <args>] - + Run "npm help start" for more info stop npm stop - + Stop a package - + Usage: npm stop [-- <args>] - + Run "npm help stop" for more info team npm team - + Manage organization teams and team memberships - + Usage: npm team create <scope:team> [--otp <otpcode>] npm team destroy <scope:team> [--otp <otpcode>] npm team add <scope:team> <user> [--otp <otpcode>] npm team rm <scope:team> <user> [--otp <otpcode>] npm team ls <scope>|<scope:team> - + Run "npm help team" for more info test npm test - + Test a package - + Usage: npm test [-- <args>] - + aliases: tst, t - + Run "npm help test" for more info token npm token - + Manage your authentication tokens - + Usage: npm token list npm token revoke <id|token> npm token create [--read-only] [--cidr=list] - + Run "npm help token" for more info uninstall npm uninstall - + Remove a package - + Usage: npm uninstall [<@scope>/]<pkg>... - + Options: [-S|--save|--no-save|--save-prod|--save-dev|--save-optional|--save-peer] - + aliases: un, unlink, remove, rm, r - + Run "npm help uninstall" for more info unpublish npm unpublish - + Remove a package from the registry - + Usage: npm unpublish [<@scope>/]<pkg>[@<version>] - + Run "npm help unpublish" for more info unstar npm unstar - + Remove an item from your favorite packages - + Usage: npm unstar [<pkg>...] - + Run "npm help unstar" for more info update npm update - + Update packages - + Usage: npm update [<pkg>...] - + Options: [-g|--global] - + aliases: up, upgrade, udpate - + Run "npm help update" for more info version npm version - + Bump a package version - + Usage: npm version [<newversion> | major | minor | patch | premajor | preminor | prepatch | prerelease [--preid=<prerelease-id>] | from-git] - + alias: verison - + Run "npm help version" for more info view npm view - + View registry info - + Usage: npm view [<@scope>/]<pkg>[@<version>] [<field>[.subfield]...] - + aliases: v, info, show - + Run "npm help view" for more info whoami npm whoami - + Display npm username - + Usage: npm whoami - + Options: [--registry <registry>] - + Run "npm help whoami" for more info Specify configs in the ini-formatted file: diff --git a/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js index b0b38de341f1b2..5c3813dd8db43b 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-tar.js-TAP.test.js @@ -20,7 +20,7 @@ bundle-dep name: my-cool-pkg version: 1.0.0 filename: my-cool-pkg-1.0.0.tgz -package size: 216 B +package size: 216 B unpacked size: 101 B shasum: a604258e06adecec0b18f48e901c5802f19f7dab integrity: sha512-fnN6NmI8DerTt[...]6rH17jx7OIFig== diff --git a/deps/npm/test/lib/config.js b/deps/npm/test/lib/config.js index 14cd816171da57..074db976517e51 100644 --- a/deps/npm/test/lib/config.js +++ b/deps/npm/test/lib/config.js @@ -54,6 +54,7 @@ const cliConfig = { const npm = { log: { + warn: () => null, info: () => null, enableProgress: () => null, disableProgress: () => null, @@ -93,6 +94,22 @@ t.test('config no args', t => { }) }) +t.test('config ignores workspaces', t => { + npm.log.warn = (title, msg) => { + t.equal(title, 'config', 'should warn with expected title') + t.equal( + msg, + 'This command does not support workspaces.', + 'should warn with unsupported option msg' + ) + } + config.execWorkspaces([], [], (err) => { + t.match(err, /usage instructions/, 'should not error out when workspaces are defined') + npm.log.warn = () => null + t.end() + }) +}) + t.test('config list', t => { t.plan(2) @@ -389,7 +406,7 @@ t.test('config set invalid key', t => { npm.config.validate = npmConfigValidate delete npm.config.save delete npm.config.set - delete npm.log.warn + npm.log.warn = () => null }) config.exec(['set', 'foo', 'bar'], (err) => { diff --git a/deps/npm/test/lib/install.js b/deps/npm/test/lib/install.js index b44452a69cc6f3..619f0bb346984f 100644 --- a/deps/npm/test/lib/install.js +++ b/deps/npm/test/lib/install.js @@ -32,7 +32,7 @@ test('should install using Arborist', (t) => { const npm = mockNpm({ config: { dev: true }, - flatOptions: { global: false }, + flatOptions: { global: false, auditLevel: 'low' }, globalDir: 'path/to/node_modules/', prefix: 'foo', }) @@ -42,7 +42,9 @@ test('should install using Arborist', (t) => { install.exec(['fizzbuzz'], er => { if (er) throw er - t.match(ARB_ARGS, { global: false, path: 'foo' }) + t.match(ARB_ARGS, + { global: false, path: 'foo', auditLevel: null }, + 'Arborist gets correct args and ignores auditLevel') t.equal(REIFY_CALLED, true, 'called reify') t.strictSame(SCRIPTS, [], 'no scripts when adding dep') t.end() diff --git a/deps/npm/test/lib/set-script.js b/deps/npm/test/lib/set-script.js index b6b6e2519f5bad..dc00fd374cc155 100644 --- a/deps/npm/test/lib/set-script.js +++ b/deps/npm/test/lib/set-script.js @@ -2,6 +2,43 @@ const test = require('tap') const requireInject = require('require-inject') const parseJSON = require('json-parse-even-better-errors') +test.test('completion', t => { + const SetScript = requireInject('../../lib/set-script.js') + const emptyDir = t.testdir() + t.test('already have a script name', async t => { + const setScript = new SetScript({localPrefix: emptyDir}) + const res = await setScript.completion({conf: {argv: {remain: ['npm', 'run', 'x']}}}) + t.equal(res, undefined) + t.end() + }) + t.test('no package.json', async t => { + const setScript = new SetScript({localPrefix: emptyDir}) + const res = await setScript.completion({conf: {argv: {remain: ['npm', 'run']}}}) + t.strictSame(res, []) + t.end() + }) + t.test('has package.json, no scripts', async t => { + const localPrefix = t.testdir({ + 'package.json': JSON.stringify({}), + }) + const setScript = new SetScript({localPrefix}) + const res = await setScript.completion({conf: {argv: {remain: ['npm', 'run']}}}) + t.strictSame(res, []) + t.end() + }) + t.test('has package.json, with scripts', async t => { + const localPrefix = t.testdir({ + 'package.json': JSON.stringify({ + scripts: { hello: 'echo hello', world: 'echo world' }, + }), + }) + const setScript = new SetScript({localPrefix}) + const res = await setScript.completion({conf: {argv: {remain: ['npm', 'run']}}}) + t.strictSame(res, ['hello', 'world']) + t.end() + }) + t.end() +}) test.test('fails on invalid arguments', (t) => { const SetScript = requireInject('../../lib/set-script.js', { npmlog: {}, diff --git a/deps/npm/test/lib/uninstall.js b/deps/npm/test/lib/uninstall.js index 5cb8a243ec19be..589191ea28e6bc 100644 --- a/deps/npm/test/lib/uninstall.js +++ b/deps/npm/test/lib/uninstall.js @@ -22,6 +22,7 @@ const uninstall = new Uninstall(npm) t.afterEach(cb => { npm.globalDir = '' npm.prefix = '' + npm.localPrefix = '' npm.flatOptions.global = false npm.flatOptions.prefix = '' cb() @@ -85,7 +86,7 @@ t.test('remove single installed lib', t => { const b = resolve(path, 'node_modules/b') t.ok(() => fs.statSync(b)) - npm.config.set('prefix', path) + npm.localPrefix = path uninstall.exec(['b'], err => { if (err) @@ -148,7 +149,7 @@ t.test('remove multiple installed libs', t => { t.ok(() => fs.statSync(a)) t.ok(() => fs.statSync(b)) - npm.config.set('prefix', path) + npm.localPrefix = path uninstall.exec(['b'], err => { if (err) @@ -196,7 +197,6 @@ t.test('no args global', t => { npm.localPrefix = resolve(path, 'projects', 'a') npm.globalDir = resolve(path, 'lib', 'node_modules') npm.config.set('global', true) - npm.config.set('prefix', path) const a = resolve(path, 'lib/node_modules/a') t.ok(() => fs.statSync(a)) diff --git a/deps/npm/test/lib/update.js b/deps/npm/test/lib/update.js index 695218a7f69cd9..780484afbeb03e 100644 --- a/deps/npm/test/lib/update.js +++ b/deps/npm/test/lib/update.js @@ -10,6 +10,7 @@ const config = { const noop = () => null const npm = mockNpm({ globalDir: '', + log: noop, config, prefix: '', }) @@ -38,7 +39,7 @@ t.test('no args', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...npm.flatOptions, path: npm.prefix, log: noop }, 'should call arborist contructor with expected args' ) } @@ -72,7 +73,7 @@ t.test('with args', t => { constructor (args) { t.deepEqual( args, - { ...npm.flatOptions, path: npm.prefix }, + { ...npm.flatOptions, path: npm.prefix, log: noop }, 'should call arborist contructor with expected args' ) } @@ -140,7 +141,7 @@ t.test('update --global', t => { const { path, ...opts } = args t.deepEqual( opts, - npm.flatOptions, + { ...npm.flatOptions, log: noop }, 'should call arborist contructor with expected options' ) diff --git a/deps/npm/test/lib/utils/config/definitions.js b/deps/npm/test/lib/utils/config/definitions.js index 3169feefb8f914..b6e1d1d01529e1 100644 --- a/deps/npm/test/lib/utils/config/definitions.js +++ b/deps/npm/test/lib/utils/config/definitions.js @@ -194,18 +194,26 @@ t.test('flatteners that populate flat.omit array', t => { // ignored if setting is not dev or development obj.also = 'ignored' definitions.also.flatten('also', obj, flat) - t.strictSame(obj, {also: 'ignored'}, 'nothing done') - t.strictSame(flat, {}, 'nothing done') + t.strictSame(obj, {also: 'ignored', omit: [], include: []}, 'nothing done') + t.strictSame(flat, {omit: []}, 'nothing done') obj.also = 'development' definitions.also.flatten('also', obj, flat) - t.strictSame(obj, { also: 'development', include: ['dev'] }, 'marked dev as included') + t.strictSame(obj, { + also: 'development', + omit: [], + include: ['dev'], + }, 'marked dev as included') t.strictSame(flat, { omit: [] }, 'nothing omitted, so nothing changed') obj.omit = ['dev', 'optional'] obj.include = [] definitions.also.flatten('also', obj, flat) - t.strictSame(obj, { also: 'development', omit: ['dev', 'optional'], include: ['dev'] }, 'marked dev as included') + t.strictSame(obj, { + also: 'development', + omit: ['optional'], + include: ['dev'], + }, 'marked dev as included') t.strictSame(flat, { omit: ['optional'] }, 'removed dev from omit') t.end() }) @@ -237,7 +245,7 @@ t.test('flatteners that populate flat.omit array', t => { const flat = {} const obj = { only: 'asdf' } definitions.only.flatten('only', obj, flat) - t.strictSame(flat, {}, 'ignored if value is not production') + t.strictSame(flat, { omit: [] }, 'ignored if value is not production') obj.only = 'prod' definitions.only.flatten('only', obj, flat) @@ -256,18 +264,30 @@ t.test('flatteners that populate flat.omit array', t => { const obj = { optional: null } definitions.optional.flatten('optional', obj, flat) - t.strictSame(obj, { optional: null }, 'do nothing by default') - t.strictSame(flat, {}, 'do nothing by default') + t.strictSame(obj, { + optional: null, + omit: [], + include: [], + }, 'do nothing by default') + t.strictSame(flat, { omit: [] }, 'do nothing by default') obj.optional = true definitions.optional.flatten('optional', obj, flat) - t.strictSame(obj, {include: ['optional'], optional: true}, 'include optional when set') + t.strictSame(obj, { + omit: [], + optional: true, + include: ['optional'], + }, 'include optional when set') t.strictSame(flat, {omit: []}, 'nothing to omit in flatOptions') delete obj.include obj.optional = false definitions.optional.flatten('optional', obj, flat) - t.strictSame(obj, {omit: ['optional'], optional: false}, 'omit optional when set false') + t.strictSame(obj, { + omit: ['optional'], + optional: false, + include: [], + }, 'omit optional when set false') t.strictSame(flat, {omit: ['optional']}, 'omit optional when set false') t.end() @@ -277,25 +297,49 @@ t.test('flatteners that populate flat.omit array', t => { const flat = {} const obj = {production: true} definitions.production.flatten('production', obj, flat) - t.strictSame(obj, {production: true, omit: ['dev']}, '--production sets --omit=dev') + t.strictSame(obj, { + production: true, + omit: ['dev'], + include: [], + }, '--production sets --omit=dev') t.strictSame(flat, {omit: ['dev']}, '--production sets --omit=dev') delete obj.omit obj.production = false delete flat.omit definitions.production.flatten('production', obj, flat) - t.strictSame(obj, {production: false}, '--no-production has no effect') - t.strictSame(flat, {}, '--no-production has no effect') + t.strictSame(obj, { + production: false, + include: ['dev'], + omit: [], + }, '--no-production explicitly includes dev') + t.strictSame(flat, { omit: [] }, '--no-production has no effect') obj.production = true obj.include = ['dev'] definitions.production.flatten('production', obj, flat) - t.strictSame(obj, {production: true, include: ['dev'], omit: ['dev']}, 'omit and include dev') + t.strictSame(obj, { + production: true, + include: ['dev'], + omit: [], + }, 'omit and include dev') t.strictSame(flat, {omit: []}, 'do not omit dev when included') t.end() }) + t.test('dev', t => { + const flat = {} + const obj = {dev: true} + definitions.dev.flatten('dev', obj, flat) + t.strictSame(obj, { + dev: true, + omit: [], + include: ['dev'], + }) + t.end() + }) + t.end() }) @@ -695,3 +739,18 @@ t.test('user-agent', t => { t.equal(flat.userAgent, expectCI) t.end() }) + +t.test('save-prefix', t => { + const obj = { + 'save-exact': true, + 'save-prefix': '~1.2.3', + } + const flat = {} + definitions['save-prefix'] + .flatten('save-prefix', { ...obj, 'save-exact': true }, flat) + t.strictSame(flat, { savePrefix: '' }) + definitions['save-prefix'] + .flatten('save-prefix', { ...obj, 'save-exact': false }, flat) + t.strictSame(flat, { savePrefix: '~1.2.3' }) + t.end() +}) diff --git a/deps/npm/test/lib/utils/config/flatten.js b/deps/npm/test/lib/utils/config/flatten.js index 9fac0820cb0ea9..6fc91b4847e386 100644 --- a/deps/npm/test/lib/utils/config/flatten.js +++ b/deps/npm/test/lib/utils/config/flatten.js @@ -6,6 +6,8 @@ delete process.env.NODE process.execPath = '/path/to/node' const obj = { + 'save-exact': true, + 'save-prefix': 'ignored', 'save-dev': true, '@foobar:registry': 'https://foo.bar.com/', '//foo.bar.com:_authToken': 'foobarbazquuxasdf', @@ -15,6 +17,8 @@ const obj = { const flat = flatten(obj) t.strictSame(flat, { saveType: 'dev', + saveExact: true, + savePrefix: '', '@foobar:registry': 'https://foo.bar.com/', '//foo.bar.com:_authToken': 'foobarbazquuxasdf', npmBin: '/path/to/npm', @@ -26,6 +30,8 @@ t.strictSame(flat, { process.env.NODE = '/usr/local/bin/node.exe' flatten({ 'save-dev': false }, flat) t.strictSame(flat, { + saveExact: true, + savePrefix: '', '@foobar:registry': 'https://foo.bar.com/', '//foo.bar.com:_authToken': 'foobarbazquuxasdf', npmBin: '/path/to/npm', From 76ebc4bbd91bb83d09c8b0bdaab69d06846d88cb Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Wed, 24 Mar 2021 20:01:44 -0700 Subject: [PATCH 58/85] test: increase wiggle room for memory in test-worker-resource-limits PR-URL: https://github.com/nodejs/node/pull/37901 Fixes: https://github.com/nodejs/node/issues/37844 Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> --- test/parallel/test-worker-resource-limits.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/parallel/test-worker-resource-limits.js b/test/parallel/test-worker-resource-limits.js index 0c168bbea1a488..ffda452f6c6335 100644 --- a/test/parallel/test-worker-resource-limits.js +++ b/test/parallel/test-worker-resource-limits.js @@ -35,9 +35,10 @@ if (!process.env.HAS_STARTED_WORKER) { assert.deepStrictEqual(resourceLimits, testResourceLimits); const array = []; while (true) { - // Leave 10 % wiggle room here. + // Leave 10% wiggle room here, and 20% on debug builds. + const wiggleRoom = common.buildType === 'Release' ? 1.1 : 1.2; const usedMB = v8.getHeapStatistics().used_heap_size / 1024 / 1024; - assert(usedMB < resourceLimits.maxOldGenerationSizeMb * 1.1); + assert(usedMB < resourceLimits.maxOldGenerationSizeMb * wiggleRoom); let seenSpaces = 0; for (const { space_name, space_size } of v8.getHeapSpaceStatistics()) { From a6f21e2cfc57ae787f1f26c4c64a2c4db2ae6d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= <tniessen@users.noreply.github.com> Date: Wed, 24 Mar 2021 19:27:34 +0100 Subject: [PATCH 59/85] doc: fix wording in outgoingMessage.write PR-URL: https://github.com/nodejs/node/pull/37894 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- doc/api/http.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/api/http.md b/doc/api/http.md index 65fb0f47bf8023..d3215a5f4b8f16 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -2589,7 +2589,7 @@ This method handles the raw body of the HTTP message and has nothing to do with higher-level multi-part body encodings that may be used. If it is the first call to this method of a message, it will send the -buffered header first, then flush the the `chunk` as described above. +buffered header first, then flush the `chunk` as described above. The second and successive calls to this method will assume the data will be streamed and send the new data separately. It means that the response From e84252b35dda21c20dc5bfe034bf827e0823ef3d Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 07:20:40 -0700 Subject: [PATCH 60/85] doc: reduce header nesting in async_hooks.md Maximum header level reduced to 5. PR-URL: https://github.com/nodejs/node/pull/37839 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> --- doc/api/async_hooks.md | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/doc/api/async_hooks.md b/doc/api/async_hooks.md index 04b0929f91baae..726f30c1cd83df 100644 --- a/doc/api/async_hooks.md +++ b/doc/api/async_hooks.md @@ -25,9 +25,7 @@ as the abstract concept that is a resource. If [`Worker`][]s are used, each thread has an independent `async_hooks` interface, and each thread will use a new set of async IDs. -## Public API - -### Overview +## Overview Following is a simple overview of the public API. @@ -79,7 +77,7 @@ function destroy(asyncId) { } function promiseResolve(asyncId) { } ``` -#### `async_hooks.createHook(callbacks)` +## `async_hooks.createHook(callbacks)` <!-- YAML added: v8.1.0 @@ -133,7 +131,7 @@ Because promises are asynchronous resources whose lifecycle is tracked via the async hooks mechanism, the `init()`, `before()`, `after()`, and `destroy()` callbacks *must not* be async functions that return promises. -##### Error handling +### Error handling If any `AsyncHook` callbacks throw, the application will print the stack trace and exit. The exit path does follow that of an uncaught exception, but @@ -150,7 +148,7 @@ future. This is subject to change in the future if a comprehensive analysis is performed to ensure an exception can follow the normal control flow without unintentional side effects. -##### Printing in AsyncHooks callbacks +### Printing in AsyncHooks callbacks Because printing to the console is an asynchronous operation, `console.log()` will cause the AsyncHooks callbacks to be called. Using `console.log()` or @@ -176,12 +174,12 @@ provided by AsyncHooks itself. The logging should then be skipped when it was the logging itself that caused AsyncHooks callback to call. By doing this the otherwise infinite recursion is broken. -### Class: `AsyncHook` +## Class: `AsyncHook` The class `AsyncHook` exposes an interface for tracking lifetime events of asynchronous operations. -#### `asyncHook.enable()` +### `asyncHook.enable()` * Returns: {AsyncHook} A reference to `asyncHook`. @@ -197,7 +195,7 @@ const async_hooks = require('async_hooks'); const hook = async_hooks.createHook(callbacks).enable(); ``` -#### `asyncHook.disable()` +### `asyncHook.disable()` * Returns: {AsyncHook} A reference to `asyncHook`. @@ -207,13 +205,13 @@ be called again until enabled. For API consistency `disable()` also returns the `AsyncHook` instance. -#### Hook callbacks +### Hook callbacks Key events in the lifetime of asynchronous events have been categorized into four areas: instantiation, before/after the callback is called, and when the instance is destroyed. -##### `init(asyncId, type, triggerAsyncId, resource)` +#### `init(asyncId, type, triggerAsyncId, resource)` * `asyncId` {number} A unique ID for the async resource. * `type` {string} The type of the async resource. @@ -240,7 +238,7 @@ clearTimeout(setTimeout(() => {}, 10)); Every new resource is assigned an ID that is unique within the scope of the current Node.js instance. -###### `type` +##### `type` The `type` is a string identifying the type of resource that caused `init` to be called. Generally, it will correspond to the name of the @@ -263,7 +261,7 @@ It is possible to have type name collisions. Embedders are encouraged to use unique prefixes, such as the npm package name, to prevent collisions when listening to the hooks. -###### `triggerAsyncId` +##### `triggerAsyncId` `triggerAsyncId` is the `asyncId` of the resource that caused (or "triggered") the new resource to initialize and that caused `init` to call. This is different @@ -302,7 +300,7 @@ that information, it would be impossible to link resources together in terms of what caused them to be created, so `triggerAsyncId` is given the task of propagating what resource is responsible for the new resource's existence. -###### `resource` +##### `resource` `resource` is an object that represents the actual async resource that has been initialized. This can contain useful information that can vary based on @@ -316,7 +314,7 @@ could contain the SQL query being executed. In some cases the resource object is reused for performance reasons, it is thus not safe to use it as a key in a `WeakMap` or add properties to it. -###### Asynchronous context example +##### Asynchronous context example The following is an example with additional information about the calls to `init` between the `before` and `after` calls, specifically what the @@ -415,7 +413,7 @@ TCPSERVERWRAP(5) Timeout(7) ``` -##### `before(asyncId)` +#### `before(asyncId)` * `asyncId` {number} @@ -432,7 +430,7 @@ asynchronous resources like a TCP server will typically call the `before` callback multiple times, while other operations like `fs.open()` will call it only once. -##### `after(asyncId)` +#### `after(asyncId)` * `asyncId` {number} @@ -442,7 +440,7 @@ If an uncaught exception occurs during execution of the callback, then `after` will run *after* the `'uncaughtException'` event is emitted or a `domain`'s handler runs. -##### `destroy(asyncId)` +#### `destroy(asyncId)` * `asyncId` {number} @@ -454,7 +452,7 @@ made to the `resource` object passed to `init` it is possible that `destroy` will never be called, causing a memory leak in the application. If the resource does not depend on garbage collection, then this will not be an issue. -##### `promiseResolve(asyncId)` +#### `promiseResolve(asyncId)` <!-- YAML added: v8.6.0 @@ -485,7 +483,7 @@ init for PROMISE with id 6, trigger id: 5 # the Promise returned by then() after 6 ``` -#### `async_hooks.executionAsyncResource()` +### `async_hooks.executionAsyncResource()` <!-- YAML added: @@ -543,7 +541,7 @@ const server = createServer((req, res) => { }).listen(3000); ``` -#### `async_hooks.executionAsyncId()` +### `async_hooks.executionAsyncId()` <!-- YAML added: v8.1.0 @@ -584,7 +582,7 @@ const server = net.createServer((conn) => { Promise contexts may not get precise `executionAsyncIds` by default. See the section on [promise execution tracking][]. -#### `async_hooks.triggerAsyncId()` +### `async_hooks.triggerAsyncId()` * Returns: {number} The ID of the resource responsible for calling the callback that is currently being executed. From 3925458df787f3bd05f0676ac08f41a749635d96 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Sat, 20 Mar 2021 07:21:36 -0700 Subject: [PATCH 61/85] doc,tools: use only one level 1 header per page Increment the header levels from markdown files when producing HTML documents. This is both better semantically (as the two h1 headers in current docs are not actually equivalent level semantically--the second belongs below/inside the first) and better for accessibility. (It is valid HTML to have multiple h1 headers in a document, but it can be bad for screen reader experience.) PR-URL: https://github.com/nodejs/node/pull/37839 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> --- test/doctool/test-doctool-html.js | 38 +++++++++++++++---------------- tools/doc/html.js | 17 ++++++++++---- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/test/doctool/test-doctool-html.js b/test/doctool/test-doctool-html.js index e3d0d4251dcc7d..dd651a66c1e94f 100644 --- a/test/doctool/test-doctool-html.js +++ b/test/doctool/test-doctool-html.js @@ -60,25 +60,25 @@ function toHTML({ input, filename, nodeVersion, versions }) { const testData = [ { file: fixtures.path('order_of_end_tags_5873.md'), - html: '<h3>Static method: Buffer.from(array) <span> ' + + html: '<h4>Static method: Buffer.from(array) <span> ' + '<a class="mark" href="#foo_static_method_buffer_from_array" ' + - 'id="foo_static_method_buffer_from_array">#</a> </span> </h3>' + + 'id="foo_static_method_buffer_from_array">#</a> </span> </h4>' + '<ul><li><code>array</code><a ' + 'href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/' + 'Reference/Global_Objects/Array" class="type"><Array></a></li></ul>' }, { file: fixtures.path('doc_with_yaml.md'), - html: '<h1>Sample Markdown with YAML info' + + html: '<h2>Sample Markdown with YAML info' + '<span><a class="mark" href="#foo_sample_markdown_with_yaml_info" ' + - ' id="foo_sample_markdown_with_yaml_info">#</a></span></h1>' + - '<section><h2>Foobar<span><a class="mark" href="#foo_foobar" ' + - 'id="foo_foobar">#</a></span></h2>' + + ' id="foo_sample_markdown_with_yaml_info">#</a></span></h2>' + + '<section><h3>Foobar<span><a class="mark" href="#foo_foobar" ' + + 'id="foo_foobar">#</a></span></h3>' + '<div class="api_metadata"><span>Added in: v1.0.0</span></div> ' + '<p>Describe <code>Foobar</code> in more detail here.</p>' + '</section><section>' + - '<h2>Foobar II<span><a class="mark" href="#foo_foobar_ii" ' + - 'id="foo_foobar_ii">#</a></span></h2><div class="api_metadata">' + + '<h3>Foobar II<span><a class="mark" href="#foo_foobar_ii" ' + + 'id="foo_foobar_ii">#</a></span></h3><div class="api_metadata">' + '<details class="changelog"><summary>History</summary>' + '<table><tbody><tr><th>Version</th><th>Changes</th></tr>' + '<tr><td>v5.3.0, v4.2.0</td>' + @@ -88,15 +88,15 @@ const testData = [ '<p>Describe <code>Foobar II</code> in more detail here.' + '<a href="http://man7.org/linux/man-pages/man1/fg.1.html"><code>fg(1)' + '</code></a></p></section><section>' + - '<h2>Deprecated thingy<span><a class="mark" ' + + '<h3>Deprecated thingy<span><a class="mark" ' + 'href="#foo_deprecated_thingy" id="foo_deprecated_thingy">#</a>' + - '</span></h2><div class="api_metadata"><span>Added in: v1.0.0</span>' + + '</span></h3><div class="api_metadata"><span>Added in: v1.0.0</span>' + '<span>Deprecated since: v2.0.0</span></div><p>Describe ' + '<code>Deprecated thingy</code> in more detail here.' + '<a href="http://man7.org/linux/man-pages/man1/fg.1p.html"><code>fg(1p)' + '</code></a></p></section><section>' + - '<h2>Something<span><a class="mark" href="#foo_something' + - '" id="foo_something">#</a></span></h2> ' + + '<h3>Something<span><a class="mark" href="#foo_something' + + '" id="foo_something">#</a></span></h3> ' + '<!-- This is not a metadata comment --> ' + '<p>Describe <code>Something</code> in more detail here. </p></section>' }, @@ -111,19 +111,19 @@ const testData = [ }, { file: fixtures.path('document_with_links.md'), - html: '<h1>Usage and Example<span><a class="mark"' + + html: '<h2>Usage and Example<span><a class="mark"' + 'href="#foo_usage_and_example" id="foo_usage_and_example">#</a>' + - '</span></h1><section><h2>Usage<span><a class="mark" href="#foo_usage"' + - 'id="foo_usage">#</a></span></h2><p><code>node \\[options\\] index.js' + + '</span></h2><section><h3>Usage<span><a class="mark" href="#foo_usage"' + + 'id="foo_usage">#</a></span></h3><p><code>node \\[options\\] index.js' + '</code></p><p>Please see the<a href="cli.html#cli-options">' + 'Command Line Options</a>document for more information.</p>' + - '</section><section><h2>' + + '</section><section><h3>' + 'Example<span><a class="mark" href="#foo_example" id="foo_example">' + - '#</a></span></h2><p>An example of a<a href="example.html">' + + '#</a></span></h3><p>An example of a<a href="example.html">' + 'webserver</a>written with Node.js which responds with<code>' + '\'Hello, World!\'</code>:</p></section><section>' + - '<h2>See also<span><a class="mark"' + - 'href="#foo_see_also" id="foo_see_also">#</a></span></h2><p>Check' + + '<h3>See also<span><a class="mark"' + + 'href="#foo_see_also" id="foo_see_also">#</a></span></h3><p>Check' + 'out also<a href="https://nodejs.org/">this guide</a></p></section>' }, { diff --git a/tools/doc/html.js b/tools/doc/html.js index 67157ba6b5fb09..9089ce7e443232 100644 --- a/tools/doc/html.js +++ b/tools/doc/html.js @@ -66,10 +66,19 @@ const gtocHTML = unified() const templatePath = path.join(docPath, 'template.html'); const template = fs.readFileSync(templatePath, 'utf8'); -function wrapSections(content) { +function processContent(content) { + content = content.toString(); + // Increment header tag levels to avoid multiple h1 tags in a doc. + // This means we can't already have an <h6>. + if (content.includes('<h6>')) { + throw new Error('Cannot increment a level 6 header'); + } + // `++level` to convert the string to a number and increment it. + content = content.replace(/(?<=<\/?h)[1-5](?=[^<>]*>)/g, (level) => ++level); + // Wrap h3 tags in section tags. let firstTime = true; - return content.toString() - .replace(/<h2/g, (heading) => { + return content + .replace(/<h3/g, (heading) => { if (firstTime) { firstTime = false; return '<section>' + heading; @@ -91,7 +100,7 @@ function toHTML({ input, content, filename, nodeVersion, versions }) { .replace('__GTOC__', gtocHTML.replace( `class="nav-${id}"`, `class="nav-${id} active"`)) .replace('__EDIT_ON_GITHUB__', editOnGitHub(filename)) - .replace('__CONTENT__', wrapSections(content)); + .replace('__CONTENT__', processContent(content)); const docCreated = input.match( /<!--\s*introduced_in\s*=\s*v([0-9]+)\.([0-9]+)\.[0-9]+\s*-->/); From 50fc6b9df09f05f85550b5ebe5b5568a8e9086f3 Mon Sep 17 00:00:00 2001 From: Filip Skokan <panva.ip@gmail.com> Date: Fri, 19 Mar 2021 21:24:57 +0100 Subject: [PATCH 62/85] crypto: clear errors in SignTraits::DeriveBits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37820 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> --- src/crypto/crypto_sig.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crypto/crypto_sig.cc b/src/crypto/crypto_sig.cc index 24b1a7f4315f76..1988aeb1c05d93 100644 --- a/src/crypto/crypto_sig.cc +++ b/src/crypto/crypto_sig.cc @@ -829,6 +829,7 @@ bool SignTraits::DeriveBits( Environment* env, const SignConfiguration& params, ByteSource* out) { + ClearErrorOnReturn clear_error_on_return; EVPMDPointer context(EVP_MD_CTX_new()); EVP_PKEY_CTX* ctx = nullptr; From 2da532cef8b2159290c2bcc4f6fb61c9697ed738 Mon Sep 17 00:00:00 2001 From: Stephen Belanger <stephen.belanger@datadoghq.com> Date: Mon, 22 Mar 2021 16:27:31 -0700 Subject: [PATCH 63/85] src: report idle time correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change, the V8 profiler will attribute any time between prepare and check cycles, except any entrances to InternalCallbackScope, to be "idle" time. All callbacks, microtasks, and timers will be marked as not idle. The one exception is native modules which don't use the MakeCallback helper, but those are already broken anyway for async context tracking so we should just encourage broken modules to be fixed. PR-URL: https://github.com/nodejs/node/pull/37868 Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> --- src/api/callback.cc | 4 +++ src/env.cc | 24 +++++++++++++ src/env.h | 4 +++ test/parallel/test-inspector-has-idle.js | 43 ++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 test/parallel/test-inspector-has-idle.js diff --git a/src/api/callback.cc b/src/api/callback.cc index a7b23dd4924baf..8aed3fb8d31425 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -60,6 +60,8 @@ InternalCallbackScope::InternalCallbackScope(Environment* env, // If you hit this assertion, you forgot to enter the v8::Context first. CHECK_EQ(Environment::GetCurrent(env->isolate()), env); + env->isolate()->SetIdle(false); + env->async_hooks()->push_async_context( async_context_.async_id, async_context_.trigger_async_id, object); @@ -81,6 +83,8 @@ void InternalCallbackScope::Close() { if (closed_) return; closed_ = true; + auto idle = OnScopeLeave([&]() { env_->isolate()->SetIdle(true); }); + if (!env_->can_call_into_js()) return; auto perform_stopping_check = [&]() { if (env_->is_stopping()) { diff --git a/src/env.cc b/src/env.cc index 119706129713bd..abe9bc029d133a 100644 --- a/src/env.cc +++ b/src/env.cc @@ -555,6 +555,13 @@ void Environment::InitializeLibuv() { uv_check_start(immediate_check_handle(), CheckImmediate); + // Inform V8's CPU profiler when we're idle. The profiler is sampling-based + // but not all samples are created equal; mark the wall clock time spent in + // epoll_wait() and friends so profiling tools can filter it out. The samples + // still end up in v8.log but with state=IDLE rather than state=EXTERNAL. + uv_prepare_init(event_loop(), &idle_prepare_handle_); + uv_check_init(event_loop(), &idle_check_handle_); + uv_async_init( event_loop(), &task_queues_async_, @@ -565,6 +572,8 @@ void Environment::InitializeLibuv() { Context::Scope context_scope(env->context()); env->RunAndClearNativeImmediates(); }); + uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_)); + uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_)); uv_unref(reinterpret_cast<uv_handle_t*>(&task_queues_async_)); { @@ -581,6 +590,8 @@ void Environment::InitializeLibuv() { // the one environment per process setup, but will be called in // FreeEnvironment. RegisterHandleCleanups(); + + StartProfilerIdleNotifier(); } void Environment::ExitEnv() { @@ -608,6 +619,8 @@ void Environment::RegisterHandleCleanups() { register_handle(reinterpret_cast<uv_handle_t*>(timer_handle())); register_handle(reinterpret_cast<uv_handle_t*>(immediate_check_handle())); register_handle(reinterpret_cast<uv_handle_t*>(immediate_idle_handle())); + register_handle(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_)); + register_handle(reinterpret_cast<uv_handle_t*>(&idle_check_handle_)); register_handle(reinterpret_cast<uv_handle_t*>(&task_queues_async_)); } @@ -639,6 +652,17 @@ void Environment::CleanupHandles() { } } +void Environment::StartProfilerIdleNotifier() { + uv_prepare_start(&idle_prepare_handle_, [](uv_prepare_t* handle) { + Environment* env = ContainerOf(&Environment::idle_prepare_handle_, handle); + env->isolate()->SetIdle(true); + }); + uv_check_start(&idle_check_handle_, [](uv_check_t* handle) { + Environment* env = ContainerOf(&Environment::idle_check_handle_, handle); + env->isolate()->SetIdle(false); + }); +} + void Environment::PrintSyncTrace() const { if (!trace_sync_io_) return; diff --git a/src/env.h b/src/env.h index fa61450d0945d5..2dabdbb296ace8 100644 --- a/src/env.h +++ b/src/env.h @@ -1057,6 +1057,8 @@ class Environment : public MemoryRetainer { inline void AssignToContext(v8::Local<v8::Context> context, const ContextInfo& info); + void StartProfilerIdleNotifier(); + inline v8::Isolate* isolate() const; inline uv_loop_t* event_loop() const; inline void TryLoadAddon( @@ -1408,6 +1410,8 @@ class Environment : public MemoryRetainer { uv_timer_t timer_handle_; uv_check_t immediate_check_handle_; uv_idle_t immediate_idle_handle_; + uv_prepare_t idle_prepare_handle_; + uv_check_t idle_check_handle_; uv_async_t task_queues_async_; int64_t task_queues_async_refs_ = 0; diff --git a/test/parallel/test-inspector-has-idle.js b/test/parallel/test-inspector-has-idle.js new file mode 100644 index 00000000000000..c14590353e6780 --- /dev/null +++ b/test/parallel/test-inspector-has-idle.js @@ -0,0 +1,43 @@ +'use strict'; +const common = require('../common'); + +common.skipIfInspectorDisabled(); + +const assert = require('assert'); +const { Session } = require('inspector'); +const { promisify } = require('util'); + +const sleep = promisify(setTimeout); + +async function test() { + const inspector = new Session(); + inspector.connect(); + + inspector.post('Profiler.enable'); + inspector.post('Profiler.start'); + + await sleep(1000); + + const { profile } = await new Promise((resolve, reject) => { + inspector.post('Profiler.stop', (err, params) => { + if (err) return reject(err); + resolve(params); + }); + }); + + let hasIdle = false; + for (const node of profile.nodes) { + if (node.callFrame.functionName === '(idle)') { + hasIdle = true; + break; + } + } + assert(hasIdle); + + inspector.post('Profiler.disable'); + inspector.disconnect(); +} + +test().then(common.mustCall(() => { + console.log('Done!'); +})); From ca93e5278368875ebc94636fe78d1a8016abddd4 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Tue, 23 Mar 2021 16:15:34 -0700 Subject: [PATCH 64/85] tools: simplify eslint comma-dangle configuration (tools) Remove the comma-dangle settings in tools/.eslintrc.yaml. They are duplicated in .eslintrc.js. PR-URL: https://github.com/nodejs/node/pull/37883 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> --- tools/.eslintrc.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tools/.eslintrc.yaml b/tools/.eslintrc.yaml index fbc3738465169f..b98c59a15c1b77 100644 --- a/tools/.eslintrc.yaml +++ b/tools/.eslintrc.yaml @@ -8,12 +8,6 @@ rules: - properties: 'never' ignoreDestructuring: true allow: ['child_process'] - comma-dangle: - - error - - arrays: 'always-multiline' - objects: 'only-multiline' - imports: 'only-multiline' - exports: 'only-multiline' no-unused-vars: [error, { args: 'after-used' }] prefer-arrow-callback: error no-var: error From 364c8ac40d7d16440a7b737a74500ae22cf4eb62 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Tue, 23 Mar 2021 23:10:48 -0700 Subject: [PATCH 65/85] doc: update GOVERNANCE.md for TSC Charter changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TSC charter changes can be approved by the Cross-Project Council. PR-URL: https://github.com/nodejs/node/pull/37888 Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- GOVERNANCE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GOVERNANCE.md b/GOVERNANCE.md index f47fb56e5b0cfb..6ff4d1f5a28ecd 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -82,7 +82,7 @@ The current list of TSC members is in [the project README](./README.md#current-project-team-members). The [TSC Charter][] governs the operations of the TSC. All changes to the -Charter need approval by the OpenJS Foundation Board of Directors. +Charter need approval by the OpenJS Foundation Cross-Project Council (CPC). ### TSC meetings From a5bf7de6eb1acd1d1c10a4c8ad1331626ee23703 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Tue, 23 Mar 2021 15:44:21 +0100 Subject: [PATCH 66/85] http2: fix setting options before handle exists Currently, when a JS Http2Session object is created, we have to handle the situation in which the native object corresponding to it does not yet exist. As part of that, we create a typed array for storing options that are passed through the `AliasedStruct` mechanism, and up until now, we copied that typed array over the native one once the native one was available. This was not good, because it was overwriting the defaults that were set during construction of the native typed array with zeroes. In order to fix this, create a wrapper for the JS-created typed array that keeps track of which fields were changed, which enables us to only overwrite fields that were intentionally changed on the JS side. It is surprising that this behavior was not tested (which is, guessing from the commit history around these features, my fault). The subseqeuent commit introduces a test that would fail without this change. PR-URL: https://github.com/nodejs/node/pull/37875 Fixes: https://github.com/nodejs/node/issues/37849 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- lib/internal/http2/core.js | 54 ++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index bb1cc9e6c56fa2..bfdb45ab7ba216 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -19,14 +19,16 @@ const { Promise, PromisePrototypeCatch, ReflectApply, + ReflectGet, ReflectGetPrototypeOf, + ReflectSet, RegExpPrototypeTest, SafeArrayIterator, SafeMap, SafeSet, StringPrototypeSlice, Symbol, - TypedArrayPrototypeSet, + TypedArrayPrototypeGetLength, Uint32Array, Uint8Array, } = primordials; @@ -959,6 +961,36 @@ const validateSettings = hideStackFrames((settings) => { } }); +// Wrap a typed array in a proxy, and allow selectively copying the entries +// that have explicitly been set to another typed array. +function trackAssignmentsTypedArray(typedArray) { + const typedArrayLength = TypedArrayPrototypeGetLength(typedArray); + const modifiedEntries = new Uint8Array(typedArrayLength); + + function copyAssigned(target) { + for (let i = 0; i < typedArrayLength; i++) { + if (modifiedEntries[i]) { + target[i] = typedArray[i]; + } + } + } + + return new Proxy(typedArray, { + get(obj, prop, receiver) { + if (prop === 'copyAssigned') { + return copyAssigned; + } + return ReflectGet(obj, prop, receiver); + }, + set(obj, prop, value) { + if (`${+prop}` === prop) { + modifiedEntries[prop] = 1; + } + return ReflectSet(obj, prop, value); + } + }); +} + // Creates the internal binding.Http2Session handle for an Http2Session // instance. This occurs only after the socket connection has been // established. Note: the binding.Http2Session will take over ownership @@ -989,10 +1021,13 @@ function setupHandle(socket, type, options) { handle.consume(socket._handle); this[kHandle] = handle; - if (this[kNativeFields]) - TypedArrayPrototypeSet(handle.fields, this[kNativeFields]); - else - this[kNativeFields] = handle.fields; + if (this[kNativeFields]) { + // If some options have already been set before the handle existed, copy + // those (and only those) that have manually been set over. + this[kNativeFields].copyAssigned(handle.fields); + } + + this[kNativeFields] = handle.fields; if (socket.encrypted) { this[kAlpnProtocol] = socket.alpnProtocol; @@ -1044,7 +1079,8 @@ function cleanupSession(session) { session[kProxySocket] = undefined; session[kSocket] = undefined; session[kHandle] = undefined; - session[kNativeFields] = new Uint8Array(kSessionUint8FieldCount); + session[kNativeFields] = trackAssignmentsTypedArray( + new Uint8Array(kSessionUint8FieldCount)); if (handle) handle.ondone = null; if (socket) { @@ -1212,8 +1248,10 @@ class Http2Session extends EventEmitter { setupFn(); } - if (!this[kNativeFields]) - this[kNativeFields] = new Uint8Array(kSessionUint8FieldCount); + if (!this[kNativeFields]) { + this[kNativeFields] = trackAssignmentsTypedArray( + new Uint8Array(kSessionUint8FieldCount)); + } this.on('newListener', sessionListenerAdded); this.on('removeListener', sessionListenerRemoved); From 1c043272ea775621156bfa4e78718108803b3315 Mon Sep 17 00:00:00 2001 From: Anna Henningsen <anna@addaleax.net> Date: Tue, 23 Mar 2021 15:48:41 +0100 Subject: [PATCH 67/85] http2: treat non-EOF empty frames like other invalid frames Use the existing mechanism that we have to keep track of invalid frames for treating this specific kind of invalid frame. The commit that originally introduced this check was 695e38be69a780417, which was supposed to proected against CVE-2019-9518, which in turn was specifically about a *flood* of empty data frames. While these are still invalid frames either way, it makes sense to be forgiving here and just treat them like other invalid frames, i.e. to allow a small (configurable) number of them. Fixes: https://github.com/nodejs/node/issues/37849 PR-URL: https://github.com/nodejs/node/pull/37875 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- src/node_http2.cc | 6 ++- test/fixtures/emptyframe.http2 | Bin 0 -> 4233 bytes .../test-http2-empty-frame-without-eof.js | 39 ++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/emptyframe.http2 create mode 100644 test/parallel/test-http2-empty-frame-without-eof.js diff --git a/src/node_http2.cc b/src/node_http2.cc index ccff6dc4ca60c1..004bc8df22a567 100644 --- a/src/node_http2.cc +++ b/src/node_http2.cc @@ -1285,7 +1285,11 @@ int Http2Session::HandleDataFrame(const nghttp2_frame* frame) { frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { stream->EmitRead(UV_EOF); } else if (frame->hd.length == 0) { - return 1; // Consider 0-length frame without END_STREAM an error. + if (invalid_frame_count_++ > js_fields_->max_invalid_frames) { + Debug(this, "rejecting empty-frame-without-END_STREAM flood\n"); + // Consider a flood of 0-length frames without END_STREAM an error. + return 1; + } } return 0; } diff --git a/test/fixtures/emptyframe.http2 b/test/fixtures/emptyframe.http2 new file mode 100644 index 0000000000000000000000000000000000000000..c4a095c4334529276c7b8435cd8fa6a86fb77fbb GIT binary patch literal 4233 zcma)8c{r497a!T8tYyhIV;TEc#x9dk#$J|2wlu~V!<ZQ}V~av$mo4<lntcf&`;t97 z5sFBbDX&yWWc$3;x4hT&{_#E6_1yP!&V9~#&iS4Dy6+zVz(5E1`OqGZ&c`nu%}?<} z3C&*%G!Z}vdOQY;_XTnaT6v*mK~N=-DUt+&DnJ!MiprV_Dw+!FAh@X&ke!!;_NB<X zQao_yK`2)wiHxSeyeKXZb#_q{0Z&2WDG)Lm<we3${2)XE4vX@GkkCW|i2@<uaejf7 zC!EnPNH5&66$u5BAfs_E5}@-StOw?&LK@_Zq#!kqHDnYCOZ;u+g(o9j&=9mY5-0I* zA0HndIShe-!J#2YJQC+e!J^1=D1rxwOve55!hby|NQ}p^69!HC>G40}uZTaj|4!t% zDm)tXw|xH_>Hq!rd5{O%8H@aT(PWC>arXa%gUd01<lo67iP&HD{Bw2^zcIn$aacV1 zF9`o~{)b>>3i245zg6_RAICicxl$-ZvZlN|icI|7COIO3Ou>-QWKWzNfrOD~XZMBp zlF1Mvi9k7q0ZYKMC>~csa7M$Na0HY)@Dv^97M2K<Wz#{S(8nFSj7Jfiv3LwSlP|;@ zNy1~vu53^R4FzSW8VIVPq7GG11~Sk<fsk{Ga*Cke79dH)FH?C{IYlX;6uSq~7joPn zn3AH3lB$A&0+5#8$&Z320|3lFcP<Su`sA-IObeik`lTvE7{|@1vbR#L71mT9$bJ?# zwKu+;07>i=aZPAhI(zk{fs<aOZDQ5b?l-|zq~(StWK{^4pp;I?6d2w{mqYzsgRN>) z`l6rgvG+ZK7L1NA(Z5DKC+{ad;X>$cghOj5zU+?du{2oPSnHu0xJq8fe{j)T&sj<g zus}FhvM_Q;*oQ3VEvR%+ByP@2dr0+Jq0F@k{1axRhmqkHW%JD<k2w&;K9e4B*O-ix zG$f5MD2+JDuNIXVB;#(!5SKCCy>$TS7aEnha_^o5{{@ZjcZ6-apV9F9)ZQdWF$uEU zT*t~31*46mKjrLG$eSxv9}bpOGjs{e)f~}k&BhgPS^t8u*dk^~aP4BrWOr=9Xo)+V zmZ1hw%3TBNvPynrX3RFMfmKD;&wmc_4pNDOUTr+B7%3iu*Zuf-eyg1=y)-rFla{7^ zZcOMYDtx`*lZBKC{jRl)n`1zWEgbjE#e9qXEgqrv=-X<QoB1Z^VTPm1(%taKa(bC1 z!(osrlaripqSRDfA8jxSHNM%L*hzkE43l>Wy;mVum|2wCWz%=1A3O;zRz9mJXgWMP zc%pk(D5gaC`9a0l57V0dT%QN7@ro+jAes=Sl+24M>EsD3DfhA!ND~V_M6&4e;^>Hi zbTiTh6Zs-NL)4_*2M>HV@7h$lZ`|k*8^hC!eRy$0Z=AnlsH1{|z%OK{U$UVGEqc*? z-#L+gah;h#Upd=G_<X|nLf3u_!hgdWQzprZ8>B5Ir!4SnnL9<wz*skKK=m&?hGa9L zl*~HgEb2`f#5=>=Ee9T@_mM;le2NWiS)<Qgc>hI%*;vIWDdoxiF<XP|(<z%`;A@eQ z3YzB&c6y%;FE{fA0vnd*Xdt*#yLaQgCrezAPp_e+6!TEE;YtsdW;YV17!ezj&o8Ys z(oLP7*|2208-{J))53x|{M&i&GW(gFk`pw!lC0&$^?g*vbAfxASUN1?a&_OY+QxCN zPn7~sIb>^<E!w>G@ok#LZIb0uCL3mjLPExr7T7Ayy+(8P%q(YMrXAhN;#F*i;x6m{ zz;lhqP0PBzKXmv}n{)PviBOa+x=O$sCmoWx@a5aFR$AIR^RZ{sOiE?A-s!Cb?+vN` z;Zb3cRNS~uY*kOEv}745YsgiQ(Y{n2|3eco+*IMWYqlm+jW(-!i<=j-`|5XqtCH3% zb?*whKRH~O{jN!GQ_$F>bQHa+e6Cpet550{uG37;bw>AN+D#eVi?sF_F7VDvYvu~C zk2h2^8#qY|e>PA}Z%s;E8uv~~J}C2E2dh$gKl}HH3ih_ckG^%W%D09*N*q0XD3&AT z`6mC*sfrBnx!PSqP$y$+Ws*MTXu?bi3m(a~;G9<+BoGwa&*-pgx!<3Mz{YLdM(q9s zDF*U>5oiYv<h;w!8m1{Qb`REgEtF(S)MFm*If)`&+6WjI+@#Fq`a|_KKW43tR(pTF z`JK_hDg`#jw8!Qjk-TEccp<QLH{1rrt88EgP0t%GvVu8eYDNq|XRNO;bdvZi4;cV} zqobea(eZG6%l^7(X)XZ(nZHh{m?74?3hbeuwYvM;lbX;|HB1o#FuyNUCbu%;#pku5 zJKL&bT^fT+1I}R@-bA9Bw%rkfRH#f5gfa))_TyPRE1jQ$&cU93`c1C+7T@jCHIMXM zzFE$RHk~=<D&8frrMoOC`n;cry%y2>pKoD8nsO`0qzTZVai7~TU~run|4ref^hk}% zI_X7XdWl4jtZ}&n@iF3V#;bnS(lr3!FN$!SwrJ4L`R6y_B=1jwX3%DV^j@9wQ2WXl z6=>&kP4T{xVb~hywg8dSnxoU{#pM3kT7c=fnS^EMqBp=z*6-ifY1UQN%~uS|!L*9g z>=+D5%_`sNneAIgpHe%IU59~_)2KbcB1h!afG&wIdWi+8X+Zeh$_UbJJ{Q>_`UNF! zEI$oBLP*==L+Lqgd$+!_5U8uz9je$^=5kmgmV1Y-PdbG8+vV)6PN&CNYhPN#3Uv|m z@L|i#z2{Dl%?eY~IJ)k*xq=>^{Fpx(dw>h}=4^M`8l9{}$E)slF;Uyr%GKuiWtl%v zd+cS|`$1;iF_xd}gdTogu9C*-gX@h*;IG;6lG}(8cfgGlF-0EbApd?#W!OPOV#SHB ztSm|T?h$qPJil;8f<hGiCU$+^tFm_XwC0wWXnhp9+Av@8Kv7Na3fACujj_8AX~F+) z_2I!Y0jio#(&xmsmPzbh0{^@yV8syTe#L0Ab|@(%^FaGGYO=bwtg?5X<C8`?X2kOz ztXDf5#uH1<#mrxa)oGJ7w{L{RSKv4}+mZ|`iU*IH(q{AHlw;2L@wHq{cI0W!;H9%L zed3X?ZtMv#>okT$6?*a-`EyOpJ>U18n;If1B_8#dH)?PL=j8xAvDImY#O=qM2~?GG z>$a9Z>^uBy&rq=}>4k)d&D*Bw%X&>QWJk5{Fk<FJm(2zIfcke}+x-3UvdHRym=w=- zZA6<O)$`bH@o5bC$aD9J`XiOiA{)`TG{QspP@3OFSps}WZ7|nmNXu>VMeYj!ig~q* zkc#_~`6kP;ol;zy%0|&{nBP{8i_q<&1UT?UDm<_eUHq=7StNmN!9+X6SJSjOe#B+B z-<faoUgHy;K;H+%z&o*LS$t(3;-#Y)PD!sQLWhb*gLXEYXdNDo@Ll^ZD{BO$arz@W zUgOjWJ}i3z{e38lkm19zj+v(nJqx9LVAHIx>c%BAPakXQXp0(PUv}D3J6EpRrj-tq zLTavrh>2au=tW-K%}=pob21Z_oO=f-$13yL((6y8i;I;66<@hjQwYYHLWSQCi8#~k zkJD;`Z6aQfWnSW{ZtA+%O*3d3B8|4L8N7jnEI7)Q{wOifovCZL4IJ}QU=Grm8sIT_ zY4@q~5#jQ5%aGltqF40qg85rIjP-<SbTv!RPx9_wvzCf7Gf<2lD{~k$QPz^EjS=G+ zor|*QSOjg}n?2WUT$kJyls_7o1jbZ11Rd@34@O6CRyZDdk^-DMoQrGWISR9(U+GkM z=Bx2}=lLtHX5L5_ym}|9e`GQ(oDwE&VS-f=fSa^fMdasE3|=TWafs4?qV4CAP5vwh za(ZELEA*bQjZnJ?>-5>9B*~zloB`f-aFi2=9^aMjGYeLx&<Sg!lj%0PiC@K1IJ0<D z7)_o_<u^Q634VSCbD|EwuKYmyD0^RH`79*O^X`nO`iZT6(NkZz3<o2xrX(2d8*sgx z<?)+N#@$m8762zYw~Cm*7*L3I0cF0Y?w&PByx9-XJB^gQ3{B$}SgL=w-QHUFmczFW zQNfsN$KO*<Ca}RUTO2>8MxWjq_6q!<$!-67lw4Mt$&!2F`Z}EVx}I}~%?C}rCMC@i zy$;%bv`vfGVTP;f4iliPgz9l0*j}9?Cy~2xGQ$hyA62ThuDQ;#^<(ZNL+9Z$CEZ39 zik9K0I5=l)2le%C?qcv%o6rHC#Upf_-X8Gw4kke2S!{Z%e;$sO`lHgJE+xEHIQLbH zk5*F?LivM#CV6=aA=B(h^`@_X*BO$Se5PD5Z|lP2xMs5v(z@32`Ae{xQ6{?{U^~~F zNG^cjZE6gAe^FpwrMgc@X2>EHZSg7twl@2I)HdhAjf-s!wbbIu>(ZCZFtgQR3cPG% zB276<9s=Q+t-U*xg-M||s5*Bxnu9lRO#KYkp>Q<4`1+LmQJIKc=DKgG)!~n2$3OF0 ztF++y9G)$_CuXA#N;Ap1y3{^cZ3lx-%Hn<86zRMwzU=GGQQ&)4ezwL*gT@hW!t#UD zO@-Uj4sE5)<#fwWONw4KEjQa2swD>qSF1sM*f*1E3t43bA;ZO4f!{8!%Au-^Jqs#h zlnjbZ*1#(&;_?O9T{;Asrdp`<xosau)o6MZs3Yrhgnom-2WluuUsP703MAEjowqX4 zSr`#21s3TP34awc9v*`PhhcjXq6eOnRMD+318YRJn4}DgWeb|b@r}ms_dL0oIvpE^ zB0gBY3J0v+_I_h<ZEmUEZp_4qo@zBGjzqrQW>49PQ8i-p-RSsufcIyxva9^|wtSRl zZP!II)xP&3AfPkaA)jxcB}p4`M{PF1Y4+e&J$3uZ;~|Jc^RueTny<QR4<-}-IF*p6 z%^&8QNaZBl42p5~V$}|<nztPUEnny%>d@++7T~|6Rjr)p8U`au$~|i%K)Q&}PFr1r z@5Myw>o^Z$61Of%^i=erkxnUfL0s_<LRk{|yR9R$(Kvr+4BOhCbTjiBS<PaaQU2Z+ zjZ@;&bi7QMxGqO=&lme(Z`dw-lcWY>9)2|4hJY>v$3`wKM(azk7P)!7H>fq2m+gs? zJ<@yHcfS6j&Elzi1DI5*nQzXwUg_<%q%7x<jc7LxuUDY^t6t&vx@L_^w5$=i6I59a zyWpb-d|5)!{?s3PJ=jiMp0J%Vx2#)$5qf8ni!oI~(Ob`6jeo^&amUM8;w0<LrNj?~ zM!Til@)7S3UukKm#nEm$`JZd;?P|9Wx>_Y(JAc6Qi|?@fA_&K9=jiASdgI1~+<)l9 LFADS*52E=OrzwSx literal 0 HcmV?d00001 diff --git a/test/parallel/test-http2-empty-frame-without-eof.js b/test/parallel/test-http2-empty-frame-without-eof.js new file mode 100644 index 00000000000000..02da78d940a92d --- /dev/null +++ b/test/parallel/test-http2-empty-frame-without-eof.js @@ -0,0 +1,39 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); +const { readSync } = require('../common/fixtures'); +const net = require('net'); +const http2 = require('http2'); +const { once } = require('events'); + +async function main() { + const blobWithEmptyFrame = readSync('emptyframe.http2'); + const server = net.createServer((socket) => { + socket.end(blobWithEmptyFrame); + }).listen(0); + await once(server, 'listening'); + + for (const maxSessionInvalidFrames of [0, 2]) { + const client = http2.connect(`http://localhost:${server.address().port}`, { + maxSessionInvalidFrames + }); + const stream = client.request({ + ':method': 'GET', + ':path': '/' + }); + if (maxSessionInvalidFrames) { + stream.on('error', common.mustNotCall()); + client.on('error', common.mustNotCall()); + } else { + stream.on('error', common.mustCall()); + client.on('error', common.mustCall()); + } + stream.resume(); + await once(stream, 'end'); + client.close(); + } + server.close(); +} + +main().then(common.mustCall()); From ec82feb7280c83df725f2ca6db2d4e373b59b661 Mon Sep 17 00:00:00 2001 From: Ruy Adorno <ruyadorno@hotmail.com> Date: Thu, 25 Mar 2021 18:21:14 -0400 Subject: [PATCH 68/85] deps: upgrade npm to 7.7.5 PR-URL: https://github.com/nodejs/node/pull/37919 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> --- deps/npm/.npmignore | 4 + deps/npm/AUTHORS | 1 + deps/npm/CHANGELOG.md | 28 + deps/npm/docs/output/commands/npm-ls.html | 2 +- deps/npm/docs/output/commands/npm.html | 2 +- deps/npm/lib/help.js | 2 +- deps/npm/lib/npm.js | 2 +- deps/npm/lib/utils/config/definitions.js | 11 +- deps/npm/man/man1/npm-ls.1 | 2 +- deps/npm/man/man1/npm.1 | 2 +- deps/npm/package.json | 5 +- .../smoke-tests-index.js-TAP.test.js | 649 ++++++++++++++++++ deps/npm/test/lib/help.js | 23 + deps/npm/test/lib/npm.js | 9 +- deps/npm/test/lib/utils/config/definitions.js | 21 + deps/npm/test/lib/utils/config/flatten.js | 2 - 16 files changed, 752 insertions(+), 13 deletions(-) create mode 100644 deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js diff --git a/deps/npm/.npmignore b/deps/npm/.npmignore index 409905efd27762..9d02b99f91b39a 100644 --- a/deps/npm/.npmignore +++ b/deps/npm/.npmignore @@ -39,3 +39,7 @@ docs/template.html Session.vim .nyc_output /.editorconfig + +# don't ship smoke tests +smoke-tests/ +tap-snapshots/smoke-tests-index.js-TAP.test.js diff --git a/deps/npm/AUTHORS b/deps/npm/AUTHORS index 769d18f83d5d98..c4a8c4e45ec94f 100644 --- a/deps/npm/AUTHORS +++ b/deps/npm/AUTHORS @@ -767,3 +767,4 @@ Eric Chow <eric.zjp.chow@gmail.com> kbayrhammer <klaus.bayrhammer@redbull.com> James Chen-Smith <jameschensmith@gmail.com> Yash Singh <saiansh2525@gmail.com> +Danielle Church <dani.church@gmail.com> diff --git a/deps/npm/CHANGELOG.md b/deps/npm/CHANGELOG.md index 8bec00e87d7b57..74f5b5f1c58ea7 100644 --- a/deps/npm/CHANGELOG.md +++ b/deps/npm/CHANGELOG.md @@ -1,3 +1,31 @@ +## v7.7.5 (2021-03-25) + +### BUG FIXES + +* [`95ba87622`](https://github.com/npm/cli/commit/95ba87622e00d68270eda9e071b19737718fca16) + [#2949](https://github.com/npm/cli/issues/2949) + fix handling manual indexes in `npm help` + ([@dmchurch](https://github.com/dmchurch)) +* [`59cf37962`](https://github.com/npm/cli/commit/59cf37962a2286e0f7d3bd37fa9c8bc3bac94218) + [#2958](https://github.com/npm/cli/issues/2958) + always set `npm.command` to canonical command name + ([@isaacs](https://github.com/isaacs)) +* [`1415b4bde`](https://github.com/npm/cli/commit/1415b4bdeeaabb6e0ba12b6b1b0cc56502bd64ab) + [#2964](https://github.com/npm/cli/issues/2964) + fix(config): properly translate user-agent + ([@wraithgar](https://github.com/wraithgar)) +* [`59271936d`](https://github.com/npm/cli/commit/59271936d90fbd6956a41967119f578c0ba63db9) + [#2965](https://github.com/npm/cli/issues/2965) + fix(config): tie save-exact/save-prefix together + ([@wraithgar](https://github.com/wraithgar)) + +### TESTS + +* [`97b415287`](https://github.com/npm/cli/commit/97b41528739460b2e9e72e09000aded412418cb2) + [#2959](https://github.com/npm/cli/issues/2959) + add smoke tests + ([@ruyadorno](https://github.com/ruyadorno)) + ## v7.7.4 (2021-03-24) ### BUG FIXES diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index 287149aba986dd..9b4aa1b0abf45a 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -159,7 +159,7 @@ <h3 id="description">Description</h3> the results to only the paths to the packages named. Note that nested packages will <em>also</em> show the paths to the specified packages. For example, running <code>npm ls promzard</code> in npm’s source tree will show:</p> -<pre lang="bash"><code>npm@7.7.4 /path/to/npm +<pre lang="bash"><code>npm@7.7.5 /path/to/npm └─┬ init-package-json@0.0.4 └── promzard@0.1.5 </code></pre> diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index 90e4019f1bc5d6..e6cd6878a22820 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -148,7 +148,7 @@ <h2 id="table-of-contents">Table of contents</h2> <pre lang="bash"><code>npm <command> [args] </code></pre> <h3 id="version">Version</h3> -<p>7.7.4</p> +<p>7.7.5</p> <h3 id="description">Description</h3> <p>npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency diff --git a/deps/npm/lib/help.js b/deps/npm/lib/help.js index b9ff1c95760c6a..589819eb02a454 100644 --- a/deps/npm/lib/help.js +++ b/deps/npm/lib/help.js @@ -9,7 +9,7 @@ const BaseCommand = require('./base-command.js') // Strips out the number from foo.7 or foo.7. or foo.7.tgz // We don't currently compress our man pages but if we ever did this would // seemlessly continue supporting it -const manNumberRegex = /\.(\d+)(\..*)?$/ +const manNumberRegex = /\.(\d+)(\.[^/\\]*)?$/ class Help extends BaseCommand { /* istanbul ignore next - see test/lib/load-all-commands.js */ diff --git a/deps/npm/lib/npm.js b/deps/npm/lib/npm.js index 42541a90ffb793..5f8b2ff3d703d1 100644 --- a/deps/npm/lib/npm.js +++ b/deps/npm/lib/npm.js @@ -25,7 +25,7 @@ const proxyCmds = new Proxy({}, { // old way of doing things, until we can make breaking changes to the // npm.commands[x] api target[actual] = new Proxy( - (args, cb) => npm[_runCmd](cmd, impl, args, cb), + (args, cb) => npm[_runCmd](actual, impl, args, cb), { get: (target, attr, receiver) => { return Reflect.get(impl, attr, receiver) diff --git a/deps/npm/lib/utils/config/definitions.js b/deps/npm/lib/utils/config/definitions.js index 67a830448e311d..512ea8af98cb4e 100644 --- a/deps/npm/lib/utils/config/definitions.js +++ b/deps/npm/lib/utils/config/definitions.js @@ -1526,7 +1526,10 @@ define('save-exact', { Dependencies saved to package.json will be configured with an exact version rather than using npm's default semver range operator. `, - flatten, + flatten (key, obj, flatOptions) { + // just call the save-prefix flattener, it reads from obj['save-exact'] + definitions['save-prefix'].flatten('save-prefix', obj, flatOptions) + }, }) define('save-optional', { @@ -1595,6 +1598,7 @@ define('save-prefix', { `, flatten (key, obj, flatOptions) { flatOptions.savePrefix = obj['save-exact'] ? '' : obj['save-prefix'] + obj['save-prefix'] = flatOptions.savePrefix }, }) @@ -1970,6 +1974,11 @@ define('user-agent', { .replace(/\{arch\}/gi, process.arch) .replace(/\{ci\}/gi, ciName ? `ci/${ciName}` : '') .trim() + // user-agent is a unique kind of config item that gets set from a template + // and ends up translated. Because of this, the normal "should we set this + // to process.env also doesn't work + obj[key] = flatOptions.userAgent + process.env.npm_config_user_agent = flatOptions.userAgent }, }) diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index 182f113638960d..b1fd65b15be901 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@7\.7\.4 /path/to/npm +npm@7\.7\.5 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index f756c4c7324082..81e5ab92149e27 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -10,7 +10,7 @@ npm <command> [args] .RE .SS Version .P -7\.7\.4 +7\.7\.5 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts diff --git a/deps/npm/package.json b/deps/npm/package.json index 4d531d7de90590..1e821ececafc40 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "7.7.4", + "version": "7.7.5", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -207,7 +207,8 @@ "lint": "npm run eslint -- test/lib test/bin \"lib/**/*.js\"", "lintfix": "npm run lint -- --fix", "prelint": "rimraf test/npm_cache*", - "resetdeps": "bash scripts/resetdeps.sh" + "resetdeps": "bash scripts/resetdeps.sh", + "smoke-tests": "tap smoke-tests/index.js" }, "//": [ "XXX temporarily only run unit tests while v7 beta is in progress", diff --git a/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js b/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js new file mode 100644 index 00000000000000..4d840ceef6ada1 --- /dev/null +++ b/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js @@ -0,0 +1,649 @@ +/* IMPORTANT + * This snapshot file is auto-generated, but designed for humans. + * It should be checked into source control and tracked carefully. + * Re-generate by setting TAP_SNAPSHOT=1 and running tests. + * Make sure to inspect the output below. Do not ignore changes! + */ +'use strict' +exports[`smoke-tests/index.js TAP npm diff > should have expected diff output 1`] = ` +diff --git a/package.json b/package.json +index v1.0.4..v1.1.1 100644 +--- a/package.json ++++ b/package.json +@@ -1,15 +1,21 @@ + { + "name": "abbrev", +- "version": "1.0.4", ++ "version": "1.1.1", + "description": "Like ruby's abbrev module, but in js", + "author": "Isaac Z. Schlueter <i@izs.me>", +- "main": "./lib/abbrev.js", ++ "main": "abbrev.js", + "scripts": { +- "test": "node lib/abbrev.js" ++ "test": "tap test.js --100", ++ "preversion": "npm test", ++ "postversion": "npm publish", ++ "postpublish": "git push origin --all; git push origin --tags" + }, + "repository": "http://github.com/isaacs/abbrev-js", +- "license": { +- "type": "MIT", +- "url": "https://github.com/isaacs/abbrev-js/raw/master/LICENSE" +- } ++ "license": "ISC", ++ "devDependencies": { ++ "tap": "^10.1" ++ }, ++ "files": [ ++ "abbrev.js" ++ ] + } +diff --git a/LICENSE b/LICENSE +index v1.0.4..v1.1.1 100644 +--- a/LICENSE ++++ b/LICENSE +@@ -1,4 +1,27 @@ +-Copyright 2009, 2010, 2011 Isaac Z. Schlueter. ++This software is dual-licensed under the ISC and MIT licenses. ++You may use this software under EITHER of the following licenses. ++ ++---------- ++ ++The ISC License ++ ++Copyright (c) Isaac Z. Schlueter and Contributors ++ ++Permission to use, copy, modify, and/or distribute this software for any ++purpose with or without fee is hereby granted, provided that the above ++copyright notice and this permission notice appear in all copies. ++ ++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR ++IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++---------- ++ ++Copyright Isaac Z. Schlueter and Contributors + All rights reserved. + + Permission is hereby granted, free of charge, to any person +diff --git a/lib/abbrev.js b/lib/abbrev.js +deleted file mode 100644 +index v1.0.4..v1.1.1 +--- a/lib/abbrev.js ++++ b/lib/abbrev.js +@@ -1,111 +0,0 @@ +- +-module.exports = exports = abbrev.abbrev = abbrev +- +-abbrev.monkeyPatch = monkeyPatch +- +-function monkeyPatch () { +- Object.defineProperty(Array.prototype, 'abbrev', { +- value: function () { return abbrev(this) }, +- enumerable: false, configurable: true, writable: true +- }) +- +- Object.defineProperty(Object.prototype, 'abbrev', { +- value: function () { return abbrev(Object.keys(this)) }, +- enumerable: false, configurable: true, writable: true +- }) +-} +- +-function abbrev (list) { +- if (arguments.length !== 1 || !Array.isArray(list)) { +- list = Array.prototype.slice.call(arguments, 0) +- } +- for (var i = 0, l = list.length, args = [] ; i < l ; i ++) { +- args[i] = typeof list[i] === "string" ? list[i] : String(list[i]) +- } +- +- // sort them lexicographically, so that they're next to their nearest kin +- args = args.sort(lexSort) +- +- // walk through each, seeing how much it has in common with the next and previous +- var abbrevs = {} +- , prev = "" +- for (var i = 0, l = args.length ; i < l ; i ++) { +- var current = args[i] +- , next = args[i + 1] || "" +- , nextMatches = true +- , prevMatches = true +- if (current === next) continue +- for (var j = 0, cl = current.length ; j < cl ; j ++) { +- var curChar = current.charAt(j) +- nextMatches = nextMatches && curChar === next.charAt(j) +- prevMatches = prevMatches && curChar === prev.charAt(j) +- if (!nextMatches && !prevMatches) { +- j ++ +- break +- } +- } +- prev = current +- if (j === cl) { +- abbrevs[current] = current +- continue +- } +- for (var a = current.substr(0, j) ; j <= cl ; j ++) { +- abbrevs[a] = current +- a += current.charAt(j) +- } +- } +- return abbrevs +-} +- +-function lexSort (a, b) { +- return a === b ? 0 : a > b ? 1 : -1 +-} +- +- +-// tests +-if (module === require.main) { +- +-var assert = require("assert") +-var util = require("util") +- +-console.log("running tests") +-function test (list, expect) { +- var actual = abbrev(list) +- assert.deepEqual(actual, expect, +- "abbrev("+util.inspect(list)+") === " + util.inspect(expect) + "/n"+ +- "actual: "+util.inspect(actual)) +- actual = abbrev.apply(exports, list) +- assert.deepEqual(abbrev.apply(exports, list), expect, +- "abbrev("+list.map(JSON.stringify).join(",")+") === " + util.inspect(expect) + "/n"+ +- "actual: "+util.inspect(actual)) +-} +- +-test([ "ruby", "ruby", "rules", "rules", "rules" ], +-{ rub: 'ruby' +-, ruby: 'ruby' +-, rul: 'rules' +-, rule: 'rules' +-, rules: 'rules' +-}) +-test(["fool", "foom", "pool", "pope"], +-{ fool: 'fool' +-, foom: 'foom' +-, poo: 'pool' +-, pool: 'pool' +-, pop: 'pope' +-, pope: 'pope' +-}) +-test(["a", "ab", "abc", "abcd", "abcde", "acde"], +-{ a: 'a' +-, ab: 'ab' +-, abc: 'abc' +-, abcd: 'abcd' +-, abcde: 'abcde' +-, ac: 'acde' +-, acd: 'acde' +-, acde: 'acde' +-}) +- +-console.log("pass") +- +-} +/ No newline at end of file +diff --git a/abbrev.js b/abbrev.js +new file mode 100644 +index v1.0.4..v1.1.1 +--- a/abbrev.js ++++ b/abbrev.js +@@ -0,0 +1,61 @@ ++module.exports = exports = abbrev.abbrev = abbrev ++ ++abbrev.monkeyPatch = monkeyPatch ++ ++function monkeyPatch () { ++ Object.defineProperty(Array.prototype, 'abbrev', { ++ value: function () { return abbrev(this) }, ++ enumerable: false, configurable: true, writable: true ++ }) ++ ++ Object.defineProperty(Object.prototype, 'abbrev', { ++ value: function () { return abbrev(Object.keys(this)) }, ++ enumerable: false, configurable: true, writable: true ++ }) ++} ++ ++function abbrev (list) { ++ if (arguments.length !== 1 || !Array.isArray(list)) { ++ list = Array.prototype.slice.call(arguments, 0) ++ } ++ for (var i = 0, l = list.length, args = [] ; i < l ; i ++) { ++ args[i] = typeof list[i] === "string" ? list[i] : String(list[i]) ++ } ++ ++ // sort them lexicographically, so that they're next to their nearest kin ++ args = args.sort(lexSort) ++ ++ // walk through each, seeing how much it has in common with the next and previous ++ var abbrevs = {} ++ , prev = "" ++ for (var i = 0, l = args.length ; i < l ; i ++) { ++ var current = args[i] ++ , next = args[i + 1] || "" ++ , nextMatches = true ++ , prevMatches = true ++ if (current === next) continue ++ for (var j = 0, cl = current.length ; j < cl ; j ++) { ++ var curChar = current.charAt(j) ++ nextMatches = nextMatches && curChar === next.charAt(j) ++ prevMatches = prevMatches && curChar === prev.charAt(j) ++ if (!nextMatches && !prevMatches) { ++ j ++ ++ break ++ } ++ } ++ prev = current ++ if (j === cl) { ++ abbrevs[current] = current ++ continue ++ } ++ for (var a = current.substr(0, j) ; j <= cl ; j ++) { ++ abbrevs[a] = current ++ a += current.charAt(j) ++ } ++ } ++ return abbrevs ++} ++ ++function lexSort (a, b) { ++ return a === b ? 0 : a > b ? 1 : -1 ++} + +` + +exports[`smoke-tests/index.js TAP npm explain > should have expected explain output 1`] = ` +abbrev@1.0.4 +node_modules/abbrev + abbrev@"^1.0.4" from the root project + +` + +exports[`smoke-tests/index.js TAP npm fund > should have expected fund output 1`] = ` +project@1.0.0 +\`-- https://github.com/sponsors/isaacs + \`-- promise-all-reject-late@1.0.1 + + +` + +exports[`smoke-tests/index.js TAP npm init > should have successful npm init result 1`] = ` +Wrote to {CWD}/smoke-tests/index/project/package.json: + +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} + + + +` + +exports[`smoke-tests/index.js TAP npm install dev dep > should have expected dev dep added lockfile result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + }, + "devDependencies": { + "promise-all-reject-late": "^1.0.1" + } + }, + "node_modules/abbrev": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz", + "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0=" + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + } + }, + "dependencies": { + "abbrev": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz", + "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0=" + }, + "promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true + } + } +} + +` + +exports[`smoke-tests/index.js TAP npm install dev dep > should have expected dev dep added package.json result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + }, + "devDependencies": { + "promise-all-reject-late": "^1.0.1" + } +} + +` + +exports[`smoke-tests/index.js TAP npm install dev dep > should have expected dev dep added reify output 1`] = ` + +added 1 package + +1 package is looking for funding + run \`npm fund\` for details + +` + +exports[`smoke-tests/index.js TAP npm install prodDep@version > should have expected install reify output 1`] = ` + +added 1 package + +` + +exports[`smoke-tests/index.js TAP npm install prodDep@version > should have expected lockfile result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + } + }, + "node_modules/abbrev": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz", + "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0=" + } + }, + "dependencies": { + "abbrev": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz", + "integrity": "sha1-vVWuXkE7oXIu5Mq6H26hBBSlns0=" + } + } +} + +` + +exports[`smoke-tests/index.js TAP npm install prodDep@version > should have expected package.json result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + } +} + +` + +exports[`smoke-tests/index.js TAP npm ls > should have expected ls output 1`] = ` +project@1.0.0 {CWD}/smoke-tests/index/project ++-- abbrev@1.0.4 +\`-- promise-all-reject-late@1.0.1 + + +` + +exports[`smoke-tests/index.js TAP npm outdated > should have expected outdated output 1`] = ` +Package Current Wanted Latest Location Depended by +abbrev 1.0.4 1.1.1 1.1.1 node_modules/abbrev project + +` + +exports[`smoke-tests/index.js TAP npm prefix > should have expected prefix output 1`] = ` +{CWD}/smoke-tests/index/project + +` + +exports[`smoke-tests/index.js TAP npm run-script > should have expected run-script output 1`] = ` + +> project@1.0.0 hello +> echo Hello + +Hello + +` + +exports[`smoke-tests/index.js TAP npm set-script > should have expected script added package.json result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1", + "hello": "echo Hello" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + }, + "devDependencies": { + "promise-all-reject-late": "^1.0.1" + } +} + +` + +exports[`smoke-tests/index.js TAP npm set-script > should have expected set-script output 1`] = ` + +` + +exports[`smoke-tests/index.js TAP npm uninstall > should have expected uninstall lockfile result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + } + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + } + } +} + +` + +exports[`smoke-tests/index.js TAP npm uninstall > should have expected uninstall package.json result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1", + "hello": "echo Hello" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + } +} + +` + +exports[`smoke-tests/index.js TAP npm uninstall > should have expected uninstall reify output 1`] = ` + +removed 1 package + +` + +exports[`smoke-tests/index.js TAP npm update dep > should have expected update lockfile result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + }, + "devDependencies": { + "promise-all-reject-late": "^1.0.1" + } + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + } + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true + } + } +} + +` + +exports[`smoke-tests/index.js TAP npm update dep > should have expected update package.json result 1`] = ` +{ + "name": "project", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo /"Error: no test specified/" && exit 1", + "hello": "echo Hello" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.4" + }, + "devDependencies": { + "promise-all-reject-late": "^1.0.1" + } +} + +` + +exports[`smoke-tests/index.js TAP npm update dep > should have expected update reify output 1`] = ` + +changed 1 package + +1 package is looking for funding + run \`npm fund\` for details + +` + +exports[`smoke-tests/index.js TAP npm view > should have expected view output 1`] = ` + +[4m[1m[32mabbrev[39m@[32m1.0.4[39m[22m[24m | [32mMIT[39m | deps: [32mnone[39m | versions: [33m8[39m +Like ruby's abbrev module, but in js +[36mhttps://github.com/isaacs/abbrev-js#readme[39m + +dist +.tarball: [36mhttps://registry.npmjs.org/abbrev/-/abbrev-1.0.4.tgz[39m +.shasum: [33mbd55ae5e413ba1722ee4caba1f6ea10414a59ecd[39m + +maintainers: +- [33mnlf[39m <[36mquitlahok@gmail.com[39m> +- [33mruyadorno[39m <[36mruyadorno@hotmail.com[39m> +- [33mdarcyclarke[39m <[36mdarcy@darcyclarke.me[39m> +- [33madam_baldwin[39m <[36mevilpacket@gmail.com[39m> +- [33misaacs[39m <[36mi@izs.me[39m> + +dist-tags: +[1m[32mlatest[39m[22m: 1.1.1 + +published [33mover a year ago[39m by [33misaacs[39m <[36mi@izs.me[39m> + +` diff --git a/deps/npm/test/lib/help.js b/deps/npm/test/lib/help.js index ccf13a7e4660c2..b5fe297b5c4307 100644 --- a/deps/npm/test/lib/help.js +++ b/deps/npm/test/lib/help.js @@ -340,3 +340,26 @@ test('npm help - man viewer propagates errors', t => { t.end() }) }) + +test('npm help with complex installation path finds proper help file', t => { + npmConfig.viewer = 'browser' + globResult = [ + 'C:/Program Files/node-v14.15.5-win-x64/node_modules/npm/man/man1/npm-install.1', + // glob always returns forward slashes, even on Windows + ] + + t.teardown(() => { + npmConfig.viewer = undefined + globResult = globDefaults + spawnBin = null + spawnArgs = null + }) + + return help.exec(['1', 'install'], (err) => { + if (err) + throw err + + t.match(openUrlArg, /commands(\/|\\)npm-install.html$/, 'attempts to open the correct url') + t.end() + }) +}) diff --git a/deps/npm/test/lib/npm.js b/deps/npm/test/lib/npm.js index 57391939800b5b..4d40792af8f38f 100644 --- a/deps/npm/test/lib/npm.js +++ b/deps/npm/test/lib/npm.js @@ -15,7 +15,7 @@ for (const env of Object.keys(process.env).filter(e => /^npm_/.test(e))) { 'should match "npm test" or "npm run test"' ) } else - t.match(process.env[env], /^(run)|(run-script)|(exec)$/) + t.match(process.env[env], /^(run-script|exec)$/) } delete process.env[env] } @@ -411,10 +411,15 @@ t.test('npm.load', t => { npm.localPrefix = dir await new Promise((res, rej) => { - npm.commands['run-script']([], er => { + // verify that calling the command with a short name still sets + // the npm.command property to the full canonical name of the cmd. + npm.command = null + npm.commands.run([], er => { if (er) rej(er) + t.equal(npm.command, 'run-script', 'npm.command set to canonical name') + t.match( consoleLogs, [ diff --git a/deps/npm/test/lib/utils/config/definitions.js b/deps/npm/test/lib/utils/config/definitions.js index b6e1d1d01529e1..0b80b34d55771d 100644 --- a/deps/npm/test/lib/utils/config/definitions.js +++ b/deps/npm/test/lib/utils/config/definitions.js @@ -733,10 +733,16 @@ t.test('user-agent', t => { `${process.platform} ${process.arch}` definitions['user-agent'].flatten('user-agent', obj, flat) t.equal(flat.userAgent, expectNoCI) + t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set') + t.equal(obj['user-agent'], flat.userAgent, 'config user-agent template is translated') + obj['ci-name'] = 'foo' + obj['user-agent'] = definitions['user-agent'].default const expectCI = `${expectNoCI} ci/foo` definitions['user-agent'].flatten('user-agent', obj, flat) t.equal(flat.userAgent, expectCI) + t.equal(process.env.npm_config_user_agent, flat.userAgent, 'npm_user_config environment is set') + t.equal(obj['user-agent'], flat.userAgent, 'config user-agent template is translated') t.end() }) @@ -754,3 +760,18 @@ t.test('save-prefix', t => { t.strictSame(flat, { savePrefix: '~1.2.3' }) t.end() }) + +t.test('save-exact', t => { + const obj = { + 'save-exact': true, + 'save-prefix': '~1.2.3', + } + const flat = {} + definitions['save-exact'] + .flatten('save-exact', { ...obj, 'save-exact': true }, flat) + t.strictSame(flat, { savePrefix: '' }) + definitions['save-exact'] + .flatten('save-exact', { ...obj, 'save-exact': false }, flat) + t.strictSame(flat, { savePrefix: '~1.2.3' }) + t.end() +}) diff --git a/deps/npm/test/lib/utils/config/flatten.js b/deps/npm/test/lib/utils/config/flatten.js index 6fc91b4847e386..7e135639208887 100644 --- a/deps/npm/test/lib/utils/config/flatten.js +++ b/deps/npm/test/lib/utils/config/flatten.js @@ -17,7 +17,6 @@ const obj = { const flat = flatten(obj) t.strictSame(flat, { saveType: 'dev', - saveExact: true, savePrefix: '', '@foobar:registry': 'https://foo.bar.com/', '//foo.bar.com:_authToken': 'foobarbazquuxasdf', @@ -30,7 +29,6 @@ t.strictSame(flat, { process.env.NODE = '/usr/local/bin/node.exe' flatten({ 'save-dev': false }, flat) t.strictSame(flat, { - saveExact: true, savePrefix: '', '@foobar:registry': 'https://foo.bar.com/', '//foo.bar.com:_authToken': 'foobarbazquuxasdf', From 0fe3c7edd351b773cb019ba8b819abeb2ff8ed3d Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Fri, 26 Mar 2021 22:03:11 -0700 Subject: [PATCH 69/85] doc: spell out ICU acronym on first occurrence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37942 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- doc/api/intl.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/api/intl.md b/doc/api/intl.md index 08571c637e56ea..015872330c9433 100644 --- a/doc/api/intl.md +++ b/doc/api/intl.md @@ -22,7 +22,8 @@ programs. Some of them are: * [`require('util').TextDecoder`][] * [`RegExp` Unicode Property Escapes][] -Node.js (and its underlying V8 engine) uses [ICU][] to implement these features +Node.js and the underlying V8 engine use +[International Components for Unicode (ICU)][ICU] to implement these features in native C/C++ code. The full ICU data set is provided by Node.js by default. However, due to the size of the ICU data file, several options are provided for customizing the ICU data set either when From 377830fd285bb41bf1ef7635e97d1225750491b9 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 25 Mar 2021 22:09:48 -0700 Subject: [PATCH 70/85] child_process: remove unused argument The internal validateTimeout() takes a single parameter, so do not pass a second value. PR-URL: https://github.com/nodejs/node/pull/37923 Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> --- lib/child_process.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/child_process.js b/lib/child_process.js index da2f9628ce0808..26e1bb33d0c9ef 100644 --- a/lib/child_process.js +++ b/lib/child_process.js @@ -600,7 +600,7 @@ function abortChildProcess(child, killSignal) { function spawn(file, args, options) { options = normalizeSpawnArguments(file, args, options); - validateTimeout(options.timeout, 'options.timeout'); + validateTimeout(options.timeout); validateAbortSignal(options.signal, 'options.signal'); const killSignal = sanitizeKillSignal(options.killSignal); const child = new ChildProcess(); From e256c4d11ddace7e53cf146383d2a1234ddd781e Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 25 Mar 2021 23:16:46 -0700 Subject: [PATCH 71/85] test: fix typeof comparison MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The typeof operator returns a string. Compare to the string 'undefined' and not the value undefined. PR-URL: https://github.com/nodejs/node/pull/37924 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- test/parallel/test-assert-calltracker-report.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parallel/test-assert-calltracker-report.js b/test/parallel/test-assert-calltracker-report.js index 79186c88fd058b..64b068760ca720 100644 --- a/test/parallel/test-assert-calltracker-report.js +++ b/test/parallel/test-assert-calltracker-report.js @@ -19,7 +19,7 @@ callsfoo(); // Ensures that foo was removed from the callChecks array after being called the // expected number of times. -if (typeof tracker.report()[0] === undefined) { +if (typeof tracker.report()[0] === 'undefined') { process.exit(1); } From b7e738410915ece886b193fef6de9d892cd25645 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Thu, 25 Mar 2021 23:17:23 -0700 Subject: [PATCH 72/85] tools: improve valid-typeof lint rule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Require that `typeof` comparisons be to string literals. PR-URL: https://github.com/nodejs/node/pull/37924 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- .eslintrc.js | 2 +- lib/internal/encoding.js | 1 + test/parallel/test-assert-calltracker-report.js | 12 +++--------- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 7574e1b4b6c7f7..4914b7b6a8f42a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -307,7 +307,7 @@ module.exports = { 'template-curly-spacing': 'error', 'unicode-bom': 'error', 'use-isnan': 'error', - 'valid-typeof': 'error', + 'valid-typeof': ['error', { requireStringLiterals: true }], // Custom rules from eslint-plugin-node-core 'node-core/no-unescaped-regexp-dot': 'error', diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js index 560f0daeae7ca2..f6e52238a1270c 100644 --- a/lib/internal/encoding.js +++ b/lib/internal/encoding.js @@ -64,6 +64,7 @@ function validateDecoder(obj) { } function validateArgument(prop, expected, propName, expectedName) { + // eslint-disable-next-line valid-typeof if (typeof prop !== expected) throw new ERR_INVALID_ARG_TYPE(propName, expectedName, prop); } diff --git a/test/parallel/test-assert-calltracker-report.js b/test/parallel/test-assert-calltracker-report.js index 64b068760ca720..87ef0bff17fc0b 100644 --- a/test/parallel/test-assert-calltracker-report.js +++ b/test/parallel/test-assert-calltracker-report.js @@ -11,22 +11,16 @@ function foo() {} const callsfoo = tracker.calls(foo, 1); // Ensures that foo was added to the callChecks array. -if (tracker.report()[0].operator !== 'foo') { - process.exit(1); -} +assert.strictEqual(tracker.report()[0].operator, 'foo'); callsfoo(); // Ensures that foo was removed from the callChecks array after being called the // expected number of times. -if (typeof tracker.report()[0] === 'undefined') { - process.exit(1); -} +assert.strictEqual(typeof tracker.report()[0], 'undefined'); callsfoo(); // Ensures that foo was added back to the callChecks array after being called // more than the expected number of times. -if (tracker.report()[0].operator !== 'foo') { - process.exit(1); -} +assert.strictEqual(tracker.report()[0].operator, 'foo'); From bd87e195ed74cc4cd462acdbfd449ab0c3d085a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Nie=C3=9Fen?= <tniessen@users.noreply.github.com> Date: Sun, 28 Mar 2021 14:34:10 +0200 Subject: [PATCH 73/85] src: fix typo in src code guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/37956 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Zijian Liu <lxxyxzj@gmail.com> --- src/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/README.md b/src/README.md index d811a6280273c6..b278c0287366cf 100644 --- a/src/README.md +++ b/src/README.md @@ -812,7 +812,7 @@ reference to its associated JavaScript object. This can be useful when one `BaseObject` refers to another `BaseObject` and wants to make sure it stays alive during the lifetime of that reference. -A `BaseObject` can be “detached” throught the `BaseObject::Detach()` method. +A `BaseObject` can be “detached” through the `BaseObject::Detach()` method. In this case, it will be deleted once the last `BaseObjectPtr` referring to it is destroyed. There must be at least one such pointer when `Detach()` is called. This can be useful when one `BaseObject` fully owns another From ac60d018e280c8d243e0fb4fd25c66c7ca5ec0ea Mon Sep 17 00:00:00 2001 From: Danielle Adams <adamzdanielle@gmail.com> Date: Wed, 17 Mar 2021 15:59:39 -0400 Subject: [PATCH 74/85] doc: add more commands for cherry-picking and changelog to release docs PR-URL: https://github.com/nodejs/node/pull/37785 Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Beth Griggs <bgriggs@redhat.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> --- doc/guides/releases.md | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/doc/guides/releases.md b/doc/guides/releases.md index c2eaadbdbeafc8..2309460e3d1b3a 100644 --- a/doc/guides/releases.md +++ b/doc/guides/releases.md @@ -175,10 +175,10 @@ duplicate or not. For a list of commits that could be landed in a patch release on v1.x: ```console -$ branch-diff v1.x-staging master --exclude-label=semver-major,semver-minor,dont-land-on-v1.x,backport-requested-v1.x --filter-release --format=simple +$ branch-diff v1.x-staging master --exclude-label=semver-major,semver-minor,dont-land-on-v1.x,backport-requested-v1.x,backport-blocked-v1.x --filter-release --format=simple ``` -Previous release commits and version bumps do not need to be +Previously released commits and version bumps do not need to be cherry-picked. Carefully review the list of commits: @@ -187,8 +187,16 @@ Carefully review the list of commits: * Checking semver status - Commits labeled as `semver-minor` or `semver-major` should only be cherry-picked when appropriate for the type of release being made. -* If you think it's risky so should wait for a while, add the `baking-for-lts` -tag. +* If you think it's risky and the change should wait for a while, add the +`baking-for-lts` tag. + +When you are ready to cherry-pick commits, you can automate with the following +command. (For semver-minor releases, make sure to remove the `semver-minor` tag +from `exclude-label`.) + +```console +$ branch-diff v1.x-staging master --exclude-label=semver-major,semver-minor,dont-land-on-v1.x,backport-requested-v1.x,backport-blocked-v1.x --filter-release --format=sha --reverse | xargs git cherry-pick +``` When cherry-picking commits, if there are simple conflicts you can resolve them. Otherwise, add the `backport-requested-vN.x` label to the original PR @@ -196,8 +204,13 @@ and post a comment stating that it does not land cleanly and will require a backport PR. You can refer the owner of the PR to the "[Backporting to Release Lines](https://github.com/nodejs/node/blob/HEAD/doc/guides/backporting-to-release-lines.md)" guide. -If commits were cherry-picked in this step, check that the test still pass and -push to the staging branch to keep it up-to-date. +If commits were cherry-picked in this step, check that the test still pass. + +```console +$ make test +``` + +Then, push to the staging branch to keep it up-to-date. ```console $ git push upstream v1.x-staging @@ -274,9 +287,11 @@ in the repository was not on the current branch you may have to supply a `--start-ref` argument: ```console -$ changelog-maker --group --start-ref v1.2.2 +$ changelog-maker --group --filter-release --start-ref v1.2.2 ``` +`--filter-release` will remove the release commit from the previous release. + #### Step 2: Update the appropriate doc/changelogs/CHANGELOG_*.md file There is a separate `CHANGELOG_Vx.md` file for each major Node.js release line. From d44b268910a71c4666033cdf59175d88d74beeb5 Mon Sep 17 00:00:00 2001 From: Nitzan Uziely <linkgoron@gmail.com> Date: Sat, 20 Mar 2021 01:50:31 +0200 Subject: [PATCH 75/85] timers: fix arbitrary object clearImmediate errors Fix errors that are caused by invoking clearImmediate with arbitrary objects. fixes: https://github.com/nodejs/node/issues/37806 PR-URL: https://github.com/nodejs/node/pull/37824 Fixes: https://github.com/nodejs/node/issues/37806 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> --- lib/internal/timers.js | 4 ++-- lib/timers.js | 2 +- test/parallel/test-repl-clear-immediate-crash.js | 12 ++++++++++++ .../test-timers-clear-object-does-not-throw-error.js | 8 ++++++++ 4 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 test/parallel/test-repl-clear-immediate-crash.js create mode 100644 test/parallel/test-timers-clear-object-does-not-throw-error.js diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 050d356b202d70..9521c343038b4f 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -279,11 +279,11 @@ ImmediateList.prototype.append = function(item) { // Removes an item from the linked list, adjusting the pointers of adjacent // items and the linked list's head or tail pointers as necessary ImmediateList.prototype.remove = function(item) { - if (item._idleNext !== null) { + if (item._idleNext) { item._idleNext._idlePrev = item._idlePrev; } - if (item._idlePrev !== null) { + if (item._idlePrev) { item._idlePrev._idleNext = item._idleNext; } diff --git a/lib/timers.js b/lib/timers.js index 3cce2e37b007e8..73844fdbcc7613 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -283,7 +283,7 @@ function clearImmediate(immediate) { toggleImmediateRef(false); immediate[kRefed] = null; - if (destroyHooksExist()) { + if (destroyHooksExist() && immediate[async_id_symbol] !== undefined) { emitDestroy(immediate[async_id_symbol]); } diff --git a/test/parallel/test-repl-clear-immediate-crash.js b/test/parallel/test-repl-clear-immediate-crash.js new file mode 100644 index 00000000000000..ce8aaf48e7fa0e --- /dev/null +++ b/test/parallel/test-repl-clear-immediate-crash.js @@ -0,0 +1,12 @@ +'use strict'; +const common = require('../common'); +const child_process = require('child_process'); +const assert = require('assert'); + +// Regression test for https://github.com/nodejs/node/issues/37806: +const proc = child_process.spawn(process.execPath, ['-i']); +proc.on('error', common.mustNotCall()); +proc.on('exit', common.mustCall((code) => { + assert.strictEqual(code, 0); +})); +proc.stdin.write('clearImmediate({});\n.exit\n'); diff --git a/test/parallel/test-timers-clear-object-does-not-throw-error.js b/test/parallel/test-timers-clear-object-does-not-throw-error.js new file mode 100644 index 00000000000000..9752f53abd0755 --- /dev/null +++ b/test/parallel/test-timers-clear-object-does-not-throw-error.js @@ -0,0 +1,8 @@ +'use strict'; +require('../common'); + +// This test makes sure clearing timers with +// objects doesn't throw +clearImmediate({}); +clearTimeout({}); +clearInterval({}); From 13ecff63d6ff2da3b3f4e02c2c4b373cb557f994 Mon Sep 17 00:00:00 2001 From: David Glasser <glasser@davidglasser.net> Date: Wed, 24 Mar 2021 16:07:26 -0700 Subject: [PATCH 76/85] src: document newer values for --unhandled-rejections flag These values were added in v15.0.0 by #33475 but were not included in the `node --help` output. Also documents which value is the default. PR-URL: https://github.com/nodejs/node/pull/37899 Fixes: https://github.com/nodejs/node/issues/37896 Refs: https://github.com/nodejs/node/issues/33475 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- src/node_options.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/node_options.cc b/src/node_options.cc index 2a67800ef172c0..a0bbb1681cc1b8 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -500,8 +500,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { &EnvironmentOptions::trace_warnings, kAllowedInEnvironment); AddOption("--unhandled-rejections", - "define unhandled rejections behavior. Options are 'strict' (raise " - "an error), 'warn' (enforce warnings) or 'none' (silence warnings)", + "define unhandled rejections behavior. Options are 'strict' " + "(always raise an error), 'throw' (raise an error unless " + "'unhandledRejection' hook is set), 'warn' (log a warning), 'none' " + "(silence warnings), 'warn-with-error-code' (log a warning and set " + "exit code 1 unless 'unhandledRejection' hook is set). (default: " + "throw)", &EnvironmentOptions::unhandled_rejections, kAllowedInEnvironment); AddOption("--verify-base-objects", From b6ad8e4cc1b29fe3ac318f2830bee7e3d9ea6970 Mon Sep 17 00:00:00 2001 From: David Glasser <glasser@davidglasser.net> Date: Thu, 25 Mar 2021 07:46:28 -0700 Subject: [PATCH 77/85] src: indent long help text properly The previous code passed an ignored argument to StringPrototypeTrimLeft, and tried to trim a string that didn't start with whitespace. The trim makes more sense after the indentation has been added. Now wrapped lines actually show up with the rest of the help text. Doing this made an uncharacteristic trailing newline in the `--icu-data-dir` help text more obvious, so I removed that. PR-URL: https://github.com/nodejs/node/pull/37911 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/internal/main/print_help.js | 4 ++-- src/node_options.cc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/internal/main/print_help.js b/lib/internal/main/print_help.js index 87db8a7cff7592..029701307980b1 100644 --- a/lib/internal/main/print_help.js +++ b/lib/internal/main/print_help.js @@ -152,8 +152,8 @@ function format( else text += StringPrototypeRepeat(' ', firstColumn - displayName.length); - text += indent(StringPrototypeTrimLeft(fold(displayHelpText, secondColumn), - firstColumn)) + '\n'; + text += StringPrototypeTrimLeft( + indent(fold(displayHelpText, secondColumn), firstColumn)) + '\n'; } if (maxFirstColumnUsed < firstColumn - 4) { diff --git a/src/node_options.cc b/src/node_options.cc index a0bbb1681cc1b8..d5c2600688767b 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -729,7 +729,7 @@ PerProcessOptionsParser::PerProcessOptionsParser( AddOption("--icu-data-dir", "set ICU data load path to dir (overrides NODE_ICU_DATA)" #ifndef NODE_HAVE_SMALL_ICU - " (note: linked-in ICU data is present)\n" + " (note: linked-in ICU data is present)" #endif , &PerProcessOptions::icu_data_dir, From 659fc5d6848ce38a942ee8519cc3f532acc968a1 Mon Sep 17 00:00:00 2001 From: marsonya <akhil.marsonya27@gmail.com> Date: Mon, 8 Mar 2021 21:39:56 +0530 Subject: [PATCH 78/85] doc: fix typos in lib/internal/bootstrap/pre_execution.js PR-URL: https://github.com/nodejs/node/pull/37658 Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Pooja D P <Pooja.D.P@ibm.com> --- lib/internal/bootstrap/pre_execution.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js index 23b328b14487e4..694ef44c325331 100644 --- a/lib/internal/bootstrap/pre_execution.js +++ b/lib/internal/bootstrap/pre_execution.js @@ -236,9 +236,9 @@ function setupInspectorHooks() { } } -// In general deprecations are intialized wherever the APIs are implemented, +// In general deprecations are initialized wherever the APIs are implemented, // this is used to deprecate APIs implemented in C++ where the deprecation -// utitlities are not easily accessible. +// utilities are not easily accessible. function initializeDeprecations() { const { deprecate } = require('internal/util'); const pendingDeprecation = getOptionValue('--pending-deprecation'); From d33f446abded4a71f6ae180b30c954e455b5aed8 Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Fri, 26 Mar 2021 20:18:06 -0700 Subject: [PATCH 79/85] util: remove unreachable inspect code Convert invariant from if statement to an assertion. The condition is believed to be impossible to trigger. PR-URL: https://github.com/nodejs/node/pull/37941 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/internal/util/inspect.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index fed88e963a178c..a22f1e7a1b6e9b 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -1412,9 +1412,7 @@ function formatNamespaceObject(keys, ctx, value, recurseTimes) { output[i] = formatProperty(ctx, value, recurseTimes, keys[i], kObjectType); } catch (err) { - if (!(isNativeError(err) && err.name === 'ReferenceError')) { - throw err; - } + assert(isNativeError(err) && err.name === 'ReferenceError'); // Use the existing functionality. This makes sure the indentation and // line breaks are always correct. Otherwise it is very difficult to keep // this aligned, even though this is a hacky way of dealing with this. From e60bd1a7dc5e51c08f9fc188558e5532602ac2bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= <targos@protonmail.com> Date: Sat, 6 Mar 2021 10:07:54 +0100 Subject: [PATCH 80/85] perf_hooks: make Performance extend EventTarget Refs: https://www.w3.org/TR/hr-time/#sec-performance PR-URL: https://github.com/nodejs/node/pull/37621 Backport-PR-URL: https://github.com/nodejs/node/pull/37832 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> --- lib/perf_hooks.js | 6 +++++- test/wpt/status/hr-time.json | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/perf_hooks.js b/lib/perf_hooks.js index 6a759bbbf4e2d6..474cdadc772983 100644 --- a/lib/perf_hooks.js +++ b/lib/perf_hooks.js @@ -54,6 +54,9 @@ const { NODE_PERFORMANCE_MILESTONE_ENVIRONMENT } = constants; +const { + EventTarget, +} = require('internal/event_target'); const L = require('internal/linkedlist'); const kInspect = require('internal/util').customInspectSymbol; @@ -418,8 +421,9 @@ class PerformanceObserver { } } -class Performance { +class Performance extends EventTarget { constructor() { + super(); this[kIndex] = { [kMarks]: new SafeSet() }; diff --git a/test/wpt/status/hr-time.json b/test/wpt/status/hr-time.json index 4910d925b5be94..cb9f26a2ebca7e 100644 --- a/test/wpt/status/hr-time.json +++ b/test/wpt/status/hr-time.json @@ -1,7 +1,4 @@ { - "basic.any.js": { - "fail": "self.performance.addEventListener is not a function" - }, "idlharness.any.js": { "skip": "TODO: update IDL parser" }, From b0226b39f2bd56b7249f3b63b879ca1deef0adcb Mon Sep 17 00:00:00 2001 From: Rich Trott <rtrott@gmail.com> Date: Fri, 26 Mar 2021 22:47:50 -0700 Subject: [PATCH 81/85] test: split promisified timers test for coverage purposes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because of lazy loading, running promisified timers tests for setTimeout and setImmediate from the same file means that there is a piece of code that doesn't get covered. Split into separate files to cover everything. Refs: https://coverage.nodejs.org/coverage-290c158018ac0277/lib/timers.js.html#L269 PR-URL: https://github.com/nodejs/node/pull/37943 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> --- .../test-timers-immediate-promisified.js | 99 +++++++++++ ...js => test-timers-interval-promisified.js} | 160 ------------------ .../test-timers-timeout-promisified.js | 99 +++++++++++ 3 files changed, 198 insertions(+), 160 deletions(-) create mode 100644 test/parallel/test-timers-immediate-promisified.js rename test/parallel/{test-timers-promisified.js => test-timers-interval-promisified.js} (64%) create mode 100644 test/parallel/test-timers-timeout-promisified.js diff --git a/test/parallel/test-timers-immediate-promisified.js b/test/parallel/test-timers-immediate-promisified.js new file mode 100644 index 00000000000000..65c8411f1b2ffd --- /dev/null +++ b/test/parallel/test-timers-immediate-promisified.js @@ -0,0 +1,99 @@ +// Flags: --no-warnings --expose-internals +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const timers = require('timers'); +const { promisify } = require('util'); +const child_process = require('child_process'); + +// TODO(benjamingr) - refactor to use getEventListeners when #35991 lands +const { NodeEventTarget } = require('internal/event_target'); + +const timerPromises = require('timers/promises'); + +const setPromiseImmediate = promisify(timers.setImmediate); +const exec = promisify(child_process.exec); + +assert.strictEqual(setPromiseImmediate, timerPromises.setImmediate); + +process.on('multipleResolves', common.mustNotCall()); + +{ + const promise = setPromiseImmediate(); + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + })); +} + +{ + const promise = setPromiseImmediate('foobar'); + promise.then(common.mustCall((value) => { + assert.strictEqual(value, 'foobar'); + })); +} + +{ + const ac = new AbortController(); + const signal = ac.signal; + assert.rejects(setPromiseImmediate(10, { signal }), /AbortError/) + .then(common.mustCall()); + ac.abort(); +} + +{ + const signal = AbortSignal.abort(); // Abort in advance + assert.rejects(setPromiseImmediate(10, { signal }), /AbortError/) + .then(common.mustCall()); +} + +{ + // Check that aborting after resolve will not reject. + const ac = new AbortController(); + const signal = ac.signal; + setPromiseImmediate(10, { signal }) + .then(common.mustCall(() => { ac.abort(); })) + .then(common.mustCall()); +} + +{ + // Check that timer adding signals does not leak handlers + const signal = new NodeEventTarget(); + signal.aborted = false; + setPromiseImmediate(0, { signal }).finally(common.mustCall(() => { + assert.strictEqual(signal.listenerCount('abort'), 0); + })); +} + +{ + Promise.all( + [1, '', false, Infinity].map( + (i) => assert.rejects(setPromiseImmediate(10, i), { + code: 'ERR_INVALID_ARG_TYPE' + }) + ) + ).then(common.mustCall()); + + Promise.all( + [1, '', false, Infinity, null, {}].map( + (signal) => assert.rejects(setPromiseImmediate(10, { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }) + ) + ).then(common.mustCall()); + + Promise.all( + [1, '', Infinity, null, {}].map( + (ref) => assert.rejects(setPromiseImmediate(10, { ref }), { + code: 'ERR_INVALID_ARG_TYPE' + }) + ) + ).then(common.mustCall()); +} + +{ + exec(`${process.execPath} -pe "const assert = require('assert');` + + 'require(\'timers/promises\').setImmediate(null, { ref: false }).' + + 'then(assert.fail)"').then(common.mustCall(({ stderr }) => { + assert.strictEqual(stderr, ''); + })); +} diff --git a/test/parallel/test-timers-promisified.js b/test/parallel/test-timers-interval-promisified.js similarity index 64% rename from test/parallel/test-timers-promisified.js rename to test/parallel/test-timers-interval-promisified.js index 45da5036727061..60511372485963 100644 --- a/test/parallel/test-timers-promisified.js +++ b/test/parallel/test-timers-interval-promisified.js @@ -12,43 +12,12 @@ const { NodeEventTarget } = require('internal/event_target'); const timerPromises = require('timers/promises'); const setPromiseTimeout = promisify(timers.setTimeout); -const setPromiseImmediate = promisify(timers.setImmediate); const exec = promisify(child_process.exec); -assert.strictEqual(setPromiseTimeout, timerPromises.setTimeout); -assert.strictEqual(setPromiseImmediate, timerPromises.setImmediate); const { setInterval } = timerPromises; process.on('multipleResolves', common.mustNotCall()); -{ - const promise = setPromiseTimeout(1); - promise.then(common.mustCall((value) => { - assert.strictEqual(value, undefined); - })); -} - -{ - const promise = setPromiseTimeout(1, 'foobar'); - promise.then(common.mustCall((value) => { - assert.strictEqual(value, 'foobar'); - })); -} - -{ - const promise = setPromiseImmediate(); - promise.then(common.mustCall((value) => { - assert.strictEqual(value, undefined); - })); -} - -{ - const promise = setPromiseImmediate('foobar'); - promise.then(common.mustCall((value) => { - assert.strictEqual(value, 'foobar'); - })); -} - { const iterable = setInterval(1, undefined); const iterator = iterable[Symbol.asyncIterator](); @@ -89,34 +58,6 @@ process.on('multipleResolves', common.mustNotCall()); .then(common.mustCall()); } -{ - const ac = new AbortController(); - const signal = ac.signal; - assert.rejects(setPromiseTimeout(10, undefined, { signal }), /AbortError/) - .then(common.mustCall()); - ac.abort(); -} - -{ - const signal = AbortSignal.abort(); // Abort in advance - assert.rejects(setPromiseTimeout(10, undefined, { signal }), /AbortError/) - .then(common.mustCall()); -} - -{ - const ac = new AbortController(); - const signal = ac.signal; - assert.rejects(setPromiseImmediate(10, { signal }), /AbortError/) - .then(common.mustCall()); - ac.abort(); -} - -{ - const signal = AbortSignal.abort(); // Abort in advance - assert.rejects(setPromiseImmediate(10, { signal }), /AbortError/) - .then(common.mustCall()); -} - { const signal = AbortSignal.abort(); // Abort in advance @@ -155,23 +96,6 @@ process.on('multipleResolves', common.mustNotCall()); assert.rejects(abortPromise, /AbortError/).then(common.mustCall()); } -{ - // Check that aborting after resolve will not reject. - const ac = new AbortController(); - const signal = ac.signal; - setPromiseTimeout(10, undefined, { signal }) - .then(common.mustCall(() => { ac.abort(); })) - .then(common.mustCall()); -} -{ - // Check that aborting after resolve will not reject. - const ac = new AbortController(); - const signal = ac.signal; - setPromiseImmediate(10, { signal }) - .then(common.mustCall(() => { ac.abort(); })) - .then(common.mustCall()); -} - { [1, '', Infinity, null, {}].forEach((ref) => { const iterable = setInterval(10, undefined, { ref }); @@ -192,24 +116,6 @@ process.on('multipleResolves', common.mustNotCall()); }); } -{ - // Check that timer adding signals does not leak handlers - const signal = new NodeEventTarget(); - signal.aborted = false; - setPromiseTimeout(0, null, { signal }).finally(common.mustCall(() => { - assert.strictEqual(signal.listenerCount('abort'), 0); - })); -} - -{ - // Check that timer adding signals does not leak handlers - const signal = new NodeEventTarget(); - signal.aborted = false; - setPromiseImmediate(0, { signal }).finally(common.mustCall(() => { - assert.strictEqual(signal.listenerCount('abort'), 0); - })); -} - { // Check that timer adding signals does not leak handlers const signal = new NodeEventTarget(); @@ -247,72 +153,6 @@ process.on('multipleResolves', common.mustNotCall()); tryBreak().then(common.mustCall()); } -{ - Promise.all( - [1, '', false, Infinity].map( - (i) => assert.rejects(setPromiseImmediate(10, i), { - code: 'ERR_INVALID_ARG_TYPE' - }) - ) - ).then(common.mustCall()); - - Promise.all( - [1, '', false, Infinity, null, {}].map( - (signal) => assert.rejects(setPromiseImmediate(10, { signal }), { - code: 'ERR_INVALID_ARG_TYPE' - }) - ) - ).then(common.mustCall()); - - Promise.all( - [1, '', Infinity, null, {}].map( - (ref) => assert.rejects(setPromiseImmediate(10, { ref }), { - code: 'ERR_INVALID_ARG_TYPE' - }) - ) - ).then(common.mustCall()); - - Promise.all( - [1, '', false, Infinity].map( - (i) => assert.rejects(setPromiseTimeout(10, null, i), { - code: 'ERR_INVALID_ARG_TYPE' - }) - ) - ).then(common.mustCall()); - - Promise.all( - [1, '', false, Infinity, null, {}].map( - (signal) => assert.rejects(setPromiseTimeout(10, null, { signal }), { - code: 'ERR_INVALID_ARG_TYPE' - }) - ) - ).then(common.mustCall()); - - Promise.all( - [1, '', Infinity, null, {}].map( - (ref) => assert.rejects(setPromiseTimeout(10, null, { ref }), { - code: 'ERR_INVALID_ARG_TYPE' - }) - ) - ).then(common.mustCall()); -} - -{ - exec(`${process.execPath} -pe "const assert = require('assert');` + - 'require(\'timers/promises\').setTimeout(1000, null, { ref: false }).' + - 'then(assert.fail)"').then(common.mustCall(({ stderr }) => { - assert.strictEqual(stderr, ''); - })); -} - -{ - exec(`${process.execPath} -pe "const assert = require('assert');` + - 'require(\'timers/promises\').setImmediate(null, { ref: false }).' + - 'then(assert.fail)"').then(common.mustCall(({ stderr }) => { - assert.strictEqual(stderr, ''); - })); -} - { exec(`${process.execPath} -pe "const assert = require('assert');` + 'const interval = require(\'timers/promises\')' + diff --git a/test/parallel/test-timers-timeout-promisified.js b/test/parallel/test-timers-timeout-promisified.js new file mode 100644 index 00000000000000..0b9a6b6f19a1c2 --- /dev/null +++ b/test/parallel/test-timers-timeout-promisified.js @@ -0,0 +1,99 @@ +// Flags: --no-warnings --expose-internals +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const timers = require('timers'); +const { promisify } = require('util'); +const child_process = require('child_process'); + +// TODO(benjamingr) - refactor to use getEventListeners when #35991 lands +const { NodeEventTarget } = require('internal/event_target'); + +const timerPromises = require('timers/promises'); + +const setPromiseTimeout = promisify(timers.setTimeout); +const exec = promisify(child_process.exec); + +assert.strictEqual(setPromiseTimeout, timerPromises.setTimeout); + +process.on('multipleResolves', common.mustNotCall()); + +{ + const promise = setPromiseTimeout(1); + promise.then(common.mustCall((value) => { + assert.strictEqual(value, undefined); + })); +} + +{ + const promise = setPromiseTimeout(1, 'foobar'); + promise.then(common.mustCall((value) => { + assert.strictEqual(value, 'foobar'); + })); +} + +{ + const ac = new AbortController(); + const signal = ac.signal; + assert.rejects(setPromiseTimeout(10, undefined, { signal }), /AbortError/) + .then(common.mustCall()); + ac.abort(); +} + +{ + const signal = AbortSignal.abort(); // Abort in advance + assert.rejects(setPromiseTimeout(10, undefined, { signal }), /AbortError/) + .then(common.mustCall()); +} + +{ + // Check that aborting after resolve will not reject. + const ac = new AbortController(); + const signal = ac.signal; + setPromiseTimeout(10, undefined, { signal }) + .then(common.mustCall(() => { ac.abort(); })) + .then(common.mustCall()); +} + +{ + // Check that timer adding signals does not leak handlers + const signal = new NodeEventTarget(); + signal.aborted = false; + setPromiseTimeout(0, null, { signal }).finally(common.mustCall(() => { + assert.strictEqual(signal.listenerCount('abort'), 0); + })); +} + +{ + Promise.all( + [1, '', false, Infinity].map( + (i) => assert.rejects(setPromiseTimeout(10, null, i), { + code: 'ERR_INVALID_ARG_TYPE' + }) + ) + ).then(common.mustCall()); + + Promise.all( + [1, '', false, Infinity, null, {}].map( + (signal) => assert.rejects(setPromiseTimeout(10, null, { signal }), { + code: 'ERR_INVALID_ARG_TYPE' + }) + ) + ).then(common.mustCall()); + + Promise.all( + [1, '', Infinity, null, {}].map( + (ref) => assert.rejects(setPromiseTimeout(10, null, { ref }), { + code: 'ERR_INVALID_ARG_TYPE' + }) + ) + ).then(common.mustCall()); +} + +{ + exec(`${process.execPath} -pe "const assert = require('assert');` + + 'require(\'timers/promises\').setTimeout(1000, null, { ref: false }).' + + 'then(assert.fail)"').then(common.mustCall(({ stderr }) => { + assert.strictEqual(stderr, ''); + })); +} From f2cef54b6f93526c9c33db6bb2bf203c6e40859b Mon Sep 17 00:00:00 2001 From: Ruy Adorno <ruyadorno@hotmail.com> Date: Mon, 29 Mar 2021 15:27:51 -0400 Subject: [PATCH 82/85] deps: upgrade npm to 7.7.6 PR-URL: https://github.com/nodejs/node/pull/37968 Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- deps/npm/AUTHORS | 2 + deps/npm/CHANGELOG.md | 28 ++++++++++++++ deps/npm/docs/content/using-npm/config.md | 2 +- deps/npm/docs/content/using-npm/workspaces.md | 2 +- deps/npm/docs/output/commands/npm-ls.html | 2 +- deps/npm/docs/output/commands/npm.html | 2 +- deps/npm/docs/output/using-npm/config.html | 2 +- .../npm/docs/output/using-npm/workspaces.html | 2 +- deps/npm/lib/cli.js | 4 +- deps/npm/lib/utils/config/definitions.js | 2 +- deps/npm/man/man1/npm-ls.1 | 2 +- deps/npm/man/man1/npm.1 | 2 +- deps/npm/man/man7/config.7 | 2 +- deps/npm/man/man7/workspaces.7 | 2 +- .../metavuln-calculator/lib/get-dep-spec.js | 6 +-- .../@npmcli/metavuln-calculator/package.json | 2 +- deps/npm/node_modules/libnpmversion/README.md | 5 +++ .../node_modules/libnpmversion/lib/index.js | 2 + .../node_modules/libnpmversion/package.json | 2 +- deps/npm/node_modules/npm-packlist/index.js | 9 ++++- .../node_modules/npm-packlist/package.json | 4 +- deps/npm/package.json | 8 ++-- .../smoke-tests-index.js-TAP.test.js | 37 +++++++++++++++++++ ...b-utils-config-describe-all.js-TAP.test.js | 2 +- deps/npm/test/lib/cli.js | 29 +++++++++++++-- 25 files changed, 131 insertions(+), 31 deletions(-) diff --git a/deps/npm/AUTHORS b/deps/npm/AUTHORS index c4a8c4e45ec94f..a257559bbf593f 100644 --- a/deps/npm/AUTHORS +++ b/deps/npm/AUTHORS @@ -768,3 +768,5 @@ kbayrhammer <klaus.bayrhammer@redbull.com> James Chen-Smith <jameschensmith@gmail.com> Yash Singh <saiansh2525@gmail.com> Danielle Church <dani.church@gmail.com> +Seth Thomas <seth@emailseth.com> +Andreas <andreas@bielk.se> diff --git a/deps/npm/CHANGELOG.md b/deps/npm/CHANGELOG.md index 74f5b5f1c58ea7..b4fbc426b7811b 100644 --- a/deps/npm/CHANGELOG.md +++ b/deps/npm/CHANGELOG.md @@ -1,3 +1,31 @@ +## v7.7.6 (2021-03-29) + +### BUG FIXES + +* [`9dd2ed518`](https://github.com/npm/cli/commit/9dd2ed5189b6f283094664e9e192cf1598ec3f79) + fix empty newline printed to stderr + ([@ruyadorno](https://github.com/ruyadorno)) +* [`9d391462a`](https://github.com/npm/cli/commit/9d391462a25f637219501e2430ef1f7b89710816) + [#2973](https://github.com/npm/cli/issues/2973) + fix spelling in workspaces.md file + ([@sethomas](https://github.com/sethomas)) +* [`4b100249a`](https://github.com/npm/cli/commit/4b100249a6cad67e002186816e64817313b636c7) + [#2979](https://github.com/npm/cli/issues/2979) + change 'maxsockets' default value back to 15 + ([@wallrat](https://github.com/wallrat)) + +### DEPENDENCIES + +* [`a28f89572`](https://github.com/npm/cli/commit/a28f89572a708cced69cc938f877eaa969dbad9e) + `libnpmversion@1.1.0` + * fix reading `script-shell` config on `npm version` lifecycle scripts +* [`03734c29e`](https://github.com/npm/cli/commit/03734c29e00191d17f164d1c0e75d9f228268842) + `npm-packlist@2.1.5` + * fix packaging `bundledDependencies` +* [`80ce2a019`](https://github.com/npm/cli/commit/80ce2a019526632b01b70e1c75c42608dc160332) + `@npmcli/metavuln-calculator@1.1.1` + * fix error auditing package documents with missing dependencies + ## v7.7.5 (2021-03-25) ### BUG FIXES diff --git a/deps/npm/docs/content/using-npm/config.md b/deps/npm/docs/content/using-npm/config.md index c701a361afbb94..cfce5396f40a7f 100644 --- a/deps/npm/docs/content/using-npm/config.md +++ b/deps/npm/docs/content/using-npm/config.md @@ -795,7 +795,7 @@ Show extended information in `npm ls` and `npm search`. #### `maxsockets` -* Default: Infinity +* Default: 15 * Type: Number The maximum number of connections to use per origin (protocol/host/port diff --git a/deps/npm/docs/content/using-npm/workspaces.md b/deps/npm/docs/content/using-npm/workspaces.md index 28fccd2200c322..ab37cc16c22c66 100644 --- a/deps/npm/docs/content/using-npm/workspaces.md +++ b/deps/npm/docs/content/using-npm/workspaces.md @@ -90,7 +90,7 @@ nested workspaces to be consumed elsewhere. ### Running commands in the context of workspaces -You man use the `workspace` configuration option to run commands in the context +You can use the `workspace` configuration option to run commands in the context of a configured workspace. Following is a quick example on how to use the `npm run` command in the context diff --git a/deps/npm/docs/output/commands/npm-ls.html b/deps/npm/docs/output/commands/npm-ls.html index 9b4aa1b0abf45a..914d1d1214482b 100644 --- a/deps/npm/docs/output/commands/npm-ls.html +++ b/deps/npm/docs/output/commands/npm-ls.html @@ -159,7 +159,7 @@ <h3 id="description">Description</h3> the results to only the paths to the packages named. Note that nested packages will <em>also</em> show the paths to the specified packages. For example, running <code>npm ls promzard</code> in npm’s source tree will show:</p> -<pre lang="bash"><code>npm@7.7.5 /path/to/npm +<pre lang="bash"><code>npm@7.7.6 /path/to/npm └─┬ init-package-json@0.0.4 └── promzard@0.1.5 </code></pre> diff --git a/deps/npm/docs/output/commands/npm.html b/deps/npm/docs/output/commands/npm.html index e6cd6878a22820..c71daa36198c67 100644 --- a/deps/npm/docs/output/commands/npm.html +++ b/deps/npm/docs/output/commands/npm.html @@ -148,7 +148,7 @@ <h2 id="table-of-contents">Table of contents</h2> <pre lang="bash"><code>npm <command> [args] </code></pre> <h3 id="version">Version</h3> -<p>7.7.5</p> +<p>7.7.6</p> <h3 id="description">Description</h3> <p>npm is the package manager for the Node JavaScript platform. It puts modules in place so that node can find them, and manages dependency diff --git a/deps/npm/docs/output/using-npm/config.html b/deps/npm/docs/output/using-npm/config.html index 6fe8f828157683..717f1021e5c36a 100644 --- a/deps/npm/docs/output/using-npm/config.html +++ b/deps/npm/docs/output/using-npm/config.html @@ -811,7 +811,7 @@ <h4 id="long"><code>long</code></h4> <p>Show extended information in <code>npm ls</code> and <code>npm search</code>.</p> <h4 id="maxsockets"><code>maxsockets</code></h4> <ul> -<li>Default: Infinity</li> +<li>Default: 15</li> <li>Type: Number</li> </ul> <p>The maximum number of connections to use per origin (protocol/host/port diff --git a/deps/npm/docs/output/using-npm/workspaces.html b/deps/npm/docs/output/using-npm/workspaces.html index 0fb1391c7970c7..66ab8b1f793692 100644 --- a/deps/npm/docs/output/using-npm/workspaces.html +++ b/deps/npm/docs/output/using-npm/workspaces.html @@ -207,7 +207,7 @@ <h3 id="using-workspaces">Using workspaces</h3> in such a way that is also easy to <a href="../commands/npm-publish.html">publish</a> these nested workspaces to be consumed elsewhere.</p> <h3 id="running-commands-in-the-context-of-workspaces">Running commands in the context of workspaces</h3> -<p>You man use the <code>workspace</code> configuration option to run commands in the context +<p>You can use the <code>workspace</code> configuration option to run commands in the context of a configured workspace.</p> <p>Following is a quick example on how to use the <code>npm run</code> command in the context of nested workspaces. For a project containing multiple workspaces, e.g:</p> diff --git a/deps/npm/lib/cli.js b/deps/npm/lib/cli.js index 46859f150e3b9d..f42132f9443900 100644 --- a/deps/npm/lib/cli.js +++ b/deps/npm/lib/cli.js @@ -61,9 +61,6 @@ module.exports = (process) => { impl(npm.argv, errorHandler) else { try { - // I don't know why this is needed but we get a cb() not called if we - // omit it - npm.log.level = 'silent' if (cmd) { const didYouMean = require('./utils/did-you-mean.js') const suggestions = await didYouMean(npm, npm.localPrefix, cmd) @@ -71,6 +68,7 @@ module.exports = (process) => { } else npm.output(npm.usage) process.exitCode = 1 + return errorHandler() } catch (err) { errorHandler(err) } diff --git a/deps/npm/lib/utils/config/definitions.js b/deps/npm/lib/utils/config/definitions.js index 512ea8af98cb4e..db66aa495ba0f4 100644 --- a/deps/npm/lib/utils/config/definitions.js +++ b/deps/npm/lib/utils/config/definitions.js @@ -1154,7 +1154,7 @@ define('long', { }) define('maxsockets', { - default: Infinity, + default: 15, type: Number, description: ` The maximum number of connections to use per origin (protocol/host/port diff --git a/deps/npm/man/man1/npm-ls.1 b/deps/npm/man/man1/npm-ls.1 index b1fd65b15be901..61b45baff32442 100644 --- a/deps/npm/man/man1/npm-ls.1 +++ b/deps/npm/man/man1/npm-ls.1 @@ -26,7 +26,7 @@ example, running \fBnpm ls promzard\fP in npm's source tree will show: .P .RS 2 .nf -npm@7\.7\.5 /path/to/npm +npm@7\.7\.6 /path/to/npm └─┬ init\-package\-json@0\.0\.4 └── promzard@0\.1\.5 .fi diff --git a/deps/npm/man/man1/npm.1 b/deps/npm/man/man1/npm.1 index 81e5ab92149e27..db27e4087d1e47 100644 --- a/deps/npm/man/man1/npm.1 +++ b/deps/npm/man/man1/npm.1 @@ -10,7 +10,7 @@ npm <command> [args] .RE .SS Version .P -7\.7\.5 +7\.7\.6 .SS Description .P npm is the package manager for the Node JavaScript platform\. It puts diff --git a/deps/npm/man/man7/config.7 b/deps/npm/man/man7/config.7 index bd581c88809487..d232871c0c5df9 100644 --- a/deps/npm/man/man7/config.7 +++ b/deps/npm/man/man7/config.7 @@ -1065,7 +1065,7 @@ Show extended information in \fBnpm ls\fP and \fBnpm search\fP\|\. .SS \fBmaxsockets\fP .RS 0 .IP \(bu 2 -Default: Infinity +Default: 15 .IP \(bu 2 Type: Number diff --git a/deps/npm/man/man7/workspaces.7 b/deps/npm/man/man7/workspaces.7 index 2fa6a9ccccdd9e..f8c5a0c4d44740 100644 --- a/deps/npm/man/man7/workspaces.7 +++ b/deps/npm/man/man7/workspaces.7 @@ -92,7 +92,7 @@ in such a way that is also easy to npm help publish these nested workspaces to be consumed elsewhere\. .SS Running commands in the context of workspaces .P -You man use the \fBworkspace\fP configuration option to run commands in the context +You can use the \fBworkspace\fP configuration option to run commands in the context of a configured workspace\. .P Following is a quick example on how to use the \fBnpm run\fP command in the context diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js b/deps/npm/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js index 35e83d02a1b632..8d1d72b8c46eb5 100644 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js +++ b/deps/npm/node_modules/@npmcli/metavuln-calculator/lib/get-dep-spec.js @@ -8,8 +8,8 @@ module.exports = (mani, name) => { peerDependencies: peerDeps = {}, } = mani - return typeof deps[name] === 'string' ? deps[name] - : typeof optDeps[name] === 'string' ? optDeps[name] - : typeof peerDeps[name] === 'string' ? peerDeps[name] + return deps && typeof deps[name] === 'string' ? deps[name] + : optDeps && typeof optDeps[name] === 'string' ? optDeps[name] + : peerDeps && typeof peerDeps[name] === 'string' ? peerDeps[name] : null } diff --git a/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json b/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json index f7a4f5cc47a7e6..4ad6193ae6aa8e 100644 --- a/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json +++ b/deps/npm/node_modules/@npmcli/metavuln-calculator/package.json @@ -1,6 +1,6 @@ { "name": "@npmcli/metavuln-calculator", - "version": "1.1.0", + "version": "1.1.1", "main": "lib/index.js", "files": [ "lib" diff --git a/deps/npm/node_modules/libnpmversion/README.md b/deps/npm/node_modules/libnpmversion/README.md index daa0b88157c6c3..e82e7cd6f8730f 100644 --- a/deps/npm/node_modules/libnpmversion/README.md +++ b/deps/npm/node_modules/libnpmversion/README.md @@ -25,6 +25,7 @@ npmVersion(arg, { signGitTag: false, // default false, gpg sign the git tag force: false, // push forward recklessly if any problems happen ignoreScripts: false, // do not run pre/post/version lifecycle scripts + scriptShell: '/bin/bash', // shell to run lifecycle scripts in message: 'v%s', // message for tag and commit, replace %s with the version }).then(newVersion => { console.error('version updated!', newVersion) @@ -149,6 +150,10 @@ Push forward recklessly if any problems happen. Default `false`. Do not run pre/post/version lifecycle scripts. Default `false`. +#### `scriptShell` String + +Path to the shell, which should execute the lifecycle scripts. Defaults to `/bin/sh` on unix, or `cmd.exe` on windows. + #### `message` String The message for the git commit and annotated git tag that are created. diff --git a/deps/npm/node_modules/libnpmversion/lib/index.js b/deps/npm/node_modules/libnpmversion/lib/index.js index c3f554834bf81d..683941cdea4f35 100644 --- a/deps/npm/node_modules/libnpmversion/lib/index.js +++ b/deps/npm/node_modules/libnpmversion/lib/index.js @@ -13,6 +13,7 @@ module.exports = async (newversion, opts = {}) => { signGitTag = false, force = false, ignoreScripts = false, + scriptShell = undefined, preid = null, log = proclog, message = 'v%s', @@ -31,6 +32,7 @@ module.exports = async (newversion, opts = {}) => { signGitTag, force, ignoreScripts, + scriptShell, preid, pkg, log, diff --git a/deps/npm/node_modules/libnpmversion/package.json b/deps/npm/node_modules/libnpmversion/package.json index 0135c21e7232cb..30d94c7a14699e 100644 --- a/deps/npm/node_modules/libnpmversion/package.json +++ b/deps/npm/node_modules/libnpmversion/package.json @@ -1,6 +1,6 @@ { "name": "libnpmversion", - "version": "1.0.12", + "version": "1.1.0", "main": "lib/index.js", "files": [ "lib/*.js" diff --git a/deps/npm/node_modules/npm-packlist/index.js b/deps/npm/node_modules/npm-packlist/index.js index cf87b18528b013..8f62983e6f6c0f 100644 --- a/deps/npm/node_modules/npm-packlist/index.js +++ b/deps/npm/node_modules/npm-packlist/index.js @@ -182,6 +182,13 @@ const npmWalker = Class => class Walker extends Class { getPackageFiles (entries, pkg) { try { + // XXX this could be changed to use read-package-json-fast + // which handles the normalizing of bins for us, and simplifies + // the test for bundleDependencies and bundledDependencies later. + // HOWEVER if we do this, we need to be sure that we're careful + // about what we write back out since rpj-fast removes some fields + // that the user likely wants to keep. it also would add a second + // file read that we would want to optimize away. pkg = normalizePackageBin(JSON.parse(pkg.toString())) } catch (er) { // not actually a valid package.json @@ -202,7 +209,7 @@ const npmWalker = Class => class Walker extends Class { // the files list as the effective readdir result, that means it // looks like we don't have a node_modules folder at all unless we // include it here. - if (pkg.bundleDependencies && entries.includes('node_modules')) + if ((pkg.bundleDependencies || pkg.bundledDependencies) && entries.includes('node_modules')) pkg.files.push('node_modules') const patterns = Array.from(new Set(pkg.files)).reduce((set, pattern) => { diff --git a/deps/npm/node_modules/npm-packlist/package.json b/deps/npm/node_modules/npm-packlist/package.json index 6f60521059a048..1276b484136992 100644 --- a/deps/npm/node_modules/npm-packlist/package.json +++ b/deps/npm/node_modules/npm-packlist/package.json @@ -1,6 +1,6 @@ { "name": "npm-packlist", - "version": "2.1.4", + "version": "2.1.5", "description": "Get a list of the files to add from a folder into an npm package", "directories": { "test": "test" @@ -20,7 +20,7 @@ "devDependencies": { "mutate-fs": "^2.1.1", "require-inject": "^1.4.4", - "tap": "^14.10.7" + "tap": "^14.10.8" }, "scripts": { "test": "tap", diff --git a/deps/npm/package.json b/deps/npm/package.json index 1e821ececafc40..0f078d18be5730 100644 --- a/deps/npm/package.json +++ b/deps/npm/package.json @@ -1,5 +1,5 @@ { - "version": "7.7.5", + "version": "7.7.6", "name": "npm", "description": "a package manager for JavaScript", "keywords": [ @@ -74,7 +74,7 @@ "libnpmpublish": "^4.0.0", "libnpmsearch": "^3.1.0", "libnpmteam": "^2.0.2", - "libnpmversion": "^1.0.12", + "libnpmversion": "^1.1.0", "make-fetch-happen": "^8.0.14", "minipass": "^3.1.3", "minipass-pipeline": "^1.2.4", @@ -180,12 +180,12 @@ "devDependencies": { "@mdx-js/mdx": "^1.6.22", "cmark-gfm": "^0.8.5", - "eslint": "^7.22.0", + "eslint": "^7.23.0", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.3.1", "eslint-plugin-standard": "^5.0.0", - "jsdom": "^16.5.1", + "jsdom": "^16.5.2", "licensee": "^8.1.0", "marked-man": "^0.7.0", "require-inject": "^1.4.4", diff --git a/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js b/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js index 4d840ceef6ada1..5c3fb8231114db 100644 --- a/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js +++ b/deps/npm/tap-snapshots/smoke-tests-index.js-TAP.test.js @@ -5,6 +5,43 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' +exports[`smoke-tests/index.js TAP npm (no args) > should have expected no args output 1`] = ` +npm <command> + +Usage: + +npm install install all the dependencies in your project +npm install <foo> add the <foo> dependency to your project +npm test run this project's tests +npm run <foo> run the script named <foo> +npm <command> -h quick help on <command> +npm -l display usage info for all commands +npm help <term> search for help on <term> +npm help npm more involved overview + +All commands: + + access, adduser, audit, bin, bugs, cache, ci, completion, + config, dedupe, deprecate, diff, dist-tag, docs, doctor, + edit, exec, explain, explore, find-dupes, fund, get, help, + hook, init, install, install-ci-test, install-test, link, + ll, login, logout, ls, org, outdated, owner, pack, ping, + prefix, profile, prune, publish, rebuild, repo, restart, + root, run-script, search, set, set-script, shrinkwrap, star, + stars, start, stop, team, test, token, uninstall, unpublish, + unstar, update, version, view, whoami + +Specify configs in the ini-formatted file: + {CWD}/smoke-tests/index/.npmrc +or on the command line via: npm <command> --key=value + +More configuration info: npm help config +Configuration fields: npm help 7 config + +npm {CWD} + +` + exports[`smoke-tests/index.js TAP npm diff > should have expected diff output 1`] = ` diff --git a/package.json b/package.json index v1.0.4..v1.1.1 100644 diff --git a/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js b/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js index a85f90ac84181a..2a3d0146b187dd 100644 --- a/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js +++ b/deps/npm/tap-snapshots/test-lib-utils-config-describe-all.js-TAP.test.js @@ -674,7 +674,7 @@ Show extended information in \`npm ls\` and \`npm search\`. #### \`maxsockets\` -* Default: Infinity +* Default: 15 * Type: Number The maximum number of connections to use per origin (protocol/host/port diff --git a/deps/npm/test/lib/cli.js b/deps/npm/test/lib/cli.js index 40da77bf44e3d8..28e44394e16dcf 100644 --- a/deps/npm/test/lib/cli.js +++ b/deps/npm/test/lib/cli.js @@ -172,17 +172,37 @@ t.test('gracefully handles error printing usage', t => { t.teardown(() => { npmock.output = output errorHandlerCb = null + errorHandlerCalled = null }) const proc = { - argv: ['node', 'npm', 'asdf'], + argv: ['node', 'npm'], on: () => {}, } npmock.argv = [] - npmock.output = (msg) => { - throw new Error('test exception') + errorHandlerCb = () => { + t.match(errorHandlerCalled, [], 'should call errorHandler with no args') + t.end() + } + cli(proc) +}) + +t.test('handles output error', t => { + const { output } = npmock + t.teardown(() => { + npmock.output = output + errorHandlerCb = null + errorHandlerCalled = null + }) + const proc = { + argv: ['node', 'npm'], + on: () => {}, + } + npmock.argv = [] + npmock.output = () => { + throw new Error('ERR') } errorHandlerCb = () => { - t.match(errorHandlerCalled, /test exception/) + t.match(errorHandlerCalled, /ERR/, 'should call errorHandler with error') t.end() } cli(proc) @@ -191,6 +211,7 @@ t.test('gracefully handles error printing usage', t => { t.test('load error calls error handler', t => { t.teardown(() => { errorHandlerCb = null + errorHandlerCalled = null LOAD_ERROR = null }) From 2fd97ce687c7656a9f44daef574c2c47665ebd41 Mon Sep 17 00:00:00 2001 From: Guy Bedford <guybedford@gmail.com> Date: Mon, 22 Mar 2021 18:47:10 +0200 Subject: [PATCH 83/85] deps: v8 backport 9689b17687b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [top-level-await] Implement spec fix for cycle root detection Refs: http://github.com/v8/v8/commit/9689b17687b21c800c3f7400df4255c55b9c6ec0 PR-URL: https://github.com/nodejs/node/pull/37865 Backport-PR-URL: https://github.com/nodejs/node/pull/37985 Reviewed-By: Michaël Zasso <targos@protonmail.com> --- common.gypi | 2 +- deps/v8/src/diagnostics/objects-printer.cc | 1 + deps/v8/src/heap/factory.cc | 1 + deps/v8/src/objects/module-inl.h | 8 +++ deps/v8/src/objects/source-text-module.cc | 71 ++++--------------- deps/v8/src/objects/source-text-module.h | 7 +- deps/v8/src/objects/source-text-module.tq | 5 ++ ...modules-import-rqstd-order-async-cycle.mjs | 12 ++++ .../harmony/modules-skip-async-cycle-1.mjs | 14 ++++ .../harmony/modules-skip-async-cycle-2.mjs | 12 ++++ .../harmony/modules-skip-async-cycle-3.mjs | 12 ++++ .../harmony/modules-skip-async-cycle-leaf.mjs | 13 ++++ .../modules-skip-async-cycle-start.mjs | 13 ++++ 13 files changed, 109 insertions(+), 62 deletions(-) create mode 100644 deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-cycle.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-1.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-2.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-3.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-leaf.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-start.mjs diff --git a/common.gypi b/common.gypi index 0235880edf83bb..902d69c6272fb7 100644 --- a/common.gypi +++ b/common.gypi @@ -36,7 +36,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.27', + 'v8_embedder_string': '-node.28', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/diagnostics/objects-printer.cc b/deps/v8/src/diagnostics/objects-printer.cc index db14898c1346be..843d65cd1f7f61 100644 --- a/deps/v8/src/diagnostics/objects-printer.cc +++ b/deps/v8/src/diagnostics/objects-printer.cc @@ -1601,6 +1601,7 @@ void SourceTextModule::SourceTextModulePrint(std::ostream& os) { // NOLINT os << "\n - requested_modules: " << Brief(requested_modules()); os << "\n - script: " << Brief(script()); os << "\n - import_meta: " << Brief(import_meta()); + os << "\n - cycle_root: " << Brief(cycle_root()); os << "\n"; } diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 4d344339f8638a..22dcb1838a5b0f 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -2505,6 +2505,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule( module->set_flags(0); module->set_async(IsAsyncModule(code->kind())); module->set_async_evaluating(false); + module->set_cycle_root(roots.the_hole_value()); module->set_async_parent_modules(*async_parent_modules); module->set_pending_async_dependencies(0); return module; diff --git a/deps/v8/src/objects/module-inl.h b/deps/v8/src/objects/module-inl.h index e627aedf18a8f2..d270a9cdfc3671 100644 --- a/deps/v8/src/objects/module-inl.h +++ b/deps/v8/src/objects/module-inl.h @@ -112,6 +112,14 @@ class UnorderedModuleSet ZoneAllocator<Handle<Module>>(zone)) {} }; +Handle<SourceTextModule> SourceTextModule::GetCycleRoot( + Isolate* isolate) const { + CHECK_GE(status(), kEvaluated); + DCHECK(!cycle_root().IsTheHole(isolate)); + Handle<SourceTextModule> root(SourceTextModule::cast(cycle_root()), isolate); + return root; +} + void SourceTextModule::AddAsyncParentModule(Isolate* isolate, Handle<SourceTextModule> module, Handle<SourceTextModule> parent) { diff --git a/deps/v8/src/objects/source-text-module.cc b/deps/v8/src/objects/source-text-module.cc index bffe6b60615eeb..9000aa1e61bcf8 100644 --- a/deps/v8/src/objects/source-text-module.cc +++ b/deps/v8/src/objects/source-text-module.cc @@ -397,6 +397,7 @@ bool SourceTextModule::MaybeTransitionComponent( DCHECK_LE(module->dfs_ancestor_index(), module->dfs_index()); if (module->dfs_ancestor_index() == module->dfs_index()) { // This is the root of its strongly connected component. + Handle<SourceTextModule> cycle_root = module; Handle<SourceTextModule> ancestor; do { ancestor = stack->front(); @@ -406,6 +407,9 @@ bool SourceTextModule::MaybeTransitionComponent( if (new_status == kInstantiated) { if (!SourceTextModule::RunInitializationCode(isolate, ancestor)) return false; + } else if (new_status == kEvaluated) { + DCHECK(ancestor->cycle_root().IsTheHole(isolate)); + ancestor->set_cycle_root(*cycle_root); } ancestor->SetStatus(new_status); } while (*ancestor != *module); @@ -619,9 +623,9 @@ MaybeHandle<Object> SourceTextModule::EvaluateMaybeAsync( CHECK(module->status() == kInstantiated || module->status() == kEvaluated); // 3. If module.[[Status]] is "evaluated", set module to - // GetAsyncCycleRoot(module). + // module.[[CycleRoot]]. if (module->status() == kEvaluated) { - module = GetAsyncCycleRoot(isolate, module); + module = module->GetCycleRoot(isolate); } // 4. If module.[[TopLevelCapability]] is not undefined, then @@ -736,37 +740,27 @@ void SourceTextModule::AsyncModuleExecutionFulfilled( for (int i = 0; i < module->AsyncParentModuleCount(); i++) { Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i); - // a. If module.[[DFSIndex]] is not equal to module.[[DFSAncestorIndex]], - // then - if (module->dfs_index() != module->dfs_ancestor_index()) { - // i. Assert: m.[[DFSAncestorIndex]] is equal to - // module.[[DFSAncestorIndex]]. - DCHECK_LE(m->dfs_ancestor_index(), module->dfs_ancestor_index()); - } - // b. Decrement m.[[PendingAsyncDependencies]] by 1. + // a. Decrement m.[[PendingAsyncDependencies]] by 1. m->DecrementPendingAsyncDependencies(); - // c. If m.[[PendingAsyncDependencies]] is 0 and m.[[EvaluationError]] is + // b. If m.[[PendingAsyncDependencies]] is 0 and m.[[EvaluationError]] is // undefined, then if (!m->HasPendingAsyncDependencies() && m->status() == kEvaluated) { // i. Assert: m.[[AsyncEvaluating]] is true. DCHECK(m->async_evaluating()); - // ii. Let cycleRoot be ! GetAsyncCycleRoot(m). - auto cycle_root = GetAsyncCycleRoot(isolate, m); - - // iii. If cycleRoot.[[EvaluationError]] is not undefined, + // ii. If m.[[CycleRoot]].[[EvaluationError]] is not undefined, // return undefined. - if (cycle_root->status() == kErrored) { + if (m->GetCycleRoot(isolate)->status() == kErrored) { return; } - // iv. If m.[[Async]] is true, then + // iii. If m.[[Async]] is true, then if (m->async()) { // 1. Perform ! ExecuteAsyncModule(m). ExecuteAsyncModule(isolate, m); } else { - // v. Otherwise, + // iv. Otherwise, // 1. Let result be m.ExecuteModule(). // 2. If result is a normal completion, Handle<Object> unused_result; @@ -1046,8 +1040,8 @@ MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation( required_module->dfs_ancestor_index())); } else { // iv. Otherwise, - // 1. Set requiredModule to GetAsyncCycleRoot(requiredModule). - required_module = GetAsyncCycleRoot(isolate, required_module); + // 1. Set requiredModule to requiredModule.[[CycleRoot]]. + required_module = required_module->GetCycleRoot(isolate); // 2. Assert: requiredModule.[[Status]] is "evaluated". CHECK_GE(required_module->status(), kEvaluated); @@ -1105,43 +1099,6 @@ MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation( return result; } -Handle<SourceTextModule> SourceTextModule::GetAsyncCycleRoot( - Isolate* isolate, Handle<SourceTextModule> module) { - // 1. Assert: module.[[Status]] is "evaluated". - CHECK_GE(module->status(), kEvaluated); - - // 2. If module.[[AsyncParentModules]] is an empty List, return module. - if (module->AsyncParentModuleCount() == 0) { - return module; - } - - // 3. Repeat, while module.[[DFSIndex]] is greater than - // module.[[DFSAncestorIndex]], - while (module->dfs_index() > module->dfs_ancestor_index()) { - // a. Assert: module.[[AsyncParentModules]] is a non-empty List. - DCHECK_GT(module->AsyncParentModuleCount(), 0); - - // b. Let nextCycleModule be the first element of - // module.[[AsyncParentModules]]. - Handle<SourceTextModule> next_cycle_module = - module->GetAsyncParentModule(isolate, 0); - - // c. Assert: nextCycleModule.[[DFSAncestorIndex]] is less than or equal - // to module.[[DFSAncestorIndex]]. - DCHECK_LE(next_cycle_module->dfs_ancestor_index(), - module->dfs_ancestor_index()); - - // d. Set module to nextCycleModule - module = next_cycle_module; - } - - // 4. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]]. - DCHECK_EQ(module->dfs_index(), module->dfs_ancestor_index()); - - // 5. Return module. - return module; -} - void SourceTextModule::Reset(Isolate* isolate, Handle<SourceTextModule> module) { Factory* factory = isolate->factory(); diff --git a/deps/v8/src/objects/source-text-module.h b/deps/v8/src/objects/source-text-module.h index f7ebf3f28f6bd5..f2960d80ea1904 100644 --- a/deps/v8/src/objects/source-text-module.h +++ b/deps/v8/src/objects/source-text-module.h @@ -79,6 +79,9 @@ class SourceTextModule Handle<SourceTextModule> module, Handle<SourceTextModule> parent); + // Get the non-hole cycle root. Only valid when status >= kEvaluated. + inline Handle<SourceTextModule> GetCycleRoot(Isolate* isolate) const; + // Returns a SourceTextModule, the // ith parent in depth first traversal order of a given async child. inline Handle<SourceTextModule> GetAsyncParentModule(Isolate* isolate, @@ -163,10 +166,6 @@ class SourceTextModule Isolate* isolate, Handle<SourceTextModule> module, ZoneForwardList<Handle<SourceTextModule>>* stack, Status new_status); - // Implementation of spec GetAsyncCycleRoot. - static V8_WARN_UNUSED_RESULT Handle<SourceTextModule> GetAsyncCycleRoot( - Isolate* isolate, Handle<SourceTextModule> module); - // Implementation of spec ExecuteModule is broken up into // InnerExecuteAsyncModule for asynchronous modules and ExecuteModule // for synchronous modules. diff --git a/deps/v8/src/objects/source-text-module.tq b/deps/v8/src/objects/source-text-module.tq index 185443414ddc08..a9e3ee3db9a793 100644 --- a/deps/v8/src/objects/source-text-module.tq +++ b/deps/v8/src/objects/source-text-module.tq @@ -34,6 +34,11 @@ extern class SourceTextModule extends Module { // a JSObject afterwards. import_meta: TheHole|JSObject; + // The first visited module of a cycle. For modules not in a cycle, this is + // the module itself. It's the hole before the module state transitions to + // kEvaluated. + cycle_root: SourceTextModule|TheHole; + async_parent_modules: ArrayList; top_level_capability: JSPromise|Undefined; diff --git a/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-cycle.mjs b/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-cycle.mjs new file mode 100644 index 00000000000000..e9aa18e577d4c9 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-cycle.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-cycle-start.mjs" + +assertEquals(globalThis.test_order, [ + '2', 'async before', 'async after', '1', + '3', 'start', +]); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-1.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-1.mjs new file mode 100644 index 00000000000000..b2f7ecac865917 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-1.mjs @@ -0,0 +1,14 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-cycle-2.mjs"; +import "modules-skip-async-cycle-leaf.mjs"; + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('1'); + diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-2.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-2.mjs new file mode 100644 index 00000000000000..391927ae128ebd --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-2.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-cycle-1.mjs"; + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('2'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-3.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-3.mjs new file mode 100644 index 00000000000000..f61dcdf55a2af4 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-3.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-cycle-2.mjs"; + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('3'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-leaf.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-leaf.mjs new file mode 100644 index 00000000000000..7f8e8589f36679 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-leaf.mjs @@ -0,0 +1,13 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} + +globalThis.test_order.push('async before'); +await 0; +globalThis.test_order.push('async after'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-start.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-start.mjs new file mode 100644 index 00000000000000..feeb6c6b2aebdf --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-cycle-start.mjs @@ -0,0 +1,13 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-cycle-1.mjs"; +import "modules-skip-async-cycle-3.mjs"; + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('start'); From f09c033fafe9835814387ed71999b97745388b59 Mon Sep 17 00:00:00 2001 From: Guy Bedford <guybedford@gmail.com> Date: Mon, 22 Mar 2021 18:36:48 +0200 Subject: [PATCH 84/85] deps: backport v8 f19142e6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [top-level-await] Implement the new post-order requirement for async subgraphs Refs: https://github.com/v8/v8/commit/f19142e6139979da3a177cb0b9f382e459f5ccec PR-URL: https://github.com/nodejs/node/pull/37864 Backport-PR-URL: https://github.com/nodejs/node/pull/37985 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> --- deps/v8/src/common/globals.h | 3 + deps/v8/src/diagnostics/objects-debug.cc | 3 +- deps/v8/src/diagnostics/objects-printer.cc | 1 + deps/v8/src/execution/isolate-inl.h | 31 +++ deps/v8/src/execution/isolate.cc | 3 + deps/v8/src/execution/isolate.h | 18 ++ deps/v8/src/heap/factory.cc | 2 +- deps/v8/src/objects/module-inl.h | 8 +- deps/v8/src/objects/source-text-module.cc | 243 ++++++++++++------ deps/v8/src/objects/source-text-module.h | 36 ++- deps/v8/src/objects/source-text-module.tq | 2 +- ...ules-import-rqstd-order-async-subgraph.mjs | 11 + .../harmony/modules-skip-async-subgraph-1.mjs | 12 + .../harmony/modules-skip-async-subgraph-2.mjs | 12 + .../modules-skip-async-subgraph-async.mjs | 12 + .../modules-skip-async-subgraph-start.mjs | 14 + .../harmony/modules-skip-async-subgraph-x.mjs | 12 + 17 files changed, 336 insertions(+), 87 deletions(-) create mode 100644 deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs create mode 100644 deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs diff --git a/deps/v8/src/common/globals.h b/deps/v8/src/common/globals.h index dbc6b9af9be02a..f75544b56c0e9e 100644 --- a/deps/v8/src/common/globals.h +++ b/deps/v8/src/common/globals.h @@ -349,6 +349,9 @@ constexpr int kUC16Size = sizeof(uc16); // NOLINT // 128 bit SIMD value size. constexpr int kSimd128Size = 16; +// Maximum ordinal used for tracking asynchronous module evaluation order. +constexpr unsigned kMaxModuleAsyncEvaluatingOrdinal = (1 << 30) - 1; + // FUNCTION_ADDR(f) gets the address of a C function f. #define FUNCTION_ADDR(f) (reinterpret_cast<v8::internal::Address>(f)) diff --git a/deps/v8/src/diagnostics/objects-debug.cc b/deps/v8/src/diagnostics/objects-debug.cc index 054b9dec175fb2..ee8827f8f639f3 100644 --- a/deps/v8/src/diagnostics/objects-debug.cc +++ b/deps/v8/src/diagnostics/objects-debug.cc @@ -1390,7 +1390,8 @@ void SourceTextModule::SourceTextModuleVerify(Isolate* isolate) { (status() == kPreInstantiating && code().IsSharedFunctionInfo()) || (status() == kUninstantiated && code().IsSharedFunctionInfo())); CHECK(top_level_capability().IsUndefined() && !AsyncParentModuleCount() && - !pending_async_dependencies() && !async_evaluating()); + !pending_async_dependencies()); + CHECK(!IsAsyncEvaluating()); } CHECK_EQ(requested_modules().length(), info().module_requests().length()); diff --git a/deps/v8/src/diagnostics/objects-printer.cc b/deps/v8/src/diagnostics/objects-printer.cc index 843d65cd1f7f61..9e24b5c41d1997 100644 --- a/deps/v8/src/diagnostics/objects-printer.cc +++ b/deps/v8/src/diagnostics/objects-printer.cc @@ -1602,6 +1602,7 @@ void SourceTextModule::SourceTextModulePrint(std::ostream& os) { // NOLINT os << "\n - script: " << Brief(script()); os << "\n - import_meta: " << Brief(import_meta()); os << "\n - cycle_root: " << Brief(cycle_root()); + os << "\n - async_evaluating_ordinal: " << async_evaluating_ordinal(); os << "\n"; } diff --git a/deps/v8/src/execution/isolate-inl.h b/deps/v8/src/execution/isolate-inl.h index b3a84d01bec1e6..e39b75f02508dc 100644 --- a/deps/v8/src/execution/isolate-inl.h +++ b/deps/v8/src/execution/isolate-inl.h @@ -13,6 +13,7 @@ #include "src/objects/property-cell.h" #include "src/objects/regexp-match-info.h" #include "src/objects/shared-function-info.h" +#include "src/objects/source-text-module.h" namespace v8 { namespace internal { @@ -118,6 +119,36 @@ Isolate::ExceptionScope::~ExceptionScope() { isolate_->set_pending_exception(*pending_exception_); } +void Isolate::DidFinishModuleAsyncEvaluation(unsigned ordinal) { + // To address overflow, the ordinal is reset when the async module with the + // largest vended ordinal finishes evaluating. Modules are evaluated in + // ascending order of their async_evaluating_ordinal. + // + // While the specification imposes a global total ordering, the intention is + // that for each async module, all its parents are totally ordered by when + // they first had their [[AsyncEvaluating]] bit set. + // + // The module with largest vended ordinal finishes evaluating implies that the + // async dependency as well as all other modules in that module's graph + // depending on async dependencies are finished evaluating. + // + // If the async dependency participates in other module graphs (e.g. via + // dynamic import, or other <script type=module> tags), those module graphs + // must have been evaluated either before or after the async dependency is + // settled, as the concrete Evaluate() method on cyclic module records is + // neither reentrant nor performs microtask checkpoints during its + // evaluation. If before, then all modules that depend on the async + // dependencies were given an ordinal that ensure they are relatively ordered, + // before the global ordinal was reset. If after, then the async evaluating + // ordering does not apply, as the dependency is no longer asynchronous. + // + // https://tc39.es/ecma262/#sec-moduleevaluation + if (ordinal + 1 == next_module_async_evaluating_ordinal_) { + next_module_async_evaluating_ordinal_ = + SourceTextModule::kFirstAsyncEvaluatingOrdinal; + } +} + #define NATIVE_CONTEXT_FIELD_ACCESSOR(index, type, name) \ Handle<type> Isolate::name() { \ return Handle<type>(raw_native_context().name(), this); \ diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc index bd357d4bfc9991..6174c61cb04554 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -69,6 +69,7 @@ #include "src/objects/prototype.h" #include "src/objects/slots.h" #include "src/objects/smi.h" +#include "src/objects/source-text-module.h" #include "src/objects/stack-frame-info-inl.h" #include "src/objects/visitors.h" #include "src/profiler/heap-profiler.h" @@ -2929,6 +2930,8 @@ Isolate::Isolate(std::unique_ptr<i::IsolateAllocator> isolate_allocator) #if V8_SFI_HAS_UNIQUE_ID next_unique_sfi_id_(0), #endif + next_module_async_evaluating_ordinal_( + SourceTextModule::kFirstAsyncEvaluatingOrdinal), cancelable_task_manager_(new CancelableTaskManager()) { TRACE_ISOLATE(constructor); CheckIsolateLayout(); diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h index 8c538ab27de3da..a35ef10cf2eefb 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -1284,6 +1284,22 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { return id; } + // https://github.com/tc39/proposal-top-level-await/pull/159 + // TODO(syg): Update to actual spec link once merged. + // + // According to the spec, modules that depend on async modules (i.e. modules + // with top-level await) must be evaluated in order in which their + // [[AsyncEvaluating]] flags were set to true. V8 tracks this global total + // order with next_module_async_evaluating_ordinal_. Each module that sets its + // [[AsyncEvaluating]] to true grabs the next ordinal. + unsigned NextModuleAsyncEvaluatingOrdinal() { + unsigned ordinal = next_module_async_evaluating_ordinal_++; + CHECK_LT(ordinal, kMaxModuleAsyncEvaluatingOrdinal); + return ordinal; + } + + inline void DidFinishModuleAsyncEvaluation(unsigned ordinal); + void AddNearHeapLimitCallback(v8::NearHeapLimitCallback, void* data); void RemoveNearHeapLimitCallback(v8::NearHeapLimitCallback callback, size_t heap_limit); @@ -1815,6 +1831,8 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { std::atomic<int> next_unique_sfi_id_; #endif + unsigned next_module_async_evaluating_ordinal_; + // Vector of callbacks before a Call starts execution. std::vector<BeforeCallEnteredCallback> before_call_entered_callbacks_; diff --git a/deps/v8/src/heap/factory.cc b/deps/v8/src/heap/factory.cc index 22dcb1838a5b0f..de221e22f239b7 100644 --- a/deps/v8/src/heap/factory.cc +++ b/deps/v8/src/heap/factory.cc @@ -2504,7 +2504,7 @@ Handle<SourceTextModule> Factory::NewSourceTextModule( module->set_top_level_capability(roots.undefined_value()); module->set_flags(0); module->set_async(IsAsyncModule(code->kind())); - module->set_async_evaluating(false); + module->set_async_evaluating_ordinal(SourceTextModule::kNotAsyncEvaluated); module->set_cycle_root(roots.the_hole_value()); module->set_async_parent_modules(*async_parent_modules); module->set_pending_async_dependencies(0); diff --git a/deps/v8/src/objects/module-inl.h b/deps/v8/src/objects/module-inl.h index d270a9cdfc3671..2ce37f231f8473 100644 --- a/deps/v8/src/objects/module-inl.h +++ b/deps/v8/src/objects/module-inl.h @@ -37,8 +37,8 @@ SMI_ACCESSORS(Module, status, kStatusOffset) SMI_ACCESSORS(Module, hash, kHashOffset) BOOL_ACCESSORS(SourceTextModule, flags, async, AsyncBit::kShift) -BOOL_ACCESSORS(SourceTextModule, flags, async_evaluating, - AsyncEvaluatingBit::kShift) +BIT_FIELD_ACCESSORS(SourceTextModule, flags, async_evaluating_ordinal, + SourceTextModule::AsyncEvaluatingOrdinalBits) ACCESSORS(SourceTextModule, async_parent_modules, ArrayList, kAsyncParentModulesOffset) ACCESSORS(SourceTextModule, top_level_capability, HeapObject, @@ -141,6 +141,10 @@ int SourceTextModule::AsyncParentModuleCount() { return async_parent_modules().Length(); } +bool SourceTextModule::IsAsyncEvaluating() const { + return async_evaluating_ordinal() >= kFirstAsyncEvaluatingOrdinal; +} + bool SourceTextModule::HasPendingAsyncDependencies() { DCHECK_GE(pending_async_dependencies(), 0); return pending_async_dependencies() > 0; diff --git a/deps/v8/src/objects/source-text-module.cc b/deps/v8/src/objects/source-text-module.cc index 9000aa1e61bcf8..225636a535d794 100644 --- a/deps/v8/src/objects/source-text-module.cc +++ b/deps/v8/src/objects/source-text-module.cc @@ -76,6 +76,15 @@ class Module::ResolveSet Zone* zone_; }; +struct SourceTextModule::AsyncEvaluatingOrdinalCompare { + bool operator()(Handle<SourceTextModule> lhs, + Handle<SourceTextModule> rhs) const { + DCHECK(lhs->IsAsyncEvaluating()); + DCHECK(rhs->IsAsyncEvaluating()); + return lhs->async_evaluating_ordinal() < rhs->async_evaluating_ordinal(); + } +}; + SharedFunctionInfo SourceTextModule::GetSharedFunctionInfo() const { DisallowHeapAllocation no_alloc; switch (status()) { @@ -579,6 +588,58 @@ void SourceTextModule::FetchStarExports(Isolate* isolate, module->set_exports(*exports); } +void SourceTextModule::GatherAsyncParentCompletions( + Isolate* isolate, Zone* zone, Handle<SourceTextModule> start, + AsyncParentCompletionSet* exec_list) { + // The spec algorithm is recursive. It is transformed to an equivalent + // iterative one here. + ZoneStack<Handle<SourceTextModule>> worklist(zone); + worklist.push(start); + + while (!worklist.empty()) { + Handle<SourceTextModule> module = worklist.top(); + worklist.pop(); + + // 1. Assert: module.[[Status]] is evaluated. + DCHECK_EQ(module->status(), kEvaluated); + + // 2. For each Module m of module.[[AsyncParentModules]], do + for (int i = module->AsyncParentModuleCount(); i-- > 0;) { + Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i); + + // a. If execList does not contain m and + // m.[[CycleRoot]].[[EvaluationError]] is empty, then + if (exec_list->find(m) == exec_list->end() && + m->GetCycleRoot(isolate)->status() != kErrored) { + // i. Assert: m.[[EvaluationError]] is empty. + DCHECK_NE(m->status(), kErrored); + + // ii. Assert: m.[[AsyncEvaluating]] is true. + DCHECK(m->IsAsyncEvaluating()); + + // iii. Assert: m.[[PendingAsyncDependencies]] > 0. + DCHECK(m->HasPendingAsyncDependencies()); + + // iv. Set m.[[PendingAsyncDependencies]] to + // m.[[PendingAsyncDependencies]] - 1. + m->DecrementPendingAsyncDependencies(); + + // v. If m.[[PendingAsyncDependencies]] is equal to 0, then + if (!m->HasPendingAsyncDependencies()) { + // 1. Append m to execList. + exec_list->insert(m); + + // 2. If m.[[Async]] is false, + // perform ! GatherAsyncParentCompletions(m, execList). + if (!m->async()) worklist.push(m); + } + } + } + } + + // 3. Return undefined. +} + Handle<JSModuleNamespace> SourceTextModule::GetModuleNamespace( Isolate* isolate, Handle<SourceTextModule> module, int module_request) { Handle<Module> requested_module( @@ -665,7 +726,7 @@ MaybeHandle<Object> SourceTextModule::EvaluateMaybeAsync( CHECK_EQ(module->status(), kEvaluated); // b. If module.[[AsyncEvaluating]] is false, then - if (!module->async_evaluating()) { + if (!module->IsAsyncEvaluating()) { // i. Perform ! Call(capability.[[Resolve]], undefined, // «undefined»). JSPromise::Resolve(capability, isolate->factory()->undefined_value()) @@ -718,81 +779,97 @@ MaybeHandle<Object> SourceTextModule::Evaluate( void SourceTextModule::AsyncModuleExecutionFulfilled( Isolate* isolate, Handle<SourceTextModule> module) { - // 1. Assert: module.[[Status]] is "evaluated". - CHECK(module->status() == kEvaluated || module->status() == kErrored); + // 1. Assert: module.[[AsyncEvaluating]] is true. + DCHECK(module->IsAsyncEvaluating()); - // 2. If module.[[AsyncEvaluating]] is false, - if (!module->async_evaluating()) { - // a. Assert: module.[[EvaluationError]] is not undefined. - CHECK_EQ(module->status(), kErrored); - - // b. Return undefined. - return; - } - - // 3. Assert: module.[[EvaluationError]] is undefined. + // 2. Assert: module.[[EvaluationError]] is undefined. CHECK_EQ(module->status(), kEvaluated); - // 4. Set module.[[AsyncEvaluating]] to false. - module->set_async_evaluating(false); + // 3. Set module.[[AsyncEvaluating]] to false. + isolate->DidFinishModuleAsyncEvaluation(module->async_evaluating_ordinal()); + module->set_async_evaluating_ordinal(kAsyncEvaluateDidFinish); - // 5. For each Module m of module.[[AsyncParentModules]], do - for (int i = 0; i < module->AsyncParentModuleCount(); i++) { - Handle<SourceTextModule> m = module->GetAsyncParentModule(isolate, i); + // 4. If module.[[TopLevelCapability]] is not empty, then + if (!module->top_level_capability().IsUndefined(isolate)) { + // a. Assert: module.[[CycleRoot]] is equal to module. + DCHECK_EQ(*module->GetCycleRoot(isolate), *module); - // a. Decrement m.[[PendingAsyncDependencies]] by 1. - m->DecrementPendingAsyncDependencies(); + // i. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], undefined, + // «undefined»). + Handle<JSPromise> capability( + JSPromise::cast(module->top_level_capability()), isolate); + JSPromise::Resolve(capability, isolate->factory()->undefined_value()) + .ToHandleChecked(); + } - // b. If m.[[PendingAsyncDependencies]] is 0 and m.[[EvaluationError]] is - // undefined, then - if (!m->HasPendingAsyncDependencies() && m->status() == kEvaluated) { - // i. Assert: m.[[AsyncEvaluating]] is true. - DCHECK(m->async_evaluating()); + // 5. Let execList be a new empty List. + Zone zone(isolate->allocator(), ZONE_NAME); + AsyncParentCompletionSet exec_list(&zone); - // ii. If m.[[CycleRoot]].[[EvaluationError]] is not undefined, - // return undefined. - if (m->GetCycleRoot(isolate)->status() == kErrored) { - return; - } + // 6. Perform ! GatherAsyncParentCompletions(module, execList). + GatherAsyncParentCompletions(isolate, &zone, module, &exec_list); + + // 7. Let sortedExecList be a List of elements that are the elements of + // execList, in the order in which they had their [[AsyncEvaluating]] + // fields set to true in InnerModuleEvaluation. + // + // This step is implemented by AsyncParentCompletionSet, which is a set + // ordered on async_evaluating_ordinal. - // iii. If m.[[Async]] is true, then - if (m->async()) { - // 1. Perform ! ExecuteAsyncModule(m). - ExecuteAsyncModule(isolate, m); + // 8. Assert: All elements of sortedExecList have their [[AsyncEvaluating]] + // field set to true, [[PendingAsyncDependencies]] field set to 0 and + // [[EvaluationError]] field set to undefined. +#ifdef DEBUG + for (Handle<SourceTextModule> m : exec_list) { + DCHECK(m->IsAsyncEvaluating()); + DCHECK(!m->HasPendingAsyncDependencies()); + DCHECK_NE(m->status(), kErrored); + } +#endif + + // 9. For each Module m of sortedExecList, do + for (Handle<SourceTextModule> m : exec_list) { + // i. If m.[[AsyncEvaluating]] is false, then + if (!m->IsAsyncEvaluating()) { + // a. Assert: m.[[EvaluatingError]] is not empty. + DCHECK_EQ(m->status(), kErrored); + } else if (m->async()) { + // ii. Otherwise, if m.[[Async]] is *true*, then + // a. Perform ! ExecuteAsyncModule(m). + ExecuteAsyncModule(isolate, m); + } else { + // iii. Otherwise, + // a. Let _result_ be m.ExecuteModule(). + Handle<Object> unused_result; + // b. If _result_ is an abrupt completion, + if (!ExecuteModule(isolate, m).ToHandle(&unused_result)) { + // 1. Perform ! AsyncModuleExecutionRejected(m, result.[[Value]]). + Handle<Object> exception(isolate->pending_exception(), isolate); + isolate->clear_pending_exception(); + AsyncModuleExecutionRejected(isolate, m, exception); } else { - // iv. Otherwise, - // 1. Let result be m.ExecuteModule(). - // 2. If result is a normal completion, - Handle<Object> unused_result; - if (ExecuteModule(isolate, m).ToHandle(&unused_result)) { - // a. Perform ! AsyncModuleExecutionFulfilled(m). - AsyncModuleExecutionFulfilled(isolate, m); - } else { - // 3. Otherwise, - // a. Perform ! AsyncModuleExecutionRejected(m, - // result.[[Value]]). - Handle<Object> exception(isolate->pending_exception(), isolate); - isolate->clear_pending_exception(); - AsyncModuleExecutionRejected(isolate, m, exception); + // c. Otherwise, + // 1. Set m.[[AsyncEvaluating]] to false. + isolate->DidFinishModuleAsyncEvaluation(m->async_evaluating_ordinal()); + m->set_async_evaluating_ordinal(kAsyncEvaluateDidFinish); + + // 2. If m.[[TopLevelCapability]] is not empty, then + if (!m->top_level_capability().IsUndefined(isolate)) { + // i. Assert: m.[[CycleRoot]] is equal to m. + DCHECK_EQ(*m->GetCycleRoot(isolate), *m); + + // ii. Perform ! Call(m.[[TopLevelCapability]].[[Resolve]], + // undefined, «undefined»). + Handle<JSPromise> capability( + JSPromise::cast(m->top_level_capability()), isolate); + JSPromise::Resolve(capability, isolate->factory()->undefined_value()) + .ToHandleChecked(); } } } } - // 6. If module.[[TopLevelCapability]] is not undefined, then - if (!module->top_level_capability().IsUndefined(isolate)) { - // a. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]]. - DCHECK_EQ(module->dfs_index(), module->dfs_ancestor_index()); - - // b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], - // undefined, «undefined»). - Handle<JSPromise> capability( - JSPromise::cast(module->top_level_capability()), isolate); - JSPromise::Resolve(capability, isolate->factory()->undefined_value()) - .ToHandleChecked(); - } - - // 7. Return undefined. + // 10. Return undefined. } void SourceTextModule::AsyncModuleExecutionRejected( @@ -804,7 +881,7 @@ void SourceTextModule::AsyncModuleExecutionRejected( CHECK(module->status() == kEvaluated || module->status() == kErrored); // 2. If module.[[AsyncEvaluating]] is false, - if (!module->async_evaluating()) { + if (!module->IsAsyncEvaluating()) { // a. Assert: module.[[EvaluationError]] is not undefined. CHECK_EQ(module->status(), kErrored); @@ -816,7 +893,8 @@ void SourceTextModule::AsyncModuleExecutionRejected( module->RecordError(isolate, exception); // 5. Set module.[[AsyncEvaluating]] to false. - module->set_async_evaluating(false); + isolate->DidFinishModuleAsyncEvaluation(module->async_evaluating_ordinal()); + module->set_async_evaluating_ordinal(kAsyncEvaluateDidFinish); // 6. For each Module m of module.[[AsyncParentModules]], do for (int i = 0; i < module->AsyncParentModuleCount(); i++) { @@ -835,8 +913,8 @@ void SourceTextModule::AsyncModuleExecutionRejected( // 7. If module.[[TopLevelCapability]] is not undefined, then if (!module->top_level_capability().IsUndefined(isolate)) { - // a. Assert: module.[[DFSIndex]] is equal to module.[[DFSAncestorIndex]]. - DCHECK(module->dfs_index() == module->dfs_ancestor_index()); + // a. Assert: module.[[CycleRoot]] is equal to module. + DCHECK_EQ(*module->GetCycleRoot(isolate), *module); // b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], // undefined, «error»). @@ -857,7 +935,8 @@ void SourceTextModule::ExecuteAsyncModule(Isolate* isolate, DCHECK(module->async()); // 3. Set module.[[AsyncEvaluating]] to true. - module->set_async_evaluating(true); + module->set_async_evaluating_ordinal( + isolate->NextModuleAsyncEvaluatingOrdinal()); // 4. Let capability be ! NewPromiseCapability(%Promise%). Handle<JSPromise> capability = isolate->factory()->NewJSPromise(); @@ -1059,7 +1138,7 @@ MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation( } } // v. If requiredModule.[[AsyncEvaluating]] is true, then - if (required_module->async_evaluating()) { + if (required_module->IsAsyncEvaluating()) { // 1. Set module.[[PendingAsyncDependencies]] to // module.[[PendingAsyncDependencies]] + 1. module->IncrementPendingAsyncDependencies(); @@ -1081,16 +1160,26 @@ MaybeHandle<Object> SourceTextModule::InnerModuleEvaluation( // synchronous modules, but return undefined for AsyncModules. Handle<Object> result = isolate->factory()->undefined_value(); - // 14. If module.[[PendingAsyncDependencies]] is > 0, set - // module.[[AsyncEvaluating]] to true. - if (module->HasPendingAsyncDependencies()) { - module->set_async_evaluating(true); - } else if (module->async()) { - // 15. Otherwise, if module.[[Async]] is true, - // perform ! ExecuteAsyncModule(module). - SourceTextModule::ExecuteAsyncModule(isolate, module); + // 14. If module.[[PendingAsyncDependencies]] > 0 or module.[[Async]] is + // true, then + if (module->HasPendingAsyncDependencies() || module->async()) { + // a. Assert: module.[[AsyncEvaluating]] is false and was never previously + // set to true. + DCHECK_EQ(module->async_evaluating_ordinal(), kNotAsyncEvaluated); + + // b. Set module.[[AsyncEvaluating]] to true. + // NOTE: The order in which modules transition to async evaluating is + // significant. + module->set_async_evaluating_ordinal( + isolate->NextModuleAsyncEvaluatingOrdinal()); + + // c. If module.[[PendingAsyncDependencies]] is 0, + // perform ! ExecuteAsyncModule(_module_). + if (!module->HasPendingAsyncDependencies()) { + SourceTextModule::ExecuteAsyncModule(isolate, module); + } } else { - // 16. Otherwise, perform ? module.ExecuteModule(). + // 15. Otherwise, perform ? module.ExecuteModule(). ASSIGN_RETURN_ON_EXCEPTION(isolate, result, ExecuteModule(isolate, module), Object); } diff --git a/deps/v8/src/objects/source-text-module.h b/deps/v8/src/objects/source-text-module.h index f2960d80ea1904..b91d1e5080e776 100644 --- a/deps/v8/src/objects/source-text-module.h +++ b/deps/v8/src/objects/source-text-module.h @@ -7,6 +7,7 @@ #include "src/objects/module.h" #include "src/objects/promise.h" +#include "src/zone/zone-containers.h" #include "torque-generated/bit-fields-tq.h" // Has to be the last include (doesn't have include guards): @@ -69,10 +70,16 @@ class SourceTextModule SubclassBodyDescriptor<Module::BodyDescriptor, FixedBodyDescriptor<kCodeOffset, kSize, kSize>>; + static constexpr unsigned kFirstAsyncEvaluatingOrdinal = 2; + private: friend class Factory; friend class Module; + struct AsyncEvaluatingOrdinalCompare; + using AsyncParentCompletionSet = + ZoneSet<Handle<SourceTextModule>, AsyncEvaluatingOrdinalCompare>; + // Appends a tuple of module and generator to the async parent modules // ArrayList. inline static void AddAsyncParentModule(Isolate* isolate, @@ -90,6 +97,8 @@ class SourceTextModule // Returns the number of async parent modules for a given async child. inline int AsyncParentModuleCount(); + inline bool IsAsyncEvaluating() const; + inline bool HasPendingAsyncDependencies(); inline void IncrementPendingAsyncDependencies(); inline void DecrementPendingAsyncDependencies(); @@ -97,13 +106,26 @@ class SourceTextModule // Bits for flags. DEFINE_TORQUE_GENERATED_SOURCE_TEXT_MODULE_FLAGS() - // async_evaluating, top_level_capability, pending_async_dependencies, and - // async_parent_modules are used exclusively during evaluation of async + // async_evaluating_ordinal, top_level_capability, pending_async_dependencies, + // and async_parent_modules are used exclusively during evaluation of async // modules and the modules which depend on them. // - // Whether or not this module is async and evaluating or currently evaluating - // an async child. - DECL_BOOLEAN_ACCESSORS(async_evaluating) + // If >1, this module is async and evaluating or currently evaluating an async + // child. The integer is an ordinal for when this module first started async + // evaluation and is used for sorting async parent modules when determining + // which parent module can start executing after an async evaluation + // completes. + // + // If 1, this module has finished async evaluating. + // + // If 0, this module is not async or has not been async evaluated. + static constexpr unsigned kNotAsyncEvaluated = 0; + static constexpr unsigned kAsyncEvaluateDidFinish = 1; + STATIC_ASSERT(kNotAsyncEvaluated < kAsyncEvaluateDidFinish); + STATIC_ASSERT(kAsyncEvaluateDidFinish < kFirstAsyncEvaluatingOrdinal); + STATIC_ASSERT(kMaxModuleAsyncEvaluatingOrdinal == + AsyncEvaluatingOrdinalBits::kMax); + DECL_PRIMITIVE_ACCESSORS(async_evaluating_ordinal, unsigned) // The top level promise capability of this module. Will only be defined // for cycle roots. @@ -149,6 +171,10 @@ class SourceTextModule Handle<SourceTextModule> module, Zone* zone, UnorderedModuleSet* visited); + static void GatherAsyncParentCompletions(Isolate* isolate, Zone* zone, + Handle<SourceTextModule> start, + AsyncParentCompletionSet* exec_list); + // Implementation of spec concrete method Evaluate. static V8_WARN_UNUSED_RESULT MaybeHandle<Object> EvaluateMaybeAsync( Isolate* isolate, Handle<SourceTextModule> module); diff --git a/deps/v8/src/objects/source-text-module.tq b/deps/v8/src/objects/source-text-module.tq index a9e3ee3db9a793..f2eb8e2ce6fe3d 100644 --- a/deps/v8/src/objects/source-text-module.tq +++ b/deps/v8/src/objects/source-text-module.tq @@ -6,7 +6,7 @@ type SourceTextModuleInfo extends FixedArray; bitfield struct SourceTextModuleFlags extends uint31 { async: bool: 1 bit; - async_evaluating: bool: 1 bit; + async_evaluating_ordinal: uint32: 30 bit; } @generateCppClass diff --git a/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs b/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs new file mode 100644 index 00000000000000..317349b06300db --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-import-rqstd-order-async-subgraph.mjs @@ -0,0 +1,11 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-start.mjs" + +assertEquals(globalThis.test_order, [ + 'async before', 'async after', '1', '2', 'x', 'start' +]); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs new file mode 100644 index 00000000000000..d4bc01d8419489 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-1.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-async.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('1'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs new file mode 100644 index 00000000000000..ee37caffd4a99f --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-2.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-async.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('2'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs new file mode 100644 index 00000000000000..447f801a8ad8de --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-async.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('async before'); +await 0; +globalThis.test_order.push('async after'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs new file mode 100644 index 00000000000000..861907a9dd7ff4 --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-start.mjs @@ -0,0 +1,14 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-1.mjs" +import "modules-skip-async-subgraph-2.mjs" +import "modules-skip-async-subgraph-x.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('start'); diff --git a/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs new file mode 100644 index 00000000000000..813a9bb870e78b --- /dev/null +++ b/deps/v8/test/mjsunit/harmony/modules-skip-async-subgraph-x.mjs @@ -0,0 +1,12 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-top-level-await + +import "modules-skip-async-subgraph-1.mjs" + +if (globalThis.test_order === undefined) { + globalThis.test_order = []; +} +globalThis.test_order.push('x'); From 71a77222434b6fd961303f1e1150c2b9f309dea1 Mon Sep 17 00:00:00 2001 From: Ruy Adorno <ruyadorno@hotmail.com> Date: Mon, 29 Mar 2021 22:56:12 -0400 Subject: [PATCH 85/85] 2021-03-31, Version 15.13.0 (Current) PR-URL: https://github.com/nodejs/node/pull/37977 Notable changes: * buffer: * implement btoa and atob (James M Snell) https://github.com/nodejs/node/pull/37529 * deps: * upgrade npm to 7.7.6 (Ruy Adorno) https://github.com/nodejs/node/pull/37968 * doc: * add legacy status to stability index (James M Snell) https://github.com/nodejs/node/pull/37784 * add @linkgoron to collaborators (Nitzan Uziely) https://github.com/nodejs/node/pull/37817 * http: * add http.ClientRequest.getRawHeaderNames() (simov) https://github.com/nodejs/node/pull/37660 --- CHANGELOG.md | 3 +- doc/api/buffer.md | 4 +- doc/api/child_process.md | 4 +- doc/api/deprecations.md | 2 +- doc/api/http.md | 2 +- doc/api/url.md | 10 +-- doc/changelogs/CHANGELOG_V15.md | 104 ++++++++++++++++++++++++++++++++ src/node_version.h | 6 +- 8 files changed, 120 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb8cc9457dbbff..6b038b978dd01f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,7 +32,8 @@ release. </tr> <tr> <td valign="top"> -<b><a href="doc/changelogs/CHANGELOG_V15.md#15.12.0">15.12.0</a></b><br/> +<b><a href="doc/changelogs/CHANGELOG_V15.md#15.13.0">15.13.0</a></b><br/> +<a href="doc/changelogs/CHANGELOG_V15.md#15.12.0">15.12.0</a><br/> <a href="doc/changelogs/CHANGELOG_V15.md#15.11.0">15.11.0</a><br/> <a href="doc/changelogs/CHANGELOG_V15.md#15.10.0">15.10.0</a><br/> <a href="doc/changelogs/CHANGELOG_V15.md#15.9.0">15.9.0</a><br/> diff --git a/doc/api/buffer.md b/doc/api/buffer.md index f31b8b298b02eb..3334c260125338 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -3279,7 +3279,7 @@ accessed using `require('buffer')`. ### `buffer.atob(data)` <!-- YAML -added: REPLACEME +added: v15.13.0 --> * `data` {any} The Base64-encoded input string. @@ -3298,7 +3298,7 @@ and binary data should be performed using `Buffer.from(str, 'base64')` and ### `buffer.btoa(data)` <!-- YAML -added: REPLACEME +added: v15.13.0 --> * `data` {any} An ASCII (Latin1) string. diff --git a/doc/api/child_process.md b/doc/api/child_process.md index aace7566e49764..5284028a813458 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -374,7 +374,7 @@ controller.abort(); <!-- YAML added: v0.5.0 changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37256 description: timeout was added. - version: v15.11.0 @@ -483,7 +483,7 @@ if (process.argv[2] === 'child') { <!-- YAML added: v0.1.90 changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37256 description: timeout was added. - version: v15.11.0 diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 0a539db586bb85..338ce4f31bfce1 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -2170,7 +2170,7 @@ future release. ### DEP0116: Legacy URL API <!-- YAML changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37784 description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 diff --git a/doc/api/http.md b/doc/api/http.md index d3215a5f4b8f16..93e754b82f484e 100644 --- a/doc/api/http.md +++ b/doc/api/http.md @@ -765,7 +765,7 @@ const cookie = request.getHeader('Cookie'); ### `request.getRawHeaderNames()` <!-- YAML -added: REPLACEME +added: v15.13.0 --> * Returns: {string[]} diff --git a/doc/api/url.md b/doc/api/url.md index 8ad7c5eed5019a..ce528f5f6c8007 100644 --- a/doc/api/url.md +++ b/doc/api/url.md @@ -1101,7 +1101,7 @@ console.log(urlToHttpOptions(myUrl)); ## Legacy URL API <!-- YAML changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37784 description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 @@ -1114,7 +1114,7 @@ changes: ### Legacy `urlObject` <!-- YAML changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37784 description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 @@ -1228,7 +1228,7 @@ forward-slash characters (`/`) are required following the colon in the <!-- YAML added: v0.1.25 changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37784 description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 @@ -1324,7 +1324,7 @@ The formatting process operates as follows: <!-- YAML added: v0.1.25 changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37784 description: Deprecation revoked. Status changed to "Legacy". - version: v11.14.0 @@ -1371,7 +1371,7 @@ incorrect handling of usernames and passwords have been identified. <!-- YAML added: v0.1.25 changes: - - version: REPLACEME + - version: v15.13.0 pr-url: https://github.com/nodejs/node/pull/37784 description: Deprecation revoked. Status changed to "Legacy". - version: v11.0.0 diff --git a/doc/changelogs/CHANGELOG_V15.md b/doc/changelogs/CHANGELOG_V15.md index 12577fc386b39c..78a4b24ae6ff9e 100644 --- a/doc/changelogs/CHANGELOG_V15.md +++ b/doc/changelogs/CHANGELOG_V15.md @@ -10,6 +10,7 @@ </tr> <tr> <td> +<a href="#15.13.0">15.13.0</a><br/> <a href="#15.12.0">15.12.0</a><br/> <a href="#15.11.0">15.11.0</a><br/> <a href="#15.10.0">15.10.0</a><br/> @@ -47,6 +48,109 @@ * [io.js](CHANGELOG_IOJS.md) * [Archive](CHANGELOG_ARCHIVE.md) +<a id="15.13.0"></a> +## 2021-03-31, Version 15.13.0 (Current), @ruyadorno + +### Notable Changes + +* **buffer**: + * implement btoa and atob (James M Snell) [#37529](https://github.com/nodejs/node/pull/37529) +* **deps**: + * upgrade npm to 7.7.6 (Ruy Adorno) [#37968](https://github.com/nodejs/node/pull/37968) + * This update adds workspaces support to [`npm run`](https://github.com/npm/cli/pull/2864) and [`npm exec`](https://github.com/npm/cli/pull/2886) +* **doc**: + * add legacy status to stability index (James M Snell) [#37784](https://github.com/nodejs/node/pull/37784) + * add @linkgoron to collaborators (Nitzan Uziely) [#37817](https://github.com/nodejs/node/pull/37817) +* **http**: + * add http.ClientRequest.getRawHeaderNames() (simov) [#37660](https://github.com/nodejs/node/pull/37660) + +### Commits + +* [[`dc9cd43d8f`](https://github.com/nodejs/node/commit/dc9cd43d8f)] - **(SEMVER-MINOR)** **buffer**: implement btoa and atob (James M Snell) [#37529](https://github.com/nodejs/node/pull/37529) +* [[`377830fd28`](https://github.com/nodejs/node/commit/377830fd28)] - **child_process**: remove unused argument (Rich Trott) [#37923](https://github.com/nodejs/node/pull/37923) +* [[`cdfc1c8692`](https://github.com/nodejs/node/commit/cdfc1c8692)] - **child_process**: cleanup AbortSignal duplication (Nitzan Uziely) [#37823](https://github.com/nodejs/node/pull/37823) +* [[`95aa032413`](https://github.com/nodejs/node/commit/95aa032413)] - **(SEMVER-MINOR)** **child_process**: add timeout to spawn and fork (Nitzan Uziely) [#37256](https://github.com/nodejs/node/pull/37256) +* [[`50fc6b9df0`](https://github.com/nodejs/node/commit/50fc6b9df0)] - **crypto**: clear errors in SignTraits::DeriveBits (Filip Skokan) [#37820](https://github.com/nodejs/node/pull/37820) +* [[`79259389a1`](https://github.com/nodejs/node/commit/79259389a1)] - **crypto**: fix DiffieHellman argument validation (Antoine du Hamel) [#37810](https://github.com/nodejs/node/pull/37810) +* [[`11d45855cd`](https://github.com/nodejs/node/commit/11d45855cd)] - **crypto**: fix header name (Jiawen Geng) [#37792](https://github.com/nodejs/node/pull/37792) +* [[`c37806d0ba`](https://github.com/nodejs/node/commit/c37806d0ba)] - **crypto**: use macro map for NodeCryptoError (Darshan Sen) [#37758](https://github.com/nodejs/node/pull/37758) +* [[`bfe3f21ee0`](https://github.com/nodejs/node/commit/bfe3f21ee0)] - **crypto**: fix crypto.verify callback invocation with a private keyobject (Filip Skokan) [#37795](https://github.com/nodejs/node/pull/37795) +* [[`f09c033faf`](https://github.com/nodejs/node/commit/f09c033faf)] - **deps**: backport v8 f19142e6 (Guy Bedford) [#37864](https://github.com/nodejs/node/pull/37864) +* [[`2fd97ce687`](https://github.com/nodejs/node/commit/2fd97ce687)] - **deps**: v8 backport 9689b17687b (Guy Bedford) [#37865](https://github.com/nodejs/node/pull/37865) +* [[`f2cef54b6f`](https://github.com/nodejs/node/commit/f2cef54b6f)] - **deps**: upgrade npm to 7.7.6 (Ruy Adorno) [#37968](https://github.com/nodejs/node/pull/37968) +* [[`ec82feb728`](https://github.com/nodejs/node/commit/ec82feb728)] - **deps**: upgrade npm to 7.7.5 (Ruy Adorno) [#37919](https://github.com/nodejs/node/pull/37919) +* [[`649e04c4a5`](https://github.com/nodejs/node/commit/649e04c4a5)] - **deps**: upgrade npm to 7.7.4 (Ruy Adorno) [#37897](https://github.com/nodejs/node/pull/37897) +* [[`d5b472b70d`](https://github.com/nodejs/node/commit/d5b472b70d)] - **deps**: upgrade npm to 7.7.0 (Ruy Adorno) [#37879](https://github.com/nodejs/node/pull/37879) +* [[`9e6aa190e3`](https://github.com/nodejs/node/commit/9e6aa190e3)] - **deps**: add ngtcp2 and nghttp3 (James M Snell) [#37682](https://github.com/nodejs/node/pull/37682) +* [[`659fc5d684`](https://github.com/nodejs/node/commit/659fc5d684)] - **doc**: fix typos in lib/internal/bootstrap/pre\_execution.js (marsonya) [#37658](https://github.com/nodejs/node/pull/37658) +* [[`ac60d018e2`](https://github.com/nodejs/node/commit/ac60d018e2)] - **doc**: add more commands for cherry-picking and changelog to release docs (Danielle Adams) [#37785](https://github.com/nodejs/node/pull/37785) +* [[`0fe3c7edd3`](https://github.com/nodejs/node/commit/0fe3c7edd3)] - **doc**: spell out ICU acronym on first occurrence (Rich Trott) [#37942](https://github.com/nodejs/node/pull/37942) +* [[`364c8ac40d`](https://github.com/nodejs/node/commit/364c8ac40d)] - **doc**: update GOVERNANCE.md for TSC Charter changes (Rich Trott) [#37888](https://github.com/nodejs/node/pull/37888) +* [[`e84252b35d`](https://github.com/nodejs/node/commit/e84252b35d)] - **doc**: reduce header nesting in async\_hooks.md (Rich Trott) [#37839](https://github.com/nodejs/node/pull/37839) +* [[`a6f21e2cfc`](https://github.com/nodejs/node/commit/a6f21e2cfc)] - **doc**: fix wording in outgoingMessage.write (Tobias Nießen) [#37894](https://github.com/nodejs/node/pull/37894) +* [[`30bc2e43e4`](https://github.com/nodejs/node/commit/30bc2e43e4)] - **doc**: add examples for WHATWG URL objects (James M Snell) [#37822](https://github.com/nodejs/node/pull/37822) +* [[`c0a424f3e9`](https://github.com/nodejs/node/commit/c0a424f3e9)] - **doc**: clarify when child process 'spawn' event is \*not\* emitted (Matthew Francis Brunetti) [#37833](https://github.com/nodejs/node/pull/37833) +* [[`9defe10371`](https://github.com/nodejs/node/commit/9defe10371)] - **doc**: fix legacy stability indicator display (Rich Trott) [#37838](https://github.com/nodejs/node/pull/37838) +* [[`f97a5dd22f`](https://github.com/nodejs/node/commit/f97a5dd22f)] - **doc**: use sentence-style capitlaztion in template header (Rich Trott) [#37837](https://github.com/nodejs/node/pull/37837) +* [[`71fde07274`](https://github.com/nodejs/node/commit/71fde07274)] - **doc**: add Ayase-252 to triagers (Qingyu Deng) [#37781](https://github.com/nodejs/node/pull/37781) +* [[`8f18133de0`](https://github.com/nodejs/node/commit/8f18133de0)] - **doc**: use sentence case in issues.md headers (marsonya) [#37537](https://github.com/nodejs/node/pull/37537) +* [[`3376051a0e`](https://github.com/nodejs/node/commit/3376051a0e)] - **doc**: fix JS flavor selection (Antoine du Hamel) [#37791](https://github.com/nodejs/node/pull/37791) +* [[`b09d032683`](https://github.com/nodejs/node/commit/b09d032683)] - **doc**: move Derek Lewis back to collaborators (Derek Lewis) [#37726](https://github.com/nodejs/node/pull/37726) +* [[`6da0a0e85a`](https://github.com/nodejs/node/commit/6da0a0e85a)] - **doc**: apply style for legacy status (James M Snell) [#37784](https://github.com/nodejs/node/pull/37784) +* [[`185d4cd4aa`](https://github.com/nodejs/node/commit/185d4cd4aa)] - **doc**: revoke deprecation of legacy url, change status to legacy (James M Snell) [#37784](https://github.com/nodejs/node/pull/37784) +* [[`9d160daa89`](https://github.com/nodejs/node/commit/9d160daa89)] - **doc**: add legacy status to stability index (James M Snell) [#37784](https://github.com/nodejs/node/pull/37784) +* [[`4700042a9b`](https://github.com/nodejs/node/commit/4700042a9b)] - **doc**: add @linkgoron to collaborators (Nitzan Uziely) [#37817](https://github.com/nodejs/node/pull/37817) +* [[`c4183bbea4`](https://github.com/nodejs/node/commit/c4183bbea4)] - **doc**: fix AbortError example for timers (dbachko) [#37738](https://github.com/nodejs/node/pull/37738) +* [[`50f3ad1946`](https://github.com/nodejs/node/commit/50f3ad1946)] - **doc**: fix typo in stream docs (Ian Kerins) [#37716](https://github.com/nodejs/node/pull/37716) +* [[`2e82a97520`](https://github.com/nodejs/node/commit/2e82a97520)] - **doc**: add gyp maintain info (Jiawen Geng) [#37765](https://github.com/nodejs/node/pull/37765) +* [[`3925458df7`](https://github.com/nodejs/node/commit/3925458df7)] - **doc,tools**: use only one level 1 header per page (Rich Trott) [#37839](https://github.com/nodejs/node/pull/37839) +* [[`e9c161ce12`](https://github.com/nodejs/node/commit/e9c161ce12)] - **http**: fix double AbortSignal registration (Nitzan Uziely) [#37730](https://github.com/nodejs/node/pull/37730) +* [[`a5205819d8`](https://github.com/nodejs/node/commit/a5205819d8)] - **(SEMVER-MINOR)** **http**: add http.ClientRequest.getRawHeaderNames() (simov) [#37660](https://github.com/nodejs/node/pull/37660) +* [[`1c043272ea`](https://github.com/nodejs/node/commit/1c043272ea)] - **http2**: treat non-EOF empty frames like other invalid frames (Anna Henningsen) [#37875](https://github.com/nodejs/node/pull/37875) +* [[`a5bf7de6eb`](https://github.com/nodejs/node/commit/a5bf7de6eb)] - **http2**: fix setting options before handle exists (Anna Henningsen) [#37875](https://github.com/nodejs/node/pull/37875) +* [[`af7489cb6c`](https://github.com/nodejs/node/commit/af7489cb6c)] - **lib**: add brand checks to AbortController and AbortSignal (Mattias Buelens) [#37720](https://github.com/nodejs/node/pull/37720) +* [[`6e2b60931c`](https://github.com/nodejs/node/commit/6e2b60931c)] - **lib**: fix typo in internal/modules/esm/module\_job.js (marsonya) [#37773](https://github.com/nodejs/node/pull/37773) +* [[`3a440ecdf8`](https://github.com/nodejs/node/commit/3a440ecdf8)] - **lib**: fix typo in lib/internal/crypto/certificate.js (marsonya) [#37741](https://github.com/nodejs/node/pull/37741) +* [[`3ab223dd32`](https://github.com/nodejs/node/commit/3ab223dd32)] - **node-api**: fix crash in finalization (Michael Dawson) [#37876](https://github.com/nodejs/node/pull/37876) +* [[`d1a3e0efb6`](https://github.com/nodejs/node/commit/d1a3e0efb6)] - **node-api**: stop ref gc during environment teardown (Gabriel Schulhof) [#37616](https://github.com/nodejs/node/pull/37616) +* [[`e60bd1a7dc`](https://github.com/nodejs/node/commit/e60bd1a7dc)] - **(SEMVER-MINOR)** **perf_hooks**: make Performance extend EventTarget (Michaël Zasso) [#37621](https://github.com/nodejs/node/pull/37621) +* [[`b6ad8e4cc1`](https://github.com/nodejs/node/commit/b6ad8e4cc1)] - **src**: indent long help text properly (David Glasser) [#37911](https://github.com/nodejs/node/pull/37911) +* [[`13ecff63d6`](https://github.com/nodejs/node/commit/13ecff63d6)] - **src**: document newer values for --unhandled-rejections flag (David Glasser) [#37899](https://github.com/nodejs/node/pull/37899) +* [[`bd87e195ed`](https://github.com/nodejs/node/commit/bd87e195ed)] - **src**: fix typo in src code guide (Tobias Nießen) [#37956](https://github.com/nodejs/node/pull/37956) +* [[`2da532cef8`](https://github.com/nodejs/node/commit/2da532cef8)] - **src**: report idle time correctly (Stephen Belanger) [#37868](https://github.com/nodejs/node/pull/37868) +* [[`836cb67945`](https://github.com/nodejs/node/commit/836cb67945)] - **src**: add .note.GNU-stack section (James Addison) [#37688](https://github.com/nodejs/node/pull/37688) +* [[`9557dda2eb`](https://github.com/nodejs/node/commit/9557dda2eb)] - **(SEMVER-MINOR)** **stream**: pipeline accept Buffer as a valid first argument (Nitzan Uziely) [#37739](https://github.com/nodejs/node/pull/37739) +* [[`43c3b43ea3`](https://github.com/nodejs/node/commit/43c3b43ea3)] - **stream**: make Readable.from performance better (wwwzbwcom) [#37609](https://github.com/nodejs/node/pull/37609) +* [[`b0226b39f2`](https://github.com/nodejs/node/commit/b0226b39f2)] - **test**: split promisified timers test for coverage purposes (Rich Trott) [#37943](https://github.com/nodejs/node/pull/37943) +* [[`e256c4d11d`](https://github.com/nodejs/node/commit/e256c4d11d)] - **test**: fix typeof comparison (Rich Trott) [#37924](https://github.com/nodejs/node/pull/37924) +* [[`76ebc4bbd9`](https://github.com/nodejs/node/commit/76ebc4bbd9)] - **test**: increase wiggle room for memory in test-worker-resource-limits (Rich Trott) [#37901](https://github.com/nodejs/node/pull/37901) +* [[`5cdeb76708`](https://github.com/nodejs/node/commit/5cdeb76708)] - **test**: add OpenSSL 3.0 checks to tls-passphrase (Daniel Bevenius) [#37860](https://github.com/nodejs/node/pull/37860) +* [[`33c35a38dc`](https://github.com/nodejs/node/commit/33c35a38dc)] - **test**: add OpenSSL 3.0 checks to test-crypto-keygen (Daniel Bevenius) [#37860](https://github.com/nodejs/node/pull/37860) +* [[`86bf341a35`](https://github.com/nodejs/node/commit/86bf341a35)] - **test**: fix deprecation warning in test-doctool-html (Antoine du Hamel) [#37858](https://github.com/nodejs/node/pull/37858) +* [[`aa529b73b7`](https://github.com/nodejs/node/commit/aa529b73b7)] - **test**: fix ibmi skip message (Tobias Nießen) [#37821](https://github.com/nodejs/node/pull/37821) +* [[`d9ab1d56ce`](https://github.com/nodejs/node/commit/d9ab1d56ce)] - **test**: fix flaky test-vm-timeout-escape-promise-module-2 (Rich Trott) [#37842](https://github.com/nodejs/node/pull/37842) +* [[`5d4c610727`](https://github.com/nodejs/node/commit/5d4c610727)] - **test**: remove duplicated test for eventtarget (himself65) [#37853](https://github.com/nodejs/node/pull/37853) +* [[`44490af948`](https://github.com/nodejs/node/commit/44490af948)] - **test**: relax Y2K38 check in test-fs-utimes-y2K38 (Richard Lau) [#37825](https://github.com/nodejs/node/pull/37825) +* [[`9bc6fe7eb3`](https://github.com/nodejs/node/commit/9bc6fe7eb3)] - **test**: remove references to unsupported AIX versions (Richard Lau) [#37826](https://github.com/nodejs/node/pull/37826) +* [[`f07428ae51`](https://github.com/nodejs/node/commit/f07428ae51)] - **test**: remove skip for fixed test-benchmark-fs (Rich Trott) [#37803](https://github.com/nodejs/node/pull/37803) +* [[`9f61cbd1fd`](https://github.com/nodejs/node/commit/9f61cbd1fd)] - **test**: account for OOM risks in heapsnapshot-near-heap-limit tests (Joyee Cheung) [#37761](https://github.com/nodejs/node/pull/37761) +* [[`e85f311cf2`](https://github.com/nodejs/node/commit/e85f311cf2)] - **test**: refactor code to use AbortSignal.abort() (Wassim Chegham) [#37798](https://github.com/nodejs/node/pull/37798) +* [[`6ed9e0bd81`](https://github.com/nodejs/node/commit/6ed9e0bd81)] - **test**: improve test-arm-math-illegal-instruction (marsonya) [#37670](https://github.com/nodejs/node/pull/37670) +* [[`505f9c95d1`](https://github.com/nodejs/node/commit/505f9c95d1)] - **(SEMVER-MINOR)** **test**: app atob web platform tests (James M Snell) [#37529](https://github.com/nodejs/node/pull/37529) +* [[`a8edf1aafe`](https://github.com/nodejs/node/commit/a8edf1aafe)] - **test**: add known\_issues test for #13683 (Rich Trott) [#37744](https://github.com/nodejs/node/pull/37744) +* [[`4487483d9d`](https://github.com/nodejs/node/commit/4487483d9d)] - **test**: fix test-fs-utimes on non-Y2K38 file systems (Rich Trott) [#37707](https://github.com/nodejs/node/pull/37707) +* [[`d44b268910`](https://github.com/nodejs/node/commit/d44b268910)] - **timers**: fix arbitrary object clearImmediate errors (Nitzan Uziely) [#37824](https://github.com/nodejs/node/pull/37824) +* [[`b7e7384109`](https://github.com/nodejs/node/commit/b7e7384109)] - **tools**: improve valid-typeof lint rule (Rich Trott) [#37924](https://github.com/nodejs/node/pull/37924) +* [[`ca93e52783`](https://github.com/nodejs/node/commit/ca93e52783)] - **tools**: simplify eslint comma-dangle configuration (tools) (Rich Trott) [#37883](https://github.com/nodejs/node/pull/37883) +* [[`b5879efef1`](https://github.com/nodejs/node/commit/b5879efef1)] - **tools**: improve macos-firewall.sh output (Rich Trott) [#37846](https://github.com/nodejs/node/pull/37846) +* [[`dbc4804468`](https://github.com/nodejs/node/commit/dbc4804468)] - **tools**: simplify eslint comma-dangle configuration (Rich Trott) [#37850](https://github.com/nodejs/node/pull/37850) +* [[`0f2e142946`](https://github.com/nodejs/node/commit/0f2e142946)] - **tools**: make genv8constants.py Python3-compatible (Michaël Zasso) [#37835](https://github.com/nodejs/node/pull/37835) +* [[`b6be472456`](https://github.com/nodejs/node/commit/b6be472456)] - **tools**: update gitignore for CMake (Jiawen Geng) [#37793](https://github.com/nodejs/node/pull/37793) +* [[`2227aa61ea`](https://github.com/nodejs/node/commit/2227aa61ea)] - **tools**: partially detect quic support in shared\_openssl (James M Snell) [#37682](https://github.com/nodejs/node/pull/37682) +* [[`01dcf4d1d8`](https://github.com/nodejs/node/commit/01dcf4d1d8)] - **tools**: update ESLint to 7.22.0 (Colin Ihrig) [#37734](https://github.com/nodejs/node/pull/37734) +* [[`3452618905`](https://github.com/nodejs/node/commit/3452618905)] - **tty**: validate file descriptor to avoid int32 overflow (Antoine du Hamel) [#37809](https://github.com/nodejs/node/pull/37809) +* [[`d33f446abd`](https://github.com/nodejs/node/commit/d33f446abd)] - **util**: remove unreachable inspect code (Rich Trott) [#37941](https://github.com/nodejs/node/pull/37941) + <a id="15.12.0"></a> ## 2021-03-17, Version 15.12.0 (Current), @danielleadams diff --git a/src/node_version.h b/src/node_version.h index 12ae88222885b1..c978d1c785fde9 100644 --- a/src/node_version.h +++ b/src/node_version.h @@ -23,13 +23,13 @@ #define SRC_NODE_VERSION_H_ #define NODE_MAJOR_VERSION 15 -#define NODE_MINOR_VERSION 12 -#define NODE_PATCH_VERSION 1 +#define NODE_MINOR_VERSION 13 +#define NODE_PATCH_VERSION 0 #define NODE_VERSION_IS_LTS 0 #define NODE_VERSION_LTS_CODENAME "" -#define NODE_VERSION_IS_RELEASE 0 +#define NODE_VERSION_IS_RELEASE 1 #ifndef NODE_STRINGIFY #define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)