Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/utils/__tests__/matchAndRewriteRoute.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,22 @@ describe('matchAndRewriteURL', () => {
expect(url4.toString()).toEqual('https://localhost/googleapis/foo/v1/test:url?key=abc123');
});

it('Matches port parameters and remaps them properly', () => {
const url = attemptRemap({
url: new URL('https://foo.domain.com:4567/'),
mappings: [{prefix: '/domain/{subdomain}/{port}', target: '{subdomain}.domain.com:{port}'}],
});
expect(url.toString()).toEqual('https://localhost/domain/foo/4567/');
});

it('Does not remove port when url is not matched', () => {
const url = attemptRemap({
url: new URL('https://foo.domain.com:4567/'),
mappings: [],
});
expect(url.toString()).toEqual('https://foo.domain.com:4567/');
});

it('Applies the /.proxy/ mapping without any mappings', () => {
const url = attemptRemap({
url: new URL('https://1234567890.discordsays.com/api/token'),
Expand Down
12 changes: 9 additions & 3 deletions src/utils/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ function regexFromTarget(target: string): RegExp {
return new RegExp(`${regexString}(/|$)`);
}

function removePortParameterFromTarget(url: string): string {
return url.replace(/:\{[^}]+\}/g, '');
}

export interface MatchAndRewriteURLInputs {
originalURL: URL;
prefixHost: string;
Expand All @@ -32,15 +36,17 @@ export interface MatchAndRewriteURLInputs {
* @returns null if URL doesn't match prefix, otherwise return rewritten URL
*/
export function matchAndRewriteURL({originalURL, prefix, prefixHost, target}: MatchAndRewriteURLInputs): URL | null {
// coerce url with filler https protocol so we can retrieve host and pathname from target
const targetURL = new URL(`https://${target}`);
// Remove port parameter from target and coerce url with filler https protocol so we can retrieve host and pathname from target
const targetURL = new URL(`https://${removePortParameterFromTarget(target)}`);
// Depending on the environment, the URL constructor may turn `{` and `}` into `%7B` and `%7D`, respectively
const targetRegEx = regexFromTarget(targetURL.host.replace(/%7B/g, '{').replace(/%7D/g, '}'));
const targetRegEx = regexFromTarget(target.replace(/%7B/g, '{').replace(/%7D/g, '}'));
const match = originalURL.toString().match(targetRegEx);
// Null match indicates that this target is not relevant
if (match == null) return originalURL;
const newURL = new URL(originalURL.toString());
newURL.host = prefixHost;
// Remove port from new url (discord activities proxy doesn't listen on custom ports)
newURL.port = '';
newURL.pathname = prefix.replace(SUBSTITUTION_REGEX, (_, matchName) => {
const replaceValue = match.groups?.[matchName];
if (replaceValue == null) throw new Error('Misconfigured route.');
Expand Down