@astrale-os/kernel-server 0.5.0-beta.51 → 0.5.0-beta.53
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.
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bundleUrl } from '@astrale-os/kernel-protocol/publication';
|
|
1
2
|
import { verifyBundleBytes } from '@astrale-os/kernel-protocol/publication/bundle-evidence';
|
|
2
3
|
import { accept as acceptPublication, encode as encodePublication, } from '@astrale-os/kernel-protocol/publication/publication';
|
|
3
4
|
import { admitTable } from './table.js';
|
|
@@ -16,6 +17,14 @@ export function preparePublicationSnapshot(input) {
|
|
|
16
17
|
throw new TypeError('Publication snapshot Domain identity differs from its Publication.');
|
|
17
18
|
}
|
|
18
19
|
const table = admitTable(admittedPublication, input.table, domain);
|
|
20
|
+
const addressed = new URL(bundleUrl(admittedPublication.identity.issuer, admittedPublication.schema.bundle.ref));
|
|
21
|
+
const addressedDirectory = addressed.pathname.slice(0, addressed.pathname.lastIndexOf('/') + 1);
|
|
22
|
+
const servingBundle = new URL(admittedPublication.schema.bundle.href);
|
|
23
|
+
if (servingBundle.origin === addressed.origin &&
|
|
24
|
+
servingBundle.pathname.startsWith(addressedDirectory) &&
|
|
25
|
+
servingBundle.href !== addressed.href) {
|
|
26
|
+
throw new TypeError('Publication bundle URL differs from its artifact digest.');
|
|
27
|
+
}
|
|
19
28
|
const publicationBytes = encodePublication(admittedPublication);
|
|
20
29
|
const snapshot = Object.freeze({ publication: admittedPublication });
|
|
21
30
|
const prepared = Object.freeze({
|
package/dist/routes/routes.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
2
|
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
3
3
|
import * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
4
|
+
import { bundleUrl } from '@astrale-os/kernel-protocol/publication';
|
|
4
5
|
import { mediaType as publicationMediaType, PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
|
|
5
6
|
import { reference as publicationReference } from '@astrale-os/kernel-protocol/publication/reference';
|
|
6
7
|
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
@@ -59,6 +60,9 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
59
60
|
return publicationPreflight(cors, request, snapshot, externalOrigin);
|
|
60
61
|
}
|
|
61
62
|
const responseCors = cors.response(request);
|
|
63
|
+
const descriptor = snapshot.publication.schema.bundle;
|
|
64
|
+
const addressed = bundleUrl(snapshot.publication.identity.issuer, descriptor.ref);
|
|
65
|
+
const immutable = descriptor.href === addressed;
|
|
62
66
|
if (sameRoute(request.url, snapshot.publication.schema.bundle.href, externalOrigin)) {
|
|
63
67
|
if (!active.isAccepting)
|
|
64
68
|
return response({ error: 'Publication artifact is unavailable while draining.' }, 503, responseCors);
|
|
@@ -68,16 +72,32 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
|
|
|
68
72
|
if (body.byteLength !== snapshot.publication.schema.bundle.ref.size) {
|
|
69
73
|
return response({ error: 'Bundle differs from its Publication.' }, 500, responseCors);
|
|
70
74
|
}
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
const etag = quoteEtag(descriptor.ref.digest);
|
|
76
|
+
const unchanged = request.headers.get('if-none-match') === etag;
|
|
77
|
+
return new Response(unchanged ? null : toBody(body), {
|
|
78
|
+
status: unchanged ? 304 : 200,
|
|
73
79
|
headers: {
|
|
74
80
|
...responseCors,
|
|
75
81
|
'content-type': snapshot.publication.schema.bundle.ref.mediaType,
|
|
76
|
-
etag
|
|
77
|
-
'cache-control':
|
|
82
|
+
etag,
|
|
83
|
+
'cache-control': immutable
|
|
84
|
+
? 'public, max-age=31536000, immutable, no-transform'
|
|
85
|
+
: 'public, max-age=0, must-revalidate, no-transform',
|
|
78
86
|
},
|
|
79
87
|
});
|
|
80
88
|
}
|
|
89
|
+
if (request.method === 'GET' && immutable) {
|
|
90
|
+
const pathname = new URL(addressed).pathname;
|
|
91
|
+
const directory = pathname.slice(0, pathname.lastIndexOf('/') + 1);
|
|
92
|
+
const requested = new URL(request.url);
|
|
93
|
+
if (requested.pathname.startsWith(directory) &&
|
|
94
|
+
/^[0-9a-f]{64}\.json$/u.test(requested.pathname.slice(directory.length))) {
|
|
95
|
+
return new Response(null, {
|
|
96
|
+
status: 404,
|
|
97
|
+
headers: { ...responseCors, 'cache-control': 'no-store' },
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
81
101
|
const deliveryUrl = snapshot.publication.delivery?.href;
|
|
82
102
|
if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
|
|
83
103
|
return handleDelivery(request, snapshot, deliveryProvider, active, limits, cors.response(request));
|
|
@@ -206,12 +226,14 @@ function streamingBinaryResponse(binary, operation, cors, request) {
|
|
|
206
226
|
const iterator = binary.body[Symbol.asyncIterator]();
|
|
207
227
|
const applicationHeaders = applicationBinaryHeaders(binary.headers);
|
|
208
228
|
let terminal = false;
|
|
229
|
+
let controller;
|
|
209
230
|
let stopping;
|
|
210
|
-
const stop = () => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
231
|
+
const stop = (reason = 'Binary response was interrupted.') => {
|
|
232
|
+
if (terminal)
|
|
233
|
+
return stopping ?? Promise.resolve();
|
|
234
|
+
terminal = true;
|
|
235
|
+
controller?.error(new Error(reason));
|
|
236
|
+
stopping = Promise.resolve().then(async () => {
|
|
215
237
|
try {
|
|
216
238
|
await iterator.return?.();
|
|
217
239
|
}
|
|
@@ -222,6 +244,9 @@ function streamingBinaryResponse(binary, operation, cors, request) {
|
|
|
222
244
|
return stopping;
|
|
223
245
|
};
|
|
224
246
|
const body = new ReadableStream({
|
|
247
|
+
start(output) {
|
|
248
|
+
controller = output;
|
|
249
|
+
},
|
|
225
250
|
async pull(controller) {
|
|
226
251
|
if (terminal)
|
|
227
252
|
return;
|
|
@@ -238,6 +263,8 @@ function streamingBinaryResponse(binary, operation, cors, request) {
|
|
|
238
263
|
controller.enqueue(next.value);
|
|
239
264
|
}
|
|
240
265
|
catch (cause) {
|
|
266
|
+
if (terminal)
|
|
267
|
+
return;
|
|
241
268
|
terminal = true;
|
|
242
269
|
controller.error(cause);
|
|
243
270
|
operation.settle();
|
|
@@ -6,7 +6,7 @@ import { invocationContext, publicationRouteDecision, } from '../../invocation/i
|
|
|
6
6
|
import { readBoundedBody } from '../body.js';
|
|
7
7
|
import { loadCodecs } from '../codecs.js';
|
|
8
8
|
import { resolveCredential } from '../credential.js';
|
|
9
|
-
import { renderHttpError, renderHttpResult } from './render.js';
|
|
9
|
+
import { isStreamingBinary, renderHttpError, renderHttpResult, } from './render.js';
|
|
10
10
|
export function createHttpHandler(config) {
|
|
11
11
|
return async (request, host) => {
|
|
12
12
|
const cors = (exposedHeaders) => config.cors.response(request, exposedHeaders);
|
|
@@ -146,7 +146,8 @@ export function createHttpHandler(config) {
|
|
|
146
146
|
active,
|
|
147
147
|
maximumResponseBytes: config.maximumResponseBytes,
|
|
148
148
|
});
|
|
149
|
-
handedOff =
|
|
149
|
+
handedOff =
|
|
150
|
+
result.kind === 'stream' || (result.kind === 'binary' && isStreamingBinary(result.value));
|
|
150
151
|
return response;
|
|
151
152
|
}
|
|
152
153
|
catch (input) {
|
|
@@ -90,12 +90,14 @@ function streamingBinaryHttpResponse(binary, context) {
|
|
|
90
90
|
headers: binary.headers,
|
|
91
91
|
}), context.cors, true);
|
|
92
92
|
let terminal = false;
|
|
93
|
+
let controller;
|
|
93
94
|
let stopping;
|
|
94
|
-
const stop = () => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
const stop = (reason = 'Binary response was interrupted.') => {
|
|
96
|
+
if (terminal)
|
|
97
|
+
return stopping ?? Promise.resolve();
|
|
98
|
+
terminal = true;
|
|
99
|
+
controller?.error(new Error(reason));
|
|
100
|
+
stopping = Promise.resolve().then(async () => {
|
|
99
101
|
try {
|
|
100
102
|
await iterator.return?.();
|
|
101
103
|
}
|
|
@@ -106,6 +108,9 @@ function streamingBinaryHttpResponse(binary, context) {
|
|
|
106
108
|
return stopping;
|
|
107
109
|
};
|
|
108
110
|
const body = new ReadableStream({
|
|
111
|
+
start(output) {
|
|
112
|
+
controller = output;
|
|
113
|
+
},
|
|
109
114
|
async pull(controller) {
|
|
110
115
|
if (terminal)
|
|
111
116
|
return;
|
|
@@ -122,6 +127,8 @@ function streamingBinaryHttpResponse(binary, context) {
|
|
|
122
127
|
controller.enqueue(next.value);
|
|
123
128
|
}
|
|
124
129
|
catch (cause) {
|
|
130
|
+
if (terminal)
|
|
131
|
+
return;
|
|
125
132
|
terminal = true;
|
|
126
133
|
controller.error(cause);
|
|
127
134
|
context.active.settle();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/kernel-server",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.53",
|
|
4
4
|
"description": "Portable HTTP and WebSocket boundary for Astrale invocation, publication, installation, and issuer protocols",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@astrale-os/kernel-core": "0.9.0-beta.
|
|
36
|
+
"@astrale-os/kernel-core": "0.9.0-beta.46",
|
|
37
37
|
"@astrale-os/kernel-dsl": "0.2.0-beta.33",
|
|
38
|
-
"@astrale-os/kernel-protocol": "0.5.0-beta.
|
|
38
|
+
"@astrale-os/kernel-protocol": "0.5.0-beta.51",
|
|
39
39
|
"hono": "^4.6.20",
|
|
40
40
|
"msgpackr": "^2.0.5"
|
|
41
41
|
},
|