@antelopejs/dms-frontend 0.5.4 → 0.5.5
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
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { writeBadRequest } from "./request-failure.mjs";
|
|
2
|
+
|
|
3
|
+
// The request target as the server reads it. Node hands `request.url` over
|
|
4
|
+
// verbatim, so it is not always a path: `//host/x`, `/\host/x` and the
|
|
5
|
+
// absolute form `http://host/x` all name another host once resolved, which
|
|
6
|
+
// turned the proxy into an open one. Every routing decision and the URL sent to
|
|
7
|
+
// the backend read the same normalized path from here.
|
|
8
|
+
|
|
9
|
+
const FRONTEND_ORIGIN = "http://frontend.local";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Whether `target` is an origin-form request target: a path, optionally with a
|
|
13
|
+
* query, that stays on this server once resolved. Anything else (an absolute
|
|
14
|
+
* or authority form, `*`, a path a URL parser reads as another host) is not a
|
|
15
|
+
* request a browser sends to the frontend.
|
|
16
|
+
*/
|
|
17
|
+
export function isOriginForm(target) {
|
|
18
|
+
if (typeof target !== "string" || !target.startsWith("/")) return false;
|
|
19
|
+
try {
|
|
20
|
+
return new URL(target, FRONTEND_ORIGIN).origin === FRONTEND_ORIGIN;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The backend URL a request target is proxied to: the target's normalized path
|
|
28
|
+
* and query on the backend's origin. The path is set, not resolved, so a path
|
|
29
|
+
* normalizing to `//host` (`/x/..//host`) stays a path on the backend.
|
|
30
|
+
*
|
|
31
|
+
* @param target The request target, origin-form
|
|
32
|
+
* @param backendBaseUrl The backend's base URL
|
|
33
|
+
*/
|
|
34
|
+
export function backendUrl(
|
|
35
|
+
target,
|
|
36
|
+
backendBaseUrl = process.env.DMS_API_BASE_URL,
|
|
37
|
+
) {
|
|
38
|
+
const { pathname, search } = new URL(target, FRONTEND_ORIGIN);
|
|
39
|
+
const url = new URL(new URL(backendBaseUrl).origin);
|
|
40
|
+
url.pathname = pathname;
|
|
41
|
+
url.search = search;
|
|
42
|
+
return url;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Answers 400 to a request whose target is not origin-form, before any route
|
|
47
|
+
* reads it. Returns whether it answered.
|
|
48
|
+
*/
|
|
49
|
+
export function refuseForeignTarget(request, response) {
|
|
50
|
+
if (isOriginForm(request.url)) return false;
|
|
51
|
+
writeBadRequest(response);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
package/templates/vue/server.mjs
CHANGED
|
@@ -42,6 +42,7 @@ import { HOMEPAGE } from "./server/homepage.mjs";
|
|
|
42
42
|
import { htmlTag } from "./server/html-tag.mjs";
|
|
43
43
|
import * as icons from "./server/icons.mjs";
|
|
44
44
|
import * as requestFailure from "./server/request-failure.mjs";
|
|
45
|
+
import { backendUrl, refuseForeignTarget } from "./server/request-target.mjs";
|
|
45
46
|
import { handleTester } from "./server/tester.mjs";
|
|
46
47
|
import {
|
|
47
48
|
createInertiaPage,
|
|
@@ -239,12 +240,11 @@ async function proxy(request, response, fallbackOnNotFound = false) {
|
|
|
239
240
|
refuseCrossOrigin(response);
|
|
240
241
|
return true;
|
|
241
242
|
}
|
|
242
|
-
const target = new URL(request.url, process.env.DMS_API_BASE_URL);
|
|
243
243
|
// Cancelling the body alone misses a client that leaves before the backend
|
|
244
244
|
// answers: its stream, an SSE feed above all, then stayed open for good.
|
|
245
245
|
const abandoned = new AbortController();
|
|
246
246
|
response.once("close", () => abandoned.abort());
|
|
247
|
-
const upstream = await fetch(
|
|
247
|
+
const upstream = await fetch(backendUrl(request.url), {
|
|
248
248
|
signal: abandoned.signal,
|
|
249
249
|
method: request.method,
|
|
250
250
|
headers: backendHeaders(request, publicEndpoint),
|
|
@@ -505,6 +505,7 @@ async function writeRequestFailure(error, request, response) {
|
|
|
505
505
|
|
|
506
506
|
export async function handleRequestSafely(request, response) {
|
|
507
507
|
try {
|
|
508
|
+
if (refuseForeignTarget(request, response)) return;
|
|
508
509
|
await handleRequest(request, response);
|
|
509
510
|
} catch (error) {
|
|
510
511
|
// An error page failing too must not reject: that would end the process.
|