undici 5.14.0 → 5.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/api/Cookies.md +101 -0
- package/docs/api/DiagnosticsChannel.md +67 -0
- package/docs/api/Dispatcher.md +1 -0
- package/docs/api/Fetch.md +25 -0
- package/docs/api/WebSocket.md +20 -0
- package/index.d.ts +2 -0
- package/index.js +15 -0
- package/lib/client.js +16 -6
- package/lib/cookies/constants.js +12 -0
- package/lib/cookies/index.js +183 -0
- package/lib/cookies/parse.js +317 -0
- package/lib/cookies/util.js +291 -0
- package/lib/core/request.js +30 -5
- package/lib/core/symbols.js +1 -1
- package/lib/fetch/body.js +123 -185
- package/lib/fetch/formdata.js +1 -1
- package/lib/fetch/headers.js +17 -0
- package/lib/fetch/index.js +40 -6
- package/lib/fetch/request.js +4 -1
- package/lib/fetch/util.js +62 -34
- package/lib/fetch/webidl.js +16 -4
- package/lib/websocket/connection.js +324 -0
- package/lib/websocket/constants.js +51 -0
- package/lib/websocket/events.js +303 -0
- package/lib/websocket/frame.js +66 -0
- package/lib/websocket/receiver.js +344 -0
- package/lib/websocket/symbols.js +15 -0
- package/lib/websocket/util.js +200 -0
- package/lib/websocket/websocket.js +550 -0
- package/package.json +8 -5
- package/types/cookies.d.ts +28 -0
- package/types/dispatcher.d.ts +2 -0
- package/types/patch.d.ts +20 -0
- package/types/proxy-agent.d.ts +3 -3
- package/types/webidl.d.ts +7 -2
- package/types/websocket.d.ts +121 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Cookie Handling
|
|
2
|
+
|
|
3
|
+
## `Cookie` interface
|
|
4
|
+
|
|
5
|
+
* **name** `string`
|
|
6
|
+
* **value** `string`
|
|
7
|
+
* **expires** `Date|number` (optional)
|
|
8
|
+
* **maxAge** `number` (optional)
|
|
9
|
+
* **domain** `string` (optional)
|
|
10
|
+
* **path** `string` (optional)
|
|
11
|
+
* **secure** `boolean` (optional)
|
|
12
|
+
* **httpOnly** `boolean` (optional)
|
|
13
|
+
* **sameSite** `'String'|'Lax'|'None'` (optional)
|
|
14
|
+
* **unparsed** `string[]` (optional) Left over attributes that weren't parsed.
|
|
15
|
+
|
|
16
|
+
## `deleteCookie(headers, name[, attributes])`
|
|
17
|
+
|
|
18
|
+
Sets the expiry time of the cookie to the unix epoch, causing browsers to delete it when received.
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import { deleteCookie, Headers } from 'undici'
|
|
22
|
+
|
|
23
|
+
const headers = new Headers()
|
|
24
|
+
deleteCookie(headers, 'name')
|
|
25
|
+
|
|
26
|
+
console.log(headers.get('set-cookie')) // name=; Expires=Thu, 01 Jan 1970 00:00:00 GMT
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Arguments:
|
|
30
|
+
|
|
31
|
+
* **headers** `Headers`
|
|
32
|
+
* **name** `string`
|
|
33
|
+
* **attributes** `{ path?: string, domain?: string }` (optional)
|
|
34
|
+
|
|
35
|
+
Returns: `void`
|
|
36
|
+
|
|
37
|
+
## `getCookies(headers)`
|
|
38
|
+
|
|
39
|
+
Parses the `Cookie` header and returns a list of attributes and values.
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
import { getCookies, Headers } from 'undici'
|
|
43
|
+
|
|
44
|
+
const headers = new Headers({
|
|
45
|
+
cookie: 'get=cookies; and=attributes'
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
console.log(getCookies(headers)) // { get: 'cookies', and: 'attributes' }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Arguments:
|
|
52
|
+
|
|
53
|
+
* **headers** `Headers`
|
|
54
|
+
|
|
55
|
+
Returns: `Record<string, string>`
|
|
56
|
+
|
|
57
|
+
## `getSetCookies(headers)`
|
|
58
|
+
|
|
59
|
+
Parses all `Set-Cookie` headers.
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { getSetCookies, Headers } from 'undici'
|
|
63
|
+
|
|
64
|
+
const headers = new Headers({ 'set-cookie': 'undici=getSetCookies; Secure' })
|
|
65
|
+
|
|
66
|
+
console.log(getSetCookies(headers))
|
|
67
|
+
// [
|
|
68
|
+
// {
|
|
69
|
+
// name: 'undici',
|
|
70
|
+
// value: 'getSetCookies',
|
|
71
|
+
// secure: true
|
|
72
|
+
// }
|
|
73
|
+
// ]
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Arguments:
|
|
78
|
+
|
|
79
|
+
* **headers** `Headers`
|
|
80
|
+
|
|
81
|
+
Returns: `Cookie[]`
|
|
82
|
+
|
|
83
|
+
## `setCookie(headers, cookie)`
|
|
84
|
+
|
|
85
|
+
Appends a cookie to the `Set-Cookie` header.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
import { setCookie, Headers } from 'undici'
|
|
89
|
+
|
|
90
|
+
const headers = new Headers()
|
|
91
|
+
setCookie(headers, { name: 'undici', value: 'setCookie' })
|
|
92
|
+
|
|
93
|
+
console.log(headers.get('Set-Cookie')) // undici=setCookie
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Arguments:
|
|
97
|
+
|
|
98
|
+
* **headers** `Headers`
|
|
99
|
+
* **cookie** `Cookie`
|
|
100
|
+
|
|
101
|
+
Returns: `void`
|
|
@@ -135,3 +135,70 @@ diagnosticsChannel.channel('undici:client:connectError').subscribe(({ error, soc
|
|
|
135
135
|
// connector is a function that creates the socket
|
|
136
136
|
console.log(`Connect failed with ${error.message}`)
|
|
137
137
|
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## `undici:websocket:open`
|
|
141
|
+
|
|
142
|
+
This message is published after the client has successfully connected to a server.
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
146
|
+
|
|
147
|
+
diagnosticsChannel.channel('undici:websocket:open').subscribe(({ address, protocol, extensions }) => {
|
|
148
|
+
console.log(address) // address, family, and port
|
|
149
|
+
console.log(protocol) // negotiated subprotocols
|
|
150
|
+
console.log(extensions) // negotiated extensions
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## `undici:websocket:close`
|
|
155
|
+
|
|
156
|
+
This message is published after the connection has closed.
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
160
|
+
|
|
161
|
+
diagnosticsChannel.channel('undici:websocket:close').subscribe(({ websocket, code, reason }) => {
|
|
162
|
+
console.log(websocket) // the WebSocket object
|
|
163
|
+
console.log(code) // the closing status code
|
|
164
|
+
console.log(reason) // the closing reason
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## `undici:websocket:socket_error`
|
|
169
|
+
|
|
170
|
+
This message is published if the socket experiences an error.
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
174
|
+
|
|
175
|
+
diagnosticsChannel.channel('undici:websocket:socket_error').subscribe((error) => {
|
|
176
|
+
console.log(error)
|
|
177
|
+
})
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## `undici:websocket:ping`
|
|
181
|
+
|
|
182
|
+
This message is published after the client receives a ping frame, if the connection is not closing.
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
186
|
+
|
|
187
|
+
diagnosticsChannel.channel('undici:websocket:ping').subscribe(({ payload }) => {
|
|
188
|
+
// a Buffer or undefined, containing the optional application data of the frame
|
|
189
|
+
console.log(payload)
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## `undici:websocket:pong`
|
|
194
|
+
|
|
195
|
+
This message is published after the client receives a pong frame.
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
import diagnosticsChannel from 'diagnostics_channel'
|
|
199
|
+
|
|
200
|
+
diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload }) => {
|
|
201
|
+
// a Buffer or undefined, containing the optional application data of the frame
|
|
202
|
+
console.log(payload)
|
|
203
|
+
})
|
|
204
|
+
```
|
package/docs/api/Dispatcher.md
CHANGED
|
@@ -192,6 +192,7 @@ Returns: `Boolean` - `false` if dispatcher is busy and further dispatch calls wo
|
|
|
192
192
|
* **origin** `string | URL`
|
|
193
193
|
* **path** `string`
|
|
194
194
|
* **method** `string`
|
|
195
|
+
* **reset** `boolean` (optional) - Default: `false` - If `false`, the request will attempt to create a long-living connection by sending the `connection: keep-alive` header,otherwise will attempt to close it immediately after response by sending `connection: close` within the request and closing the socket afterwards.
|
|
195
196
|
* **body** `string | Buffer | Uint8Array | stream.Readable | Iterable | AsyncIterable | null` (optional) - Default: `null`
|
|
196
197
|
* **headers** `UndiciHeaders | string[]` (optional) - Default: `null`.
|
|
197
198
|
* **query** `Record<string, any> | null` (optional) - Default: `null` - Query string params to be embedded in the request URL. Note that both keys and values of query are encoded using `encodeURIComponent`. If for some reason you need to send them unencoded, embed query params into path directly instead.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Fetch
|
|
2
|
+
|
|
3
|
+
Undici exposes a fetch() method starts the process of fetching a resource from the network.
|
|
4
|
+
|
|
5
|
+
Documentation and examples can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
|
|
6
|
+
|
|
7
|
+
## File
|
|
8
|
+
|
|
9
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/File)
|
|
10
|
+
|
|
11
|
+
## FormData
|
|
12
|
+
|
|
13
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
|
|
14
|
+
|
|
15
|
+
## Response
|
|
16
|
+
|
|
17
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
|
18
|
+
|
|
19
|
+
## Request
|
|
20
|
+
|
|
21
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
22
|
+
|
|
23
|
+
## Header
|
|
24
|
+
|
|
25
|
+
This API is implemented as per the standard, you can find documentation on [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Headers)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Class: WebSocket
|
|
2
|
+
|
|
3
|
+
> ⚠️ Warning: the WebSocket API is experimental and has known bugs.
|
|
4
|
+
|
|
5
|
+
Extends: [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
|
|
6
|
+
|
|
7
|
+
The WebSocket object provides a way to manage a WebSocket connection to a server, allowing bidirectional communication. The API follows the [WebSocket spec](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket).
|
|
8
|
+
|
|
9
|
+
## `new WebSocket(url[, protocol])`
|
|
10
|
+
|
|
11
|
+
Arguments:
|
|
12
|
+
|
|
13
|
+
* **url** `URL | string` - The url's protocol *must* be `ws` or `wss`.
|
|
14
|
+
* **protocol** `string | string[]` (optional) - Subprotocol(s) to request the server use.
|
|
15
|
+
|
|
16
|
+
## Read More
|
|
17
|
+
|
|
18
|
+
- [MDN - WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
|
19
|
+
- [The WebSocket Specification](https://www.rfc-editor.org/rfc/rfc6455)
|
|
20
|
+
- [The WHATWG WebSocket Specification](https://websockets.spec.whatwg.org/)
|
package/index.d.ts
CHANGED
|
@@ -16,11 +16,13 @@ import mockErrors from'./types/mock-errors'
|
|
|
16
16
|
import ProxyAgent from'./types/proxy-agent'
|
|
17
17
|
import { request, pipeline, stream, connect, upgrade } from './types/api'
|
|
18
18
|
|
|
19
|
+
export * from './types/cookies'
|
|
19
20
|
export * from './types/fetch'
|
|
20
21
|
export * from './types/file'
|
|
21
22
|
export * from './types/filereader'
|
|
22
23
|
export * from './types/formdata'
|
|
23
24
|
export * from './types/diagnostics-channel'
|
|
25
|
+
export * from './types/websocket'
|
|
24
26
|
export { Interceptable } from './types/mock-interceptor'
|
|
25
27
|
|
|
26
28
|
export { Dispatcher, BalancedPool, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, setGlobalOrigin, getGlobalOrigin, MockClient, MockPool, MockAgent, mockErrors, ProxyAgent, RedirectHandler, DecoratorHandler }
|
package/index.js
CHANGED
|
@@ -119,6 +119,21 @@ if (nodeMajor > 16 || (nodeMajor === 16 && nodeMinor >= 8)) {
|
|
|
119
119
|
module.exports.getGlobalOrigin = getGlobalOrigin
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
+
if (nodeMajor >= 16) {
|
|
123
|
+
const { deleteCookie, getCookies, getSetCookies, setCookie } = require('./lib/cookies')
|
|
124
|
+
|
|
125
|
+
module.exports.deleteCookie = deleteCookie
|
|
126
|
+
module.exports.getCookies = getCookies
|
|
127
|
+
module.exports.getSetCookies = getSetCookies
|
|
128
|
+
module.exports.setCookie = setCookie
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (nodeMajor >= 18) {
|
|
132
|
+
const { WebSocket } = require('./lib/websocket/websocket')
|
|
133
|
+
|
|
134
|
+
module.exports.WebSocket = WebSocket
|
|
135
|
+
}
|
|
136
|
+
|
|
122
137
|
module.exports.request = makeDispatcher(api.request)
|
|
123
138
|
module.exports.stream = makeDispatcher(api.stream)
|
|
124
139
|
module.exports.pipeline = makeDispatcher(api.pipeline)
|
package/lib/client.js
CHANGED
|
@@ -441,6 +441,7 @@ class Parser {
|
|
|
441
441
|
|
|
442
442
|
this.keepAlive = ''
|
|
443
443
|
this.contentLength = ''
|
|
444
|
+
this.connection = ''
|
|
444
445
|
this.maxResponseSize = client[kMaxResponseSize]
|
|
445
446
|
}
|
|
446
447
|
|
|
@@ -616,6 +617,8 @@ class Parser {
|
|
|
616
617
|
const key = this.headers[len - 2]
|
|
617
618
|
if (key.length === 10 && key.toString().toLowerCase() === 'keep-alive') {
|
|
618
619
|
this.keepAlive += buf.toString()
|
|
620
|
+
} else if (key.length === 10 && key.toString().toLowerCase() === 'connection') {
|
|
621
|
+
this.connection += buf.toString()
|
|
619
622
|
} else if (key.length === 14 && key.toString().toLowerCase() === 'content-length') {
|
|
620
623
|
this.contentLength += buf.toString()
|
|
621
624
|
}
|
|
@@ -709,7 +712,11 @@ class Parser {
|
|
|
709
712
|
assert.strictEqual(this.timeoutType, TIMEOUT_HEADERS)
|
|
710
713
|
|
|
711
714
|
this.statusCode = statusCode
|
|
712
|
-
this.shouldKeepAlive =
|
|
715
|
+
this.shouldKeepAlive = (
|
|
716
|
+
shouldKeepAlive ||
|
|
717
|
+
// Override llhttp value which does not allow keepAlive for HEAD.
|
|
718
|
+
(request.method === 'HEAD' && !socket[kReset] && this.connection.toLowerCase() === 'keep-alive')
|
|
719
|
+
)
|
|
713
720
|
|
|
714
721
|
if (this.statusCode >= 200) {
|
|
715
722
|
const bodyTimeout = request.bodyTimeout != null
|
|
@@ -739,7 +746,7 @@ class Parser {
|
|
|
739
746
|
this.headers = []
|
|
740
747
|
this.headersSize = 0
|
|
741
748
|
|
|
742
|
-
if (shouldKeepAlive && client[kPipelining]) {
|
|
749
|
+
if (this.shouldKeepAlive && client[kPipelining]) {
|
|
743
750
|
const keepAliveTimeout = this.keepAlive ? util.parseKeepAliveTimeout(this.keepAlive) : null
|
|
744
751
|
|
|
745
752
|
if (keepAliveTimeout != null) {
|
|
@@ -769,7 +776,6 @@ class Parser {
|
|
|
769
776
|
}
|
|
770
777
|
|
|
771
778
|
if (request.method === 'HEAD') {
|
|
772
|
-
assert(socket[kReset])
|
|
773
779
|
return 1
|
|
774
780
|
}
|
|
775
781
|
|
|
@@ -843,6 +849,7 @@ class Parser {
|
|
|
843
849
|
this.bytesRead = 0
|
|
844
850
|
this.contentLength = ''
|
|
845
851
|
this.keepAlive = ''
|
|
852
|
+
this.connection = ''
|
|
846
853
|
|
|
847
854
|
assert(this.headers.length % 2 === 0)
|
|
848
855
|
this.headers = []
|
|
@@ -1295,7 +1302,7 @@ function _resume (client, sync) {
|
|
|
1295
1302
|
}
|
|
1296
1303
|
|
|
1297
1304
|
function write (client, request) {
|
|
1298
|
-
const { body, method, path, host, upgrade, headers, blocking } = request
|
|
1305
|
+
const { body, method, path, host, upgrade, headers, blocking, reset } = request
|
|
1299
1306
|
|
|
1300
1307
|
// https://tools.ietf.org/html/rfc7231#section-4.3.1
|
|
1301
1308
|
// https://tools.ietf.org/html/rfc7231#section-4.3.2
|
|
@@ -1363,7 +1370,6 @@ function write (client, request) {
|
|
|
1363
1370
|
|
|
1364
1371
|
if (method === 'HEAD') {
|
|
1365
1372
|
// https://github.com/mcollina/undici/issues/258
|
|
1366
|
-
|
|
1367
1373
|
// Close after a HEAD request to interop with misbehaving servers
|
|
1368
1374
|
// that may send a body in the response.
|
|
1369
1375
|
|
|
@@ -1377,6 +1383,10 @@ function write (client, request) {
|
|
|
1377
1383
|
socket[kReset] = true
|
|
1378
1384
|
}
|
|
1379
1385
|
|
|
1386
|
+
if (reset != null) {
|
|
1387
|
+
socket[kReset] = reset
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1380
1390
|
if (client[kMaxRequests] && socket[kCounter]++ >= client[kMaxRequests]) {
|
|
1381
1391
|
socket[kReset] = true
|
|
1382
1392
|
}
|
|
@@ -1395,7 +1405,7 @@ function write (client, request) {
|
|
|
1395
1405
|
|
|
1396
1406
|
if (upgrade) {
|
|
1397
1407
|
header += `connection: upgrade\r\nupgrade: ${upgrade}\r\n`
|
|
1398
|
-
} else if (client[kPipelining]) {
|
|
1408
|
+
} else if (client[kPipelining] && !socket[kReset]) {
|
|
1399
1409
|
header += 'connection: keep-alive\r\n'
|
|
1400
1410
|
} else {
|
|
1401
1411
|
header += 'connection: close\r\n'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size
|
|
4
|
+
const maxAttributeValueSize = 1024
|
|
5
|
+
|
|
6
|
+
// https://wicg.github.io/cookie-store/#cookie-maximum-name-value-pair-size
|
|
7
|
+
const maxNameValuePairSize = 4096
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
maxAttributeValueSize,
|
|
11
|
+
maxNameValuePairSize
|
|
12
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { parseSetCookie } = require('./parse')
|
|
4
|
+
const { stringify, getHeadersList } = require('./util')
|
|
5
|
+
const { webidl } = require('../fetch/webidl')
|
|
6
|
+
const { 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
|
+
*/
|
|
26
|
+
function 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
|
+
*/
|
|
53
|
+
function 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
|
+
*/
|
|
75
|
+
function 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
|
+
return cookies.map((pair) => parseSetCookie(pair[1]))
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {Headers} headers
|
|
91
|
+
* @param {Cookie} cookie
|
|
92
|
+
* @returns {void}
|
|
93
|
+
*/
|
|
94
|
+
function setCookie (headers, cookie) {
|
|
95
|
+
webidl.argumentLengthCheck(arguments, 2, { header: 'setCookie' })
|
|
96
|
+
|
|
97
|
+
webidl.brandCheck(headers, Headers, { strict: false })
|
|
98
|
+
|
|
99
|
+
cookie = webidl.converters.Cookie(cookie)
|
|
100
|
+
|
|
101
|
+
const str = stringify(cookie)
|
|
102
|
+
|
|
103
|
+
if (str) {
|
|
104
|
+
headers.append('Set-Cookie', stringify(cookie))
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
webidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([
|
|
109
|
+
{
|
|
110
|
+
converter: webidl.nullableConverter(webidl.converters.DOMString),
|
|
111
|
+
key: 'path',
|
|
112
|
+
defaultValue: null
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
converter: webidl.nullableConverter(webidl.converters.DOMString),
|
|
116
|
+
key: 'domain',
|
|
117
|
+
defaultValue: null
|
|
118
|
+
}
|
|
119
|
+
])
|
|
120
|
+
|
|
121
|
+
webidl.converters.Cookie = webidl.dictionaryConverter([
|
|
122
|
+
{
|
|
123
|
+
converter: webidl.converters.DOMString,
|
|
124
|
+
key: 'name'
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
converter: webidl.converters.DOMString,
|
|
128
|
+
key: 'value'
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
converter: webidl.nullableConverter((value) => {
|
|
132
|
+
if (typeof value === 'number') {
|
|
133
|
+
return webidl.converters['unsigned long long'](value)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return new Date(value)
|
|
137
|
+
}),
|
|
138
|
+
key: 'expires',
|
|
139
|
+
defaultValue: null
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
converter: webidl.nullableConverter(webidl.converters['long long']),
|
|
143
|
+
key: 'maxAge',
|
|
144
|
+
defaultValue: null
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
converter: webidl.nullableConverter(webidl.converters.DOMString),
|
|
148
|
+
key: 'domain',
|
|
149
|
+
defaultValue: null
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
converter: webidl.nullableConverter(webidl.converters.DOMString),
|
|
153
|
+
key: 'path',
|
|
154
|
+
defaultValue: null
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
converter: webidl.nullableConverter(webidl.converters.boolean),
|
|
158
|
+
key: 'secure',
|
|
159
|
+
defaultValue: null
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
converter: webidl.nullableConverter(webidl.converters.boolean),
|
|
163
|
+
key: 'httpOnly',
|
|
164
|
+
defaultValue: null
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
converter: webidl.converters.USVString,
|
|
168
|
+
key: 'sameSite',
|
|
169
|
+
allowedValues: ['Strict', 'Lax', 'None']
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
converter: webidl.sequenceConverter(webidl.converters.DOMString),
|
|
173
|
+
key: 'unparsed',
|
|
174
|
+
defaultValue: []
|
|
175
|
+
}
|
|
176
|
+
])
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
getCookies,
|
|
180
|
+
deleteCookie,
|
|
181
|
+
getSetCookies,
|
|
182
|
+
setCookie
|
|
183
|
+
}
|