@johntalton/http-core 1.1.0 → 1.1.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 +18 -13
- package/src/index.js +37 -10
- package/src/preamble.js +23 -9
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@johntalton/http-core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.1.
|
|
4
|
+
"version": "1.1.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/index.js"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"lint": "biome check"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@johntalton/http-util": "^7.0.
|
|
19
|
+
"@johntalton/http-util": "^7.0.3",
|
|
20
20
|
"@johntalton/sse-util": "^1.0.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
package/src/epilogue.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MIME_TYPE_JSON } from '@johntalton/http-util/headers'
|
|
2
1
|
import { Response } from '@johntalton/http-util/response/object'
|
|
3
2
|
import { ServerSentEvents } from '@johntalton/sse-util'
|
|
4
3
|
|
|
@@ -18,11 +17,11 @@ function addSSEPortHandler(stream, port, streamId, shutdownSignal) {
|
|
|
18
17
|
stream.end()
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
stream.once('close', (
|
|
20
|
+
stream.once('close', () => {
|
|
22
21
|
console.log('stream close in sse handler', streamId)
|
|
23
22
|
shutdownSignal.removeEventListener('abort', signalHandler)
|
|
24
23
|
port.close()
|
|
25
|
-
})
|
|
24
|
+
})
|
|
26
25
|
|
|
27
26
|
shutdownSignal.addEventListener('abort', signalHandler, { once: true })
|
|
28
27
|
|
|
@@ -97,15 +96,22 @@ export function epilogue(state) {
|
|
|
97
96
|
if(active) { addSSEPortHandler(stream, port, state.streamId, state.shutdownSignal) }
|
|
98
97
|
} break
|
|
99
98
|
case 'json': {
|
|
100
|
-
const { obj,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
99
|
+
const { obj, encoding, etag, lastModified, age, supportedQueryTypes } = state
|
|
100
|
+
Response.json(stream, obj, { encoding, etag, lastModified, age, cacheControl: state.cacheControl ?? {} }, { supportedQueryTypes }, meta)
|
|
101
|
+
} break
|
|
102
|
+
case 'encoded': {
|
|
103
|
+
const { contentType, encoding, etag, lastModified, age, supportedQueryTypes } = state
|
|
104
|
+
|
|
105
|
+
Response.encoded(stream, state.obj, {
|
|
106
|
+
contentType,
|
|
107
|
+
encoding,
|
|
108
|
+
etag,
|
|
109
|
+
lastModified,
|
|
110
|
+
age,
|
|
111
|
+
cacheControl: state.cacheControl ?? {}
|
|
112
|
+
}, {
|
|
113
|
+
supportedQueryTypes
|
|
114
|
+
}, meta)
|
|
109
115
|
} break
|
|
110
116
|
case 'partial-bytes': {
|
|
111
117
|
const { contentType, contentLength, etag, lastModified, age } = state
|
|
@@ -125,7 +131,6 @@ export function epilogue(state) {
|
|
|
125
131
|
Response.bytes(stream, state.obj, {
|
|
126
132
|
contentType,
|
|
127
133
|
contentLength,
|
|
128
|
-
encoding: 'identity',
|
|
129
134
|
etag,
|
|
130
135
|
lastModified,
|
|
131
136
|
age,
|
package/src/index.js
CHANGED
|
@@ -43,6 +43,7 @@ export const KNOWN_METHODS = [
|
|
|
43
43
|
/** @import { Metadata } from '@johntalton/http-util/response' */
|
|
44
44
|
/** @import { BodyFuture } from '@johntalton/http-util/body' */
|
|
45
45
|
/** @import {
|
|
46
|
+
AcceptItem,
|
|
46
47
|
EtagItem,
|
|
47
48
|
IMFFixDate,
|
|
48
49
|
IMFFixDateInput,
|
|
@@ -52,7 +53,8 @@ export const KNOWN_METHODS = [
|
|
|
52
53
|
ChallengeItem,
|
|
53
54
|
CacheControlOptions
|
|
54
55
|
} from '@johntalton/http-util/headers' */
|
|
55
|
-
/** @import {
|
|
56
|
+
/** @import { AcceptStyleItem } from '@johntalton/http-util/util' */
|
|
57
|
+
/** @import { SendBody, NonEmptyArray } from '@johntalton/http-util/response' */
|
|
56
58
|
/** @import { SecFetchSite, SecFetchMode, SecFetchDest } from '@johntalton/http-util/headers' */
|
|
57
59
|
|
|
58
60
|
|
|
@@ -97,6 +99,7 @@ export const KNOWN_METHODS = [
|
|
|
97
99
|
'sse' |
|
|
98
100
|
'bytes' |
|
|
99
101
|
'partial-bytes' |
|
|
102
|
+
'encoded' |
|
|
100
103
|
'json' |
|
|
101
104
|
'error'
|
|
102
105
|
} RouteType */
|
|
@@ -119,8 +122,24 @@ export const KNOWN_METHODS = [
|
|
|
119
122
|
* @property {AbortSignal} shutdownSignal
|
|
120
123
|
*/
|
|
121
124
|
|
|
125
|
+
/**
|
|
126
|
+
* @typedef {Object} RouteRequestAcceptParsed
|
|
127
|
+
* @property {Array<AcceptItem> | undefined} type
|
|
128
|
+
* @property {Array<AcceptStyleItem> | undefined} encoding
|
|
129
|
+
* @property {Array<AcceptStyleItem>} language
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @typedef {Object} RouteRequestAcceptFn
|
|
134
|
+
* @property {(a: Array<string>|undefined) => AcceptItem|undefined} type
|
|
135
|
+
* @property {(a: Array<string>|undefined) => AcceptStyleItem|undefined} encoding
|
|
136
|
+
* @property {(a: Array<string>|undefined) => AcceptStyleItem|undefined} language
|
|
137
|
+
*/
|
|
138
|
+
|
|
122
139
|
/**
|
|
123
140
|
* @typedef {Object} RouteRequestAccept
|
|
141
|
+
* @property {RouteRequestAcceptParsed} parsed
|
|
142
|
+
* @property {RouteRequestAcceptFn} select
|
|
124
143
|
* @property {string|undefined} type
|
|
125
144
|
* @property {string|undefined} encoding
|
|
126
145
|
* @property {string|undefined} language
|
|
@@ -164,11 +183,6 @@ export const KNOWN_METHODS = [
|
|
|
164
183
|
*/
|
|
165
184
|
/** @typedef {RouteBase & RouteRequestBase} RouteRequest */
|
|
166
185
|
|
|
167
|
-
/**
|
|
168
|
-
* @template T
|
|
169
|
-
* @typedef {[ T, ...T[] ]} NonEmptyArray
|
|
170
|
-
*/
|
|
171
|
-
|
|
172
186
|
/**
|
|
173
187
|
* @typedef {Object} PartialBytes
|
|
174
188
|
* @property {SendBody} obj
|
|
@@ -405,8 +419,8 @@ export const KNOWN_METHODS = [
|
|
|
405
419
|
* @typedef {Object} RouteBytesBase
|
|
406
420
|
* @property {'bytes'} type
|
|
407
421
|
* @property {string} contentType
|
|
422
|
+
* @property {SendBody} obj
|
|
408
423
|
* @property {number|undefined} [contentLength]
|
|
409
|
-
* @property {SendBody|undefined} obj
|
|
410
424
|
* @property {IMFFixDateInput|string|undefined} [lastModified]
|
|
411
425
|
* @property {EtagItem|undefined} [etag]
|
|
412
426
|
* @property {number|undefined} [age]
|
|
@@ -415,8 +429,6 @@ export const KNOWN_METHODS = [
|
|
|
415
429
|
*/
|
|
416
430
|
/** @typedef {RouteBase & RouteBytesBase} RouteBytes */
|
|
417
431
|
|
|
418
|
-
|
|
419
|
-
|
|
420
432
|
/**
|
|
421
433
|
* @typedef {Object} RoutePartialBytesBase
|
|
422
434
|
* @property {'partial-bytes'} type
|
|
@@ -430,11 +442,25 @@ export const KNOWN_METHODS = [
|
|
|
430
442
|
*/
|
|
431
443
|
/** @typedef {RouteBase & RoutePartialBytesBase} RoutePartialBytes */
|
|
432
444
|
|
|
445
|
+
/**
|
|
446
|
+
* @typedef {Object} RouteEncodedBase
|
|
447
|
+
* @property {'encoded'} type
|
|
448
|
+
* @property {SendBody} obj
|
|
449
|
+
* @property {string} contentType
|
|
450
|
+
* @property {string|undefined} [encoding]
|
|
451
|
+
* @property {EtagItem|undefined} [etag]
|
|
452
|
+
* @property {IMFFixDateInput|string|undefined} [lastModified]
|
|
453
|
+
* @property {number|undefined} [age]
|
|
454
|
+
* @property {CacheControlOptions|undefined} [cacheControl]
|
|
455
|
+
* @property {Array<string>|undefined} [supportedQueryTypes]
|
|
456
|
+
*/
|
|
457
|
+
/** @typedef {RouteBase & RouteEncodedBase} RouteEncoded */
|
|
458
|
+
|
|
433
459
|
/**
|
|
434
460
|
* @typedef {Object} RouteJSONBase
|
|
435
461
|
* @property {'json'} type
|
|
436
|
-
* @property {RouteRequestAccept} accept
|
|
437
462
|
* @property {Record<any, any>} obj
|
|
463
|
+
* @property {string | undefined} [encoding]
|
|
438
464
|
* @property {IMFFixDateInput|string|undefined} [lastModified]
|
|
439
465
|
* @property {EtagItem|undefined} [etag]
|
|
440
466
|
* @property {number|undefined} [age]
|
|
@@ -486,6 +512,7 @@ export const KNOWN_METHODS = [
|
|
|
486
512
|
RouteSSE |
|
|
487
513
|
RouteBytes |
|
|
488
514
|
RoutePartialBytes |
|
|
515
|
+
RouteEncoded |
|
|
489
516
|
RouteJSON |
|
|
490
517
|
RouteError
|
|
491
518
|
} RouteAction */
|
package/src/preamble.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
import { isValidHeader, isValidLikeHeader, isValidMethod } from './index.js'
|
|
36
36
|
|
|
37
37
|
/** @import { ServerHttp2Stream, IncomingHttpHeaders } from 'node:http2' */
|
|
38
|
-
/** @import { Config, RouteRequest, RouteAction, StreamID, RouteConditions, SecFetchMetadata } from './index.js' */
|
|
38
|
+
/** @import { Config, RouteRequest, RouteAction, StreamID, RouteConditions, SecFetchMetadata, RouteRequestAccept } from './index.js' */
|
|
39
39
|
|
|
40
40
|
const { HTTP2_METHOD_OPTIONS, HTTP2_METHOD_TRACE } = http2.constants
|
|
41
41
|
|
|
@@ -258,21 +258,34 @@ export function preamble(preState, headers, servername) {
|
|
|
258
258
|
//
|
|
259
259
|
// content negotiation
|
|
260
260
|
//
|
|
261
|
-
const
|
|
262
|
-
const
|
|
263
|
-
const
|
|
264
|
-
|
|
261
|
+
const acceptItem = Accept.parse(fullAccept)
|
|
262
|
+
const acceptEncodingItem = AcceptEncoding.parse(fullAcceptEncoding)
|
|
263
|
+
const acceptLanguageItem = AcceptLanguage.parse(fullAcceptLanguage)
|
|
264
|
+
|
|
265
|
+
/** @type {RouteRequestAccept} */
|
|
265
266
|
const acceptObject = {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
267
|
+
parsed: {
|
|
268
|
+
type: acceptItem,
|
|
269
|
+
encoding: acceptEncodingItem,
|
|
270
|
+
language: acceptLanguageItem,
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
get type() { return Accept.selectFrom(acceptItem, DEFAULT_SUPPORTED_MIME_TYPES) },
|
|
274
|
+
get encoding() { return AcceptEncoding.selectFrom(acceptEncodingItem, DEFAULT_SUPPORTED_ENCODINGS) },
|
|
275
|
+
get language() { return AcceptLanguage.selectFrom(acceptLanguageItem, DEFAULT_SUPPORTED_LANGUAGES) },
|
|
276
|
+
|
|
277
|
+
select: {
|
|
278
|
+
type: acceptableTypes => Accept.selectItemFrom(acceptItem, acceptableTypes ?? DEFAULT_SUPPORTED_MIME_TYPES),
|
|
279
|
+
encoding: acceptableEncodings => AcceptEncoding.selectItemFrom(acceptEncodingItem, acceptableEncodings ?? DEFAULT_SUPPORTED_ENCODINGS),
|
|
280
|
+
language: acceptableLanguages => AcceptLanguage.selectItemFrom(acceptLanguageItem, acceptableLanguages ?? DEFAULT_SUPPORTED_LANGUAGES)
|
|
281
|
+
}
|
|
269
282
|
}
|
|
270
283
|
|
|
271
284
|
//
|
|
272
285
|
// Trace
|
|
273
286
|
//
|
|
274
287
|
if(method === HTTP2_METHOD_TRACE) {
|
|
275
|
-
if(!ALLOW_TRACE) { return { ...state, type: 'not-allowed', method, methods: []
|
|
288
|
+
if(!ALLOW_TRACE) { return { ...state, type: 'not-allowed', method, methods: [] }}
|
|
276
289
|
const maxForwardsValue = maxForwards === undefined ? 0 : Number.parseInt(maxForwards)
|
|
277
290
|
const preambleEnd = performance.now()
|
|
278
291
|
state.meta.performance.push({ name: 'preamble-trace', duration: preambleEnd - preambleStart })
|
|
@@ -283,6 +296,7 @@ export function preamble(preState, headers, servername) {
|
|
|
283
296
|
//
|
|
284
297
|
// setup future body
|
|
285
298
|
//
|
|
299
|
+
const contentType = ContentType.parse(fullContentType)
|
|
286
300
|
const contentLength = fullContentLength === undefined ? undefined : Number.parseInt(fullContentLength, 10)
|
|
287
301
|
const body = requestBody(stream, {
|
|
288
302
|
byteLimit: BODY_BYTE_LENGTH,
|