2016-04-05 10:43:12 -07:00
|
|
|
|
2015-01-27 05:11:26 -08:00
|
|
|
/**
|
|
|
|
* headers.js
|
|
|
|
*
|
|
|
|
* Headers class offers convenient helpers
|
|
|
|
*/
|
|
|
|
|
2017-02-26 13:17:47 -08:00
|
|
|
import { checkInvalidHeaderChar, checkIsHttpToken } from './common.js';
|
2016-10-10 15:32:56 -07:00
|
|
|
|
|
|
|
function sanitizeName(name) {
|
|
|
|
name += '';
|
2016-11-23 11:17:42 -08:00
|
|
|
if (!checkIsHttpToken(name)) {
|
2016-10-10 15:32:56 -07:00
|
|
|
throw new TypeError(`${name} is not a legal HTTP header name`);
|
|
|
|
}
|
|
|
|
return name.toLowerCase();
|
|
|
|
}
|
|
|
|
|
|
|
|
function sanitizeValue(value) {
|
|
|
|
value += '';
|
2016-11-23 11:17:42 -08:00
|
|
|
if (checkInvalidHeaderChar(value)) {
|
2016-10-10 15:32:56 -07:00
|
|
|
throw new TypeError(`${value} is not a legal HTTP header value`);
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
2016-10-08 20:51:01 -07:00
|
|
|
|
2017-01-23 07:54:28 -08:00
|
|
|
const MAP = Symbol('map');
|
2016-10-08 20:51:01 -07:00
|
|
|
export default class Headers {
|
|
|
|
/**
|
|
|
|
* Headers class
|
|
|
|
*
|
|
|
|
* @param Object headers Response headers
|
|
|
|
* @return Void
|
|
|
|
*/
|
2017-01-29 08:58:16 -08:00
|
|
|
constructor(init = undefined) {
|
2016-10-15 10:02:52 -07:00
|
|
|
this[MAP] = Object.create(null);
|
2016-10-08 20:51:01 -07:00
|
|
|
|
2017-03-20 09:22:49 -07:00
|
|
|
if (init instanceof Headers) {
|
|
|
|
const rawHeaders = init.raw();
|
|
|
|
const headerNames = Object.keys(rawHeaders);
|
|
|
|
|
|
|
|
for (const headerName of headerNames) {
|
|
|
|
for (const value of rawHeaders[headerName]) {
|
|
|
|
this.append(headerName, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-29 08:58:16 -08:00
|
|
|
// We don't worry about converting prop to ByteString here as append()
|
|
|
|
// will handle it.
|
|
|
|
if (init == null) {
|
|
|
|
// no op
|
|
|
|
} else if (typeof init === 'object') {
|
|
|
|
const method = init[Symbol.iterator];
|
|
|
|
if (method != null) {
|
|
|
|
if (typeof method !== 'function') {
|
|
|
|
throw new TypeError('Header pairs must be iterable');
|
2017-01-14 21:11:30 -08:00
|
|
|
}
|
2017-01-29 08:58:16 -08:00
|
|
|
|
|
|
|
// sequence<sequence<ByteString>>
|
|
|
|
// Note: per spec we have to first exhaust the lists then process them
|
|
|
|
const pairs = [];
|
|
|
|
for (const pair of init) {
|
|
|
|
if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') {
|
|
|
|
throw new TypeError('Each header pair must be iterable');
|
|
|
|
}
|
|
|
|
pairs.push(Array.from(pair));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const pair of pairs) {
|
|
|
|
if (pair.length !== 2) {
|
|
|
|
throw new TypeError('Each header pair must be a name/value tuple');
|
|
|
|
}
|
|
|
|
this.append(pair[0], pair[1]);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// record<ByteString, ByteString>
|
|
|
|
for (const key of Object.keys(init)) {
|
|
|
|
const value = init[key];
|
|
|
|
this.append(key, value);
|
2016-10-10 13:49:12 -07:00
|
|
|
}
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
2017-01-29 08:58:16 -08:00
|
|
|
} else {
|
2017-01-14 21:11:30 -08:00
|
|
|
throw new TypeError('Provided initializer must be an object');
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
2016-11-23 14:36:08 -08:00
|
|
|
|
|
|
|
Object.defineProperty(this, Symbol.toStringTag, {
|
|
|
|
value: 'Headers',
|
|
|
|
writable: false,
|
|
|
|
enumerable: false,
|
|
|
|
configurable: true
|
|
|
|
});
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
2015-01-27 05:11:26 -08:00
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Return first header value given name
|
|
|
|
*
|
|
|
|
* @param String name Header name
|
|
|
|
* @return Mixed
|
|
|
|
*/
|
|
|
|
get(name) {
|
2016-10-10 15:32:56 -07:00
|
|
|
const list = this[MAP][sanitizeName(name)];
|
2016-10-10 18:31:53 -07:00
|
|
|
if (!list) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-04-02 08:43:46 -07:00
|
|
|
return list.join(', ');
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
2015-05-03 21:05:06 -07:00
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Iterate over all headers
|
|
|
|
*
|
|
|
|
* @param Function callback Executed for each item with parameters (value, name, thisArg)
|
|
|
|
* @param Boolean thisArg `this` context for callback function
|
|
|
|
* @return Void
|
|
|
|
*/
|
2016-11-23 15:06:30 -08:00
|
|
|
forEach(callback, thisArg = undefined) {
|
|
|
|
let pairs = getHeaderPairs(this);
|
|
|
|
let i = 0;
|
|
|
|
while (i < pairs.length) {
|
|
|
|
const [name, value] = pairs[i];
|
|
|
|
callback.call(thisArg, value, name, this);
|
|
|
|
pairs = getHeaderPairs(this);
|
|
|
|
i++;
|
2016-10-15 10:02:52 -07:00
|
|
|
}
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Overwrite header values given name
|
|
|
|
*
|
|
|
|
* @param String name Header name
|
|
|
|
* @param String value Header value
|
|
|
|
* @return Void
|
|
|
|
*/
|
|
|
|
set(name, value) {
|
2016-10-10 15:32:56 -07:00
|
|
|
this[MAP][sanitizeName(name)] = [sanitizeValue(value)];
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Append a value onto existing header
|
|
|
|
*
|
|
|
|
* @param String name Header name
|
|
|
|
* @param String value Header value
|
|
|
|
* @return Void
|
|
|
|
*/
|
|
|
|
append(name, value) {
|
|
|
|
if (!this.has(name)) {
|
|
|
|
this.set(name, value);
|
|
|
|
return;
|
2015-01-27 05:11:26 -08:00
|
|
|
}
|
2016-10-08 20:51:01 -07:00
|
|
|
|
2016-10-10 15:32:56 -07:00
|
|
|
this[MAP][sanitizeName(name)].push(sanitizeValue(value));
|
2015-01-27 05:11:26 -08:00
|
|
|
}
|
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Check for header name existence
|
|
|
|
*
|
|
|
|
* @param String name Header name
|
|
|
|
* @return Boolean
|
|
|
|
*/
|
|
|
|
has(name) {
|
2016-10-15 10:02:52 -07:00
|
|
|
return !!this[MAP][sanitizeName(name)];
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
2015-01-27 05:11:26 -08:00
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Delete all header values given name
|
|
|
|
*
|
|
|
|
* @param String name Header name
|
|
|
|
* @return Void
|
|
|
|
*/
|
|
|
|
delete(name) {
|
2016-10-10 15:32:56 -07:00
|
|
|
delete this[MAP][sanitizeName(name)];
|
2016-10-08 20:51:01 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return raw headers (non-spec api)
|
|
|
|
*
|
|
|
|
* @return Object
|
|
|
|
*/
|
|
|
|
raw() {
|
|
|
|
return this[MAP];
|
|
|
|
}
|
2015-01-27 05:11:26 -08:00
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Get an iterator on keys.
|
|
|
|
*
|
|
|
|
* @return Iterator
|
|
|
|
*/
|
|
|
|
keys() {
|
2016-11-23 15:06:30 -08:00
|
|
|
return createHeadersIterator(this, 'key');
|
2015-01-27 05:11:26 -08:00
|
|
|
}
|
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Get an iterator on values.
|
|
|
|
*
|
|
|
|
* @return Iterator
|
|
|
|
*/
|
2016-11-23 15:06:30 -08:00
|
|
|
values() {
|
|
|
|
return createHeadersIterator(this, 'value');
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
2015-10-26 22:13:02 -07:00
|
|
|
|
2016-10-08 20:51:01 -07:00
|
|
|
/**
|
|
|
|
* Get an iterator on entries.
|
|
|
|
*
|
|
|
|
* This is the default iterator of the Headers object.
|
|
|
|
*
|
|
|
|
* @return Iterator
|
|
|
|
*/
|
|
|
|
[Symbol.iterator]() {
|
2016-11-23 15:06:30 -08:00
|
|
|
return createHeadersIterator(this, 'key+value');
|
2016-10-08 20:51:01 -07:00
|
|
|
}
|
|
|
|
}
|
2016-11-23 15:06:30 -08:00
|
|
|
Headers.prototype.entries = Headers.prototype[Symbol.iterator];
|
2015-01-27 05:11:26 -08:00
|
|
|
|
2016-11-23 14:36:08 -08:00
|
|
|
Object.defineProperty(Headers.prototype, Symbol.toStringTag, {
|
|
|
|
value: 'HeadersPrototype',
|
|
|
|
writable: false,
|
|
|
|
enumerable: false,
|
|
|
|
configurable: true
|
|
|
|
});
|
|
|
|
|
2016-11-23 15:06:30 -08:00
|
|
|
function getHeaderPairs(headers, kind) {
|
2017-01-23 07:54:28 -08:00
|
|
|
const keys = Object.keys(headers[MAP]).sort();
|
|
|
|
return keys.map(
|
|
|
|
kind === 'key' ?
|
|
|
|
k => [k] :
|
|
|
|
k => [k, headers.get(k)]
|
|
|
|
);
|
2016-11-23 15:06:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
const INTERNAL = Symbol('internal');
|
|
|
|
|
|
|
|
function createHeadersIterator(target, kind) {
|
|
|
|
const iterator = Object.create(HeadersIteratorPrototype);
|
|
|
|
iterator[INTERNAL] = {
|
|
|
|
target,
|
|
|
|
kind,
|
|
|
|
index: 0
|
|
|
|
};
|
|
|
|
return iterator;
|
|
|
|
}
|
|
|
|
|
|
|
|
const HeadersIteratorPrototype = Object.setPrototypeOf({
|
|
|
|
next() {
|
2016-12-05 18:46:02 -08:00
|
|
|
// istanbul ignore if
|
2016-11-23 15:06:30 -08:00
|
|
|
if (!this ||
|
|
|
|
Object.getPrototypeOf(this) !== HeadersIteratorPrototype) {
|
|
|
|
throw new TypeError('Value of `this` is not a HeadersIterator');
|
|
|
|
}
|
|
|
|
|
|
|
|
const {
|
|
|
|
target,
|
|
|
|
kind,
|
|
|
|
index
|
|
|
|
} = this[INTERNAL];
|
|
|
|
const values = getHeaderPairs(target, kind);
|
|
|
|
const len = values.length;
|
|
|
|
if (index >= len) {
|
|
|
|
return {
|
|
|
|
value: undefined,
|
|
|
|
done: true
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const pair = values[index];
|
|
|
|
this[INTERNAL].index = index + 1;
|
|
|
|
|
|
|
|
let result;
|
|
|
|
if (kind === 'key') {
|
|
|
|
result = pair[0];
|
|
|
|
} else if (kind === 'value') {
|
|
|
|
result = pair[1];
|
|
|
|
} else {
|
|
|
|
result = pair;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
value: result,
|
|
|
|
done: false
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}, Object.getPrototypeOf(
|
|
|
|
Object.getPrototypeOf([][Symbol.iterator]())
|
|
|
|
));
|
|
|
|
|
|
|
|
Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, {
|
|
|
|
value: 'HeadersIterator',
|
|
|
|
writable: false,
|
|
|
|
enumerable: false,
|
|
|
|
configurable: true
|
|
|
|
});
|