@johntalton/http-util 6.1.0 → 7.0.2
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/README.md +1 -1
- package/package.json +2 -1
- package/src/body.js +11 -6
- package/src/defs.js +14 -5
- package/src/headers/accept-encoding.js +28 -5
- package/src/headers/accept-language.js +29 -5
- package/src/headers/accept.js +77 -32
- package/src/headers/cache-control.js +6 -3
- package/src/headers/client-hints.js +4 -3
- package/src/headers/conditional.js +18 -18
- package/src/headers/content-type.js +1 -1
- package/src/headers/link.js +9 -4
- package/src/headers/multipart.js +18 -17
- package/src/headers/range.js +4 -2
- package/src/headers/rate-limit.js +20 -4
- package/src/headers/server-timing.js +5 -3
- package/src/headers/util/kvp.js +2 -1
- package/src/headers/util/mime.js +17 -1
- package/src/headers/util/quote.js +1 -1
- package/src/headers/util/whitespace.js +1 -1
- package/src/headers/www-authenticate.js +35 -11
- package/src/response/2xx/accepted.js +2 -2
- package/src/response/2xx/bytes.js +2 -31
- package/src/response/2xx/created.js +8 -21
- package/src/response/2xx/json.js +6 -19
- package/src/response/2xx/no-content.js +4 -18
- package/src/response/2xx/partial-content.js +1 -28
- package/src/response/2xx/preflight.js +18 -25
- package/src/response/3xx/found.js +8 -6
- package/src/response/3xx/moved-permanently.js +8 -6
- package/src/response/3xx/multiple-choices.js +3 -3
- package/src/response/3xx/not-modified.js +14 -26
- package/src/response/3xx/permanent-redirect.js +7 -5
- package/src/response/3xx/see-other.js +8 -6
- package/src/response/3xx/temporary-redirect.js +7 -5
- package/src/response/4xx/bad-request.js +2 -3
- package/src/response/4xx/conflict.js +2 -2
- package/src/response/4xx/content-too-large.js +2 -2
- package/src/response/4xx/forbidden.js +3 -3
- package/src/response/4xx/gone.js +2 -2
- package/src/response/4xx/im-a-teapot.js +2 -2
- package/src/response/4xx/not-acceptable.js +2 -11
- package/src/response/4xx/not-allowed.js +6 -14
- package/src/response/4xx/payment-required.js +4 -4
- package/src/response/4xx/precondition-failed.js +4 -18
- package/src/response/4xx/range-not-satisfiable.js +4 -13
- package/src/response/4xx/timeout.js +3 -3
- package/src/response/4xx/too-many-requests.js +3 -20
- package/src/response/4xx/unauthorized.js +4 -4
- package/src/response/4xx/unprocessable.js +2 -2
- package/src/response/4xx/unsupported-media.js +16 -23
- package/src/response/5xx/error.js +2 -3
- package/src/response/5xx/insufficient-storage.js +2 -2
- package/src/response/5xx/not-implemented.js +2 -3
- package/src/response/5xx/unavailable.js +3 -20
- package/src/response/header-util.js +9 -5
- package/src/response/response.js +2 -2
- package/src/response/send-util.js +102 -30
package/src/headers/range.js
CHANGED
|
@@ -53,8 +53,8 @@ export class Range {
|
|
|
53
53
|
static parse(rangeHeader) {
|
|
54
54
|
if(rangeHeader === undefined) { return undefined }
|
|
55
55
|
if(!rangeHeader.startsWith(RANGE_UNITS_BYTES)) { return undefined }
|
|
56
|
-
if(!(rangeHeader.
|
|
57
|
-
const rangeStr = rangeHeader.
|
|
56
|
+
if(!(rangeHeader.slice(RANGE_UNITS_BYTES.length, RANGE_UNITS_BYTES.length + 1) === RANGE_EQUAL)) { return undefined }
|
|
57
|
+
const rangeStr = rangeHeader.slice(RANGE_UNITS_BYTES.length + RANGE_EQUAL.length).trim()
|
|
58
58
|
if(rangeStr === '') { return undefined }
|
|
59
59
|
|
|
60
60
|
const ranges = rangeStr.split(RANGE_LIST_SEPARATOR)
|
|
@@ -99,6 +99,8 @@ export class Range {
|
|
|
99
99
|
*/
|
|
100
100
|
static normalize(directive, contentLength) {
|
|
101
101
|
if(directive === undefined) { return undefined }
|
|
102
|
+
if(!Number.isInteger(contentLength)) { return undefined }
|
|
103
|
+
if(contentLength <= 0) { return undefined }
|
|
102
104
|
|
|
103
105
|
/** @type {Array<NormalizedRangeValue>} */
|
|
104
106
|
const normalizedRanges = directive.ranges.map(({ start, end }) => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { COMMON_LIST_HEADER_JOINER_COMMA } from "../defs.js"
|
|
2
|
+
|
|
1
3
|
// https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-10.html
|
|
2
4
|
|
|
3
|
-
export
|
|
5
|
+
export const HTTP_HEADER_RATE_LIMIT = 'RateLimit'
|
|
4
6
|
export const HTTP_HEADER_RATE_LIMIT_POLICY = 'RateLimit-Policy'
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -41,10 +43,17 @@ export const QUOTA_UNIT = {
|
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
export class RateLimit {
|
|
46
|
+
/**
|
|
47
|
+
* @deprecated
|
|
48
|
+
* @see {@link RateLimit.encode}
|
|
49
|
+
* @param {RateLimitInfo} limitInfo
|
|
50
|
+
*/
|
|
51
|
+
static from(limitInfo) { return RateLimit.encode(limitInfo) }
|
|
52
|
+
|
|
44
53
|
/**
|
|
45
54
|
* @param {RateLimitInfo} limitInfo
|
|
46
55
|
*/
|
|
47
|
-
static
|
|
56
|
+
static encode(limitInfo) {
|
|
48
57
|
if(limitInfo === undefined) { return undefined }
|
|
49
58
|
const { name, remaining, resetSeconds, partitionKey } = limitInfo
|
|
50
59
|
|
|
@@ -57,10 +66,17 @@ export class RateLimit {
|
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
export class RateLimitPolicy {
|
|
69
|
+
/**
|
|
70
|
+
* @deprecated
|
|
71
|
+
* @see {@link RateLimitPolicy.encode}
|
|
72
|
+
* @param {...RateLimitPolicyInfo} policies
|
|
73
|
+
*/
|
|
74
|
+
static from(...policies) { return RateLimitPolicy.encode(...policies) }
|
|
75
|
+
|
|
60
76
|
/**
|
|
61
77
|
* @param {...RateLimitPolicyInfo} policies
|
|
62
78
|
*/
|
|
63
|
-
static
|
|
79
|
+
static encode(...policies) { // todo AsArray
|
|
64
80
|
if(policies === undefined) { return undefined }
|
|
65
81
|
if(policies.length === 0) { return undefined }
|
|
66
82
|
|
|
@@ -86,6 +102,6 @@ export class RateLimitPolicy {
|
|
|
86
102
|
.filter(item => item !== undefined)
|
|
87
103
|
.join(';')
|
|
88
104
|
})
|
|
89
|
-
.join(
|
|
105
|
+
.join(COMMON_LIST_HEADER_JOINER_COMMA)
|
|
90
106
|
}
|
|
91
107
|
}
|
|
@@ -20,12 +20,13 @@ export const SERVER_TIMING_SEPARATOR = {
|
|
|
20
20
|
export class ServerTiming {
|
|
21
21
|
/**
|
|
22
22
|
* @param {Array<TimingsInfo>|undefined} timings
|
|
23
|
+
* @param {boolean} [asArray = false]
|
|
23
24
|
*/
|
|
24
|
-
static encode(timings) {
|
|
25
|
+
static encode(timings, asArray = false) {
|
|
25
26
|
if(timings === undefined) { return undefined }
|
|
26
27
|
if(timings.length <= 0) { return undefined }
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
const ary = timings
|
|
29
30
|
.map(({ name, duration, description }) => [
|
|
30
31
|
`${name}`,
|
|
31
32
|
description === undefined ? undefined : `${SERVER_TIMING_KEY_DESCRIPTION}${SERVER_TIMING_SEPARATOR.KVP}"${description}"`,
|
|
@@ -33,6 +34,7 @@ export class ServerTiming {
|
|
|
33
34
|
]
|
|
34
35
|
.filter(item => item !== undefined)
|
|
35
36
|
.join(SERVER_TIMING_SEPARATOR.PARAMETER))
|
|
36
|
-
|
|
37
|
+
|
|
38
|
+
return asArray ? ary : ary.join(SERVER_TIMING_SEPARATOR.METRIC) // todo COMMON_LIST_HEADER_JOINER_COMMA
|
|
37
39
|
}
|
|
38
40
|
}
|
package/src/headers/util/kvp.js
CHANGED
|
@@ -5,7 +5,6 @@ export const DEFAULT_DELIMITER = ';'
|
|
|
5
5
|
export const KVP_DELIMITER = '='
|
|
6
6
|
export const KVP_EMPTY = ''
|
|
7
7
|
|
|
8
|
-
|
|
9
8
|
export class KVP {
|
|
10
9
|
/**
|
|
11
10
|
* @param {Array<string>|undefined} params
|
|
@@ -40,6 +39,7 @@ export class KVP {
|
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
/**
|
|
42
|
+
* Parses Key Value pair parameter list with leading named item.
|
|
43
43
|
* @param {string|undefined} str
|
|
44
44
|
* @param {Array<string>|undefined} [acceptableKeys=undefined]
|
|
45
45
|
*/
|
|
@@ -58,6 +58,7 @@ export class KVP {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
+
* Parses Key Value pair parameter list (no special treatment for leading key)
|
|
61
62
|
* @param {string|undefined} str
|
|
62
63
|
* @param {Array<string>|undefined} [acceptableKeys=undefined]
|
|
63
64
|
*/
|
package/src/headers/util/mime.js
CHANGED
|
@@ -19,7 +19,7 @@ export const SPECIAL_CHARS = [
|
|
|
19
19
|
'{', '}',
|
|
20
20
|
'@', ',', ';', ':',
|
|
21
21
|
'\\', '"', '/', '?', '=', // '.',
|
|
22
|
-
// '%', // '!', '$', '&', // #
|
|
22
|
+
// '%', // '!', '$', '&', // # ^ * | ~ `
|
|
23
23
|
// space
|
|
24
24
|
' ', '\u000B', '\u000C',
|
|
25
25
|
// control
|
|
@@ -74,4 +74,20 @@ export class Mime {
|
|
|
74
74
|
subtype
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {MimeItem} first
|
|
80
|
+
* @param {MimeItem} second
|
|
81
|
+
* @returns {boolean}
|
|
82
|
+
*/
|
|
83
|
+
static matches(first, second) {
|
|
84
|
+
if(first === undefined) { return false }
|
|
85
|
+
if(second === undefined) { return false }
|
|
86
|
+
|
|
87
|
+
const matchType = first.type === second.type || first.type === MIME_ANY || second.type === MIME_ANY
|
|
88
|
+
if(!matchType) { return false }
|
|
89
|
+
|
|
90
|
+
const matchSubtype = first.subtype === second.subtype || first.subtype === MIME_ANY || second.subtype === MIME_ANY
|
|
91
|
+
return matchSubtype
|
|
92
|
+
}
|
|
77
93
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { COMMON_LIST_HEADER_JOINER_COMMA, COMMON_LIST_VALUE_JOINER_COMMA } from "../defs.js"
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* @typedef {Object} ChallengeItem
|
|
3
5
|
* @property {string} scheme
|
|
@@ -31,14 +33,15 @@ export function paramNeedQuotes(paramName) {
|
|
|
31
33
|
return PARAMETERS_THAT_NEED_QUOTES.includes(paramName.toLowerCase())
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
|
|
35
36
|
export class Challenge {
|
|
36
37
|
/**
|
|
37
38
|
* @param {string} realm
|
|
38
39
|
* @param {string} [charset='utf-8']
|
|
39
|
-
* @returns {ChallengeItem}
|
|
40
|
+
* @returns {ChallengeItem|undefined}
|
|
40
41
|
*/
|
|
41
42
|
static basic(realm, charset = 'utf-8') {
|
|
43
|
+
if(realm === undefined) { return undefined }
|
|
44
|
+
|
|
42
45
|
const parameters = new Map([ [ 'realm', realm ] ])
|
|
43
46
|
if(charset !== undefined) { parameters.set('charset', charset) }
|
|
44
47
|
|
|
@@ -53,10 +56,10 @@ export class Challenge {
|
|
|
53
56
|
* @param {string|undefined} [scope]
|
|
54
57
|
* @param {BearerErrorCode} [error]
|
|
55
58
|
* @param {string} [errorDescription]
|
|
56
|
-
* @param {string} [
|
|
59
|
+
* @param {string} [_errorUri]
|
|
57
60
|
* @returns {ChallengeItem}
|
|
58
61
|
*/
|
|
59
|
-
static bearer(realm, scope, error, errorDescription,
|
|
62
|
+
static bearer(realm, scope, error, errorDescription, _errorUri) {
|
|
60
63
|
const parameters = new Map()
|
|
61
64
|
if(realm !== undefined) { parameters.set('realm', realm) }
|
|
62
65
|
if(scope !== undefined) { parameters.set('scope', scope) }
|
|
@@ -70,11 +73,11 @@ export class Challenge {
|
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
/**
|
|
73
|
-
* @param {string}
|
|
74
|
-
* @param {string} [
|
|
76
|
+
* @param {string} _algorithm
|
|
77
|
+
* @param {string} [_realm]
|
|
75
78
|
* @returns {ChallengeItem}
|
|
76
79
|
*/
|
|
77
|
-
static digest(
|
|
80
|
+
static digest(_algorithm, _realm) {
|
|
78
81
|
return {
|
|
79
82
|
scheme: 'Digest'
|
|
80
83
|
}
|
|
@@ -90,18 +93,39 @@ export class Challenge {
|
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
/**
|
|
93
|
-
* @param {ChallengeItem}
|
|
96
|
+
* @param {Array<ChallengeItem> | ChallengeItem | undefined} challenges
|
|
97
|
+
* @param {boolean} [asArray = false]
|
|
94
98
|
*/
|
|
95
|
-
static encode(
|
|
99
|
+
static encode(challenges, asArray = false) {
|
|
100
|
+
if(!Array.isArray(challenges)) { return Challenge.#encode(challenges) }
|
|
101
|
+
|
|
102
|
+
const ary = challenges
|
|
103
|
+
.map(Challenge.#encode)
|
|
104
|
+
.filter(c => c !== undefined)
|
|
105
|
+
|
|
106
|
+
return asArray ? ary : ary.join(COMMON_LIST_HEADER_JOINER_COMMA)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {ChallengeItem | undefined} challenge
|
|
111
|
+
*/
|
|
112
|
+
static #encode(challenge) {
|
|
113
|
+
if(challenge === undefined) { return undefined }
|
|
114
|
+
if(challenge.scheme === undefined) { return undefined }
|
|
115
|
+
if(challenge.scheme === '') { return undefined }
|
|
116
|
+
|
|
96
117
|
const parameters = challenge.parameters?.entries().map(([ key, value ]) => {
|
|
97
118
|
if(value === undefined) { return key }
|
|
98
119
|
if(paramNeedQuotes(key)) { return `${key}="${value}"` }
|
|
99
120
|
return `${key}=${value}`
|
|
100
121
|
})
|
|
101
122
|
|
|
102
|
-
|
|
123
|
+
if(parameters === undefined) { return challenge.scheme }
|
|
124
|
+
const params = [ ...parameters ]
|
|
125
|
+
if(params.length === 0) { return challenge.scheme }
|
|
103
126
|
|
|
104
|
-
|
|
127
|
+
const paramsStr = params.join(COMMON_LIST_VALUE_JOINER_COMMA)
|
|
128
|
+
return [ challenge.scheme, paramsStr ].join(' ')
|
|
105
129
|
}
|
|
106
130
|
}
|
|
107
131
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import http2 from 'node:http2'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { send_no_body } from '../send-util.js'
|
|
4
4
|
|
|
5
5
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
6
6
|
/** @import { Metadata } from '../../defs.js' */
|
|
@@ -12,5 +12,5 @@ const { HTTP_STATUS_ACCEPTED } = http2.constants
|
|
|
12
12
|
* @param {Metadata} meta
|
|
13
13
|
*/
|
|
14
14
|
export function sendAccepted(stream, meta) {
|
|
15
|
-
|
|
15
|
+
send_no_body(stream, HTTP_STATUS_ACCEPTED, {}, [], meta)
|
|
16
16
|
}
|
|
@@ -3,39 +3,10 @@ import http2 from 'node:http2'
|
|
|
3
3
|
import { send_bytes } from '../send-util.js'
|
|
4
4
|
|
|
5
5
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
6
|
-
/** @import {
|
|
7
|
-
/** @import { EtagItem, IMFFixDateInput } from '../../headers/conditional.js' */
|
|
8
|
-
/** @import { CacheControlOptions } from '../../headers/cache-control.js' */
|
|
6
|
+
/** @import { SendContent, SendInfo, Metadata, SendBody } from '../../defs.js' */
|
|
9
7
|
|
|
10
8
|
const { HTTP_STATUS_OK } = http2.constants
|
|
11
9
|
|
|
12
|
-
/**
|
|
13
|
-
* @param {ServerHttp2Stream} stream
|
|
14
|
-
* @param {SendBody|undefined} obj
|
|
15
|
-
* @param {string|undefined} contentType
|
|
16
|
-
* @param {number|undefined} contentLength
|
|
17
|
-
* @param {string|undefined} encoding
|
|
18
|
-
* @param {EtagItem|undefined} etag
|
|
19
|
-
* @param {IMFFixDateInput|string|undefined} lastModified
|
|
20
|
-
* @param {number|undefined} age
|
|
21
|
-
* @param {CacheControlOptions} cacheControl
|
|
22
|
-
* @param {AcceptRangeUnits|undefined} acceptRanges
|
|
23
|
-
* @param {Metadata} meta
|
|
24
|
-
*/
|
|
25
|
-
export function sendBytes(stream, contentType, obj, contentLength, encoding, etag, lastModified, age, cacheControl, acceptRanges, meta) {
|
|
26
|
-
_sendBytes(stream, obj, {
|
|
27
|
-
contentType,
|
|
28
|
-
contentLength,
|
|
29
|
-
encoding,
|
|
30
|
-
etag,
|
|
31
|
-
lastModified,
|
|
32
|
-
age,
|
|
33
|
-
cacheControl
|
|
34
|
-
}, {
|
|
35
|
-
acceptRanges
|
|
36
|
-
}, meta)
|
|
37
|
-
}
|
|
38
|
-
|
|
39
10
|
/**
|
|
40
11
|
* @param {ServerHttp2Stream} stream
|
|
41
12
|
* @param {SendBody|undefined} obj
|
|
@@ -43,7 +14,7 @@ export function sendBytes(stream, contentType, obj, contentLength, encoding, eta
|
|
|
43
14
|
* @param {Pick<SendInfo, 'acceptRanges'>} info
|
|
44
15
|
* @param {Metadata} meta
|
|
45
16
|
*/
|
|
46
|
-
export function
|
|
17
|
+
export function sendBytes(stream, obj, content, info, meta) {
|
|
47
18
|
const {
|
|
48
19
|
contentType,
|
|
49
20
|
contentLength,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import http2 from 'node:http2'
|
|
2
2
|
|
|
3
3
|
import { Conditional } from '../../headers/conditional.js'
|
|
4
|
-
import {
|
|
4
|
+
import { send_no_body } from '../send-util.js'
|
|
5
5
|
|
|
6
6
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
7
7
|
/** @import { SendContent, Metadata } from '../../defs.js' */
|
|
8
|
-
/** @import { EtagItem, IMFFixDateInput } from '../../headers/conditional.js' */
|
|
9
8
|
|
|
10
9
|
const {
|
|
11
10
|
HTTP2_HEADER_LOCATION,
|
|
@@ -17,33 +16,21 @@ const { HTTP_STATUS_CREATED } = http2.constants
|
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* @param {ServerHttp2Stream} stream
|
|
20
|
-
* @param {URL} location
|
|
21
|
-
* @param {EtagItem|undefined} etag
|
|
22
|
-
* @param {IMFFixDateInput|string|undefined} lastModified
|
|
23
|
-
* @param {Metadata} meta
|
|
24
|
-
*/
|
|
25
|
-
export function sendCreated(stream, location, etag, lastModified, meta) {
|
|
26
|
-
_sendCreated(stream, location, {
|
|
27
|
-
etag,
|
|
28
|
-
lastModified
|
|
29
|
-
}, meta)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param {ServerHttp2Stream} stream
|
|
34
|
-
* @param {URL} location
|
|
19
|
+
* @param {URL|string} location
|
|
35
20
|
* @param {Pick<SendContent, 'etag' | 'lastModified'>} content
|
|
36
21
|
* @param {Metadata} meta
|
|
37
22
|
*/
|
|
38
|
-
export function
|
|
23
|
+
export function sendCreated(stream, location, content, meta) {
|
|
39
24
|
const {
|
|
40
25
|
etag,
|
|
41
26
|
lastModified
|
|
42
27
|
} = content
|
|
43
28
|
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
const loc = (location instanceof URL) ? location.href : location
|
|
30
|
+
|
|
31
|
+
send_no_body(stream, HTTP_STATUS_CREATED, {
|
|
32
|
+
[HTTP2_HEADER_LOCATION]: loc,
|
|
46
33
|
[HTTP2_HEADER_ETAG]: Conditional.encodeEtag(etag),
|
|
47
34
|
[HTTP2_HEADER_LAST_MODIFIED]: Conditional.encodeFixDate(lastModified)
|
|
48
|
-
}, [ HTTP2_HEADER_LOCATION ],
|
|
35
|
+
}, [ HTTP2_HEADER_LOCATION ], meta)
|
|
49
36
|
}
|
package/src/response/2xx/json.js
CHANGED
|
@@ -5,32 +5,19 @@ import { send_encoded } from '../send-util.js'
|
|
|
5
5
|
|
|
6
6
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
7
7
|
/** @import { SendContent, SendInfo, Metadata } from '../../defs.js' */
|
|
8
|
-
/** @import { EtagItem, IMFFixDateInput } from '../../headers/conditional.js' */
|
|
9
|
-
/** @import { CacheControlOptions } from '../../headers/cache-control.js' */
|
|
10
8
|
|
|
11
9
|
const { HTTP_STATUS_OK } = http2.constants
|
|
12
10
|
|
|
13
11
|
/**
|
|
12
|
+
* @deprecated
|
|
14
13
|
* @param {ServerHttp2Stream} stream
|
|
15
14
|
* @param {Object} obj
|
|
16
|
-
* @param {
|
|
17
|
-
* @param {
|
|
18
|
-
* @param {IMFFixDateInput|string|undefined} lastModified
|
|
19
|
-
* @param {number|undefined} age
|
|
20
|
-
* @param {CacheControlOptions} cacheControl
|
|
21
|
-
* @param {Array<string>|undefined} supportedQueryTypes
|
|
15
|
+
* @param {Omit<SendContent, 'contentType' | 'contentLength' | 'rangeDirective'>} content
|
|
16
|
+
* @param {Pick<SendInfo, 'supportedQueryTypes'>} info
|
|
22
17
|
* @param {Metadata} meta
|
|
23
18
|
*/
|
|
24
|
-
export function sendJSON_Encoded(stream, obj,
|
|
25
|
-
|
|
26
|
-
encoding,
|
|
27
|
-
etag,
|
|
28
|
-
lastModified,
|
|
29
|
-
age,
|
|
30
|
-
cacheControl
|
|
31
|
-
}, {
|
|
32
|
-
supportedQueryTypes
|
|
33
|
-
}, meta)
|
|
19
|
+
export function sendJSON_Encoded(stream, obj, content, info, meta) {
|
|
20
|
+
sendJSON(stream, obj, content, info, meta)
|
|
34
21
|
}
|
|
35
22
|
|
|
36
23
|
/**
|
|
@@ -40,7 +27,7 @@ export function sendJSON_Encoded(stream, obj, encoding, etag, lastModified, age,
|
|
|
40
27
|
* @param {Pick<SendInfo, 'supportedQueryTypes'>} info
|
|
41
28
|
* @param {Metadata} meta
|
|
42
29
|
*/
|
|
43
|
-
export function
|
|
30
|
+
export function sendJSON(stream, obj, content, info, meta) {
|
|
44
31
|
const {
|
|
45
32
|
encoding,
|
|
46
33
|
etag,
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import http2 from 'node:http2'
|
|
2
2
|
|
|
3
3
|
import { Conditional } from '../../headers/conditional.js'
|
|
4
|
-
import {
|
|
4
|
+
import { send_no_body } from '../send-util.js'
|
|
5
5
|
|
|
6
6
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
7
7
|
/** @import { SendContent, Metadata } from '../../defs.js' */
|
|
8
|
-
/** @import { EtagItem, IMFFixDateInput } from '../../headers/conditional.js' */
|
|
9
8
|
|
|
10
9
|
const {
|
|
11
10
|
HTTP2_HEADER_ETAG,
|
|
@@ -14,32 +13,19 @@ const {
|
|
|
14
13
|
|
|
15
14
|
const { HTTP_STATUS_NO_CONTENT } = http2.constants
|
|
16
15
|
|
|
17
|
-
/**
|
|
18
|
-
* @param {ServerHttp2Stream} stream
|
|
19
|
-
* @param {EtagItem|undefined} etag
|
|
20
|
-
* @param {IMFFixDateInput|string|undefined} lastModified
|
|
21
|
-
* @param {Metadata} meta
|
|
22
|
-
*/
|
|
23
|
-
export function sendNoContent(stream, etag, lastModified, meta) {
|
|
24
|
-
_sendNoContent(stream, {
|
|
25
|
-
etag,
|
|
26
|
-
lastModified
|
|
27
|
-
}, meta)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
16
|
/**
|
|
31
17
|
* @param {ServerHttp2Stream} stream
|
|
32
18
|
* @param {Pick<SendContent, 'etag' | 'lastModified'>} content
|
|
33
19
|
* @param {Metadata} meta
|
|
34
20
|
*/
|
|
35
|
-
export function
|
|
21
|
+
export function sendNoContent(stream, content, meta) {
|
|
36
22
|
const {
|
|
37
23
|
etag,
|
|
38
24
|
lastModified
|
|
39
25
|
} = content
|
|
40
26
|
|
|
41
|
-
|
|
27
|
+
send_no_body(stream, HTTP_STATUS_NO_CONTENT, {
|
|
42
28
|
[HTTP2_HEADER_ETAG]: Conditional.encodeEtag(etag),
|
|
43
29
|
[HTTP2_HEADER_LAST_MODIFIED]: Conditional.encodeFixDate(lastModified)
|
|
44
|
-
}, [],
|
|
30
|
+
}, [], meta)
|
|
45
31
|
}
|
|
@@ -7,11 +7,8 @@ import { send_bytes } from '../send-util.js'
|
|
|
7
7
|
|
|
8
8
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
9
9
|
/** @import { SendContent, Metadata, SendBody } from '../../defs.js' */
|
|
10
|
-
/** @import { EtagItem, IMFFixDateInput } from '../../headers/conditional.js' */
|
|
11
|
-
/** @import { CacheControlOptions } from '../../headers/cache-control.js' */
|
|
12
10
|
/** @import { ContentRangeDirective } from '../../headers/content-range.js' */
|
|
13
11
|
|
|
14
|
-
|
|
15
12
|
const { HTTP_STATUS_PARTIAL_CONTENT } = http2.constants
|
|
16
13
|
|
|
17
14
|
/**
|
|
@@ -25,37 +22,13 @@ const { HTTP_STATUS_PARTIAL_CONTENT } = http2.constants
|
|
|
25
22
|
* @property {ContentRangeDirective} range
|
|
26
23
|
*/
|
|
27
24
|
|
|
28
|
-
/**
|
|
29
|
-
* @param {ServerHttp2Stream} stream
|
|
30
|
-
* @param {string|undefined} contentType
|
|
31
|
-
* @param {NonEmptyArray<PartialBytes>|PartialBytes} objs
|
|
32
|
-
* @param {number|undefined} contentLength
|
|
33
|
-
* @param {string|undefined} encoding
|
|
34
|
-
* @param {EtagItem|undefined} etag
|
|
35
|
-
* @param {IMFFixDateInput|string|undefined} lastModified
|
|
36
|
-
* @param {number|undefined} age
|
|
37
|
-
* @param {CacheControlOptions} cacheControl
|
|
38
|
-
* @param {Metadata} meta
|
|
39
|
-
*/
|
|
40
|
-
export function sendPartialContent(stream, contentType, objs, contentLength, encoding, etag, lastModified, age, cacheControl, meta) {
|
|
41
|
-
return _sendPartialContent(stream, objs, {
|
|
42
|
-
contentType,
|
|
43
|
-
contentLength,
|
|
44
|
-
encoding,
|
|
45
|
-
etag,
|
|
46
|
-
lastModified,
|
|
47
|
-
age,
|
|
48
|
-
cacheControl
|
|
49
|
-
}, meta)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
25
|
/**
|
|
53
26
|
* @param {ServerHttp2Stream} stream
|
|
54
27
|
* @param {NonEmptyArray<PartialBytes>|PartialBytes} objs
|
|
55
28
|
* @param {Omit<SendContent, 'rangeDirective'>} content
|
|
56
29
|
* @param {Metadata} meta
|
|
57
30
|
*/
|
|
58
|
-
export function
|
|
31
|
+
export function sendPartialContent(stream, objs, content, meta) {
|
|
59
32
|
const {
|
|
60
33
|
contentType,
|
|
61
34
|
contentLength,
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import http2 from 'node:http2'
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
COMMON_LIST_VALUE_JOINER_COMMA,
|
|
5
|
+
// HTTP_HEADER_ACCEPT_PATCH,
|
|
6
|
+
// HTTP_HEADER_ACCEPT_POST,
|
|
4
7
|
HTTP_HEADER_ACCEPT_QUERY,
|
|
5
8
|
HTTP_METHOD_QUERY,
|
|
6
9
|
HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE,
|
|
7
10
|
PREFLIGHT_AGE_SECONDS
|
|
8
11
|
} from '../../defs.js'
|
|
9
|
-
import {
|
|
12
|
+
import { send_no_body } from '../send-util.js'
|
|
10
13
|
|
|
11
14
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
12
|
-
/** @import {
|
|
15
|
+
/** @import { SendInfo, Metadata } from '../../defs.js' */
|
|
13
16
|
|
|
14
17
|
const {
|
|
15
18
|
HTTP2_HEADER_CONTENT_TYPE,
|
|
@@ -25,27 +28,12 @@ const {
|
|
|
25
28
|
|
|
26
29
|
const { HTTP_STATUS_OK } = http2.constants
|
|
27
30
|
|
|
28
|
-
/**
|
|
29
|
-
* @param {ServerHttp2Stream} stream
|
|
30
|
-
* @param {Array<string>} supportedMethods
|
|
31
|
-
* @param {Array<string>|undefined} supportedQueryTypes
|
|
32
|
-
* @param {AcceptRangeUnits|undefined} acceptRanges
|
|
33
|
-
* @param {Metadata} meta
|
|
34
|
-
*/
|
|
35
|
-
export function sendPreflight(stream, supportedMethods, supportedQueryTypes, acceptRanges, meta) {
|
|
36
|
-
_sendPreflight(stream, {
|
|
37
|
-
supportedMethods,
|
|
38
|
-
supportedQueryTypes,
|
|
39
|
-
acceptRanges
|
|
40
|
-
}, meta)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
31
|
/**
|
|
44
32
|
* @param {ServerHttp2Stream} stream
|
|
45
33
|
* @param {Pick<SendInfo, 'supportedMethods' | 'supportedQueryTypes' | 'acceptRanges'>} info
|
|
46
34
|
* @param {Metadata} meta
|
|
47
35
|
*/
|
|
48
|
-
export function
|
|
36
|
+
export function sendPreflight(stream, info, meta) {
|
|
49
37
|
const {
|
|
50
38
|
supportedMethods,
|
|
51
39
|
supportedQueryTypes,
|
|
@@ -56,19 +44,24 @@ export function _sendPreflight(stream, info, meta) {
|
|
|
56
44
|
const exposedHeadersAcceptQuery = supportsQuery ? [ HTTP_HEADER_ACCEPT_QUERY ] : []
|
|
57
45
|
const exposedHeaders = acceptRanges === undefined ? exposedHeadersAcceptQuery : [ HTTP2_HEADER_ACCEPT_RANGES, ...exposedHeadersAcceptQuery ]
|
|
58
46
|
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
// todo: if supportedMethods includes POST | PATCH
|
|
48
|
+
// include accept-post / accept-patch headers
|
|
49
|
+
|
|
50
|
+
send_no_body(stream, HTTP_STATUS_OK, {
|
|
51
|
+
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS]: supportedMethods.join(COMMON_LIST_VALUE_JOINER_COMMA),
|
|
61
52
|
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS]: [
|
|
62
53
|
HTTP2_HEADER_IF_MATCH,
|
|
63
54
|
HTTP2_HEADER_IF_NONE_MATCH,
|
|
64
55
|
HTTP2_HEADER_AUTHORIZATION,
|
|
65
|
-
HTTP2_HEADER_CONTENT_TYPE,
|
|
66
|
-
HTTP2_HEADER_RANGE,
|
|
56
|
+
HTTP2_HEADER_CONTENT_TYPE, // overrides cors safe restriction (for json)
|
|
57
|
+
HTTP2_HEADER_RANGE, // todo cors safe override not needed
|
|
67
58
|
HTTP2_HEADER_IF_RANGE
|
|
68
|
-
].join(
|
|
59
|
+
].join(COMMON_LIST_VALUE_JOINER_COMMA),
|
|
69
60
|
[HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE]: PREFLIGHT_AGE_SECONDS,
|
|
61
|
+
// [HTTP_HEADER_ACCEPT_POST]: ,
|
|
62
|
+
// [HTTP_HEADER_ACCEPT_PATCH]: ,
|
|
70
63
|
[HTTP2_HEADER_ACCEPT_RANGES]: acceptRanges,
|
|
71
|
-
[HTTP_HEADER_ACCEPT_QUERY]: supportedQueryTypes?.join(
|
|
64
|
+
[HTTP_HEADER_ACCEPT_QUERY]: supportedQueryTypes?.join(COMMON_LIST_VALUE_JOINER_COMMA) // todo should empty array return undef
|
|
72
65
|
// Access-Control-Allow-Credentials
|
|
73
|
-
}, exposedHeaders,
|
|
66
|
+
}, exposedHeaders, meta)
|
|
74
67
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import http2 from 'node:http2'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { send_no_body } from '../send-util.js'
|
|
4
4
|
|
|
5
5
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
6
6
|
/** @import { Metadata } from '../../defs.js' */
|
|
@@ -13,11 +13,13 @@ const { HTTP_STATUS_FOUND } = http2.constants
|
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @param {ServerHttp2Stream} stream
|
|
16
|
-
* @param {URL} location
|
|
16
|
+
* @param {URL|string} location
|
|
17
17
|
* @param {Metadata} meta
|
|
18
18
|
*/
|
|
19
19
|
export function sendFound(stream, location, meta) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const loc = (location instanceof URL) ? location.href : location
|
|
21
|
+
|
|
22
|
+
send_no_body(stream, HTTP_STATUS_FOUND, {
|
|
23
|
+
[HTTP2_HEADER_LOCATION]: loc
|
|
24
|
+
}, [ HTTP2_HEADER_LOCATION ], meta)
|
|
23
25
|
}
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
import http2 from 'node:http2'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { send_no_body } from '../send-util.js'
|
|
4
4
|
|
|
5
5
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
6
6
|
/** @import { Metadata } from '../../defs.js' */
|
|
7
7
|
|
|
8
8
|
const {
|
|
9
|
-
|
|
9
|
+
HTTP2_HEADER_LOCATION
|
|
10
10
|
} = http2.constants
|
|
11
11
|
|
|
12
12
|
const { HTTP_STATUS_MOVED_PERMANENTLY } = http2.constants
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* @param {ServerHttp2Stream} stream
|
|
16
|
-
* @param {URL} location
|
|
16
|
+
* @param {URL|string} location
|
|
17
17
|
* @param {Metadata} meta
|
|
18
18
|
*/
|
|
19
19
|
export function sendMovedPermanently(stream, location, meta) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const loc = (location instanceof URL) ? location.href : location
|
|
21
|
+
|
|
22
|
+
send_no_body(stream, HTTP_STATUS_MOVED_PERMANENTLY, {
|
|
23
|
+
[HTTP2_HEADER_LOCATION]: loc
|
|
24
|
+
}, [HTTP2_HEADER_LOCATION], meta)
|
|
23
25
|
}
|