From b9d1c1a810f2029f549935472c3885b962e5f9f2 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Thu, 17 Oct 2019 10:15:21 +0200 Subject: [PATCH 1/4] feat: add decodeFullUrl option Changes onDecode signature to (req, options) instead of (req, res, next), because options are useful when decoding the url but res and next are never used (and I couldn't find a usecase). --- README.md | 57 +++++++++++++++++++++++++++++++++++++++++++---- lib/middleware.js | 2 +- lib/module.js | 19 +++++++++++++++- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1884bb3..0f5055a 100755 --- a/README.md +++ b/README.md @@ -28,6 +28,9 @@ loss or incorrect handling. But this time is over! modules: [ ['@nuxtjs/redirect-module', { // Redirect option here + rules: [ + // redirect rules here + ] }] ] } @@ -40,9 +43,12 @@ loss or incorrect handling. But this time is over! modules: [ '@nuxtjs/redirect-module' ], - redirect: [ + redirect: { // Redirect options here - ] + rules: [ + // redirect rules here + ] + } } ``` @@ -54,11 +60,39 @@ loss or incorrect handling. But this time is over! Rules of your redirects. +### `decodeFullUrl` + +- Default: `false` + +If enabled, protocol and host will also be considered for redirecting rules so you can have non-www to www or http to https redirects for example. Note that you likely have to adopt your rules when enabling this, e.g. `{ from: '^/myoldurl', to: '/mynewurl' }` will no longer work because the `from` regex no longer matches when proto and host are prepended. + ### `onDecode` -- Default: `(req, res, next) => decodeURI(req.url)` +Default: + +```js +// This will trust proxy headers. You can change this behaviour +// by providing your own onDecode function. +(req, options) => { + if (options.decodeFullUrl) { + return decodeURI(req.url) + } else { + let proto = req.headers['x-forwarded-proto'] + if (proto === undefined) { + proto = req.connection.encrypted ? 'https' : 'http' + } + + let host = req.headers['x-forwarded-host'] + if (host === undefined) { + host = req.httpVersionMajor >= 2 ? req.headers[':authority'] : req.headers.host + } + + return `${proto}://${host}${decodeURI(req.url)}` + } +} +``` -You can set decode. +Allows you to change how the url is decoded. ### `onDecodeError` @@ -126,6 +160,21 @@ redirect: async () => { } ``` +For redirecting http to https: + +```js +redirect: { + decodeFullUrl: true, + rules: [ + { + from: 'http:\/\/example\.com\/(.*)$', + to: 'https://example.com/$1', + statusCode: 301, + } + ] +} +``` + Now, if you want to customize your redirects, how your decode is done or when there is some error in the decode, you can also: diff --git a/lib/middleware.js b/lib/middleware.js index 3eeeb2a..13819fb 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -4,7 +4,7 @@ module.exports = function (options) { let decodedBaseUrl try { - decodedBaseUrl = options.onDecode(req, res, next) + decodedBaseUrl = options.onDecode(req, options) } catch (error) { return options.onDecodeError(error, req, res, next) } diff --git a/lib/module.js b/lib/module.js index c89d150..3fc9ee0 100644 --- a/lib/module.js +++ b/lib/module.js @@ -1,7 +1,24 @@ async function redirectModule (moduleOptions) { const defaults = { rules: [], - onDecode: (req, res, next) => decodeURI(req.url), + decodeFullUrl: false, + onDecode: (req, options) => { + if (options.decodeFullUrl) { + return decodeURI(req.url) + } else { + let proto = req.headers['x-forwarded-proto'] + if (proto === undefined) { + proto = req.connection.encrypted ? 'https' : 'http' + } + + let host = req.headers['x-forwarded-host'] + if (host === undefined) { + host = req.httpVersionMajor >= 2 ? req.headers[':authority'] : req.headers.host + } + + return `${proto}://${host}${decodeURI(req.url)}` + } + }, onDecodeError: (error, req, res, next) => next(error), statusCode: 302 } From b0e4dd1b59a6575ad117ae4c45f12204612ff3c9 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Thu, 17 Oct 2019 10:24:53 +0200 Subject: [PATCH 2/4] fix: onDecode returning full url when decodeFullUrl is false --- lib/module.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/module.js b/lib/module.js index 3fc9ee0..0445179 100644 --- a/lib/module.js +++ b/lib/module.js @@ -3,7 +3,7 @@ async function redirectModule (moduleOptions) { rules: [], decodeFullUrl: false, onDecode: (req, options) => { - if (options.decodeFullUrl) { + if (!options.decodeFullUrl) { return decodeURI(req.url) } else { let proto = req.headers['x-forwarded-proto'] From 723ffd9bc228990500df8167d62f7f5790d24ada Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Tue, 6 Oct 2020 14:37:12 +0200 Subject: [PATCH 3/4] test: add decodeFullUrl test --- test/module.test.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/module.test.js b/test/module.test.js index 46722e2..70f4cc8 100644 --- a/test/module.test.js +++ b/test/module.test.js @@ -212,3 +212,35 @@ describe('error', () => { }) }) }) + +describe('decodeFullUrl', () => { + beforeAll(async () => { + nuxt = await setupNuxt({ + ...config, + redirect: { + rules: [{ + from: `http://localhost:([0-9]+)/(.*)$`, + to: `https://localhost:$1/$2`, + statusCode: 301 + }], + decodeFullUrl: true + } + }) + }) + + afterAll(async () => { + await nuxt.close() + }) + + test('301 Moved Permanently', async () => { + try { + await request({ + uri: url('/'), + resolveWithFullResponse: true, + followRedirect: false + }) + } catch (e) { + expect(e.statusCode).toBe(301) + } + }) +}) From 1e1f121f67a2a318eba6c92781d7044ee29a3882 Mon Sep 17 00:00:00 2001 From: Pascal Sthamer Date: Tue, 6 Oct 2020 14:37:31 +0200 Subject: [PATCH 4/4] chore: update README http rewrite example to not contain useless escapes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f5055a..7ee0b89 100755 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ redirect: { decodeFullUrl: true, rules: [ { - from: 'http:\/\/example\.com\/(.*)$', + from: 'http://example.com/(.*)$', to: 'https://example.com/$1', statusCode: 301, }