@johntalton/http-util 1.0.0 → 2.0.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 +4 -1
- package/src/content-type.js +1 -0
- package/src/handle-stream-util.js +76 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johntalton/http-util",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"src/*.js"
|
|
14
14
|
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"url": "https://github.com/johntalton/http-util"
|
|
17
|
+
},
|
|
15
18
|
"dependencies": {
|
|
16
19
|
"@johntalton/sse-util": "^1.0.0"
|
|
17
20
|
}
|
package/src/content-type.js
CHANGED
|
@@ -6,6 +6,7 @@ export const MIME_TYPE_XML = 'application/xml'
|
|
|
6
6
|
export const MIME_TYPE_URL_FORM_DATA = 'application/x-www-form-urlencoded'
|
|
7
7
|
export const MIME_TYPE_MULTIPART_FORM_DATA = 'multipart/form-data'
|
|
8
8
|
export const MIME_TYPE_OCTET_STREAM = 'application/octet-stream'
|
|
9
|
+
export const MIME_TYPE_MESSAGE_HTTP = 'message/http'
|
|
9
10
|
|
|
10
11
|
export const KNOWN_CONTENT_TYPES = [
|
|
11
12
|
'application', 'audio', 'image', 'message',
|
|
@@ -8,7 +8,14 @@ import {
|
|
|
8
8
|
ENDING,
|
|
9
9
|
} from '@johntalton/sse-util'
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
/** @import { IncomingHttpHeaders } from 'node:http2' */
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
CHARSET_UTF8,
|
|
15
|
+
CONTENT_TYPE_JSON,
|
|
16
|
+
CONTENT_TYPE_TEXT,
|
|
17
|
+
MIME_TYPE_MESSAGE_HTTP
|
|
18
|
+
} from './content-type.js'
|
|
12
19
|
import { ServerTiming, HTTP_HEADER_SERVER_TIMING, HTTP_HEADER_TIMING_ALLOW_ORIGIN } from './server-timing.js'
|
|
13
20
|
import { HTTP_HEADER_RATE_LIMIT, HTTP_HEADER_RATE_LIMIT_POLICY, RateLimit, RateLimitPolicy } from './rate-limit.js'
|
|
14
21
|
|
|
@@ -23,7 +30,9 @@ const {
|
|
|
23
30
|
HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
|
24
31
|
HTTP2_HEADER_SERVER,
|
|
25
32
|
HTTP2_HEADER_RETRY_AFTER,
|
|
26
|
-
HTTP2_HEADER_CACHE_CONTROL
|
|
33
|
+
HTTP2_HEADER_CACHE_CONTROL,
|
|
34
|
+
HTTP2_HEADER_ETAG,
|
|
35
|
+
HTTP2_HEADER_ALLOW
|
|
27
36
|
} = http2.constants
|
|
28
37
|
|
|
29
38
|
const {
|
|
@@ -32,7 +41,8 @@ const {
|
|
|
32
41
|
HTTP_STATUS_UNAUTHORIZED,
|
|
33
42
|
HTTP_STATUS_NO_CONTENT,
|
|
34
43
|
HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
|
35
|
-
HTTP_STATUS_TOO_MANY_REQUESTS
|
|
44
|
+
HTTP_STATUS_TOO_MANY_REQUESTS,
|
|
45
|
+
HTTP_STATUS_METHOD_NOT_ALLOWED
|
|
36
46
|
} = http2.constants
|
|
37
47
|
|
|
38
48
|
export const HTTP_HEADER_ORIGIN = 'origin'
|
|
@@ -63,6 +73,7 @@ export const PREFLIGHT_AGE_SECONDS = '500'
|
|
|
63
73
|
* @property {Array<TimingsInfo>} performance
|
|
64
74
|
* @property {string|undefined} servername
|
|
65
75
|
* @property {string|undefined} origin
|
|
76
|
+
* @property {string|undefined} etag
|
|
66
77
|
*/
|
|
67
78
|
|
|
68
79
|
/**
|
|
@@ -102,14 +113,13 @@ export function sendError(stream, message, meta) {
|
|
|
102
113
|
|
|
103
114
|
/**
|
|
104
115
|
* @param {ServerHttp2Stream} stream
|
|
105
|
-
* @param {string|undefined} allowedOrigin
|
|
106
116
|
* @param {Array<string>} methods
|
|
107
117
|
* @param {Metadata} meta
|
|
108
118
|
*/
|
|
109
|
-
export function sendPreflight(stream,
|
|
119
|
+
export function sendPreflight(stream, methods, meta) {
|
|
110
120
|
stream.respond({
|
|
111
121
|
[HTTP2_HEADER_STATUS]: HTTP_STATUS_OK,
|
|
112
|
-
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]:
|
|
122
|
+
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
|
|
113
123
|
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_METHODS]: methods.join(','),
|
|
114
124
|
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_HEADERS]: ['Authorization', HTTP2_HEADER_CONTENT_TYPE].join(','),
|
|
115
125
|
[HTTP2_HEADER_ACCESS_CONTROL_MAX_AGE]: PREFLIGHT_AGE_SECONDS,
|
|
@@ -118,6 +128,20 @@ export function sendPreflight(stream, allowedOrigin, methods, meta) {
|
|
|
118
128
|
stream.end()
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
/**
|
|
132
|
+
* @param {ServerHttp2Stream} stream
|
|
133
|
+
* @param {Array<string>} methods
|
|
134
|
+
* @param {Metadata} meta
|
|
135
|
+
*/
|
|
136
|
+
export function sendNowAllowed(stream, methods, meta) {
|
|
137
|
+
stream.respond({
|
|
138
|
+
[HTTP2_HEADER_STATUS]: HTTP_STATUS_METHOD_NOT_ALLOWED,
|
|
139
|
+
[HTTP2_HEADER_ALLOW]: methods.join(','),
|
|
140
|
+
[HTTP2_HEADER_SERVER]: meta.servername
|
|
141
|
+
})
|
|
142
|
+
stream.end()
|
|
143
|
+
}
|
|
144
|
+
|
|
121
145
|
/**
|
|
122
146
|
* @param {ServerHttp2Stream} stream
|
|
123
147
|
* @param {Metadata} meta
|
|
@@ -188,10 +212,9 @@ export const ENCODER_MAP = new Map([
|
|
|
188
212
|
* @param {ServerHttp2Stream} stream
|
|
189
213
|
* @param {Object} obj
|
|
190
214
|
* @param {string|undefined} encoding
|
|
191
|
-
* @param {string|undefined} allowedOrigin
|
|
192
215
|
* @param {Metadata} meta
|
|
193
216
|
*/
|
|
194
|
-
export function sendJSON_Encoded(stream, obj, encoding,
|
|
217
|
+
export function sendJSON_Encoded(stream, obj, encoding, meta) {
|
|
195
218
|
if(stream.closed) { return }
|
|
196
219
|
|
|
197
220
|
const json = JSON.stringify(obj)
|
|
@@ -210,15 +233,16 @@ export function sendJSON_Encoded(stream, obj, encoding, allowedOrigin, meta) {
|
|
|
210
233
|
)
|
|
211
234
|
|
|
212
235
|
stream.respond({
|
|
213
|
-
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]:
|
|
236
|
+
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
|
|
214
237
|
[HTTP2_HEADER_CONTENT_TYPE]: CONTENT_TYPE_JSON,
|
|
215
238
|
[HTTP2_HEADER_CONTENT_ENCODING]: actualEncoding,
|
|
216
239
|
[HTTP2_HEADER_VARY]: 'Accept, Accept-Encoding',
|
|
217
240
|
[HTTP2_HEADER_CACHE_CONTROL]: 'private',
|
|
218
241
|
[HTTP2_HEADER_STATUS]: HTTP_STATUS_OK,
|
|
219
242
|
[HTTP2_HEADER_SERVER]: meta.servername,
|
|
220
|
-
[HTTP_HEADER_TIMING_ALLOW_ORIGIN]:
|
|
221
|
-
[HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance)
|
|
243
|
+
[HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
|
|
244
|
+
[HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
|
|
245
|
+
[HTTP2_HEADER_ETAG]: meta.etag
|
|
222
246
|
})
|
|
223
247
|
|
|
224
248
|
// stream.write(encodedData)
|
|
@@ -227,10 +251,48 @@ export function sendJSON_Encoded(stream, obj, encoding, allowedOrigin, meta) {
|
|
|
227
251
|
|
|
228
252
|
/**
|
|
229
253
|
* @param {ServerHttp2Stream} stream
|
|
230
|
-
* @param {string
|
|
254
|
+
* @param {string} method
|
|
255
|
+
* @param {URL} url
|
|
256
|
+
* @param {IncomingHttpHeaders} headers
|
|
257
|
+
* @param {Metadata} meta
|
|
258
|
+
*/
|
|
259
|
+
export function sendTrace(stream, method, url, headers, meta) {
|
|
260
|
+
const FILTER_KEYS = [ 'authorization', 'cookie' ]
|
|
261
|
+
const HTTP_VERSION = new Map([
|
|
262
|
+
[ 'h2', 'HTTP/2' ],
|
|
263
|
+
[ 'h2c', 'HTTP/2'],
|
|
264
|
+
[ 'http/1.1', 'HTTP/1.1']
|
|
265
|
+
])
|
|
266
|
+
|
|
267
|
+
const version = HTTP_VERSION.get(stream.session?.alpnProtocol ?? 'h2')
|
|
268
|
+
|
|
269
|
+
stream.respond({
|
|
270
|
+
[HTTP2_HEADER_CONTENT_TYPE]: MIME_TYPE_MESSAGE_HTTP,
|
|
271
|
+
[HTTP2_HEADER_STATUS]: HTTP_STATUS_OK,
|
|
272
|
+
[HTTP2_HEADER_SERVER]: meta.servername,
|
|
273
|
+
[HTTP_HEADER_TIMING_ALLOW_ORIGIN]: meta.origin,
|
|
274
|
+
[HTTP_HEADER_SERVER_TIMING]: ServerTiming.encode(meta.performance),
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
const reconstructed = [
|
|
278
|
+
`${method} ${url.pathname}${url.search} ${version}`,
|
|
279
|
+
Object.entries(headers)
|
|
280
|
+
.filter(([ key ]) => !key.startsWith(':'))
|
|
281
|
+
.filter(([ key ]) => !FILTER_KEYS.includes(key))
|
|
282
|
+
.map(([ key, value ]) => `${key}: ${value}`)
|
|
283
|
+
.join('\n'),
|
|
284
|
+
'\n'
|
|
285
|
+
]
|
|
286
|
+
.join('\n')
|
|
287
|
+
|
|
288
|
+
stream.end(reconstructed)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @param {ServerHttp2Stream} stream
|
|
231
293
|
* @param {SSEOptions & Metadata} meta
|
|
232
294
|
*/
|
|
233
|
-
export function sendSSE(stream,
|
|
295
|
+
export function sendSSE(stream, meta) {
|
|
234
296
|
// stream.setTimeout(0)
|
|
235
297
|
// stream.session?.setTimeout(0)
|
|
236
298
|
// stream.session?.socket.setTimeout(0)
|
|
@@ -244,7 +306,7 @@ export function sendSSE(stream, allowedOrigin, meta) {
|
|
|
244
306
|
const sendBOM = meta.bom ?? true
|
|
245
307
|
|
|
246
308
|
stream.respond({
|
|
247
|
-
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]:
|
|
309
|
+
[HTTP2_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN]: meta.origin,
|
|
248
310
|
[HTTP2_HEADER_CONTENT_TYPE]: SSE_MIME,
|
|
249
311
|
[HTTP2_HEADER_STATUS]: activeStream ? HTTP_STATUS_OK : HTTP_STATUS_NO_CONTENT, // SSE_INACTIVE_STATUS_CODE
|
|
250
312
|
// [HTTP2_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS]: 'true'
|