mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
Merge branch 'main' into versions/4.0.0
# Conflicts: # test/unit/util/errors/RedirectHttpError.test.ts
This commit is contained in:
commit
1b7cc1ea3a
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -18,5 +18,6 @@ Before this pull request can be merged, a core maintainer will check whether
|
||||
- semver.major: Breaking changes. This includes changing interfaces or configuration behaviour.
|
||||
* [ ] the correct branch is targeted. Patch updates can target main, other changes should target the latest versions/* branch.
|
||||
* [ ] the RELEASE_NOTES.md document in case of relevant feature or config changes.
|
||||
* [ ] any relevant documentation was updated to reflect the changes in this PR.
|
||||
|
||||
<!-- Try to check these to the best of your abilities before opening the PR -->
|
||||
|
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
||||
- '16.0'
|
||||
- '16.x'
|
||||
- '17.x'
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
@ -83,7 +83,7 @@ jobs:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
@ -104,7 +104,7 @@ jobs:
|
||||
- '12.x'
|
||||
- '14.x'
|
||||
- '16.x'
|
||||
timeout-minutes: 10
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
|
@ -63,7 +63,7 @@ or if you want to try a specific [branch](https://www.npmjs.com/) of the code,
|
||||
you can use:
|
||||
```shell
|
||||
git clone https://github.com/CommunitySolidServer/CommunitySolidServer.git
|
||||
cd community-server
|
||||
cd CommunitySolidServer
|
||||
npm ci
|
||||
npm start -- # add parameters if needed
|
||||
```
|
||||
@ -74,7 +74,7 @@ Docker allows you to run the server without having Node.js installed. Images are
|
||||
```shell
|
||||
# Clone the repo to get access to the configs
|
||||
git clone https://github.com/CommunitySolidServer/CommunitySolidServer.git
|
||||
cd community-server
|
||||
cd CommunitySolidServer
|
||||
# Run the image, serving your `~/Solid` directory on `http://localhost:3000`
|
||||
docker run --rm -v ~/Solid:/data -p 3000:3000 -it solidproject/community-server:latest
|
||||
# Or use one of the built-in configurations
|
||||
|
20
config/http/handler/handlers/redirect.json
Normal file
20
config/http/handler/handlers/redirect.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"@context": "https://linkedsoftwaredependencies.org/bundles/npm/@solid/community-server/^3.0.0/components/context.jsonld",
|
||||
"@graph": [
|
||||
{
|
||||
"comment": "Example handler to configure redirect patterns.",
|
||||
"@id": "urn:solid-server:default:RedirectHandler",
|
||||
"@type": "RedirectingHttpHandler",
|
||||
"redirects": [
|
||||
{
|
||||
"RedirectingHttpHandler:_redirects_key": "/from/(.*)",
|
||||
"RedirectingHttpHandler:_redirects_value": "/to/$1"
|
||||
}
|
||||
],
|
||||
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
|
||||
"targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
|
||||
"responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" },
|
||||
"statusCode": "303"
|
||||
}
|
||||
]
|
||||
}
|
206
package-lock.json
generated
206
package-lock.json
generated
@ -87,9 +87,11 @@
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-config-es": "4.1.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^26.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.14",
|
||||
"eslint-plugin-unicorn": "^37.0.1",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"husky": "^4.3.8",
|
||||
"jest": "^27.4.7",
|
||||
@ -172,7 +174,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.5.tgz",
|
||||
"integrity": "sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-visitor-keys": "^2.1.0",
|
||||
@ -191,7 +192,6 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
@ -5064,13 +5064,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.2.tgz",
|
||||
"integrity": "sha512-39Tm6f4RoZoVUWBYr3ekS75TYgpr5Y+X0xLZxXqcZNDWZdJdYbKd3q2IR4V9y5NxxiPu/jxJ8XP7EgHiEQtFnw==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz",
|
||||
"integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/visitor-keys": "5.10.2"
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"@typescript-eslint/visitor-keys": "5.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -5081,9 +5081,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/types": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.2.tgz",
|
||||
"integrity": "sha512-Qfp0qk/5j2Rz3p3/WhWgu4S1JtMcPgFLnmAKAW061uXxKSa7VWKZsDXVaMXh2N60CX9h6YLaBoy9PJAfCOjk3w==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz",
|
||||
"integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -5094,12 +5094,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.2.tgz",
|
||||
"integrity": "sha512-zHIhYGGGrFJvvyfwHk5M08C5B5K4bewkm+rrvNTKk1/S15YHR+SA/QUF8ZWscXSfEaB8Nn2puZj+iHcoxVOD/Q==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz",
|
||||
"integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -5111,9 +5111,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager/node_modules/eslint-visitor-keys": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz",
|
||||
"integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
|
||||
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -5160,15 +5160,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.2.tgz",
|
||||
"integrity": "sha512-vuJaBeig1NnBRkf7q9tgMLREiYD7zsMrsN1DA3wcoMDvr3BTFiIpKjGiYZoKPllfEwN7spUjv7ZqD+JhbVjEPg==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.18.0.tgz",
|
||||
"integrity": "sha512-+hFGWUMMri7OFY26TsOlGa+zgjEy1ssEipxpLjtl4wSll8zy85x0GrUSju/FHdKfVorZPYJLkF3I4XPtnCTewA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@typescript-eslint/scope-manager": "5.10.2",
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/typescript-estree": "5.10.2",
|
||||
"@typescript-eslint/scope-manager": "5.18.0",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"@typescript-eslint/typescript-estree": "5.18.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0"
|
||||
},
|
||||
@ -5184,9 +5184,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.2.tgz",
|
||||
"integrity": "sha512-Qfp0qk/5j2Rz3p3/WhWgu4S1JtMcPgFLnmAKAW061uXxKSa7VWKZsDXVaMXh2N60CX9h6YLaBoy9PJAfCOjk3w==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz",
|
||||
"integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -5197,13 +5197,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.2.tgz",
|
||||
"integrity": "sha512-WHHw6a9vvZls6JkTgGljwCsMkv8wu8XU8WaYKeYhxhWXH/atZeiMW6uDFPLZOvzNOGmuSMvHtZKd6AuC8PrwKQ==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz",
|
||||
"integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/visitor-keys": "5.10.2",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"@typescript-eslint/visitor-keys": "5.18.0",
|
||||
"debug": "^4.3.2",
|
||||
"globby": "^11.0.4",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -5224,12 +5224,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.2.tgz",
|
||||
"integrity": "sha512-zHIhYGGGrFJvvyfwHk5M08C5B5K4bewkm+rrvNTKk1/S15YHR+SA/QUF8ZWscXSfEaB8Nn2puZj+iHcoxVOD/Q==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz",
|
||||
"integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@ -5241,9 +5241,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz",
|
||||
"integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
|
||||
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@ -5895,7 +5895,6 @@
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
@ -6194,7 +6193,6 @@
|
||||
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
|
||||
"integrity": "sha1-jffHquUf02h06PjQW5GAvBGj/tc=",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
},
|
||||
@ -6207,7 +6205,6 @@
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
@ -7495,7 +7492,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz",
|
||||
"integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"ignore": "^5.0.5"
|
||||
@ -7515,7 +7511,6 @@
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
@ -7592,15 +7587,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-jest": {
|
||||
"version": "26.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.0.0.tgz",
|
||||
"integrity": "sha512-Fvs0YgJ/nw9FTrnqTuMGVrkozkd07jkQzWm0ajqyHlfcsdkxGfAuv30fgfWHOnHiCr9+1YQ365CcDX7vrNhqQg==",
|
||||
"version": "26.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.3.tgz",
|
||||
"integrity": "sha512-Pju+T7MFpo5VFhFlwrkK/9jRUu18r2iugvgyrWOnnGRaVTFFmFXp+xFJpHyqmjjLmGJPKLeEFLVTAxezkApcpQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/utils": "^5.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
@ -7723,7 +7718,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-37.0.1.tgz",
|
||||
"integrity": "sha512-E1jq5u9ojnadisJcPi+hMXTGSiIzkIUMDvWsBudsCGXvKUB2aNSU2TcfyW2/jAS5A4ryBXfzxLykMxX1EdluSQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.14.9",
|
||||
"ci-info": "^3.2.0",
|
||||
@ -7799,7 +7793,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-template-visitor/-/eslint-template-visitor-2.3.2.tgz",
|
||||
"integrity": "sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
@ -9586,7 +9579,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.1.0.tgz",
|
||||
"integrity": "sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"builtin-modules": "^3.0.0"
|
||||
},
|
||||
@ -11631,8 +11623,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz",
|
||||
"integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/n3": {
|
||||
"version": "1.16.0",
|
||||
@ -12636,7 +12627,6 @@
|
||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
|
||||
"integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
@ -13225,7 +13215,6 @@
|
||||
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz",
|
||||
"integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"regexp-tree": "bin/regexp-tree"
|
||||
}
|
||||
@ -13433,7 +13422,6 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"regexp-tree": "~0.1.1"
|
||||
}
|
||||
@ -15163,7 +15151,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.16.5.tgz",
|
||||
"integrity": "sha512-mUqYa46lgWqHKQ33Q6LNCGp/wPR3eqOYTUixHFsfrSQqRxH0+WOzca75iEjFr5RDGH1dDz622LaHhLOzOuQRUA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-visitor-keys": "^2.1.0",
|
||||
@ -15174,8 +15161,7 @@
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -19123,35 +19109,35 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.10.2.tgz",
|
||||
"integrity": "sha512-39Tm6f4RoZoVUWBYr3ekS75TYgpr5Y+X0xLZxXqcZNDWZdJdYbKd3q2IR4V9y5NxxiPu/jxJ8XP7EgHiEQtFnw==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.18.0.tgz",
|
||||
"integrity": "sha512-C0CZML6NyRDj+ZbMqh9FnPscg2PrzSaVQg3IpTmpe0NURMVBXlghGZgMYqBw07YW73i0MCqSDqv2SbywnCS8jQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/visitor-keys": "5.10.2"
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"@typescript-eslint/visitor-keys": "5.18.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.2.tgz",
|
||||
"integrity": "sha512-Qfp0qk/5j2Rz3p3/WhWgu4S1JtMcPgFLnmAKAW061uXxKSa7VWKZsDXVaMXh2N60CX9h6YLaBoy9PJAfCOjk3w==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz",
|
||||
"integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.2.tgz",
|
||||
"integrity": "sha512-zHIhYGGGrFJvvyfwHk5M08C5B5K4bewkm+rrvNTKk1/S15YHR+SA/QUF8ZWscXSfEaB8Nn2puZj+iHcoxVOD/Q==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz",
|
||||
"integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-visitor-keys": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz",
|
||||
"integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
|
||||
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -19178,33 +19164,33 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.10.2.tgz",
|
||||
"integrity": "sha512-vuJaBeig1NnBRkf7q9tgMLREiYD7zsMrsN1DA3wcoMDvr3BTFiIpKjGiYZoKPllfEwN7spUjv7ZqD+JhbVjEPg==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.18.0.tgz",
|
||||
"integrity": "sha512-+hFGWUMMri7OFY26TsOlGa+zgjEy1ssEipxpLjtl4wSll8zy85x0GrUSju/FHdKfVorZPYJLkF3I4XPtnCTewA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@typescript-eslint/scope-manager": "5.10.2",
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/typescript-estree": "5.10.2",
|
||||
"@typescript-eslint/scope-manager": "5.18.0",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"@typescript-eslint/typescript-estree": "5.18.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.10.2.tgz",
|
||||
"integrity": "sha512-Qfp0qk/5j2Rz3p3/WhWgu4S1JtMcPgFLnmAKAW061uXxKSa7VWKZsDXVaMXh2N60CX9h6YLaBoy9PJAfCOjk3w==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.18.0.tgz",
|
||||
"integrity": "sha512-bhV1+XjM+9bHMTmXi46p1Led5NP6iqQcsOxgx7fvk6gGiV48c6IynY0apQb7693twJDsXiVzNXTflhplmaiJaw==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.2.tgz",
|
||||
"integrity": "sha512-WHHw6a9vvZls6JkTgGljwCsMkv8wu8XU8WaYKeYhxhWXH/atZeiMW6uDFPLZOvzNOGmuSMvHtZKd6AuC8PrwKQ==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.18.0.tgz",
|
||||
"integrity": "sha512-wa+2VAhOPpZs1bVij9e5gyVu60ReMi/KuOx4LKjGx2Y3XTNUDJgQ+5f77D49pHtqef/klglf+mibuHs9TrPxdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/visitor-keys": "5.10.2",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"@typescript-eslint/visitor-keys": "5.18.0",
|
||||
"debug": "^4.3.2",
|
||||
"globby": "^11.0.4",
|
||||
"is-glob": "^4.0.3",
|
||||
@ -19213,19 +19199,19 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.2.tgz",
|
||||
"integrity": "sha512-zHIhYGGGrFJvvyfwHk5M08C5B5K4bewkm+rrvNTKk1/S15YHR+SA/QUF8ZWscXSfEaB8Nn2puZj+iHcoxVOD/Q==",
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.18.0.tgz",
|
||||
"integrity": "sha512-Hf+t+dJsjAKpKSkg3EHvbtEpFFb/1CiOHnvI8bjHgOD4/wAw3gKrA0i94LrbekypiZVanJu3McWJg7rWDMzRTg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.10.2",
|
||||
"@typescript-eslint/types": "5.18.0",
|
||||
"eslint-visitor-keys": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-visitor-keys": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz",
|
||||
"integrity": "sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ==",
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz",
|
||||
"integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@ -19732,8 +19718,7 @@
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
|
||||
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.0",
|
||||
@ -19951,7 +19936,6 @@
|
||||
"resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
|
||||
"integrity": "sha1-jffHquUf02h06PjQW5GAvBGj/tc=",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
},
|
||||
@ -19960,8 +19944,7 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -21022,7 +21005,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz",
|
||||
"integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"ignore": "^5.0.5"
|
||||
@ -21032,8 +21014,7 @@
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -21099,9 +21080,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-jest": {
|
||||
"version": "26.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.0.0.tgz",
|
||||
"integrity": "sha512-Fvs0YgJ/nw9FTrnqTuMGVrkozkd07jkQzWm0ajqyHlfcsdkxGfAuv30fgfWHOnHiCr9+1YQ365CcDX7vrNhqQg==",
|
||||
"version": "26.1.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-26.1.3.tgz",
|
||||
"integrity": "sha512-Pju+T7MFpo5VFhFlwrkK/9jRUu18r2iugvgyrWOnnGRaVTFFmFXp+xFJpHyqmjjLmGJPKLeEFLVTAxezkApcpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/utils": "^5.10.0"
|
||||
@ -21193,7 +21174,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-37.0.1.tgz",
|
||||
"integrity": "sha512-E1jq5u9ojnadisJcPi+hMXTGSiIzkIUMDvWsBudsCGXvKUB2aNSU2TcfyW2/jAS5A4ryBXfzxLykMxX1EdluSQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.14.9",
|
||||
"ci-info": "^3.2.0",
|
||||
@ -21242,7 +21222,6 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-template-visitor/-/eslint-template-visitor-2.3.2.tgz",
|
||||
"integrity": "sha512-3ydhqFpuV7x1M9EK52BPNj6V0Kwu0KKkcIAfpUhwHbR8ocRln/oUHgfxQupY8O1h4Qv/POHDumb/BwwNfxbtnA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
@ -22520,7 +22499,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.1.0.tgz",
|
||||
"integrity": "sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"builtin-modules": "^3.0.0"
|
||||
}
|
||||
@ -24109,8 +24087,7 @@
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/multimap/-/multimap-1.1.0.tgz",
|
||||
"integrity": "sha512-0ZIR9PasPxGXmRsEF8jsDzndzHDj7tIav+JUmvIFB/WHswliFnquxECT/De7GR4yg99ky/NlRKJT82G1y271bw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
},
|
||||
"n3": {
|
||||
"version": "1.16.0",
|
||||
@ -24841,8 +24818,7 @@
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
|
||||
"integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
@ -25335,8 +25311,7 @@
|
||||
"version": "0.1.24",
|
||||
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz",
|
||||
"integrity": "sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
"dev": true
|
||||
},
|
||||
"regexp.prototype.flags": {
|
||||
"version": "1.3.1",
|
||||
@ -25469,7 +25444,6 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz",
|
||||
"integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"requires": {
|
||||
"regexp-tree": "~0.1.1"
|
||||
}
|
||||
|
@ -150,9 +150,11 @@
|
||||
"eslint": "^8.8.0",
|
||||
"eslint-config-es": "4.1.0",
|
||||
"eslint-import-resolver-typescript": "^2.5.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^26.0.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.14",
|
||||
"eslint-plugin-unicorn": "^37.0.1",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"husky": "^4.3.8",
|
||||
"jest": "^27.4.7",
|
||||
|
@ -260,6 +260,7 @@ export * from './server/HttpResponse';
|
||||
export * from './server/HttpServerFactory';
|
||||
export * from './server/OperationHttpHandler';
|
||||
export * from './server/ParsingHttpHandler';
|
||||
export * from './server/RedirectingHttpHandler';
|
||||
export * from './server/WebSocketHandler';
|
||||
export * from './server/WebSocketServerFactory';
|
||||
|
||||
|
109
src/server/RedirectingHttpHandler.ts
Normal file
109
src/server/RedirectingHttpHandler.ts
Normal file
@ -0,0 +1,109 @@
|
||||
import type { TargetExtractor } from '../http/input/identifier/TargetExtractor';
|
||||
import { RedirectResponseDescription } from '../http/output/response/RedirectResponseDescription';
|
||||
import type { ResponseWriter } from '../http/output/ResponseWriter';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { FoundHttpError } from '../util/errors/FoundHttpError';
|
||||
import { MovedPermanentlyHttpError } from '../util/errors/MovedPermanentlyHttpError';
|
||||
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
|
||||
import { PermanentRedirectHttpError } from '../util/errors/PermanentRedirectHttpError';
|
||||
import type { RedirectHttpError } from '../util/errors/RedirectHttpError';
|
||||
import { SeeOtherHttpError } from '../util/errors/SeeOtherHttpError';
|
||||
import { TemporaryRedirectHttpError } from '../util/errors/TemporaryRedirectHttpError';
|
||||
import { getRelativeUrl, joinUrl } from '../util/PathUtil';
|
||||
import type { HttpHandlerInput } from './HttpHandler';
|
||||
import { HttpHandler } from './HttpHandler';
|
||||
import type { HttpRequest } from './HttpRequest';
|
||||
|
||||
const redirectErrorFactories: Record<301 | 302 | 303 | 307 | 308, (location: string) => RedirectHttpError> = {
|
||||
301: (location: string): RedirectHttpError => new MovedPermanentlyHttpError(location),
|
||||
302: (location: string): RedirectHttpError => new FoundHttpError(location),
|
||||
303: (location: string): RedirectHttpError => new SeeOtherHttpError(location),
|
||||
307: (location: string): RedirectHttpError => new TemporaryRedirectHttpError(location),
|
||||
308: (location: string): RedirectHttpError => new PermanentRedirectHttpError(location),
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler that redirects paths matching given patterns
|
||||
* to their corresponding URL, substituting selected groups.
|
||||
*/
|
||||
export class RedirectingHttpHandler extends HttpHandler {
|
||||
private readonly logger = getLoggerFor(this);
|
||||
private readonly redirects: {
|
||||
regex: RegExp;
|
||||
redirectPattern: string;
|
||||
}[];
|
||||
|
||||
/**
|
||||
* Creates a handler for the provided redirects.
|
||||
* @param redirects - A mapping between URL patterns.
|
||||
* @param targetExtractor - To extract the target from the request.
|
||||
* @param responseWriter - To write the redirect to the response.
|
||||
* @param statusCode - Desired 30x redirection code (defaults to 308).
|
||||
*/
|
||||
public constructor(
|
||||
redirects: Record<string, string>,
|
||||
private readonly baseUrl: string,
|
||||
private readonly targetExtractor: TargetExtractor,
|
||||
private readonly responseWriter: ResponseWriter,
|
||||
private readonly statusCode: 301 | 302 | 303 | 307 | 308 = 308,
|
||||
) {
|
||||
super();
|
||||
|
||||
// Create an array of (regexp, redirect) pairs
|
||||
this.redirects = Object.keys(redirects).map(
|
||||
(pattern): { regex: RegExp; redirectPattern: string } => ({
|
||||
regex: new RegExp(pattern, 'u'),
|
||||
redirectPattern: redirects[pattern],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
public async canHandle({ request }: HttpHandlerInput): Promise<void> {
|
||||
// Try to find redirect for target URL
|
||||
await this.findRedirect(request);
|
||||
}
|
||||
|
||||
public async handle({ request, response }: HttpHandlerInput): Promise<void> {
|
||||
// Try to find redirect for target URL
|
||||
const redirect = await this.findRedirect(request);
|
||||
|
||||
// Send redirect response
|
||||
this.logger.info(`Redirecting ${request.url} to ${redirect}`);
|
||||
const result = new RedirectResponseDescription(redirectErrorFactories[this.statusCode](redirect));
|
||||
await this.responseWriter.handleSafe({ response, result });
|
||||
}
|
||||
|
||||
private async findRedirect(request: HttpRequest): Promise<string> {
|
||||
// Retrieve target relative to base URL
|
||||
const target = await getRelativeUrl(this.baseUrl, request, this.targetExtractor);
|
||||
|
||||
// Get groups and redirect of first matching pattern
|
||||
let result;
|
||||
for (const { regex, redirectPattern } of this.redirects) {
|
||||
const match = regex.exec(target);
|
||||
if (match) {
|
||||
result = { match, redirectPattern };
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only return if a redirect is configured for the requested URL
|
||||
if (!result) {
|
||||
throw new NotImplementedHttpError(`No redirect configured for ${target}`);
|
||||
}
|
||||
|
||||
// Build redirect URL from regexp result
|
||||
const { match, redirectPattern } = result;
|
||||
const redirect = match.reduce(
|
||||
(prev, param, index): string => prev.replace(`$${index}`, param),
|
||||
redirectPattern,
|
||||
);
|
||||
|
||||
// Don't redirect if target is already correct
|
||||
if (redirect === target) {
|
||||
throw new NotImplementedHttpError('Target is already correct.');
|
||||
}
|
||||
|
||||
return /^(?:[a-z]+:)?\/\//ui.test(redirect) ? redirect : joinUrl(this.baseUrl, redirect);
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ const BaseHttpError = generateRedirectHttpErrorClass(302, 'FoundHttpError');
|
||||
|
||||
/**
|
||||
* Error used for resources that have been moved temporarily.
|
||||
* Methods other than GET may or may not be changed to GET in subsequent requests.
|
||||
*/
|
||||
export class FoundHttpError extends BaseHttpError {
|
||||
public constructor(location: string, message?: string, options?: HttpErrorOptions) {
|
||||
|
@ -6,6 +6,7 @@ const BaseHttpError = generateRedirectHttpErrorClass(301, 'MovedPermanentlyHttpE
|
||||
|
||||
/**
|
||||
* Error used for resources that have been moved permanently.
|
||||
* Methods other than GET may or may not be changed to GET in subsequent requests.
|
||||
*/
|
||||
export class MovedPermanentlyHttpError extends BaseHttpError {
|
||||
public constructor(location: string, message?: string, options?: HttpErrorOptions) {
|
||||
|
15
src/util/errors/PermanentRedirectHttpError.ts
Normal file
15
src/util/errors/PermanentRedirectHttpError.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { HttpErrorOptions } from './HttpError';
|
||||
import { generateRedirectHttpErrorClass } from './RedirectHttpError';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const BaseHttpError = generateRedirectHttpErrorClass(308, 'PermanentRedirectHttpError');
|
||||
|
||||
/**
|
||||
* Error used for resources that have been moved permanently.
|
||||
* Method and body should not be changed in subsequent requests.
|
||||
*/
|
||||
export class PermanentRedirectHttpError extends BaseHttpError {
|
||||
public constructor(location: string, message?: string, options?: HttpErrorOptions) {
|
||||
super(location, message, options);
|
||||
}
|
||||
}
|
16
src/util/errors/SeeOtherHttpError.ts
Normal file
16
src/util/errors/SeeOtherHttpError.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import type { HttpErrorOptions } from './HttpError';
|
||||
import { generateRedirectHttpErrorClass } from './RedirectHttpError';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const BaseHttpError = generateRedirectHttpErrorClass(303, 'SeeOtherHttpError');
|
||||
|
||||
/**
|
||||
* Error used to redirect not to the requested resource itself, but to another page,
|
||||
* for example a representation of a real-world object.
|
||||
* The method used to display this redirected page is always GET.
|
||||
*/
|
||||
export class SeeOtherHttpError extends BaseHttpError {
|
||||
public constructor(location: string, message?: string, options?: HttpErrorOptions) {
|
||||
super(location, message, options);
|
||||
}
|
||||
}
|
15
src/util/errors/TemporaryRedirectHttpError.ts
Normal file
15
src/util/errors/TemporaryRedirectHttpError.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { HttpErrorOptions } from './HttpError';
|
||||
import { generateRedirectHttpErrorClass } from './RedirectHttpError';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const BaseHttpError = generateRedirectHttpErrorClass(307, 'TemporaryRedirectHttpError');
|
||||
|
||||
/**
|
||||
* Error used for resources that have been moved temporarily.
|
||||
* Method and body should not be changed in subsequent requests.
|
||||
*/
|
||||
export class TemporaryRedirectHttpError extends BaseHttpError {
|
||||
public constructor(location: string, message?: string, options?: HttpErrorOptions) {
|
||||
super(location, message, options);
|
||||
}
|
||||
}
|
97
test/unit/server/RedirectingHttpHandler.test.ts
Normal file
97
test/unit/server/RedirectingHttpHandler.test.ts
Normal file
@ -0,0 +1,97 @@
|
||||
import type { TargetExtractor } from '../../../src/http/input/identifier/TargetExtractor';
|
||||
import type { ResponseWriter } from '../../../src/http/output/ResponseWriter';
|
||||
import type { ResourceIdentifier } from '../../../src/http/representation/ResourceIdentifier';
|
||||
import type { HttpRequest } from '../../../src/server/HttpRequest';
|
||||
import type { HttpResponse } from '../../../src/server/HttpResponse';
|
||||
import { RedirectingHttpHandler } from '../../../src/server/RedirectingHttpHandler';
|
||||
import { joinUrl } from '../../../src/util/PathUtil';
|
||||
import { SOLID_HTTP } from '../../../src/util/Vocabularies';
|
||||
|
||||
describe('A RedirectingHttpHandler', (): void => {
|
||||
const baseUrl = 'http://test.com/';
|
||||
const request = { method: 'GET' } as HttpRequest;
|
||||
const response = {} as HttpResponse;
|
||||
let targetExtractor: jest.Mocked<TargetExtractor>;
|
||||
let responseWriter: jest.Mocked<ResponseWriter>;
|
||||
let handler: RedirectingHttpHandler;
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
targetExtractor = {
|
||||
handleSafe: jest.fn(({ request: req }): ResourceIdentifier => ({ path: joinUrl(baseUrl, req.url!) })),
|
||||
} as any;
|
||||
|
||||
responseWriter = { handleSafe: jest.fn() } as any;
|
||||
|
||||
handler = new RedirectingHttpHandler({
|
||||
'/one': '/two',
|
||||
'/from/(.*)': 'http://to/t$1',
|
||||
'/f([aeiou]+)/b([aeiou]+)r': '/f$2/b$1r',
|
||||
'/s(.)me': '/s$1me',
|
||||
}, baseUrl, targetExtractor, responseWriter);
|
||||
});
|
||||
|
||||
afterEach(jest.clearAllMocks);
|
||||
|
||||
it('does not handle requests without URL.', async(): Promise<void> => {
|
||||
await expect(handler.canHandle({ request, response }))
|
||||
.rejects.toThrow('Url must be a string. Received undefined');
|
||||
await expect(handler.handle({ request, response }))
|
||||
.rejects.toThrow('Url must be a string. Received undefined');
|
||||
});
|
||||
|
||||
it('does not handle requests with unconfigured URLs.', async(): Promise<void> => {
|
||||
request.url = '/other';
|
||||
await expect(handler.canHandle({ request, response }))
|
||||
.rejects.toThrow('No redirect configured for /other');
|
||||
await expect(handler.handle({ request, response }))
|
||||
.rejects.toThrow('No redirect configured for /other');
|
||||
});
|
||||
|
||||
it('does not handle requests redirecting to their own target URL.', async(): Promise<void> => {
|
||||
request.url = '/same';
|
||||
await expect(handler.canHandle({ request, response }))
|
||||
.rejects.toThrow('Target is already correct.');
|
||||
await expect(handler.handle({ request, response }))
|
||||
.rejects.toThrow('Target is already correct.');
|
||||
});
|
||||
|
||||
it('handles requests to a known URL.', async(): Promise<void> => {
|
||||
request.url = '/one';
|
||||
|
||||
await expect(handler.handle({ request, response })).resolves.toBeUndefined();
|
||||
expect(responseWriter.handleSafe).toHaveBeenCalledTimes(1);
|
||||
expect(responseWriter.handleSafe).toHaveBeenLastCalledWith({
|
||||
response,
|
||||
result: expect.objectContaining({ statusCode: 308 }),
|
||||
});
|
||||
const { metadata } = responseWriter.handleSafe.mock.calls[0][0].result;
|
||||
expect(metadata?.get(SOLID_HTTP.terms.location)?.value).toBe(joinUrl(baseUrl, '/two'));
|
||||
});
|
||||
|
||||
it('handles correctly substitutes group patterns.', async(): Promise<void> => {
|
||||
request.url = '/fa/boor';
|
||||
|
||||
await handler.handle({ request, response });
|
||||
const { metadata } = responseWriter.handleSafe.mock.calls[0][0].result;
|
||||
expect(metadata?.get(SOLID_HTTP.terms.location)?.value).toBe(joinUrl(baseUrl, '/foo/bar'));
|
||||
});
|
||||
|
||||
it('redirects to an absolute url if provided.', async(): Promise<void> => {
|
||||
request.url = '/from/here';
|
||||
|
||||
await handler.handle({ request, response });
|
||||
const { metadata } = responseWriter.handleSafe.mock.calls[0][0].result;
|
||||
expect(metadata?.get(SOLID_HTTP.terms.location)?.value).toBe('http://to/there');
|
||||
});
|
||||
|
||||
it.each([ 301, 302, 303, 307, 308 ])('redirects with the provided status code: %i.', async(code): Promise<void> => {
|
||||
request.url = '/one';
|
||||
(handler as any).statusCode = code;
|
||||
|
||||
await handler.handle({ request, response });
|
||||
expect(responseWriter.handleSafe).toHaveBeenLastCalledWith({
|
||||
response,
|
||||
result: expect.objectContaining({ statusCode: code }),
|
||||
});
|
||||
});
|
||||
});
|
@ -2,8 +2,11 @@ import { FoundHttpError } from '../../../../src/util/errors/FoundHttpError';
|
||||
import type { HttpErrorOptions } from '../../../../src/util/errors/HttpError';
|
||||
import { generateHttpErrorUri } from '../../../../src/util/errors/HttpError';
|
||||
import { MovedPermanentlyHttpError } from '../../../../src/util/errors/MovedPermanentlyHttpError';
|
||||
import { PermanentRedirectHttpError } from '../../../../src/util/errors/PermanentRedirectHttpError';
|
||||
import { RedirectHttpError } from '../../../../src/util/errors/RedirectHttpError';
|
||||
import type { RedirectHttpErrorClass } from '../../../../src/util/errors/RedirectHttpError';
|
||||
import { SeeOtherHttpError } from '../../../../src/util/errors/SeeOtherHttpError';
|
||||
import { TemporaryRedirectHttpError } from '../../../../src/util/errors/TemporaryRedirectHttpError';
|
||||
|
||||
// Used to make sure the RedirectHttpError constructor also gets called in a test.
|
||||
class FixedRedirectHttpError extends RedirectHttpError {
|
||||
@ -20,6 +23,9 @@ describe('RedirectHttpError', (): void => {
|
||||
[ 'RedirectHttpError', 0, FixedRedirectHttpError ],
|
||||
[ 'MovedPermanentlyHttpError', 301, MovedPermanentlyHttpError ],
|
||||
[ 'FoundHttpError', 302, FoundHttpError ],
|
||||
[ 'SeeOtherHttpError', 303, SeeOtherHttpError ],
|
||||
[ 'TemporaryRedirectHttpError', 307, TemporaryRedirectHttpError ],
|
||||
[ 'PermanentRedirectHttpError', 308, PermanentRedirectHttpError ],
|
||||
];
|
||||
|
||||
describe.each(errors)('%s', (name, statusCode, constructor): void => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user