@johntalton/http-core 1.0.0 → 1.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/package.json +2 -2
- package/src/epilogue.js +5 -4
- package/src/index.js +24 -24
- package/src/preamble.js +27 -21
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johntalton/http-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.js"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"scripts": {
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@johntalton/http-util": "^5.0.
|
|
18
|
+
"@johntalton/http-util": "^5.0.1",
|
|
19
19
|
"@johntalton/sse-util": "^1.0.0"
|
|
20
20
|
}
|
|
21
21
|
}
|
package/src/epilogue.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Response } from '@johntalton/http-util/response/object'
|
|
2
1
|
import { MIME_TYPE_JSON } from '@johntalton/http-util/headers'
|
|
2
|
+
import { Response } from '@johntalton/http-util/response/object'
|
|
3
3
|
import { ServerSentEvents } from '@johntalton/sse-util'
|
|
4
4
|
|
|
5
5
|
/** @import { ServerHttp2Stream } from 'node:http2' */
|
|
@@ -34,9 +34,10 @@ function addSSEPortHandler(stream, port, streamId, shutdownSignal) {
|
|
|
34
34
|
port.onmessage = message => {
|
|
35
35
|
const { data } = message
|
|
36
36
|
console.log('sending sse data', streamId, data)
|
|
37
|
-
|
|
38
|
-
ServerSentEvents.lineGen(data)
|
|
39
|
-
|
|
37
|
+
|
|
38
|
+
for(const line of ServerSentEvents.lineGen(data)) {
|
|
39
|
+
stream.write(line)
|
|
40
|
+
}
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import http2 from 'node:http2'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
1
|
import crypto from 'node:crypto'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import http2 from 'node:http2'
|
|
4
4
|
|
|
5
5
|
import { HTTP_METHOD_QUERY } from '@johntalton/http-util/response'
|
|
6
6
|
|
|
7
|
-
import { preamble } from './preamble.js'
|
|
8
7
|
import { epilogue } from './epilogue.js'
|
|
8
|
+
import { preamble } from './preamble.js'
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
11
|
HTTP2_METHOD_GET,
|
|
@@ -18,6 +18,12 @@ const {
|
|
|
18
18
|
HTTP2_METHOD_TRACE
|
|
19
19
|
} = http2.constants
|
|
20
20
|
|
|
21
|
+
const {
|
|
22
|
+
SSL_OP_NO_TLSv1,
|
|
23
|
+
SSL_OP_NO_TLSv1_1,
|
|
24
|
+
SSL_OP_NO_TLSv1_2,
|
|
25
|
+
} = crypto.constants
|
|
26
|
+
|
|
21
27
|
export const KNOWN_METHODS = [
|
|
22
28
|
HTTP2_METHOD_GET,
|
|
23
29
|
HTTP2_METHOD_HEAD,
|
|
@@ -311,7 +317,7 @@ export const KNOWN_METHODS = [
|
|
|
311
317
|
* @param {Http2Stream} stream
|
|
312
318
|
* @returns {stream is ServerHttp2Stream}
|
|
313
319
|
*/
|
|
314
|
-
function isServerStream(stream) {
|
|
320
|
+
export function isServerStream(stream) {
|
|
315
321
|
if(stream === null) { return false }
|
|
316
322
|
return true
|
|
317
323
|
}
|
|
@@ -347,19 +353,19 @@ export function isValidMethod(method) {
|
|
|
347
353
|
*/
|
|
348
354
|
export function closeCodeToString(rstCode) {
|
|
349
355
|
if(rstCode === http2.constants.NGHTTP2_NO_ERROR) { return '(No Error)' }
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
356
|
+
if(rstCode === http2.constants.NGHTTP2_PROTOCOL_ERROR) { return '(Protocol Error)' }
|
|
357
|
+
if(rstCode === http2.constants.NGHTTP2_INTERNAL_ERROR) { return '(Internal Error)' }
|
|
358
|
+
if(rstCode === http2.constants.NGHTTP2_FLOW_CONTROL_ERROR) { return '(Flow Control Error)' }
|
|
359
|
+
if(rstCode === http2.constants.NGHTTP2_SETTINGS_TIMEOUT) { return '(Settings Timeout)' }
|
|
360
|
+
if(rstCode === http2.constants.NGHTTP2_STREAM_CLOSED) { return '(Closed)' }
|
|
361
|
+
if(rstCode === http2.constants.NGHTTP2_FRAME_SIZE_ERROR) { return '(Frame Size Error)' }
|
|
362
|
+
if(rstCode === http2.constants.NGHTTP2_REFUSED_STREAM) { return '(Refused)' }
|
|
363
|
+
if(rstCode === http2.constants.NGHTTP2_CANCEL) { return '(Cancel)' }
|
|
364
|
+
if(rstCode === http2.constants.NGHTTP2_COMPRESSION_ERROR) { return '(Compression Error)' }
|
|
365
|
+
if(rstCode === http2.constants.NGHTTP2_CONNECT_ERROR) { return '(Connect Error)' }
|
|
366
|
+
if(rstCode === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM) { return '(Chill)' }
|
|
367
|
+
if(rstCode === http2.constants.NGHTTP2_INADEQUATE_SECURITY) { return '(Inadequate Security)' }
|
|
368
|
+
if(rstCode === http2.constants.NGHTTP2_HTTP_1_1_REQUIRED) { return '(HTTP 1.1 Requested)' }
|
|
363
369
|
|
|
364
370
|
return `(${rstCode})`
|
|
365
371
|
}
|
|
@@ -372,16 +378,10 @@ export const REQUEST_ID_SIZE = 5
|
|
|
372
378
|
export function requestId() {
|
|
373
379
|
const buffer = new Uint8Array(REQUEST_ID_SIZE)
|
|
374
380
|
crypto.getRandomValues(buffer)
|
|
375
|
-
// @ts-
|
|
381
|
+
// @ts-expect-error
|
|
376
382
|
return buffer.toHex()
|
|
377
383
|
}
|
|
378
384
|
|
|
379
|
-
const {
|
|
380
|
-
SSL_OP_NO_TLSv1,
|
|
381
|
-
SSL_OP_NO_TLSv1_1,
|
|
382
|
-
SSL_OP_NO_TLSv1_2,
|
|
383
|
-
} = crypto.constants
|
|
384
|
-
|
|
385
385
|
/**
|
|
386
386
|
* @typedef {Object} H2CoreOptions
|
|
387
387
|
* @property {Config} config
|
package/src/preamble.js
CHANGED
|
@@ -3,24 +3,30 @@ import { TLSSocket } from 'node:tls'
|
|
|
3
3
|
|
|
4
4
|
import { requestBody } from '@johntalton/http-util/body'
|
|
5
5
|
import {
|
|
6
|
-
MIME_TYPE_JSON,
|
|
7
|
-
MIME_TYPE_TEXT,
|
|
8
|
-
MIME_TYPE_XML,
|
|
9
|
-
MIME_TYPE_EVENT_STREAM,
|
|
10
|
-
MIME_TYPE_MESSAGE_HTTP,
|
|
11
|
-
parseContentType,
|
|
12
|
-
|
|
13
6
|
Accept,
|
|
14
7
|
AcceptEncoding,
|
|
15
8
|
AcceptLanguage,
|
|
16
9
|
|
|
17
|
-
|
|
10
|
+
Conditional,
|
|
11
|
+
ETag,
|
|
12
|
+
|
|
18
13
|
FORWARDED_KEY_FOR,
|
|
14
|
+
Forwarded,
|
|
19
15
|
KNOWN_FORWARDED_KEYS,
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
|
|
17
|
+
MIME_TYPE_EVENT_STREAM,
|
|
18
|
+
MIME_TYPE_JSON,
|
|
19
|
+
MIME_TYPE_MESSAGE_HTTP,
|
|
20
|
+
MIME_TYPE_TEXT,
|
|
21
|
+
MIME_TYPE_XML,
|
|
22
|
+
parseContentType
|
|
22
23
|
} from '@johntalton/http-util/headers'
|
|
23
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
ENCODER_MAP,
|
|
26
|
+
HTTP_HEADER_FORWARDED,
|
|
27
|
+
HTTP_HEADER_ORIGIN
|
|
28
|
+
} from '@johntalton/http-util/response'
|
|
29
|
+
|
|
24
30
|
import { isValidHeader, isValidLikeHeader, isValidMethod } from './index.js'
|
|
25
31
|
|
|
26
32
|
/** @import { ServerHttp2Stream, IncomingHttpHeaders } from 'node:http2' */
|
|
@@ -29,8 +35,8 @@ import { isValidHeader, isValidLikeHeader, isValidMethod } from './index.js'
|
|
|
29
35
|
const { HTTP2_METHOD_OPTIONS, HTTP2_METHOD_TRACE } = http2.constants
|
|
30
36
|
|
|
31
37
|
const {
|
|
32
|
-
HTTP2_HEADER_METHOD,
|
|
33
38
|
HTTP2_HEADER_AUTHORITY,
|
|
39
|
+
HTTP2_HEADER_METHOD,
|
|
34
40
|
HTTP2_HEADER_SCHEME,
|
|
35
41
|
HTTP2_HEADER_PATH,
|
|
36
42
|
HTTP2_HEADER_AUTHORIZATION,
|
|
@@ -73,8 +79,10 @@ const ALLOWED_ORIGINS = (process.env['ALLOWED_ORIGINS'] ?? '').split(',').map(s
|
|
|
73
79
|
|
|
74
80
|
const ALLOW_TRACE = process.env['ALLOW_TRACE'] === 'true'
|
|
75
81
|
|
|
76
|
-
const
|
|
77
|
-
const
|
|
82
|
+
const MSEC_PER_SEC = 1000
|
|
83
|
+
const BODY_TIMEOUT_MSEC = 2 * MSEC_PER_SEC
|
|
84
|
+
const BYTE_PER_K = 1024
|
|
85
|
+
const BODY_BYTE_LENGTH = BYTE_PER_K * BYTE_PER_K
|
|
78
86
|
|
|
79
87
|
// const ipRateLimitStore = new Map()
|
|
80
88
|
// const ipRateLimitPolicy = {
|
|
@@ -139,10 +147,8 @@ export function preamble(config, streamId, stream, headers, servername, shutdown
|
|
|
139
147
|
// const secFetchDest = header[HTTP_HEADER_SEC_FETCH_DEST]
|
|
140
148
|
|
|
141
149
|
//
|
|
142
|
-
const allowedOrigin = (origin !== undefined) ?
|
|
143
|
-
|
|
144
|
-
(URL.canParse(origin) ?
|
|
145
|
-
origin : undefined) : undefined) : undefined
|
|
150
|
+
const allowedOrigin = (ALLOWED_ORIGINS.includes('*') || ((origin !== undefined) && URL.canParse(origin) && ALLOWED_ORIGINS.includes(origin))) ? origin : undefined
|
|
151
|
+
|
|
146
152
|
|
|
147
153
|
/** @type {RouteRequest|RouteAction} */
|
|
148
154
|
const state = {
|
|
@@ -249,7 +255,7 @@ export function preamble(config, streamId, stream, headers, servername, shutdown
|
|
|
249
255
|
//
|
|
250
256
|
if(method === HTTP2_METHOD_TRACE) {
|
|
251
257
|
if(!ALLOW_TRACE) { return { ...state, type: 'not-allowed', method, methods: [], url: requestUrl }}
|
|
252
|
-
const maxForwardsValue = maxForwards !== undefined ? parseInt(maxForwards) : 0
|
|
258
|
+
const maxForwardsValue = maxForwards !== undefined ? Number.parseInt(maxForwards) : 0
|
|
253
259
|
const preambleEnd = performance.now()
|
|
254
260
|
state.meta.performance.push({ name: 'preamble-trace', duration: preambleEnd - preambleStart })
|
|
255
261
|
if(acceptObject.type !== MIME_TYPE_MESSAGE_HTTP) { return { ...state, type: 'not-acceptable', acceptableMediaTypes: [ MIME_TYPE_MESSAGE_HTTP ] } }
|
|
@@ -259,14 +265,14 @@ export function preamble(config, streamId, stream, headers, servername, shutdown
|
|
|
259
265
|
//
|
|
260
266
|
// setup future body
|
|
261
267
|
//
|
|
262
|
-
const contentLength = fullContentLength === undefined ? undefined : parseInt(fullContentLength, 10)
|
|
268
|
+
const contentLength = fullContentLength === undefined ? undefined : Number.parseInt(fullContentLength, 10)
|
|
263
269
|
const body = requestBody(stream, {
|
|
264
270
|
byteLimit: BODY_BYTE_LENGTH,
|
|
265
271
|
contentLength,
|
|
266
272
|
contentType,
|
|
267
273
|
signal: AbortSignal.any([
|
|
268
274
|
shutdownSignal,
|
|
269
|
-
AbortSignal.timeout(
|
|
275
|
+
AbortSignal.timeout(BODY_TIMEOUT_MSEC)
|
|
270
276
|
])
|
|
271
277
|
})
|
|
272
278
|
|