ultimate-express 1.3.14 → 1.3.15

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/src/request.js CHANGED
@@ -1,404 +1,404 @@
1
- /*
2
- Copyright 2024 dimden.dev
3
-
4
- Licensed under the Apache License, Version 2.0 (the "License");
5
- you may not use this file except in compliance with the License.
6
- You may obtain a copy of the License at
7
-
8
- http://www.apache.org/licenses/LICENSE-2.0
9
-
10
- Unless required by applicable law or agreed to in writing, software
11
- distributed under the License is distributed on an "AS IS" BASIS,
12
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- See the License for the specific language governing permissions and
14
- limitations under the License.
15
- */
16
-
17
- const { patternToRegex, deprecated, NullObject } = require("./utils.js");
18
- const accepts = require("accepts");
19
- const typeis = require("type-is");
20
- const parseRange = require("range-parser");
21
- const proxyaddr = require("proxy-addr");
22
- const fresh = require("fresh");
23
- const { Readable } = require("stream");
24
-
25
- const discardedDuplicates = [
26
- "age", "authorization", "content-length", "content-type", "etag", "expires",
27
- "from", "host", "if-modified-since", "if-unmodified-since", "last-modified",
28
- "location", "max-forwards", "proxy-authorization", "referer", "retry-after",
29
- "server", "user-agent"
30
- ];
31
-
32
- let key = 0;
33
-
34
- module.exports = class Request extends Readable {
35
- #cachedQuery = null;
36
- #cachedHeaders = null;
37
- #cachedDistinctHeaders = null;
38
- #rawHeadersEntries = [];
39
- #cachedParsedIp = null;
40
- constructor(req, res, app) {
41
- super();
42
- this._res = res;
43
- this._req = req;
44
- this.readable = true;
45
- this._req.forEach((key, value) => {
46
- this.#rawHeadersEntries.push([key, value]);
47
- });
48
- this.routeCount = 0;
49
- this.key = key++;
50
- if(key > 100000) {
51
- key = 0;
52
- }
53
- this.app = app;
54
- this.urlQuery = req.getQuery() ?? '';
55
- if(this.urlQuery) {
56
- this.urlQuery = '?' + this.urlQuery;
57
- }
58
- this.originalUrl = req.getUrl() + this.urlQuery;
59
- this.url = this.originalUrl;
60
- const iq = this.url.indexOf('?');
61
- this.path = iq !== -1 ? this.url.substring(0, iq) : this.url;
62
- this.endsWithSlash = this.path[this.path.length - 1] === '/';
63
- this._opPath = this.path;
64
- this._originalPath = this.path;
65
- if(this.endsWithSlash && this.path !== '/' && !this.app.get('strict routing')) {
66
- this._opPath = this._opPath.slice(0, -1);
67
- }
68
- this.method = req.getCaseSensitiveMethod().toUpperCase();
69
- this.params = {};
70
-
71
- this._gotParams = new Set();
72
- this._stack = [];
73
- this._paramStack = [];
74
- this.receivedData = false;
75
- // reading ip is very slow in UWS, so its better to not do it unless truly needed
76
- if(this.app.needsIpAfterResponse || this.key < 100) {
77
- // if app needs ip after response, read it now because after response its not accessible
78
- // also read it for first 100 requests to not error
79
- this.rawIp = this._res.getRemoteAddress();
80
- }
81
-
82
- const additionalMethods = this.app.get('body methods');
83
- // skip reading body for non-POST requests
84
- // this makes it +10k req/sec faster
85
- if(
86
- this.method === 'POST' ||
87
- this.method === 'PUT' ||
88
- this.method === 'PATCH' ||
89
- (additionalMethods && additionalMethods.includes(this.method))
90
- ) {
91
- this.bufferedData = Buffer.allocUnsafe(0);
92
- this._res.onData((ab, isLast) => {
93
- // make stream actually readable
94
- this.receivedData = true;
95
- // instead of pushing data immediately, buffer it
96
- // because writable streams cant handle the amount of data uWS gives (usually 512kb+)
97
- const chunk = Buffer.from(ab);
98
- this.bufferedData = Buffer.concat([this.bufferedData, chunk]);
99
- if(isLast) {
100
- // once its done start pushing data
101
- this._read();
102
- }
103
- });
104
- } else {
105
- this.receivedData = true;
106
- }
107
- }
108
-
109
- async _read() {
110
- if(!this.receivedData || !this.bufferedData) {
111
- return;
112
- }
113
- if(this.bufferedData.length > 0) {
114
- // push 64kb chunks
115
- const chunk = this.bufferedData.subarray(0, 1024 * 64);
116
- this.bufferedData = this.bufferedData.subarray(1024 * 64);
117
- this.push(chunk);
118
- } else {
119
- this.push(null);
120
- }
121
- }
122
-
123
- get baseUrl() {
124
- let match = this._originalPath.match(patternToRegex(this._stack.join(""), true));
125
- return match ? match[0] : '';
126
- }
127
-
128
- get #host() {
129
- const trust = this.app.get('trust proxy fn');
130
- if(!trust) {
131
- return this.get('host');
132
- }
133
- let val = this.headers['x-forwarded-host'];
134
- if (!val || !trust(this.connection.remoteAddress, 0)) {
135
- val = this.headers['host'];
136
- } else if (val.indexOf(',') !== -1) {
137
- // Note: X-Forwarded-Host is normally only ever a
138
- // single value, but this is to be safe.
139
- val = val.substring(0, val.indexOf(',')).trimRight()
140
- }
141
-
142
- return val ? val.split(':')[0] : undefined;
143
- }
144
-
145
- get host() {
146
- deprecated('req.host', 'req.hostname');
147
- return this.hostname;
148
- }
149
-
150
- get hostname() {
151
- const host = this.#host;
152
- if(!host) return this.headers['host'].split(':')[0];
153
- const offset = host[0] === '[' ? host.indexOf(']') + 1 : 0;
154
- const index = host.indexOf(':', offset);
155
- return index !== -1 ? host.slice(0, index) : host;
156
- }
157
-
158
- get httpVersion() {
159
- return '1.1';
160
- }
161
-
162
- get httpVersionMajor() {
163
- return 1;
164
- }
165
-
166
- get httpVersionMinor() {
167
- return 1;
168
- }
169
-
170
- get ip() {
171
- const trust = this.app.get('trust proxy fn');
172
- if(!trust) {
173
- return this.parsedIp;
174
- }
175
- return proxyaddr(this, trust);
176
- }
177
-
178
- get ips() {
179
- const trust = this.app.get('trust proxy fn');
180
- if(!trust) {
181
- return [];
182
- }
183
- const addrs = proxyaddr.all(this, trust);
184
- addrs.reverse().pop();
185
- return addrs;
186
- }
187
-
188
- get protocol() {
189
- const proto = this.app.ssl ? 'https' : 'http';
190
- const trust = this.app.get('trust proxy fn');
191
- if(!trust) {
192
- return proto;
193
- }
194
- if(!trust(this.connection.remoteAddress, 0)) {
195
- return proto;
196
- }
197
- const header = this.headers['x-forwarded-proto'] || proto;
198
- const index = header.indexOf(',');
199
-
200
- return index !== -1 ? header.slice(0, index).trim() : header.trim();
201
- }
202
-
203
- get query() {
204
- if(this.#cachedQuery) {
205
- return this.#cachedQuery;
206
- }
207
- const qp = this.app.get('query parser fn');
208
- if(qp) {
209
- this.#cachedQuery = {...qp(this.urlQuery.slice(1))};
210
- } else {
211
- this.#cachedQuery = {...new NullObject()};
212
- }
213
- return this.#cachedQuery;
214
- }
215
-
216
- get secure() {
217
- return this.protocol === 'https';
218
- }
219
-
220
- get subdomains() {
221
- let host = this.hostname;
222
- let subdomains = host.split('.');
223
- const so = this.app.get('subdomain offset');
224
- if(so === 0) {
225
- return subdomains.reverse();
226
- }
227
- return subdomains.slice(0, -so).reverse();
228
- }
229
-
230
- get xhr() {
231
- return this.headers['x-requested-with'] === 'XMLHttpRequest';
232
- }
233
-
234
- get parsedIp() {
235
- if(this.#cachedParsedIp) {
236
- return this.#cachedParsedIp;
237
- }
238
- const finished = !this.res.socket.writable;
239
- if(finished) {
240
- // mark app as one that needs ip after response
241
- this.app.needsIpAfterResponse = true;
242
- }
243
- if(!this.rawIp) {
244
- if(finished) {
245
- // fallback once
246
- return '127.0.0.1';
247
- }
248
- this.rawIp = this._res.getRemoteAddress();
249
- }
250
- let ip = '';
251
- if(this.rawIp.byteLength === 4) {
252
- // ipv4
253
- ip = this.rawIp.join('.');
254
- } else {
255
- // ipv6
256
- const dv = new DataView(this.rawIp);
257
- for(let i = 0; i < 8; i++) {
258
- ip += dv.getUint16(i * 2).toString(16).padStart(4, '0');
259
- if(i < 7) {
260
- ip += ':';
261
- }
262
- }
263
- }
264
- this.#cachedParsedIp = ip;
265
- return ip;
266
- }
267
-
268
- get connection() {
269
- return {
270
- remoteAddress: this.parsedIp,
271
- localPort: this.app.port,
272
- remotePort: this.app.port,
273
- encrypted: this.app.ssl,
274
- end: (body) => this.res.end(body)
275
- };
276
- }
277
-
278
- get socket() {
279
- return this.connection;
280
- }
281
-
282
- get fresh() {
283
- if(this.method !== 'HEAD' && this.method !== 'GET') {
284
- return false;
285
- }
286
- if((this.res.statusCode >= 200 && this.res.statusCode < 300) || this.res.statusCode === 304) {
287
- return fresh(this.headers, {
288
- 'etag': this.res.headers['etag'],
289
- 'last-modified': this.res.headers['last-modified'],
290
- });
291
- }
292
- return false;
293
- }
294
-
295
- get stale() {
296
- return !this.fresh;
297
- }
298
-
299
- get(field) {
300
- field = field.toLowerCase();
301
- if(field === 'referrer' || field === 'referer') {
302
- const res = this.headers['referrer'];
303
- if(!res) {
304
- return this.headers['referer'];
305
- }
306
- return res;
307
- }
308
- return this.headers[field];
309
- }
310
- header = this.get
311
-
312
- accepts(...types) {
313
- return accepts(this).types(...types);
314
- }
315
-
316
- acceptsCharsets(...charsets) {
317
- return accepts(this).charsets(...charsets);
318
- }
319
-
320
- acceptsEncodings(...encodings) {
321
- return accepts(this).encodings(...encodings);
322
- }
323
-
324
- acceptsLanguages(...languages) {
325
- return accepts(this).languages(...languages);
326
- }
327
-
328
- is(type) {
329
- return typeis(this, type);
330
- }
331
-
332
- param(name, defaultValue) {
333
- deprecated('req.param(name)', 'req.params, req.body, or req.query');
334
- if(this.params[name]) {
335
- return this.params[name];
336
- }
337
- if(this.body && this.body[name]) {
338
- return this.body[name];
339
- }
340
- return this.query[name] ?? defaultValue;
341
- }
342
-
343
- range(size, options) {
344
- const range = this.headers['range'];
345
- if(!range) return;
346
- return parseRange(size, range, options);
347
- }
348
-
349
- get headers() {
350
- // https://nodejs.org/api/http.html#messageheaders
351
- if(this.#cachedHeaders) {
352
- return this.#cachedHeaders;
353
- }
354
- this.#cachedHeaders = {...new NullObject()}; // seems to be faster
355
- for (let index = 0, len = this.#rawHeadersEntries.length; index < len; index++) {
356
- let [key, value] = this.#rawHeadersEntries[index];
357
- key = key.toLowerCase();
358
- if(this.#cachedHeaders[key]) {
359
- if(discardedDuplicates.includes(key)) {
360
- continue;
361
- }
362
- if(key === 'cookie') {
363
- this.#cachedHeaders[key] += '; ' + value;
364
- } else if(key === 'set-cookie') {
365
- this.#cachedHeaders[key].push(value);
366
- } else {
367
- this.#cachedHeaders[key] += ', ' + value;
368
- }
369
- continue;
370
- }
371
- if(key === 'set-cookie') {
372
- this.#cachedHeaders[key] = [value];
373
- } else {
374
- this.#cachedHeaders[key] = value;
375
- }
376
- }
377
- return this.#cachedHeaders;
378
- }
379
-
380
- get headersDistinct() {
381
- if(this.#cachedDistinctHeaders) {
382
- return this.#cachedDistinctHeaders;
383
- }
384
- this.#cachedDistinctHeaders = {...new NullObject()};
385
- this.#rawHeadersEntries.forEach((val) => {
386
- const [key, value] = val;
387
- if(!this.#cachedDistinctHeaders[key]) {
388
- this.#cachedDistinctHeaders[key] = [value];
389
- return;
390
- }
391
- this.#cachedDistinctHeaders[key].push(value);
392
- });
393
- return this.#cachedDistinctHeaders;
394
- }
395
-
396
- get rawHeaders() {
397
- const res = [];
398
- for (let index = 0, len = this.#rawHeadersEntries.length; index < len; index++) {
399
- const val = this.#rawHeadersEntries[index];
400
- res.push(val[0], val[1]);
401
- }
402
- return res;
403
- }
404
- }
1
+ /*
2
+ Copyright 2024 dimden.dev
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ const { patternToRegex, deprecated, NullObject } = require("./utils.js");
18
+ const accepts = require("accepts");
19
+ const typeis = require("type-is");
20
+ const parseRange = require("range-parser");
21
+ const proxyaddr = require("proxy-addr");
22
+ const fresh = require("fresh");
23
+ const { Readable } = require("stream");
24
+
25
+ const discardedDuplicates = [
26
+ "age", "authorization", "content-length", "content-type", "etag", "expires",
27
+ "from", "host", "if-modified-since", "if-unmodified-since", "last-modified",
28
+ "location", "max-forwards", "proxy-authorization", "referer", "retry-after",
29
+ "server", "user-agent"
30
+ ];
31
+
32
+ let key = 0;
33
+
34
+ module.exports = class Request extends Readable {
35
+ #cachedQuery = null;
36
+ #cachedHeaders = null;
37
+ #cachedDistinctHeaders = null;
38
+ #rawHeadersEntries = [];
39
+ #cachedParsedIp = null;
40
+ constructor(req, res, app) {
41
+ super();
42
+ this._res = res;
43
+ this._req = req;
44
+ this.readable = true;
45
+ this._req.forEach((key, value) => {
46
+ this.#rawHeadersEntries.push([key, value]);
47
+ });
48
+ this.routeCount = 0;
49
+ this.key = key++;
50
+ if(key > 100000) {
51
+ key = 0;
52
+ }
53
+ this.app = app;
54
+ this.urlQuery = req.getQuery() ?? '';
55
+ if(this.urlQuery) {
56
+ this.urlQuery = '?' + this.urlQuery;
57
+ }
58
+ this.originalUrl = req.getUrl() + this.urlQuery;
59
+ this.url = this.originalUrl;
60
+ const iq = this.url.indexOf('?');
61
+ this.path = iq !== -1 ? this.url.substring(0, iq) : this.url;
62
+ this.endsWithSlash = this.path[this.path.length - 1] === '/';
63
+ this._opPath = this.path;
64
+ this._originalPath = this.path;
65
+ if(this.endsWithSlash && this.path !== '/' && !this.app.get('strict routing')) {
66
+ this._opPath = this._opPath.slice(0, -1);
67
+ }
68
+ this.method = req.getCaseSensitiveMethod().toUpperCase();
69
+ this.params = {};
70
+
71
+ this._gotParams = new Set();
72
+ this._stack = [];
73
+ this._paramStack = [];
74
+ this.receivedData = false;
75
+ // reading ip is very slow in UWS, so its better to not do it unless truly needed
76
+ if(this.app.needsIpAfterResponse || this.key < 100) {
77
+ // if app needs ip after response, read it now because after response its not accessible
78
+ // also read it for first 100 requests to not error
79
+ this.rawIp = this._res.getRemoteAddress();
80
+ }
81
+
82
+ const additionalMethods = this.app.get('body methods');
83
+ // skip reading body for non-POST requests
84
+ // this makes it +10k req/sec faster
85
+ if(
86
+ this.method === 'POST' ||
87
+ this.method === 'PUT' ||
88
+ this.method === 'PATCH' ||
89
+ (additionalMethods && additionalMethods.includes(this.method))
90
+ ) {
91
+ this.bufferedData = Buffer.allocUnsafe(0);
92
+ this._res.onData((ab, isLast) => {
93
+ // make stream actually readable
94
+ this.receivedData = true;
95
+ // instead of pushing data immediately, buffer it
96
+ // because writable streams cant handle the amount of data uWS gives (usually 512kb+)
97
+ const chunk = Buffer.from(ab);
98
+ this.bufferedData = Buffer.concat([this.bufferedData, chunk]);
99
+ if(isLast) {
100
+ // once its done start pushing data
101
+ this._read();
102
+ }
103
+ });
104
+ } else {
105
+ this.receivedData = true;
106
+ }
107
+ }
108
+
109
+ async _read() {
110
+ if(!this.receivedData || !this.bufferedData) {
111
+ return;
112
+ }
113
+ if(this.bufferedData.length > 0) {
114
+ // push 64kb chunks
115
+ const chunk = this.bufferedData.subarray(0, 1024 * 64);
116
+ this.bufferedData = this.bufferedData.subarray(1024 * 64);
117
+ this.push(chunk);
118
+ } else {
119
+ this.push(null);
120
+ }
121
+ }
122
+
123
+ get baseUrl() {
124
+ let match = this._originalPath.match(patternToRegex(this._stack.join(""), true));
125
+ return match ? match[0] : '';
126
+ }
127
+
128
+ get #host() {
129
+ const trust = this.app.get('trust proxy fn');
130
+ if(!trust) {
131
+ return this.get('host');
132
+ }
133
+ let val = this.headers['x-forwarded-host'];
134
+ if (!val || !trust(this.connection.remoteAddress, 0)) {
135
+ val = this.headers['host'];
136
+ } else if (val.indexOf(',') !== -1) {
137
+ // Note: X-Forwarded-Host is normally only ever a
138
+ // single value, but this is to be safe.
139
+ val = val.substring(0, val.indexOf(',')).trimRight()
140
+ }
141
+
142
+ return val ? val.split(':')[0] : undefined;
143
+ }
144
+
145
+ get host() {
146
+ deprecated('req.host', 'req.hostname');
147
+ return this.hostname;
148
+ }
149
+
150
+ get hostname() {
151
+ const host = this.#host;
152
+ if(!host) return this.headers['host'].split(':')[0];
153
+ const offset = host[0] === '[' ? host.indexOf(']') + 1 : 0;
154
+ const index = host.indexOf(':', offset);
155
+ return index !== -1 ? host.slice(0, index) : host;
156
+ }
157
+
158
+ get httpVersion() {
159
+ return '1.1';
160
+ }
161
+
162
+ get httpVersionMajor() {
163
+ return 1;
164
+ }
165
+
166
+ get httpVersionMinor() {
167
+ return 1;
168
+ }
169
+
170
+ get ip() {
171
+ const trust = this.app.get('trust proxy fn');
172
+ if(!trust) {
173
+ return this.parsedIp;
174
+ }
175
+ return proxyaddr(this, trust);
176
+ }
177
+
178
+ get ips() {
179
+ const trust = this.app.get('trust proxy fn');
180
+ if(!trust) {
181
+ return [];
182
+ }
183
+ const addrs = proxyaddr.all(this, trust);
184
+ addrs.reverse().pop();
185
+ return addrs;
186
+ }
187
+
188
+ get protocol() {
189
+ const proto = this.app.ssl ? 'https' : 'http';
190
+ const trust = this.app.get('trust proxy fn');
191
+ if(!trust) {
192
+ return proto;
193
+ }
194
+ if(!trust(this.connection.remoteAddress, 0)) {
195
+ return proto;
196
+ }
197
+ const header = this.headers['x-forwarded-proto'] || proto;
198
+ const index = header.indexOf(',');
199
+
200
+ return index !== -1 ? header.slice(0, index).trim() : header.trim();
201
+ }
202
+
203
+ get query() {
204
+ if(this.#cachedQuery) {
205
+ return this.#cachedQuery;
206
+ }
207
+ const qp = this.app.get('query parser fn');
208
+ if(qp) {
209
+ this.#cachedQuery = {...qp(this.urlQuery.slice(1))};
210
+ } else {
211
+ this.#cachedQuery = {...new NullObject()};
212
+ }
213
+ return this.#cachedQuery;
214
+ }
215
+
216
+ get secure() {
217
+ return this.protocol === 'https';
218
+ }
219
+
220
+ get subdomains() {
221
+ let host = this.hostname;
222
+ let subdomains = host.split('.');
223
+ const so = this.app.get('subdomain offset');
224
+ if(so === 0) {
225
+ return subdomains.reverse();
226
+ }
227
+ return subdomains.slice(0, -so).reverse();
228
+ }
229
+
230
+ get xhr() {
231
+ return this.headers['x-requested-with'] === 'XMLHttpRequest';
232
+ }
233
+
234
+ get parsedIp() {
235
+ if(this.#cachedParsedIp) {
236
+ return this.#cachedParsedIp;
237
+ }
238
+ const finished = !this.res.socket.writable;
239
+ if(finished) {
240
+ // mark app as one that needs ip after response
241
+ this.app.needsIpAfterResponse = true;
242
+ }
243
+ if(!this.rawIp) {
244
+ if(finished) {
245
+ // fallback once
246
+ return '127.0.0.1';
247
+ }
248
+ this.rawIp = this._res.getRemoteAddress();
249
+ }
250
+ let ip = '';
251
+ if(this.rawIp.byteLength === 4) {
252
+ // ipv4
253
+ ip = this.rawIp.join('.');
254
+ } else {
255
+ // ipv6
256
+ const dv = new DataView(this.rawIp);
257
+ for(let i = 0; i < 8; i++) {
258
+ ip += dv.getUint16(i * 2).toString(16).padStart(4, '0');
259
+ if(i < 7) {
260
+ ip += ':';
261
+ }
262
+ }
263
+ }
264
+ this.#cachedParsedIp = ip;
265
+ return ip;
266
+ }
267
+
268
+ get connection() {
269
+ return {
270
+ remoteAddress: this.parsedIp,
271
+ localPort: this.app.port,
272
+ remotePort: this.app.port,
273
+ encrypted: this.app.ssl,
274
+ end: (body) => this.res.end(body)
275
+ };
276
+ }
277
+
278
+ get socket() {
279
+ return this.connection;
280
+ }
281
+
282
+ get fresh() {
283
+ if(this.method !== 'HEAD' && this.method !== 'GET') {
284
+ return false;
285
+ }
286
+ if((this.res.statusCode >= 200 && this.res.statusCode < 300) || this.res.statusCode === 304) {
287
+ return fresh(this.headers, {
288
+ 'etag': this.res.headers['etag'],
289
+ 'last-modified': this.res.headers['last-modified'],
290
+ });
291
+ }
292
+ return false;
293
+ }
294
+
295
+ get stale() {
296
+ return !this.fresh;
297
+ }
298
+
299
+ get(field) {
300
+ field = field.toLowerCase();
301
+ if(field === 'referrer' || field === 'referer') {
302
+ const res = this.headers['referrer'];
303
+ if(!res) {
304
+ return this.headers['referer'];
305
+ }
306
+ return res;
307
+ }
308
+ return this.headers[field];
309
+ }
310
+ header = this.get
311
+
312
+ accepts(...types) {
313
+ return accepts(this).types(...types);
314
+ }
315
+
316
+ acceptsCharsets(...charsets) {
317
+ return accepts(this).charsets(...charsets);
318
+ }
319
+
320
+ acceptsEncodings(...encodings) {
321
+ return accepts(this).encodings(...encodings);
322
+ }
323
+
324
+ acceptsLanguages(...languages) {
325
+ return accepts(this).languages(...languages);
326
+ }
327
+
328
+ is(type) {
329
+ return typeis(this, type);
330
+ }
331
+
332
+ param(name, defaultValue) {
333
+ deprecated('req.param(name)', 'req.params, req.body, or req.query');
334
+ if(this.params[name]) {
335
+ return this.params[name];
336
+ }
337
+ if(this.body && this.body[name]) {
338
+ return this.body[name];
339
+ }
340
+ return this.query[name] ?? defaultValue;
341
+ }
342
+
343
+ range(size, options) {
344
+ const range = this.headers['range'];
345
+ if(!range) return;
346
+ return parseRange(size, range, options);
347
+ }
348
+
349
+ get headers() {
350
+ // https://nodejs.org/api/http.html#messageheaders
351
+ if(this.#cachedHeaders) {
352
+ return this.#cachedHeaders;
353
+ }
354
+ this.#cachedHeaders = {...new NullObject()}; // seems to be faster
355
+ for (let index = 0, len = this.#rawHeadersEntries.length; index < len; index++) {
356
+ let [key, value] = this.#rawHeadersEntries[index];
357
+ key = key.toLowerCase();
358
+ if(this.#cachedHeaders[key]) {
359
+ if(discardedDuplicates.includes(key)) {
360
+ continue;
361
+ }
362
+ if(key === 'cookie') {
363
+ this.#cachedHeaders[key] += '; ' + value;
364
+ } else if(key === 'set-cookie') {
365
+ this.#cachedHeaders[key].push(value);
366
+ } else {
367
+ this.#cachedHeaders[key] += ', ' + value;
368
+ }
369
+ continue;
370
+ }
371
+ if(key === 'set-cookie') {
372
+ this.#cachedHeaders[key] = [value];
373
+ } else {
374
+ this.#cachedHeaders[key] = value;
375
+ }
376
+ }
377
+ return this.#cachedHeaders;
378
+ }
379
+
380
+ get headersDistinct() {
381
+ if(this.#cachedDistinctHeaders) {
382
+ return this.#cachedDistinctHeaders;
383
+ }
384
+ this.#cachedDistinctHeaders = {...new NullObject()};
385
+ this.#rawHeadersEntries.forEach((val) => {
386
+ const [key, value] = val;
387
+ if(!this.#cachedDistinctHeaders[key]) {
388
+ this.#cachedDistinctHeaders[key] = [value];
389
+ return;
390
+ }
391
+ this.#cachedDistinctHeaders[key].push(value);
392
+ });
393
+ return this.#cachedDistinctHeaders;
394
+ }
395
+
396
+ get rawHeaders() {
397
+ const res = [];
398
+ for (let index = 0, len = this.#rawHeadersEntries.length; index < len; index++) {
399
+ const val = this.#rawHeadersEntries[index];
400
+ res.push(val[0], val[1]);
401
+ }
402
+ return res;
403
+ }
404
+ }