22
33'use strict'
44
5- const { validateHeaderName, validateHeaderValue } = require ( 'http' )
65const { kHeadersList } = require ( '../core/symbols' )
76const { kGuard } = require ( './symbols' )
87const { kEnumerableProperty } = require ( '../core/util' )
9- const { makeIterator } = require ( './util' )
8+ const {
9+ makeIterator,
10+ isValidHeaderName,
11+ isValidHeaderValue
12+ } = require ( './util' )
13+ const { webidl } = require ( './webidl' )
1014
1115const kHeadersMap = Symbol ( 'headers map' )
1216const kHeadersSortedMap = Symbol ( 'headers map sorted' )
1317
14- function normalizeAndValidateHeaderName ( name ) {
15- if ( name === undefined ) {
16- throw new TypeError ( `Header name ${ name } ` )
17- }
18- const normalizedHeaderName = name . toLocaleLowerCase ( )
19- validateHeaderName ( normalizedHeaderName )
20- return normalizedHeaderName
21- }
22-
23- function normalizeAndValidateHeaderValue ( name , value ) {
24- if ( value === undefined ) {
25- throw new TypeError ( value , name )
26- }
27- const normalizedHeaderValue = `${ value } ` . replace (
28- / ^ [ \n \t \r \x20 ] + | [ \n \t \r \x20 ] + $ / g,
18+ /**
19+ * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
20+ * @param {string } potentialValue
21+ */
22+ function headerValueNormalize ( potentialValue ) {
23+ // To normalize a byte sequence potentialValue, remove
24+ // any leading and trailing HTTP whitespace bytes from
25+ // potentialValue.
26+ return potentialValue . replace (
27+ / ^ [ \r \n \t ] + | [ \r \n \t ] + $ / g,
2928 ''
3029 )
31- validateHeaderValue ( name , normalizedHeaderValue )
32- return normalizedHeaderValue
3330}
3431
3532function fill ( headers , object ) {
33+ // TODO: use idl converters once implemented
3634 // To fill a Headers object headers with a given object object, run these steps:
3735
3836 if ( object [ Symbol . iterator ] ) {
@@ -85,48 +83,75 @@ class HeadersList {
8583 }
8684 }
8785
86+ // https://fetch.spec.whatwg.org/#header-list-contains
87+ contains ( name ) {
88+ // A header list list contains a header name name if list
89+ // contains a header whose name is a byte-case-insensitive
90+ // match for name.
91+ name = name . toLowerCase ( )
92+
93+ return this [ kHeadersMap ] . has ( name )
94+ }
95+
8896 clear ( ) {
8997 this [ kHeadersMap ] . clear ( )
9098 this [ kHeadersSortedMap ] = null
9199 }
92100
101+ // https://fetch.spec.whatwg.org/#concept-header-list-append
93102 append ( name , value ) {
94103 this [ kHeadersSortedMap ] = null
95104
96- const normalizedName = normalizeAndValidateHeaderName ( name )
97- const normalizedValue = normalizeAndValidateHeaderValue ( name , value )
98-
99- const exists = this [ kHeadersMap ] . get ( normalizedName )
105+ // 1. If list contains name, then set name to the first such
106+ // header’s name.
107+ name = name . toLowerCase ( )
108+ const exists = this [ kHeadersMap ] . get ( name )
100109
110+ // 2. Append (name, value) to list.
101111 if ( exists ) {
102- this [ kHeadersMap ] . set ( normalizedName , `${ exists } , ${ normalizedValue } ` )
112+ this [ kHeadersMap ] . set ( name , `${ exists } , ${ value } ` )
103113 } else {
104- this [ kHeadersMap ] . set ( normalizedName , `${ normalizedValue } ` )
114+ this [ kHeadersMap ] . set ( name , `${ value } ` )
105115 }
106116 }
107117
118+ // https://fetch.spec.whatwg.org/#concept-header-list-set
108119 set ( name , value ) {
109120 this [ kHeadersSortedMap ] = null
110121
111- const normalizedName = normalizeAndValidateHeaderName ( name )
112- return this [ kHeadersMap ] . set ( normalizedName , value )
122+ // 1. If list contains name, then set the value of
123+ // the first such header to value and remove the
124+ // others.
125+ // 2. Otherwise, append header (name, value) to list.
126+ return this [ kHeadersMap ] . set ( name , value )
113127 }
114128
129+ // https://fetch.spec.whatwg.org/#concept-header-list-delete
115130 delete ( name ) {
116131 this [ kHeadersSortedMap ] = null
117132
118- const normalizedName = normalizeAndValidateHeaderName ( name )
119- return this [ kHeadersMap ] . delete ( normalizedName )
133+ name = name . toLowerCase ( )
134+ return this [ kHeadersMap ] . delete ( name )
120135 }
121136
137+ // https://fetch.spec.whatwg.org/#concept-header-list-get
122138 get ( name ) {
123- const normalizedName = normalizeAndValidateHeaderName ( name )
124- return this [ kHeadersMap ] . get ( normalizedName ) ?? null
139+ name = name . toLowerCase ( )
140+
141+ // 1. If list does not contain name, then return null.
142+ if ( ! this . contains ( name ) ) {
143+ return null
144+ }
145+
146+ // 2. Return the values of all headers in list whose name
147+ // is a byte-case-insensitive match for name,
148+ // separated from each other by 0x2C 0x20, in order.
149+ return this [ kHeadersMap ] . get ( name ) ?? null
125150 }
126151
127152 has ( name ) {
128- const normalizedName = normalizeAndValidateHeaderName ( name )
129- return this [ kHeadersMap ] . has ( normalizedName )
153+ name = name . toLowerCase ( )
154+ return this [ kHeadersMap ] . has ( name )
130155 }
131156
132157 keys ( ) {
@@ -186,14 +211,36 @@ class Headers {
186211 )
187212 }
188213
214+ name = webidl . converters . ByteString ( name )
215+ value = webidl . converters . ByteString ( value )
216+
217+ // 1. Normalize value.
218+ value = headerValueNormalize ( value )
219+
220+ // 2. If name is not a header name or value is not a
221+ // header value, then throw a TypeError.
222+ if ( ! isValidHeaderName ( name ) || ! isValidHeaderValue ( value ) ) {
223+ throw new TypeError ( )
224+ }
225+
226+ // 3. If headers’s guard is "immutable", then throw a TypeError.
227+ // 4. Otherwise, if headers’s guard is "request" and name is a
228+ // forbidden header name, return.
189229 // Note: undici does not implement forbidden header names
190230 if ( this [ kGuard ] === 'immutable' ) {
191231 throw new TypeError ( 'immutable' )
192232 } else if ( this [ kGuard ] === 'request-no-cors' ) {
233+ // 5. Otherwise, if headers’s guard is "request-no-cors":
193234 // TODO
194235 }
195236
196- return this [ kHeadersList ] . append ( String ( name ) , String ( value ) )
237+ // 6. Otherwise, if headers’s guard is "response" and name is a
238+ // forbidden response-header name, return.
239+
240+ // 7. Append (name, value) to headers’s header list.
241+ // 8. If headers’s guard is "request-no-cors", then remove
242+ // privileged no-CORS request headers from headers
243+ return this [ kHeadersList ] . append ( name , value )
197244 }
198245
199246 // https://fetch.spec.whatwg.org/#dom-headers-delete
@@ -208,14 +255,39 @@ class Headers {
208255 )
209256 }
210257
258+ name = webidl . converters . ByteString ( name )
259+
260+ // 1. If name is not a header name, then throw a TypeError.
261+ if ( ! isValidHeaderName ( name ) ) {
262+ throw new TypeError ( )
263+ }
264+
265+ // 2. If this’s guard is "immutable", then throw a TypeError.
266+ // 3. Otherwise, if this’s guard is "request" and name is a
267+ // forbidden header name, return.
268+ // 4. Otherwise, if this’s guard is "request-no-cors", name
269+ // is not a no-CORS-safelisted request-header name, and
270+ // name is not a privileged no-CORS request-header name,
271+ // return.
272+ // 5. Otherwise, if this’s guard is "response" and name is
273+ // a forbidden response-header name, return.
211274 // Note: undici does not implement forbidden header names
212275 if ( this [ kGuard ] === 'immutable' ) {
213276 throw new TypeError ( 'immutable' )
214277 } else if ( this [ kGuard ] === 'request-no-cors' ) {
215278 // TODO
216279 }
217280
218- return this [ kHeadersList ] . delete ( String ( name ) )
281+ // 6. If this’s header list does not contain name, then
282+ // return.
283+ if ( ! this [ kHeadersList ] . contains ( name ) ) {
284+ return
285+ }
286+
287+ // 7. Delete name from this’s header list.
288+ // 8. If this’s guard is "request-no-cors", then remove
289+ // privileged no-CORS request headers from this.
290+ return this [ kHeadersList ] . delete ( name )
219291 }
220292
221293 // https://fetch.spec.whatwg.org/#dom-headers-get
@@ -230,7 +302,16 @@ class Headers {
230302 )
231303 }
232304
233- return this [ kHeadersList ] . get ( String ( name ) )
305+ name = webidl . converters . ByteString ( name )
306+
307+ // 1. If name is not a header name, then throw a TypeError.
308+ if ( ! isValidHeaderName ( name ) ) {
309+ throw new TypeError ( )
310+ }
311+
312+ // 2. Return the result of getting name from this’s header
313+ // list.
314+ return this [ kHeadersList ] . get ( name )
234315 }
235316
236317 // https://fetch.spec.whatwg.org/#dom-headers-has
@@ -245,7 +326,16 @@ class Headers {
245326 )
246327 }
247328
248- return this [ kHeadersList ] . has ( String ( name ) )
329+ name = webidl . converters . ByteString ( name )
330+
331+ // 1. If name is not a header name, then throw a TypeError.
332+ if ( ! isValidHeaderName ( name ) ) {
333+ throw new TypeError ( )
334+ }
335+
336+ // 2. Return true if this’s header list contains name;
337+ // otherwise false.
338+ return this [ kHeadersList ] . contains ( name )
249339 }
250340
251341 // https://fetch.spec.whatwg.org/#dom-headers-set
@@ -260,14 +350,37 @@ class Headers {
260350 )
261351 }
262352
353+ name = webidl . converters . ByteString ( name )
354+ value = webidl . converters . ByteString ( value )
355+
356+ // 1. Normalize value.
357+ value = headerValueNormalize ( value )
358+
359+ // 2. If name is not a header name or value is not a
360+ // header value, then throw a TypeError.
361+ if ( ! isValidHeaderName ( name ) || ! isValidHeaderValue ( value ) ) {
362+ throw new TypeError ( )
363+ }
364+
365+ // 3. If this’s guard is "immutable", then throw a TypeError.
366+ // 4. Otherwise, if this’s guard is "request" and name is a
367+ // forbidden header name, return.
368+ // 5. Otherwise, if this’s guard is "request-no-cors" and
369+ // name/value is not a no-CORS-safelisted request-header,
370+ // return.
371+ // 6. Otherwise, if this’s guard is "response" and name is a
372+ // forbidden response-header name, return.
263373 // Note: undici does not implement forbidden header names
264374 if ( this [ kGuard ] === 'immutable' ) {
265375 throw new TypeError ( 'immutable' )
266376 } else if ( this [ kGuard ] === 'request-no-cors' ) {
267377 // TODO
268378 }
269379
270- return this [ kHeadersList ] . set ( String ( name ) , String ( value ) )
380+ // 7. Set (name, value) in this’s header list.
381+ // 8. If this’s guard is "request-no-cors", then remove
382+ // privileged no-CORS request headers from this
383+ return this [ kHeadersList ] . set ( name , value )
271384 }
272385
273386 get [ kHeadersSortedMap ] ( ) {
@@ -351,7 +464,5 @@ Object.defineProperties(Headers.prototype, {
351464module . exports = {
352465 fill,
353466 Headers,
354- HeadersList,
355- normalizeAndValidateHeaderName,
356- normalizeAndValidateHeaderValue
467+ HeadersList
357468}
0 commit comments