diff --git a/src/index.js b/src/index.js index 564f012..9b61f37 100644 --- a/src/index.js +++ b/src/index.js @@ -138,7 +138,8 @@ const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) } return { target: ensureSlashStart(decodedPath), - statusCode: defaultType + statusCode: defaultType, + preserveSearch: true }; } @@ -162,7 +163,8 @@ const shouldRedirect = (decodedPath, {redirects = [], trailingSlash}, cleanUrl) if (target) { return { target: ensureSlashStart(target), - statusCode: defaultType + statusCode: defaultType, + preserveSearch: true }; } } @@ -551,6 +553,7 @@ module.exports = async (request, response, config = {}, methods = {}) => { const handlers = getHandlers(methods); let relativePath = null; + let search = ''; let acceptsJSON = null; if (request.headers.accept) { @@ -558,7 +561,9 @@ module.exports = async (request, response, config = {}, methods = {}) => { } try { - relativePath = decodeURIComponent(url.parse(request.url).pathname); + const parsedUrl = url.parse(request.url); + relativePath = decodeURIComponent(parsedUrl.pathname); + search = parsedUrl.search || ''; } catch (err) { return sendError('/', response, acceptsJSON, current, handlers, config, { statusCode: 400, @@ -583,8 +588,10 @@ module.exports = async (request, response, config = {}, methods = {}) => { const redirect = shouldRedirect(relativePath, config, cleanUrl); if (redirect) { + const location = redirect.preserveSearch ? `${redirect.target}${search}` : redirect.target; + response.writeHead(redirect.statusCode, { - Location: encodeURI(redirect.target) + Location: encodeURI(location) }); response.end(); diff --git a/test/integration.test.js b/test/integration.test.js index cebc479..95d9eeb 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -245,6 +245,22 @@ test('set `trailingSlash` config property to `true`', async () => { expect(location).toBe(`${target}/`); }); +test('set `trailingSlash` config property to `true` and preserve query string', async () => { + const url = await getUrl({ + trailingSlash: true + }); + + const target = `${url}/test?foo=bar`; + + const response = await fetch(target, { + redirect: 'manual', + follow: 0 + }); + + const location = response.headers.get('location'); + expect(location).toBe(`${url}/test/?foo=bar`); +}); + test('set `trailingSlash` config property to any boolean and remove multiple slashes', async () => { const url = await getUrl({ trailingSlash: true @@ -754,6 +770,22 @@ test('set `cleanUrls` config property to `true` and try with file', async () => expect(location).toBe(`${url}${target}`); }); +test('set `cleanUrls` config property to `true` and preserve query string', async () => { + const target = '/directory/clean-file'; + + const url = await getUrl({ + cleanUrls: true + }); + + const response = await fetch(`${url}${target}.html?foo=bar`, { + redirect: 'manual', + follow: 0 + }); + + const location = response.headers.get('location'); + expect(location).toBe(`${url}${target}?foo=bar`); +}); + test('set `cleanUrls` config property to `true` and not index file found', async () => { const contents = await getDirectoryContents(); const url = await getUrl({cleanUrls: true});