mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Reject unacceptable content types
This commit is contained in:
@@ -1,26 +1,51 @@
|
||||
import type { RepresentationPreference } from '../../ldp/representation/RepresentationPreference';
|
||||
import type { RepresentationPreferences } from '../../ldp/representation/RepresentationPreferences';
|
||||
import { InternalServerError } from '../../util/errors/InternalServerError';
|
||||
import { UnsupportedHttpError } from '../../util/errors/UnsupportedHttpError';
|
||||
import { matchingMediaType } from '../../util/Util';
|
||||
import type { RepresentationConverterArgs } from './RepresentationConverter';
|
||||
|
||||
/**
|
||||
* Filters out the media types from the preferred types that correspond to one of the supported types.
|
||||
* Filters media types based on the given preferences.
|
||||
* Based on RFC 7231 - Content negotiation.
|
||||
*
|
||||
* @param preferences - Preferences for output type.
|
||||
* @param supported - Types supported by the parser.
|
||||
* @param types - Media types to compare to the preferences.
|
||||
*
|
||||
* @throws UnsupportedHttpError
|
||||
* If the type preferences are undefined.
|
||||
* If the type preferences are undefined or if there are duplicate preferences.
|
||||
*
|
||||
* @returns The filtered list of preferences.
|
||||
* @returns The weighted and filtered list of matching types.
|
||||
*/
|
||||
export const matchingTypes = (preferences: RepresentationPreferences, supported: string[]):
|
||||
export const matchingTypes = (preferences: RepresentationPreferences, types: string[]):
|
||||
RepresentationPreference[] => {
|
||||
if (!Array.isArray(preferences.type)) {
|
||||
throw new UnsupportedHttpError('Output type required for conversion.');
|
||||
}
|
||||
return preferences.type.filter(({ value, weight }): boolean => weight > 0 &&
|
||||
supported.some((type): boolean => matchingMediaType(value, type)));
|
||||
|
||||
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}`);
|
||||
}
|
||||
map[pref.value] = pref.weight;
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
// RFC 7231
|
||||
// Media ranges can be overridden by more specific media ranges or
|
||||
// specific media types. If more than one media range applies to a
|
||||
// given type, the most specific reference has precedence.
|
||||
const weightedSupported = types.map((type): RepresentationPreference => {
|
||||
const match = /^([^/]+)\/([^\s;]+)/u.exec(type);
|
||||
if (!match) {
|
||||
throw new InternalServerError(`Unexpected type preference: ${type}`);
|
||||
}
|
||||
const [ , main, sub ] = match;
|
||||
const weight = prefMap[type] ?? prefMap[`${main}/${sub}`] ?? prefMap[`${main}/*`] ?? prefMap['*/*'] ?? 0;
|
||||
return { value: type, weight };
|
||||
});
|
||||
|
||||
return weightedSupported.filter((preference): boolean => preference.weight !== 0);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user