Your IP : 13.59.54.188
'use strict'
/**
* headers.js
*
* Headers class offers convenient helpers
*/
const common = require('./common.js')
const checkInvalidHeaderChar = common.checkInvalidHeaderChar
const checkIsHttpToken = common.checkIsHttpToken
function sanitizeName (name) {
name += ''
if (!checkIsHttpToken(name)) {
throw new TypeError(`${name} is not a legal HTTP header name`)
}
return name.toLowerCase()
}
function sanitizeValue (value) {
value += ''
if (checkInvalidHeaderChar(value)) {
throw new TypeError(`${value} is not a legal HTTP header value`)
}
return value
}
const MAP = Symbol('map')
class Headers {
/**
* Headers class
*
* @param Object headers Response headers
* @return Void
*/
constructor (init) {
this[MAP] = Object.create(null)
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
}
// 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')
}
// 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)
}
}
} else {
throw new TypeError('Provided initializer must be an object')
}
Object.defineProperty(this, Symbol.toStringTag, {
value: 'Headers',
writable: false,
enumerable: false,
configurable: true
})
}
/**
* Return first header value given name
*
* @param String name Header name
* @return Mixed
*/
get (name) {
const list = this[MAP][sanitizeName(name)]
if (!list) {
return null
}
return list.join(', ')
}
/**
* 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
*/
forEach (callback, thisArg) {
let pairs = getHeaderPairs(this)
let i = 0
while (i < pairs.length) {
const name = pairs[i][0]
const value = pairs[i][1]
callback.call(thisArg, value, name, this)
pairs = getHeaderPairs(this)
i++
}
}
/**
* Overwrite header values given name
*
* @param String name Header name
* @param String value Header value
* @return Void
*/
set (name, value) {
this[MAP][sanitizeName(name)] = [sanitizeValue(value)]
}
/**
* 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
}
this[MAP][sanitizeName(name)].push(sanitizeValue(value))
}
/**
* Check for header name existence
*
* @param String name Header name
* @return Boolean
*/
has (name) {
return !!this[MAP][sanitizeName(name)]
}
/**
* Delete all header values given name
*
* @param String name Header name
* @return Void
*/
delete (name) {
delete this[MAP][sanitizeName(name)]
};
/**
* Return raw headers (non-spec api)
*
* @return Object
*/
raw () {
return this[MAP]
}
/**
* Get an iterator on keys.
*
* @return Iterator
*/
keys () {
return createHeadersIterator(this, 'key')
}
/**
* Get an iterator on values.
*
* @return Iterator
*/
values () {
return createHeadersIterator(this, 'value')
}
/**
* Get an iterator on entries.
*
* This is the default iterator of the Headers object.
*
* @return Iterator
*/
[Symbol.iterator] () {
return createHeadersIterator(this, 'key+value')
}
}
Headers.prototype.entries = Headers.prototype[Symbol.iterator]
Object.defineProperty(Headers.prototype, Symbol.toStringTag, {
value: 'HeadersPrototype',
writable: false,
enumerable: false,
configurable: true
})
function getHeaderPairs (headers, kind) {
const keys = Object.keys(headers[MAP]).sort()
return keys.map(
kind === 'key'
? k => [k]
: k => [k, headers.get(k)]
)
}
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 () {
// istanbul ignore if
if (!this ||
Object.getPrototypeOf(this) !== HeadersIteratorPrototype) {
throw new TypeError('Value of `this` is not a HeadersIterator')
}
const target = this[INTERNAL].target
const kind = this[INTERNAL].kind
const index = this[INTERNAL].index
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
})
module.exports = Headers