@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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@johntalton/http-core",
3
3
  "type": "module",
4
- "version": "1.0.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.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
- // ServerSentEvents.messageToEventStreamLines(data)
38
- ServerSentEvents.lineGen(data)
39
- .forEach(line => stream.write(line))
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
- else if(rstCode === http2.constants.NGHTTP2_PROTOCOL_ERROR) { return '(Protocol Error)' }
351
- else if(rstCode === http2.constants.NGHTTP2_INTERNAL_ERROR) { return '(Internal Error)' }
352
- else if(rstCode === http2.constants.NGHTTP2_FLOW_CONTROL_ERROR) { return '(Flow Control Error)' }
353
- else if(rstCode === http2.constants.NGHTTP2_SETTINGS_TIMEOUT) { return '(Settings Timeout)' }
354
- else if(rstCode === http2.constants.NGHTTP2_STREAM_CLOSED) { return '(Closed)' }
355
- else if(rstCode === http2.constants.NGHTTP2_FRAME_SIZE_ERROR) { return '(Frame Size Error)' }
356
- else if(rstCode === http2.constants.NGHTTP2_REFUSED_STREAM) { return '(Refused)' }
357
- else if(rstCode === http2.constants.NGHTTP2_CANCEL) { return '(Cancel)' }
358
- else if(rstCode === http2.constants.NGHTTP2_COMPRESSION_ERROR) { return '(Compression Error)' }
359
- else if(rstCode === http2.constants.NGHTTP2_CONNECT_ERROR) { return '(Connect Error)' }
360
- else if(rstCode === http2.constants.NGHTTP2_ENHANCE_YOUR_CALM) { return '(Chill)' }
361
- else if(rstCode === http2.constants.NGHTTP2_INADEQUATE_SECURITY) { return '(Inadequate Security)' }
362
- else if(rstCode === http2.constants.NGHTTP2_HTTP_1_1_REQUIRED) { return '(HTTP 1.1 Requested)' }
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-ignore
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
- Forwarded,
10
+ Conditional,
11
+ ETag,
12
+
18
13
  FORWARDED_KEY_FOR,
14
+ Forwarded,
19
15
  KNOWN_FORWARDED_KEYS,
20
- Conditional,
21
- ETag
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 { ENCODER_MAP, HTTP_HEADER_FORWARDED, HTTP_HEADER_ORIGIN } from '@johntalton/http-util/response'
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 BODY_TIMEOUT_SEC = 2 * 1000
77
- const BODY_BYTE_LENGTH = 1000 * 1000
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
- ((ALLOWED_ORIGINS.includes(origin) || ALLOWED_ORIGINS.includes('*')) ?
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(BODY_TIMEOUT_SEC)
275
+ AbortSignal.timeout(BODY_TIMEOUT_MSEC)
270
276
  ])
271
277
  })
272
278