feat: Parse Accept headers as early as possible

This commit is contained in:
Joachim Van Herwegen
2022-05-11 11:01:22 +02:00
parent dee08ebd89
commit df0825936a
13 changed files with 119 additions and 64 deletions

View File

@@ -3,12 +3,13 @@ import type { OperationMetadataCollector } from '../http/ldp/metadata/OperationM
import type { ErrorHandler } from '../http/output/error/ErrorHandler';
import type { ResponseDescription } from '../http/output/response/ResponseDescription';
import type { ResponseWriter } from '../http/output/ResponseWriter';
import type { RepresentationPreferences } from '../http/representation/RepresentationPreferences';
import { getLoggerFor } from '../logging/LogUtil';
import { assertError } from '../util/errors/ErrorUtil';
import { HttpError } from '../util/errors/HttpError';
import type { HttpHandlerInput } from './HttpHandler';
import { HttpHandler } from './HttpHandler';
import type { HttpRequest } from './HttpRequest';
import type { HttpResponse } from './HttpResponse';
import type { OperationHttpHandler } from './OperationHttpHandler';
export interface ParsingHttpHandlerArgs {
@@ -35,9 +36,9 @@ export interface ParsingHttpHandlerArgs {
}
/**
* Parses requests and sends the resulting Operation to wrapped operationHandler.
* Errors are caught and handled by the Errorhandler.
* In case the operationHandler returns a result it will be sent to the ResponseWriter.
* Parses requests and sends the resulting {@link Operation} to the wrapped {@link OperationHttpHandler}.
* Errors are caught and handled by the {@link ErrorHandler}.
* In case the {@link OperationHttpHandler} returns a result it will be sent to the {@link ResponseWriter}.
*/
export class ParsingHttpHandler extends HttpHandler {
private readonly logger = getLoggerFor(this);
@@ -58,30 +59,45 @@ export class ParsingHttpHandler extends HttpHandler {
}
public async handle({ request, response }: HttpHandlerInput): Promise<void> {
let result: ResponseDescription | undefined;
let preferences: RepresentationPreferences = { type: { 'text/plain': 1 }};
let result: ResponseDescription;
try {
const operation = await this.requestParser.handleSafe(request);
({ preferences } = operation);
result = await this.operationHandler.handleSafe({ operation, request, response });
if (result?.metadata) {
await this.metadataCollector.handleSafe({ operation, metadata: result.metadata });
}
this.logger.verbose(`Parsed ${operation.method} operation on ${operation.target.path}`);
result = await this.handleRequest(request, response);
} catch (error: unknown) {
assertError(error);
result = await this.errorHandler.handleSafe({ error, preferences });
if (HttpError.isInstance(error) && result.metadata) {
const quads = error.generateMetadata(result.metadata.identifier);
result.metadata.addQuads(quads);
}
result = await this.handleError(error, request);
}
if (result) {
await this.responseWriter.handleSafe({ response, result });
}
}
/**
* Interprets the request and passes the generated Operation object to the stored OperationHttpHandler.
*/
protected async handleRequest(request: HttpRequest, response: HttpResponse):
Promise<ResponseDescription> {
const operation = await this.requestParser.handleSafe(request);
const result = await this.operationHandler.handleSafe({ operation, request, response });
if (result?.metadata) {
await this.metadataCollector.handleSafe({ operation, metadata: result.metadata });
}
this.logger.verbose(`Parsed ${operation.method} operation on ${operation.target.path}`);
return result;
}
/**
* Handles the error output correctly based on the preferences.
*/
protected async handleError(error: unknown, request: HttpRequest): Promise<ResponseDescription> {
assertError(error);
const result = await this.errorHandler.handleSafe({ error, request });
if (HttpError.isInstance(error) && result.metadata) {
const quads = error.generateMetadata(result.metadata.identifier);
result.metadata.addQuads(quads);
}
return result;
}
}