• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict'
2
3const { parseSetCookie } = require('./parse')
4const { stringify, getHeadersList } = require('./util')
5const { webidl } = require('../fetch/webidl')
6const { Headers } = require('../fetch/headers')
7
8/**
9 * @typedef {Object} Cookie
10 * @property {string} name
11 * @property {string} value
12 * @property {Date|number|undefined} expires
13 * @property {number|undefined} maxAge
14 * @property {string|undefined} domain
15 * @property {string|undefined} path
16 * @property {boolean|undefined} secure
17 * @property {boolean|undefined} httpOnly
18 * @property {'Strict'|'Lax'|'None'} sameSite
19 * @property {string[]} unparsed
20 */
21
22/**
23 * @param {Headers} headers
24 * @returns {Record<string, string>}
25 */
26function getCookies (headers) {
27  webidl.argumentLengthCheck(arguments, 1, { header: 'getCookies' })
28
29  webidl.brandCheck(headers, Headers, { strict: false })
30
31  const cookie = headers.get('cookie')
32  const out = {}
33
34  if (!cookie) {
35    return out
36  }
37
38  for (const piece of cookie.split(';')) {
39    const [name, ...value] = piece.split('=')
40
41    out[name.trim()] = value.join('=')
42  }
43
44  return out
45}
46
47/**
48 * @param {Headers} headers
49 * @param {string} name
50 * @param {{ path?: string, domain?: string }|undefined} attributes
51 * @returns {void}
52 */
53function deleteCookie (headers, name, attributes) {
54  webidl.argumentLengthCheck(arguments, 2, { header: 'deleteCookie' })
55
56  webidl.brandCheck(headers, Headers, { strict: false })
57
58  name = webidl.converters.DOMString(name)
59  attributes = webidl.converters.DeleteCookieAttributes(attributes)
60
61  // Matches behavior of
62  // https://github.com/denoland/deno_std/blob/63827b16330b82489a04614027c33b7904e08be5/http/cookie.ts#L278
63  setCookie(headers, {
64    name,
65    value: '',
66    expires: new Date(0),
67    ...attributes
68  })
69}
70
71/**
72 * @param {Headers} headers
73 * @returns {Cookie[]}
74 */
75function getSetCookies (headers) {
76  webidl.argumentLengthCheck(arguments, 1, { header: 'getSetCookies' })
77
78  webidl.brandCheck(headers, Headers, { strict: false })
79
80  const cookies = getHeadersList(headers).cookies
81
82  if (!cookies) {
83    return []
84  }
85
86  // In older versions of undici, cookies is a list of name:value.
87  return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair))
88}
89
90/**
91 * @param {Headers} headers
92 * @param {Cookie} cookie
93 * @returns {void}
94 */
95function setCookie (headers, cookie) {
96  webidl.argumentLengthCheck(arguments, 2, { header: 'setCookie' })
97
98  webidl.brandCheck(headers, Headers, { strict: false })
99
100  cookie = webidl.converters.Cookie(cookie)
101
102  const str = stringify(cookie)
103
104  if (str) {
105    headers.append('Set-Cookie', stringify(cookie))
106  }
107}
108
109webidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([
110  {
111    converter: webidl.nullableConverter(webidl.converters.DOMString),
112    key: 'path',
113    defaultValue: null
114  },
115  {
116    converter: webidl.nullableConverter(webidl.converters.DOMString),
117    key: 'domain',
118    defaultValue: null
119  }
120])
121
122webidl.converters.Cookie = webidl.dictionaryConverter([
123  {
124    converter: webidl.converters.DOMString,
125    key: 'name'
126  },
127  {
128    converter: webidl.converters.DOMString,
129    key: 'value'
130  },
131  {
132    converter: webidl.nullableConverter((value) => {
133      if (typeof value === 'number') {
134        return webidl.converters['unsigned long long'](value)
135      }
136
137      return new Date(value)
138    }),
139    key: 'expires',
140    defaultValue: null
141  },
142  {
143    converter: webidl.nullableConverter(webidl.converters['long long']),
144    key: 'maxAge',
145    defaultValue: null
146  },
147  {
148    converter: webidl.nullableConverter(webidl.converters.DOMString),
149    key: 'domain',
150    defaultValue: null
151  },
152  {
153    converter: webidl.nullableConverter(webidl.converters.DOMString),
154    key: 'path',
155    defaultValue: null
156  },
157  {
158    converter: webidl.nullableConverter(webidl.converters.boolean),
159    key: 'secure',
160    defaultValue: null
161  },
162  {
163    converter: webidl.nullableConverter(webidl.converters.boolean),
164    key: 'httpOnly',
165    defaultValue: null
166  },
167  {
168    converter: webidl.converters.USVString,
169    key: 'sameSite',
170    allowedValues: ['Strict', 'Lax', 'None']
171  },
172  {
173    converter: webidl.sequenceConverter(webidl.converters.DOMString),
174    key: 'unparsed',
175    defaultValue: []
176  }
177])
178
179module.exports = {
180  getCookies,
181  deleteCookie,
182  getSetCookies,
183  setCookie
184}
185