0.35.5
This commit is contained in:
1 parent
ff42a13dbf
commit
e7b4991316
14 files changed
+176
-50
No files matched your search
@@ -1,6 +1,6 @@
|
||||
# RustProxy source and documentation maintenance
|
||||
|
||||
Current source version: **0.35.4**. The Cargo package and binary are named `rust-proxy`.
|
||||
Current source version: **0.35.5**. The Cargo package and binary are named `rust-proxy`.
|
||||
|
||||
Before editing, read [README.md](README.md), the relevant [documentation guide](docs/README.md) and the actual code and tests. Historical release experiments are not current specifications. Use the bundled `src/admin/openapi.json` for API routes and payloads; the source implementation is authoritative for runtime behavior.
|
||||
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
|
||||
Release history focused on **features, settings, default behavior, compatibility and fixes**. Changes to internal code structure and preliminary performance experiments are intentionally omitted. Historical descriptions reflect their respective releases; consult the current documentation for the settings available in the latest release.
|
||||
|
||||
## 0.35.5 — Overview polish and cache build fix
|
||||
|
||||
- **Overview charts:** the cache-results heading now matches the other chart headings and its HIT/MISS/BYPASS legend stays on one line; system uptime is shown in the System load header; the backend count is aligned with Open connections; the redundant Requests per second footer was removed from the PHP requests tile.
|
||||
- **Polish status labels:** the sidebar healthy-state label is now **SYSTEM OK**, and the application self-check uses the shorter **API** label.
|
||||
- **Cache build fix:** restored the detached-header helper functions used when finalizing cache fills, fixing the `E0425` build failure for `detached_header_value` and `detach_header_map` while preserving the sensitive flag on copied header values.
|
||||
- **Upgrade notes:** no configuration or database changes are required.
|
||||
|
||||
## 0.35.4 — Dashboard status and control cleanup
|
||||
|
||||
- **Overview samples:** the history sample selector is right-aligned on desktop and mobile and now saves immediately in the browser; the separate Remember choice button is removed.
|
||||
|
||||
Generated
+1
-1
@@ -1943,7 +1943,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-proxy"
|
||||
version = "0.35.4"
|
||||
version = "0.35.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rust-proxy"
|
||||
version = "0.35.4"
|
||||
version = "0.35.5"
|
||||
edition = "2021"
|
||||
rust-version = "1.99.1"
|
||||
description = "RustProxy - independent reverse proxy with local administration"
|
||||
|
||||
+13
-13
@@ -1,10 +1,10 @@
|
||||
61bf40a0572889dc8b0dda9eae4e93e44b0de9364b3c798106c85700ffb37c23 .dockerignore
|
||||
4140a2956264b44f259e6a37fc00ba02097bbe68b09296e04d3d243c1287129b .env.example
|
||||
e76e688874828e3743d03693b1d2872b9633b4db09909673786caf30f7cfe489 .gitignore
|
||||
f8272b8b6a11d50811e2f25b5d43f4713dd7fc7d22c2b4892cb85757d481c10d AGENTS.md
|
||||
9539278957c4d6dbbe613b7ba5388499e215b28aa153f099eefe4949b3eb4f78 CHANGELOG.md
|
||||
c593deba438fb6f5acbc6868612ddfa30d4f1e15134e9654c0f399f49b2c32fd Cargo.lock
|
||||
9795da5b826e58378910a45c3c9d9bb3a9cbdf9e009ad03ee82d1a59da1f4e83 Cargo.toml
|
||||
140ddb17001c2c821dd763335c1cecbfd75b27166b2a3ab1e815a8d53471a856 AGENTS.md
|
||||
2feb306d156b92773b6bf376d439c9f823840b745309a61b158ddc68a59186c3 CHANGELOG.md
|
||||
f7c3fa3eb61e00a0ec0156229877a01d14bd3fa568dff0b04d0d73bb12cf6fae Cargo.lock
|
||||
bf974faca111295d73a0c092be3536d70efeb222d5599f28814cba169cbeef25 Cargo.toml
|
||||
25c7f3c8162cbf5cc156439774118a81dae9fdc52b611ec7e40c230fc99a772c Dockerfile
|
||||
0d96a4ff68ad6d4b6f1f30f713b18d5184912ba8dd389f86aa7710db079abcb0 LICENSE
|
||||
82d1dee2fc68158f0ea73259151461e64c8b18d807cd1fecfb1ed99dc30cf5f4 README.md
|
||||
@@ -86,12 +86,12 @@ de6550ab8f48d556d752c252212c38ba65491fd224f972c10fbe552f91ed7520 integrations/o
|
||||
45be151cd85fc2ef2f1fdc3003ace63381af262e73af8a685ab48cb697cc0de1 integrations/routeros-oci-build/.env.routeros.example
|
||||
11aa102de1089ecc375f20c4bd5420fcfd3b2ef23fe0c8bdf04953fcfaf40ef0 integrations/routeros-oci-build/Dockerfile.routeros
|
||||
7c7a3e51f43b43e1c962a4b0bb591f99c1f06977bf77f23719f7f867dfe7eb8e integrations/routeros-oci-build/Dockerfile.routeros.dockerignore
|
||||
a68d5780f9eeb084db5ec465791b71332deb0456fdb245a253d464c5c76431c9 integrations/routeros-oci-build/README.md
|
||||
70852c6f13ef6a287c8f815aab085cc53e1150461a88342ad83715a9c7b0bbef integrations/routeros-oci-build/README.md
|
||||
3f092a9ab80221eddd67be4d1a776acbc6e04c1eb6011b588280c28e83b2582f integrations/routeros-oci-build/build-routeros-oci.sh
|
||||
3f267cafa16006e5456ade78ee2d5fae02cc7ce747839523068ffd8d7c98798a integrations/routeros-oci-build/docker-entrypoint.sh
|
||||
13189fa546c3488dddd8efc80c47f6da5241e5fc28b79cbfe795414ea05c7a4f langs/README.md
|
||||
cfe8fff42f98edc85a626f1d87521fa75d24f8b6b4573c46b1541820681a7080 langs/en_US.json
|
||||
771b13ae8d77ec0348cb56a7093d328a8b3abcde184a65a81f5db8e44fb910b3 langs/pl_pl.json
|
||||
34628bddc76c02eff482aa8300e0523e0edad87dfa4ebc3ef3285d68b6c46cfb langs/pl_pl.json
|
||||
9664543e4d03422788a65e42f968c1f026c2235cb5be6fc3e0ad318997077538 make_zip.py
|
||||
e830d37dd1f81a4b0aa7d535f5bba69e34e433b0fd99cc8ee0b296786c32379a migrations/0002_admin.sql
|
||||
20112cc82192098b8d602f1c4f7ca08712e9e1366c845fabb92753187c370d35 migrations/0003_certificate_renewals.sql
|
||||
@@ -162,7 +162,7 @@ d58a2ed83319176de4e66790ed292447c9497ae031464ec55cfe27174acb639a src/admin/file
|
||||
77b8c05645969834cc8a58e249a7b60a57d6c47ad1c5cd92ffa5e4a9b5a8e73c src/admin/file_logs_query.rs
|
||||
444d7715406ffd2305a72decf7fdf633aa4c2752cd881c8fa3b95ed971d79820 src/admin/logging_api.rs
|
||||
243f4a9c3fa99781e19c3952354d2632c0ce65e1dafac729567cbe3c177f94cb src/admin/mod.rs
|
||||
ee4075f5f16bb0940fb785b2c527669a605d4352efe1030d4ca98bddb1ce051c src/admin/openapi.json
|
||||
30b0ea16c6960e8efd682e95429c70c1b2ff178aef67e570e678d0dd82dce060 src/admin/openapi.json
|
||||
1f4ec45d146e00ef664fae37517f8fcb5455d47daec496c02b2fc15ddec49131 src/admin/operations.rs
|
||||
257d93a29d0bda96462bcf695aba8d0ec0f9412c379e066ec46ae8ee034e3a53 src/admin/panel_assets.rs
|
||||
f5127855c2994d052678865df36d703b74f4a0afc633daf7718b10da18d26e37 src/admin/panel_content.rs
|
||||
@@ -198,7 +198,7 @@ bfd9fafb3112197e8f875dbf87a9a66c81aef0fe83687601a84a66f5e13f69bc src/admin/web/
|
||||
46c7cd36397375eb7e8c230c8aa97b1dbea0a63800beb977efe1ec67a14b237e src/admin/web/app/preferences.js
|
||||
9e2b12d34ab1fb29716645720577ad49b16c03b46c482a03695e8f645d170e79 src/admin/web/app/rate-sampling.js
|
||||
d34f9e25a04cc4eafdbc48daa4e69aab18944e8d34e555a50d9c122c680595f8 src/admin/web/app/refresh.js
|
||||
564c8d81c9164e3a63f230579facd3c75281a806477e97ef9c05145807e08c94 src/admin/web/app/runtime.js
|
||||
c6ae53c0e95e67b9165822813efc622001be5354c19bb221a52514221d1df23e src/admin/web/app/runtime.js
|
||||
31201586925e6f4792071d7d03fb05dcc4a69a03c02a4e85eb0746f45af68074 src/admin/web/app/session-resume.js
|
||||
c7a18238ac426de4d1cf84a395ee4dc66410d60a57a3822695dc10adc668d956 src/admin/web/app/settings.js
|
||||
b7ead49224a4ec67181d3f24f9d9634bd6d76756f826a4d5d993300637d1d8f3 src/admin/web/app/stream-dashboard.js
|
||||
@@ -208,7 +208,7 @@ e9788a9d1e0bce8775f612a5c842afea8a0dcf01301acdef4b69ff6a52d2ef45 src/admin/web/
|
||||
a09f060b47771c56e9b9e57f1b896a32a2f87f1fb33401ac76932a91d247c67e src/admin/web/css/00-tokens.css
|
||||
77b8b748f31ef383c64ba5ca1740e9b6073f0de1e77c2c8c62d0f38bb52dd296 src/admin/web/css/10-base.css
|
||||
6ed54b27a4a81b073f1d30cf61e366f3d5c1f6f8495f0d0a8af48e061937eb4e src/admin/web/css/20-shell.css
|
||||
65c1bd6d0adf05de60738f5a557dff18ad0d4b0b531e58aab29b01519416b09c src/admin/web/css/30-overview.css
|
||||
c6d5696f0cdf4149e3341d18f7fd955f2411d50aac49a307c3eb0de2db9f92cb src/admin/web/css/30-overview.css
|
||||
dd82b5e3ead91c20bb08cf8d71bbba31ea8e3d108c417913c63409f3e34da7e1 src/admin/web/css/40-editors.css
|
||||
6af9fcb439823ec63abe2c1dd86c9ecba17c233262fd8ab80d321d64ae2758ab src/admin/web/css/50-settings.css
|
||||
f8b515c63080c71ee647cfd8c36628271e8357b31cd84f00cf591e9b752b44fe src/admin/web/css/60-monitoring.css
|
||||
@@ -245,8 +245,8 @@ cdd390e69ca4cd010262e8fddb653e4097fbcfdc678b081c422edcd68e974f85 src/admin/web/
|
||||
ac9f667273918e18406df06cb2613c923f50a88897693a03a18f500e22dc6ec2 src/admin/web/login.html
|
||||
c2e5f554810b8e34fb47361fb90a01073a2e332ee26d82e030956fad3ec1f83e src/admin/web/login.js
|
||||
734aeac4855f0913a84a82d7b5c17b37de4504b50a8701edeb775d6926929bce src/admin/web/management.js
|
||||
4481a592fcce9a2aa1db5e4a25619e19fd459c39039b86c0549dfc427eec1b45 src/admin/web/partials/overview-charts.html
|
||||
69296af507dbc8c211800f4841c3d7ad6a2490aad2e93ecb297246b7470e3be3 src/admin/web/partials/overview-metrics.html
|
||||
1f3d10e389c9c20e6eb019509fa6e82b50c6b43bbf988c1210a4d42bb73985d0 src/admin/web/partials/overview-charts.html
|
||||
2842346eb66569b0aba2938ff0f724b50d21254b2aa0d2c00f6380ad03449b61 src/admin/web/partials/overview-metrics.html
|
||||
3928afce2ed6d5f2d3e29475dd29e4c3bd3c6df180ad5119beaff0c7a58e3b3b src/admin/web/prometheus-token.js
|
||||
2d491fe9a547fe109dc5db6a74806d50b08c3daca15a6622c79c9fea33b10c63 src/admin/web/redirects.js
|
||||
2e891f34cad2a2950a47e529e15ed9dd41beedec71280b8035b19cfb91a5d317 src/admin/web/settings.js
|
||||
@@ -291,7 +291,7 @@ d789bf40cf9433e2fd16409d112210fe93154588b1ac9bddc9c7f78e1db14426 src/balance/po
|
||||
a361c341932ffd186c0b21356c91ab1fe8043f5020d40798266d15ae896a114e src/balance/tests.rs
|
||||
ae06d238b1451328c751a511476ef39c012f3a397393f939ca02f96ecd0119a6 src/basic_auth/mod.rs
|
||||
9a8858b7ff461a4d961ba701ca3ed6eaec15c3f7f05eef8e0e30edc63617605a src/basic_auth/tests.rs
|
||||
e35648537d5ac0bfb604997200d1be25652ee4e8c49bf8b720b68df087bd0249 src/cache/cache_regression_tests.rs
|
||||
90f26bcd36720076f874cd0a21230316b7303f68e23839e987cf51e13c23352b src/cache/cache_regression_tests.rs
|
||||
7def03ace07dc38a4806277d75b6ce673d33ddaaf1c4855fa0a3372be1cf9489 src/cache/cold.rs
|
||||
b75ff512a1c6344483e00d2c67578ceea6b5472c279da0246f3622daa68d7e16 src/cache/cold/cleanup.rs
|
||||
5fde9e9f77277d557372e4614264512804192bb51f3d2eae785988c17b2e7253 src/cache/cold/cold_tier_tests.rs
|
||||
@@ -301,7 +301,7 @@ b75ff512a1c6344483e00d2c67578ceea6b5472c279da0246f3622daa68d7e16 src/cache/cold
|
||||
c6c54984cf3460e6227d40d8d6917d7ca5d809ac364c80effe354c64133c765d src/cache/collapse.rs
|
||||
346fdb40d85627b583a01fa17c65e45c822dc7216198cdeadbb3d07904286fa3 src/cache/dashboard.rs
|
||||
be3d80cd340edb47755190a668e2f2a2776940665f95ae7110893b706eb94faa src/cache/dashboard_tests.rs
|
||||
5b0a3dec613fa22a38237618ddaeeca9df5c79acb499bfd78a31d9e7ce104d61 src/cache/fill.rs
|
||||
847c660e6cb7d752f9dac2807c5045a000172c25b0e437cbade06ad7b101dcba src/cache/fill.rs
|
||||
675d18afb53f25c56cba49bbb431fa39ecd9d7bdd192c7b39b0698225de7a2a2 src/cache/invalidation.rs
|
||||
c1357a13658e1ed5158084d3a41ba9ea906512774a5863ce9c56cb508d3fa6c8 src/cache/invalidation/ban.rs
|
||||
bbd9f372648d89b5e14d5b58029582d37f66a89752caf2c14eb1cbf8f24bb7a0 src/cache/invalidation/http_invalidation_tests.rs
|
||||
|
||||
@@ -77,10 +77,10 @@ ENV_FILE=integrations/routeros-oci-build/.env \
|
||||
bash integrations/routeros-oci-build/build-routeros-oci.sh push
|
||||
```
|
||||
|
||||
For example, with RustProxy version `0.35.4` and the defaults this publishes:
|
||||
For example, with RustProxy version `0.35.5` and the defaults this publishes:
|
||||
|
||||
```text
|
||||
zot.example.net/network/rustproxy:0.35.4
|
||||
zot.example.net/network/rustproxy:0.35.5
|
||||
zot.example.net/network/rustproxy:latest
|
||||
zot.example.net/network/rustproxy:routeros
|
||||
```
|
||||
|
||||
+2
-2
@@ -2849,12 +2849,12 @@
|
||||
"appHealthWarning": "Wymaga uwagi",
|
||||
"appHealthError": "Wykryto problem",
|
||||
"appHealthSidebarChecking": "SPRAWDZANIE",
|
||||
"appHealthSidebarOk": "APLIKACJA OK",
|
||||
"appHealthSidebarOk": "SYSTEM OK",
|
||||
"appHealthSidebarWarning": "APLIKACJA: UWAGA",
|
||||
"appHealthSidebarError": "APLIKACJA: BŁĄD",
|
||||
"appHealthUpdated": "Aktualizacja: {time}",
|
||||
"appHealthNoStatus": "Brak aktualnego statusu",
|
||||
"appHealthApi": "API sterujące",
|
||||
"appHealthApi": "API",
|
||||
"appHealthApiOk": "Status live aplikacji odpowiada.",
|
||||
"appHealthApiError": "Brak aktualnego statusu aplikacji.",
|
||||
"appHealthListeners": "Listenery",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "RustProxy Administration API",
|
||||
"version": "0.35.4",
|
||||
"version": "0.35.5",
|
||||
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust. The API also permits explicitly scoped GeneralApiBearer tokens on a narrow route allowlist, with RO/RW and source CIDR checks; API-token management and Prometheus-token management are browser-session-only."
|
||||
},
|
||||
"servers": [
|
||||
|
||||
@@ -435,8 +435,6 @@ function renderDiagnostics(status, appendSample = true, systemSample = appendSam
|
||||
$("process-summary").hidden = d.process_threads == null;
|
||||
if (d.process_threads != null) $("process-summary").textContent = `${fmt(d.process_threads)} ${t("threads")}`;
|
||||
setOptionalMetric("system-cpu", d.cpu_percent != null, `${fmt(d.cpu_percent, 1)}%`);
|
||||
const sourceSupported = !!d.source && d.source !== "unsupported";
|
||||
$("system-source").hidden = !sourceSupported; if (sourceSupported) $("system-source").textContent = t("realSample");
|
||||
const systemMemory = knownPair(d.memory_used_bytes, d.memory_total_bytes, value => fmtBytes(value));
|
||||
setOptionalMetric("system-memory", systemMemory != null, systemMemory);
|
||||
const ramMeter = $("memory-meter"), ramFill = $("memory-meter-fill");
|
||||
@@ -457,7 +455,9 @@ function renderDiagnostics(status, appendSample = true, systemSample = appendSam
|
||||
setOptionalMetric("system-swap", systemSwap != null, systemSwap);
|
||||
const loadSupported = Array.isArray(d.load_average) && d.load_average.length > 0;
|
||||
setOptionalMetric("system-load", loadSupported, loadSupported ? d.load_average.map(n => fmt(n, 2)).join(" / ") : "");
|
||||
setOptionalMetric("system-uptime", d.system_uptime_seconds != null, d.system_uptime_seconds == null ? "" : uptime(d.system_uptime_seconds));
|
||||
const systemUptime = $("system-uptime"), systemUptimeMeta = systemUptime?.closest(".system-uptime-meta");
|
||||
if (systemUptimeMeta) systemUptimeMeta.hidden = d.system_uptime_seconds == null;
|
||||
if (systemUptime && d.system_uptime_seconds != null) systemUptime.textContent = uptime(d.system_uptime_seconds);
|
||||
}
|
||||
$("system-chart").hidden = live.cpu_percent == null; if (appendSample && live.cpu_percent != null) { systemSamples.push(Number(live.cpu_percent)); systemSamples = systemSamples.slice(-window.rustProxyOverviewSamples.getLimit()); }
|
||||
$("system-sparkline").setAttribute("points", systemSamples.map((n, i) => `${i * 300 / Math.max(1, systemSamples.length - 1)},${95 - Math.max(0, Math.min(100, n)) * .9}`).join(" "));
|
||||
|
||||
@@ -1004,6 +1004,10 @@ html[data-dashboard-density=compact] #panel-overview .stat>span {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
html[data-dashboard-density=compact] #panel-overview .stat-cache-outcomes .cache-mix-header>span {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
html[data-dashboard-density=compact] #panel-overview .stat strong {
|
||||
font-size: 22px;
|
||||
margin: 3px 0 2px;
|
||||
@@ -1028,6 +1032,10 @@ html[data-dashboard-density=ultra] #panel-overview .stat>span {
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
html[data-dashboard-density=ultra] #panel-overview .stat-cache-outcomes .cache-mix-header>span {
|
||||
line-height: 1.08;
|
||||
}
|
||||
|
||||
html[data-dashboard-density=ultra] #panel-overview .stat strong {
|
||||
font-size: 18px;
|
||||
margin: 2px 0 1px;
|
||||
@@ -2097,7 +2105,12 @@ html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .over
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
font-family: var(--sans);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
#panel-overview .stat-cache-outcomes .cache-mix-caption {
|
||||
@@ -2132,16 +2145,18 @@ html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .over
|
||||
}
|
||||
|
||||
#panel-overview .cache-mix-legend {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 5px 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 7px;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#panel-overview .cache-mix-legend>span {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
gap: 3px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@@ -2164,19 +2179,49 @@ html[data-dashboard-density] #panel-overview .stat :is(.stat-chart-legend, .over
|
||||
}
|
||||
|
||||
#panel-overview .cache-mix-legend b {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
margin-left: auto;
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 2px;
|
||||
margin-left: 1px;
|
||||
color: var(--text);
|
||||
font: 500 10px var(--mono);
|
||||
overflow-wrap: anywhere;
|
||||
text-align: right;
|
||||
font: 500 9px var(--mono);
|
||||
}
|
||||
|
||||
#panel-overview .cache-mix-legend .cache-mix-percent {
|
||||
color: var(--muted);
|
||||
font: 400 10px var(--mono);
|
||||
font: 400 8px var(--mono);
|
||||
}
|
||||
|
||||
#panel-overview .cache-mix-legend .cache-mix-percent::before {
|
||||
content: "· ";
|
||||
}
|
||||
|
||||
#panel-overview .system-card .system-uptime-meta,
|
||||
#panel-overview .connection-card .connection-backends-meta {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#panel-overview .system-card .system-uptime-meta b,
|
||||
#panel-overview .connection-card .connection-backends-meta b {
|
||||
color: var(--text);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
#panel-overview .connection-card .connection-heading-main {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#panel-overview .connection-card .connection-current-main {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#panel-overview .connection-card .connection-backends-meta {
|
||||
margin-left: auto;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Live values stay on one line so refreshes do not change card heights. */
|
||||
|
||||
@@ -334,7 +334,8 @@
|
||||
<div>
|
||||
<h3 class="overview-chart-heading" data-i18n="systemLoad">System load</h3>
|
||||
<h2><span id="system-cpu">-</span> <small>CPU</small></h2>
|
||||
</div><span class="meta" id="system-source">-</span>
|
||||
</div><span class="meta system-uptime-meta"><span data-i18n="systemUptime">System uptime</span>: <b
|
||||
id="system-uptime">-</b></span>
|
||||
</div><svg preserveAspectRatio="none" aria-label="System CPU history" data-i18n-aria="systemChart" id="system-chart"
|
||||
role="img" viewbox="0 0 300 100">
|
||||
<path class="chart-grid" d="M0 25H300 M0 50H300 M0 75H300"></path>
|
||||
@@ -348,14 +349,15 @@
|
||||
<div class="memory-line"><span data-i18n="systemSwap">System swap</span><b id="system-swap">-</b></div>
|
||||
<div class="chart-footer"><span data-i18n="loadAverage">Load 1 / 5 / 15 min</span><span id="system-load">-</span>
|
||||
</div>
|
||||
<div class="chart-footer"><span data-i18n="systemUptime">System uptime</span><span id="system-uptime">-</span></div>
|
||||
</article>
|
||||
<article class="card connection-card" data-dashboard-widget="connection_chart">
|
||||
<div class="card-heading">
|
||||
<div>
|
||||
<div class="connection-heading-main">
|
||||
<h3 class="overview-chart-heading" data-i18n="connectionActivity">Connection activity</h3>
|
||||
<h2><span id="connection-current">0</span> <small data-i18n="connections">Open
|
||||
connections</small></h2>
|
||||
<h2><span class="connection-current-main"><span id="connection-current">0</span> <small
|
||||
data-i18n="connections">Open connections</small></span><small
|
||||
class="meta connection-backends-meta"><span data-i18n="backends">Backends</span>: <b
|
||||
id="connection-backends">0</b></small></h2>
|
||||
</div>
|
||||
</div>
|
||||
<svg preserveAspectRatio="none" aria-label="Connection activity history" data-i18n-aria="connectionActivity"
|
||||
@@ -368,6 +370,4 @@
|
||||
connections</span> <b id="connection-open-value">0</b></span><span
|
||||
class="connection-exchange-key"><i></i><span data-i18n="activeExchanges">Active
|
||||
exchanges</span> <b id="connection-exchange-value">0</b></span></div>
|
||||
<div class="chart-footer"><span><span data-i18n="backends">Backends</span>: <b id="connection-backends">0</b></span>
|
||||
</div>
|
||||
</article>
|
||||
@@ -517,5 +517,4 @@
|
||||
<path class="chart-grid" d="M0 15H300 M0 30H300 M0 45H300"></path>
|
||||
<polyline class="diagnostic-line proto-h2-line" id="www_php_requests-line" points="0,55 300,55"></polyline>
|
||||
</svg>
|
||||
<small data-i18n="webMetricPerSecond">Requests per second</small>
|
||||
</article>
|
||||
Vendored
+39
-2
@@ -6,7 +6,6 @@ mod tests {
|
||||
h.insert("cache-control", value.parse().unwrap());
|
||||
h
|
||||
}
|
||||
// Legacy policy; varnish-compatible behavior is tested separately.
|
||||
fn settings() -> Settings {
|
||||
Settings {
|
||||
enabled: true,
|
||||
@@ -1559,7 +1558,10 @@ mod tests {
|
||||
let second = cache
|
||||
.ticket(1, "http", "example.test", "app", "/second", &headers)
|
||||
.unwrap();
|
||||
fill(&cache, first.clone(), "first", &s).await;
|
||||
// Stored bodies are trimmed to their real size, so the first object must
|
||||
// itself be large for the next fill reservation to need its space.
|
||||
let large: &'static str = Box::leak("f".repeat(60 * 1024).into_boxed_str());
|
||||
fill(&cache, first.clone(), large, &s).await;
|
||||
assert!(cache.get(&first).is_some());
|
||||
let before = cache.view();
|
||||
assert!(before.hot_allocated_bytes <= s.max_bytes);
|
||||
@@ -1821,4 +1823,39 @@ mod tests {
|
||||
assert_eq!(cache.view().entries, 0);
|
||||
assert_eq!(cache.view().fills_active, 0);
|
||||
}
|
||||
#[tokio::test]
|
||||
async fn stored_entries_do_not_pin_origin_buffers_or_spare_capacity() {
|
||||
let cache = Cache::new(1);
|
||||
let s = settings();
|
||||
let t = ticket(&cache, &HeaderMap::new());
|
||||
// Header values parsed by hyper are slices of a larger read buffer.
|
||||
let read_buffer = Bytes::from(vec![b'x'; 64 * 1024]);
|
||||
let mut h = headers("max-age=60");
|
||||
h.insert(
|
||||
"x-origin",
|
||||
HeaderValue::from_maybe_shared(read_buffer.slice(0..16)).unwrap(),
|
||||
);
|
||||
h.insert(
|
||||
::http::header::ETAG,
|
||||
HeaderValue::from_maybe_shared(read_buffer.slice(16..24)).unwrap(),
|
||||
);
|
||||
let p = plan(StatusCode::OK, &h, &s, Duration::ZERO).unwrap();
|
||||
let mut response = Response::new(Body::from("small body"));
|
||||
*response.headers_mut() = h;
|
||||
cache
|
||||
.wrap(response, t.clone(), p, &s)
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(cache.get(&t).is_some());
|
||||
assert!(
|
||||
read_buffer.try_into_mut().is_ok(),
|
||||
"cached headers must not keep the origin read buffer alive"
|
||||
);
|
||||
let store = cache.store.lock().unwrap();
|
||||
let entry = store.entries.values().next().expect("stored object");
|
||||
assert_eq!(entry.body.len(), 10);
|
||||
assert!(entry.body_allocated <= 10 + 10 / 8 + 1);
|
||||
}
|
||||
}
|
||||
Vendored
+40
-2
@@ -1,3 +1,16 @@
|
||||
fn detached_header_value(value: &HeaderValue) -> HeaderValue {
|
||||
let mut detached = HeaderValue::from_bytes(value.as_bytes())
|
||||
.expect("existing HTTP header value must remain valid when copied");
|
||||
detached.set_sensitive(value.is_sensitive());
|
||||
detached
|
||||
}
|
||||
|
||||
fn detach_header_map(headers: &mut HeaderMap) {
|
||||
for value in headers.values_mut() {
|
||||
*value = detached_header_value(value);
|
||||
}
|
||||
}
|
||||
|
||||
struct ByteGaugeGuard<'a> {
|
||||
counter: &'a AtomicU64,
|
||||
bytes: u64,
|
||||
@@ -102,6 +115,9 @@ impl Candidate {
|
||||
let (body, body_allocation) = if let Some(body) = self.shared.take() {
|
||||
(body, self.shared_allocated)
|
||||
} else {
|
||||
if self.data.capacity() > self.data.len().saturating_add(self.data.len() / 8) {
|
||||
self.data.shrink_to_fit();
|
||||
}
|
||||
let allocated = self.data.capacity().max(self.data.len());
|
||||
(Bytes::from(std::mem::take(&mut self.data)), allocated)
|
||||
};
|
||||
@@ -111,6 +127,12 @@ impl Candidate {
|
||||
let domain = self.ticket.domain.clone();
|
||||
let path = self.ticket.path.clone();
|
||||
let target = self.ticket.target.clone();
|
||||
detach_header_map(&mut self.headers);
|
||||
self.origin_etag = self.origin_etag.as_ref().map(detached_header_value);
|
||||
self.origin_last_modified = self
|
||||
.origin_last_modified
|
||||
.as_ref()
|
||||
.map(detached_header_value);
|
||||
let tags = cache_tags(&self.headers);
|
||||
let cost = hot_entry_cost(
|
||||
body_allocation,
|
||||
@@ -226,7 +248,21 @@ impl HttpBody for FillBody {
|
||||
let needed = candidate.data.len().saturating_add(data.len());
|
||||
if needed <= candidate.settings.max_object_bytes {
|
||||
let before = candidate.data.capacity();
|
||||
let required_growth = needed.saturating_sub(before);
|
||||
let target_capacity = if needed <= before {
|
||||
before
|
||||
} else if let Some(length) =
|
||||
candidate.plan.expected_length.filter(|length| {
|
||||
*length >= needed
|
||||
&& *length <= candidate.settings.max_object_bytes
|
||||
})
|
||||
{
|
||||
length
|
||||
} else {
|
||||
needed
|
||||
.max(before.saturating_mul(2))
|
||||
.min(candidate.settings.max_object_bytes)
|
||||
};
|
||||
let required_growth = target_capacity.saturating_sub(before);
|
||||
if required_growth > 0
|
||||
&& !candidate
|
||||
.fill_gauge
|
||||
@@ -246,7 +282,9 @@ impl HttpBody for FillBody {
|
||||
} else if required_growth > 0
|
||||
&& candidate
|
||||
.data
|
||||
.try_reserve_exact(needed.saturating_sub(candidate.data.len()))
|
||||
.try_reserve_exact(
|
||||
target_capacity.saturating_sub(candidate.data.len()),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
candidate.cache.record_reason(
|
||||
|
||||
Reference in new issue
Block a user