mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
refactor: Make request related handle calls consistent
This commit is contained in:
parent
995a2dc74d
commit
f17054c647
@ -34,7 +34,7 @@ export class DPoPWebIdExtractor extends CredentialsExtractor {
|
|||||||
if (!dpop) {
|
if (!dpop) {
|
||||||
throw new BadRequestHttpError('No DPoP header specified.');
|
throw new BadRequestHttpError('No DPoP header specified.');
|
||||||
}
|
}
|
||||||
const resource = await this.targetExtractor.handleSafe(request);
|
const resource = await this.targetExtractor.handleSafe({ request });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { webid: webId } = await this.verify(
|
const { webid: webId } = await this.verify(
|
||||||
|
@ -27,7 +27,7 @@ const parsers: {
|
|||||||
* Supports Accept, Accept-Charset, Accept-Encoding, Accept-Language and Accept-DateTime.
|
* Supports Accept, Accept-Charset, Accept-Encoding, Accept-Language and Accept-DateTime.
|
||||||
*/
|
*/
|
||||||
export class AcceptPreferenceParser extends PreferenceParser {
|
export class AcceptPreferenceParser extends PreferenceParser {
|
||||||
public async handle({ headers }: HttpRequest): Promise<RepresentationPreferences> {
|
public async handle({ request: { headers }}: { request: HttpRequest }): Promise<RepresentationPreferences> {
|
||||||
const preferences: RepresentationPreferences = {};
|
const preferences: RepresentationPreferences = {};
|
||||||
for (const { name, header, parse } of parsers) {
|
for (const { name, header, parse } of parsers) {
|
||||||
const value = headers[header];
|
const value = headers[header];
|
||||||
|
@ -36,9 +36,9 @@ export class BasicRequestParser extends RequestParser {
|
|||||||
if (!method) {
|
if (!method) {
|
||||||
throw new Error('No method specified on the HTTP request');
|
throw new Error('No method specified on the HTTP request');
|
||||||
}
|
}
|
||||||
const target = await this.targetExtractor.handleSafe(request);
|
const target = await this.targetExtractor.handleSafe({ request });
|
||||||
const preferences = await this.preferenceParser.handleSafe(request);
|
const preferences = await this.preferenceParser.handleSafe({ request });
|
||||||
const metadata = await this.metadataExtractor.handleSafe(request);
|
const metadata = await this.metadataExtractor.handleSafe({ request });
|
||||||
const body = await this.bodyParser.handleSafe({ request, metadata });
|
const body = await this.bodyParser.handleSafe({ request, metadata });
|
||||||
|
|
||||||
return { method, target, preferences, body };
|
return { method, target, preferences, body };
|
||||||
|
@ -11,7 +11,7 @@ import { TargetExtractor } from './TargetExtractor';
|
|||||||
* TODO: input requires more extensive cleaning/parsing based on headers (see #22).
|
* TODO: input requires more extensive cleaning/parsing based on headers (see #22).
|
||||||
*/
|
*/
|
||||||
export class BasicTargetExtractor extends TargetExtractor {
|
export class BasicTargetExtractor extends TargetExtractor {
|
||||||
public async handle({ url, connection, headers }: HttpRequest): Promise<ResourceIdentifier> {
|
public async handle({ request: { url, connection, headers }}: { request: HttpRequest }): Promise<ResourceIdentifier> {
|
||||||
if (!url) {
|
if (!url) {
|
||||||
throw new Error('Missing URL');
|
throw new Error('Missing URL');
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,4 @@ import type { RepresentationPreferences } from '../representation/Representation
|
|||||||
/**
|
/**
|
||||||
* Creates {@link RepresentationPreferences} based on the incoming HTTP headers in a {@link HttpRequest}.
|
* Creates {@link RepresentationPreferences} based on the incoming HTTP headers in a {@link HttpRequest}.
|
||||||
*/
|
*/
|
||||||
export abstract class PreferenceParser extends AsyncHandler<HttpRequest, RepresentationPreferences> {}
|
export abstract class PreferenceParser extends AsyncHandler<{ request: HttpRequest }, RepresentationPreferences> {}
|
||||||
|
@ -5,4 +5,4 @@ import type { ResourceIdentifier } from '../representation/ResourceIdentifier';
|
|||||||
/**
|
/**
|
||||||
* Extracts a {@link ResourceIdentifier} from an incoming {@link HttpRequest}.
|
* Extracts a {@link ResourceIdentifier} from an incoming {@link HttpRequest}.
|
||||||
*/
|
*/
|
||||||
export abstract class TargetExtractor extends AsyncHandler<HttpRequest, ResourceIdentifier> {}
|
export abstract class TargetExtractor extends AsyncHandler<{ request: HttpRequest }, ResourceIdentifier> {}
|
||||||
|
@ -14,7 +14,7 @@ export class BasicMetadataExtractor extends MetadataExtractor {
|
|||||||
this.parsers = parsers;
|
this.parsers = parsers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async handle(request: HttpRequest):
|
public async handle({ request }: { request: HttpRequest }):
|
||||||
Promise<RepresentationMetadata> {
|
Promise<RepresentationMetadata> {
|
||||||
const metadata = new RepresentationMetadata();
|
const metadata = new RepresentationMetadata();
|
||||||
for (const parser of this.parsers) {
|
for (const parser of this.parsers) {
|
||||||
|
@ -6,4 +6,4 @@ import type { RepresentationMetadata } from '../../representation/Representation
|
|||||||
* Parses the metadata of a {@link HttpRequest} into a {@link RepresentationMetadata}.
|
* Parses the metadata of a {@link HttpRequest} into a {@link RepresentationMetadata}.
|
||||||
*/
|
*/
|
||||||
export abstract class MetadataExtractor extends
|
export abstract class MetadataExtractor extends
|
||||||
AsyncHandler<HttpRequest, RepresentationMetadata> {}
|
AsyncHandler<{ request: HttpRequest }, RepresentationMetadata> {}
|
||||||
|
@ -74,7 +74,7 @@ describe('A DPoPWebIdExtractor', (): void => {
|
|||||||
it('calls the target extractor with the correct parameters.', async(): Promise<void> => {
|
it('calls the target extractor with the correct parameters.', async(): Promise<void> => {
|
||||||
await webIdExtractor.handleSafe(request);
|
await webIdExtractor.handleSafe(request);
|
||||||
expect(targetExtractor.handle).toHaveBeenCalledTimes(1);
|
expect(targetExtractor.handle).toHaveBeenCalledTimes(1);
|
||||||
expect(targetExtractor.handle).toHaveBeenCalledWith(request);
|
expect(targetExtractor.handle).toHaveBeenCalledWith({ request });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the DPoP verifier with the correct parameters.', async(): Promise<void> => {
|
it('calls the DPoP verifier with the correct parameters.', async(): Promise<void> => {
|
||||||
|
@ -3,41 +3,47 @@ import type { HttpRequest } from '../../../../src/server/HttpRequest';
|
|||||||
|
|
||||||
describe('An AcceptPreferenceParser', (): void => {
|
describe('An AcceptPreferenceParser', (): void => {
|
||||||
const preferenceParser = new AcceptPreferenceParser();
|
const preferenceParser = new AcceptPreferenceParser();
|
||||||
|
let request: HttpRequest;
|
||||||
|
beforeEach(async(): Promise<void> => {
|
||||||
|
request = { headers: {}} as HttpRequest;
|
||||||
|
});
|
||||||
|
|
||||||
it('can handle all input.', async(): Promise<void> => {
|
it('can handle all input.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.canHandle({} as HttpRequest)).resolves.toBeUndefined();
|
await expect(preferenceParser.canHandle({ request })).resolves.toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns an empty result if there is no relevant input.', async(): Promise<void> => {
|
it('returns an empty result if there is no relevant input.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.handle({ headers: {}} as HttpRequest)).resolves.toEqual({});
|
await expect(preferenceParser.handle({ request })).resolves.toEqual({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses accept headers.', async(): Promise<void> => {
|
it('parses accept headers.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.handle({ headers: { accept: 'audio/*; q=0.2, audio/basic' }} as HttpRequest))
|
request.headers = { accept: 'audio/*; q=0.2, audio/basic' };
|
||||||
|
await expect(preferenceParser.handle({ request }))
|
||||||
.resolves.toEqual({ type: { 'audio/basic': 1, 'audio/*': 0.2 }});
|
.resolves.toEqual({ type: { 'audio/basic': 1, 'audio/*': 0.2 }});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses accept-charset headers.', async(): Promise<void> => {
|
it('parses accept-charset headers.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.handle(
|
request.headers = { 'accept-charset': 'iso-8859-5, unicode-1-1;q=0.8' };
|
||||||
{ headers: { 'accept-charset': 'iso-8859-5, unicode-1-1;q=0.8' }} as unknown as HttpRequest,
|
await expect(preferenceParser.handle({ request }))
|
||||||
)).resolves.toEqual({ charset: { 'iso-8859-5': 1, 'unicode-1-1': 0.8 }});
|
.resolves.toEqual({ charset: { 'iso-8859-5': 1, 'unicode-1-1': 0.8 }});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses accept-datetime headers.', async(): Promise<void> => {
|
it('parses accept-datetime headers.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.handle(
|
request.headers = { 'accept-datetime': 'Tue, 20 Mar 2001 20:35:00 GMT' };
|
||||||
{ headers: { 'accept-datetime': 'Tue, 20 Mar 2001 20:35:00 GMT' }} as unknown as HttpRequest,
|
await expect(preferenceParser.handle({ request }))
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
)).resolves.toEqual({ datetime: { 'Tue, 20 Mar 2001 20:35:00 GMT': 1 }});
|
.resolves.toEqual({ datetime: { 'Tue, 20 Mar 2001 20:35:00 GMT': 1 }});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses accept-encoding headers.', async(): Promise<void> => {
|
it('parses accept-encoding headers.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.handle(
|
request.headers = { 'accept-encoding': 'gzip;q=1.0, identity; q=0.5, *;q=0' };
|
||||||
{ headers: { 'accept-encoding': 'gzip;q=1.0, identity; q=0.5, *;q=0' }} as unknown as HttpRequest,
|
await expect(preferenceParser.handle({ request }))
|
||||||
)).resolves.toEqual({ encoding: { gzip: 1, identity: 0.5, '*': 0 }});
|
.resolves.toEqual({ encoding: { gzip: 1, identity: 0.5, '*': 0 }});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('parses accept-language headers.', async(): Promise<void> => {
|
it('parses accept-language headers.', async(): Promise<void> => {
|
||||||
await expect(preferenceParser.handle({ headers: { 'accept-language': 'da, en-gb;q=0.8, en;q=0.7' }} as HttpRequest))
|
request.headers = { 'accept-language': 'da, en-gb;q=0.8, en;q=0.7' };
|
||||||
|
await expect(preferenceParser.handle({ request }))
|
||||||
.resolves.toEqual({ language: { da: 1, 'en-gb': 0.8, en: 0.7 }});
|
.resolves.toEqual({ language: { da: 1, 'en-gb': 0.8, en: 0.7 }});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -8,47 +8,48 @@ describe('A BasicTargetExtractor', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('errors if there is no URL.', async(): Promise<void> => {
|
it('errors if there is no URL.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ headers: { host: 'test.com' }} as any)).rejects.toThrow('Missing URL');
|
await expect(extractor.handle({ request: { headers: { host: 'test.com' }} as any })).rejects.toThrow('Missing URL');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if there is no host.', async(): Promise<void> => {
|
it('errors if there is no host.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ url: 'url', headers: {}} as any)).rejects.toThrow('Missing Host header');
|
await expect(extractor.handle({ request: { url: 'url', headers: {}} as any }))
|
||||||
|
.rejects.toThrow('Missing Host header');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('errors if the host is invalid.', async(): Promise<void> => {
|
it('errors if the host is invalid.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ url: 'url', headers: { host: 'test.com/forbidden' }} as any))
|
await expect(extractor.handle({ request: { url: 'url', headers: { host: 'test.com/forbidden' }} as any }))
|
||||||
.rejects.toThrow('The request has an invalid Host header: test.com/forbidden');
|
.rejects.toThrow('The request has an invalid Host header: test.com/forbidden');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns the input URL.', async(): Promise<void> => {
|
it('returns the input URL.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ url: 'url', headers: { host: 'test.com' }} as any))
|
await expect(extractor.handle({ request: { url: 'url', headers: { host: 'test.com' }} as any }))
|
||||||
.resolves.toEqual({ path: 'http://test.com/url' });
|
.resolves.toEqual({ path: 'http://test.com/url' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('supports host:port combinations.', async(): Promise<void> => {
|
it('supports host:port combinations.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ url: 'url', headers: { host: 'localhost:3000' }} as any))
|
await expect(extractor.handle({ request: { url: 'url', headers: { host: 'localhost:3000' }} as any }))
|
||||||
.resolves.toEqual({ path: 'http://localhost:3000/url' });
|
.resolves.toEqual({ path: 'http://localhost:3000/url' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses https protocol if the connection is secure.', async(): Promise<void> => {
|
it('uses https protocol if the connection is secure.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle(
|
await expect(extractor.handle(
|
||||||
{ url: 'url', headers: { host: 'test.com' }, connection: { encrypted: true } as any } as any,
|
{ request: { url: 'url', headers: { host: 'test.com' }, connection: { encrypted: true } as any } as any },
|
||||||
)).resolves.toEqual({ path: 'https://test.com/url' });
|
)).resolves.toEqual({ path: 'https://test.com/url' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('encodes paths.', async(): Promise<void> => {
|
it('encodes paths.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ url: '/a%20path%26/name', headers: { host: 'test.com' }} as any))
|
await expect(extractor.handle({ request: { url: '/a%20path%26/name', headers: { host: 'test.com' }} as any }))
|
||||||
.resolves.toEqual({ path: 'http://test.com/a%20path%26/name' });
|
.resolves.toEqual({ path: 'http://test.com/a%20path%26/name' });
|
||||||
|
|
||||||
await expect(extractor.handle({ url: '/a path%26/name', headers: { host: 'test.com' }} as any))
|
await expect(extractor.handle({ request: { url: '/a path%26/name', headers: { host: 'test.com' }} as any }))
|
||||||
.resolves.toEqual({ path: 'http://test.com/a%20path%26/name' });
|
.resolves.toEqual({ path: 'http://test.com/a%20path%26/name' });
|
||||||
|
|
||||||
await expect(extractor.handle({ url: '/path&%26/name', headers: { host: 'test.com' }} as any))
|
await expect(extractor.handle({ request: { url: '/path&%26/name', headers: { host: 'test.com' }} as any }))
|
||||||
.resolves.toEqual({ path: 'http://test.com/path%26%26/name' });
|
.resolves.toEqual({ path: 'http://test.com/path%26%26/name' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('encodes hosts.', async(): Promise<void> => {
|
it('encodes hosts.', async(): Promise<void> => {
|
||||||
await expect(extractor.handle({ url: '/', headers: { host: '點看' }} as any))
|
await expect(extractor.handle({ request: { url: '/', headers: { host: '點看' }} as any }))
|
||||||
.resolves.toEqual({ path: 'http://xn--c1yn36f/' });
|
.resolves.toEqual({ path: 'http://xn--c1yn36f/' });
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ describe('A BasicTargetExtractor', (): void => {
|
|||||||
host: 'test.com',
|
host: 'test.com',
|
||||||
forwarded: 'by=203.0.113.60',
|
forwarded: 'by=203.0.113.60',
|
||||||
};
|
};
|
||||||
await expect(extractor.handle({ url: '/foo/bar', headers } as any))
|
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||||
.resolves.toEqual({ path: 'http://test.com/foo/bar' });
|
.resolves.toEqual({ path: 'http://test.com/foo/bar' });
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ describe('A BasicTargetExtractor', (): void => {
|
|||||||
host: 'test.com',
|
host: 'test.com',
|
||||||
forwarded: 'proto=https;host=pod.example',
|
forwarded: 'proto=https;host=pod.example',
|
||||||
};
|
};
|
||||||
await expect(extractor.handle({ url: '/foo/bar', headers } as any))
|
await expect(extractor.handle({ request: { url: '/foo/bar', headers } as any }))
|
||||||
.resolves.toEqual({ path: 'https://pod.example/foo/bar' });
|
.resolves.toEqual({ path: 'https://pod.example/foo/bar' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -32,7 +32,7 @@ describe('A BasicMetadataExtractor', (): void => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('will add metadata from the parsers.', async(): Promise<void> => {
|
it('will add metadata from the parsers.', async(): Promise<void> => {
|
||||||
const metadata = await handler.handle({ headers: { aa: 'valA', bb: 'valB' } as any } as HttpRequest);
|
const metadata = await handler.handle({ request: { headers: { aa: 'valA', bb: 'valB' } as any } as HttpRequest });
|
||||||
expect(metadata.getAll(RDF.type).map((term): any => term.value)).toEqual([ 'valA', 'valB' ]);
|
expect(metadata.getAll(RDF.type).map((term): any => term.value)).toEqual([ 'valA', 'valB' ]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user