source: trip-planner-front/node_modules/minipass-fetch/lib/headers.js@ 1ad8e64

Last change on this file since 1ad8e64 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 6.2 KB
Line 
1'use strict'
2const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/
3const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/
4
5const validateName = name => {
6 name = `${name}`
7 if (invalidTokenRegex.test(name) || name === '')
8 throw new TypeError(`${name} is not a legal HTTP header name`)
9}
10
11const validateValue = value => {
12 value = `${value}`
13 if (invalidHeaderCharRegex.test(value))
14 throw new TypeError(`${value} is not a legal HTTP header value`)
15}
16
17const find = (map, name) => {
18 name = name.toLowerCase()
19 for (const key in map) {
20 if (key.toLowerCase() === name)
21 return key
22 }
23 return undefined
24}
25
26const MAP = Symbol('map')
27class Headers {
28 constructor (init = undefined) {
29 this[MAP] = Object.create(null)
30 if (init instanceof Headers) {
31 const rawHeaders = init.raw()
32 const headerNames = Object.keys(rawHeaders)
33 for (const headerName of headerNames) {
34 for (const value of rawHeaders[headerName]) {
35 this.append(headerName, value)
36 }
37 }
38 return
39 }
40
41 // no-op
42 if (init === undefined || init === null)
43 return
44
45 if (typeof init === 'object') {
46 const method = init[Symbol.iterator]
47 if (method !== null && method !== undefined) {
48 if (typeof method !== 'function')
49 throw new TypeError('Header pairs must be iterable')
50
51 // sequence<sequence<ByteString>>
52 // Note: per spec we have to first exhaust the lists then process them
53 const pairs = []
54 for (const pair of init) {
55 if (typeof pair !== 'object' ||
56 typeof pair[Symbol.iterator] !== 'function')
57 throw new TypeError('Each header pair must be iterable')
58 const arrPair = Array.from(pair)
59 if (arrPair.length !== 2)
60 throw new TypeError('Each header pair must be a name/value tuple')
61 pairs.push(arrPair)
62 }
63
64 for (const pair of pairs) {
65 this.append(pair[0], pair[1])
66 }
67 } else {
68 // record<ByteString, ByteString>
69 for (const key of Object.keys(init)) {
70 this.append(key, init[key])
71 }
72 }
73 } else
74 throw new TypeError('Provided initializer must be an object')
75 }
76
77 get (name) {
78 name = `${name}`
79 validateName(name)
80 const key = find(this[MAP], name)
81 if (key === undefined)
82 return null
83
84 return this[MAP][key].join(', ')
85 }
86
87 forEach (callback, thisArg = undefined) {
88 let pairs = getHeaders(this)
89 for (let i = 0; i < pairs.length; i++) {
90 const [name, value] = pairs[i]
91 callback.call(thisArg, value, name, this)
92 // refresh in case the callback added more headers
93 pairs = getHeaders(this)
94 }
95 }
96
97 set (name, value) {
98 name = `${name}`
99 value = `${value}`
100 validateName(name)
101 validateValue(value)
102 const key = find(this[MAP], name)
103 this[MAP][key !== undefined ? key : name] = [value]
104 }
105
106 append (name, value) {
107 name = `${name}`
108 value = `${value}`
109 validateName(name)
110 validateValue(value)
111 const key = find(this[MAP], name)
112 if (key !== undefined)
113 this[MAP][key].push(value)
114 else
115 this[MAP][name] = [value]
116 }
117
118 has (name) {
119 name = `${name}`
120 validateName(name)
121 return find(this[MAP], name) !== undefined
122 }
123
124 delete (name) {
125 name = `${name}`
126 validateName(name)
127 const key = find(this[MAP], name)
128 if (key !== undefined)
129 delete this[MAP][key]
130 }
131
132 raw () {
133 return this[MAP]
134 }
135
136 keys () {
137 return new HeadersIterator(this, 'key')
138 }
139
140 values () {
141 return new HeadersIterator(this, 'value')
142 }
143
144 [Symbol.iterator]() {
145 return new HeadersIterator(this, 'key+value')
146 }
147
148 entries () {
149 return new HeadersIterator(this, 'key+value')
150 }
151
152 get [Symbol.toStringTag] () {
153 return 'Headers'
154 }
155
156 static exportNodeCompatibleHeaders (headers) {
157 const obj = Object.assign(Object.create(null), headers[MAP])
158
159 // http.request() only supports string as Host header. This hack makes
160 // specifying custom Host header possible.
161 const hostHeaderKey = find(headers[MAP], 'Host')
162 if (hostHeaderKey !== undefined)
163 obj[hostHeaderKey] = obj[hostHeaderKey][0]
164
165 return obj
166 }
167
168 static createHeadersLenient (obj) {
169 const headers = new Headers()
170 for (const name of Object.keys(obj)) {
171 if (invalidTokenRegex.test(name))
172 continue
173
174 if (Array.isArray(obj[name])) {
175 for (const val of obj[name]) {
176 if (invalidHeaderCharRegex.test(val))
177 continue
178
179 if (headers[MAP][name] === undefined)
180 headers[MAP][name] = [val]
181 else
182 headers[MAP][name].push(val)
183 }
184 } else if (!invalidHeaderCharRegex.test(obj[name]))
185 headers[MAP][name] = [obj[name]]
186 }
187 return headers
188 }
189}
190
191Object.defineProperties(Headers.prototype, {
192 get: { enumerable: true },
193 forEach: { enumerable: true },
194 set: { enumerable: true },
195 append: { enumerable: true },
196 has: { enumerable: true },
197 delete: { enumerable: true },
198 keys: { enumerable: true },
199 values: { enumerable: true },
200 entries: { enumerable: true },
201})
202
203const getHeaders = (headers, kind = 'key+value') =>
204 Object.keys(headers[MAP]).sort().map(
205 kind === 'key' ? k => k.toLowerCase()
206 : kind === 'value' ? k => headers[MAP][k].join(', ')
207 : k => [k.toLowerCase(), headers[MAP][k].join(', ')]
208 )
209
210const INTERNAL = Symbol('internal')
211
212class HeadersIterator {
213 constructor (target, kind) {
214 this[INTERNAL] = {
215 target,
216 kind,
217 index: 0,
218 }
219 }
220
221 get [Symbol.toStringTag] () {
222 return 'HeadersIterator'
223 }
224
225 next () {
226 /* istanbul ignore if: should be impossible */
227 if (!this || Object.getPrototypeOf(this) !== HeadersIterator.prototype)
228 throw new TypeError('Value of `this` is not a HeadersIterator')
229
230 const { target, kind, index } = this[INTERNAL]
231 const values = getHeaders(target, kind)
232 const len = values.length
233 if (index >= len) {
234 return {
235 value: undefined,
236 done: true,
237 }
238 }
239
240 this[INTERNAL].index++
241
242 return { value: values[index], done: false }
243 }
244}
245
246// manually extend because 'extends' requires a ctor
247Object.setPrototypeOf(HeadersIterator.prototype,
248 Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())))
249
250module.exports = Headers
Note: See TracBrowser for help on using the repository browser.