@v4fire/core
Version:
V4Fire core library
230 lines (193 loc) • 5.25 kB
text/typescript
/*!
* V4Fire Core
* https://github.com/V4Fire/Core
*
* Released under the MIT license
* https://github.com/V4Fire/Core/blob/master/LICENSE
*/
/**
* [[include:core/request/headers/README.md]]
* @packageDocumentation
*/
import { applyQueryForStr } from 'core/request/helpers/interpolation';
import { requestQuery } from 'core/request/headers/const';
import type { RawHeaders, HeadersForEachCb } from 'core/request/headers/interface';
export * from 'core/request/headers/interface';
/**
* Class to create a set of HTTP headers
*/
export default class V4Headers {
/**
* Request query object (to interpolate values from headers)
*/
[requestQuery]?: Dictionary;
/**
* @param [headers] - headers to initialize
* @param [query] - request query object (to interpolate values from headers)
*/
constructor(headers?: RawHeaders, query?: Dictionary) {
Object.defineProperty(this, requestQuery, {
enumerable: false,
configurable: false,
writable: true,
value: query
});
if (Object.isString(headers)) {
let
lastHeaderName = '';
for (let o = headers.split(/[\r\n]+/), i = 0; i < o.length; i++) {
const
header = o[i].trim();
if (header === '') {
continue;
}
const
headerChunks = header.split(':', 2);
if (headerChunks.length === 2) {
lastHeaderName = headerChunks[0];
headerChunks.shift();
}
const
value = <CanUndef<string>>headerChunks[0];
if (value != null) {
this.append(lastHeaderName, value.split(','));
}
}
} else if (headers != null) {
let
iter;
// eslint-disable-next-line @typescript-eslint/unbound-method
if (headers instanceof V4Headers || Object.isFunction(headers.entries)) {
iter = Object.cast<Headers>(headers).entries();
} else {
iter = Object.entries(headers);
}
for (const [name, value] of iter) {
if (value != null) {
this.set(name, value);
}
}
}
}
/**
* Returns an iterator over headers.
* It produces tuples with headers' names and values.
*/
[Symbol.iterator](): IterableIterator<[string, string]> {
return this.entries();
}
/**
* Returns a header value by the specified name
* @param name
*/
get(name: string): string | null {
return this[this.normalizeHeaderName(name)] ?? null;
}
/**
* Returns true if the structure contains a header by the specified name
* @param name
*/
has(name: string): boolean {
return this.normalizeHeaderName(name) in this;
}
/**
* Sets a new header value by the specified name.
* To set multiple values for one header, provide its value as a list of values.
*
* @param name
* @param value
*/
set(name: string, value: CanArray<string>): void {
const
normalizedName = this.normalizeHeaderName(name);
if (normalizedName === '') {
return;
}
if (Object.isArray(value)) {
this.delete(name);
value.forEach((val) => this.append(name, val));
return;
}
Object.defineProperty(this, normalizedName, {
configurable: true,
enumerable: true,
writable: true,
value: this.normalizeHeaderValue(value)
});
}
/**
* Appends a new value into an existing header or adds the header if it does not already exist.
* To set multiple values for one header, provide its value as a list of values.
*
* @param name
* @param value
*/
append(name: string, value: CanArray<string>): void {
const
normalizedName = this.normalizeHeaderName(name),
normalizedVal = this.normalizeHeaderValue(value);
if (normalizedName === '' || normalizedVal === '') {
return;
}
if (Object.isArray(value)) {
value.forEach((val) => this.append(name, val));
return;
}
const
currentVal = this[normalizedName],
newVal = currentVal != null ? `${currentVal}, ${this.normalizeHeaderValue(value)}` : value;
this.set(name, newVal);
}
/**
* Deletes a header by the specified name
* @param name
*/
delete(name: string): void {
delete this[name];
delete this[this.normalizeHeaderName(name)];
}
/**
* Iterates over the headers and invokes the given callback function at each header
*
* @param cb
* @param [thisArg] - value to use as `this` when executing callback
*/
forEach(cb: HeadersForEachCb, thisArg?: any): void {
for (const [key, value] of this.entries()) {
cb.call(thisArg, value, key, this);
}
}
/**
* Returns an iterator over headers' values
*/
values(): IterableIterator<string> {
return Object.values(this).values();
}
/**
* Returns an iterator over headers' names
*/
keys(): IterableIterator<string> {
return Object.keys(this).values();
}
/**
* Returns an iterator over headers.
* It produces tuples with headers' names and values.
*/
entries(): IterableIterator<[string, string]> {
return Object.entries(this).values();
}
/**
* Normalizes the specified header name
* @param name
*/
protected normalizeHeaderName(name: string): string {
return applyQueryForStr(String(name).trim(), this[requestQuery]).toLowerCase();
}
/**
* Normalizes the specified header value
* @param value
*/
protected normalizeHeaderValue(value: unknown): string {
return applyQueryForStr(String(value != null ? value : '').trim(), this[requestQuery]);
}
}