@johntalton/http-util 4.0.0 → 4.1.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/package.json +1 -1
- package/src/body.js +2 -2
- package/src/rate-limit.js +2 -0
- package/src/response/accepted.js +1 -1
- package/src/response/conflict.js +1 -1
- package/src/response/created.js +1 -1
- package/src/response/defs.js +1 -0
- package/src/response/error.js +1 -1
- package/src/response/header-util.js +18 -3
- package/src/response/index.js +2 -0
- package/src/response/json.js +1 -1
- package/src/response/no-content.js +1 -1
- package/src/response/not-acceptable.js +1 -0
- package/src/response/not-allowed.js +1 -1
- package/src/response/not-found.js +1 -1
- package/src/response/not-implemented.js +17 -0
- package/src/response/not-modified.js +1 -1
- package/src/response/precondition-failed.js +1 -1
- package/src/response/preflight.js +1 -1
- package/src/response/response.js +4 -0
- package/src/response/send-util.js +14 -5
- package/src/response/sse.js +1 -1
- package/src/response/timeout.js +1 -1
- package/src/response/too-many-requests.js +5 -0
- package/src/response/trace.js +1 -1
- package/src/response/unauthorized.js +1 -1
- package/src/response/unavailable.js +24 -0
- package/src/response/unprocessable.js +1 -1
- package/src/response/unsupported-media.js +1 -1
package/package.json
CHANGED
package/src/body.js
CHANGED
|
@@ -14,8 +14,8 @@ export const DEFAULT_BYTE_LIMIT = 1024 * 1024 //
|
|
|
14
14
|
/**
|
|
15
15
|
* @typedef {Object} BodyOptions
|
|
16
16
|
* @property {AbortSignal|undefined} [signal]
|
|
17
|
-
* @property {number} [byteLimit]
|
|
18
|
-
* @property {number} [contentLength]
|
|
17
|
+
* @property {number|undefined} [byteLimit]
|
|
18
|
+
* @property {number|undefined} [contentLength]
|
|
19
19
|
* @property {ContentType|undefined} [contentType]
|
|
20
20
|
*/
|
|
21
21
|
|
package/src/rate-limit.js
CHANGED
package/src/response/accepted.js
CHANGED
|
@@ -11,5 +11,5 @@ const { HTTP_STATUS_ACCEPTED } = http2.constants
|
|
|
11
11
|
* @param {Metadata} meta
|
|
12
12
|
*/
|
|
13
13
|
export function sendAccepted(stream, meta) {
|
|
14
|
-
send(stream, HTTP_STATUS_ACCEPTED, {}, undefined, undefined, meta)
|
|
14
|
+
send(stream, HTTP_STATUS_ACCEPTED, {}, [], undefined, undefined, meta)
|
|
15
15
|
}
|
package/src/response/conflict.js
CHANGED
|
@@ -11,5 +11,5 @@ const { HTTP_STATUS_CONFLICT } = http2.constants
|
|
|
11
11
|
* @param {Metadata} meta
|
|
12
12
|
*/
|
|
13
13
|
export function sendConflict(stream, meta) {
|
|
14
|
-
send(stream, HTTP_STATUS_CONFLICT, {}, undefined, undefined, meta)
|
|
14
|
+
send(stream, HTTP_STATUS_CONFLICT, {}, [], undefined, undefined, meta)
|
|
15
15
|
}
|
package/src/response/created.js
CHANGED
|
@@ -23,5 +23,5 @@ export function sendCreated(stream, location, etag, meta) {
|
|
|
23
23
|
send(stream, HTTP_STATUS_CREATED, {
|
|
24
24
|
[HTTP2_HEADER_LOCATION]: location.href,
|
|
25
25
|
[HTTP2_HEADER_ETAG]: Conditional.encodeEtag(etag)
|
|
26
|
-
}, undefined, undefined, meta)
|
|
26
|
+
}, [ HTTP2_HEADER_LOCATION ], undefined, undefined, meta)
|
|
27
27
|
}
|
package/src/response/defs.js
CHANGED
package/src/response/error.js
CHANGED
|
@@ -13,5 +13,5 @@ const { HTTP_STATUS_INTERNAL_SERVER_ERROR } = http2.constants
|
|
|
13
13
|
* @param {Metadata} meta
|
|
14
14
|
*/
|
|
15
15
|
export function sendError(stream, message, meta) {
|
|
16
|
-
send(stream, HTTP_STATUS_INTERNAL_SERVER_ERROR, {}, CONTENT_TYPE_TEXT, message, meta)
|
|
16
|
+
send(stream, HTTP_STATUS_INTERNAL_SERVER_ERROR, {}, [], CONTENT_TYPE_TEXT, message, meta)
|
|
17
17
|
}
|
|
@@ -12,19 +12,25 @@ const {
|
|
|
12
12
|
HTTP2_HEADER_STATUS,
|
|
13
13
|
HTTP2_HEADER_SERVER,
|
|
14
14
|
HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
|
15
|
-
HTTP2_HEADER_CONTENT_TYPE
|
|
15
|
+
HTTP2_HEADER_CONTENT_TYPE,
|
|
16
|
+
HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
|
|
17
|
+
|
|
18
|
+
HTTP2_HEADER_ETAG
|
|
16
19
|
} = http2.constants
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* @param {number} status
|
|
20
23
|
* @param {string|undefined} contentType
|
|
24
|
+
* @param {Array<string>} exposedHeaders
|
|
21
25
|
* @param {Metadata} meta
|
|
22
26
|
* @returns {OutgoingHttpHeaders}
|
|
23
27
|
*/
|
|
24
|
-
export function coreHeaders(status, contentType, meta) {
|
|
28
|
+
export function coreHeaders(status, contentType, exposedHeaders, meta) {
|
|
29
|
+
const exposed = [ HTTP2_HEADER_ETAG, HTTP2_HEADER_SERVER, ...exposedHeaders ]
|
|
30
|
+
|
|
25
31
|
return {
|
|
26
32
|
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
|
|
27
|
-
|
|
33
|
+
[HTTP2_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS]: exposed.join(','),
|
|
28
34
|
// Access-Control-Allow-Credentials // for non-preflight
|
|
29
35
|
[HTTP2_HEADER_STATUS]: status,
|
|
30
36
|
[HTTP2_HEADER_CONTENT_TYPE]: contentType,
|
|
@@ -42,3 +48,12 @@ export function performanceHeaders(meta) {
|
|
|
42
48
|
[HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance)
|
|
43
49
|
}
|
|
44
50
|
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @param {Metadata} meta
|
|
54
|
+
* @returns {OutgoingHttpHeaders}
|
|
55
|
+
*/
|
|
56
|
+
export function customHeaders(meta) {
|
|
57
|
+
const m = new Map(meta.customHeaders?.filter(h => h[0].startsWith('X-')))
|
|
58
|
+
return Object.fromEntries(m)
|
|
59
|
+
}
|
package/src/response/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export * from './no-content.js'
|
|
|
9
9
|
export * from './not-acceptable.js'
|
|
10
10
|
export * from './not-allowed.js'
|
|
11
11
|
export * from './not-found.js'
|
|
12
|
+
export * from './not-implemented.js'
|
|
12
13
|
export * from './not-modified.js'
|
|
13
14
|
export * from './precondition-failed.js'
|
|
14
15
|
export * from './preflight.js'
|
|
@@ -17,5 +18,6 @@ export * from './timeout.js'
|
|
|
17
18
|
export * from './too-many-requests.js'
|
|
18
19
|
export * from './trace.js'
|
|
19
20
|
export * from './unauthorized.js'
|
|
21
|
+
export * from './unavailable.js'
|
|
20
22
|
export * from './unprocessable.js'
|
|
21
23
|
export * from './unsupported-media.js'
|
package/src/response/json.js
CHANGED
|
@@ -71,5 +71,5 @@ export function sendJSON_Encoded(stream, obj, encoding, etag, age, cacheControl,
|
|
|
71
71
|
[HTTP2_HEADER_CACHE_CONTROL]: CacheControl.encode(cacheControl),
|
|
72
72
|
[HTTP2_HEADER_ETAG]: Conditional.encodeEtag(etag),
|
|
73
73
|
[HTTP2_HEADER_AGE]: age !== undefined ? `${age}` : undefined
|
|
74
|
-
}, CONTENT_TYPE_JSON, encodedData, meta)
|
|
74
|
+
}, [ HTTP2_HEADER_AGE ], CONTENT_TYPE_JSON, encodedData, meta)
|
|
75
75
|
}
|
|
@@ -20,5 +20,5 @@ const { HTTP_STATUS_NO_CONTENT } = http2.constants
|
|
|
20
20
|
export function sendNoContent(stream, etag, meta) {
|
|
21
21
|
send(stream, HTTP_STATUS_NO_CONTENT, {
|
|
22
22
|
[HTTP2_HEADER_ETAG]: Conditional.encodeEtag(etag)
|
|
23
|
-
}, undefined, undefined, meta)
|
|
23
|
+
}, [], undefined, undefined, meta)
|
|
24
24
|
}
|
|
@@ -18,5 +18,5 @@ const { HTTP2_HEADER_ALLOW } = http2.constants
|
|
|
18
18
|
export function sendNotAllowed(stream, methods, meta) {
|
|
19
19
|
send(stream, HTTP_STATUS_METHOD_NOT_ALLOWED, {
|
|
20
20
|
[HTTP2_HEADER_ALLOW]: methods.join(',')
|
|
21
|
-
}, undefined, undefined, meta)
|
|
21
|
+
}, [ HTTP2_HEADER_ALLOW ], undefined, undefined, meta)
|
|
22
22
|
}
|
|
@@ -13,5 +13,5 @@ const { HTTP_STATUS_NOT_FOUND } = http2.constants
|
|
|
13
13
|
* @param {Metadata} meta
|
|
14
14
|
*/
|
|
15
15
|
export function sendNotFound(stream, message, meta) {
|
|
16
|
-
send(stream, HTTP_STATUS_NOT_FOUND, {}, CONTENT_TYPE_TEXT, message, meta)
|
|
16
|
+
send(stream, HTTP_STATUS_NOT_FOUND, {}, [], CONTENT_TYPE_TEXT, message, meta)
|
|
17
17
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import http2 from 'node:http2'
|
|
2
|
+
import { CONTENT_TYPE_TEXT } from '../content-type.js'
|
|
3
|
+
import { send } from './send-util.js'
|
|
4
|
+
|
|
5
|
+
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
6
|
+
/** @import { Metadata } from './defs.js' */
|
|
7
|
+
|
|
8
|
+
const { HTTP_STATUS_NOT_IMPLEMENTED } = http2.constants
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {ServerHttp2Stream} stream
|
|
12
|
+
* @param {string|undefined} message
|
|
13
|
+
* @param {Metadata} meta
|
|
14
|
+
*/
|
|
15
|
+
export function sendNotImplemented(stream, message, meta) {
|
|
16
|
+
send(stream, HTTP_STATUS_NOT_IMPLEMENTED, {}, [], CONTENT_TYPE_TEXT, message, meta)
|
|
17
|
+
}
|
|
@@ -30,5 +30,5 @@ export function sendNotModified(stream, etag, age, cacheControl, meta) {
|
|
|
30
30
|
[HTTP2_HEADER_CACHE_CONTROL]: CacheControl.encode(cacheControl),
|
|
31
31
|
[HTTP2_HEADER_ETAG]: Conditional.encodeEtag(etag),
|
|
32
32
|
[HTTP2_HEADER_AGE]: age !== undefined ? `${age}` : undefined
|
|
33
|
-
}, undefined, undefined, meta)
|
|
33
|
+
}, [ HTTP2_HEADER_AGE ], undefined, undefined, meta)
|
|
34
34
|
}
|
|
@@ -11,5 +11,5 @@ const { HTTP_STATUS_PRECONDITION_FAILED } = http2.constants
|
|
|
11
11
|
* @param {Metadata} meta
|
|
12
12
|
*/
|
|
13
13
|
export function sendPreconditionFailed(stream, meta) {
|
|
14
|
-
send(stream, HTTP_STATUS_PRECONDITION_FAILED, {}, undefined, undefined, meta)
|
|
14
|
+
send(stream, HTTP_STATUS_PRECONDITION_FAILED, {}, [], undefined, undefined, meta)
|
|
15
15
|
}
|
package/src/response/response.js
CHANGED
|
@@ -7,6 +7,7 @@ import { sendNoContent } from './no-content.js'
|
|
|
7
7
|
import { sendNotAcceptable } from './not-acceptable.js'
|
|
8
8
|
import { sendNotAllowed } from './not-allowed.js'
|
|
9
9
|
import { sendNotFound } from './not-found.js'
|
|
10
|
+
import { sendNotImplemented } from './not-implemented.js'
|
|
10
11
|
import { sendNotModified } from './not-modified.js'
|
|
11
12
|
import { sendPreconditionFailed } from './precondition-failed.js'
|
|
12
13
|
import { sendPreflight } from './preflight.js'
|
|
@@ -15,6 +16,7 @@ import { sendTimeout } from './timeout.js'
|
|
|
15
16
|
import { sendTooManyRequests } from './too-many-requests.js'
|
|
16
17
|
import { sendTrace } from './trace.js'
|
|
17
18
|
import { sendUnauthorized } from './unauthorized.js'
|
|
19
|
+
import { sendUnavailable } from './unavailable.js'
|
|
18
20
|
import { sendUnprocessable } from './unprocessable.js'
|
|
19
21
|
import { sendUnsupportedMediaType } from './unsupported-media.js'
|
|
20
22
|
|
|
@@ -28,6 +30,7 @@ export const Response = {
|
|
|
28
30
|
notAcceptable: sendNotAcceptable,
|
|
29
31
|
notAllowed: sendNotAllowed,
|
|
30
32
|
notFound: sendNotFound,
|
|
33
|
+
notImplemented: sendNotImplemented,
|
|
31
34
|
notModified: sendNotModified,
|
|
32
35
|
preconditionFailed: sendPreconditionFailed,
|
|
33
36
|
preflight: sendPreflight,
|
|
@@ -36,6 +39,7 @@ export const Response = {
|
|
|
36
39
|
tooManyRequests: sendTooManyRequests,
|
|
37
40
|
trace: sendTrace,
|
|
38
41
|
unauthorized: sendUnauthorized,
|
|
42
|
+
unavailable: sendUnavailable,
|
|
39
43
|
unprocessable: sendUnprocessable,
|
|
40
44
|
unsupportedMediaType: sendUnsupportedMediaType
|
|
41
45
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
coreHeaders,
|
|
3
|
+
customHeaders,
|
|
4
|
+
performanceHeaders
|
|
5
|
+
} from './header-util.js'
|
|
2
6
|
|
|
3
7
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
4
8
|
/** @import { IncomingHttpHeaders } from 'node:http2' */
|
|
@@ -8,24 +12,29 @@ import { coreHeaders, performanceHeaders } from './header-util.js'
|
|
|
8
12
|
* @param {ServerHttp2Stream} stream
|
|
9
13
|
* @param {number} status
|
|
10
14
|
* @param {IncomingHttpHeaders} headers
|
|
15
|
+
* @param {Array<string>} exposedHeaders
|
|
11
16
|
* @param {string|undefined} contentType
|
|
12
17
|
* @param {ArrayBufferLike|ArrayBufferView|string|undefined} body
|
|
13
18
|
* @param {Metadata} meta
|
|
14
19
|
*/
|
|
15
|
-
export function send(stream, status, headers, contentType, body, meta) {
|
|
20
|
+
export function send(stream, status, headers, exposedHeaders, contentType, body, meta) {
|
|
16
21
|
// if(status >= 400) { console.warn(status, body) }
|
|
17
22
|
if(status === 401) { console.warn(status, body) }
|
|
18
23
|
if(status === 404) { console.warn(status, body) }
|
|
19
|
-
if(status
|
|
24
|
+
if(status >= 500) { console.warn(status, body) }
|
|
20
25
|
|
|
21
26
|
if(stream === undefined) { return }
|
|
22
27
|
if(stream.closed) { return }
|
|
23
28
|
|
|
24
29
|
if(!stream.headersSent) {
|
|
30
|
+
const custom = customHeaders(meta)
|
|
31
|
+
const exposed = [ ...exposedHeaders, ...Object.keys(custom) ]
|
|
32
|
+
|
|
25
33
|
stream.respond({
|
|
26
|
-
...coreHeaders(status, contentType, meta),
|
|
34
|
+
...coreHeaders(status, contentType, exposed, meta),
|
|
27
35
|
...performanceHeaders(meta),
|
|
28
|
-
...headers
|
|
36
|
+
...headers,
|
|
37
|
+
...custom
|
|
29
38
|
})
|
|
30
39
|
}
|
|
31
40
|
|
package/src/response/sse.js
CHANGED
|
@@ -23,7 +23,7 @@ export function sendSSE(stream, meta) {
|
|
|
23
23
|
const status = activeStream ? HTTP_STATUS_OK : SSE_INACTIVE_STATUS_CODE
|
|
24
24
|
|
|
25
25
|
stream.respond({
|
|
26
|
-
...coreHeaders(status, SSE_MIME, meta),
|
|
26
|
+
...coreHeaders(status, SSE_MIME, [], meta),
|
|
27
27
|
...performanceHeaders(meta)
|
|
28
28
|
|
|
29
29
|
// [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true'
|
package/src/response/timeout.js
CHANGED
|
@@ -30,6 +30,11 @@ export function sendTooManyRequests(stream, limitInfo, policies, meta) {
|
|
|
30
30
|
[HTTP_HEADER_RATE_LIMIT]: RateLimit.from(limitInfo),
|
|
31
31
|
[HTTP_HEADER_RATE_LIMIT_POLICY]: RateLimitPolicy.from(...policies)
|
|
32
32
|
},
|
|
33
|
+
[
|
|
34
|
+
HTTP2_HEADER_RETRY_AFTER,
|
|
35
|
+
HTTP_HEADER_RATE_LIMIT,
|
|
36
|
+
HTTP_HEADER_RATE_LIMIT_POLICY
|
|
37
|
+
],
|
|
33
38
|
CONTENT_TYPE_TEXT,
|
|
34
39
|
`Retry After ${limitInfo.resetSeconds} Seconds`,
|
|
35
40
|
meta)
|
package/src/response/trace.js
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import http2 from 'node:http2'
|
|
2
|
+
import { CONTENT_TYPE_TEXT } from '../content-type.js'
|
|
3
|
+
import { send } from './send-util.js'
|
|
4
|
+
|
|
5
|
+
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
6
|
+
/** @import { Metadata } from './defs.js' */
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
HTTP2_HEADER_RETRY_AFTER
|
|
10
|
+
} = http2.constants
|
|
11
|
+
|
|
12
|
+
const { HTTP_STATUS_SERVICE_UNAVAILABLE } = http2.constants
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {ServerHttp2Stream} stream
|
|
16
|
+
* @param {string|undefined} message
|
|
17
|
+
* @param {number|undefined} retryAfter
|
|
18
|
+
* @param {Metadata} meta
|
|
19
|
+
*/
|
|
20
|
+
export function sendUnavailable(stream, message, retryAfter, meta) {
|
|
21
|
+
send(stream, HTTP_STATUS_SERVICE_UNAVAILABLE, {
|
|
22
|
+
[HTTP2_HEADER_RETRY_AFTER]: Number.isInteger(retryAfter) ? `${retryAfter}` : undefined
|
|
23
|
+
}, [ HTTP2_HEADER_RETRY_AFTER ], CONTENT_TYPE_TEXT, message, meta)
|
|
24
|
+
}
|
|
@@ -11,5 +11,5 @@ const { HTTP_STATUS_UNPROCESSABLE_ENTITY } = http2.constants
|
|
|
11
11
|
* @param {Metadata} meta
|
|
12
12
|
*/
|
|
13
13
|
export function sendUnprocessable(stream, meta) {
|
|
14
|
-
send(stream, HTTP_STATUS_UNPROCESSABLE_ENTITY, {}, undefined, undefined, meta)
|
|
14
|
+
send(stream, HTTP_STATUS_UNPROCESSABLE_ENTITY, {}, [], undefined, undefined, meta)
|
|
15
15
|
}
|
|
@@ -17,5 +17,5 @@ export function sendUnsupportedMediaType(stream, acceptableMediaType, meta) {
|
|
|
17
17
|
|
|
18
18
|
send(stream, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, {
|
|
19
19
|
[HTTP_HEADER_ACCEPT_POST]: acceptable.join(',')
|
|
20
|
-
}, undefined, undefined, meta)
|
|
20
|
+
}, [ HTTP_HEADER_ACCEPT_POST ], undefined, undefined, meta)
|
|
21
21
|
}
|