headers.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. // https://github.com/Ethan-Arrowood/undici-fetch
  2. 'use strict'
  3. const { kConstruct } = require('../../core/symbols')
  4. const { kEnumerableProperty } = require('../../core/util')
  5. const {
  6. iteratorMixin,
  7. isValidHeaderName,
  8. isValidHeaderValue
  9. } = require('./util')
  10. const { webidl } = require('../webidl')
  11. const assert = require('node:assert')
  12. const util = require('node:util')
  13. /**
  14. * @param {number} code
  15. * @returns {code is (0x0a | 0x0d | 0x09 | 0x20)}
  16. */
  17. function isHTTPWhiteSpaceCharCode (code) {
  18. return code === 0x0a || code === 0x0d || code === 0x09 || code === 0x20
  19. }
  20. /**
  21. * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize
  22. * @param {string} potentialValue
  23. * @returns {string}
  24. */
  25. function headerValueNormalize (potentialValue) {
  26. // To normalize a byte sequence potentialValue, remove
  27. // any leading and trailing HTTP whitespace bytes from
  28. // potentialValue.
  29. let i = 0; let j = potentialValue.length
  30. while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(j - 1))) --j
  31. while (j > i && isHTTPWhiteSpaceCharCode(potentialValue.charCodeAt(i))) ++i
  32. return i === 0 && j === potentialValue.length ? potentialValue : potentialValue.substring(i, j)
  33. }
  34. /**
  35. * @param {Headers} headers
  36. * @param {Array|Object} object
  37. */
  38. function fill (headers, object) {
  39. // To fill a Headers object headers with a given object object, run these steps:
  40. // 1. If object is a sequence, then for each header in object:
  41. // Note: webidl conversion to array has already been done.
  42. if (Array.isArray(object)) {
  43. for (let i = 0; i < object.length; ++i) {
  44. const header = object[i]
  45. // 1. If header does not contain exactly two items, then throw a TypeError.
  46. if (header.length !== 2) {
  47. throw webidl.errors.exception({
  48. header: 'Headers constructor',
  49. message: `expected name/value pair to be length 2, found ${header.length}.`
  50. })
  51. }
  52. // 2. Append (header’s first item, header’s second item) to headers.
  53. appendHeader(headers, header[0], header[1])
  54. }
  55. } else if (typeof object === 'object' && object !== null) {
  56. // Note: null should throw
  57. // 2. Otherwise, object is a record, then for each key → value in object,
  58. // append (key, value) to headers
  59. const keys = Object.keys(object)
  60. for (let i = 0; i < keys.length; ++i) {
  61. appendHeader(headers, keys[i], object[keys[i]])
  62. }
  63. } else {
  64. throw webidl.errors.conversionFailed({
  65. prefix: 'Headers constructor',
  66. argument: 'Argument 1',
  67. types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']
  68. })
  69. }
  70. }
  71. /**
  72. * @see https://fetch.spec.whatwg.org/#concept-headers-append
  73. * @param {Headers} headers
  74. * @param {string} name
  75. * @param {string} value
  76. */
  77. function appendHeader (headers, name, value) {
  78. // 1. Normalize value.
  79. value = headerValueNormalize(value)
  80. // 2. If name is not a header name or value is not a
  81. // header value, then throw a TypeError.
  82. if (!isValidHeaderName(name)) {
  83. throw webidl.errors.invalidArgument({
  84. prefix: 'Headers.append',
  85. value: name,
  86. type: 'header name'
  87. })
  88. } else if (!isValidHeaderValue(value)) {
  89. throw webidl.errors.invalidArgument({
  90. prefix: 'Headers.append',
  91. value,
  92. type: 'header value'
  93. })
  94. }
  95. // 3. If headers’s guard is "immutable", then throw a TypeError.
  96. // 4. Otherwise, if headers’s guard is "request" and name is a
  97. // forbidden header name, return.
  98. // 5. Otherwise, if headers’s guard is "request-no-cors":
  99. // TODO
  100. // Note: undici does not implement forbidden header names
  101. if (getHeadersGuard(headers) === 'immutable') {
  102. throw new TypeError('immutable')
  103. }
  104. // 6. Otherwise, if headers’s guard is "response" and name is a
  105. // forbidden response-header name, return.
  106. // 7. Append (name, value) to headers’s header list.
  107. return getHeadersList(headers).append(name, value, false)
  108. // 8. If headers’s guard is "request-no-cors", then remove
  109. // privileged no-CORS request headers from headers
  110. }
  111. // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
  112. /**
  113. * @param {Headers} target
  114. */
  115. function headersListSortAndCombine (target) {
  116. const headersList = getHeadersList(target)
  117. if (!headersList) {
  118. return []
  119. }
  120. if (headersList.sortedMap) {
  121. return headersList.sortedMap
  122. }
  123. // 1. Let headers be an empty list of headers with the key being the name
  124. // and value the value.
  125. const headers = []
  126. // 2. Let names be the result of convert header names to a sorted-lowercase
  127. // set with all the names of the headers in list.
  128. const names = headersList.toSortedArray()
  129. const cookies = headersList.cookies
  130. // fast-path
  131. if (cookies === null || cookies.length === 1) {
  132. // Note: The non-null assertion of value has already been done by `HeadersList#toSortedArray`
  133. return (headersList.sortedMap = names)
  134. }
  135. // 3. For each name of names:
  136. for (let i = 0; i < names.length; ++i) {
  137. const { 0: name, 1: value } = names[i]
  138. // 1. If name is `set-cookie`, then:
  139. if (name === 'set-cookie') {
  140. // 1. Let values be a list of all values of headers in list whose name
  141. // is a byte-case-insensitive match for name, in order.
  142. // 2. For each value of values:
  143. // 1. Append (name, value) to headers.
  144. for (let j = 0; j < cookies.length; ++j) {
  145. headers.push([name, cookies[j]])
  146. }
  147. } else {
  148. // 2. Otherwise:
  149. // 1. Let value be the result of getting name from list.
  150. // 2. Assert: value is non-null.
  151. // Note: This operation was done by `HeadersList#toSortedArray`.
  152. // 3. Append (name, value) to headers.
  153. headers.push([name, value])
  154. }
  155. }
  156. // 4. Return headers.
  157. return (headersList.sortedMap = headers)
  158. }
  159. function compareHeaderName (a, b) {
  160. return a[0] < b[0] ? -1 : 1
  161. }
  162. class HeadersList {
  163. /** @type {[string, string][]|null} */
  164. cookies = null
  165. sortedMap
  166. headersMap
  167. constructor (init) {
  168. if (init instanceof HeadersList) {
  169. this.headersMap = new Map(init.headersMap)
  170. this.sortedMap = init.sortedMap
  171. this.cookies = init.cookies === null ? null : [...init.cookies]
  172. } else {
  173. this.headersMap = new Map(init)
  174. this.sortedMap = null
  175. }
  176. }
  177. /**
  178. * @see https://fetch.spec.whatwg.org/#header-list-contains
  179. * @param {string} name
  180. * @param {boolean} isLowerCase
  181. */
  182. contains (name, isLowerCase) {
  183. // A header list list contains a header name name if list
  184. // contains a header whose name is a byte-case-insensitive
  185. // match for name.
  186. return this.headersMap.has(isLowerCase ? name : name.toLowerCase())
  187. }
  188. clear () {
  189. this.headersMap.clear()
  190. this.sortedMap = null
  191. this.cookies = null
  192. }
  193. /**
  194. * @see https://fetch.spec.whatwg.org/#concept-header-list-append
  195. * @param {string} name
  196. * @param {string} value
  197. * @param {boolean} isLowerCase
  198. */
  199. append (name, value, isLowerCase) {
  200. this.sortedMap = null
  201. // 1. If list contains name, then set name to the first such
  202. // header’s name.
  203. const lowercaseName = isLowerCase ? name : name.toLowerCase()
  204. const exists = this.headersMap.get(lowercaseName)
  205. // 2. Append (name, value) to list.
  206. if (exists) {
  207. const delimiter = lowercaseName === 'cookie' ? '; ' : ', '
  208. this.headersMap.set(lowercaseName, {
  209. name: exists.name,
  210. value: `${exists.value}${delimiter}${value}`
  211. })
  212. } else {
  213. this.headersMap.set(lowercaseName, { name, value })
  214. }
  215. if (lowercaseName === 'set-cookie') {
  216. (this.cookies ??= []).push(value)
  217. }
  218. }
  219. /**
  220. * @see https://fetch.spec.whatwg.org/#concept-header-list-set
  221. * @param {string} name
  222. * @param {string} value
  223. * @param {boolean} isLowerCase
  224. */
  225. set (name, value, isLowerCase) {
  226. this.sortedMap = null
  227. const lowercaseName = isLowerCase ? name : name.toLowerCase()
  228. if (lowercaseName === 'set-cookie') {
  229. this.cookies = [value]
  230. }
  231. // 1. If list contains name, then set the value of
  232. // the first such header to value and remove the
  233. // others.
  234. // 2. Otherwise, append header (name, value) to list.
  235. this.headersMap.set(lowercaseName, { name, value })
  236. }
  237. /**
  238. * @see https://fetch.spec.whatwg.org/#concept-header-list-delete
  239. * @param {string} name
  240. * @param {boolean} isLowerCase
  241. */
  242. delete (name, isLowerCase) {
  243. this.sortedMap = null
  244. if (!isLowerCase) name = name.toLowerCase()
  245. if (name === 'set-cookie') {
  246. this.cookies = null
  247. }
  248. this.headersMap.delete(name)
  249. }
  250. /**
  251. * @see https://fetch.spec.whatwg.org/#concept-header-list-get
  252. * @param {string} name
  253. * @param {boolean} isLowerCase
  254. * @returns {string | null}
  255. */
  256. get (name, isLowerCase) {
  257. // 1. If list does not contain name, then return null.
  258. // 2. Return the values of all headers in list whose name
  259. // is a byte-case-insensitive match for name,
  260. // separated from each other by 0x2C 0x20, in order.
  261. return this.headersMap.get(isLowerCase ? name : name.toLowerCase())?.value ?? null
  262. }
  263. * [Symbol.iterator] () {
  264. // use the lowercased name
  265. for (const { 0: name, 1: { value } } of this.headersMap) {
  266. yield [name, value]
  267. }
  268. }
  269. get entries () {
  270. const headers = {}
  271. if (this.headersMap.size !== 0) {
  272. for (const { name, value } of this.headersMap.values()) {
  273. headers[name] = value
  274. }
  275. }
  276. return headers
  277. }
  278. rawValues () {
  279. return this.headersMap.values()
  280. }
  281. get entriesList () {
  282. const headers = []
  283. if (this.headersMap.size !== 0) {
  284. for (const { 0: lowerName, 1: { name, value } } of this.headersMap) {
  285. if (lowerName === 'set-cookie') {
  286. for (const cookie of this.cookies) {
  287. headers.push([name, cookie])
  288. }
  289. } else {
  290. headers.push([name, value])
  291. }
  292. }
  293. }
  294. return headers
  295. }
  296. // https://fetch.spec.whatwg.org/#convert-header-names-to-a-sorted-lowercase-set
  297. toSortedArray () {
  298. const size = this.headersMap.size
  299. const array = new Array(size)
  300. // In most cases, you will use the fast-path.
  301. // fast-path: Use binary insertion sort for small arrays.
  302. if (size <= 32) {
  303. if (size === 0) {
  304. // If empty, it is an empty array. To avoid the first index assignment.
  305. return array
  306. }
  307. // Improve performance by unrolling loop and avoiding double-loop.
  308. // Double-loop-less version of the binary insertion sort.
  309. const iterator = this.headersMap[Symbol.iterator]()
  310. const firstValue = iterator.next().value
  311. // set [name, value] to first index.
  312. array[0] = [firstValue[0], firstValue[1].value]
  313. // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
  314. // 3.2.2. Assert: value is non-null.
  315. assert(firstValue[1].value !== null)
  316. for (
  317. let i = 1, j = 0, right = 0, left = 0, pivot = 0, x, value;
  318. i < size;
  319. ++i
  320. ) {
  321. // get next value
  322. value = iterator.next().value
  323. // set [name, value] to current index.
  324. x = array[i] = [value[0], value[1].value]
  325. // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
  326. // 3.2.2. Assert: value is non-null.
  327. assert(x[1] !== null)
  328. left = 0
  329. right = i
  330. // binary search
  331. while (left < right) {
  332. // middle index
  333. pivot = left + ((right - left) >> 1)
  334. // compare header name
  335. if (array[pivot][0] <= x[0]) {
  336. left = pivot + 1
  337. } else {
  338. right = pivot
  339. }
  340. }
  341. if (i !== pivot) {
  342. j = i
  343. while (j > left) {
  344. array[j] = array[--j]
  345. }
  346. array[left] = x
  347. }
  348. }
  349. /* c8 ignore next 4 */
  350. if (!iterator.next().done) {
  351. // This is for debugging and will never be called.
  352. throw new TypeError('Unreachable')
  353. }
  354. return array
  355. } else {
  356. // This case would be a rare occurrence.
  357. // slow-path: fallback
  358. let i = 0
  359. for (const { 0: name, 1: { value } } of this.headersMap) {
  360. array[i++] = [name, value]
  361. // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine
  362. // 3.2.2. Assert: value is non-null.
  363. assert(value !== null)
  364. }
  365. return array.sort(compareHeaderName)
  366. }
  367. }
  368. }
  369. // https://fetch.spec.whatwg.org/#headers-class
  370. class Headers {
  371. #guard
  372. /**
  373. * @type {HeadersList}
  374. */
  375. #headersList
  376. /**
  377. * @param {HeadersInit|Symbol} [init]
  378. * @returns
  379. */
  380. constructor (init = undefined) {
  381. webidl.util.markAsUncloneable(this)
  382. if (init === kConstruct) {
  383. return
  384. }
  385. this.#headersList = new HeadersList()
  386. // The new Headers(init) constructor steps are:
  387. // 1. Set this’s guard to "none".
  388. this.#guard = 'none'
  389. // 2. If init is given, then fill this with init.
  390. if (init !== undefined) {
  391. init = webidl.converters.HeadersInit(init, 'Headers constructor', 'init')
  392. fill(this, init)
  393. }
  394. }
  395. // https://fetch.spec.whatwg.org/#dom-headers-append
  396. append (name, value) {
  397. webidl.brandCheck(this, Headers)
  398. webidl.argumentLengthCheck(arguments, 2, 'Headers.append')
  399. const prefix = 'Headers.append'
  400. name = webidl.converters.ByteString(name, prefix, 'name')
  401. value = webidl.converters.ByteString(value, prefix, 'value')
  402. return appendHeader(this, name, value)
  403. }
  404. // https://fetch.spec.whatwg.org/#dom-headers-delete
  405. delete (name) {
  406. webidl.brandCheck(this, Headers)
  407. webidl.argumentLengthCheck(arguments, 1, 'Headers.delete')
  408. const prefix = 'Headers.delete'
  409. name = webidl.converters.ByteString(name, prefix, 'name')
  410. // 1. If name is not a header name, then throw a TypeError.
  411. if (!isValidHeaderName(name)) {
  412. throw webidl.errors.invalidArgument({
  413. prefix: 'Headers.delete',
  414. value: name,
  415. type: 'header name'
  416. })
  417. }
  418. // 2. If this’s guard is "immutable", then throw a TypeError.
  419. // 3. Otherwise, if this’s guard is "request" and name is a
  420. // forbidden header name, return.
  421. // 4. Otherwise, if this’s guard is "request-no-cors", name
  422. // is not a no-CORS-safelisted request-header name, and
  423. // name is not a privileged no-CORS request-header name,
  424. // return.
  425. // 5. Otherwise, if this’s guard is "response" and name is
  426. // a forbidden response-header name, return.
  427. // Note: undici does not implement forbidden header names
  428. if (this.#guard === 'immutable') {
  429. throw new TypeError('immutable')
  430. }
  431. // 6. If this’s header list does not contain name, then
  432. // return.
  433. if (!this.#headersList.contains(name, false)) {
  434. return
  435. }
  436. // 7. Delete name from this’s header list.
  437. // 8. If this’s guard is "request-no-cors", then remove
  438. // privileged no-CORS request headers from this.
  439. this.#headersList.delete(name, false)
  440. }
  441. // https://fetch.spec.whatwg.org/#dom-headers-get
  442. get (name) {
  443. webidl.brandCheck(this, Headers)
  444. webidl.argumentLengthCheck(arguments, 1, 'Headers.get')
  445. const prefix = 'Headers.get'
  446. name = webidl.converters.ByteString(name, prefix, 'name')
  447. // 1. If name is not a header name, then throw a TypeError.
  448. if (!isValidHeaderName(name)) {
  449. throw webidl.errors.invalidArgument({
  450. prefix,
  451. value: name,
  452. type: 'header name'
  453. })
  454. }
  455. // 2. Return the result of getting name from this’s header
  456. // list.
  457. return this.#headersList.get(name, false)
  458. }
  459. // https://fetch.spec.whatwg.org/#dom-headers-has
  460. has (name) {
  461. webidl.brandCheck(this, Headers)
  462. webidl.argumentLengthCheck(arguments, 1, 'Headers.has')
  463. const prefix = 'Headers.has'
  464. name = webidl.converters.ByteString(name, prefix, 'name')
  465. // 1. If name is not a header name, then throw a TypeError.
  466. if (!isValidHeaderName(name)) {
  467. throw webidl.errors.invalidArgument({
  468. prefix,
  469. value: name,
  470. type: 'header name'
  471. })
  472. }
  473. // 2. Return true if this’s header list contains name;
  474. // otherwise false.
  475. return this.#headersList.contains(name, false)
  476. }
  477. // https://fetch.spec.whatwg.org/#dom-headers-set
  478. set (name, value) {
  479. webidl.brandCheck(this, Headers)
  480. webidl.argumentLengthCheck(arguments, 2, 'Headers.set')
  481. const prefix = 'Headers.set'
  482. name = webidl.converters.ByteString(name, prefix, 'name')
  483. value = webidl.converters.ByteString(value, prefix, 'value')
  484. // 1. Normalize value.
  485. value = headerValueNormalize(value)
  486. // 2. If name is not a header name or value is not a
  487. // header value, then throw a TypeError.
  488. if (!isValidHeaderName(name)) {
  489. throw webidl.errors.invalidArgument({
  490. prefix,
  491. value: name,
  492. type: 'header name'
  493. })
  494. } else if (!isValidHeaderValue(value)) {
  495. throw webidl.errors.invalidArgument({
  496. prefix,
  497. value,
  498. type: 'header value'
  499. })
  500. }
  501. // 3. If this’s guard is "immutable", then throw a TypeError.
  502. // 4. Otherwise, if this’s guard is "request" and name is a
  503. // forbidden header name, return.
  504. // 5. Otherwise, if this’s guard is "request-no-cors" and
  505. // name/value is not a no-CORS-safelisted request-header,
  506. // return.
  507. // 6. Otherwise, if this’s guard is "response" and name is a
  508. // forbidden response-header name, return.
  509. // Note: undici does not implement forbidden header names
  510. if (this.#guard === 'immutable') {
  511. throw new TypeError('immutable')
  512. }
  513. // 7. Set (name, value) in this’s header list.
  514. // 8. If this’s guard is "request-no-cors", then remove
  515. // privileged no-CORS request headers from this
  516. this.#headersList.set(name, value, false)
  517. }
  518. // https://fetch.spec.whatwg.org/#dom-headers-getsetcookie
  519. getSetCookie () {
  520. webidl.brandCheck(this, Headers)
  521. // 1. If this’s header list does not contain `Set-Cookie`, then return « ».
  522. // 2. Return the values of all headers in this’s header list whose name is
  523. // a byte-case-insensitive match for `Set-Cookie`, in order.
  524. const list = this.#headersList.cookies
  525. if (list) {
  526. return [...list]
  527. }
  528. return []
  529. }
  530. [util.inspect.custom] (depth, options) {
  531. options.depth ??= depth
  532. return `Headers ${util.formatWithOptions(options, this.#headersList.entries)}`
  533. }
  534. static getHeadersGuard (o) {
  535. return o.#guard
  536. }
  537. static setHeadersGuard (o, guard) {
  538. o.#guard = guard
  539. }
  540. /**
  541. * @param {Headers} o
  542. */
  543. static getHeadersList (o) {
  544. return o.#headersList
  545. }
  546. /**
  547. * @param {Headers} target
  548. * @param {HeadersList} list
  549. */
  550. static setHeadersList (target, list) {
  551. target.#headersList = list
  552. }
  553. }
  554. const { getHeadersGuard, setHeadersGuard, getHeadersList, setHeadersList } = Headers
  555. Reflect.deleteProperty(Headers, 'getHeadersGuard')
  556. Reflect.deleteProperty(Headers, 'setHeadersGuard')
  557. Reflect.deleteProperty(Headers, 'getHeadersList')
  558. Reflect.deleteProperty(Headers, 'setHeadersList')
  559. iteratorMixin('Headers', Headers, headersListSortAndCombine, 0, 1)
  560. Object.defineProperties(Headers.prototype, {
  561. append: kEnumerableProperty,
  562. delete: kEnumerableProperty,
  563. get: kEnumerableProperty,
  564. has: kEnumerableProperty,
  565. set: kEnumerableProperty,
  566. getSetCookie: kEnumerableProperty,
  567. [Symbol.toStringTag]: {
  568. value: 'Headers',
  569. configurable: true
  570. },
  571. [util.inspect.custom]: {
  572. enumerable: false
  573. }
  574. })
  575. webidl.converters.HeadersInit = function (V, prefix, argument) {
  576. if (webidl.util.Type(V) === webidl.util.Types.OBJECT) {
  577. const iterator = Reflect.get(V, Symbol.iterator)
  578. // A work-around to ensure we send the properly-cased Headers when V is a Headers object.
  579. // Read https://github.com/nodejs/undici/pull/3159#issuecomment-2075537226 before touching, please.
  580. if (!util.types.isProxy(V) && iterator === Headers.prototype.entries) { // Headers object
  581. try {
  582. return getHeadersList(V).entriesList
  583. } catch {
  584. // fall-through
  585. }
  586. }
  587. if (typeof iterator === 'function') {
  588. return webidl.converters['sequence<sequence<ByteString>>'](V, prefix, argument, iterator.bind(V))
  589. }
  590. return webidl.converters['record<ByteString, ByteString>'](V, prefix, argument)
  591. }
  592. throw webidl.errors.conversionFailed({
  593. prefix: 'Headers constructor',
  594. argument: 'Argument 1',
  595. types: ['sequence<sequence<ByteString>>', 'record<ByteString, ByteString>']
  596. })
  597. }
  598. module.exports = {
  599. fill,
  600. // for test.
  601. compareHeaderName,
  602. Headers,
  603. HeadersList,
  604. getHeadersGuard,
  605. setHeadersGuard,
  606. setHeadersList,
  607. getHeadersList
  608. }