diff --git a/src/http/input/identifier/OriginalUrlExtractor.ts b/src/http/input/identifier/OriginalUrlExtractor.ts index 7ae929294..72b5c2114 100644 --- a/src/http/input/identifier/OriginalUrlExtractor.ts +++ b/src/http/input/identifier/OriginalUrlExtractor.ts @@ -45,12 +45,11 @@ export class OriginalUrlExtractor extends TargetExtractor { } // URL object applies punycode encoding to domain - const base = `${protocol}://${host}`; - const originalUrl = new URL(toCanonicalUriPath(url), base); - - // Drop the query string if requested - if (!this.includeQueryString) { - originalUrl.search = ''; + const originalUrl = new URL(`${protocol}://${host}`); + const [ , pathname, search ] = /^([^?]*)(.*)/u.exec(toCanonicalUriPath(url))!; + originalUrl.pathname = pathname; + if (this.includeQueryString && search) { + originalUrl.search = search; } return { path: originalUrl.href }; diff --git a/test/unit/http/input/identifier/OriginalUrlExtractor.test.ts b/test/unit/http/input/identifier/OriginalUrlExtractor.test.ts index c60668130..be4fc75cf 100644 --- a/test/unit/http/input/identifier/OriginalUrlExtractor.test.ts +++ b/test/unit/http/input/identifier/OriginalUrlExtractor.test.ts @@ -32,6 +32,12 @@ describe('A OriginalUrlExtractor', (): void => { .resolves.toEqual({ path: 'http://test.com/url' }); }); + it('returns an input URL with multiple leading slashes.', async(): Promise => { + const noQuery = new OriginalUrlExtractor({ includeQueryString: true }); + await expect(noQuery.handle({ request: { url: '///url?abc=def&xyz', headers: { host: 'test.com' }} as any })) + .resolves.toEqual({ path: 'http://test.com///url?abc=def&xyz' }); + }); + it('drops the query string when includeQueryString is set to false.', async(): Promise => { await expect(extractor.handle({ request: { url: '/url?abc=def&xyz', headers: { host: 'test.com' }} as any })) .resolves.toEqual({ path: 'http://test.com/url?abc=def&xyz' });