mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Support conditions for GET/HEAD requests
* fix: updated WrappedExpiringStorage tests and timer.unref calls * fix: removed finalizable configs and inheritors that only used timer * fix: updated test function to test setSafeInterval and timer.unref * fix: added NotModifiedHttpError class * fix: added 304 error test to HttpError test file * fix: 304 errors when making read request with matching ETag * Update src/util/errors/NotModifiedHttpError.ts Co-authored-by: Ted Thibodeau Jr <tthibodeau@openlinksw.com> * fix: updated tests * fix: try notMatchesEtag in test * fix: DataAccessorBasedStore test passes * fix: removed conditions check and added extra test --------- Co-authored-by: Ted Thibodeau Jr <tthibodeau@openlinksw.com>
This commit is contained in:
@@ -7,6 +7,7 @@ import { BasicRepresentation } from '../http/representation/BasicRepresentation'
|
||||
import type { Patch } from '../http/representation/Patch';
|
||||
import type { Representation } from '../http/representation/Representation';
|
||||
import { RepresentationMetadata } from '../http/representation/RepresentationMetadata';
|
||||
import type { RepresentationPreferences } from '../http/representation/RepresentationPreferences';
|
||||
import type { ResourceIdentifier } from '../http/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { INTERNAL_QUADS } from '../util/ContentTypes';
|
||||
@@ -17,6 +18,7 @@ import { ForbiddenHttpError } from '../util/errors/ForbiddenHttpError';
|
||||
import { MethodNotAllowedHttpError } from '../util/errors/MethodNotAllowedHttpError';
|
||||
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
|
||||
import { NotImplementedHttpError } from '../util/errors/NotImplementedHttpError';
|
||||
import { NotModifiedHttpError } from '../util/errors/NotModifiedHttpError';
|
||||
import { PreconditionFailedHttpError } from '../util/errors/PreconditionFailedHttpError';
|
||||
import type { IdentifierStrategy } from '../util/identifiers/IdentifierStrategy';
|
||||
import { concat } from '../util/IterableUtil';
|
||||
@@ -103,7 +105,8 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
}
|
||||
}
|
||||
|
||||
public async getRepresentation(identifier: ResourceIdentifier): Promise<Representation> {
|
||||
public async getRepresentation(identifier: ResourceIdentifier,
|
||||
preferences?: RepresentationPreferences, conditions?: Conditions): Promise<Representation> {
|
||||
this.validateIdentifier(identifier);
|
||||
let isMetadata = false;
|
||||
|
||||
@@ -116,6 +119,8 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
let metadata = await this.accessor.getMetadata(identifier);
|
||||
let representation: Representation;
|
||||
|
||||
this.validateConditions(true, conditions, metadata);
|
||||
|
||||
// Potentially add auxiliary related metadata
|
||||
// Solid, §4.3: "Clients can discover auxiliary resources associated with a subject resource by making an HTTP HEAD
|
||||
// or GET request on the target URL, and checking the HTTP Link header with the rel parameter"
|
||||
@@ -181,7 +186,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
throw new MethodNotAllowedHttpError([ 'POST' ], 'The given path is not a container.');
|
||||
}
|
||||
|
||||
this.validateConditions(conditions, parentMetadata);
|
||||
this.validateConditions(false, conditions, parentMetadata);
|
||||
|
||||
// Solid, §5.1: "Servers MAY allow clients to suggest the URI of a resource created through POST,
|
||||
// using the HTTP Slug header as defined in [RFC5023].
|
||||
@@ -246,7 +251,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
await this.accessor.canHandle(representation);
|
||||
}
|
||||
|
||||
this.validateConditions(conditions, oldMetadata);
|
||||
this.validateConditions(false, conditions, oldMetadata);
|
||||
|
||||
if (this.metadataStrategy.isAuxiliaryIdentifier(identifier)) {
|
||||
return await this.writeMetadata(identifier, representation);
|
||||
@@ -268,7 +273,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
}
|
||||
}
|
||||
|
||||
this.validateConditions(conditions, metadata);
|
||||
this.validateConditions(false, conditions, metadata);
|
||||
}
|
||||
|
||||
throw new NotImplementedHttpError('Patches are not supported by the default store.');
|
||||
@@ -309,7 +314,7 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
throw new ConflictHttpError('Can only delete empty containers.');
|
||||
}
|
||||
|
||||
this.validateConditions(conditions, metadata);
|
||||
this.validateConditions(false, conditions, metadata);
|
||||
|
||||
// Solid, §5.4: "When a contained resource is deleted,
|
||||
// the server MUST also delete the associated auxiliary resources"
|
||||
@@ -347,10 +352,13 @@ export class DataAccessorBasedStore implements ResourceStore {
|
||||
/**
|
||||
* Verify if the given metadata matches the conditions.
|
||||
*/
|
||||
protected validateConditions(conditions?: Conditions, metadata?: RepresentationMetadata): void {
|
||||
protected validateConditions(read: boolean, conditions?: Conditions, metadata?: RepresentationMetadata): void {
|
||||
// The 412 (Precondition Failed) status code indicates
|
||||
// that one or more conditions given in the request header fields evaluated to false when tested on the server.
|
||||
if (conditions && !conditions.matchesMetadata(metadata)) {
|
||||
if (read) {
|
||||
throw new NotModifiedHttpError();
|
||||
}
|
||||
throw new PreconditionFailedHttpError();
|
||||
}
|
||||
}
|
||||
|
||||
14
src/util/errors/NotModifiedHttpError.ts
Normal file
14
src/util/errors/NotModifiedHttpError.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { HttpErrorOptions } from './HttpError';
|
||||
import { generateHttpErrorClass } from './HttpError';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const BaseHttpError = generateHttpErrorClass(304, 'NotModifiedHttpError');
|
||||
|
||||
/**
|
||||
* An error is thrown when a request conflicts with the current state of the server.
|
||||
*/
|
||||
export class NotModifiedHttpError extends BaseHttpError {
|
||||
public constructor(message?: string, options?: HttpErrorOptions) {
|
||||
super(message, options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user