mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
Merge branch 'main' into versions/next-major
This commit is contained in:
@@ -3,7 +3,7 @@ import type { NamedNode } from '@rdfjs/types';
|
||||
import arrayifyStream from 'arrayify-stream';
|
||||
import type { ParserOptions } from 'n3';
|
||||
import { StreamParser, StreamWriter } from 'n3';
|
||||
import type { Quad } from 'rdf-js';
|
||||
import type { Quad, Term } from 'rdf-js';
|
||||
import type { Guarded } from './GuardedStream';
|
||||
import { guardedStreamFrom, pipeSafely } from './StreamUtil';
|
||||
import { toNamedTerm } from './TermUtil';
|
||||
@@ -45,6 +45,18 @@ export function uniqueQuads(quads: Quad[]): Quad[] {
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a term to a number. Returns undefined if the term was undefined.
|
||||
*
|
||||
* @param term - Term to parse.
|
||||
* @param radix - Radix to use when parsing. Default is 10.
|
||||
*/
|
||||
export function termToInt(term?: Term, radix = 10): number | undefined {
|
||||
if (term) {
|
||||
return Number.parseInt(term.value, radix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a triple pattern to be used as a filter.
|
||||
*/
|
||||
|
||||
107
src/util/SliceStream.ts
Normal file
107
src/util/SliceStream.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type { Readable, TransformCallback, TransformOptions } from 'stream';
|
||||
import { Transform } from 'stream';
|
||||
import { RangeNotSatisfiedHttpError } from './errors/RangeNotSatisfiedHttpError';
|
||||
import { pipeSafely } from './StreamUtil';
|
||||
|
||||
export interface SliceStreamOptions extends TransformOptions {
|
||||
start: number;
|
||||
end?: number;
|
||||
size?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A stream that slices a part out of another stream.
|
||||
* `start` and `end` are inclusive.
|
||||
* If `end` is not defined it is until the end of the stream.
|
||||
*
|
||||
* Negative `start` values can be used to instead slice that many streams off the end of the stream.
|
||||
* This requires the `size` field to be defined.
|
||||
*
|
||||
* Both object and non-object streams are supported.
|
||||
* This needs to be explicitly specified,
|
||||
* as the class makes no assumptions based on the object mode of the source stream.
|
||||
*/
|
||||
export class SliceStream extends Transform {
|
||||
protected readonly source: Readable;
|
||||
protected remainingSkip: number;
|
||||
protected remainingRead: number;
|
||||
|
||||
public constructor(source: Readable, options: SliceStreamOptions) {
|
||||
super(options);
|
||||
let start = options.start;
|
||||
const end = options.end ?? Number.POSITIVE_INFINITY;
|
||||
if (options.start < 0) {
|
||||
if (typeof options.size !== 'number') {
|
||||
throw new RangeNotSatisfiedHttpError('Slicing data at the end of a stream requires a known size.');
|
||||
} else {
|
||||
// `start` is a negative number here so need to add
|
||||
start = options.size + start;
|
||||
}
|
||||
}
|
||||
|
||||
if (start >= end) {
|
||||
throw new RangeNotSatisfiedHttpError('Range start should be less than end.');
|
||||
}
|
||||
|
||||
// Not using `end` variable as that could be infinity
|
||||
if (typeof options.end === 'number' && typeof options.size === 'number' && options.end >= options.size) {
|
||||
throw new RangeNotSatisfiedHttpError('Range end should be less than the total size.');
|
||||
}
|
||||
|
||||
this.remainingSkip = start;
|
||||
// End value is inclusive
|
||||
this.remainingRead = end - options.start + 1;
|
||||
|
||||
this.source = source;
|
||||
pipeSafely(source, this);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
public _transform(chunk: any, encoding: BufferEncoding, callback: TransformCallback): void {
|
||||
this.source.pause();
|
||||
if (this.writableObjectMode) {
|
||||
this.objectSlice(chunk);
|
||||
} else {
|
||||
this.binarySlice(chunk);
|
||||
}
|
||||
// eslint-disable-next-line callback-return
|
||||
callback();
|
||||
this.source.resume();
|
||||
}
|
||||
|
||||
protected binarySlice(chunk: Buffer): void {
|
||||
let length = chunk.length;
|
||||
if (this.remainingSkip > 0) {
|
||||
chunk = chunk.slice(this.remainingSkip);
|
||||
this.remainingSkip -= length - chunk.length;
|
||||
length = chunk.length;
|
||||
}
|
||||
if (length > 0 && this.remainingSkip <= 0) {
|
||||
chunk = chunk.slice(0, this.remainingRead);
|
||||
this.push(chunk);
|
||||
this.remainingRead -= length;
|
||||
this.checkEnd();
|
||||
}
|
||||
}
|
||||
|
||||
protected objectSlice(chunk: unknown): void {
|
||||
if (this.remainingSkip > 0) {
|
||||
this.remainingSkip -= 1;
|
||||
} else {
|
||||
this.remainingRead -= 1;
|
||||
this.push(chunk);
|
||||
this.checkEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop piping the source stream and close everything once the slice is finished.
|
||||
*/
|
||||
protected checkEnd(): void {
|
||||
if (this.remainingRead <= 0) {
|
||||
this.source.unpipe();
|
||||
this.end();
|
||||
this.source.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,8 +266,12 @@ export const SOLID_ERROR_TERM = createVocabulary('urn:npm:solid:community-server
|
||||
);
|
||||
|
||||
export const SOLID_HTTP = createVocabulary('urn:npm:solid:community-server:http:',
|
||||
// Unit, start, and end are used for range headers
|
||||
'end',
|
||||
'location',
|
||||
'start',
|
||||
'slug',
|
||||
'unit',
|
||||
);
|
||||
|
||||
export const SOLID_META = createVocabulary('urn:npm:solid:community-server:meta:',
|
||||
|
||||
19
src/util/errors/RangeNotSatisfiedHttpError.ts
Normal file
19
src/util/errors/RangeNotSatisfiedHttpError.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { HttpErrorOptions } from './HttpError';
|
||||
import { generateHttpErrorClass } from './HttpError';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
const BaseHttpError = generateHttpErrorClass(416, 'RangeNotSatisfiedHttpError');
|
||||
|
||||
/**
|
||||
* An error thrown when the requested range is not supported.
|
||||
*/
|
||||
export class RangeNotSatisfiedHttpError extends BaseHttpError {
|
||||
/**
|
||||
* Default message is 'The requested range is not supported.'.
|
||||
* @param message - Optional, more specific, message.
|
||||
* @param options - Optional error options.
|
||||
*/
|
||||
public constructor(message?: string, options?: HttpErrorOptions) {
|
||||
super(message ?? 'The requested range is not supported.', options);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user