@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.
Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/package.json +2 -1
  3. package/src/body.js +11 -6
  4. package/src/defs.js +14 -5
  5. package/src/headers/accept-encoding.js +28 -5
  6. package/src/headers/accept-language.js +29 -5
  7. package/src/headers/accept.js +77 -32
  8. package/src/headers/cache-control.js +6 -3
  9. package/src/headers/client-hints.js +4 -3
  10. package/src/headers/conditional.js +18 -18
  11. package/src/headers/content-type.js +1 -1
  12. package/src/headers/link.js +9 -4
  13. package/src/headers/multipart.js +18 -17
  14. package/src/headers/range.js +4 -2
  15. package/src/headers/rate-limit.js +20 -4
  16. package/src/headers/server-timing.js +5 -3
  17. package/src/headers/util/kvp.js +2 -1
  18. package/src/headers/util/mime.js +17 -1
  19. package/src/headers/util/quote.js +1 -1
  20. package/src/headers/util/whitespace.js +1 -1
  21. package/src/headers/www-authenticate.js +35 -11
  22. package/src/response/2xx/accepted.js +2 -2
  23. package/src/response/2xx/bytes.js +2 -31
  24. package/src/response/2xx/created.js +8 -21
  25. package/src/response/2xx/json.js +6 -19
  26. package/src/response/2xx/no-content.js +4 -18
  27. package/src/response/2xx/partial-content.js +1 -28
  28. package/src/response/2xx/preflight.js +18 -25
  29. package/src/response/3xx/found.js +8 -6
  30. package/src/response/3xx/moved-permanently.js +8 -6
  31. package/src/response/3xx/multiple-choices.js +3 -3
  32. package/src/response/3xx/not-modified.js +14 -26
  33. package/src/response/3xx/permanent-redirect.js +7 -5
  34. package/src/response/3xx/see-other.js +8 -6
  35. package/src/response/3xx/temporary-redirect.js +7 -5
  36. package/src/response/4xx/bad-request.js +2 -3
  37. package/src/response/4xx/conflict.js +2 -2
  38. package/src/response/4xx/content-too-large.js +2 -2
  39. package/src/response/4xx/forbidden.js +3 -3
  40. package/src/response/4xx/gone.js +2 -2
  41. package/src/response/4xx/im-a-teapot.js +2 -2
  42. package/src/response/4xx/not-acceptable.js +2 -11
  43. package/src/response/4xx/not-allowed.js +6 -14
  44. package/src/response/4xx/payment-required.js +4 -4
  45. package/src/response/4xx/precondition-failed.js +4 -18
  46. package/src/response/4xx/range-not-satisfiable.js +4 -13
  47. package/src/response/4xx/timeout.js +3 -3
  48. package/src/response/4xx/too-many-requests.js +3 -20
  49. package/src/response/4xx/unauthorized.js +4 -4
  50. package/src/response/4xx/unprocessable.js +2 -2
  51. package/src/response/4xx/unsupported-media.js +16 -23
  52. package/src/response/5xx/error.js +2 -3
  53. package/src/response/5xx/insufficient-storage.js +2 -2
  54. package/src/response/5xx/not-implemented.js +2 -3
  55. package/src/response/5xx/unavailable.js +3 -20
  56. package/src/response/header-util.js +9 -5
  57. package/src/response/response.js +2 -2
  58. package/src/response/send-util.js +102 -30
@@ -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.substring(RANGE_UNITS_BYTES.length, RANGE_UNITS_BYTES.length + 1) === RANGE_EQUAL)) { return undefined }
57
- const rangeStr = rangeHeader.substring(RANGE_UNITS_BYTES.length + RANGE_EQUAL.length).trim()
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 const HTTP_HEADER_RATE_LIMIT = 'RateLimit'
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 from(limitInfo) {
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 from(...policies) {
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
- return timings
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
- .join(SERVER_TIMING_SEPARATOR.METRIC)
37
+
38
+ return asArray ? ary : ary.join(SERVER_TIMING_SEPARATOR.METRIC) // todo COMMON_LIST_HEADER_JOINER_COMMA
37
39
  }
38
40
  }
@@ -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
  */
@@ -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
  }
@@ -5,7 +5,7 @@ export const QUOTE = '"'
5
5
  */
6
6
  export function stripQuotes(value) {
7
7
  if(value === undefined) { return undefined }
8
- return value.substring(1, value.length - 1)
8
+ return value.slice(1, -1)
9
9
  }
10
10
 
11
11
  /**
@@ -4,5 +4,5 @@ export const WHITESPACE_REGEX = /\s/
4
4
  * @param {string} c
5
5
  */
6
6
  export function isWhitespace(c){
7
- return WHITESPACE_REGEX.test(c)
7
+ return WHITESPACE_REGEX.test(c)
8
8
  }
@@ -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} [errorUri]
59
+ * @param {string} [_errorUri]
57
60
  * @returns {ChallengeItem}
58
61
  */
59
- static bearer(realm, scope, error, errorDescription, errorUri) {
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} algorithm
74
- * @param {string} [realm]
76
+ * @param {string} _algorithm
77
+ * @param {string} [_realm]
75
78
  * @returns {ChallengeItem}
76
79
  */
77
- static digest(algorithm, realm) {
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} challenge
96
+ * @param {Array<ChallengeItem> | ChallengeItem | undefined} challenges
97
+ * @param {boolean} [asArray = false]
94
98
  */
95
- static encode(challenge) {
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
- const params = parameters === undefined ? '' : [ ...parameters ].join(',')
123
+ if(parameters === undefined) { return challenge.scheme }
124
+ const params = [ ...parameters ]
125
+ if(params.length === 0) { return challenge.scheme }
103
126
 
104
- return `${challenge.scheme} ${params}`
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 { send } from '../send-util.js'
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
- send(stream, HTTP_STATUS_ACCEPTED, {}, [], undefined, undefined, meta)
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 { AcceptRangeUnits, SendContent, SendInfo, Metadata, SendBody } from '../../defs.js' */
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 _sendBytes(stream, obj, content, info, meta) {
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 { send } from '../send-util.js'
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 _sendCreated(stream, location, content, meta) {
23
+ export function sendCreated(stream, location, content, meta) {
39
24
  const {
40
25
  etag,
41
26
  lastModified
42
27
  } = content
43
28
 
44
- send(stream, HTTP_STATUS_CREATED, {
45
- [HTTP2_HEADER_LOCATION]: location.href,
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 ], undefined, undefined, meta)
35
+ }, [ HTTP2_HEADER_LOCATION ], meta)
49
36
  }
@@ -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 {string|undefined} encoding
17
- * @param {EtagItem|undefined} etag
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, encoding, etag, lastModified, age, cacheControl, supportedQueryTypes, meta) {
25
- _sendJSON_Encoded(stream, obj, {
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 _sendJSON_Encoded(stream, obj, content, info, meta) {
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 { send } from '../send-util.js'
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 _sendNoContent(stream, content, meta) {
21
+ export function sendNoContent(stream, content, meta) {
36
22
  const {
37
23
  etag,
38
24
  lastModified
39
25
  } = content
40
26
 
41
- send(stream, HTTP_STATUS_NO_CONTENT, {
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
- }, [], undefined, undefined, meta)
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 _sendPartialContent(stream, objs, content, meta) {
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 { send } from '../send-util.js'
12
+ import { send_no_body } from '../send-util.js'
10
13
 
11
14
  /** @import { ServerHttp2Stream } from 'node:http2' */
12
- /** @import { AcceptRangeUnits, SendInfo, Metadata } from '../../defs.js' */
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 _sendPreflight(stream, info, meta) {
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
- send(stream, HTTP_STATUS_OK, {
60
- [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS]: supportedMethods.join(','),
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(',') // todo should empty array return undef
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, undefined, undefined, meta)
66
+ }, exposedHeaders, meta)
74
67
  }
@@ -1,6 +1,6 @@
1
- import http2 from 'node:http2'
1
+ import http2 from 'node:http2'
2
2
 
3
- import { send } from '../send-util.js'
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
- send(stream, HTTP_STATUS_FOUND, {
21
- [HTTP2_HEADER_LOCATION]: location.href
22
- }, [ HTTP2_HEADER_LOCATION ], undefined, undefined, meta)
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 { send } from '../send-util.js'
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
- HTTP2_HEADER_LOCATION
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
- send(stream, HTTP_STATUS_MOVED_PERMANENTLY, {
21
- [HTTP2_HEADER_LOCATION]: location.href
22
- }, [ HTTP2_HEADER_LOCATION ], undefined, undefined, meta)
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
  }