nucleus-core-ts 0.9.925 → 0.9.926
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/dist/.build-ok
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.9.
|
|
1
|
+
0.9.926
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { evaluateAccess, ManifestCache, RoleClaimsCache, resolveClaimsFromRoles, verifyJwtHS256 } from './authz';
|
|
2
2
|
import { createProxyLogger, matchPath, parseCookies, rewritePath } from './utils';
|
|
3
|
+
import { canBufferForRetry, timeoutForBody } from './largeBody';
|
|
3
4
|
async function fetchSuppressedClaims(url, cookieHeader, logger) {
|
|
4
5
|
const empty = {
|
|
5
6
|
suppressedClaims: [],
|
|
@@ -461,12 +462,33 @@ export function createHttpProxyHandler(config) {
|
|
|
461
462
|
} else if (!accessToken && isAuthPath) {
|
|
462
463
|
logger.warn(`[Auth:proxy] NO TOKEN available for ${path} — request will likely fail with 401`);
|
|
463
464
|
}
|
|
464
|
-
const
|
|
465
|
+
const baseTimeout = target.timeout ?? 30000;
|
|
466
|
+
/*
|
|
467
|
+
* A file needs minutes, not thirty seconds.
|
|
468
|
+
*
|
|
469
|
+
* The fixed timeout aborted a 200 MB video long before the last byte, and
|
|
470
|
+
* the browser had no way to tell that from a slow line — the bar simply
|
|
471
|
+
* stopped. The allowance now grows with the declared size.
|
|
472
|
+
*/ const timeout = timeoutForBody(req.headers, baseTimeout);
|
|
465
473
|
const followRedirects = target.followRedirects !== false;
|
|
466
474
|
// Buffer body for potential retry (only if 401 retry is enabled)
|
|
467
475
|
let bodyForRetry = null;
|
|
468
476
|
let bodyToSend = req.body;
|
|
469
|
-
|
|
477
|
+
/*
|
|
478
|
+
* Only a body small enough to hold.
|
|
479
|
+
*
|
|
480
|
+
* Buffering exists so a 401 can be answered by refreshing the token and
|
|
481
|
+
* sending the same request again — worth it for a form post, fatal for a
|
|
482
|
+
* file. An 800 MB video was read into an ArrayBuffer inside a container
|
|
483
|
+
* limited to 256 MiB: the upload appeared to run, because the browser
|
|
484
|
+
* really was sending bytes, and then died with nothing to show for it.
|
|
485
|
+
*
|
|
486
|
+
* A large body streams straight through and gives up the retry. That is
|
|
487
|
+
* the right way round: an upload that is refused can be started again by
|
|
488
|
+
* the person who chose the file; an upload that never completes cannot be
|
|
489
|
+
* rescued by anybody.
|
|
490
|
+
*/ const bufferable = canBufferForRetry(req.headers);
|
|
491
|
+
if (refreshEnabled && (refreshConfig.retryOn401 ?? true) && req.body && bufferable) {
|
|
470
492
|
try {
|
|
471
493
|
bodyForRetry = await req.arrayBuffer();
|
|
472
494
|
bodyToSend = bodyForRetry;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a request body is small enough to hold in memory for a retry.
|
|
3
|
+
*
|
|
4
|
+
* The proxy buffers the whole body so that a 401 can be answered by refreshing
|
|
5
|
+
* the token and sending the same request again. That is a good trade for a form
|
|
6
|
+
* post and a catastrophic one for a file: an eight-hundred-megabyte video was
|
|
7
|
+
* read into `ArrayBuffer` inside a container limited to 256 MiB, so the upload
|
|
8
|
+
* appeared to run — the browser really was sending bytes — and then died with
|
|
9
|
+
* nothing to show for it. Measured on the customer's install: the video module
|
|
10
|
+
* accepts up to 1 GB, the ingress passes 1 GB, and the proxy in between could
|
|
11
|
+
* never carry more than a fraction of it.
|
|
12
|
+
*
|
|
13
|
+
* A large body is streamed straight through instead. The cost is that it cannot
|
|
14
|
+
* be replayed after a token refresh, which is the right way round: an upload
|
|
15
|
+
* that is refused can be started again by the person who chose the file, while
|
|
16
|
+
* an upload that never completes cannot be rescued by anybody.
|
|
17
|
+
*/
|
|
18
|
+
export declare const RETRY_BUFFER_LIMIT_BYTES: number;
|
|
19
|
+
/**
|
|
20
|
+
* `null` when the size is not declared.
|
|
21
|
+
*
|
|
22
|
+
* An undeclared length is treated as LARGE: a chunked upload announces nothing,
|
|
23
|
+
* and guessing "small" there is how the buffer gets filled by the one request
|
|
24
|
+
* that must not fill it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function declaredBodySize(headers: Headers): number | null;
|
|
27
|
+
export declare function canBufferForRetry(headers: Headers, limit?: number): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* How long to allow, given what is being carried.
|
|
30
|
+
*
|
|
31
|
+
* Thirty seconds is right for an API call and absurd for a file: a 200 MB video
|
|
32
|
+
* over an office line needs minutes, and the old fixed timeout aborted it long
|
|
33
|
+
* before the last byte. The allowance grows with the declared size — a minute
|
|
34
|
+
* per fifty megabytes — and is never shorter than the configured default.
|
|
35
|
+
*/
|
|
36
|
+
export declare function timeoutForBody(headers: Headers, baseTimeoutMs: number): number;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a request body is small enough to hold in memory for a retry.
|
|
3
|
+
*
|
|
4
|
+
* The proxy buffers the whole body so that a 401 can be answered by refreshing
|
|
5
|
+
* the token and sending the same request again. That is a good trade for a form
|
|
6
|
+
* post and a catastrophic one for a file: an eight-hundred-megabyte video was
|
|
7
|
+
* read into `ArrayBuffer` inside a container limited to 256 MiB, so the upload
|
|
8
|
+
* appeared to run — the browser really was sending bytes — and then died with
|
|
9
|
+
* nothing to show for it. Measured on the customer's install: the video module
|
|
10
|
+
* accepts up to 1 GB, the ingress passes 1 GB, and the proxy in between could
|
|
11
|
+
* never carry more than a fraction of it.
|
|
12
|
+
*
|
|
13
|
+
* A large body is streamed straight through instead. The cost is that it cannot
|
|
14
|
+
* be replayed after a token refresh, which is the right way round: an upload
|
|
15
|
+
* that is refused can be started again by the person who chose the file, while
|
|
16
|
+
* an upload that never completes cannot be rescued by anybody.
|
|
17
|
+
*/ export const RETRY_BUFFER_LIMIT_BYTES = 2 * 1024 * 1024;
|
|
18
|
+
/**
|
|
19
|
+
* `null` when the size is not declared.
|
|
20
|
+
*
|
|
21
|
+
* An undeclared length is treated as LARGE: a chunked upload announces nothing,
|
|
22
|
+
* and guessing "small" there is how the buffer gets filled by the one request
|
|
23
|
+
* that must not fill it.
|
|
24
|
+
*/ export function declaredBodySize(headers) {
|
|
25
|
+
const raw = headers.get('content-length');
|
|
26
|
+
if (!raw) return null;
|
|
27
|
+
const size = Number(raw);
|
|
28
|
+
return Number.isFinite(size) && size >= 0 ? size : null;
|
|
29
|
+
}
|
|
30
|
+
export function canBufferForRetry(headers, limit = RETRY_BUFFER_LIMIT_BYTES) {
|
|
31
|
+
const size = declaredBodySize(headers);
|
|
32
|
+
if (size === null) return false;
|
|
33
|
+
return size <= limit;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* How long to allow, given what is being carried.
|
|
37
|
+
*
|
|
38
|
+
* Thirty seconds is right for an API call and absurd for a file: a 200 MB video
|
|
39
|
+
* over an office line needs minutes, and the old fixed timeout aborted it long
|
|
40
|
+
* before the last byte. The allowance grows with the declared size — a minute
|
|
41
|
+
* per fifty megabytes — and is never shorter than the configured default.
|
|
42
|
+
*/ export function timeoutForBody(headers, baseTimeoutMs) {
|
|
43
|
+
const size = declaredBodySize(headers);
|
|
44
|
+
if (size === null || size <= RETRY_BUFFER_LIMIT_BYTES) return baseTimeoutMs;
|
|
45
|
+
const perFiftyMb = 60000;
|
|
46
|
+
const allowance = Math.ceil(size / (50 * 1024 * 1024)) * perFiftyMb;
|
|
47
|
+
return Math.max(baseTimeoutMs, allowance);
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nucleus-core-ts",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.926",
|
|
4
4
|
"description": "Production-ready, enterprise-grade TypeScript framework for building multi-tenant APIs",
|
|
5
5
|
"author": "Hidayet Can Özcan <hidayetcan@gmail.com>",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|