├── .eslintignore ├── .eslintrc.yml ├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── codeql.yml │ └── scorecard.yml ├── .gitignore ├── HISTORY.md ├── LICENSE ├── README.md ├── benchmark ├── fullurl.js ├── index.js ├── pathquery.js ├── samerequest.js ├── simplepath.js └── slash.js ├── index.js ├── package.json └── test ├── .eslintrc.yml └── test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | node_modules 3 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | extends: standard 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: / 5 | schedule: 6 | interval: monthly 7 | 8 | - package-ecosystem: npm 9 | directory: / 10 | schedule: 11 | interval: monthly 12 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | permissions: 8 | contents: read 9 | 10 | jobs: 11 | test: 12 | permissions: 13 | checks: write # for coverallsapp/github-action to create new checks 14 | contents: read # for actions/checkout to fetch code 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | name: 19 | - Node.js 0.8 20 | - Node.js 0.10 21 | - Node.js 0.12 22 | - io.js 1.x 23 | - io.js 2.x 24 | - io.js 3.x 25 | - Node.js 4.x 26 | - Node.js 5.x 27 | - Node.js 6.x 28 | - Node.js 7.x 29 | - Node.js 8.x 30 | - Node.js 9.x 31 | - Node.js 10.x 32 | - Node.js 11.x 33 | - Node.js 12.x 34 | - Node.js 13.x 35 | - Node.js 14.x 36 | - Node.js 15.x 37 | - Node.js 16.x 38 | - Node.js 17.x 39 | - Node.js 18.x 40 | - Node.js 19.x 41 | - Node.js 20.x 42 | - Node.js 21.x 43 | - Node.js 22.x 44 | 45 | include: 46 | - name: Node.js 0.8 47 | node-version: "0.8" 48 | npm-i: mocha@2.5.3 49 | npm-rm: beautify-benchmark benchmark fast-url-parser nyc 50 | 51 | - name: Node.js 0.10 52 | node-version: "0.10" 53 | npm-i: mocha@3.5.3 nyc@10.3.2 54 | npm-rm: beautify-benchmark benchmark fast-url-parser 55 | 56 | - name: Node.js 0.12 57 | node-version: "0.12" 58 | npm-i: mocha@3.5.3 nyc@10.3.2 59 | npm-rm: beautify-benchmark benchmark fast-url-parser 60 | 61 | - name: io.js 1.x 62 | node-version: "1.8" 63 | npm-i: mocha@3.5.3 nyc@10.3.2 64 | npm-rm: beautify-benchmark benchmark fast-url-parser 65 | 66 | - name: io.js 2.x 67 | node-version: "2.5" 68 | npm-i: mocha@3.5.3 nyc@10.3.2 69 | npm-rm: beautify-benchmark benchmark fast-url-parser 70 | 71 | - name: io.js 3.x 72 | node-version: "3.3" 73 | npm-i: mocha@3.5.3 nyc@10.3.2 74 | npm-rm: beautify-benchmark benchmark fast-url-parser 75 | 76 | - name: Node.js 4.x 77 | node-version: "4.9" 78 | npm-i: mocha@5.2.0 nyc@11.9.0 79 | npm-rm: beautify-benchmark benchmark fast-url-parser 80 | 81 | - name: Node.js 5.x 82 | node-version: "5.12" 83 | npm-i: mocha@5.2.0 nyc@11.9.0 84 | npm-rm: beautify-benchmark benchmark fast-url-parser 85 | 86 | - name: Node.js 6.x 87 | node-version: "6.17" 88 | npm-i: mocha@6.2.3 nyc@14.1.1 89 | npm-rm: beautify-benchmark benchmark fast-url-parser 90 | 91 | - name: Node.js 7.x 92 | node-version: "7.10" 93 | npm-i: mocha@6.2.3 nyc@14.1.1 94 | npm-rm: beautify-benchmark benchmark fast-url-parser 95 | 96 | - name: Node.js 8.x 97 | node-version: "8.17" 98 | npm-i: mocha@7.2.0 nyc@14.1.1 99 | npm-rm: beautify-benchmark benchmark fast-url-parser 100 | 101 | - name: Node.js 9.x 102 | node-version: "9.11" 103 | npm-i: mocha@7.2.0 nyc@14.1.1 104 | npm-rm: beautify-benchmark benchmark fast-url-parser 105 | 106 | - name: Node.js 10.x 107 | node-version: "10.24" 108 | npm-i: mocha@8.4.0 109 | npm-rm: beautify-benchmark benchmark fast-url-parser 110 | 111 | - name: Node.js 11.x 112 | node-version: "11.15" 113 | npm-i: mocha@8.4.0 114 | npm-rm: beautify-benchmark benchmark fast-url-parser 115 | 116 | - name: Node.js 12.x 117 | node-version: "12.22" 118 | npm-i: mocha@9.2.2 119 | npm-rm: beautify-benchmark benchmark fast-url-parser 120 | 121 | - name: Node.js 13.x 122 | node-version: "13.14" 123 | npm-i: mocha@9.2.2 124 | npm-rm: beautify-benchmark benchmark fast-url-parser 125 | 126 | - name: Node.js 14.x 127 | node-version: "14.21" 128 | npm-rm: beautify-benchmark benchmark fast-url-parser 129 | 130 | - name: Node.js 15.x 131 | node-version: "15.14" 132 | npm-rm: beautify-benchmark benchmark fast-url-parser 133 | 134 | - name: Node.js 16.x 135 | node-version: "16.19" 136 | npm-rm: beautify-benchmark benchmark fast-url-parser 137 | 138 | - name: Node.js 17.x 139 | node-version: "17.9" 140 | npm-rm: beautify-benchmark benchmark fast-url-parser 141 | 142 | - name: Node.js 18.x 143 | node-version: "18.13" 144 | npm-rm: beautify-benchmark benchmark fast-url-parser 145 | 146 | - name: Node.js 19.x 147 | node-version: "19.6" 148 | 149 | - name: Node.js 20.x 150 | node-version: "20.12" 151 | 152 | - name: Node.js 21.x 153 | node-version: "21.7" 154 | 155 | - name: Node.js 22.x 156 | node-version: "22.0" 157 | 158 | steps: 159 | - uses: actions/checkout@v4 160 | 161 | - name: Install Node.js ${{ matrix.node-version }} 162 | shell: bash -eo pipefail -l {0} 163 | run: | 164 | nvm install --default ${{ matrix.node-version }} 165 | if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then 166 | nvm install --alias=npm 0.10 167 | nvm use ${{ matrix.node-version }} 168 | sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")" 169 | npm config set strict-ssl false 170 | fi 171 | dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH" 172 | 173 | - name: Configure npm 174 | run: | 175 | if [[ "$(npm config get package-lock)" == "true" ]]; then 176 | npm config set package-lock false 177 | else 178 | npm config set shrinkwrap false 179 | fi 180 | 181 | - name: Remove npm module(s) ${{ matrix.npm-rm }} 182 | run: npm rm --silent --save-dev ${{ matrix.npm-rm }} 183 | if: matrix.npm-rm != '' 184 | 185 | - name: Install npm module(s) ${{ matrix.npm-i }} 186 | run: npm install --save-dev ${{ matrix.npm-i }} 187 | if: matrix.npm-i != '' 188 | 189 | - name: Setup Node.js version-specific dependencies 190 | shell: bash 191 | run: | 192 | # eslint for linting 193 | # - remove on Node.js < 12 194 | if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then 195 | node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \ 196 | grep -E '^eslint(-|$)' | \ 197 | sort -r | \ 198 | xargs -n1 npm rm --silent --save-dev 199 | fi 200 | 201 | - name: Install Node.js dependencies 202 | run: npm install 203 | 204 | - name: List environment 205 | id: list_env 206 | shell: bash 207 | run: | 208 | echo "node@$(node -v)" 209 | echo "npm@$(npm -v)" 210 | npm -s ls ||: 211 | (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print "::set-output name=" $2 "::" $3 }' 212 | 213 | - name: Run tests 214 | shell: bash 215 | run: | 216 | if npm -ps ls nyc | grep -q nyc; then 217 | npm run test-ci 218 | else 219 | npm test 220 | fi 221 | 222 | - name: Lint code 223 | if: steps.list_env.outputs.eslint != '' 224 | run: npm run lint 225 | 226 | - name: Collect code coverage 227 | uses: coverallsapp/github-action@master 228 | if: steps.list_env.outputs.nyc != '' 229 | with: 230 | github-token: ${{ secrets.GITHUB_TOKEN }} 231 | flag-name: run-${{ matrix.test_number }} 232 | parallel: true 233 | 234 | coverage: 235 | permissions: 236 | checks: write # for coverallsapp/github-action to create new checks 237 | needs: test 238 | runs-on: ubuntu-latest 239 | steps: 240 | - name: Upload code coverage 241 | uses: coverallsapp/github-action@master 242 | with: 243 | github-token: ${{ secrets.github_token }} 244 | parallel-finished: true 245 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: ["master"] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: ["master"] 20 | schedule: 21 | - cron: "0 0 * * 1" 22 | 23 | permissions: 24 | contents: read 25 | 26 | jobs: 27 | analyze: 28 | name: Analyze 29 | runs-on: ubuntu-latest 30 | permissions: 31 | actions: read 32 | contents: read 33 | security-events: write 34 | 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | language: ["javascript"] 39 | # CodeQL supports [ $supported-codeql-languages ] 40 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 41 | 42 | steps: 43 | - name: Checkout repository 44 | uses: actions/checkout@v4 45 | 46 | # Initializes the CodeQL tools for scanning. 47 | - name: Initialize CodeQL 48 | uses: github/codeql-action/init@v3 49 | with: 50 | languages: ${{ matrix.language }} 51 | # If you wish to specify custom queries, you can do so here or in a config file. 52 | # By default, queries listed here will override any specified in a config file. 53 | # Prefix the list here with "+" to use these queries and those in the config file. 54 | 55 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 56 | # If this step fails, then you should remove it and run the build manually (see below) 57 | - name: Autobuild 58 | uses: github/codeql-action/autobuild@v3 59 | 60 | # ℹ️ Command-line programs to run using the OS shell. 61 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 62 | 63 | # If the Autobuild fails above, remove it and uncomment the following three lines. 64 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 65 | 66 | # - run: | 67 | # echo "Run, Build Application using script" 68 | # ./location_of_script_within_repo/buildscript.sh 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v3 72 | with: 73 | category: "/language:${{matrix.language}}" 74 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | 7 | on: 8 | # For Branch-Protection check. Only the default branch is supported. See 9 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 10 | branch_protection_rule: 11 | # To guarantee Maintained check is occasionally updated. See 12 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 13 | schedule: 14 | - cron: '16 21 * * 1' 15 | push: 16 | branches: [ "master" ] 17 | 18 | # Declare default permissions as read only. 19 | permissions: read-all 20 | 21 | jobs: 22 | analysis: 23 | name: Scorecard analysis 24 | runs-on: ubuntu-latest 25 | permissions: 26 | # Needed to upload the results to code-scanning dashboard. 27 | security-events: write 28 | # Needed to publish results and get a badge (see publish_results below). 29 | id-token: write 30 | # Uncomment the permissions below if installing in a private repository. 31 | # contents: read 32 | # actions: read 33 | 34 | steps: 35 | - name: "Checkout code" 36 | uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.2 37 | with: 38 | persist-credentials: false 39 | 40 | - name: "Run analysis" 41 | uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 42 | with: 43 | results_file: results.sarif 44 | results_format: sarif 45 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 46 | # - you want to enable the Branch-Protection check on a *public* repository, or 47 | # - you are installing Scorecard on a *private* repository 48 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 49 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 50 | 51 | # Public repositories: 52 | # - Publish results to OpenSSF REST API for easy access by consumers 53 | # - Allows the repository to include the Scorecard badge. 54 | # - See https://github.com/ossf/scorecard-action#publishing-results. 55 | # For private repositories: 56 | # - `publish_results` will always be set to `false`, regardless 57 | # of the value entered here. 58 | publish_results: true 59 | 60 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 61 | # format to the repository Actions tab. 62 | - name: "Upload artifact" 63 | uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 64 | with: 65 | name: SARIF file 66 | path: results.sarif 67 | retention-days: 5 68 | 69 | # Upload the results to GitHub's code scanning dashboard. 70 | - name: "Upload to code-scanning" 71 | uses: github/codeql-action/upload-sarif@2f93e4319b2f04a2efc38fa7f78bd681bc3f7b2f # v2.23.2 72 | with: 73 | sarif_file: results.sarif 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | coverage 3 | node_modules 4 | npm-debug.log 5 | package-lock.json 6 | *.log 7 | -------------------------------------------------------------------------------- /HISTORY.md: -------------------------------------------------------------------------------- 1 | 1.3.3 / 2019-04-15 2 | ================== 3 | 4 | * Fix Node.js 0.8 return value inconsistencies 5 | 6 | 1.3.2 / 2017-09-09 7 | ================== 8 | 9 | * perf: reduce overhead for full URLs 10 | * perf: unroll the "fast-path" `RegExp` 11 | 12 | 1.3.1 / 2016-01-17 13 | ================== 14 | 15 | * perf: enable strict mode 16 | 17 | 1.3.0 / 2014-08-09 18 | ================== 19 | 20 | * Add `parseurl.original` for parsing `req.originalUrl` with fallback 21 | * Return `undefined` if `req.url` is `undefined` 22 | 23 | 1.2.0 / 2014-07-21 24 | ================== 25 | 26 | * Cache URLs based on original value 27 | * Remove no-longer-needed URL mis-parse work-around 28 | * Simplify the "fast-path" `RegExp` 29 | 30 | 1.1.3 / 2014-07-08 31 | ================== 32 | 33 | * Fix typo 34 | 35 | 1.1.2 / 2014-07-08 36 | ================== 37 | 38 | * Seriously fix Node.js 0.8 compatibility 39 | 40 | 1.1.1 / 2014-07-08 41 | ================== 42 | 43 | * Fix Node.js 0.8 compatibility 44 | 45 | 1.1.0 / 2014-07-08 46 | ================== 47 | 48 | * Incorporate URL href-only parse fast-path 49 | 50 | 1.0.1 / 2014-03-08 51 | ================== 52 | 53 | * Add missing `require` 54 | 55 | 1.0.0 / 2014-03-08 56 | ================== 57 | 58 | * Genesis from `connect` 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | (The MIT License) 3 | 4 | Copyright (c) 2014 Jonathan Ong 5 | Copyright (c) 2014-2017 Douglas Christopher Wilson 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining 8 | a copy of this software and associated documentation files (the 9 | 'Software'), to deal in the Software without restriction, including 10 | without limitation the rights to use, copy, modify, merge, publish, 11 | distribute, sublicense, and/or sell copies of the Software, and to 12 | permit persons to whom the Software is furnished to do so, subject to 13 | the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be 16 | included in all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 23 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parseurl 2 | 3 | [![NPM Version][npm-version-image]][npm-url] 4 | [![NPM Downloads][npm-downloads-image]][npm-url] 5 | [![Node.js Version][node-image]][node-url] 6 | [![Build Status][github-actions-ci-image]][github-actions-ci-url] 7 | [![Test Coverage][coveralls-image]][coveralls-url] 8 | 9 | Parse a URL with memoization. 10 | 11 | ## Install 12 | 13 | This is a [Node.js](https://nodejs.org/en/) module available through the 14 | [npm registry](https://www.npmjs.com/). Installation is done using the 15 | [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally): 16 | 17 | ```sh 18 | $ npm install parseurl 19 | ``` 20 | 21 | ## API 22 | 23 | ```js 24 | var parseurl = require('parseurl') 25 | ``` 26 | 27 | ### parseurl(req) 28 | 29 | Parse the URL of the given request object (looks at the `req.url` property) 30 | and return the result. The result is the same as `url.parse` in Node.js core. 31 | Calling this function multiple times on the same `req` where `req.url` does 32 | not change will return a cached parsed object, rather than parsing again. 33 | 34 | ### parseurl.original(req) 35 | 36 | Parse the original URL of the given request object and return the result. 37 | This works by trying to parse `req.originalUrl` if it is a string, otherwise 38 | parses `req.url`. The result is the same as `url.parse` in Node.js core. 39 | Calling this function multiple times on the same `req` where `req.originalUrl` 40 | does not change will return a cached parsed object, rather than parsing again. 41 | 42 | ## Benchmark 43 | 44 | ```bash 45 | $ npm run-script bench 46 | 47 | > parseurl@1.3.3 bench nodejs-parseurl 48 | > node benchmark/index.js 49 | 50 | http_parser@2.8.0 51 | node@10.6.0 52 | v8@6.7.288.46-node.13 53 | uv@1.21.0 54 | zlib@1.2.11 55 | ares@1.14.0 56 | modules@64 57 | nghttp2@1.32.0 58 | napi@3 59 | openssl@1.1.0h 60 | icu@61.1 61 | unicode@10.0 62 | cldr@33.0 63 | tz@2018c 64 | 65 | > node benchmark/fullurl.js 66 | 67 | Parsing URL "http://localhost:8888/foo/bar?user=tj&pet=fluffy" 68 | 69 | 4 tests completed. 70 | 71 | fasturl x 2,207,842 ops/sec ±3.76% (184 runs sampled) 72 | nativeurl - legacy x 507,180 ops/sec ±0.82% (191 runs sampled) 73 | nativeurl - whatwg x 290,044 ops/sec ±1.96% (189 runs sampled) 74 | parseurl x 488,907 ops/sec ±2.13% (192 runs sampled) 75 | 76 | > node benchmark/pathquery.js 77 | 78 | Parsing URL "/foo/bar?user=tj&pet=fluffy" 79 | 80 | 4 tests completed. 81 | 82 | fasturl x 3,812,564 ops/sec ±3.15% (188 runs sampled) 83 | nativeurl - legacy x 2,651,631 ops/sec ±1.68% (189 runs sampled) 84 | nativeurl - whatwg x 161,837 ops/sec ±2.26% (189 runs sampled) 85 | parseurl x 4,166,338 ops/sec ±2.23% (184 runs sampled) 86 | 87 | > node benchmark/samerequest.js 88 | 89 | Parsing URL "/foo/bar?user=tj&pet=fluffy" on same request object 90 | 91 | 4 tests completed. 92 | 93 | fasturl x 3,821,651 ops/sec ±2.42% (185 runs sampled) 94 | nativeurl - legacy x 2,651,162 ops/sec ±1.90% (187 runs sampled) 95 | nativeurl - whatwg x 175,166 ops/sec ±1.44% (188 runs sampled) 96 | parseurl x 14,912,606 ops/sec ±3.59% (183 runs sampled) 97 | 98 | > node benchmark/simplepath.js 99 | 100 | Parsing URL "/foo/bar" 101 | 102 | 4 tests completed. 103 | 104 | fasturl x 12,421,765 ops/sec ±2.04% (191 runs sampled) 105 | nativeurl - legacy x 7,546,036 ops/sec ±1.41% (188 runs sampled) 106 | nativeurl - whatwg x 198,843 ops/sec ±1.83% (189 runs sampled) 107 | parseurl x 24,244,006 ops/sec ±0.51% (194 runs sampled) 108 | 109 | > node benchmark/slash.js 110 | 111 | Parsing URL "/" 112 | 113 | 4 tests completed. 114 | 115 | fasturl x 17,159,456 ops/sec ±3.25% (188 runs sampled) 116 | nativeurl - legacy x 11,635,097 ops/sec ±3.79% (184 runs sampled) 117 | nativeurl - whatwg x 240,693 ops/sec ±0.83% (189 runs sampled) 118 | parseurl x 42,279,067 ops/sec ±0.55% (190 runs sampled) 119 | ``` 120 | 121 | ## License 122 | 123 | [MIT](LICENSE) 124 | 125 | [coveralls-image]: https://badgen.net/coveralls/c/github/pillarjs/parseurl/master 126 | [coveralls-url]: https://coveralls.io/r/pillarjs/parseurl?branch=master 127 | [github-actions-ci-image]: https://badgen.net/github/checks/pillarjs/parseurl/master?label=ci 128 | [github-actions-ci-url]: https://github.com/pillarjs/parseurl/actions?query=workflow%3Aci 129 | [node-image]: https://badgen.net/npm/node/parseurl 130 | [node-url]: https://nodejs.org/en/download 131 | [npm-downloads-image]: https://badgen.net/npm/dm/parseurl 132 | [npm-url]: https://npmjs.org/package/parseurl 133 | [npm-version-image]: https://badgen.net/npm/v/parseurl 134 | -------------------------------------------------------------------------------- /benchmark/fullurl.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Globals for benchmark.js 4 | */ 5 | 6 | global.assert = require('assert') 7 | global.createReq = createReq 8 | global.fasturl = require('fast-url-parser') 9 | global.nativeurl = require('url') 10 | global.parseurl = require('..') 11 | global.url = 'http://localhost:8888/foo/bar?user=tj&pet=fluffy' 12 | 13 | /** 14 | * Module dependencies. 15 | */ 16 | 17 | var benchmark = require('benchmark') 18 | var benchmarks = require('beautify-benchmark') 19 | 20 | var assertValues = 'assert.strictEqual(obj.pathname, "/foo/bar"); assert.strictEqual(obj.search, "?user=tj&pet=fluffy");' 21 | var suite = new benchmark.Suite() 22 | 23 | suite.add({ 24 | name: 'fasturl', 25 | minSamples: 100, 26 | fn: 'var obj = fasturl.parse(createReq(url).url);' + assertValues 27 | }) 28 | 29 | suite.add({ 30 | name: 'nativeurl' + (global.nativeurl.URL ? ' - legacy' : ''), 31 | minSamples: 100, 32 | fn: 'var obj = nativeurl.parse(createReq(url).url);' + assertValues 33 | }) 34 | 35 | if (global.nativeurl.URL) { 36 | suite.add({ 37 | name: 'nativeurl - whatwg', 38 | minSamples: 100, 39 | fn: 'var obj = new nativeurl.URL(createReq(url).url);' + assertValues 40 | }) 41 | } 42 | 43 | suite.add({ 44 | name: 'parseurl', 45 | minSamples: 100, 46 | fn: 'var obj = parseurl(createReq(url));' + assertValues 47 | }) 48 | 49 | suite.on('start', function onCycle (event) { 50 | process.stdout.write(' Parsing URL ' + JSON.stringify(global.url) + '\n\n') 51 | }) 52 | 53 | suite.on('cycle', function onCycle (event) { 54 | benchmarks.add(event.target) 55 | }) 56 | 57 | suite.on('complete', function onComplete () { 58 | benchmarks.log() 59 | }) 60 | 61 | suite.run({ async: false }) 62 | 63 | function createReq (url) { 64 | return { 65 | url: url 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /benchmark/index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | var spawn = require('child_process').spawn 4 | 5 | var exe = process.argv[0] 6 | var cwd = process.cwd() 7 | 8 | for (var dep in process.versions) { 9 | console.log(' %s@%s', dep, process.versions[dep]) 10 | } 11 | 12 | console.log('') 13 | 14 | runScripts(fs.readdirSync(__dirname)) 15 | 16 | function runScripts (fileNames) { 17 | var fileName = fileNames.shift() 18 | 19 | if (!fileName) return 20 | if (!/\.js$/i.test(fileName)) return runScripts(fileNames) 21 | if (fileName.toLowerCase() === 'index.js') return runScripts(fileNames) 22 | 23 | var fullPath = path.join(__dirname, fileName) 24 | 25 | console.log('> %s %s', exe, path.relative(cwd, fullPath)) 26 | 27 | var proc = spawn(exe, [fullPath], { 28 | stdio: 'inherit' 29 | }) 30 | 31 | proc.on('exit', function () { 32 | runScripts(fileNames) 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /benchmark/pathquery.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Globals for benchmark.js 4 | */ 5 | 6 | global.assert = require('assert') 7 | global.createReq = createReq 8 | global.fasturl = require('fast-url-parser') 9 | global.nativeurl = require('url') 10 | global.parseurl = require('..') 11 | global.url = '/foo/bar?user=tj&pet=fluffy' 12 | 13 | /** 14 | * Module dependencies. 15 | */ 16 | 17 | var benchmark = require('benchmark') 18 | var benchmarks = require('beautify-benchmark') 19 | 20 | var assertValues = 'assert.strictEqual(obj.pathname, "/foo/bar"); assert.strictEqual(obj.search, "?user=tj&pet=fluffy");' 21 | var suite = new benchmark.Suite() 22 | 23 | suite.add({ 24 | name: 'fasturl', 25 | minSamples: 100, 26 | fn: 'var obj = fasturl.parse(createReq(url).url);' + assertValues 27 | }) 28 | 29 | suite.add({ 30 | name: 'nativeurl' + (global.nativeurl.URL ? ' - legacy' : ''), 31 | minSamples: 100, 32 | fn: 'var obj = nativeurl.parse(createReq(url).url);' + assertValues 33 | }) 34 | 35 | if (global.nativeurl.URL) { 36 | suite.add({ 37 | name: 'nativeurl - whatwg', 38 | minSamples: 100, 39 | fn: 'var obj = new nativeurl.URL(createReq(url).url, "http://localhost");' + assertValues 40 | }) 41 | } 42 | 43 | suite.add({ 44 | name: 'parseurl', 45 | minSamples: 100, 46 | fn: 'var obj = parseurl(createReq(url));' + assertValues 47 | }) 48 | 49 | suite.on('start', function onCycle (event) { 50 | process.stdout.write(' Parsing URL ' + JSON.stringify(global.url) + '\n\n') 51 | }) 52 | 53 | suite.on('cycle', function onCycle (event) { 54 | benchmarks.add(event.target) 55 | }) 56 | 57 | suite.on('complete', function onComplete () { 58 | benchmarks.log() 59 | }) 60 | 61 | suite.run({ async: false }) 62 | 63 | function createReq (url) { 64 | return { 65 | url: url 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /benchmark/samerequest.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Globals for benchmark.js 4 | */ 5 | 6 | global.assert = require('assert') 7 | global.createReq = createReq 8 | global.fasturl = require('fast-url-parser') 9 | global.nativeurl = require('url') 10 | global.parseurl = require('..') 11 | global.url = '/foo/bar?user=tj&pet=fluffy' 12 | 13 | /** 14 | * Module dependencies. 15 | */ 16 | 17 | var benchmark = require('benchmark') 18 | var benchmarks = require('beautify-benchmark') 19 | 20 | var assertValues = 'assert.strictEqual(obj.pathname, "/foo/bar"); assert.strictEqual(obj.search, "?user=tj&pet=fluffy");' 21 | var suite = new benchmark.Suite() 22 | 23 | suite.add({ 24 | name: 'fasturl', 25 | minSamples: 100, 26 | fn: 'var obj = fasturl.parse(req.url);' + assertValues, 27 | setup: 'req = createReq(url)' 28 | }) 29 | 30 | suite.add({ 31 | name: 'nativeurl' + (global.nativeurl.URL ? ' - legacy' : ''), 32 | minSamples: 100, 33 | fn: 'var obj = nativeurl.parse(req.url);' + assertValues, 34 | setup: 'req = createReq(url)' 35 | }) 36 | 37 | if (global.nativeurl.URL) { 38 | suite.add({ 39 | name: 'nativeurl - whatwg', 40 | minSamples: 100, 41 | fn: 'var obj = new nativeurl.URL(req.url, "http://localhost");' + assertValues, 42 | setup: 'req = createReq(url)' 43 | }) 44 | } 45 | 46 | suite.add({ 47 | name: 'parseurl', 48 | minSamples: 100, 49 | fn: 'var obj = parseurl(req);' + assertValues, 50 | setup: 'req = createReq(url)' 51 | }) 52 | 53 | suite.on('start', function onCycle (event) { 54 | process.stdout.write(' Parsing URL ' + JSON.stringify(global.url) + ' on same request object\n\n') 55 | }) 56 | 57 | suite.on('cycle', function onCycle (event) { 58 | benchmarks.add(event.target) 59 | }) 60 | 61 | suite.on('complete', function onComplete () { 62 | benchmarks.log() 63 | }) 64 | 65 | suite.run({ async: false }) 66 | 67 | function createReq (url) { 68 | return { 69 | url: url 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /benchmark/simplepath.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Globals for benchmark.js 4 | */ 5 | 6 | global.assert = require('assert') 7 | global.createReq = createReq 8 | global.fasturl = require('fast-url-parser') 9 | global.nativeurl = require('url') 10 | global.parseurl = require('..') 11 | global.url = '/foo/bar' 12 | 13 | /** 14 | * Module dependencies. 15 | */ 16 | 17 | var benchmark = require('benchmark') 18 | var benchmarks = require('beautify-benchmark') 19 | 20 | var assertValues = 'assert.strictEqual(obj.pathname, "/foo/bar"); assert.ok(!obj.search);' 21 | var suite = new benchmark.Suite() 22 | 23 | suite.add({ 24 | name: 'fasturl', 25 | minSamples: 100, 26 | fn: 'var obj = fasturl.parse(createReq(url).url);' + assertValues 27 | }) 28 | 29 | suite.add({ 30 | name: 'nativeurl' + (global.nativeurl.URL ? ' - legacy' : ''), 31 | minSamples: 100, 32 | fn: 'var obj = nativeurl.parse(createReq(url).url);' + assertValues 33 | }) 34 | 35 | if (global.nativeurl.URL) { 36 | suite.add({ 37 | name: 'nativeurl - whatwg', 38 | minSamples: 100, 39 | fn: 'var obj = new nativeurl.URL(createReq(url).url, "http://localhost");' + assertValues, 40 | setup: 'req = createReq(url)' 41 | }) 42 | } 43 | 44 | suite.add({ 45 | name: 'parseurl', 46 | minSamples: 100, 47 | fn: 'var obj = parseurl(createReq(url));' + assertValues 48 | }) 49 | 50 | suite.on('start', function onCycle (event) { 51 | process.stdout.write(' Parsing URL ' + JSON.stringify(global.url) + '\n\n') 52 | }) 53 | 54 | suite.on('cycle', function onCycle (event) { 55 | benchmarks.add(event.target) 56 | }) 57 | 58 | suite.on('complete', function onComplete () { 59 | benchmarks.log() 60 | }) 61 | 62 | suite.run({ async: false }) 63 | 64 | function createReq (url) { 65 | return { 66 | url: url 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /benchmark/slash.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Globals for benchmark.js 4 | */ 5 | 6 | global.assert = require('assert') 7 | global.createReq = createReq 8 | global.fasturl = require('fast-url-parser') 9 | global.nativeurl = require('url') 10 | global.parseurl = require('..') 11 | global.url = '/' 12 | 13 | /** 14 | * Module dependencies. 15 | */ 16 | 17 | var benchmark = require('benchmark') 18 | var benchmarks = require('beautify-benchmark') 19 | 20 | var assertValues = 'assert.strictEqual(obj.pathname, "/"); assert.ok(!obj.search);' 21 | var suite = new benchmark.Suite() 22 | 23 | suite.add({ 24 | name: 'fasturl', 25 | minSamples: 100, 26 | fn: 'var obj = fasturl.parse(createReq(url).url);' + assertValues 27 | }) 28 | 29 | suite.add({ 30 | name: 'nativeurl' + (global.nativeurl.URL ? ' - legacy' : ''), 31 | minSamples: 100, 32 | fn: 'var obj = nativeurl.parse(createReq(url).url);' + assertValues 33 | }) 34 | 35 | if (global.nativeurl.URL) { 36 | suite.add({ 37 | name: 'nativeurl - whatwg', 38 | minSamples: 100, 39 | fn: 'var obj = new nativeurl.URL(createReq(url).url, "http://localhost");' + assertValues 40 | }) 41 | } 42 | 43 | suite.add({ 44 | name: 'parseurl', 45 | minSamples: 100, 46 | fn: 'var obj = parseurl(createReq(url));' + assertValues 47 | }) 48 | 49 | suite.on('start', function onCycle (event) { 50 | process.stdout.write(' Parsing URL ' + JSON.stringify(global.url) + '\n\n') 51 | }) 52 | 53 | suite.on('cycle', function onCycle (event) { 54 | benchmarks.add(event.target) 55 | }) 56 | 57 | suite.on('complete', function onComplete () { 58 | benchmarks.log() 59 | }) 60 | 61 | suite.run({ async: false }) 62 | 63 | function createReq (url) { 64 | return { 65 | url: url 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * parseurl 3 | * Copyright(c) 2014 Jonathan Ong 4 | * Copyright(c) 2014-2017 Douglas Christopher Wilson 5 | * MIT Licensed 6 | */ 7 | 8 | 'use strict' 9 | 10 | /** 11 | * Module dependencies. 12 | * @private 13 | */ 14 | 15 | var url = require('url') 16 | var parse = url.parse // eslint-disable-line 17 | var Url = url.Url 18 | 19 | /** 20 | * Module exports. 21 | * @public 22 | */ 23 | 24 | module.exports = parseurl 25 | module.exports.original = originalurl 26 | 27 | /** 28 | * Parse the `req` url with memoization. 29 | * 30 | * @param {ServerRequest} req 31 | * @return {Object} 32 | * @public 33 | */ 34 | 35 | function parseurl (req) { 36 | var url = req.url 37 | 38 | if (url === undefined) { 39 | // URL is undefined 40 | return undefined 41 | } 42 | 43 | var parsed = req._parsedUrl 44 | 45 | if (fresh(url, parsed)) { 46 | // Return cached URL parse 47 | return parsed 48 | } 49 | 50 | // Parse the URL 51 | parsed = fastparse(url) 52 | parsed._raw = url 53 | 54 | return (req._parsedUrl = parsed) 55 | }; 56 | 57 | /** 58 | * Parse the `req` original url with fallback and memoization. 59 | * 60 | * @param {ServerRequest} req 61 | * @return {Object} 62 | * @public 63 | */ 64 | 65 | function originalurl (req) { 66 | var url = req.originalUrl 67 | 68 | if (typeof url !== 'string') { 69 | // Fallback 70 | return parseurl(req) 71 | } 72 | 73 | var parsed = req._parsedOriginalUrl 74 | 75 | if (fresh(url, parsed)) { 76 | // Return cached URL parse 77 | return parsed 78 | } 79 | 80 | // Parse the URL 81 | parsed = fastparse(url) 82 | parsed._raw = url 83 | 84 | return (req._parsedOriginalUrl = parsed) 85 | }; 86 | 87 | /** 88 | * Parse the `str` url with fast-path short-cut. 89 | * 90 | * @param {string} str 91 | * @return {Object} 92 | * @private 93 | */ 94 | 95 | function fastparse (str) { 96 | if (typeof str !== 'string' || str.charCodeAt(0) !== 0x2f /* / */) { 97 | return parse(str) 98 | } 99 | 100 | var pathname = str 101 | var query = null 102 | var search = null 103 | 104 | // This takes the regexp from https://github.com/joyent/node/pull/7878 105 | // Which is /^(\/[^?#\s]*)(\?[^#\s]*)?$/ 106 | // And unrolls it into a for loop 107 | for (var i = 1; i < str.length; i++) { 108 | switch (str.charCodeAt(i)) { 109 | case 0x3f: /* ? */ 110 | if (search === null) { 111 | pathname = str.substring(0, i) 112 | query = str.substring(i + 1) 113 | search = str.substring(i) 114 | } 115 | break 116 | case 0x09: /* \t */ 117 | case 0x0a: /* \n */ 118 | case 0x0c: /* \f */ 119 | case 0x0d: /* \r */ 120 | case 0x20: /* */ 121 | case 0x23: /* # */ 122 | case 0xa0: 123 | case 0xfeff: 124 | return parse(str) 125 | } 126 | } 127 | 128 | var url = Url !== undefined 129 | ? new Url() 130 | : {} 131 | 132 | url.path = str 133 | url.href = str 134 | url.pathname = pathname 135 | 136 | if (search !== null) { 137 | url.query = query 138 | url.search = search 139 | } 140 | 141 | return url 142 | } 143 | 144 | /** 145 | * Determine if parsed is still fresh for url. 146 | * 147 | * @param {string} url 148 | * @param {object} parsedUrl 149 | * @return {boolean} 150 | * @private 151 | */ 152 | 153 | function fresh (url, parsedUrl) { 154 | return typeof parsedUrl === 'object' && 155 | parsedUrl !== null && 156 | (Url === undefined || parsedUrl instanceof Url) && 157 | parsedUrl._raw === url 158 | } 159 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parseurl", 3 | "description": "parse a url with memoization", 4 | "version": "1.3.3", 5 | "contributors": [ 6 | "Douglas Christopher Wilson ", 7 | "Jonathan Ong (http://jongleberry.com)" 8 | ], 9 | "repository": "pillarjs/parseurl", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "beautify-benchmark": "0.2.4", 13 | "benchmark": "2.1.4", 14 | "eslint": "8.33.0", 15 | "eslint-config-standard": "15.0.1", 16 | "eslint-plugin-import": "2.27.5", 17 | "eslint-plugin-node": "11.1.0", 18 | "eslint-plugin-promise": "6.1.1", 19 | "eslint-plugin-standard": "4.1.0", 20 | "fast-url-parser": "1.1.3", 21 | "mocha": "10.2.0", 22 | "nyc": "15.1.0" 23 | }, 24 | "files": [ 25 | "LICENSE", 26 | "HISTORY.md", 27 | "README.md", 28 | "index.js" 29 | ], 30 | "engines": { 31 | "node": ">= 0.8" 32 | }, 33 | "scripts": { 34 | "bench": "node benchmark/index.js", 35 | "lint": "eslint .", 36 | "test": "mocha --check-leaks --bail --reporter spec test/", 37 | "test-ci": "nyc --reporter=lcovonly --reporter=text npm test", 38 | "test-cov": "nyc --reporter=html --reporter=text npm test" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | mocha: true 3 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | var assert = require('assert') 3 | var parseurl = require('..') 4 | var url = require('url') 5 | 6 | var URL_EMPTY_VALUE = url.Url 7 | ? null 8 | : undefined 9 | 10 | describe('parseurl(req)', function () { 11 | it('should parse the request URL', function () { 12 | var req = createReq('/foo/bar') 13 | var url = parseurl(req) 14 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 15 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 16 | assert.strictEqual(url.href, '/foo/bar') 17 | assert.strictEqual(url.pathname, '/foo/bar') 18 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 19 | assert.strictEqual(url.query, URL_EMPTY_VALUE) 20 | assert.strictEqual(url.search, URL_EMPTY_VALUE) 21 | }) 22 | 23 | it('should parse with query string', function () { 24 | var req = createReq('/foo/bar?fizz=buzz') 25 | var url = parseurl(req) 26 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 27 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 28 | assert.strictEqual(url.href, '/foo/bar?fizz=buzz') 29 | assert.strictEqual(url.pathname, '/foo/bar') 30 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 31 | assert.strictEqual(url.query, 'fizz=buzz') 32 | assert.strictEqual(url.search, '?fizz=buzz') 33 | }) 34 | 35 | it('should parse with hash', function () { 36 | var req = createReq('/foo/bar#bazz') 37 | var url = parseurl(req) 38 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 39 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 40 | assert.strictEqual(url.href, '/foo/bar#bazz') 41 | assert.strictEqual(url.pathname, '/foo/bar') 42 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 43 | assert.strictEqual(url.query, URL_EMPTY_VALUE) 44 | assert.strictEqual(url.search, URL_EMPTY_VALUE) 45 | }) 46 | 47 | it('should parse with query string and hash', function () { 48 | var req = createReq('/foo/bar?fizz=buzz#bazz') 49 | var url = parseurl(req) 50 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 51 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 52 | assert.strictEqual(url.href, '/foo/bar?fizz=buzz#bazz') 53 | assert.strictEqual(url.pathname, '/foo/bar') 54 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 55 | assert.strictEqual(url.query, 'fizz=buzz') 56 | assert.strictEqual(url.search, '?fizz=buzz') 57 | }) 58 | 59 | it('should parse a full URL', function () { 60 | var req = createReq('http://localhost:8888/foo/bar') 61 | var url = parseurl(req) 62 | assert.strictEqual(url.host, 'localhost:8888') 63 | assert.strictEqual(url.hostname, 'localhost') 64 | assert.strictEqual(url.href, 'http://localhost:8888/foo/bar') 65 | assert.strictEqual(url.pathname, '/foo/bar') 66 | assert.strictEqual(url.port, '8888') 67 | assert.strictEqual(url.query, URL_EMPTY_VALUE) 68 | assert.strictEqual(url.search, URL_EMPTY_VALUE) 69 | }) 70 | 71 | it('should not choke on auth-looking URL', function () { 72 | var req = createReq('//todo@txt') 73 | assert.strictEqual(parseurl(req).pathname, '//todo@txt') 74 | }) 75 | 76 | it('should return undefined missing url', function () { 77 | var req = createReq() 78 | var url = parseurl(req) 79 | assert.strictEqual(url, undefined) 80 | }) 81 | 82 | describe('when using the same request', function () { 83 | it('should parse multiple times', function () { 84 | var req = createReq('/foo/bar') 85 | assert.strictEqual(parseurl(req).pathname, '/foo/bar') 86 | assert.strictEqual(parseurl(req).pathname, '/foo/bar') 87 | assert.strictEqual(parseurl(req).pathname, '/foo/bar') 88 | }) 89 | 90 | it('should reflect url changes', function () { 91 | var req = createReq('/foo/bar') 92 | var url = parseurl(req) 93 | var val = Math.random() 94 | 95 | url._token = val 96 | assert.strictEqual(url._token, val) 97 | assert.strictEqual(url.pathname, '/foo/bar') 98 | 99 | req.url = '/bar/baz' 100 | url = parseurl(req) 101 | assert.strictEqual(url._token, undefined) 102 | assert.strictEqual(parseurl(req).pathname, '/bar/baz') 103 | }) 104 | 105 | it('should cache parsing', function () { 106 | var req = createReq('/foo/bar') 107 | var url = parseurl(req) 108 | var val = Math.random() 109 | 110 | url._token = val 111 | assert.strictEqual(url._token, val) 112 | assert.strictEqual(url.pathname, '/foo/bar') 113 | 114 | url = parseurl(req) 115 | assert.strictEqual(url._token, val) 116 | assert.strictEqual(url.pathname, '/foo/bar') 117 | }) 118 | 119 | it('should cache parsing where href does not match', function () { 120 | var req = createReq('/foo/bar ') 121 | var url = parseurl(req) 122 | var val = Math.random() 123 | 124 | url._token = val 125 | assert.strictEqual(url._token, val) 126 | assert.strictEqual(url.pathname, '/foo/bar') 127 | 128 | url = parseurl(req) 129 | assert.strictEqual(url._token, val) 130 | assert.strictEqual(url.pathname, '/foo/bar') 131 | }) 132 | }) 133 | }) 134 | 135 | describe('parseurl.original(req)', function () { 136 | it('should parse the request original URL', function () { 137 | var req = createReq('/foo/bar', '/foo/bar') 138 | var url = parseurl.original(req) 139 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 140 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 141 | assert.strictEqual(url.href, '/foo/bar') 142 | assert.strictEqual(url.pathname, '/foo/bar') 143 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 144 | assert.strictEqual(url.query, URL_EMPTY_VALUE) 145 | assert.strictEqual(url.search, URL_EMPTY_VALUE) 146 | }) 147 | 148 | it('should parse originalUrl when different', function () { 149 | var req = createReq('/bar', '/foo/bar') 150 | var url = parseurl.original(req) 151 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 152 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 153 | assert.strictEqual(url.href, '/foo/bar') 154 | assert.strictEqual(url.pathname, '/foo/bar') 155 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 156 | assert.strictEqual(url.query, URL_EMPTY_VALUE) 157 | assert.strictEqual(url.search, URL_EMPTY_VALUE) 158 | }) 159 | 160 | it('should parse req.url when originalUrl missing', function () { 161 | var req = createReq('/foo/bar') 162 | var url = parseurl.original(req) 163 | assert.strictEqual(url.host, URL_EMPTY_VALUE) 164 | assert.strictEqual(url.hostname, URL_EMPTY_VALUE) 165 | assert.strictEqual(url.href, '/foo/bar') 166 | assert.strictEqual(url.pathname, '/foo/bar') 167 | assert.strictEqual(url.port, URL_EMPTY_VALUE) 168 | assert.strictEqual(url.query, URL_EMPTY_VALUE) 169 | assert.strictEqual(url.search, URL_EMPTY_VALUE) 170 | }) 171 | 172 | it('should return undefined missing req.url and originalUrl', function () { 173 | var req = createReq() 174 | var url = parseurl.original(req) 175 | assert.strictEqual(url, undefined) 176 | }) 177 | 178 | describe('when using the same request', function () { 179 | it('should parse multiple times', function () { 180 | var req = createReq('/foo/bar', '/foo/bar') 181 | assert.strictEqual(parseurl.original(req).pathname, '/foo/bar') 182 | assert.strictEqual(parseurl.original(req).pathname, '/foo/bar') 183 | assert.strictEqual(parseurl.original(req).pathname, '/foo/bar') 184 | }) 185 | 186 | it('should reflect changes', function () { 187 | var req = createReq('/foo/bar', '/foo/bar') 188 | var url = parseurl.original(req) 189 | var val = Math.random() 190 | 191 | url._token = val 192 | assert.strictEqual(url._token, val) 193 | assert.strictEqual(url.pathname, '/foo/bar') 194 | 195 | req.originalUrl = '/bar/baz' 196 | url = parseurl.original(req) 197 | assert.strictEqual(url._token, undefined) 198 | assert.strictEqual(parseurl.original(req).pathname, '/bar/baz') 199 | }) 200 | 201 | it('should cache parsing', function () { 202 | var req = createReq('/foo/bar', '/foo/bar') 203 | var url = parseurl.original(req) 204 | var val = Math.random() 205 | 206 | url._token = val 207 | assert.strictEqual(url._token, val) 208 | assert.strictEqual(url.pathname, '/foo/bar') 209 | 210 | url = parseurl.original(req) 211 | assert.strictEqual(url._token, val) 212 | assert.strictEqual(url.pathname, '/foo/bar') 213 | }) 214 | 215 | it('should cache parsing if req.url changes', function () { 216 | var req = createReq('/foo/bar', '/foo/bar') 217 | var url = parseurl.original(req) 218 | var val = Math.random() 219 | 220 | url._token = val 221 | assert.strictEqual(url._token, val) 222 | assert.strictEqual(url.pathname, '/foo/bar') 223 | 224 | req.url = '/baz' 225 | url = parseurl.original(req) 226 | assert.strictEqual(url._token, val) 227 | assert.strictEqual(url.pathname, '/foo/bar') 228 | }) 229 | 230 | it('should cache parsing where href does not match', function () { 231 | var req = createReq('/foo/bar ', '/foo/bar ') 232 | var url = parseurl.original(req) 233 | var val = Math.random() 234 | 235 | url._token = val 236 | assert.strictEqual(url._token, val) 237 | assert.strictEqual(url.pathname, '/foo/bar') 238 | 239 | url = parseurl.original(req) 240 | assert.strictEqual(url._token, val) 241 | assert.strictEqual(url.pathname, '/foo/bar') 242 | }) 243 | }) 244 | }) 245 | 246 | function createReq (url, originalUrl) { 247 | return { 248 | originalUrl: originalUrl, 249 | url: url 250 | } 251 | } 252 | --------------------------------------------------------------------------------