anbaric-cloud-hosting 1.16.1 → 1.16.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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anbaric-cloud-hosting",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.2",
|
|
4
4
|
"description": "Anbaric Cloud hosting service: Postgres-backed job persistence and queuing exposed over an HTTP API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "chris@anbaric.ai",
|
|
@@ -18,17 +18,17 @@
|
|
|
18
18
|
"@aws-sdk/client-s3": "^3.1110.0",
|
|
19
19
|
"@aws-sdk/client-secrets-manager": "^3.700.0",
|
|
20
20
|
"@aws-sdk/client-servicediscovery": "^3.1110.0",
|
|
21
|
-
"anbaric-data-store": "^1.16.
|
|
22
|
-
"anbaric-plugins": "^1.16.
|
|
23
|
-
"anbaric-tsapi": "^1.16.
|
|
21
|
+
"anbaric-data-store": "^1.16.2",
|
|
22
|
+
"anbaric-plugins": "^1.16.2",
|
|
23
|
+
"anbaric-tsapi": "^1.16.2",
|
|
24
24
|
"esbuild": "^0.28.2",
|
|
25
25
|
"pg": "^8.16.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^26.2.0",
|
|
29
29
|
"@types/pg": "^8.15.0",
|
|
30
|
-
"anbaric-impl-cloud": "^1.16.
|
|
31
|
-
"anbaric-state-machine": "^1.16.
|
|
30
|
+
"anbaric-impl-cloud": "^1.16.2",
|
|
31
|
+
"anbaric-state-machine": "^1.16.2",
|
|
32
32
|
"tsx": "^4.20.0",
|
|
33
33
|
"typescript": "^7.0.2"
|
|
34
34
|
},
|
|
@@ -1,9 +1,21 @@
|
|
|
1
|
+
import {IncomingHttpHeaders, OutgoingHttpHeaders, request as httpRequest} from "node:http";
|
|
1
2
|
import {BuildLayer} from "../../app-management/BuildLayer";
|
|
2
3
|
import {Request} from "../Request";
|
|
3
4
|
import {RequestHandler} from "../RequestHandler";
|
|
4
5
|
|
|
5
|
-
/*
|
|
6
|
-
|
|
6
|
+
/* Headers that describe a single hop and must not be forwarded across the
|
|
7
|
+
proxy in either direction. */
|
|
8
|
+
const HOP_BY_HOP = new Set([
|
|
9
|
+
"connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
|
|
10
|
+
"te", "trailer", "transfer-encoding", "upgrade",
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
/* The router's fallback: any unregistered top-level path naming a running app
|
|
14
|
+
is reverse-proxied to it. The app is exposed at /<appName>; that prefix is
|
|
15
|
+
stripped before forwarding (the app sees the sub-path) and surfaced to the
|
|
16
|
+
app as X-Forwarded-Prefix so it can rebuild public URLs. Request and response
|
|
17
|
+
headers pass through both ways - notably cookies, Set-Cookie and Location - so
|
|
18
|
+
sessions and redirects work from app-served HTML. */
|
|
7
19
|
class AppProxyHandler implements RequestHandler {
|
|
8
20
|
|
|
9
21
|
constructor(private buildLayer : BuildLayer) {}
|
|
@@ -16,17 +28,41 @@ class AppProxyHandler implements RequestHandler {
|
|
|
16
28
|
const appPath = request.url.pathname.slice(`/${appName}`.length) || "/";
|
|
17
29
|
const body = request.method === "GET" || request.method === "HEAD" ? undefined : await request.rawBody();
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
31
|
+
await new Promise<void>((resolve, reject) => {
|
|
32
|
+
const upstream = httpRequest({
|
|
33
|
+
host: app.appHost,
|
|
34
|
+
port: app.appPort,
|
|
35
|
+
method: request.method,
|
|
36
|
+
path: `${appPath}${request.url.search}`,
|
|
37
|
+
headers: this.forwardHeaders(request, appName),
|
|
38
|
+
}, response => {
|
|
39
|
+
request.rawResponse.writeHead(response.statusCode ?? 502, this.passThrough(response.headers));
|
|
40
|
+
response.pipe(request.rawResponse);
|
|
41
|
+
response.on("end", resolve);
|
|
42
|
+
response.on("error", reject);
|
|
43
|
+
});
|
|
44
|
+
upstream.on("error", reject);
|
|
45
|
+
if (body && body.length > 0) upstream.write(body);
|
|
46
|
+
upstream.end();
|
|
23
47
|
});
|
|
48
|
+
}
|
|
24
49
|
|
|
25
|
-
|
|
26
|
-
request.
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
request.
|
|
50
|
+
private forwardHeaders(request : Request, appName : string) : OutgoingHttpHeaders {
|
|
51
|
+
const headers = this.passThrough(request.raw.headers);
|
|
52
|
+
delete headers.host;
|
|
53
|
+
headers["x-forwarded-prefix"] = `/${appName}`;
|
|
54
|
+
headers["x-forwarded-host"] = request.raw.headers.host;
|
|
55
|
+
headers["x-forwarded-proto"] = "https";
|
|
56
|
+
return headers;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private passThrough(headers : IncomingHttpHeaders) : OutgoingHttpHeaders {
|
|
60
|
+
const kept : OutgoingHttpHeaders = {};
|
|
61
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
62
|
+
if (value === undefined || HOP_BY_HOP.has(name)) continue;
|
|
63
|
+
kept[name] = value;
|
|
64
|
+
}
|
|
65
|
+
return kept;
|
|
30
66
|
}
|
|
31
67
|
|
|
32
68
|
}
|
|
@@ -27,7 +27,7 @@ class AppsHandler implements RequestHandler {
|
|
|
27
27
|
case "POST": {
|
|
28
28
|
const appPort = Number(request.query("port"));
|
|
29
29
|
if (!Number.isInteger(appPort) || appPort <= 0) {
|
|
30
|
-
return request.reply(400, { error: "Expected a numeric port query parameter" });
|
|
30
|
+
return request.reply(400, { error: "Expected a numeric ?port query parameter - the app's internal port (\"internalPort\" in .anbaric/app-config.json)" });
|
|
31
31
|
}
|
|
32
32
|
const tarball = await request.rawBody();
|
|
33
33
|
if (tarball.length === 0) return request.reply(400, { error: "Expected a gzipped tarball body" });
|