Skip to content

Commit

Permalink
feat: Decode URI in target extractor
Browse files Browse the repository at this point in the history
  • Loading branch information
joachimvh committed Oct 7, 2020
1 parent b47dc3f commit bb28af9
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 3 deletions.
3 changes: 2 additions & 1 deletion src/ldp/http/BasicTargetExtractor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { TLSSocket } from 'tls';
import { format } from 'url';
import type { HttpRequest } from '../../server/HttpRequest';
import { toCanonicalUrl } from '../../util/Util';
import type { ResourceIdentifier } from '../representation/ResourceIdentifier';
import { TargetExtractor } from './TargetExtractor';

Expand Down Expand Up @@ -28,6 +29,6 @@ export class BasicTargetExtractor extends TargetExtractor {
pathname: input.url,
});

return { path: url };
return { path: toCanonicalUrl(url) };
}
}
13 changes: 13 additions & 0 deletions src/util/Util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,16 @@ export const pipeStreamsAndErrors = <T extends Writable>(readable: Readable, des
readable.on('error', (error): boolean => destination.emit('error', new UnsupportedHttpError(error.message)));
return destination;
};

/**
* Converts a URL string to the "canonical" version that should be used internally for consistency.
* Decodes all percent encodings and then makes sure only the necessary characters are encoded again.
*/
export const toCanonicalUrl = (url: string): string => {
const match = /(\w+:\/\/[^/]+\/)(.*)/u.exec(url);
if (!match) {
throw new UnsupportedHttpError(`Invalid URL ${url}`);
}
const [ , domain, path ] = match;
return encodeURI(domain + path.split('/').map(decodeURIComponent).join('/'));
};
8 changes: 7 additions & 1 deletion test/unit/ldp/http/BasicTargetExtractor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,18 @@ describe('A BasicTargetExtractor', (): void => {
});

it('returns the input URL.', async(): Promise<void> => {
await expect(extractor.handle({ url: 'url', headers: { host: 'test.com' }} as any)).resolves.toEqual({ path: 'http://test.com/url' });
await expect(extractor.handle({ url: 'url', headers: { host: 'test.com' }} as any))
.resolves.toEqual({ path: 'http://test.com/url' });
});

it('uses https protocol if the connection is secure.', async(): Promise<void> => {
await expect(extractor.handle(
{ url: 'url', headers: { host: 'test.com' }, connection: { encrypted: true } as any } as any,
)).resolves.toEqual({ path: 'https://test.com/url' });
});

it('decodes relevant percent encodings.', async(): Promise<void> => {
await expect(extractor.handle({ url: '/a%20path%26/name', headers: { host: 'test.com' }} as any))
.resolves.toEqual({ path: 'http://test.com/a%20path&/name' });
});
});
14 changes: 13 additions & 1 deletion test/unit/util/Util.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import streamifyArray from 'streamify-array';
import { ensureTrailingSlash, matchingMediaType, readableToString } from '../../../src/util/Util';
import { UnsupportedHttpError } from '../../../src/util/errors/UnsupportedHttpError';
import { ensureTrailingSlash, matchingMediaType, readableToString, toCanonicalUrl } from '../../../src/util/Util';

describe('Util function', (): void => {
describe('ensureTrailingSlash', (): void => {
Expand Down Expand Up @@ -31,4 +32,15 @@ describe('Util function', (): void => {
expect(matchingMediaType('text/plain', 'text/turtle')).toBeFalsy();
});
});

describe('toCanonicalUrl', (): void => {
it('makes sure only the necessary parts are encoded.', async(): Promise<void> => {
expect(toCanonicalUrl('http://test.com/a%20path%26/name'))
.toEqual('http://test.com/a%20path&/name');
});

it('errors on invalid URLs.', async(): Promise<void> => {
expect((): any => toCanonicalUrl('notAnUrl')).toThrow(new UnsupportedHttpError('Invalid URL notAnUrl'));
});
});
});

0 comments on commit bb28af9

Please sign in to comment.