refactor: Simplify AcceptPreferenceParser.

This commit is contained in:
Ruben Verborgh 2021-01-05 01:17:12 +01:00
parent 0bd73115cc
commit 4aed8c8b4c
2 changed files with 39 additions and 39 deletions

View File

@ -5,52 +5,37 @@ import {
parseAcceptCharset, parseAcceptCharset,
parseAcceptEncoding, parseAcceptEncoding,
parseAcceptLanguage, parseAcceptLanguage,
parseAcceptDateTime,
} from '../../util/HeaderUtil'; } from '../../util/HeaderUtil';
import type { RepresentationPreferences } from '../representation/RepresentationPreferences'; import type { RepresentationPreferences } from '../representation/RepresentationPreferences';
import { PreferenceParser } from './PreferenceParser'; import { PreferenceParser } from './PreferenceParser';
const parsers: {
name: keyof RepresentationPreferences;
header: string;
parse: (value: string) => AcceptHeader[];
}[] = [
{ name: 'type', header: 'accept', parse: parseAccept },
{ name: 'charset', header: 'accept-charset', parse: parseAcceptCharset },
{ name: 'encoding', header: 'accept-encoding', parse: parseAcceptEncoding },
{ name: 'language', header: 'accept-language', parse: parseAcceptLanguage },
{ name: 'datetime', header: 'accept-datetime', parse: parseAcceptDateTime },
];
/** /**
* Extracts preferences from the accept-* headers from an incoming {@link HttpRequest}. * Extracts preferences from the Accept-* headers from an incoming {@link HttpRequest}.
* Supports Accept, Accept-Charset, Accept-Encoding, Accept-Language and Accept-DateTime. * Supports Accept, Accept-Charset, Accept-Encoding, Accept-Language and Accept-DateTime.
*/ */
export class AcceptPreferenceParser extends PreferenceParser { export class AcceptPreferenceParser extends PreferenceParser {
public constructor() { public async handle({ headers }: HttpRequest): Promise<RepresentationPreferences> {
super(); const preferences: RepresentationPreferences = {};
for (const { name, header, parse } of parsers) {
const value = headers[header];
if (typeof value === 'string') {
preferences[name] = Object.fromEntries(parse(value)
.map(({ range, weight }): [string, number] => [ range, weight ]));
} }
public async handle(input: HttpRequest): Promise<RepresentationPreferences> {
const result: RepresentationPreferences = {};
const headers:
{ [T in keyof RepresentationPreferences]: { val?: string; func: (inp: string) => AcceptHeader[] }} = {
type: { val: input.headers.accept, func: parseAccept },
charset: { val: input.headers['accept-charset'] as string, func: parseAcceptCharset },
encoding: { val: input.headers['accept-encoding'] as string, func: parseAcceptEncoding },
language: { val: input.headers['accept-language'], func: parseAcceptLanguage },
};
(Object.keys(headers) as (keyof RepresentationPreferences)[]).forEach((key): void => {
const preferences = this.parseHeader(headers[key]!.func, headers[key]!.val);
if (preferences.length > 0) {
result[key] = Object.fromEntries(preferences);
} }
}); return preferences;
// Accept-DateTime is currently specified to simply have a datetime as value
if (input.headers['accept-datetime']) {
result.datetime = { [input.headers['accept-datetime'] as string]: 1 };
}
return result;
}
/**
* Converts a header string using the given parse function to {@link RepresentationPreference}[].
* @param input - Input header string.
* @param parseFunction - Function that converts header string to {@link AcceptHeader}.
*
* @returns A list of preferences. Returns an empty list if input was not defined.
*/
private parseHeader(parseFunction: (input: string) => AcceptHeader[], input?: string): [string, number][] {
return (input ? parseFunction(input) : [])
.map(({ range, weight }): [string, number] => [ range, weight ]);
} }
} }

View File

@ -93,6 +93,11 @@ export interface AcceptEncoding extends AcceptHeader { }
*/ */
export interface AcceptLanguage extends AcceptHeader { } export interface AcceptLanguage extends AcceptHeader { }
/**
* Contents of an HTTP Accept-Datetime header.
*/
export interface AcceptDatetime extends AcceptHeader { }
// REUSED REGEXES // REUSED REGEXES
const token = /^[a-zA-Z0-9!#$%&'*+-.^_`|~]+$/u; const token = /^[a-zA-Z0-9!#$%&'*+-.^_`|~]+$/u;
@ -248,10 +253,10 @@ const parseAcceptPart = (part: string, replacements: Record<string, string>): Ac
* *
* @returns An array of ranges and weights. * @returns An array of ranges and weights.
*/ */
const parseNoParameters = (input: string): { range: string; weight: number }[] => { const parseNoParameters = (input: string): AcceptHeader[] => {
const parts = splitAndClean(input); const parts = splitAndClean(input);
return parts.map((part): { range: string; weight: number } => { return parts.map((part): AcceptHeader => {
const [ range, qvalue ] = part.split(';').map((param): string => param.trim()); const [ range, qvalue ] = part.split(';').map((param): string => param.trim());
const result = { range, weight: 1 }; const result = { range, weight: 1 };
if (qvalue) { if (qvalue) {
@ -357,6 +362,16 @@ export const parseAcceptLanguage = (input: string): AcceptLanguage[] => {
return results; return results;
}; };
/**
* Parses an Accept-DateTime header string.
*
* @param input - The Accept-DateTime header string.
*
* @returns An array with a single {@link AcceptDatetime} object.
*/
export const parseAcceptDateTime = (input: string): AcceptDatetime[] =>
[{ range: input, weight: 1 }];
/** /**
* Adds a header value without overriding previous values. * Adds a header value without overriding previous values.
*/ */