@johntalton/http-util 2.0.0 → 2.0.3

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.
@@ -0,0 +1,78 @@
1
+ import http2 from 'node:http2'
2
+ import { brotliCompressSync, deflateSync, gzipSync, zstdCompressSync } from 'node:zlib'
3
+ import { ServerTiming, HTTP_HEADER_SERVER_TIMING, HTTP_HEADER_TIMING_ALLOW_ORIGIN } from '../server-timing.js'
4
+ import {
5
+ CHARSET_UTF8,
6
+ CONTENT_TYPE_JSON
7
+ } from '../content-type.js'
8
+
9
+ /** @import { ServerHttp2Stream } from 'node:http2' */
10
+ /** @import { Metadata } from './defs.js' */
11
+
12
+ /** @typedef { (data: string, charset: BufferEncoding) => Buffer } EncoderFun */
13
+
14
+ const {
15
+ HTTP2_HEADER_STATUS,
16
+ HTTP2_HEADER_CONTENT_TYPE,
17
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
18
+ HTTP2_HEADER_SERVER,
19
+ HTTP2_HEADER_CONTENT_ENCODING,
20
+ HTTP2_HEADER_VARY,
21
+ HTTP2_HEADER_CACHE_CONTROL,
22
+ HTTP2_HEADER_ETAG
23
+ } = http2.constants
24
+
25
+ const {
26
+ HTTP_STATUS_OK
27
+ } = http2.constants
28
+
29
+ /** @type {Map<string, EncoderFun>} */
30
+ export const ENCODER_MAP = new Map([
31
+ [ 'br', (data, charset) => brotliCompressSync(Buffer.from(data, charset)) ],
32
+ [ 'gzip', (data, charset) => gzipSync(Buffer.from(data, charset)) ],
33
+ [ 'deflate', (data, charset) => deflateSync(Buffer.from(data, charset)) ],
34
+ [ 'zstd', (data, charset) => zstdCompressSync(Buffer.from(data, charset)) ]
35
+ ])
36
+
37
+ /**
38
+ * @param {ServerHttp2Stream} stream
39
+ * @param {Object} obj
40
+ * @param {string|undefined} encoding
41
+ * @param {Metadata} meta
42
+ */
43
+ export function sendJSON_Encoded(stream, obj, encoding, meta) {
44
+ if(stream.closed) { return }
45
+
46
+ const json = JSON.stringify(obj)
47
+
48
+ const useIdentity = encoding === 'identity'
49
+ const encoder = encoding !== undefined ? ENCODER_MAP.get(encoding) : undefined
50
+ const hasEncoder = encoder !== undefined
51
+ const actualEncoding = hasEncoder ? encoding : undefined
52
+
53
+ const encodeStart = performance.now()
54
+ const encodedData = hasEncoder && !useIdentity ? encoder(json, CHARSET_UTF8) : json
55
+ const encodeEnd = performance.now()
56
+
57
+ meta.performance.push(
58
+ { name: 'encode', duration: encodeEnd - encodeStart }
59
+ )
60
+
61
+ stream.respond({
62
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
63
+ [HTTP2_HEADER_CONTENT_TYPE]: CONTENT_TYPE_JSON,
64
+ [HTTP2_HEADER_CONTENT_ENCODING]: actualEncoding,
65
+ [HTTP2_HEADER_VARY]: 'Accept, Accept-Encoding',
66
+ [HTTP2_HEADER_CACHE_CONTROL]: 'private',
67
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_OK,
68
+ [HTTP2_HEADER_SERVER]: meta.servername,
69
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
70
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
71
+ [HTTP2_HEADER_ETAG]: `"${meta.etag}"`
72
+ // [HTTP2_HEADER_AGE]: age
73
+ })
74
+
75
+ // stream.write(encodedData)
76
+ stream.end(encodedData)
77
+ }
78
+
@@ -0,0 +1,36 @@
1
+ import http2 from 'node:http2'
2
+ import {
3
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
4
+ HTTP_HEADER_SERVER_TIMING,
5
+ ServerTiming
6
+ } from '../server-timing.js'
7
+
8
+ /** @import { ServerHttp2Stream } from 'node:http2' */
9
+ /** @import { Metadata } from './defs.js' */
10
+
11
+ const {
12
+ HTTP_STATUS_NO_CONTENT
13
+ } = http2.constants
14
+
15
+ const {
16
+ HTTP2_HEADER_STATUS,
17
+ HTTP2_HEADER_SERVER,
18
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
19
+ HTTP2_HEADER_ETAG
20
+ } = http2.constants
21
+
22
+ /**
23
+ * @param {ServerHttp2Stream} stream
24
+ * @param {Metadata} meta
25
+ */
26
+ export function sendNoContent(stream, meta) {
27
+ stream.respond({
28
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
29
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_NO_CONTENT,
30
+ [HTTP2_HEADER_SERVER]: meta.servername,
31
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
32
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
33
+ [HTTP2_HEADER_ETAG]: `"${meta.etag}"`
34
+ })
35
+ stream.end()
36
+ }
@@ -0,0 +1,46 @@
1
+ import http2 from 'node:http2'
2
+ import { MIME_TYPE_JSON } from '../content-type.js'
3
+ import {
4
+ HTTP_HEADER_SERVER_TIMING,
5
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
6
+ ServerTiming
7
+ } from '../server-timing.js'
8
+
9
+ /** @import { ServerHttp2Stream } from 'node:http2' */
10
+ /** @import { Metadata } from './defs.js' */
11
+
12
+ const {
13
+ HTTP_STATUS_NOT_ACCEPTABLE
14
+ } = http2.constants
15
+
16
+ const {
17
+ HTTP2_HEADER_STATUS,
18
+ HTTP2_HEADER_SERVER,
19
+ HTTP2_HEADER_CONTENT_TYPE,
20
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
21
+ } = http2.constants
22
+
23
+ /**
24
+ * @param {ServerHttp2Stream} stream
25
+ * @param {Array<string>|string} supportedTypes
26
+ * @param {Metadata} meta
27
+ */
28
+ export function sendNotAcceptable(stream, supportedTypes, meta) {
29
+ const supportedTypesList = Array.isArray(supportedTypes) ? supportedTypes : [ supportedTypes ]
30
+ const has = supportedTypesList.length !== 0
31
+
32
+ stream.respond({
33
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
34
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_NOT_ACCEPTABLE,
35
+ [HTTP2_HEADER_SERVER]: meta.servername,
36
+ [HTTP2_HEADER_CONTENT_TYPE]: has ? MIME_TYPE_JSON : undefined,
37
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
38
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
39
+ })
40
+
41
+ if(has) {
42
+ stream.write(JSON.stringify(supportedTypes))
43
+ }
44
+
45
+ stream.end()
46
+ }
@@ -0,0 +1,30 @@
1
+ import http2 from 'node:http2'
2
+
3
+ /** @import { ServerHttp2Stream } from 'node:http2' */
4
+ /** @import { Metadata } from './defs.js' */
5
+
6
+ const {
7
+ HTTP2_HEADER_STATUS,
8
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
9
+ HTTP2_HEADER_SERVER,
10
+ HTTP2_HEADER_ALLOW
11
+ } = http2.constants
12
+
13
+ const {
14
+ HTTP_STATUS_METHOD_NOT_ALLOWED
15
+ } = http2.constants
16
+
17
+ /**
18
+ * @param {ServerHttp2Stream} stream
19
+ * @param {Array<string>} methods
20
+ * @param {Metadata} meta
21
+ */
22
+ export function sendNotAllowed(stream, methods, meta) {
23
+ stream.respond({
24
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
25
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_METHOD_NOT_ALLOWED,
26
+ [HTTP2_HEADER_ALLOW]: methods.join(','),
27
+ [HTTP2_HEADER_SERVER]: meta.servername
28
+ })
29
+ stream.end()
30
+ }
@@ -0,0 +1,37 @@
1
+ import http2 from 'node:http2'
2
+ import {
3
+ CONTENT_TYPE_TEXT
4
+ } from '../content-type.js'
5
+
6
+ /** @import { ServerHttp2Stream } from 'node:http2' */
7
+ /** @import { Metadata } from './defs.js' */
8
+
9
+ const {
10
+ HTTP2_HEADER_STATUS,
11
+ HTTP2_HEADER_CONTENT_TYPE,
12
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
13
+ HTTP2_HEADER_SERVER
14
+ } = http2.constants
15
+
16
+ const {
17
+ HTTP_STATUS_NOT_FOUND
18
+ } = http2.constants
19
+
20
+ /**
21
+ * @param {ServerHttp2Stream} stream
22
+ * @param {string} message
23
+ * @param {Metadata} meta
24
+ */
25
+ export function sendNotFound(stream, message, meta) {
26
+ console.log('404', message)
27
+
28
+ stream.respond({
29
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
30
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_NOT_FOUND,
31
+ [HTTP2_HEADER_CONTENT_TYPE]: CONTENT_TYPE_TEXT,
32
+ [HTTP2_HEADER_SERVER]: meta.servername
33
+ })
34
+
35
+ if(message !== undefined) { stream.write(message) }
36
+ stream.end()
37
+ }
@@ -0,0 +1,43 @@
1
+ import http2 from 'node:http2'
2
+ import {
3
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
4
+ HTTP_HEADER_SERVER_TIMING,
5
+ ServerTiming
6
+ } from '../server-timing.js'
7
+
8
+ /** @import { ServerHttp2Stream } from 'node:http2' */
9
+ /** @import { Metadata } from './defs.js' */
10
+
11
+ const {
12
+ HTTP_STATUS_NOT_MODIFIED
13
+ } = http2.constants
14
+
15
+ const {
16
+ HTTP2_HEADER_STATUS,
17
+ HTTP2_HEADER_SERVER,
18
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
19
+ HTTP2_HEADER_AGE,
20
+ HTTP2_HEADER_ETAG,
21
+ HTTP2_HEADER_VARY,
22
+ HTTP2_HEADER_CACHE_CONTROL
23
+ } = http2.constants
24
+
25
+ /**
26
+ * @param {ServerHttp2Stream} stream
27
+ * @param {number} age
28
+ * @param {Metadata} meta
29
+ */
30
+ export function sendNotModified(stream, age, meta) {
31
+ stream.respond({
32
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
33
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_NOT_MODIFIED,
34
+ [HTTP2_HEADER_VARY]: 'Accept, Accept-Encoding',
35
+ [HTTP2_HEADER_CACHE_CONTROL]: 'private',
36
+ [HTTP2_HEADER_SERVER]: meta.servername,
37
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
38
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
39
+ [HTTP2_HEADER_ETAG]: `"${meta.etag}"`,
40
+ [HTTP2_HEADER_AGE]: age
41
+ })
42
+ stream.end()
43
+ }
@@ -0,0 +1,38 @@
1
+ import http2 from 'node:http2'
2
+ import {
3
+ HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE,
4
+ PREFLIGHT_AGE_SECONDS
5
+ } from './defs.js'
6
+
7
+ /** @import { ServerHttp2Stream } from 'node:http2' */
8
+ /** @import { Metadata } from './defs.js' */
9
+
10
+ const {
11
+ HTTP2_HEADER_STATUS,
12
+ HTTP2_HEADER_CONTENT_TYPE,
13
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
14
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS,
15
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS,
16
+ HTTP2_HEADER_SERVER
17
+ } = http2.constants
18
+
19
+ const {
20
+ HTTP_STATUS_OK
21
+ } = http2.constants
22
+
23
+ /**
24
+ * @param {ServerHttp2Stream} stream
25
+ * @param {Array<string>} methods
26
+ * @param {Metadata} meta
27
+ */
28
+ export function sendPreflight(stream, methods, meta) {
29
+ stream.respond({
30
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_OK,
31
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
32
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS]: methods.join(','),
33
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS]: ['Authorization', HTTP2_HEADER_CONTENT_TYPE].join(','),
34
+ [HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE]: PREFLIGHT_AGE_SECONDS,
35
+ [HTTP2_HEADER_SERVER]: meta.servername
36
+ })
37
+ stream.end()
38
+ }
@@ -0,0 +1,39 @@
1
+ import { sendAccepted } from './accepted.js'
2
+ import { sendConflict } from './conflict.js'
3
+ import { sendCreated } from './created.js'
4
+ import { sendError } from './error.js'
5
+ import { sendJSON_Encoded } from './json.js'
6
+ import { sendNoContent } from './no-content.js'
7
+ import { sendNotAcceptable } from './not-acceptable.js'
8
+ import { sendNotAllowed } from './not-allowed.js'
9
+ import { sendNotFound } from './not-found.js'
10
+ import { sendNotModified } from './not-modified.js'
11
+ import { sendPreflight } from './preflight.js'
12
+ import { sendSSE } from './sse.js'
13
+ import { sendTimeout } from './timeout.js'
14
+ import { sendTooManyRequests } from './too-many-requests.js'
15
+ import { sendTrace } from './trace.js'
16
+ import { sendUnauthorized } from './unauthorized.js'
17
+ import { sendUnsupportedMediaType } from './unsupported-media.js'
18
+ import { sendUnprocessable } from './unprocessable.js'
19
+
20
+ export const Response = {
21
+ accepted: sendAccepted,
22
+ conflict: sendConflict,
23
+ created: sendCreated,
24
+ error: sendError,
25
+ noContent: sendNoContent,
26
+ json: sendJSON_Encoded,
27
+ notAcceptable: sendNotAcceptable,
28
+ notAllowed: sendNotAllowed,
29
+ notFound: sendNotFound,
30
+ notModified: sendNotModified,
31
+ preflight: sendPreflight,
32
+ sse: sendSSE,
33
+ timeout: sendTimeout,
34
+ tooManyRequests: sendTooManyRequests,
35
+ trace: sendTrace,
36
+ unauthorized: sendUnauthorized,
37
+ unsupportedMediaType: sendUnsupportedMediaType,
38
+ unprocessable: sendUnprocessable
39
+ }
@@ -0,0 +1,57 @@
1
+ import http2 from 'node:http2'
2
+ import {
3
+ SSE_MIME,
4
+ SSE_INACTIVE_STATUS_CODE,
5
+ SSE_BOM,
6
+ ENDING,
7
+ } from '@johntalton/sse-util'
8
+
9
+ /** @import { ServerHttp2Stream } from 'node:http2' */
10
+ /** @import { Metadata, SSEOptions } from './defs.js' */
11
+
12
+ const {
13
+ HTTP2_HEADER_STATUS,
14
+ HTTP2_HEADER_CONTENT_TYPE,
15
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
16
+ HTTP2_HEADER_SERVER
17
+ } = http2.constants
18
+
19
+ const {
20
+ HTTP_STATUS_OK,
21
+ HTTP_STATUS_NO_CONTENT
22
+ } = http2.constants
23
+
24
+ /**
25
+ * @param {ServerHttp2Stream} stream
26
+ * @param {SSEOptions & Metadata} meta
27
+ */
28
+ export function sendSSE(stream, meta) {
29
+ // stream.setTimeout(0)
30
+ // stream.session?.setTimeout(0)
31
+ // stream.session?.socket.setTimeout(0)
32
+ // stream.session.socket.setNoDelay(true)
33
+ // stream.session.socket.setKeepAlive(true)
34
+
35
+ // stream.on('close', () => console.log('SSE stream closed'))
36
+ // stream.on('aborted', () => console.log('SSE stream aborted'))
37
+
38
+ const activeStream = meta.active ?? true
39
+ const sendBOM = meta.bom ?? true
40
+
41
+ stream.respond({
42
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
43
+ [HTTP2_HEADER_CONTENT_TYPE]: SSE_MIME,
44
+ [HTTP2_HEADER_STATUS]: activeStream ? HTTP_STATUS_OK : HTTP_STATUS_NO_CONTENT, // SSE_INACTIVE_STATUS_CODE
45
+ // [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true'
46
+ [HTTP2_HEADER_SERVER]: meta.servername
47
+ })
48
+
49
+ if(!activeStream) {
50
+ stream.end()
51
+ return
52
+ }
53
+
54
+ if(sendBOM) {
55
+ stream.write(SSE_BOM + ENDING.CRLF)
56
+ }
57
+ }
@@ -0,0 +1,41 @@
1
+ import http2 from 'node:http2'
2
+ import { CONTENT_TYPE_JSON } from '../content-type.js'
3
+ import {
4
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
5
+ HTTP_HEADER_SERVER_TIMING,
6
+ ServerTiming
7
+ } from '../server-timing.js'
8
+
9
+ /** @import { ServerHttp2Stream } from 'node:http2' */
10
+ /** @import { Metadata } from './defs.js' */
11
+
12
+ const {
13
+ HTTP_STATUS_REQUEST_TIMEOUT
14
+ } = http2.constants
15
+
16
+ const {
17
+ HTTP2_HEADER_STATUS,
18
+ HTTP2_HEADER_SERVER,
19
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
20
+ HTTP2_HEADER_CONNECTION
21
+ } = http2.constants
22
+
23
+ /**
24
+ * @param {ServerHttp2Stream} stream
25
+ * @param {Metadata} meta
26
+ */
27
+ export function sendTimeout(stream, meta) {
28
+ stream.respond({
29
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
30
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_REQUEST_TIMEOUT,
31
+ // [HTTP2_HEADER_CONTENT_TYPE]: CONTENT_TYPE_JSON,
32
+ [HTTP2_HEADER_SERVER]: meta.servername,
33
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
34
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
35
+ [HTTP2_HEADER_CONNECTION]: 'close'
36
+ })
37
+
38
+ // stream.write(JSON.stringify( ... ))
39
+
40
+ stream.end()
41
+ }
@@ -0,0 +1,47 @@
1
+ import http2 from 'node:http2'
2
+ import { CONTENT_TYPE_TEXT } from '../content-type.js'
3
+ import {
4
+ HTTP_HEADER_RATE_LIMIT,
5
+ HTTP_HEADER_RATE_LIMIT_POLICY,
6
+ RateLimit,
7
+ RateLimitPolicy
8
+ } from '../rate-limit.js'
9
+
10
+
11
+ /** @import { ServerHttp2Stream } from 'node:http2' */
12
+ /** @import { Metadata } from './defs.js' */
13
+
14
+ const {
15
+ HTTP2_HEADER_STATUS,
16
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
17
+ HTTP2_HEADER_SERVER,
18
+ HTTP2_HEADER_RETRY_AFTER,
19
+ HTTP2_HEADER_CONTENT_TYPE
20
+ } = http2.constants
21
+
22
+ const {
23
+ HTTP_STATUS_TOO_MANY_REQUESTS
24
+ } = http2.constants
25
+
26
+ /**
27
+ * @param {ServerHttp2Stream} stream
28
+ * @param {*} limitInfo
29
+ * @param {Array<any>} policies
30
+ * @param {Metadata} meta
31
+ */
32
+ export function sendTooManyRequests(stream, limitInfo, policies, meta) {
33
+ stream.respond({
34
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
35
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_TOO_MANY_REQUESTS,
36
+ [HTTP2_HEADER_CONTENT_TYPE]: CONTENT_TYPE_TEXT,
37
+ [HTTP2_HEADER_SERVER]: meta.servername,
38
+ [HTTP2_HEADER_RETRY_AFTER]: limitInfo.retryAfterS,
39
+ [HTTP_HEADER_RATE_LIMIT]: RateLimit.from(limitInfo),
40
+ [HTTP_HEADER_RATE_LIMIT_POLICY]: RateLimitPolicy.from(...policies)
41
+ })
42
+
43
+ stream.write(`Retry After ${limitInfo.retryAfterS} Seconds`)
44
+
45
+ stream.end()
46
+ }
47
+
@@ -0,0 +1,63 @@
1
+ import http2 from 'node:http2'
2
+ import {
3
+ MIME_TYPE_MESSAGE_HTTP
4
+ } from '../content-type.js'
5
+ import {
6
+ HTTP_HEADER_SERVER_TIMING,
7
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
8
+ ServerTiming
9
+ } from '../server-timing.js'
10
+
11
+ /** @import { ServerHttp2Stream } from 'node:http2' */
12
+ /** @import { IncomingHttpHeaders } from 'node:http2' */
13
+ /** @import { Metadata } from './defs.js' */
14
+
15
+ const {
16
+ HTTP2_HEADER_STATUS,
17
+ HTTP2_HEADER_CONTENT_TYPE,
18
+ HTTP2_HEADER_SERVER
19
+ } = http2.constants
20
+
21
+ const {
22
+ HTTP_STATUS_OK
23
+ } = http2.constants
24
+
25
+ /**
26
+ * @param {ServerHttp2Stream} stream
27
+ * @param {string} method
28
+ * @param {URL} url
29
+ * @param {IncomingHttpHeaders} headers
30
+ * @param {Metadata} meta
31
+ */
32
+ export function sendTrace(stream, method, url, headers, meta) {
33
+ const FILTER_KEYS = [ 'authorization', 'cookie' ]
34
+ const HTTP_VERSION = new Map([
35
+ [ 'h2', 'HTTP/2' ],
36
+ [ 'h2c', 'HTTP/2'],
37
+ [ 'http/1.1', 'HTTP/1.1']
38
+ ])
39
+
40
+ const version = HTTP_VERSION.get(stream.session?.alpnProtocol ?? 'h2')
41
+
42
+ stream.respond({
43
+ [HTTP2_HEADER_CONTENT_TYPE]: MIME_TYPE_MESSAGE_HTTP,
44
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_OK,
45
+ [HTTP2_HEADER_SERVER]: meta.servername,
46
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
47
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
48
+ })
49
+
50
+ const reconstructed = [
51
+ `${method} ${url.pathname}${url.search} ${version}`,
52
+ Object.entries(headers)
53
+ .filter(([ key ]) => !key.startsWith(':'))
54
+ .filter(([ key ]) => !FILTER_KEYS.includes(key))
55
+ .map(([ key, value ]) => `${key}: ${value}`)
56
+ .join('\n'),
57
+ '\n'
58
+ ]
59
+ .join('\n')
60
+
61
+ stream.end(reconstructed)
62
+ }
63
+
@@ -0,0 +1,30 @@
1
+ import http2 from 'node:http2'
2
+
3
+ /** @import { ServerHttp2Stream } from 'node:http2' */
4
+ /** @import { Metadata } from './defs.js' */
5
+
6
+ const {
7
+ HTTP2_HEADER_STATUS,
8
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
9
+ HTTP2_HEADER_SERVER
10
+ } = http2.constants
11
+
12
+ const {
13
+ HTTP_STATUS_UNAUTHORIZED
14
+ } = http2.constants
15
+
16
+ /**
17
+ * @param {ServerHttp2Stream} stream
18
+ * @param {Metadata} meta
19
+ */
20
+ export function sendUnauthorized(stream, meta) {
21
+ console.log('Unauthorized')
22
+
23
+ stream.respond({
24
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
25
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_UNAUTHORIZED,
26
+ [HTTP2_HEADER_SERVER]: meta.servername
27
+ // WWW-Authenticate
28
+ })
29
+ stream.end()
30
+ }
@@ -0,0 +1,39 @@
1
+ import http2 from 'node:http2'
2
+ import { CONTENT_TYPE_JSON } from '../content-type.js'
3
+ import {
4
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
5
+ HTTP_HEADER_SERVER_TIMING,
6
+ ServerTiming
7
+ } from '../server-timing.js'
8
+
9
+ /** @import { ServerHttp2Stream } from 'node:http2' */
10
+ /** @import { Metadata } from './defs.js' */
11
+
12
+ const {
13
+ HTTP_STATUS_UNPROCESSABLE_ENTITY
14
+ } = http2.constants
15
+
16
+ const {
17
+ HTTP2_HEADER_STATUS,
18
+ HTTP2_HEADER_SERVER,
19
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
20
+ } = http2.constants
21
+
22
+ /**
23
+ * @param {ServerHttp2Stream} stream
24
+ * @param {Metadata} meta
25
+ */
26
+ export function sendUnprocessable(stream, meta) {
27
+ stream.respond({
28
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
29
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_UNPROCESSABLE_ENTITY,
30
+ // [HTTP2_HEADER_CONTENT_TYPE]: CONTENT_TYPE_JSON,
31
+ [HTTP2_HEADER_SERVER]: meta.servername,
32
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
33
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance)
34
+ })
35
+
36
+ // stream.write(JSON.stringify( ... ))
37
+
38
+ stream.end()
39
+ }
@@ -0,0 +1,40 @@
1
+ import http2 from 'node:http2'
2
+ import { HTTP_HEADER_ACCEPT_POST } from './defs.js'
3
+ import {
4
+ HTTP_HEADER_TIMING_ALLOW_ORIGIN,
5
+ HTTP_HEADER_SERVER_TIMING,
6
+ ServerTiming
7
+ } from '../server-timing.js'
8
+
9
+ /** @import { ServerHttp2Stream } from 'node:http2' */
10
+ /** @import { Metadata } from './defs.js' */
11
+
12
+ const {
13
+ HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE
14
+ } = http2.constants
15
+
16
+ const {
17
+ HTTP2_HEADER_STATUS,
18
+ HTTP2_HEADER_SERVER,
19
+ HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN
20
+ } = http2.constants
21
+
22
+ /**
23
+ * @param {ServerHttp2Stream} stream
24
+ * @param {Array<string>|string} acceptableMediaType
25
+ * @param {Metadata} meta
26
+ */
27
+ export function sendUnsupportedMediaType(stream, acceptableMediaType, meta) {
28
+ const acceptable = Array.isArray(acceptableMediaType) ? acceptableMediaType : [ acceptableMediaType ]
29
+
30
+ stream.respond({
31
+ [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
32
+ [HTTP2_HEADER_STATUS]: HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE,
33
+ [HTTP_HEADER_ACCEPT_POST]: acceptable.join(','),
34
+ [HTTP2_HEADER_SERVER]: meta.servername,
35
+ [HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
36
+ [HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
37
+ })
38
+
39
+ stream.end()
40
+ }