fix: Rename UnsupportedHttpError into BadRequestError.

This commit is contained in:
Ruben Verborgh
2020-11-27 10:25:05 +01:00
committed by Joachim Van Herwegen
parent 03ffaaed43
commit af8f1976cd
53 changed files with 177 additions and 171 deletions

View File

@@ -1,7 +1,7 @@
import { getLoggerFor } from '../../logging/LogUtil';
import type { HttpResponse } from '../../server/HttpResponse';
import { INTERNAL_QUADS } from '../../util/ContentTypes';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { pipeSafely } from '../../util/StreamUtil';
import type { MetadataWriter } from './metadata/MetadataWriter';
import type { ResponseDescription } from './response/ResponseDescription';
@@ -22,7 +22,7 @@ export class BasicResponseWriter extends ResponseWriter {
public async canHandle(input: { response: HttpResponse; result: ResponseDescription | Error }): Promise<void> {
if (input.result instanceof Error || input.result.metadata?.contentType === INTERNAL_QUADS) {
this.logger.warn('This writer only supports binary ResponseDescriptions');
throw new UnsupportedHttpError('Only successful binary responses are supported');
throw new NotImplementedHttpError('Only successful binary responses are supported');
}
}

View File

@@ -1,7 +1,7 @@
import { getLoggerFor } from '../../logging/LogUtil';
import type { HttpResponse } from '../../server/HttpResponse';
import { HttpError } from '../../util/errors/HttpError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import type { ResponseDescription } from './response/ResponseDescription';
import { ResponseWriter } from './ResponseWriter';
@@ -14,7 +14,7 @@ export class ErrorResponseWriter extends ResponseWriter {
public async canHandle(input: { response: HttpResponse; result: ResponseDescription | Error }): Promise<void> {
if (!(input.result instanceof Error)) {
this.logger.warn('This writer can only write errors');
throw new UnsupportedHttpError('Only errors are supported');
throw new NotImplementedHttpError('Only errors are supported');
}
}

View File

@@ -1,5 +1,5 @@
import { getLoggerFor } from '../../logging/LogUtil';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import type { Representation } from '../representation/Representation';
import type { BodyParserArgs } from './BodyParser';
import { BodyParser } from './BodyParser';
@@ -24,7 +24,7 @@ export class RawBodyParser extends BodyParser {
// such an omission likely signals a mistake, so force clients to make this explicit.
if (!request.headers['content-type']) {
this.logger.warn('A body was passed, but the content length was not specified');
throw new UnsupportedHttpError('HTTP request body was passed without Content-Type header');
throw new BadRequestHttpError('HTTP request body was passed without Content-Type header');
}
return {

View File

@@ -3,7 +3,7 @@ import type { Algebra } from 'sparqlalgebrajs';
import { translate } from 'sparqlalgebrajs';
import { getLoggerFor } from '../../logging/LogUtil';
import { APPLICATION_SPARQL_UPDATE } from '../../util/ContentTypes';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError';
import { pipeSafely, readableToString } from '../../util/StreamUtil';
import type { BodyParserArgs } from './BodyParser';
@@ -36,9 +36,9 @@ export class SparqlUpdateBodyParser extends BodyParser {
} catch (error: unknown) {
this.logger.warn('Could not translate SPARQL query to SPARQL algebra', { error });
if (error instanceof Error) {
throw new UnsupportedHttpError(error.message);
throw new BadRequestHttpError(error.message);
}
throw new UnsupportedHttpError();
throw new BadRequestHttpError();
}
// Prevent body from being requested again

View File

@@ -1,6 +1,6 @@
import { getLoggerFor } from '../../../logging/LogUtil';
import type { HttpRequest } from '../../../server/HttpRequest';
import { UnsupportedHttpError } from '../../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../../util/errors/BadRequestHttpError';
import { HTTP } from '../../../util/UriConstants';
import type { RepresentationMetadata } from '../../representation/RepresentationMetadata';
import type { MetadataParser } from './MetadataParser';
@@ -16,7 +16,7 @@ export class SlugParser implements MetadataParser {
if (slug) {
if (Array.isArray(slug)) {
this.logger.warn(`Expected 0 or 1 Slug headers but received ${slug.length}`);
throw new UnsupportedHttpError('Request has multiple Slug headers');
throw new BadRequestHttpError('Request has multiple Slug headers');
}
this.logger.debug(`Request Slug is '${slug}'.`);
metadata.set(HTTP.slug, slug);

View File

@@ -1,5 +1,5 @@
import type { ResourceStore } from '../../storage/ResourceStore';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { ResetResponseDescription } from '../http/response/ResetResponseDescription';
import type { ResponseDescription } from '../http/response/ResponseDescription';
import type { Operation } from './Operation';
@@ -19,7 +19,7 @@ export class DeleteOperationHandler extends OperationHandler {
public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'DELETE') {
throw new UnsupportedHttpError('This handler only supports DELETE operations');
throw new NotImplementedHttpError('This handler only supports DELETE operations');
}
}

View File

@@ -1,5 +1,5 @@
import type { ResourceStore } from '../../storage/ResourceStore';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { OkResponseDescription } from '../http/response/OkResponseDescription';
import type { ResponseDescription } from '../http/response/ResponseDescription';
import type { Operation } from './Operation';
@@ -19,7 +19,7 @@ export class GetOperationHandler extends OperationHandler {
public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'GET') {
throw new UnsupportedHttpError('This handler only supports GET operations');
throw new NotImplementedHttpError('This handler only supports GET operations');
}
}

View File

@@ -1,5 +1,5 @@
import type { ResourceStore } from '../../storage/ResourceStore';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { OkResponseDescription } from '../http/response/OkResponseDescription';
import type { ResponseDescription } from '../http/response/ResponseDescription';
import type { Operation } from './Operation';
@@ -19,7 +19,7 @@ export class HeadOperationHandler extends OperationHandler {
public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'HEAD') {
throw new UnsupportedHttpError('This handler only supports HEAD operations');
throw new NotImplementedHttpError('This handler only supports HEAD operations');
}
}

View File

@@ -1,5 +1,5 @@
import type { ResourceStore } from '../../storage/ResourceStore';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import type { Patch } from '../http/Patch';
import { ResetResponseDescription } from '../http/response/ResetResponseDescription';
import type { ResponseDescription } from '../http/response/ResponseDescription';
@@ -16,7 +16,7 @@ export class PatchOperationHandler extends OperationHandler {
public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'PATCH') {
throw new UnsupportedHttpError('This handler only supports PATCH operations.');
throw new NotImplementedHttpError('This handler only supports PATCH operations.');
}
}

View File

@@ -1,6 +1,7 @@
import { getLoggerFor } from '../../logging/LogUtil';
import type { ResourceStore } from '../../storage/ResourceStore';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { CreatedResponseDescription } from '../http/response/CreatedResponseDescription';
import type { ResponseDescription } from '../http/response/ResponseDescription';
import type { Operation } from './Operation';
@@ -22,14 +23,14 @@ export class PostOperationHandler extends OperationHandler {
public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'POST') {
throw new UnsupportedHttpError('This handler only supports POST operations');
throw new NotImplementedHttpError('This handler only supports POST operations');
}
}
public async handle(input: Operation): Promise<ResponseDescription> {
if (!input.body) {
this.logger.warn('POST operations require a body');
throw new UnsupportedHttpError('POST operations require a body');
throw new BadRequestHttpError('POST operations require a body');
}
const identifier = await this.store.addResource(input.target, input.body);
return new CreatedResponseDescription(identifier);

View File

@@ -1,6 +1,7 @@
import { getLoggerFor } from '../../logging/LogUtil';
import type { ResourceStore } from '../../storage/ResourceStore';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { ResetResponseDescription } from '../http/response/ResetResponseDescription';
import type { ResponseDescription } from '../http/response/ResponseDescription';
import type { Operation } from './Operation';
@@ -22,14 +23,14 @@ export class PutOperationHandler extends OperationHandler {
public async canHandle(input: Operation): Promise<void> {
if (input.method !== 'PUT') {
throw new UnsupportedHttpError('This handler only supports PUT operations');
throw new NotImplementedHttpError('This handler only supports PUT operations');
}
}
public async handle(input: Operation): Promise<ResponseDescription> {
if (typeof input.body !== 'object') {
this.logger.warn('No body specified on PUT request');
throw new UnsupportedHttpError('PUT operations require a body');
throw new BadRequestHttpError('PUT operations require a body');
}
await this.store.setRepresentation(input.target, input.body);
return new ResetResponseDescription();

View File

@@ -1,5 +1,5 @@
import { getLoggerFor } from '../../logging/LogUtil';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import type { Operation } from '../operations/Operation';
import type { PermissionSet } from './PermissionSet';
import { PermissionsExtractor } from './PermissionsExtractor';
@@ -18,7 +18,7 @@ export class MethodPermissionsExtractor extends PermissionsExtractor {
public async canHandle({ method }: Operation): Promise<void> {
if (!SUPPORTED_METHODS.has(method)) {
this.logger.warn(`Unrecognized method ${method}`);
throw new UnsupportedHttpError(`Cannot determine permissions of ${method}`);
throw new NotImplementedHttpError(`Cannot determine permissions of ${method}`);
}
}

View File

@@ -1,5 +1,5 @@
import { Algebra } from 'sparqlalgebrajs';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import type { SparqlUpdatePatch } from '../http/SparqlUpdatePatch';
import type { Operation } from '../operations/Operation';
import type { Representation } from '../representation/Representation';
@@ -14,16 +14,16 @@ import { PermissionsExtractor } from './PermissionsExtractor';
export class SparqlPatchPermissionsExtractor extends PermissionsExtractor {
public async canHandle({ method, body }: Operation): Promise<void> {
if (method !== 'PATCH') {
throw new UnsupportedHttpError(`Cannot determine permissions of ${method}, only PATCH.`);
throw new NotImplementedHttpError(`Cannot determine permissions of ${method}, only PATCH.`);
}
if (!body) {
throw new UnsupportedHttpError('Cannot determine permissions of PATCH operations without a body.');
throw new NotImplementedHttpError('Cannot determine permissions of PATCH operations without a body.');
}
if (!this.isSparql(body)) {
throw new UnsupportedHttpError('Cannot determine permissions of non-SPARQL patches.');
throw new NotImplementedHttpError('Cannot determine permissions of non-SPARQL patches.');
}
if (!this.isDeleteInsert(body.algebra)) {
throw new UnsupportedHttpError('Cannot determine permissions of a PATCH without DELETE/INSERT.');
throw new NotImplementedHttpError('Cannot determine permissions of a PATCH without DELETE/INSERT.');
}
}

View File

@@ -6,11 +6,11 @@ import type { Representation } from '../ldp/representation/Representation';
import { RepresentationMetadata } from '../ldp/representation/RepresentationMetadata';
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { INTERNAL_QUADS } from '../util/ContentTypes';
import { BadRequestHttpError } from '../util/errors/BadRequestHttpError';
import { ConflictHttpError } from '../util/errors/ConflictHttpError';
import { MethodNotAllowedHttpError } from '../util/errors/MethodNotAllowedHttpError';
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
import { NotImplementedError } from '../util/errors/NotImplementedError';
import { UnsupportedHttpError } from '../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
import type { Guarded } from '../util/GuardedStream';
import {
ensureTrailingSlash,
@@ -136,7 +136,7 @@ export class DataAccessorBasedStore implements ResourceStore {
throw new ConflictHttpError('Input resource type does not match existing resource type.');
}
if (isContainer !== isContainerIdentifier(identifier)) {
throw new UnsupportedHttpError('Containers should have a `/` at the end of their path, resources should not.');
throw new BadRequestHttpError('Containers should have a `/` at the end of their path, resources should not.');
}
// Potentially have to create containers if it didn't exist yet
@@ -144,7 +144,7 @@ export class DataAccessorBasedStore implements ResourceStore {
}
public async modifyResource(): Promise<void> {
throw new NotImplementedError('Patches are not supported by the default store.');
throw new NotImplementedHttpError('Patches are not supported by the default store.');
}
public async deleteResource(identifier: ResourceIdentifier): Promise<void> {
@@ -246,7 +246,7 @@ export class DataAccessorBasedStore implements ResourceStore {
quads = await parseQuads(representation.data);
} catch (error: unknown) {
if (error instanceof Error) {
throw new UnsupportedHttpError(`Can only create containers with RDF data. ${error.message}`);
throw new BadRequestHttpError(`Can only create containers with RDF data. ${error.message}`);
}
throw error;
}

View File

@@ -3,7 +3,7 @@ import type { Representation } from '../ldp/representation/Representation';
import type { RepresentationPreferences } from '../ldp/representation/RepresentationPreferences';
import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
import { UnsupportedHttpError } from '../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
import type { Conditions } from './Conditions';
import type { ResourceStore } from './ResourceStore';
import type { RouterRule } from './routing/RouterRule';
@@ -53,7 +53,7 @@ export class RoutingResourceStore implements ResourceStore {
try {
return await this.rule.handleSafe({ identifier });
} catch (error: unknown) {
if (error instanceof UnsupportedHttpError) {
if (error instanceof NotImplementedHttpError) {
throw new NotFoundHttpError();
}
throw error;

View File

@@ -15,10 +15,10 @@ import type { Guarded } from '../../util/GuardedStream';
*/
export interface DataAccessor {
/**
* Should throw an UnsupportedHttpError if the DataAccessor does not support storing the given Representation.
* Should throw a NotImplementedHttpError if the DataAccessor does not support storing the given Representation.
* @param representation - Incoming Representation.
*
* @throws UnsupportedHttpError
* @throws BadRequestHttpError
* If it does not support the incoming data.
*/
canHandle: (representation: Representation) => Promise<void>;

View File

@@ -21,7 +21,7 @@ import { getLoggerFor } from '../../logging/LogUtil';
import { INTERNAL_QUADS } from '../../util/ContentTypes';
import { ConflictHttpError } from '../../util/errors/ConflictHttpError';
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { UnsupportedMediaTypeHttpError } from '../../util/errors/UnsupportedMediaTypeHttpError';
import { guardStream } from '../../util/GuardedStream';
import type { Guarded } from '../../util/GuardedStream';
@@ -126,7 +126,7 @@ export class SparqlDataAccessor implements DataAccessor {
const triples = await arrayifyStream(data) as Quad[];
const def = defaultGraph();
if (triples.some((triple): boolean => !def.equals(triple.graph))) {
throw new UnsupportedHttpError('Only triples in the default graph are supported.');
throw new NotImplementedHttpError('Only triples in the default graph are supported.');
}
// Not relevant since all content is triples

View File

@@ -1,8 +1,9 @@
import type { RepresentationPreference } from '../../ldp/representation/RepresentationPreference';
import type { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
import { INTERNAL_ALL } from '../../util/ContentTypes';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { InternalServerError } from '../../util/errors/InternalServerError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import type { RepresentationConverterArgs } from './RepresentationConverter';
/**
@@ -15,7 +16,7 @@ import type { RepresentationConverterArgs } from './RepresentationConverter';
* @param preferences - Preferences for output type.
* @param types - Media types to compare to the preferences.
*
* @throws UnsupportedHttpError
* @throws BadRequestHttpError
* If the type preferences are undefined or if there are duplicate preferences.
*
* @returns The weighted and filtered list of matching types.
@@ -23,12 +24,12 @@ import type { RepresentationConverterArgs } from './RepresentationConverter';
export const matchingTypes = (preferences: RepresentationPreferences, types: string[]):
RepresentationPreference[] => {
if (!Array.isArray(preferences.type)) {
throw new UnsupportedHttpError('Output type required for conversion.');
throw new BadRequestHttpError('Output type required for conversion.');
}
const prefMap = preferences.type.reduce((map: Record<string, number>, pref): Record<string, number> => {
if (map[pref.value]) {
throw new UnsupportedHttpError(`Duplicate type preference found: ${pref.value}`);
throw new BadRequestHttpError(`Duplicate type preference found: ${pref.value}`);
}
map[pref.value] = pref.weight;
return map;
@@ -96,12 +97,12 @@ export const validateRequestArgs = (request: RepresentationConverterArgs, suppor
supportedOut: string[]): void => {
const inType = request.representation.metadata.contentType;
if (!inType) {
throw new UnsupportedHttpError('Input type required for conversion.');
throw new BadRequestHttpError('Input type required for conversion.');
}
if (!supportedIn.some((type): boolean => matchingMediaType(inType, type))) {
throw new UnsupportedHttpError(`Can only convert from ${supportedIn} to ${supportedOut}.`);
throw new NotImplementedHttpError(`Can only convert from ${supportedIn} to ${supportedOut}.`);
}
if (matchingTypes(request.preferences, supportedOut).length <= 0) {
throw new UnsupportedHttpError(`Can only convert from ${supportedIn} to ${supportedOut}.`);
throw new NotImplementedHttpError(`Can only convert from ${supportedIn} to ${supportedOut}.`);
}
};

View File

@@ -3,7 +3,7 @@ import rdfParser from 'rdf-parse';
import type { Representation } from '../../ldp/representation/Representation';
import { RepresentationMetadata } from '../../ldp/representation/RepresentationMetadata';
import { INTERNAL_QUADS } from '../../util/ContentTypes';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { pipeSafely } from '../../util/StreamUtil';
import { CONTENT_TYPE } from '../../util/UriConstants';
import { validateRequestArgs } from './ConversionUtil';
@@ -38,7 +38,7 @@ export class RdfToQuadConverter extends TypedRepresentationConverter {
});
const pass = new PassThrough({ objectMode: true });
const data = pipeSafely(rawQuads, pass, (error): Error => new UnsupportedHttpError(error.message));
const data = pipeSafely(rawQuads, pass, (error): Error => new BadRequestHttpError(error.message));
return {
binary: false,

View File

@@ -5,7 +5,7 @@ import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdenti
import { getLoggerFor } from '../../logging/LogUtil';
import { APPLICATION_OCTET_STREAM, TEXT_TURTLE } from '../../util/ContentTypes';
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import {
encodeUriPathComponents,
ensureTrailingSlash,
@@ -82,7 +82,7 @@ export class ExtensionBasedMapper implements FileIdentifierMapper {
// Would conflict with how new extensions are stored
if (/\$\.\w+$/u.test(filePath)) {
this.logger.warn(`Identifier ${identifier.path} contains a dollar sign before its extension`);
throw new UnsupportedHttpError('Identifiers cannot contain a dollar sign before their extension');
throw new NotImplementedHttpError('Identifiers cannot contain a dollar sign before their extension');
}
// Existing file
@@ -123,7 +123,7 @@ export class ExtensionBasedMapper implements FileIdentifierMapper {
const extension = mime.extension(contentType);
if (!extension) {
this.logger.warn(`No extension found for ${contentType}`);
throw new UnsupportedHttpError(`Unsupported content type ${contentType}`);
throw new NotImplementedHttpError(`Unsupported content type ${contentType}`);
}
filePath += `$.${extension}`;
}

View File

@@ -1,7 +1,7 @@
import { posix } from 'path';
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import { getLoggerFor } from '../../logging/LogUtil';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import {
encodeUriPathComponents,
ensureTrailingSlash, isContainerIdentifier,
@@ -45,7 +45,7 @@ export class FixedContentTypeMapper implements FileIdentifierMapper {
// Only allow the configured content type
if (contentType && contentType !== this.contentType) {
throw new UnsupportedHttpError(`Unsupported content type ${contentType}, only ${this.contentType} is allowed`);
throw new NotImplementedHttpError(`Unsupported content type ${contentType}, only ${this.contentType} is allowed`);
}
this.logger.info(`The path for ${identifier.path} is ${filePath}`);

View File

@@ -1,8 +1,8 @@
import { posix } from 'path';
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import type { Logger } from '../../logging/Logger';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { NotFoundHttpError } from '../../util/errors/NotFoundHttpError';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { decodeUriPathComponents } from '../../util/PathUtil';
const { join: joinPath } = posix;
@@ -40,7 +40,7 @@ export const getRelativePath = (baseRequestURI: string, identifier: ResourceIden
/**
* Check if the given relative path is valid.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* If the relative path is invalid.
*
* @param path - A relative path, as generated by {@link getRelativePath}.
@@ -50,11 +50,11 @@ export const getRelativePath = (baseRequestURI: string, identifier: ResourceIden
export const validateRelativePath = (path: string, identifier: ResourceIdentifier, logger: Logger): void => {
if (!path.startsWith('/')) {
logger.warn(`URL ${identifier.path} needs a / after the base`);
throw new UnsupportedHttpError('URL needs a / after the base');
throw new BadRequestHttpError('URL needs a / after the base');
}
if (path.includes('/..')) {
logger.warn(`Disallowed /.. segment in URL ${identifier.path}.`);
throw new UnsupportedHttpError('Disallowed /.. segment in URL');
throw new BadRequestHttpError('Disallowed /.. segment in URL');
}
};

View File

@@ -10,7 +10,7 @@ import { RepresentationMetadata } from '../../ldp/representation/RepresentationM
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import { getLoggerFor } from '../../logging/LogUtil';
import { INTERNAL_QUADS } from '../../util/ContentTypes';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { guardStream } from '../../util/GuardedStream';
import type { ResourceLocker } from '../../util/locking/ResourceLocker';
import { CONTENT_TYPE } from '../../util/UriConstants';
@@ -35,7 +35,7 @@ export class SparqlUpdatePatchHandler extends PatchHandler {
public async canHandle(input: {identifier: ResourceIdentifier; patch: SparqlUpdatePatch}): Promise<void> {
if (typeof input.patch.algebra !== 'object') {
throw new UnsupportedHttpError('Only SPARQL update patch requests are supported');
throw new NotImplementedHttpError('Only SPARQL update patch requests are supported');
}
}
@@ -43,7 +43,7 @@ export class SparqlUpdatePatchHandler extends PatchHandler {
const op = input.patch.algebra;
if (!this.isDeleteInsert(op)) {
this.logger.warn(`Unsupported operation: ${op.type}`);
throw new UnsupportedHttpError('Only DELETE/INSERT SPARQL update operations are supported');
throw new NotImplementedHttpError('Only DELETE/INSERT SPARQL update operations are supported');
}
const def = defaultGraph();
@@ -52,16 +52,16 @@ export class SparqlUpdatePatchHandler extends PatchHandler {
if (!deletes.every((pattern): boolean => pattern.graph.equals(def))) {
this.logger.warn('GRAPH statement in DELETE clause');
throw new UnsupportedHttpError('GRAPH statements are not supported');
throw new NotImplementedHttpError('GRAPH statements are not supported');
}
if (!inserts.every((pattern): boolean => pattern.graph.equals(def))) {
this.logger.warn('GRAPH statement in INSERT clause');
throw new UnsupportedHttpError('GRAPH statements are not supported');
throw new NotImplementedHttpError('GRAPH statements are not supported');
}
if (op.where ?? deletes.some((pattern): boolean =>
someTerms(pattern, (term): boolean => term.termType === 'Variable'))) {
this.logger.warn('WHERE statements are not supported');
throw new UnsupportedHttpError('WHERE statements are not supported');
throw new NotImplementedHttpError('WHERE statements are not supported');
}
const lock = await this.locker.acquire(input.identifier);

View File

@@ -1,6 +1,7 @@
import type { Representation } from '../../ldp/representation/Representation';
import type { ResourceIdentifier } from '../../ldp/representation/ResourceIdentifier';
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
import { BadRequestHttpError } from '../../util/errors/BadRequestHttpError';
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
import { trimTrailingSlashes } from '../../util/PathUtil';
import type { ResourceStore } from '../ResourceStore';
import { RouterRule } from './RouterRule';
@@ -47,7 +48,7 @@ export class RegexRouterRule extends RouterRule {
return this.regexes.get(regex)!;
}
}
throw new UnsupportedHttpError(`No stored regexes match ${identifier.path}`);
throw new NotImplementedHttpError(`No stored regexes match ${identifier.path}`);
}
/**
@@ -55,7 +56,7 @@ export class RegexRouterRule extends RouterRule {
*/
private toRelative(identifier: ResourceIdentifier): string {
if (!identifier.path.startsWith(this.base)) {
throw new UnsupportedHttpError(`Identifiers need to start with ${this.base}`);
throw new BadRequestHttpError(`Identifiers need to start with ${this.base}`);
}
return identifier.path.slice(this.base.length);
}

View File

@@ -1,8 +1,8 @@
import { getLoggerFor } from '../logging/LogUtil';
import type { AsyncHandler } from './AsyncHandler';
import { BadRequestHttpError } from './errors/BadRequestHttpError';
import { HttpError } from './errors/HttpError';
import { InternalServerError } from './errors/InternalServerError';
import { UnsupportedHttpError } from './errors/UnsupportedHttpError';
/**
* Handler that combines several other handlers,
@@ -106,6 +106,6 @@ export class FirstCompositeHandler<TIn, TOut> implements AsyncHandler<TIn, TOut>
if (errors.some((error): boolean => error.statusCode >= 500)) {
throw new InternalServerError(message);
}
throw new UnsupportedHttpError(message);
throw new BadRequestHttpError(message);
}
}

View File

@@ -1,6 +1,6 @@
import { getLoggerFor } from '../logging/LogUtil';
import type { HttpResponse } from '../server/HttpResponse';
import { UnsupportedHttpError } from './errors/UnsupportedHttpError';
import { BadRequestHttpError } from './errors/BadRequestHttpError';
const logger = getLoggerFor('HeaderUtil');
@@ -110,7 +110,7 @@ export const transformQuotedStrings = (input: string): { result: string; replace
// Not all characters allowed in quoted strings, see BNF above
if (!/^"(?:[\t !\u0023-\u005B\u005D-\u007E\u0080-\u00FF]|(?:\\[\t\u0020-\u007E\u0080-\u00FF]))*"$/u.test(match)) {
logger.warn(`Invalid quoted string in header: ${match}`);
throw new UnsupportedHttpError(`Invalid quoted string in header: ${match}`);
throw new BadRequestHttpError(`Invalid quoted string in header: ${match}`);
}
const replacement = `"${idx}"`;
replacements[replacement] = match.slice(1, -1);
@@ -135,13 +135,13 @@ export const splitAndClean = (input: string): string[] =>
*
* @param qvalue - Input qvalue string (so "q=....").
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid syntax.
*/
const testQValue = (qvalue: string): void => {
if (!/^(?:(?:0(?:\.\d{0,3})?)|(?:1(?:\.0{0,3})?))$/u.test(qvalue)) {
logger.warn(`Invalid q value: ${qvalue}`);
throw new UnsupportedHttpError(
throw new BadRequestHttpError(
`Invalid q value: ${qvalue} does not match ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] ).`,
);
}
@@ -154,7 +154,7 @@ const testQValue = (qvalue: string): void => {
* @param replacements - The double quoted strings that need to be replaced.
*
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid parameter syntax.
*
* @returns An array of name/value objects corresponding to the parameters.
@@ -168,7 +168,7 @@ export const parseParameters = (parameters: string[], replacements: Record<strin
// second part is optional for certain parameters
if (!(token.test(name) && (!rawValue || /^"\d+"$/u.test(rawValue) || token.test(rawValue)))) {
logger.warn(`Invalid parameter value: ${name}=${replacements[rawValue] || rawValue}`);
throw new UnsupportedHttpError(
throw new BadRequestHttpError(
`Invalid parameter value: ${name}=${replacements[rawValue] || rawValue} ` +
`does not match (token ( "=" ( token / quoted-string ))?). `,
);
@@ -191,7 +191,7 @@ export const parseParameters = (parameters: string[], replacements: Record<strin
* @param part - A string corresponding to a media range and its corresponding parameters.
* @param replacements - The double quoted strings that need to be replaced.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid type, qvalue or parameter syntax.
*
* @returns {@link Accept} object corresponding to the header string.
@@ -203,7 +203,7 @@ const parseAcceptPart = (part: string, replacements: Record<string, string>): Ac
const [ type, subtype ] = range.split('/');
if (!type || !subtype || !token.test(type) || !token.test(subtype)) {
logger.warn(`Invalid Accept range: ${range}`);
throw new UnsupportedHttpError(
throw new BadRequestHttpError(
`Invalid Accept range: ${range} does not match ( "*/*" / ( token "/" "*" ) / ( token "/" token ) )`,
);
}
@@ -222,7 +222,7 @@ const parseAcceptPart = (part: string, replacements: Record<string, string>): Ac
} else {
if (!value && map !== extensionParams) {
logger.warn(`Invalid Accept parameter ${name}`);
throw new UnsupportedHttpError(`Invalid Accept parameter ${name}: ` +
throw new BadRequestHttpError(`Invalid Accept parameter ${name}: ` +
`Accept parameter values are not optional when preceding the q value`);
}
map[name] = value || '';
@@ -243,7 +243,7 @@ const parseAcceptPart = (part: string, replacements: Record<string, string>): Ac
* Parses an Accept-* header where each part is only a value and a weight, so roughly /.*(q=.*)?/ separated by commas.
* @param input - Input header string.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid qvalue syntax.
*
* @returns An array of ranges and weights.
@@ -257,7 +257,7 @@ const parseNoParameters = (input: string): { range: string; weight: number }[] =
if (qvalue) {
if (!qvalue.startsWith('q=')) {
logger.warn(`Only q parameters are allowed in ${input}`);
throw new UnsupportedHttpError(`Only q parameters are allowed in ${input}`);
throw new BadRequestHttpError(`Only q parameters are allowed in ${input}`);
}
const val = qvalue.slice(2);
testQValue(val);
@@ -274,7 +274,7 @@ const parseNoParameters = (input: string): { range: string; weight: number }[] =
*
* @param input - The Accept header string.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid header syntax.
*
* @returns An array of {@link Accept} objects, sorted by weight.
@@ -292,7 +292,7 @@ export const parseAccept = (input: string): Accept[] => {
*
* @param input - The Accept-Charset header string.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid header syntax.
*
* @returns An array of {@link AcceptCharset} objects, sorted by weight.
@@ -302,7 +302,7 @@ export const parseAcceptCharset = (input: string): AcceptCharset[] => {
results.forEach((result): void => {
if (!token.test(result.range)) {
logger.warn(`Invalid Accept-Charset range: ${result.range}`);
throw new UnsupportedHttpError(
throw new BadRequestHttpError(
`Invalid Accept-Charset range: ${result.range} does not match (content-coding / "identity" / "*")`,
);
}
@@ -315,7 +315,7 @@ export const parseAcceptCharset = (input: string): AcceptCharset[] => {
*
* @param input - The Accept-Encoding header string.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid header syntax.
*
* @returns An array of {@link AcceptEncoding} objects, sorted by weight.
@@ -325,7 +325,7 @@ export const parseAcceptEncoding = (input: string): AcceptEncoding[] => {
results.forEach((result): void => {
if (!token.test(result.range)) {
logger.warn(`Invalid Accept-Encoding range: ${result.range}`);
throw new UnsupportedHttpError(`Invalid Accept-Encoding range: ${result.range} does not match (charset / "*")`);
throw new BadRequestHttpError(`Invalid Accept-Encoding range: ${result.range} does not match (charset / "*")`);
}
});
return results;
@@ -336,7 +336,7 @@ export const parseAcceptEncoding = (input: string): AcceptEncoding[] => {
*
* @param input - The Accept-Language header string.
*
* @throws {@link UnsupportedHttpError}
* @throws {@link BadRequestHttpError}
* Thrown on invalid header syntax.
*
* @returns An array of {@link AcceptLanguage} objects, sorted by weight.
@@ -349,7 +349,7 @@ export const parseAcceptLanguage = (input: string): AcceptLanguage[] => {
logger.warn(
`Invalid Accept-Language range: ${result.range}`,
);
throw new UnsupportedHttpError(
throw new BadRequestHttpError(
`Invalid Accept-Language range: ${result.range} does not match ((1*8ALPHA *("-" 1*8alphanum)) / "*")`,
);
}

View File

@@ -4,12 +4,12 @@ import { HttpError } from './HttpError';
* An error thrown when incoming data is not supported.
* Probably because an {@link AsyncHandler} returns false on the canHandle call.
*/
export class UnsupportedHttpError extends HttpError {
export class BadRequestHttpError extends HttpError {
/**
* Default message is 'The given input is not supported by the server configuration.'.
* @param message - Optional, more specific, message.
*/
public constructor(message?: string) {
super(400, 'UnsupportedHttpError', message ?? 'The given input is not supported by the server configuration.');
super(400, 'BadRequestHttpError', message ?? 'The given input is not supported by the server configuration.');
}
}

View File

@@ -3,8 +3,8 @@ import { HttpError } from './HttpError';
* The server either does not recognize the request method, or it lacks the ability to fulfil the request.
* Usually this implies future availability (e.g., a new feature of a web-service API).
*/
export class NotImplementedError extends HttpError {
export class NotImplementedHttpError extends HttpError {
public constructor(message?: string) {
super(501, 'NotImplementedError', message);
super(501, 'NotImplementedHttpError', message);
}
}