webhoster 0.1.1 → 0.3.0
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/.eslintrc.json +74 -58
- package/.github/copilot-instructions.md +100 -0
- package/.github/workflows/test-matrix.yml +37 -0
- package/.test/benchmark.js +28 -0
- package/.test/constants.js +4 -0
- package/{test → .test}/http2server.js +1 -1
- package/{test → .test}/httpserver.js +1 -1
- package/{test → .test}/index.js +178 -192
- package/.test/multipromise.js +32 -0
- package/{test → .test}/tls.js +3 -3
- package/{test → .test}/urlencoded.js +3 -0
- package/.vscode/launch.json +24 -3
- package/README.md +116 -90
- package/data/CookieObject.js +14 -14
- package/errata/socketio.js +6 -11
- package/examples/starter.js +11 -0
- package/helpers/HeadersParser.js +7 -8
- package/helpers/HttpListener.js +387 -42
- package/helpers/RequestHeaders.js +43 -36
- package/helpers/RequestReader.js +27 -26
- package/helpers/ResponseHeaders.js +47 -36
- package/jsconfig.json +1 -1
- package/lib/HttpHandler.js +447 -277
- package/lib/HttpRequest.js +383 -39
- package/lib/HttpResponse.js +316 -52
- package/lib/HttpTransaction.js +146 -0
- package/middleware/AutoHeadersMiddleware.js +73 -0
- package/middleware/CORSMiddleware.js +45 -47
- package/middleware/CaseInsensitiveHeadersMiddleware.js +5 -11
- package/middleware/ContentDecoderMiddleware.js +81 -35
- package/middleware/ContentEncoderMiddleware.js +179 -132
- package/middleware/ContentLengthMiddleware.js +66 -41
- package/middleware/ContentWriterMiddleware.js +5 -5
- package/middleware/HashMiddleware.js +68 -40
- package/middleware/HeadMethodMiddleware.js +24 -21
- package/middleware/MethodMiddleware.js +29 -36
- package/middleware/PathMiddleware.js +49 -66
- package/middleware/ReadFormData.js +99 -0
- package/middleware/SendJsonMiddleware.js +131 -0
- package/middleware/SendStringMiddleware.js +87 -0
- package/package.json +38 -29
- package/polyfill/FormData.js +164 -0
- package/rollup.config.js +0 -1
- package/scripts/test-all-sync.sh +6 -0
- package/scripts/test-all.sh +7 -0
- package/templates/starter.js +53 -0
- package/test/fixtures/stream.js +68 -0
- package/test/helpers/HttpListener/construct.js +18 -0
- package/test/helpers/HttpListener/customOptions.js +22 -0
- package/test/helpers/HttpListener/doubleCreate.js +40 -0
- package/test/helpers/HttpListener/events.js +77 -0
- package/test/helpers/HttpListener/http.js +31 -0
- package/test/helpers/HttpListener/http2.js +41 -0
- package/test/helpers/HttpListener/https.js +38 -0
- package/test/helpers/HttpListener/startAll.js +31 -0
- package/test/helpers/HttpListener/stopNotStarted.js +23 -0
- package/test/lib/HttpHandler/class.js +8 -0
- package/test/lib/HttpHandler/handleRequest.js +11 -0
- package/test/lib/HttpHandler/middleware.js +941 -0
- package/test/lib/HttpHandler/parse.js +41 -0
- package/test/lib/HttpRequest/class.js +8 -0
- package/test/lib/HttpRequest/downstream.js +171 -0
- package/test/lib/HttpRequest/properties.js +101 -0
- package/test/lib/HttpRequest/read.js +518 -0
- package/test/lib/HttpResponse/class.js +8 -0
- package/test/lib/HttpResponse/properties.js +59 -0
- package/test/lib/HttpResponse/send.js +275 -0
- package/test/lib/HttpTransaction/class.js +8 -0
- package/test/lib/HttpTransaction/ping.js +50 -0
- package/test/lib/HttpTransaction/push.js +89 -0
- package/test/middleware/SendJsonMiddleware.js +222 -0
- package/test/sanity.js +10 -0
- package/test/templates/starter.js +93 -0
- package/tsconfig.json +12 -0
- package/types/index.js +61 -34
- package/types/typings.d.ts +8 -9
- package/utils/AsyncObject.js +6 -3
- package/utils/CaseInsensitiveObject.js +2 -3
- package/utils/function.js +1 -7
- package/utils/headers.js +42 -0
- package/utils/qualityValues.js +1 -1
- package/utils/stream.js +4 -20
- package/index.cjs +0 -3190
- package/test/constants.js +0 -4
- /package/{test → .test}/cookietester.js +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import HeadersHandler from './HeadersParser.js';
|
|
2
2
|
|
|
3
|
-
/** @typedef {import('../
|
|
3
|
+
/** @typedef {import('../lib/HttpRequest.js').default} HttpRequest */
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @param {string} cookieString
|
|
@@ -15,42 +15,50 @@ function getEntriesFromCookie(cookieString) {
|
|
|
15
15
|
name = '';
|
|
16
16
|
value = pair.trim();
|
|
17
17
|
} else {
|
|
18
|
-
name = pair.
|
|
19
|
-
value = pair.
|
|
18
|
+
name = pair.slice(0, indexOfEquals).trim();
|
|
19
|
+
value = pair.slice(indexOfEquals + 1).trim();
|
|
20
20
|
}
|
|
21
21
|
const firstQuote = value.indexOf('"');
|
|
22
22
|
const lastQuote = value.lastIndexOf('"');
|
|
23
23
|
if (firstQuote !== -1 && lastQuote !== -1) {
|
|
24
|
-
value = value.
|
|
24
|
+
value = value.slice(firstQuote + 1, lastQuote);
|
|
25
25
|
}
|
|
26
26
|
return [name, value];
|
|
27
27
|
});
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** @type {WeakMap<HttpRequest, RequestHeaders>} */
|
|
31
|
+
const instanceCache = new WeakMap();
|
|
32
|
+
|
|
30
33
|
export default class RequestHeaders extends HeadersHandler {
|
|
31
|
-
/** @param {HttpRequest}
|
|
32
|
-
constructor(
|
|
33
|
-
|
|
34
|
+
/** @param {HttpRequest} request */
|
|
35
|
+
constructor(request) {
|
|
36
|
+
const instance = instanceCache.get(request);
|
|
37
|
+
if (instance) return instance;
|
|
38
|
+
super(request.headers);
|
|
39
|
+
instanceCache.set(request, this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {HttpRequest} request
|
|
44
|
+
* @return {{get:(name:string)=>string,all:(name:string)=>string[]}}
|
|
45
|
+
*/
|
|
46
|
+
static cookies(request) {
|
|
47
|
+
const instance = new RequestHeaders(request);
|
|
48
|
+
return instance.cookies;
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
/** @type {Object<string,string[]>} */
|
|
37
52
|
#cookiesProxy = null;
|
|
38
53
|
|
|
54
|
+
/** @return {{get:(name:string)=>string,all:(name:string)=>string[]}} */
|
|
39
55
|
get cookies() {
|
|
40
56
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
41
57
|
const instance = this;
|
|
42
58
|
return {
|
|
43
|
-
/**
|
|
44
|
-
* @param {string} name
|
|
45
|
-
* @return {string}
|
|
46
|
-
*/
|
|
47
59
|
get(name) {
|
|
48
60
|
return instance.cookieEntries[name]?.[0];
|
|
49
61
|
},
|
|
50
|
-
/**
|
|
51
|
-
* @param {string} name
|
|
52
|
-
* @return {string[]}
|
|
53
|
-
*/
|
|
54
62
|
all(name) {
|
|
55
63
|
return instance.cookieEntries[name] ?? [];
|
|
56
64
|
},
|
|
@@ -73,42 +81,41 @@ export default class RequestHeaders extends HeadersHandler {
|
|
|
73
81
|
const cookieString = (instance.headers.cookie ?? '');
|
|
74
82
|
const split = cookieString.split(';');
|
|
75
83
|
const values = [];
|
|
76
|
-
for (
|
|
77
|
-
const [key, value] =
|
|
84
|
+
for (const element of split) {
|
|
85
|
+
const [key, value] = element.split('=');
|
|
78
86
|
if (key.trim() === cookieName) values.push(value);
|
|
79
87
|
}
|
|
80
88
|
const arrayProxy = new Proxy(values, {
|
|
81
|
-
get: (arrayTarget,
|
|
82
|
-
if (typeof
|
|
83
|
-
return Reflect.get(arrayTarget,
|
|
89
|
+
get: (arrayTarget, arrayProperty, receiver) => {
|
|
90
|
+
if (typeof arrayProperty !== 'string') {
|
|
91
|
+
return Reflect.get(arrayTarget, arrayProperty, receiver);
|
|
84
92
|
}
|
|
85
|
-
if (
|
|
93
|
+
if (arrayProperty === 'length') {
|
|
86
94
|
return getEntriesFromCookie(instance.headers.cookie ?? '')
|
|
87
95
|
.filter(([key]) => (key === cookieName)).length;
|
|
88
96
|
}
|
|
89
|
-
if (Number.isNaN(parseInt(
|
|
90
|
-
return Reflect.get(arrayTarget,
|
|
97
|
+
if (Number.isNaN(Number.parseInt(arrayProperty, 10))) {
|
|
98
|
+
return Reflect.get(arrayTarget, arrayProperty, receiver);
|
|
91
99
|
}
|
|
92
100
|
const entries = getEntriesFromCookie(instance.headers.cookie ?? '');
|
|
93
101
|
let count = 0;
|
|
94
|
-
for (
|
|
95
|
-
const entry = entries[i];
|
|
102
|
+
for (const entry of entries) {
|
|
96
103
|
if (entry[0] === cookieName) {
|
|
97
|
-
if (
|
|
104
|
+
if (arrayProperty === count.toString()) {
|
|
98
105
|
return entry[1];
|
|
99
106
|
}
|
|
100
107
|
count += 1;
|
|
101
108
|
}
|
|
102
109
|
}
|
|
103
|
-
return Reflect.get(arrayTarget,
|
|
110
|
+
return Reflect.get(arrayTarget, arrayProperty, receiver);
|
|
104
111
|
},
|
|
105
|
-
set: (arrayTarget,
|
|
106
|
-
Reflect.set(arrayTarget,
|
|
107
|
-
if (typeof
|
|
108
|
-
const result = getEntriesFromCookie(instance.headers.cookie ?? '').reduce((
|
|
109
|
-
if (!
|
|
110
|
-
if (
|
|
111
|
-
return `${
|
|
112
|
+
set: (arrayTarget, arrayProperty, value, receiver) => {
|
|
113
|
+
Reflect.set(arrayTarget, arrayProperty, value, receiver);
|
|
114
|
+
if (typeof arrayProperty !== 'string') return true;
|
|
115
|
+
const result = getEntriesFromCookie(instance.headers.cookie ?? '').reduce((previous, current) => {
|
|
116
|
+
if (!current[0]) return previous;
|
|
117
|
+
if (current[0] === cookieName) return previous;
|
|
118
|
+
return `${previous};${current[0]}=${current[1]}`;
|
|
112
119
|
}, arrayTarget.map((v) => `${cookieName}=${v}`).join(';'));
|
|
113
120
|
instance.headers.cookie = result;
|
|
114
121
|
return true;
|
|
@@ -122,8 +129,8 @@ export default class RequestHeaders extends HeadersHandler {
|
|
|
122
129
|
const split = cookieString.split(';');
|
|
123
130
|
/** @type {string[]} */
|
|
124
131
|
const keys = [];
|
|
125
|
-
for (
|
|
126
|
-
const [key] =
|
|
132
|
+
for (const element of split) {
|
|
133
|
+
const [key] = element.split('=');
|
|
127
134
|
const trimmed = key?.trim();
|
|
128
135
|
if (trimmed && !keys.includes(trimmed)) {
|
|
129
136
|
keys.push(trimmed);
|
package/helpers/RequestReader.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** @typedef {import('../types').HttpRequest} HttpRequest */
|
|
2
2
|
|
|
3
|
-
import { TextDecoder } from 'util';
|
|
3
|
+
import { TextDecoder } from 'node:util';
|
|
4
4
|
import AsyncObject from '../utils/AsyncObject.js';
|
|
5
5
|
import { noop } from '../utils/function.js';
|
|
6
6
|
import RequestHeaders from './RequestHeaders.js';
|
|
@@ -43,7 +43,7 @@ export default class RequestReader {
|
|
|
43
43
|
if (this.#buffer.isBusy() || this.#buffer.hasValue()) return this.#buffer.get();
|
|
44
44
|
this.#buffer.prepare();
|
|
45
45
|
const hp = new RequestHeaders(this.request);
|
|
46
|
-
let data = Buffer.
|
|
46
|
+
let data = Buffer.allocUnsafe(Math.min(BUFFER_SIZE, hp.contentLength || BUFFER_SIZE));
|
|
47
47
|
let bytesWritten = 0;
|
|
48
48
|
/** @type {NodeJS.Timeout} */
|
|
49
49
|
let sendPingTimeout = null;
|
|
@@ -64,7 +64,7 @@ export default class RequestReader {
|
|
|
64
64
|
while (newLength < buffer.length + data.length) {
|
|
65
65
|
newLength *= 2;
|
|
66
66
|
}
|
|
67
|
-
const newBuffer = Buffer.
|
|
67
|
+
const newBuffer = Buffer.allocUnsafe(newLength);
|
|
68
68
|
data.copy(newBuffer);
|
|
69
69
|
data = newBuffer;
|
|
70
70
|
}
|
|
@@ -80,12 +80,13 @@ export default class RequestReader {
|
|
|
80
80
|
this.request.stream.on('end', () => {
|
|
81
81
|
clearTimeout(sendPingTimeout);
|
|
82
82
|
if (data.length > bytesWritten) {
|
|
83
|
+
// Must partition to clear unsafe allocation
|
|
83
84
|
data = data.subarray(0, bytesWritten);
|
|
84
85
|
}
|
|
85
86
|
this.#buffer.set(data);
|
|
86
87
|
});
|
|
87
|
-
this.request.stream.on('error', (
|
|
88
|
-
this.#buffer.reset(
|
|
88
|
+
this.request.stream.on('error', (error) => {
|
|
89
|
+
this.#buffer.reset(error);
|
|
89
90
|
});
|
|
90
91
|
return this.#buffer.get();
|
|
91
92
|
}
|
|
@@ -93,9 +94,9 @@ export default class RequestReader {
|
|
|
93
94
|
/** @return {Promise<string>} */
|
|
94
95
|
readString() {
|
|
95
96
|
return this.readBuffer().then((buffer) => {
|
|
96
|
-
const
|
|
97
|
+
const requestHeaders = new RequestHeaders(this.request);
|
|
97
98
|
// TODO: Compare TextDecoder, Buffer.from(), and StringDecoder performance
|
|
98
|
-
const decoder = new TextDecoder(
|
|
99
|
+
const decoder = new TextDecoder(requestHeaders.charset || 'utf-8');
|
|
99
100
|
return decoder.decode(buffer);
|
|
100
101
|
});
|
|
101
102
|
}
|
|
@@ -116,38 +117,38 @@ export default class RequestReader {
|
|
|
116
117
|
*/
|
|
117
118
|
readUrlEncoded() {
|
|
118
119
|
// https://url.spec.whatwg.org/#urlencoded-parsing
|
|
119
|
-
const
|
|
120
|
-
const decoder = new TextDecoder(
|
|
120
|
+
const requestHeaders = new RequestHeaders(this.request);
|
|
121
|
+
const decoder = new TextDecoder(requestHeaders.charset || 'utf-8');
|
|
121
122
|
return this.readBuffer().then((buffer) => {
|
|
122
123
|
const sequences = [];
|
|
123
124
|
let startIndex = 0;
|
|
124
|
-
for (let
|
|
125
|
-
if (buffer[
|
|
126
|
-
sequences.push(buffer.subarray(startIndex,
|
|
127
|
-
startIndex =
|
|
125
|
+
for (let index = 0; index < buffer.length; index += 1) {
|
|
126
|
+
if (buffer[index] === 0x26) {
|
|
127
|
+
sequences.push(buffer.subarray(startIndex, index));
|
|
128
|
+
startIndex = index + 1;
|
|
128
129
|
}
|
|
129
|
-
if (
|
|
130
|
-
sequences.push(buffer.subarray(startIndex,
|
|
130
|
+
if (index === buffer.length - 1) {
|
|
131
|
+
sequences.push(buffer.subarray(startIndex, index));
|
|
131
132
|
}
|
|
132
133
|
}
|
|
133
134
|
/** @type {[string, string][]} */
|
|
134
135
|
const output = [];
|
|
135
|
-
|
|
136
|
-
if (!bytes.length)
|
|
136
|
+
for (const bytes of sequences) {
|
|
137
|
+
if (!bytes.length) continue;
|
|
137
138
|
|
|
138
139
|
// Find 0x3D and replace 0x2B in one loop for better performance
|
|
139
140
|
let indexOf0x3D = -1;
|
|
140
|
-
for (let
|
|
141
|
-
switch (bytes[
|
|
141
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
142
|
+
switch (bytes[index]) {
|
|
142
143
|
case 0x3D:
|
|
143
144
|
if (indexOf0x3D === -1) {
|
|
144
|
-
indexOf0x3D =
|
|
145
|
+
indexOf0x3D = index;
|
|
145
146
|
}
|
|
146
147
|
break;
|
|
147
148
|
case 0x2B:
|
|
148
149
|
// Replace bytes on original stream for memory conservation
|
|
149
150
|
// eslint-disable-next-line no-param-reassign
|
|
150
|
-
bytes[
|
|
151
|
+
bytes[index] = 0x20;
|
|
151
152
|
break;
|
|
152
153
|
default:
|
|
153
154
|
}
|
|
@@ -164,7 +165,7 @@ export default class RequestReader {
|
|
|
164
165
|
const nameString = decodeURIComponent(decoder.decode(name));
|
|
165
166
|
const valueString = decodeURIComponent(decoder.decode(value));
|
|
166
167
|
output.push([nameString, valueString]);
|
|
167
|
-
}
|
|
168
|
+
}
|
|
168
169
|
return output;
|
|
169
170
|
});
|
|
170
171
|
}
|
|
@@ -184,8 +185,8 @@ export default class RequestReader {
|
|
|
184
185
|
* @return {Promise<Object<string, any>|null>}
|
|
185
186
|
*/
|
|
186
187
|
readObject() {
|
|
187
|
-
const
|
|
188
|
-
const mediaType =
|
|
188
|
+
const requestHeaders = new RequestHeaders(this.request);
|
|
189
|
+
const mediaType = requestHeaders.mediaType?.toLowerCase() ?? '';
|
|
189
190
|
switch (mediaType) {
|
|
190
191
|
case 'application/json':
|
|
191
192
|
return this.readJSON();
|
|
@@ -201,8 +202,8 @@ export default class RequestReader {
|
|
|
201
202
|
* @return {Promise<Object<string,any>|string|Buffer>}
|
|
202
203
|
*/
|
|
203
204
|
read() {
|
|
204
|
-
const
|
|
205
|
-
const mediaType =
|
|
205
|
+
const requestHeaders = new RequestHeaders(this.request);
|
|
206
|
+
const mediaType = requestHeaders.mediaType?.toLowerCase() ?? '';
|
|
206
207
|
switch (mediaType) {
|
|
207
208
|
case 'application/json':
|
|
208
209
|
return this.readJSON();
|
|
@@ -2,7 +2,7 @@ import CookieObject from '../data/CookieObject.js';
|
|
|
2
2
|
|
|
3
3
|
import HeadersHandler from './HeadersParser.js';
|
|
4
4
|
|
|
5
|
-
/** @typedef {import('../
|
|
5
|
+
/** @typedef {import('../lib/HttpResponse.js').default} HttpResponse */
|
|
6
6
|
/** @typedef {import('../types').CookieDetails} CookieDetails */
|
|
7
7
|
|
|
8
8
|
/** @type {(keyof CookieDetails)[]} */
|
|
@@ -18,10 +18,23 @@ const COOKIE_DETAIL_KEYS = [
|
|
|
18
18
|
'sameSite',
|
|
19
19
|
];
|
|
20
20
|
|
|
21
|
+
/** @type {WeakMap<HttpResponse, ResponseHeaders>} */
|
|
22
|
+
const instanceCache = new WeakMap();
|
|
23
|
+
|
|
21
24
|
export default class ResponseHeaders extends HeadersHandler {
|
|
22
25
|
/** @param {HttpResponse} res */
|
|
26
|
+
// @ts-ignore Cached constructor
|
|
23
27
|
constructor(res) {
|
|
28
|
+
const instance = instanceCache.get(res);
|
|
29
|
+
if (instance) return instance;
|
|
24
30
|
super(res.headers);
|
|
31
|
+
instanceCache.set(res, this);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @param {HttpResponse} res */
|
|
35
|
+
static cookies(res) {
|
|
36
|
+
const instance = new ResponseHeaders(res);
|
|
37
|
+
return instance.cookies;
|
|
25
38
|
}
|
|
26
39
|
|
|
27
40
|
/** @type {CookieObject[]} */
|
|
@@ -29,8 +42,8 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
29
42
|
|
|
30
43
|
/** @type {ProxyHandler<CookieObject>} */
|
|
31
44
|
#cookieObjectProxyHandler = {
|
|
32
|
-
set: (cookieTarget,
|
|
33
|
-
Reflect.set(cookieTarget,
|
|
45
|
+
set: (cookieTarget, cookieProperty, cookieValue, receiver) => {
|
|
46
|
+
Reflect.set(cookieTarget, cookieProperty, cookieValue, receiver);
|
|
34
47
|
const index = this.cookieEntries.findIndex((entry) => entry.toString() === cookieTarget.toString());
|
|
35
48
|
if (index !== -1) {
|
|
36
49
|
// Force reflection
|
|
@@ -94,9 +107,9 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
94
107
|
case 'utf-8':
|
|
95
108
|
case 'utf8':
|
|
96
109
|
return 'utf-8';
|
|
97
|
-
default:
|
|
98
110
|
case 'base64':
|
|
99
111
|
case 'hex':
|
|
112
|
+
default:
|
|
100
113
|
return /** @type {BufferEncoding} */ (charset);
|
|
101
114
|
}
|
|
102
115
|
}
|
|
@@ -118,9 +131,9 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
118
131
|
case 'utf8':
|
|
119
132
|
this.charset = 'utf-8';
|
|
120
133
|
break;
|
|
121
|
-
default:
|
|
122
134
|
case 'base64':
|
|
123
135
|
case 'hex':
|
|
136
|
+
default:
|
|
124
137
|
this.charset = bufferEncoding;
|
|
125
138
|
}
|
|
126
139
|
}
|
|
@@ -149,7 +162,7 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
get cookies() {
|
|
152
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
165
|
+
// eslint-disable-next-line unicorn/no-this-assignment, @typescript-eslint/no-this-alias
|
|
153
166
|
const instance = this;
|
|
154
167
|
return {
|
|
155
168
|
/**
|
|
@@ -205,7 +218,7 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
205
218
|
return details[key] === cookieObject[key];
|
|
206
219
|
}
|
|
207
220
|
}))
|
|
208
|
-
.sort((a, b) => (b?.path?.length ?? 0 - a?.path?.length ?? 0));
|
|
221
|
+
.sort((a, b) => ((b?.path?.length ?? 0) - (a?.path?.length ?? 0)));
|
|
209
222
|
},
|
|
210
223
|
/**
|
|
211
224
|
* @param {string|CookieDetails} cookie
|
|
@@ -217,14 +230,14 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
217
230
|
path: '',
|
|
218
231
|
...details,
|
|
219
232
|
});
|
|
220
|
-
if (
|
|
233
|
+
if (cookieObject) {
|
|
234
|
+
for (const key of COOKIE_DETAIL_KEYS) {
|
|
235
|
+
// @ts-expect-error Coerce
|
|
236
|
+
cookieObject[key] = details[key];
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
221
239
|
cookieObject = new Proxy(new CookieObject(details), instance.#cookieObjectProxyHandler);
|
|
222
240
|
instance.cookieEntries.push(cookieObject);
|
|
223
|
-
} else {
|
|
224
|
-
COOKIE_DETAIL_KEYS.forEach((key) => {
|
|
225
|
-
// @ts-ignore Coerce
|
|
226
|
-
cookieObject[key] = details[key];
|
|
227
|
-
});
|
|
228
241
|
}
|
|
229
242
|
return cookieObject;
|
|
230
243
|
},
|
|
@@ -235,9 +248,9 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
235
248
|
remove(partial) {
|
|
236
249
|
const items = this.getAll(partial);
|
|
237
250
|
const count = items.length;
|
|
238
|
-
|
|
251
|
+
for (const item of items) {
|
|
239
252
|
instance.cookieEntries.splice(instance.cookieEntries.indexOf(item), 1);
|
|
240
|
-
}
|
|
253
|
+
}
|
|
241
254
|
return count;
|
|
242
255
|
},
|
|
243
256
|
/**
|
|
@@ -262,13 +275,11 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
262
275
|
*/
|
|
263
276
|
expireAll(partial) {
|
|
264
277
|
const items = this.getAll(partial);
|
|
265
|
-
|
|
266
|
-
/* eslint-disable no-param-reassign */
|
|
278
|
+
for (const item of items) {
|
|
267
279
|
item.expires = null;
|
|
268
280
|
item.maxAge = 0;
|
|
269
281
|
item.value = '';
|
|
270
|
-
|
|
271
|
-
});
|
|
282
|
+
}
|
|
272
283
|
return items;
|
|
273
284
|
},
|
|
274
285
|
};
|
|
@@ -289,37 +300,37 @@ export default class ResponseHeaders extends HeadersHandler {
|
|
|
289
300
|
instance.#cookieObjectProxyHandler,
|
|
290
301
|
));
|
|
291
302
|
this.#setCookiesProxy = new Proxy(values, {
|
|
292
|
-
get: (arrayTarget,
|
|
293
|
-
if (typeof
|
|
294
|
-
return Reflect.get(arrayTarget,
|
|
303
|
+
get: (arrayTarget, arrayProperty, receiver) => {
|
|
304
|
+
if (typeof arrayProperty !== 'string') {
|
|
305
|
+
return Reflect.get(arrayTarget, arrayProperty, receiver);
|
|
295
306
|
}
|
|
296
|
-
if (
|
|
307
|
+
if (arrayProperty === 'length') {
|
|
297
308
|
return instance.headers['set-cookie'].length;
|
|
298
309
|
}
|
|
299
|
-
if (Number.isNaN(parseInt(
|
|
300
|
-
return Reflect.get(arrayTarget,
|
|
310
|
+
if (Number.isNaN(Number.parseInt(arrayProperty, 10))) {
|
|
311
|
+
return Reflect.get(arrayTarget, arrayProperty, receiver);
|
|
301
312
|
}
|
|
302
|
-
const entry = instance.headers['set-cookie'][
|
|
303
|
-
if (
|
|
313
|
+
const entry = instance.headers['set-cookie'][arrayProperty];
|
|
314
|
+
if (entry === undefined) {
|
|
304
315
|
return entry;
|
|
305
316
|
}
|
|
306
|
-
if (
|
|
317
|
+
if (arrayProperty in arrayTarget === false) {
|
|
307
318
|
Reflect.set(
|
|
308
319
|
arrayTarget,
|
|
309
|
-
|
|
320
|
+
arrayProperty,
|
|
310
321
|
new Proxy(new CookieObject(entry), instance.#cookieObjectProxyHandler),
|
|
311
322
|
);
|
|
312
323
|
}
|
|
313
|
-
return Reflect.get(arrayTarget,
|
|
324
|
+
return Reflect.get(arrayTarget, arrayProperty, receiver);
|
|
314
325
|
},
|
|
315
|
-
set: (arrayTarget,
|
|
316
|
-
Reflect.set(arrayTarget,
|
|
317
|
-
if (typeof
|
|
318
|
-
if (
|
|
319
|
-
Reflect.set(instance.headers['set-cookie'],
|
|
326
|
+
set: (arrayTarget, arrayProperty, value, receiver) => {
|
|
327
|
+
Reflect.set(arrayTarget, arrayProperty, value, receiver);
|
|
328
|
+
if (typeof arrayProperty !== 'string') return true;
|
|
329
|
+
if (arrayProperty === 'length') {
|
|
330
|
+
Reflect.set(instance.headers['set-cookie'], arrayProperty, value);
|
|
320
331
|
}
|
|
321
332
|
if (value instanceof CookieObject) {
|
|
322
|
-
instance.headers['set-cookie'][
|
|
333
|
+
instance.headers['set-cookie'][arrayProperty] = value.toString();
|
|
323
334
|
}
|
|
324
335
|
return true;
|
|
325
336
|
},
|