refactor: Replace RedirectAllHttpHandler usage with RedirectingHttpHandler

This commit is contained in:
Joachim Van Herwegen 2022-04-21 11:26:47 +02:00
parent 89eeb52b01
commit d2bc995272
7 changed files with 37 additions and 131 deletions

View File

@ -26,6 +26,7 @@ The following changes are relevant for v3 custom configs that replaced certain f
### Interface changes ### Interface changes
These changes are relevant if you wrote custom modules for the server that depend on existing interfaces. These changes are relevant if you wrote custom modules for the server that depend on existing interfaces.
- `YargsCliExtractor` was changed to now take as input an array of parameter objects. - `YargsCliExtractor` was changed to now take as input an array of parameter objects.
- `RedirectAllHttpHandler` was removed and fully replaced by `RedirectingHttpHandler`.
## v4.0.0 ## v4.0.0
### New features ### New features

View File

@ -4,11 +4,17 @@
{ {
"comment": "Redirects all request to the setup.", "comment": "Redirects all request to the setup.",
"@id": "urn:solid-server:default:SetupRedirectHandler", "@id": "urn:solid-server:default:SetupRedirectHandler",
"@type": "RedirectAllHttpHandler", "@type": "RedirectingHttpHandler",
"args_baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" }, "redirects": [
"args_target": "/setup", {
"args_targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" }, "RedirectingHttpHandler:_redirects_key": ".*",
"args_responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" } "RedirectingHttpHandler:_redirects_value": "/setup"
}, }
],
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"targetExtractor": { "@id": "urn:solid-server:default:TargetExtractor" },
"responseWriter": { "@id": "urn:solid-server:default:ResponseWriter" },
"statusCode": 302
}
] ]
} }

View File

@ -269,7 +269,6 @@ export * from './server/HttpResponse';
export * from './server/HttpServerFactory'; export * from './server/HttpServerFactory';
export * from './server/OperationHttpHandler'; export * from './server/OperationHttpHandler';
export * from './server/ParsingHttpHandler'; export * from './server/ParsingHttpHandler';
export * from './server/RedirectingHttpHandler';
export * from './server/WebSocketHandler'; export * from './server/WebSocketHandler';
export * from './server/WebSocketServerFactory'; export * from './server/WebSocketServerFactory';
@ -280,7 +279,7 @@ export * from './server/middleware/StaticAssetHandler';
export * from './server/middleware/WebSocketAdvertiser'; export * from './server/middleware/WebSocketAdvertiser';
// Server/Util // Server/Util
export * from './server/util/RedirectAllHttpHandler'; export * from './server/util/RedirectingHttpHandler';
export * from './server/util/RouterHandler'; export * from './server/util/RouterHandler';
// Storage/Accessors // Storage/Accessors

View File

@ -1,47 +0,0 @@
import type { TargetExtractor } from '../../http/input/identifier/TargetExtractor';
import { RedirectResponseDescription } from '../../http/output/response/RedirectResponseDescription';
import type { ResponseWriter } from '../../http/output/ResponseWriter';
import { FoundHttpError } from '../../util/errors/FoundHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { getRelativeUrl, joinUrl } from '../../util/PathUtil';
import type { HttpHandlerInput } from '../HttpHandler';
import { HttpHandler } from '../HttpHandler';
export interface RedirectAllHttpHandlerArgs {
baseUrl: string;
target: string;
targetExtractor: TargetExtractor;
responseWriter: ResponseWriter;
}
/**
* Will redirect all incoming requests to the given target.
* In case the incoming request already has the correct target,
* the `canHandle` call will reject the input.
*/
export class RedirectAllHttpHandler extends HttpHandler {
private readonly baseUrl: string;
private readonly target: string;
private readonly targetExtractor: TargetExtractor;
private readonly responseWriter: ResponseWriter;
public constructor(args: RedirectAllHttpHandlerArgs) {
super();
this.baseUrl = args.baseUrl;
this.target = args.target;
this.targetExtractor = args.targetExtractor;
this.responseWriter = args.responseWriter;
}
public async canHandle({ request }: HttpHandlerInput): Promise<void> {
const target = await getRelativeUrl(this.baseUrl, request, this.targetExtractor);
if (target === this.target) {
throw new NotImplementedHttpError('Target is already correct.');
}
}
public async handle({ response }: HttpHandlerInput): Promise<void> {
const result = new RedirectResponseDescription(new FoundHttpError(joinUrl(this.baseUrl, this.target)));
await this.responseWriter.handleSafe({ response, result });
}
}

View File

@ -1,18 +1,18 @@
import type { TargetExtractor } from '../http/input/identifier/TargetExtractor'; import type { TargetExtractor } from '../../http/input/identifier/TargetExtractor';
import { RedirectResponseDescription } from '../http/output/response/RedirectResponseDescription'; import { RedirectResponseDescription } from '../../http/output/response/RedirectResponseDescription';
import type { ResponseWriter } from '../http/output/ResponseWriter'; import type { ResponseWriter } from '../../http/output/ResponseWriter';
import { getLoggerFor } from '../logging/LogUtil'; import { getLoggerFor } from '../../logging/LogUtil';
import { FoundHttpError } from '../util/errors/FoundHttpError'; import { FoundHttpError } from '../../util/errors/FoundHttpError';
import { MovedPermanentlyHttpError } from '../util/errors/MovedPermanentlyHttpError'; import { MovedPermanentlyHttpError } from '../../util/errors/MovedPermanentlyHttpError';
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError'; import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { PermanentRedirectHttpError } from '../util/errors/PermanentRedirectHttpError'; import { PermanentRedirectHttpError } from '../../util/errors/PermanentRedirectHttpError';
import type { RedirectHttpError } from '../util/errors/RedirectHttpError'; import type { RedirectHttpError } from '../../util/errors/RedirectHttpError';
import { SeeOtherHttpError } from '../util/errors/SeeOtherHttpError'; import { SeeOtherHttpError } from '../../util/errors/SeeOtherHttpError';
import { TemporaryRedirectHttpError } from '../util/errors/TemporaryRedirectHttpError'; import { TemporaryRedirectHttpError } from '../../util/errors/TemporaryRedirectHttpError';
import { getRelativeUrl, joinUrl } from '../util/PathUtil'; import { getRelativeUrl, joinUrl } from '../../util/PathUtil';
import type { HttpHandlerInput } from './HttpHandler'; import type { HttpHandlerInput } from '../HttpHandler';
import { HttpHandler } from './HttpHandler'; import { HttpHandler } from '../HttpHandler';
import type { HttpRequest } from './HttpRequest'; import type { HttpRequest } from '../HttpRequest';
const redirectErrorFactories: Record<301 | 302 | 303 | 307 | 308, (location: string) => RedirectHttpError> = { const redirectErrorFactories: Record<301 | 302 | 303 | 307 | 308, (location: string) => RedirectHttpError> = {
301: (location: string): RedirectHttpError => new MovedPermanentlyHttpError(location), 301: (location: string): RedirectHttpError => new MovedPermanentlyHttpError(location),

View File

@ -1,53 +0,0 @@
import type { TargetExtractor } from '../../../../src/http/input/identifier/TargetExtractor';
import type { ResponseWriter } from '../../../../src/http/output/ResponseWriter';
import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier';
import type { HttpRequest } from '../../../../src/server/HttpRequest';
import type { HttpResponse } from '../../../../src/server/HttpResponse';
import { RedirectAllHttpHandler } from '../../../../src/server/util/RedirectAllHttpHandler';
import { NotImplementedHttpError } from '../../../../src/util/errors/NotImplementedHttpError';
import { joinUrl } from '../../../../src/util/PathUtil';
import { SOLID_HTTP } from '../../../../src/util/Vocabularies';
describe('A RedirectAllHttpHandler', (): void => {
const baseUrl = 'http://test.com/';
const target = '/foo';
const absoluteTarget = 'http://test.com/foo';
let request: HttpRequest;
const response: HttpResponse = {} as any;
let targetExtractor: jest.Mocked<TargetExtractor>;
let responseWriter: jest.Mocked<ResponseWriter>;
let handler: RedirectAllHttpHandler;
beforeEach(async(): Promise<void> => {
request = { url: '/foo' } as any;
targetExtractor = {
handleSafe: jest.fn(({ request: req }): ResourceIdentifier => ({ path: joinUrl(baseUrl, req.url!) })),
} as any;
responseWriter = { handleSafe: jest.fn() } as any;
handler = new RedirectAllHttpHandler({ baseUrl, target, targetExtractor, responseWriter });
});
it('rejects requests for the target.', async(): Promise<void> => {
request.url = target;
await expect(handler.canHandle({ request, response })).rejects.toThrow(NotImplementedHttpError);
});
it('accepts all other requests.', async(): Promise<void> => {
request.url = '/otherPath';
await expect(handler.canHandle({ request, response })).resolves.toBeUndefined();
});
it('writes out a redirect response.', async(): Promise<void> => {
await expect(handler.handle({ request, response })).resolves.toBeUndefined();
expect(responseWriter.handleSafe).toHaveBeenCalledTimes(1);
expect(responseWriter.handleSafe).toHaveBeenLastCalledWith({
response,
result: expect.objectContaining({ statusCode: 302 }),
});
const { metadata } = responseWriter.handleSafe.mock.calls[0][0].result;
expect(metadata?.get(SOLID_HTTP.terms.location)?.value).toBe(absoluteTarget);
});
});

View File

@ -1,11 +1,11 @@
import type { TargetExtractor } from '../../../src/http/input/identifier/TargetExtractor'; import type { TargetExtractor } from '../../../../src/http/input/identifier/TargetExtractor';
import type { ResponseWriter } from '../../../src/http/output/ResponseWriter'; import type { ResponseWriter } from '../../../../src/http/output/ResponseWriter';
import type { ResourceIdentifier } from '../../../src/http/representation/ResourceIdentifier'; import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier';
import type { HttpRequest } from '../../../src/server/HttpRequest'; import type { HttpRequest } from '../../../../src/server/HttpRequest';
import type { HttpResponse } from '../../../src/server/HttpResponse'; import type { HttpResponse } from '../../../../src/server/HttpResponse';
import { RedirectingHttpHandler } from '../../../src/server/RedirectingHttpHandler'; import { RedirectingHttpHandler } from '../../../../src/server/util/RedirectingHttpHandler';
import { joinUrl } from '../../../src/util/PathUtil'; import { joinUrl } from '../../../../src/util/PathUtil';
import { SOLID_HTTP } from '../../../src/util/Vocabularies'; import { SOLID_HTTP } from '../../../../src/util/Vocabularies';
describe('A RedirectingHttpHandler', (): void => { describe('A RedirectingHttpHandler', (): void => {
const baseUrl = 'http://test.com/'; const baseUrl = 'http://test.com/';